如何在自定义重写操作中返回引用AST?

时间:2012-08-10 19:00:26

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

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

这是规则来自Terence Parr的“The Definitive ANTLR Reference”(这些书是针对ANTLR3的):

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

如果INT后面没有+,则结果将是INT(单个节点),如果是 - 子树将使用第一个INT构建(称为{{1} })作为左分支。

我想使用自定义操作构建类似的规则:

$expr

ANTLR接受这样的规则,但是当我使用输入(例如)“5 * 3”运行我的解析器时,它给出了一个错误“第1行:1行'E'缺少EOF”。

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

2 个答案:

答案 0 :(得分:1)

我建议您创建自己的CommonTreeAdaptor并将创建ow自定义节点移动到此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 */
  }
  ...
}

答案 1 :(得分:1)

我是一个坚持不懈的家伙,这个在一步中使用我的自定义节点的想法困扰着我......; - )

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

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

  • 标记代币时,将标记放在代币旁边,而不是分组,因此(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) })*;