Parsing 如何在自定义重写操作中反向引用AST?

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

我已经知道这个问题的解决方法,但我想真正使用这个方法,至少有一个原因——它应该可以工作

这条规则摘自特伦斯·帕尔(Terence Parr)的“最终ANTLR参考”(该书适用于ANTLR3):

如果
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”标记而生气,但是我终于找到了问题的答案。直接的。不,当然不是!:)感谢您发布解决方案。虽然我不会使用您的解决方案(我认为),但知道它是可能的还是件好事。