如何从这个AST树中删除无用的节点?

时间:2012-11-16 03:27:12

标签: antlr antlr3

我已经看过这个question,尽管问题标题看起来是一样的;它没有回答我的问题,至少不是我能理解的任何方式。

解析数学

以下是我正在解析的内容:

PI -> 3.14.
Number area(Number radius) -> PI * radius^2.

这就是我希望我的AST树看起来的方式,减去所有无用的根节点。

how it should look http://vertigrated.com/images/How%20I%20want%20the%20tree%20to%20look.png

我希望这是我语法的相关片段:

term : '(' expression ')'
     | number -> ^(NUMBER number)
     | (function_invocation)=> function_invocation 
     | ATOM
     | ID
     ;

power : term ('^' term)* -> ^(POWER term (term)* ) ;
unary : ('+'! | '-'^)* power ;
multiply : unary ('*' unary)* -> ^(MULTIPLY unary (unary)* ) ;
divide : multiply ('/' multiply)* -> ^(DIVIDE multiply (multiply)* );
modulo : divide ('%' divide)* -> ^(MODULO divide (divide)*) ;
subtract : modulo ('-' modulo)* -> ^(SUBTRACT modulo (modulo)* ) ;  
add : subtract ('+' subtract)* -> ^(ADDITION subtract (subtract)*) ;

relation : add (('=' | '!=' | '<' | '<=' | '>=' | '>') add)* ;

expression : relation (and_or relation)*
           | string  
           | container_access
           ;
and_or : '&' | '|' ;

优先级

我仍然希望保留precedence,如下图所示,但是如果可能的话,想要消除无用的节点。

资料来源:Number a(x) -> 0 - 1 + 2 * 3 / 4 % 5 ^ 6.

以下是我想要消除的节点:

how I want the precedence tree to look http://vertigrated.com/images/example%202%20desired%20result.png

基本上我想要消除任何那些没有直接在其下面有分支的节点到二进制选项。

4 个答案:

答案 0 :(得分:2)

你的规则(和其他类似的)

 add : subtract ('+' subtract)* -> ^(ADDITION subtract (subtract)*) ;
当你没有一系列添加操作时,

会产生无用的生产。

我不是ANTLR专家,但我猜您需要两个案例,一个用于添加术语 这是一元的,一个是一组孩子,第一个生成你的孩子 标准树,第二个简单地将子树传递给父树, 没有创建新节点?

add : subtract ( ('+' subtract)+ -> ^(ADDITION subtract (subtract)*) 
               | -> subtract ) ;

对于具有操作符操作数序列的其他规则的类似更改。

答案 1 :(得分:2)

你必须意识到这两条规则:

add : sub ( ('+' sub)+ -> ^(ADD sub (sub)*) | -> sub ) ;

add : sub ('+'^ sub)* ;

执行生成相同的AST。给定输入1+2+3,第一个规则将产生:

  ADD
   |
.--+--.
|  |  |
1  2  3

第二条规则产生的地方:

     (+)
      |
   .--+--.  
   |     |
  (+)    3
   |
.--+--.
|     |
1     2

后者更有意义:中缀表达式应该有2个子节点,而不是更多。

为什么不简单地删除解析器规则中的文字,只需执行:

add : sub (ADD^ sub)*;

ADD : '+';

使用重写规则创建相同的AST将如下所示:

add : (sub -> sub) ('+' s=sub -> ^(ADD $add $s))*;

另请参阅The Definitive ANTLR Reference中的第7章:树构造。特别是段落重写规则(第173页)和在重写规则中引用先前规则AST (第174/175页)。

答案 2 :(得分:0)

尽管我接受了Barts的答案是正确的,但我想用我为完整性而工作的示例代码发布我自己的完整答案。

以下是我根据巴特的答案所做的事情:

unary : ('+'! | '-'^)? term ;
pow : (unary -> unary) ('^' s=unary -> ^(POWER $pow $s))*;
mod : (pow -> pow) ('%' s=pow -> ^(MODULO $mod $s))*;
mult : (mod -> mod) ('*' s=mod -> ^(MULTIPLY $mult $s))*;
div : (mult -> mult) ('/' s=mult -> ^(DIVIDE $div $s))*;
sub : (div -> div) ('-' s=div -> ^(SUBTRACT $sub $s))*;
add : (sub -> sub) ('+' s=sub -> ^(ADD $add $s))*;

以下是生成的树的样子:

working answer http://vertigrated.com/images/working_answer.png

有一种替代解决方案可以不使用重写并将符号本身提升为根,但是如果可能的话,我希望树中的所有描述性标签。 我只是在讨论如何表示树,以便我的树行走代码尽可能干净!

power : unary ('^'^ unary)* ;
mod : power ('%'^ power)* ;
mult : mod ('*'^ mod)* ;
div : mult ('/'^ mult)* ;
sub : div ('-'^ div)* ;
add : sub ('+'^ sub)* ;

这看起来像这样:

without rewrites http://vertigrated.com/images/without_the_rewrites.png

答案 3 :(得分:0)

要摆脱不相关的节点,只需明确:

 subtract
    :
    modulo
    ( 
       ( '-' modulo)+  -> ^(SUBTRACT modulo+) // no need for parenthesis or asterisk
       |
      () -> modulo
    )
    ;