我可以在树的构建过程中进行这种重新排列,还是应该写一个树的语法?
两者都可以,但我建议在令牌解析期间对节点进行分组。
我对我编写的组节点的任何树重写语法都不满意,因为这些语法必须重新发现每个可分组节点的位置——因此需要分组。令牌解析器在常规处理期间触及所有这些数据,并且树语法最终会为这些节点遍历树,就像令牌解析器已经遍历其输入的令牌一样。如果树解析器只是只是用于分组,我认为树解析器不值得麻烦。
无论如何,在解析器中管理分组归结为在 decl 和 assign 节点生成后保存它们,然后在它们的分组级别发生时再次将它们推出。这是一个简单的例子。
声明性.g
grammar Declarative;
options {
output = AST;
}
tokens {
PROGRAM; DECLARATIONS; ASSIGNMENTS;
}
@parser::header {
import java.util.ArrayList;
}
@members {
private ArrayList<Object> assigns = new ArrayList<Object>();
private ArrayList<Object> decls = new ArrayList<Object>();
private Object createTree(int ttype, ArrayList<Object> children) {
Object tree = adaptor.create(ttype, tokenNames[ttype]);
for (Object child : children){
adaptor.addChild(tree, child);
}
return tree;
}
}
compilationUnit : statement* EOF -> ^(PROGRAM {createTree(DECLARATIONS, decls)} {createTree(ASSIGNMENTS, assigns)});
statement : decl {decls.add($decl.tree);}
| assign {assigns.add($assign.tree);}
;
decl : DECL^ ID;
assign : ASSIGN^ ID INT;
DECL : 'decl';
ASSIGN : 'assign';
ID : ('a'..'z'|'A'..'Z')('a'..'z'|'A'..'Z')*;
INT : ('0'..'9')+;
WS : (' '|'\t'|'\f'|'\n'|'\r'){skip();};
每个decl 节点都被statement 规则保存在decls 列表中,对于每个assign 节点也是如此。
方法createTree 使用解析器的TreeAdaptor 构建组节点并填充它们。
CommonTree tree = (CommonTree) adaptor.create(ttype, tokenNames[ttype]);
for (Object child : children){
adaptor.addChild(tree, child);
}
return tree;
compilationUnit 的产生式是^(PROGRAM {createTree(DECLARATIONS, decls)} {createTree(ASSIGNMENTS, assigns)}),它将分组节点添加到PROGRAM。方法createTree用于一次性构建分组节点及其子节点。
可能有一种棘手的方法可以让 ANTLR 为您整合所有内容,但这很有效,而且不言自明。
所以给定这个输入...
decl x
assign y 2
assign x 1
decl y
...为上述语法生成的标记解析器生成此树作为输出:
(PROGRAM
(DECLARATIONS
(decl x)
(decl y))
(ASSIGNMENTS
(assign y 2)
(assign x 1)))