Reputation: 4588
I have written a JJTree (JavaCC) configuration for a type of DSL and it successfully tokenizes a given format of file and will dump the AST when requested.
The problem is that each of the nodes in the tree is empty as I am not currently storing the tokens (I can't understand the examples).
Here is part of my .jjt file:
SimpleNode Start() #Root : {} {
(
( Section1() )?
( Section2() )?
( Section3() )*
) {
return jjtThis;
}
}
void Section3() #Section3 : {}
{
< SECTION_3 > Identifier() <LBRACE >
Header()
(Details() < SEMICOLON > )*
< RBRACE >
}
I would like the root node to store references to Section1, Section2 and a list of references to Section3. I would like the Section3 node to store the identifier, the header block and keep a list of the details block.
My fill .jjt file is hundreds of lines, but I feel that if I can understand it for these two sections then I can understand how JJTree works. Please let me know how to use JJTree properly.
Thanks.
Upvotes: 3
Views: 3028
Reputation: 3594
If you look at the SimpleNode class, you'll notice that its instances automatically store references to their parent and children nodes (unless their creation was suppressed using #void). For example, your root node will contain references to 0..1 Section1 nodes, 0..1 Section2 nodes and 0..* Section3 nodes and they can be accessed using the jjtGetChild() method which returns a Node object. To determine whether this child node is a Section1, Section2 or Section3 node, you could call its toString() method (as dump() does).
Alternatively, if you get sick of this style of naive Node iteration and toString checking, you can define your own node types instead of relying on the SimpleNode implementation. In my example below, Start() now returns a custom RootNode instead of a plain SimpleNode. RootNode contains specific references to its children nodes (define getters for these as you see fit). Note that my brief snippet assumes Section1/2/3() all return custom nodes, but this doesn't have to be the case... from what you said, you'd want a custom node for Section3() but if Section1/2 are trivial, you might leave them as SimpleNodes.
RootNode Start() :
{
Section1Node s1Node = null;
Section2Node s2Node = null;
List s3Nodes = new LinkedList();
Section3Node s3Node = null;
}
{
(
( s1Node = Section1() )?
( s2Node = Section2() )?
( s3Node = Section3() {s3Nodes.add(s3Node); } )*
) {
return new RootNode(s1Node, s2Node, s3Nodes);
}
}
If you're traversing your parse tree and doing complex things with your nodes, it might be a good idea to move some of it to a Visitor class so that what you're doing with your nodes is decoupled from the node classes themselves. You might end up with several visitor classes which each perform a function on the parse tree and have visit method overloads for each type of node.
Let me know if there's anything you don't understand. I'm no JavaCC expert (I used it at uni once) but I should be able to help you :)
Upvotes: 3