什么是添加新标记(重写)以创建不在输入流上的AST节点的正确方法

时间:2011-06-15 15:53:32

标签: antlr antlr3

我在这里为ANTLR提供了一个非常基本的数学表达式语法,感兴趣的是在括号之间处理隐含的*运算符,例如: (2-3)(4+5)(6*7)实际应该是(2-3)*(4+5)*(6*7)

鉴于输入(2-3)(4+5)(6*7)我正在尝试在解析时将缺少的*运算符添加到AST树中,在下面的语法中我认为我已经设法实现了这一点,但我想知道如果这是正确的,最优雅的方式?

grammar G; 

options {
    language = Java;
    output=AST;
ASTLabelType=CommonTree;
}

tokens {
  ADD = '+' ;
  SUB = '-' ;
  MUL = '*' ;
  DIV = '/' ;
  OPARN = '(' ;
  CPARN = ')' ;
}

start
    : expression EOF!
    ;

expression
    : mult (( ADD^ | SUB^ ) mult)*
    ;

mult
   : atom (( MUL^ | DIV^) atom)*    
   ;

atom
   : INTEGER
   | (
       OPARN  expression CPARN -> expression
     )

     (
       OPARN  expression CPARN -> ^(MUL expression)+
     )*  
   ;


INTEGER : ('0'..'9')+ ;
WS  : (' ' | '\t' | '\n' | '\r' | '\f')+ {$channel = HIDDEN;};

这个语法似乎在ANTLRworks中输出正确的AST树:

AST Output

我只是刚刚开始掌握解析和ANTLR,没有太多经验所以反馈真的很感激!

提前致谢!卡尔

1 个答案:

答案 0 :(得分:3)

首先,鉴于你以前从未使用过ANTLR,你做得很好。

您可以省略language=JavaASTLabelType=CommonTree,这是默认值。所以你可以这样做:

options {
  output=AST;
}

此外,您不必单独为每个运算符指定根节点。所以你不必这样做:

(ADD^ | SUB^)

但以下内容:

(ADD | SUB)^

就足够了。只有两个运算符,没有太大区别,但在实现关系运算符(>=<=><)时,后者更容易一些。

现在,对你来说AST:你可能想要创建一个二叉树:这样,所有内部节点都是运算符,而叶子将是操作数,这使得对表达式的实际评估更加容易。要获取二叉树,您必须稍微更改atom规则:

atom
   : INTEGER
   | (
       OPARN  expression CPARN -> expression
     )
     (
       OPARN  e=expression CPARN -> ^(MUL $atom $e)
     )*  
   ;

在输入"(2-3)(4+5)(6*7)"

的情况下生成以下AST

enter image description here

(由graphviz-dev.appspot.com生成的图片)

使用以下测试类生成DOT文件:

import org.antlr.runtime.*;
import org.antlr.runtime.tree.*;
import org.antlr.stringtemplate.*;

public class Main {
  public static void main(String[] args) throws Exception {
    GLexer lexer = new GLexer(new ANTLRStringStream("(2-3)(4+5)(6*7)"));
    GParser parser = new GParser(new CommonTokenStream(lexer));
    CommonTree tree = (CommonTree)parser.start().getTree();
    DOTTreeGenerator gen = new DOTTreeGenerator();
    StringTemplate st = gen.toDOT(tree);
    System.out.println(st);
  }
}