通过抽象语法树(AST)求值
现在你已经看到了基本的语法指导的翻译/计算,在此文法/语法指示了什么时候去执行动作。一个更为强大的策略是创建一个中介表达,它拥有所有或绝大部分的输入符号,并在数据的结构中,将这些记号之间的关系编码。比如:输入“3+4”会被表达成如下所示的AST:
+
/
3 4
对这种类型的树,你会使用一个树遍历程序(由ANTLR从树形语法生成)计算出和前面一样的值,但是却使用了不同的策略。
为了确定计算什么,或进行语法树的重写,或者向另一种语言进行树变形,你必须对语法树进行多重遍历。这时,AST的效果会变得清晰。
AST构造
对许多语法,让ANTLR生成一个有用的AST是相当简单的。在我们这里,打开buildAST选项并且添加少许后缀操作符来告诉ANTLR那些记号应该是子树的根。
class ExprParser extends Parser;
options {
buildAST=true;
}
expr: mexpr ((PLUS^|MINUS^) mexpr)*
;
mexpr
: atom (STAR^ atom)*
;
atom: INT
| LPAREN! expr RPAREN!
;
同样,lexer不变。主程序询问目标语法树,并将其打印:
import antlr.*;
import antlr.collections.*;
public class Main {
public static void main(String[] args) throws Exception {
ExprLexer lexer = new ExprLexer(System.in);
ExprParser parser = new ExprParser(lexer);
parser.expr();
AST t = parser.getAST();
System.out.println(t.toStringTree());
}
}
$ java Main
3+4
( + 3 4 )
$ java Main
3+4*5
( + 3 ( * 4 5 ) )
$ java Main
(3+4)*5
( * ( + 3 4 ) 5 )
$
AST解析和求值
有上面的parser创建的语法树非常简单。语法树解析程序中的单一规则就足够了。
class ExprTreeParser extends TreeParser;
options {
importVocab=ExprParser;
}
expr returns [int r=0]
{ int a,b; }
: #(PLUS a=expr b=expr) {r = a+b;}
| #(MINUS a=expr b=expr) {r = a-b;}
| #(STAR a=expr b=expr) {r = a*b;}
| i:INT {r = (int)Integer.parseInt(i.getText());}
;
主程序则修改为使用新的语法树parser来求值:
import antlr.*;
import antlr.collections.*;
public class Main {
public static void main(String[] args) throws Exception {
ExprLexer lexer = new ExprLexer(System.in);
ExprParser parser = new ExprParser(lexer);
parser.expr();
AST t = parser.getAST();
System.out.println(t.toStringTree());
ExprTreeParser treeParser = new ExprTreeParser();
int x = treeParser.expr(t);
System.out.println(x);
}
}
现在你得到了树形结构和结果值。
$ java Main
3+4
( + 3 4 )
7
$ java Main
3+(4*5)+10
( + ( + 3 ( * 4 5 ) ) 10 )
33
$