Parsing 如何在自定义重写操作中反向引用AST?
我已经知道这个问题的解决方法,但我想真正使用这个方法,至少有一个原因——它应该可以工作 这条规则摘自特伦斯·帕尔(Terence Parr)的“最终ANTLR参考”(该书适用于ANTLR3): 如果Parsing 如何在自定义重写操作中反向引用AST?,parsing,rewrite,antlr,abstract-syntax-tree,backreference,Parsing,Rewrite,Antlr,Abstract Syntax Tree,Backreference,我已经知道这个问题的解决方法,但我想真正使用这个方法,至少有一个原因——它应该可以工作 这条规则摘自特伦斯·帕尔(Terence Parr)的“最终ANTLR参考”(该书适用于ANTLR3): 如果INT后面没有+,则结果将是INT(单节点),如果是-,子树将以第一个INT(称为$expr)作为左分支构建 我想建立类似的规则,但需要自定义操作: mult_expr : (pow_expr -> pow_expr ) (op=MUL exr=pow_expr
INT
后面没有+
,则结果将是INT(单节点),如果是-,子树将以第一个INT
(称为$expr
)作为左分支构建
我想建立类似的规则,但需要自定义操作:
mult_expr : (pow_expr -> pow_expr )
(op=MUL exr=pow_expr
-> { new BinExpr($op,$mult_expr.tree,$exr.tree) })*;
ANTLR接受这样的规则,但当我使用输入(例如)“5*3”运行解析器时,它会给我一个错误“行1:1在'*'5'处缺少EOF”
问题:如何在自定义重写操作中使用反向引用?我建议您创建自己的
CommonTreeAdaptor
并将自定义节点的创建移动到此CommonTreeAdaptor
,而不是在语法文件中执行此操作。有关这方面的更多信息,请参阅:
对于可能具有多种含义的运算符,如减号(二进制或一元运算符),让解析器规则重写一元运算符,如下所示:
grammar X;
...
tokens { U_SUB; }
add_expr
: mult_expr ((SUB | ADD)^ mult_expr)*
;
...
unary_expr
: SUB atom -> ^(U_SUB atom)
| atom
;
...
@Override
public Object create(Token t) {
...
switch(t.getType()) {
case X.SUB : /* return a binary-tree */
...
case X.U_SUB : /* return an unary-tree */
}
...
}
mult_expr : (exl=pow_expr -> $exl )
((op=MUL|op=IDIV|op=RDIV|op=MOD) exr=pow_expr
-> { new BinaryExpression($op,$exl.tree,$exr.tree) })*;
然后在您的CommonTreeAdaptor
的实现中,执行以下操作:
grammar X;
...
tokens { U_SUB; }
add_expr
: mult_expr ((SUB | ADD)^ mult_expr)*
;
...
unary_expr
: SUB atom -> ^(U_SUB atom)
| atom
;
...
@Override
public Object create(Token t) {
...
switch(t.getType()) {
case X.SUB : /* return a binary-tree */
...
case X.U_SUB : /* return an unary-tree */
}
...
}
mult_expr : (exl=pow_expr -> $exl )
((op=MUL|op=IDIV|op=RDIV|op=MOD) exr=pow_expr
-> { new BinaryExpression($op,$exl.tree,$exr.tree) })*;
我是一个执着的人,一步到位使用自定义节点的想法让我很困扰…;-)
所以,我做到了。关键点是:
- 把
EOF代码>在“主”规则的末尾
- 标记令牌时,将标签放在令牌旁边,而不是分组,因此
,而不是(op='*'| op='/')
op=('*'|'/')
grammar X;
...
tokens { U_SUB; }
add_expr
: mult_expr ((SUB | ADD)^ mult_expr)*
;
...
unary_expr
: SUB atom -> ^(U_SUB atom)
| atom
;
...
@Override
public Object create(Token t) {
...
switch(t.getType()) {
case X.SUB : /* return a binary-tree */
...
case X.U_SUB : /* return an unary-tree */
}
...
}
mult_expr : (exl=pow_expr -> $exl )
((op=MUL|op=IDIV|op=RDIV|op=MOD) exr=pow_expr
-> { new BinaryExpression($op,$exl.tree,$exr.tree) })*;
@Bart Kiers,不在解析器语法中使用自定义操作,而是依赖于默认AST。然后编写树语法,并将整个AST重写为自定义语法。我非常想避免这种情况,因为这会使我的工作量加倍,而且我越是看到这种变通方法,我就越怀疑ANTLR的能力(它应该可以节省我的工作量;-D)。不确定这是否可能。。。但是,是否可以选择创建自己的
CommonTreeAdaptor
?(见:)@Bart Kiers,我不知道适配器如何改变这里的情况(顺便说一句,当你引入自定义AST时,你也必须引入自定义适配器,我做到了)。你已经有了自己的适配器了吗?那你为什么不在它的create(Token)
方法中创建自己节点类的实例,让你的规则就是:mult\u expr:pow\u expr(MUL^pow\u expr)*
?是的,这就是我的意思。在一元-
的情况下,只需将U_SUB
放在tokens{…}
块中,然后执行:1元^(U_SUB atom)| atom代码>。然后,适配器的create(…)
方法中出现的SUB
将是二进制的-
和U_SUB
一元的-
。我希望你不会因为我切换了“solution”标记而生气,但是我终于找到了问题的答案。直接的。不,当然不是!:)感谢您发布解决方案。虽然我不会使用您的解决方案(我认为),但知道它是可能的还是件好事。