jjTree 简介

 

YACC等一样,JavaCC不直接支持分析树或抽象语法树(AST)的生成,如果要完成这些功能,用户需要自己编写相应的代码。幸运的是,JavaCC有一个扩充支持分析树或抽象语法树的生成,这就是jjTree

 

实际上,jjTree可以看成是JavaCC的预处理程序。它的输入文件的后缀是jjt。经它处理之后的jj文件就包含了生成分析树的能力。jjTree采用压栈出栈的方法生成分析树。当它碰到一个非终结符要展开时,它会做一个标记,然后开始分析展开后的各个非终结符(此时,分析子树作为节点压入栈中),之后从栈中弹出合适个数的节点(分析子树),并以被展开的非终结符生成一个新节点,以这个新节点为根节点,以刚弹出的节点为子节点生成新的分析子树(分析树)。由此可见,这个过程是递归的。

 

jjTree处理后的分析器类里有一个叫做jjtree的成员,它实际上代表了分析栈。我们可以手动操作这个栈,如弹出、压入、取栈顶等基本操作。jjTree也会生成一个实现了Node接口的SimpleNode类,它是真正被压入分析栈的节点的类。

 

缺省情况下得到的分析树是严格对应于分析过程的。它只是将非终结符作为节点,以非终结符的名字作为节点的名字构成分析树,其他的符号不会存在于分析树中。因此大部分情况下,缺省的结果是不理想的,可喜的是jjTree为我们提供了修改它的行为的方法。

 

jjTree允许我们声明何时生成什么样的节点。在jjTree中,有两种形式的节点。

 

l         #NodeName(数字):这种形式的节点称为确定性节点,当生成以这个节点为根的分析子树时,它会从栈里弹出指定个数的节点作为它的孩子。

l         #NodeName(布尔表达式):这种形式的节点称为非确定节点,当对它所对应的非终结符进行展开时,jjTree会对栈做一个记号,说明非终结符从何处开始展开,当展开结束后,如果布尔表达式的值为真,就从标记处弹出所有上面的节点,构成新的分析子树,并将它压入栈中。如果布尔表达式的值为假,就什么也不做。

 

第二种形式的节点有一些简单的记号:#NodeName表示#NodeName(true)#NodeName(>数字)表示#NodeName(jjtree.arity>0)等。

 

如果不希望jjTree生成以某个非终结符为根的分析子树,可以在这个非终结符后面加一个声明#void

 

需要注意:节点生命可以放到一个非终结符声明的后面,例如下面:

Void Term() #MyTerm:

{……}

{……}

这样当对Term的展开成功时,就会生成一个名字为MyTerm的节点。

也可以放到某个非终结符的展开的某一段的后面,例如:

Void VarList():

{…}

{

       <Type> (<Var> (<Comma> <Var>)*) #list

}

这样,VarList下面最多只有两个子节点,其中一个可能是list

 

事实上,对jjTree而言,缺省的非终结符的声明相当于它后面声明了一个跟它同名的(大写)节点,例如:

Void Term():

{…}

{…}

相当于

Void Term()#TERM:

{…}

{…}

为了使对非终结符的处理采用 #void 方式的处理,可以把选项 NODE_DEFAULT_VOID置为真(缺省为假)。   我们上面已经提到,jjTree对于终结符的处理是不将它作为节点压入,因此,为了解决这个问题, 我们可以在一个终结符的后面插入一个节点声明,例如: Void A(): {…} {     (<B>#B) C() }   一个节点的作用域是它对应的非终结符或它前面控制的串展开时,可以用jjtThis访问这个节点。 一个节点的生命期如下:
  1. the node's constructor is called with a unique integer parameter. This parameter identifies the kind of node and is especially useful in simple mode. JJTree automatically generates a file called parserTreeConstants.java and declares Java constants for the node identifiers. It also declares an array of Strings called jjtNodeName[] which maps the identifier integers to the names of the nodes.
  2. the node's jjtOpen() method is called.
  3. if the option NODE_SCOPE_HOOK is set, the user-defined parser method openNodeScope() is called and passed the node as its parameter. This method can initialize fields in the node or call its methods. For example, it might store the node's first token in the node.
  4. if an unhandled exception is thrown while the node is being parsed then the node is abandoned. JJTree will never refer to it again. It will not be closed, and the user-defined node scope hook closeNodeHook() will not be called with it as a parameter.
  5. otherwise, if the node is conditional and its conditional expression evaluates to false then the node is abandoned. It will not be closed, although the user-defined node scope hook closeNodeHook() might be called with it as a parameter.
  6. otherwise, all of the children of the node as specified by the integer expression of a definite node, or all the nodes that were pushed on the stack within a conditional node scope are added to the node. The order they are added is not specified.
  7. the node's jjtClose() method is called.
  8. the node is pushed on the stack.
  9. if the option NODE_SCOPE_HOOK is set, the user-defined parser method closenNodeScope() is called and passed the node as its parameter.
  10. if the node is not the root node, it is added as a child of another node and its jjtSetParent() method is called.

上面提到的处理完全由jjTree为我们生成代码完成,如果对生成的代码不满意,我们完全可以自己手动来写,这个类似于JavaCC{}里面的内容,jjTree为我们提供了几个API来操作节点、树等对象,而且我们可以通过编写对应于作用域打开或作用域关闭的回调函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值