MudBytes
» MUDBytes Community » Language Discussions » C and C++ » abstract trees in c
Pages: << prev 1 next >>
abstract trees in c
JohnnyStarr
Wizard






Group: Members
Posts: 953
Joined: Feb 14, 2009

Go to the bottom of the page Go to the top of the page
#1 id:46769 Posted Jun 6, 2010, 4:15 pm

I've been learning about trees again to better understand them.
I came up with the following solution in C++ to see how it might work:
Code (c++):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
 
#include <iostream>
#include <vector>
 
using namespace std;
 
class TreeNode {
	public:
	TreeNode( string v ) { value = v; };
	string value;
	vector<TreeNode*> children;	
};
 
void traverse(TreeNode *t)
{
	cout << t->value << endl;
	vector<TreeNode*>::iterator c;
	for ( c = t->children.begin(); c != t->children.end(); c++)
		traverse(*c);	
}
 
main()
{
	TreeNode *root = new TreeNode("Root");
	TreeNode *branchA = new TreeNode("BranchA");
	TreeNode *childA = new TreeNode("   ChildA");
	TreeNode *branchB = new TreeNode("BranchB");
	TreeNode *childB = new TreeNode("   ChildB");
 
	branchA->children.push_back(childA);
	branchB->children.push_back(childB);
 
	root->children.push_back(branchA);
	root->children.push_back(branchB);
 
	traverse(root);
 
}
 


Code (output):
1
2
3
4
5
6
7
8
9
 
Root
BranchA
   ChildA
BranchB
   ChildB
 


A vector works nice as it keeps track of the nodes that are children of the current node.

This is obviously not useful, other than the fact that it helps me understand more how the recursive
algorithm goes through each node and respective children. I still want to create my own scripting language,
but I see the benefit in creating it in C, and not C++. I would like a similar solution in C to practice with, but
without the vector / list I cant figure out how to go about it.

Say I was to make the "children" variable a pointer to the "next" TreeNode? Say you have Node-A, which has 2 children:
Child-A and Child-B, both "A" and "B" are peers in a sense, they both have the same parent node. Well, if the list was just a C-Style linked list, then Child-B would in essence be Child-A's child, not Node-A's.

I thought about adding a pointer called "next" calling the next child in the subset, but haven't decided if that would make
the most sense.

Last edited Jun 6, 2010, 4:17 pm by JohnnyStarr
JohnnyStarr
Wizard






Group: Members
Posts: 953
Joined: Feb 14, 2009

Go to the bottom of the page Go to the top of the page
#2 id:46770 Posted Jun 6, 2010, 6:04 pm

Ok, heres the answer to my own question:
Code (C):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
typedef struct tree_node {
	char* value;
	struct tree_node *children; /* head pointer of children list */
	struct tree_node *next;     /* next peer in child list */
}TreeNode;
 
/* local protos */
 
void traverse(TreeNode*);
TreeNode *talloc(const char*);
 
TreeNode *talloc(const char *value)
{
	TreeNode *t = (TreeNode*) malloc(sizeof(TreeNode));
	t->value = strdup(value);
	t->children = NULL;
	t->next = NULL;
	return t;
}
 
void traverse(TreeNode* t)
{
	puts(t->value);
	TreeNode *c;
 
	for ( c = t->children; c; c = c->next)
		traverse(c);
}
 
int main(void) 
{
	TreeNode *root = talloc("Root");
	TreeNode *branch1 = talloc("BranchA");
	TreeNode *child1 = talloc("   ChildA");
	TreeNode *branch2 = talloc("BranchB");
	TreeNode *child2 = talloc("   ChildB");
 
	branch1->children = child1;
	branch2->children = child2;
 
	branch1->next = branch2;
	root->children = branch1;
 
	traverse(root);	
}
 


Code (output):
1
2
3
4
5
6
7
8
9
 
Root
BranchA
   ChildA
BranchB
   ChildB
 

Last edited Jun 6, 2010, 6:05 pm by JohnnyStarr
Scandum
Wizard






Group: Members
Posts: 1,783
Joined: Aug 8, 2006

Go to the bottom of the page Go to the top of the page
#3 id:46771 Posted Jun 6, 2010, 6:51 pm

Doing it in c++ gives you proper string handling and associative arrays, both of which are difficult to handle in C.
.........................
TinTin++ Mud Client - I can't believe it's not butter!

David Haley
Wizard






Group: Members
Posts: 7,783
Joined: Jun 30, 2007

Go to the bottom of the page Go to the top of the page
#4 id:46776 Posted Jun 6, 2010, 7:31 pm

Quote:
but I see the benefit in creating it in C, and not C++.

Ah, you do? And what is that benefit?
.........................
-- d.c.h --
BabbleMUD Project (custom codebase)
Legends of the Darkstone (head coder)
http://david.the-haleys.org
.........................

JohnnyStarr
Wizard






Group: Members
Posts: 953
Joined: Feb 14, 2009

Go to the bottom of the page Go to the top of the page
#5 id:46777 Posted Jun 6, 2010, 7:50 pm

From the Lua manual:
Lua said:

Compiling Lua is straightforward. Lua, including the language processor and its core libraries, is written
in plain vanilla C so that it can be built on a wide variety of platforms with any ANSI-compliant C com-
piler. The advantage is that the resulting libraries and interpreter program are compatible with the sys-
tem on which it’s built. This is one of the principal advantages of open-source software in general. As
long as the target platform supports the tools and libraries needed to compile the source code — in Lua’s
case, this is a standard C compiler — the resulting binary program is compatible with the platform.


Obviously C++ is much easier and better suited for the development process, but I would like to really
write a scripting language that is used by other people. If it takes me 10 years and noone uses it is for
another thread. I know the odds are against me, but who knows ;)

David Haley
Wizard






Group: Members
Posts: 7,783
Joined: Jun 30, 2007

Go to the bottom of the page Go to the top of the page
#6 id:46778 Posted Jun 6, 2010, 7:53 pm

The wide variety of platforms they're talking about are things like embedded devices and hardware that hasn't had compiler upgrades for years. Recall that Lua is supposed to run on embedded devices -- so this extreme degree of cross-platform compatibility is very important.

But if your target platforms are Linux from the past 10 years and similarly for Windows, then you have nothing to worry about, really.
.........................
-- d.c.h --
BabbleMUD Project (custom codebase)
Legends of the Darkstone (head coder)
http://david.the-haleys.org
.........................

JohnnyStarr
Wizard






Group: Members
Posts: 953
Joined: Feb 14, 2009

Go to the bottom of the page Go to the top of the page
#7 id:46780 Posted Jun 6, 2010, 8:18 pm

Well, that's cool. I'm really starting to like C++  :smile:

JohnnyStarr
Wizard






Group: Members
Posts: 953
Joined: Feb 14, 2009

Go to the bottom of the page Go to the top of the page
#8 id:46957 Posted Jun 8, 2010, 12:50 pm

My quick solution above uses recursion instead of a stack object to keep
track of the tree's state. I have seen some examples where a stack is used
instead. Perhaps someone could point out the benefit / usefulness of using a stack.

David Haley
Wizard






Group: Members
Posts: 7,783
Joined: Jun 30, 2007

Go to the bottom of the page Go to the top of the page
#9 id:46960 Posted Jun 8, 2010, 1:10 pm

A stack lets you track the state explicitly, meaning that you can have a stack in your host program and a stack in your scripting language.

Using recursion in the host to track the hosted state means that you are using the host's stack, meaning that separating the two stacks of execution becomes quite difficult.

Consider what you would need to do in order to pause execution in your hosted language, go to the host language and muck around for a while, let some network ticks pass, and eventually return to the interpreted language. How would you restore the stack, when it was implicit in your host?
.........................
-- d.c.h --
BabbleMUD Project (custom codebase)
Legends of the Darkstone (head coder)
http://david.the-haleys.org
.........................

JohnnyStarr
Wizard






Group: Members
Posts: 953
Joined: Feb 14, 2009

Go to the bottom of the page Go to the top of the page
#10 id:46980 Posted Jun 8, 2010, 8:43 pm

quick question on static variables:
In Lua, each function returns a static int. If you are passing the LuaState*, shouldn't that be enough?
If you have several LuaStates for example, why would you want the value returned static?

David Haley
Wizard






Group: Members
Posts: 7,783
Joined: Jun 30, 2007

Go to the bottom of the page Go to the top of the page
#11 id:46984 Posted Jun 8, 2010, 10:05 pm

Ah, the joys of overloaded C keywords.

For a global function definition, the declaration

Code (text):
1
2
3
static int foo()


doesn't mean that the returned int is static. It means that the function identifier is static, i.e. visible to that compilation unit only. (In fact, any global identifier defined as static behaves this way. Local variables that are static have the behavior that you describe.)
.........................
-- d.c.h --
BabbleMUD Project (custom codebase)
Legends of the Darkstone (head coder)
http://david.the-haleys.org
.........................

JohnnyStarr
Wizard






Group: Members
Posts: 953
Joined: Feb 14, 2009

Go to the bottom of the page Go to the top of the page
#12 id:46995 Posted Jun 9, 2010, 6:09 am

David Haley said:
Ah, the joys of overloaded C keywords.

haha, indeed.

Noplex
Conjurer






Group: Members
Posts: 107
Joined: May 20, 2006

Go to the bottom of the page Go to the top of the page
#13 id:47000 Posted Jun 9, 2010, 11:16 am

Code (text):
1
2
3
4
5
 
TreeNode( const string& v ) { value = v; };
 

:-p, why hello all.
.........................
jb

David Haley
Wizard






Group: Members
Posts: 7,783
Joined: Jun 30, 2007

Go to the bottom of the page Go to the top of the page
#14 id:47001 Posted Jun 9, 2010, 11:19 am

Long time no see. :tongue:

And yeah, you rarely want to pass around strings: passing a const ref is almost always a better idea. (It's more efficient, because you don't need to copy the string, and since it's const, the function can't mess with the caller's string.)
.........................
-- d.c.h --
BabbleMUD Project (custom codebase)
Legends of the Darkstone (head coder)
http://david.the-haleys.org
.........................

JohnnyStarr
Wizard






Group: Members
Posts: 953
Joined: Feb 14, 2009

Go to the bottom of the page Go to the top of the page
#15 id:47013 Posted Jun 9, 2010, 6:44 pm

Sort of on the same topic, I've been playing around with Ruby to get the core concepts down.
I've found that the hardest part is not seeing it work while you learn it. I'm sure this will be much
longer and harder in C or even C++. This is my take on a quick Lexer in Ruby.

No it's not perfect, and it doesn't account for string literals yet but:

Code (Ruby):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
 
#!/usr/bin/ruby1.9
 
# Lexical Analyzer
#
class Lex
 
  def initialize()
	@list = []
	@tokens = {
	  'def'   => :define,
	  'if'    => :if,
	  'then'  => :then,
	  'end'   => :end,
	  'else'  => :else,
	  'do'    => :do,
	  'while' => :while,
	  'loop'  => :loop,
	  'case'  => :case,
	  'nil'   => :nil,
	  'null'  => :null,
	  'void'  => :void,
	  ?''     => :smallquote, # double '' and "" to humor the mudbytes syntax highlighter :)
	  ?""     => :quote,
	  ';'     => :semicol,
	  '{'     => :opencurley,
	  '}'     => :closecurley, 
	  '['     => :openbracket,
	  ']'     => :closebracket,
	  '('     => :openparen,
	  ')'     => :closeparen,
	  '+'     => :op_plus,
	  '-'     => :op_minus,
	  '*'     => :op_times,
	  '/'     => :op_divide,
	  '='     => :op_equals,
	  '=='    => :op_isequal,
	  '.'     => :op_deref_dot,
	  '->'    => :op_deref_arrow,
	  '::'    => :op_scope,
	}
  end
 
  def tokenize()	
	for s in @pile
	  if @tokens[s]
		@list << @tokens[s]
	  else
		@list << "identifier [#{s}]"
	  end
	end
  end
 
  def scan(str)
	[?(, ?), ?., ?{, ?}, ?[, ?], ?;, "'", "->", "::"].each {|c| str.gsub!(c, " #{c} ")}
	@pile = str.split(' ')
	tokenize()
	output()
  end
 
  def output()
	puts "\nLexemes:\n__________________"
	@list.each {|s| puts ":#{s}"}
  end
 
end
 
code =  "def main(ch)\n"
code << "{\n"
code << "   if ch.level = 34\n"
code << "   {\n"
code << "      Server::sendGreeting(ch.name());\n"
code << "   }\n"
code << "}"
 
 
puts "Original Code:\n\n" + code
 
Lex.new.scan(code)
 


Code (output):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
 
Original Code:
 
def main(ch)
{
   if ch.level = 34
   {
      Server::sendGreeting(ch.name());
   }
}
 
Lexemes:
__________________
:define
:identifier [main]
:openparen
:identifier [ch]
:closeparen
:opencurley
:if
:identifier [ch]
:op_deref_dot
:identifier [level]
:op_equals
:identifier [34]
:opencurley
:identifier [Server]
:op_scope
:identifier [sendGreeting]
:openparen
:identifier [ch]
:op_deref_dot
:identifier [name]
:openparen
:closeparen
:closeparen
:semicol
:closecurley
:closecurley
 
 


Next, it's on to the parser. So far, I'm going to iterate through the list and create an AST.
Then, on with the semantic analysis.

Last edited Jun 9, 2010, 6:55 pm by JohnnyStarr
Pages:<< prev 1 next >>

Valid XHTML 1.1! Valid CSS!