我为一种DSL编写了一个JJTree(JavaCC)配置,它成功地对给定格式的文件进行了标记,并在请求时转储AST。
问题是树中的每个节点都是空的,因为我目前没有存储令牌(我无法理解这些示例)。
这是我的.jjt文件的一部分:
SimpleNode Start() #Root : {} {
(
( Section1() )?
( Section2() )?
( Section3() )*
) {
return jjtThis;
}
}
void Section3() #Section3 : {}
{
< SECTION_3 > Identifier() <LBRACE >
Header()
(Details() < SEMICOLON > )*
< RBRACE >
}
我希望根节点存储对Section1,Section2的引用以及对Section3的引用列表。我希望Section3节点存储标识符,标题块并保留详细信息块的列表。
我的填充.jjt文件是数百行,但我觉得如果我能理解这两个部分,那么我就能理解JJTree是如何工作的。请让我知道如何正确使用JJTree。
感谢。
答案 0 :(得分:2)
如果查看SimpleNode类,您会注意到它的实例会自动存储对其父节点和子节点的引用(除非使用#void抑制它们的创建)。例如,您的根节点将包含对0..1 Section1节点,0..1 Section2节点和0 .. * Section3节点的引用,并且可以使用返回Node对象的jjtGetChild()方法访问它们。要确定此子节点是Section1,Section2还是Section3节点,可以调用其toString()方法(如dump()那样)。
或者,如果您厌倦了这种天真的Node迭代和toString检查,您可以定义自己的节点类型,而不是依赖于SimpleNode实现。在下面的示例中,Start()现在返回自定义RootNode而不是简单的SimpleNode。 RootNode包含对其子节点的特定引用(根据需要为这些节点定义getter)。请注意,我的简短代码段假定Section1 / 2/3()都返回自定义节点,但这不是 的情况......从你所说的,你想要一个自定义节点对于Section3()但如果Section1 / 2很简单,您可以将它们保留为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);
}
}
如果您正在遍历解析树并对节点执行复杂的操作,那么将其中的一些移动到Visitor class可能是个好主意,这样您对节点执行的操作就会脱离节点类本身。最终可能会有几个访问者类,每个访问者类在解析树上执行一个函数,并且对每种类型的节点都有访问方法重载。
让我知道你有什么不明白的地方。我不是JavaCC专家(我在大学曾经使用它一次)但我应该能够帮助你:)