ANTLR中抽象语法树(AST)的生成和使用

本文介绍了ANTLR中如何生成和使用抽象语法树(AST),包括通过设置全局option为AST来让ANTLR识别器返回AST节点,以及使用tree grammar进行树遍历来实现计算功能。示例展示了如何在语法文件中定义AST构造规则,并提供了测试代码来验证AST的正确性和功能。

ANTLR中抽象语法树(AST)的生成和使用

直接在语法文件中嵌入求值处理代码的方式在ANTLR中称为嵌入式动作。复杂情况下需要基于语法树遍历生成目标代码。前者语法复杂时使语法文件臃肿。另外,语法可能经常需要修改,但语法的主要表达式不会变动,将语法识别与转换、生成(目标代码)等处理分离是有好处的。

1. AST构造

使用全局option设置output=ASTANTLR生成的识别器中每个方法都返回一个AST节点或节点集合,起始规则返回的是所有匹配到的AST节点的集合。AST节点集合都是一个链表结构。为了使识别器返回AST树,需要在语法文件中使用AST构造,告诉ANTLR在语法识别时如何构造树结构。

ANTLR在语法规则表达式后面添加后缀实现基于堆栈的递归算法一般采用的文本表达方式,如3+4*5表示成+3*4 5))。后缀^表示它前面的表达式为操作符,后缀!表示它前面的表达式不需要生成AST节点。

 

实例的语法文件ExprTree.g文件(原为C#,我改成了Java)如下:

  1. grammar ExprTree;   
  2. options{   
  3.     output=AST;   
  4.     ASTLabelType=CommonTree;   
  5.     language=Java;   
  6. }   
  7.   
  8. prog: ( stat {System.out.println($stat.tree.toStringTree());} )+ ;   
  9. stat: expr NEWLINE -> expr   
  10.     | ID '=' expr NEWLINE -> ^('=' ID expr)   
  11.     | NEWLINE ->   
  12.     ;   
  13.   
  14. expr: multExpr (('+' ^|'-' ^) multExpr)* ;   
  15. multExpr: atom ('*' ^ atom)* ;   
  16. atom: INT   
  17.     | ID   
  18.     | '(' ! expr ')' !   
  19.     ;   
  20.   
  21. ID  :   ('a'..'z'|'A'..'Z')+ ;   
  22. INT :   '0'..'9'+ ;   
  23. NEWLINE: (('/r''/n')|';')+ ;   
  24. WS  :   (' '|'/t')+ { $channel = HIDDEN; } ;  

 

测试文件Test_0.java如下:

  1. import org.antlr.runtime.*;   
  2. public class Test_0{   
  3. public static void main(String[] args)throws Exception   
  4. {   
  5.     ANTLRInputStream input = new ANTLRInputStream(System.in);   
  6.     ExprTreeLexer lex = new ExprTreeLexer(input);   
  7.     CommonTokenStream tokens = new CommonTokenStream(lex);   
  8.     ExprTreeParser parser = new ExprTreeParser(tokens);   
  9.     try  
  10.     {   
  11.         parser.prog();   
  12.     }   
  13.     catch (RecognitionException e)   
  14.     {   
  15.         e.printStackTrace();   
  16.     }   
  17. }   
  18. }  

 

 

2. AST树语法,树的遍历

  使用ANTLRtree  grammer可以完成对上面语法树的遍历,完成计算功能。

  实例的语法文件ExprEval.g如下:

 

  1. tree grammar ExprEval;   
  2. options {   
  3.     tokenVocab=ExprTree; //指定符号表文件ExprTree.tokens   
  4.     ASTLabelType=CommonTree;   
  5.     language=Java;   
  6. }   
  7.   
  8. @header {import java.util.HashMap;}   
  9.   
  10. @members { HashMap memory = new HashMap();}   
  11.   
  12. prog: stat+ ;   
  13.   
  14. stat: expr {System.out.println($expr.value);}   
  15.     | ^('=' ID expr) {memory.put($ID.text, $expr.value);}   
  16.     ;   
  17.   
  18. expr returns [int value]   
  19.     : ^('+' a=expr b=expr) {$value = a+b;}   
  20.     | ^('-' a=expr b=expr) {$value = a-b;}   
  21.     | ^('*' a=expr b=expr) {$value = a*b;}   
  22.     | ID   
  23.     {   
  24.         $value = 0;   
  25.         Integer v = (Integer)memory.get($ID.text);   
  26.         if ( v!=null ) $value = v.intValue();   
  27.         else System.err.println("#ff0000 variable "+$ID.text);   
  28.            
  29.     }   
  30.     | INT {$value = Integer.parseInt($INT.text);}   
  31. ;  

 

 

测试文件Test.java如下:

  1. import org.antlr.runtime.*;   
  2. import org.antlr.runtime.tree.*;   
  3. public class Test {   
  4.     public static void main(String[] args) throws Exception {   
  5.    ANTLRInputStream input = new ANTLRInputStream(System.in);   
  6.    ExprTreeLexer lex = new ExprTreeLexer(input);   
  7.     CommonTokenStream tokens = new CommonTokenStream(lex);   
  8.     ExprTreeParser parser = new ExprTreeParser(tokens);   
  9.     ExprTreeParser.prog_return r = parser.prog();   
  10.     CommonTree tree = r.tree;   
  11.     CommonTreeNodeStream treeStream = new CommonTreeNodeStream(tree);   
  12.     ExprEval walker = new ExprEval(treeStream);   
  13.     try  
  14.     {   
  15.         walker.prog();   
  16.     }   
  17.     catch (RecognitionException e)   
  18.     {   
  19.         e.printStackTrace();   
  20.     }   
  21.   
  22. }   
  23. }  

 

 

测试结果:3.jpg

http://blog.youkuaiyun.com/bianzhiqi/archive/2009/09/29/4619079.aspx

测试结果:2.jpg

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值