【问题标题】:How to back reference AST in custom rewrite action?如何在自定义重写操作中反向引用 AST?
【发布时间】:2012-08-23 00:28:17
【问题描述】:

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

这条规则取自 Terence Parr 的“The Definitive ANTLR Reference”(本书适用于 ANTLR3):

expr : (INT -> INT) ('+' i=INT -> ^('+' $expr $i) )*;

如果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”运行解析器时,它会给我一个错误“line 1:1 missing EOF at '*'5”。

问题:如何在自定义重写操作中使用反向引用?

【问题讨论】:

  • @Bart Kiers,不要在解析器语法中使用自定义操作,而是依赖默认的 AST。然后编写树语法,并将整个 AST 重写为自定义的。我希望非常避免这种情况,因为它使我的工作加倍,而且我看到的这种变通方法越多,我就越怀疑 ANTLR 的能力(它应该可以节省我的工作;-D)。跨度>
  • 不确定这是否可能...但是,可以选择创建自己的CommonTreeAdaptor 吗? (见:stackoverflow.com/questions/7635729/extend-antlr3-asts
  • @Bart Kiers,我看不出适配器如何改变这里的东西(顺便说一句。当你引入自定义 AST 时,你也必须引入自定义适配器,我做到了)。
  • 您已经拥有自己的适配器了吗?那你为什么不在 create(Token) 方法中创建你自己的节点类的实例,让你的规则是:mult_expr : pow_expr (MUL^ pow_expr)*
  • 是的,我就是这个意思。如果是一元-,只需将U_SUB 放入您的tokens { ... } 块并执行:unary_expr : SUB atom -> ^(U_SUB atom) | atom;。那么在适配器的create(...) 方法中出现的任何SUB 都将是二进制-U_SUB 一元-

标签: parsing rewrite antlr abstract-syntax-tree backreference


【解决方案1】:

我建议创建您自己的CommonTreeAdaptor 并将创建的自定义节点移动到此CommonTreeAdaptor,而不是在您的语法文件中执行此操作。有关这方面的更多信息,请参阅:Extend ANTLR3 AST's

如果运算符可能具有多种含义,例如减号(二元或一元运算符),请让您的解析器规则像这样重写一元运算符:

grammar X;

...

tokens { U_SUB; } 

add_expr
 : mult_expr ((SUB | ADD)^ mult_expr)*
 ;

...

unary_expr
 : SUB atom -> ^(U_SUB atom)
 | atom
 ;

...

然后在你的CommonTreeAdaptor 的实现中,做这样的事情:

@Override
public Object create(Token t) {
  ...
  switch(t.getType()) {
    case X.SUB   : /* return a binary-tree */
    ...
    case X.U_SUB : /* return an unary-tree */
  }
  ...
}

【讨论】:

  • 我希望你不会因为我切换“解决方案”标记而生气,但我终于找到了我的问题的答案。一个直接的。
【解决方案2】:

我是一个坚持不懈的人,一步使用自定义节点的想法让我很困扰...... ;-)

所以,我做到了。关键点是:

  • EOF! 放在“main”规则的末尾,

  • 标记令牌时,将标签放在令牌旁边,而不是分组,所以(op='*'|op='/'),而不是op=('*'|'/')

我不确定这种使用语法规则来立即创建自定义节点的方法是否是一个好主意,但由于这解决了所提出的问题,我将其标记为解决方案。

为了记录,最有趣的规则现在看起来像这样:

mult_expr : (exl=pow_expr -> $exl ) 
        ((op=MUL|op=IDIV|op=RDIV|op=MOD) exr=pow_expr 
        -> { new BinaryExpression($op,$exl.tree,$exr.tree) })*; 

【讨论】:

  • 不,当然不是! :) 感谢您发布解决方案。虽然我不会使用您的解决方案(我认为),但很高兴知道这是可能的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-12
  • 1970-01-01
相关资源
最近更新 更多