ANTLR中抽象语法树(AST)的生成和使用
直接在语法文件中嵌入求值处理代码的方式在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)如下:
- 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如下:
- 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.jpg
2. AST树语法,树的遍历
使用ANTLR的tree grammer可以完成对上面语法树的遍历,完成计算功能。
实例的语法文件ExprEval.g如下:
- 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如下:
- 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
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/163081.html