ANTLR:从CommonTree到有用的对象图

我今天开始使用ANTLR,我已经创建了一个基本的解析器。

解析后,我最终得到了一棵树。 对我来说,似乎这只是一堆String在树节点的树结构中放在一起。 这对我来说不是很有用。 我想要一个对象图。

澄清(这是一个例子,而不是我的真实应用):对于"5-1+6"我似乎最终得到:

 new String("PLUS") new String("MINUS") new String("5") new String("1") new String("6") 

我会发现更有用的东西:

 new Plus( new Minus( new IntegerLiteral(5), new IntegerLiteral(1)), new IntegerLiteral(6)) 

从第一个表示到另一个表示最方便的方法是什么? 在本文中 ,作者做了类似的事情:

 public Expression createExpr(CommonTree ast) { // ... switch (ast.getType()) { case SimpleExpressionParser.INT: return new IntegerLiteral(ast.getText()) case SimpleExpressionParser.PLUS: return new Plus(createExpr((CommonTree)ast.getChild(0)), // recurse createExpr((CommonTree)ast.getChild(1))); // recurse case SimpleExpressionParser.MINUS: return new Minus(createExpr((CommonTree)ast.getChild(0)), // recurse createExpr((CommonTree)ast.getChild(1))); // recurse } // ... } 

这是首选方式吗?! 我不能指示ANTLR以某种方式生成这个样板代码(它会是巨大的)吗?


可能相关的问题:

  • 将Antlr语法树转换为有用的对象 (但我无法看到答案如何回答我的问题。)

这是一种可能的方式。 简而言之,这些是您要执行的步骤:

  1. 创建一个组合语法,生成词法分析器和解析器;
  2. 在(1)语法中混合AST重写规则,将令牌的平面列表转换为适当的树;
  3. 写一个可以从(2)走树的树语法;
  4. 在树步行者中混合自定义代码;
  5. 测试一下。

1

让我们创建一个支持+-*/(...)和数字的小型表达式解析器,它们看起来像:

 grammar Exp; // file: Exp.g eval : exp EOF ; exp : addExp ; addExp : mulExp ((Add | Sub) mulExp)* ; mulExp : unaryExp ((Mul | Div) unaryExp)* ; unaryExp : Sub atom | atom ; atom : Number | '(' exp ')' ; Add : '+'; Sub : '-'; Mul : '*'; Div : '/'; Number : '0'..'9'+; Space : ' ' {skip();}; 

2

包括重写规则,它将如下所示:

 grammar Exp; // file: Exp.g options { output=AST; } tokens { U_SUB; } eval : exp EOF -> exp ; exp : addExp ; addExp : mulExp ((Add | Sub)^ mulExp)* ; mulExp : unaryExp ((Mul | Div)^ unaryExp)* ; unaryExp : Sub atom -> ^(U_SUB atom) | atom ; atom : Number | '(' exp ')' -> exp ; Add : '+'; Sub : '-'; Mul : '*'; Div : '/'; Number : '0'..'9'+; Space : ' ' {skip();}; 

现在,像10 - 2 * (3 + 8)这样的表达式将转换为:

在此处输入图像描述

3

要创建一个为(2)中生成的AST生成迭代器的树语法,你可以这样做:

 tree grammar ExpWalker; // file: ExpWalker.g options { tokenVocab=Exp; // use the tokens from Exp.g ASTLabelType=CommonTree; } eval : exp ; exp : ^(Add exp exp) | ^(Sub exp exp) | ^(Mul exp exp) | ^(Div exp exp) | ^(U_SUB exp) | Number ; 

4

并在此树迭代器中混合您的自定义类,执行以下操作:

 tree grammar ExpWalker; // file: ExpWalker.g options { tokenVocab=Exp; // use the tokens from Exp.g ASTLabelType=CommonTree; } eval returns [ExpNode e] : exp {e = $exp.e;} ; exp returns [ExpNode e] : ^(Add a=exp b=exp) {e = new AddExp($ae, $be);} | ^(Sub a=exp b=exp) {e = new SubExp($ae, $be);} | ^(Mul a=exp b=exp) {e = new MulExp($ae, $be);} | ^(Div a=exp b=exp) {e = new DivExp($ae, $be);} | ^(U_SUB a=exp) {e = new UnaryExp($ae);} | Number {e = new NumberExp($Number.text);} ; 

这里有一些代码来测试所有类(只将它们放在一个文件中: Main.java ):

 import org.antlr.runtime.*; import org.antlr.runtime.tree.*; import org.antlr.stringtemplate.*; public class Main { public static void main(String[] args) throws Exception { String source = "10 - 2 * (3 + 8)"; ExpLexer lexer = new ExpLexer(new ANTLRStringStream(source)); CommonTokenStream tokens = new CommonTokenStream(lexer); ExpParser parser = new ExpParser(tokens); ExpParser.eval_return returnValue = parser.eval(); CommonTree tree = (CommonTree)returnValue.getTree(); CommonTreeNodeStream nodes = new CommonTreeNodeStream(tree); ExpWalker walker = new ExpWalker(nodes); ExpNode root = walker.eval(); System.out.println(source + " = " + root.evaluate()); } } interface ExpNode { double evaluate(); } class NumberExp implements ExpNode { final double num; NumberExp(String s) { num = Double.parseDouble(s); } @Override public double evaluate() { return num; } } class AddExp implements ExpNode { final ExpNode left, right; AddExp(ExpNode a, ExpNode b) { left = a; right = b; } @Override public double evaluate() { return left.evaluate() + right.evaluate(); } } class SubExp implements ExpNode { final ExpNode left, right; SubExp(ExpNode a, ExpNode b) { left = a; right = b; } @Override public double evaluate() { return left.evaluate() - right.evaluate(); } } class MulExp implements ExpNode { final ExpNode left, right; MulExp(ExpNode a, ExpNode b) { left = a; right = b; } @Override public double evaluate() { return left.evaluate() * right.evaluate(); } } class DivExp implements ExpNode { final ExpNode left, right; DivExp(ExpNode a, ExpNode b) { left = a; right = b; } @Override public double evaluate() { return left.evaluate() / right.evaluate(); } } class UnaryExp implements ExpNode { final ExpNode exp; UnaryExp(ExpNode e) { exp = e; } @Override public double evaluate() { return -exp.evaluate(); } } 

然后做:

 #生成词法分析器和解析器
 java -cp antlr-3.2.jar org.antlr.Tool Exp.g

 #生成树步行者
 java -cp antlr-3.2.jar org.antlr.Tool ExpWalker.g

 #编译一切
 javac -cp antlr-3.2.jar * .java

 #运行主类
 java -cp。:antlr-3.2.jar Main#* nix 
 java -cp .; antlr-3.2.jar主要#Windows

打印:

 10  -  2 *(3 + 8)=  -  12.0

您可以跳过树行走者并混合所有代码并在组合语法中returns [...] ,但IMO,树语法使事情更有序,因为词法分析器规则,以及像()等标记从中移除它。

HTH