直接在语法文件中嵌入求值处理代码的方式在ANTLR中称为嵌入式动作。复杂情况下需要基于语法树遍历生成目标代码。前者语法复杂时使语法文件臃肿。另外,语法可能经常需要修改,但语法的主要表达式不会变动,将语法识别与转换、生成(目标代码)等处理分离是有好处的。
1. AST构造
使用全局option设置output=AST,ANTLR生成的识别器中每个方法都返回一个AST节点或节点集合,起始规则返回的是所有匹配到的AST节点的集合。AST节点集合都是一个链表结构。为了使识别器返回AST树,需要在语法文件中使用AST构造,告诉ANTLR在语法识别时如何构造树结构。
ANTLR在语法规则表达式后面添加后缀实现基于堆栈的递归算法一般采用的文本表达方式,如3+4*5表示成+(3(*4 5))。后缀^表示它前面的表达式为操作符,后缀!表示它前面的表达式不需要生成AST节点。
实例的语法文件ExprTree.g文件(原为C#,我改成了Java)如下:
view plain copy to clipboard print ? grammar ExprTree; options{ output=AST; ASTLabelType=CommonTree; language=Java; } prog: ( stat {System.out.println($stat.tree.toStringTree());} )+ ; stat: expr NEWLINE -> expr | ID '=' expr NEWLINE -> ^('=' ID expr) | NEWLINE -> ; expr: multExpr (('+' ^|'-' ^) multExpr)* ; multExpr: atom ('*' ^ atom)* ; atom: INT | ID | '(' ! expr ')' ! ; ID : ('a'..'z'|'A'..'Z')+ ; INT : '0'..'9'+ ; NEWLINE: (('/r'? '/n')|';')+ ; WS : (' '|'/t')+ { $channel = HIDDEN; } ;
grammar ExprTree;options{ output=AST; ASTLabelType=CommonTree; language=Java;}prog: ( stat {System.out.println($stat.tree.toStringTree());} )+ ;stat: expr NEWLINE -> expr | ID '=' expr NEWLINE -> ^('=' ID expr) | NEWLINE -> ;expr: multExpr (('+' ^|'-' ^) multExpr)* ;multExpr: atom ('*' ^ atom)* ;atom: INT | ID | '(' ! expr ')' ! ;ID : ('a'..'z'|'A'..'Z')+ ;INT : '0'..'9'+ ;NEWLINE: (('/r'? '/n')|';')+ ;WS : (' '|'/t')+ { $channel = HIDDEN; } ;
测试文件Test_0.java如下:
view plain copy to clipboard print ? import org.antlr.runtime.*; public class Test_0{ public static void main(String[] args)throws Exception { ANTLRInputStream input = new ANTLRInputStream(System.in); ExprTreeLexer lex = new ExprTreeLexer(input); CommonTokenStream tokens = new CommonTokenStream(lex); ExprTreeParser parser = new ExprTreeParser(tokens); try { parser.prog(); } catch (RecognitionException e) { e.printStackTrace(); } } }
import org.antlr.runtime.*;public class Test_0{public static void main(String[] args)throws Exception{ ANTLRInputStream input = new ANTLRInputStream(System.in); ExprTreeLexer lex = new ExprTreeLexer(input); CommonTokenStream tokens = new CommonTokenStream(lex); ExprTreeParser parser = new ExprTreeParser(tokens); try { parser.prog(); } catch (RecognitionException e) { e.printStackTrace(); }}}
2. AST树语法,树的遍历
使用ANTLR的tree grammer可以完成对上面语法树的遍历,完成计算功能。
实例的语法文件ExprEval.g如下:
view plain copy to clipboard print ? tree grammar ExprEval; options { tokenVocab=ExprTree; //指定符号表文件ExprTree.tokens ASTLabelType=CommonTree; language=Java; } @header {import java.util.HashMap;} @members { HashMap memory = new HashMap();} prog: stat+ ; stat: expr {System.out.println($expr.value);} | ^('=' ID expr) {memory.put($ID.text, $expr.value);} ; expr returns [int value] : ^('+' a=expr b=expr) {$value = a+b;} | ^('-' a=expr b=expr) {$value = a-b;} | ^('*' a=expr b=expr) {$value = a*b;} | ID { $value = 0; Integer v = (Integer)memory.get($ID.text); if ( v!=null ) $value = v.intValue(); else System.err.println("#ff0000 variable "+$ID.text); } | INT {$value = Integer.parseInt($INT.text);} ;
tree grammar ExprEval;options { tokenVocab=ExprTree; //指定符号表文件ExprTree.tokens ASTLabelType=CommonTree; language=Java;}@header {import java.util.HashMap;}@members { HashMap memory = new HashMap();}prog: stat+ ;stat: expr {System.out.println($expr.value);} | ^('=' ID expr) {memory.put($ID.text, $expr.value);} ;expr returns [int value] : ^('+' a=expr b=expr) {$value = a+b;} | ^('-' a=expr b=expr) {$value = a-b;} | ^('*' a=expr b=expr) {$value = a*b;} | ID { $value = 0; Integer v = (Integer)memory.get($ID.text); if ( v!=null ) $value = v.intValue(); else System.err.println("#ff0000 variable "+$ID.text); } | INT {$value = Integer.parseInt($INT.text);};
测试文件Test.java如下:
view plain copy to clipboard print ? import org.antlr.runtime.*; import org.antlr.runtime.tree.*; public class Test { public static void main(String[] args) throws Exception { ANTLRInputStream input = new ANTLRInputStream(System.in); ExprTreeLexer lex = new ExprTreeLexer(input); CommonTokenStream tokens = new CommonTokenStream(lex); ExprTreeParser parser = new ExprTreeParser(tokens); ExprTreeParser.prog_return r = parser.prog(); CommonTree tree = r.tree; CommonTreeNodeStream treeStream = new CommonTreeNodeStream(tree); ExprEval walker = new ExprEval(treeStream); try { walker.prog(); } catch (RecognitionException e) { e.printStackTrace(); } } }
import org.antlr.runtime.*;import org.antlr.runtime.tree.*;public class Test { public static void main(String[] args) throws Exception { ANTLRInputStream input = new ANTLRInputStream(System.in); ExprTreeLexer lex = new ExprTreeLexer(input); CommonTokenStream tokens = new CommonTokenStream(lex); ExprTreeParser parser = new ExprTreeParser(tokens); ExprTreeParser.prog_return r = parser.prog(); CommonTree tree = r.tree; CommonTreeNodeStream treeStream = new CommonTreeNodeStream(tree); ExprEval walker = new ExprEval(treeStream); try { walker.prog(); } catch (RecognitionException e) { e.printStackTrace(); }}}
测试结果:3.jpg
http://blog.csdn.net/bianzhiqi/archive/2009/09/29/4619079.aspx
测试结果:2.jpg
