ANTLR树分析器
                本章翻译人 CowNew开源团队 周晓

曾经的SORCERER

在ANTLR 2.xx版本中,只要增加一些树操作符,就可以帮助你建立一种中间形式的树结构(抽象语法树) 来重写语法规则和语义动作。ANTLR同样允许你去指定AST树的文法结构,因此,可以通过操作或简单遍历树结点的方式来进行文法翻译。
以前,树分析器用一个单独的工具SORCERER来生成,但是ANTLR已经取代了它的功能。ANTLR现在可以为字符流,记号流,以及树节点来建立识别器。

什么是树分析器?

可以分析什么类型的树?

ANTLR树分析器可以遍历实现了AST接口的任何树。AST接口是一种基于类似儿子-兄弟结点的树通用结构,有如下重要的制导方法:

  • getFirstChild: 返回第一个子结点的引用.
  • getNextSibling: 返回下一个兄弟结点的引用.
每一个AST结点有一个子女列表,一些文本和一个"记号类型"。每个树的结点都是一棵树,因此我们说树是自相似的。AST接口的完整定义如下:
/** 最小AST结点接口用于ANTLR的AST成生
*   和树遍历
*/
public interface AST {
/** 添加一个子结点到最右边 */
public void addChild(AST c);
public boolean equals(AST t);
public boolean equalsList(AST t);
public boolean equalsListPartial(AST t);
public boolean equalsTree(AST t);
public boolean equalsTreePartial(AST t);
public ASTEnumeration findAll(AST tree);
public ASTEnumeration findAllPartial(AST subtree);
/** 得到第一个子结点; 如果没有子结点则返回null */
public AST getFirstChild();
/** 得到本结点的下一个兄弟结点 */
public AST getNextSibling();
/** 得到本结点的记号文本 */
public String getText();
/** 得到本结点的记号类型 */
public int getType();
/** 得到本结点的子结点总数; 如果是叶子结点, 返回0 */
public int getNumberOfChildren();
public void initialize(int t, String txt);
public void initialize(AST t);
public void initialize(Token t);
/** 设置第一个子结点. */
public void setFirstChild(AST c);
/** 设置下一个兄弟结点. */
public void setNextSibling(AST n);
/** 设置本结点的记号文本 */
public void setText(String text);
/** 设置本结点的记号类型 */
public void setType(int ttype);
public String toString();
public String toStringList();
public String toStringTree();
}

树的语法规则

规则: 可选产生式1
|   可选产生式2
...
|   可选产生式n
;
每一个可选的产生式都是由一个元素列表所组成的,列表中的元素是加入了树模式的ANTLR正规语法中的一个,有如下的形式:
#( 根结点 子结点1 子结点2 ... 子结点n )
例如:下列的树模式匹配一个以PLUS为根结点,并有两个INT子结点简单树结构:
#( PLUS INT INT )
树模式的根必须是一个记号引用,但是子结点元素不限于此,它甚至可以是子规则。例如,一种常见结构是if-then-else树结构,其中的else子句声明子树是可选的:
#( IF expr stat (stat)? )    
值得一提的是,当指定树模式和树语法后,通常,会进行满足条件的匹配而不是精确的匹配。一旦树满足给定的模式,不管剩下多少没有分析,都会报告一次匹配。例如,#( A B ),对于像#( A #(B C) D)这样有相同结构的树,不管有多长,都会报告一次匹配。

句法断言

expr:   ( #(MINUS expr expr) )=> #( MINUS expr expr )
|   #( MINUS expr )
...
;
赋值的次序很重要,因为第二个可选产生式是第一个可选产生式的“子集”.

语义断言

一个树遍历器的例子

我们的识别器, CalcParser, 通过如下的代码来定义:
class CalcParser extends Parser;
options {
buildAST = true;   // // 默认使用 CommonAST
}
expr:   mexpr (PLUS^ mexpr)* SEMI!
;
mexpr
:   atom (STAR^ atom)*
;
atom:   INT
;
PLUSSTAR记号是操作符,因此把它们作为子树的根结点,在它们后面注释上字符'^'。SEMI记号后缀有字符'!',这指出了它不应该被加入到树中。
这个计算器的词法分析定义如下:
class CalcLexer extends Lexer;
WS  :   (' '
|   '\t'
|   '\n'
|   '\r')
{ _ttype = Token.SKIP; }
;
LPAREN: '('
;
RPAREN: ')'
;
STAR:   '*'
;
PLUS:   '+'
;
SEMI:   ';'
;
INT :   ('0'..'9')+
;    
识别器生成的树是一棵简单的表达式树。例如,输入"3*4+5"所产生的树的形式为#( + ( * 3 4 ) 5 )。为了给这种形式的树建立树遍历器,你必须要为ANTLR递归的描述树的结构:
class CalcTreeWalker extends TreeParser;
expr    :   #(PLUS expr expr)
|   #(STAR expr expr)
|   INT
;
一旦指定结构,就可以自由的嵌入语义动作去计算出结果。一个简单的实现办法就是使expr规则返回一个整型的值,然后使每一条可选产生式计算每个子树的值。下面的树文法和动作达到了我们期望的效果:
class CalcTreeWalker extends TreeParser;
expr returns [int r]
{
int a,b;
r=0;
}
:   #(PLUS a=expr b=expr) {r = a+b;}
|   #(STAR a=expr b=expr) {r = a*b;}
|   i:INT             {r = Integer.parseInt(i.getText());}
;    
注意到当计算表达式值得时候,没有必要指定优先级,因为它已经隐含在树的结构中了。这也解释了为什么在以中间树形式表示的时候,要比它的输入要多很多。输入的符号确实作为结点储存在树结构中,而且这种结构隐含了结点之间的关系。
要想执行分析器和树遍历器,还需要以下的代码:
import java.io.*;
import antlr.CommonAST;
import antlr.collections.AST;
class Calc {
public static void main(String[] args) {
try {
CalcLexer lexer =
new CalcLexer(new DataInputStream(System.in));
CalcParser parser = new CalcParser(lexer);
// 分析输入的表达式
parser.expr();
CommonAST t = (CommonAST)parser.getAST();
// 以LISP符号的形式输出树
System.out.println(t.toStringList());
CalcTreeWalker walker = new CalcTreeWalker();
// 遍历由分析器建立的树
int r = walker.expr(t);
System.out.println("value is "+r);
} catch(Exception e) {
System.err.println("exception: "+e);
}
}
}    

翻译

树分析器对检查树或者从一棵树产生输出来说是很有用的,但必须要为它们添加处理树转换的代码。就像正则分析器一样,ANTLR树分析器支持buildAST选项,这类似于SORCERER的翻译模式。程序员不去修改代码,树分析器自动把输入树拷贝到作为结果的树。每一个规则都隐含(自动定义的)一个结果树。通过getAST 方法,我们可以从树分析器中获得此树的开始符号。如果要一些可选产生式和文法元素不被自动添加到输入的树上,它们后面要注释上"!"。子树可以被部分的或者全部重写。
嵌入到规则中的语义动作可以根据测试和树结构来对结果树进行设置。参考文法动作翻译章节.

一个树翻译的例子

再来看一下上面提到的简单计算器的例子,我们可以执行树翻译来代替计算表达式的值。下面树文法的动作优化了加法的恒等运算(加0)。
class CalcTreeWalker extends TreeParser;
options{
buildAST = true;   // "翻译"模式
}
expr:!  #(PLUS left:expr right:expr)
// '!'关闭自动翻译
{
// x+0 = x
if ( #right.getType()==INT &&
Integer.parseInt(#right.getText())==0 )
{
#expr = #left;
}
// 0+x = x
else if ( #left.getType()==INT &&
Integer.parseInt(#left.getText())==0 )
{
#expr = #right;
}
// x+y
else {
#expr = #(PLUS, left, right);
}
}
|   #(STAR expr expr)  // 使用自动翻译
|   i:INT
;    
执行分析器和树翻译器的代码如下:
import java.io.*;
import antlr.CommonAST;
import antlr.collections.AST;
class Calc {
public static void main(String[] args) {
try {
CalcLexer lexer =
new CalcLexer(new DataInputStream(System.in));
CalcParser parser = new CalcParser(lexer);
// 分析输入的表达式
parser.expr();
CommonAST t = (CommonAST)parser.getAST();
// 以LISP符号的形式输出树
System.out.println(t.toLispString());
CalcTreeWalker walker = new CalcTreeWalker();
// 遍历由分析器建立的树
walker.expr(t);
// 遍历,并得到结果
t = (CommonAST)walker.getAST();
System.out.println(t.toLispString());
} catch(Exception e) {
System.err.println("exception: "+e);
}
}
}
}

检查/调试AST

当开发树分析器的时候,经常会得到分析错误。不幸的是,你的树通常异乎寻常的大,使得很难去确定AST结构错误到底在哪里。针对这种情况(当创建Java树分析器的时候,我发现它非常有用),,我创建了一个ASTFrame类(一个JFrame),这样,你就可以用Swing树视图来查看你的AST。它没有拷贝这棵树,而是用了一个TreeModel。以应用程序方式运行antlr.debug.misc.ASTFrame去或者看看Java代码Main.java。就像不确定如何去调试一样,我不确定它们在相同的包下,总之,将会在以后的ANTLR版本中给出。这里有一个简单的使用例子:
public static void main(String args[]) {
// 创建树结点
ASTFactory factory = new ASTFactory();
CommonAST r = (CommonAST)factory.create(0, "ROOT");
r.addChild((CommonAST)factory.create(0, "C1"));
r.addChild((CommonAST)factory.create(0, "C2"));
r.addChild((CommonAST)factory.create(0, "C3"));
ASTFrame frame = new ASTFrame("AST JTree Example", r);
frame.setVisible(true);
}

©著作权归作者所有:来自51CTO博客作者cownew的原创作品,如需转载,请与作者联系,否则将追究法律责任
职场 休闲 Antlr

1

分享

微博 QQ 微信

收藏

上一篇:《J2EE开发全程实录》第一次印... 下一篇:[转]db2的SQL堆
cownew

293篇文章,51W+人气,0粉丝

转载于:https://blog.51cto.com/cownew/86701

Antlr中文文档初稿2(《ANTLR树分析器》)相关推荐

  1. 使用编译器——Solidity中文文档(8)

    写在前面:HiBlock区块链社区成立了翻译小组,翻译区块链相关的技术文档及资料,本文为Solidity文档翻译的第八部分<使用编译器>,特发布出来邀请solidity爱好者.开发者做公开 ...

  2. Hyperopt官方中文文档导读

    在2017年的圣诞节前,我翻译了有关HyperOpt的中文文档,这也时填补了空白,以此作为献给所有中国程序员,以及所有其他机器学习相关行业人员的圣诞礼物.圣诞快乐,各位. 更新 Hyperopt官方文 ...

  3. Hyperopt中文文档导读

    在2017年的圣诞节前,我翻译了有关HyperOpt的中文文档,这也时填补了空白,以此作为献给所有中国程序员,以及所有其他机器学习相关行业人员的圣诞礼物.圣诞快乐,各位. HyperOpt中文文档导读 ...

  4. semantic ui中文文档_Vuetify-广受欢迎的Material风格的开源UI框架

    全世界范围内广受欢迎的 Vue UI 框架,一个非常精致的 Material Design UI 套件. Material Design 风格 UI 框架 Vuetify 是一个基于 Vue.js 精 ...

  5. Chrome 开发者工具官方中文文档

    传送门 Chrome开发者工具官方中文文档 Chrome开发者工具详解 前端学习front-end-study系列 Elements面板(https://zhuanlan.zhihu.com/p/24 ...

  6. React-Redux 中文文档

    介绍 快速开始 React-Redux是Redux的官方React绑定库.它能够使你的React组件从Redux store中读取数据,并且向store分发actions以更新数据 安装 在你的Rea ...

  7. React-bootstrap 中文文档

    翻译了 React-bootstrap 中文文档,参考 react.tgwoo.com. React-Bootstrap 是可重用的前端组件库.与 Twitter Bootstrap 一致外观与感受, ...

  8. Beetl2.7.13 中文文档

    ## Beetl2.7.13 中文文档 ### 1. 什么是Beetl Beetl目前版本是2.7.13,相对于其他java模板引擎,具有功能齐全,语法直观,性能超高,以及编写的模板容易维护等特点.使 ...

  9. BeautifulSoup4 模块中文文档

    原文出处 -> Beautiful Soup Documentation 目录 一.前言 1.1 寻求帮助 二.快速开始 三.安装 BeautifulSoup 3.1 安装完成后的问题 3.2 ...

最新文章

  1. mockito_使用Mockito模拟自动装配的字段
  2. stcisp一直检测单片机_三种方法对比:STC51单片机实现免冷启动
  3. PowerShell_零基础自学课程_8_高级主题:WMI对象和COM组件
  4. Java学习 第三章 数组(三)排序算法
  5. 软件测试(一)-黑盒测试 随机测试技巧
  6. 学web前端有什么计划?
  7. 从业余挖洞到微软漏洞研究员,我的遗憾、惊喜和建议
  8. win11系统安装打印机的方法
  9. MacFree ePlicy Orchestrator
  10. 19 个接私活平台汇总升级版,你有技术就有钱
  11. Gradle化的Android Gallery3D App
  12. iOSButton文字和图片摆放的个字方式
  13. SCA声明办理 TSCA限用物质检测 TSCA有毒物质控制法案检测
  14. Google advertiser api开发概述——批量处理
  15. Hadoop之HDFS01【介绍】,字节跳动解决方案架构师
  16. 文艺范儿的程序猿和攻城狮们
  17. 段永平-雪球专刊·段永平投资问答录(上册 商业逻辑篇)(二)
  18. 龙珠 超宇宙 [Dragon Ball Xenoverse]
  19. java开发crm框架_这可能是2020年度最完整、详细的Java高级框架+CRM课程哟,小白看完直呼过瘾!...
  20. 设计模式六大原则(1):单一职责原则

热门文章

  1. hive 取消打印日志信息_Hive及其相关大数据组件
  2. php get 分页,PHP_codeigniter实现get分页的方法,本文实例讲述了codeigniter实现ge - phpStudy...
  3. java搭建线程池框架_JAVA线程池经管及分布式HADOOP调剂框架搭建
  4. mysql 版本太高_mysql创建新库以及解决版本过高8.0以上导致navicat无法连接的问题...
  5. matlab模拟砂轮形貌,基于多颗磨粒随机分布的虚拟砂轮建模及磨削力预测
  6. java中userservice是什么,【图片】求助大神~~我在Reaml中注入userService对象启动tomcat就报错【java吧】_百度贴吧...
  7. php arrayaccess 二维,PHP数组式访问接口ArrayAccess用法分析
  8. .wav格式的声音文件怎么变成matlab 中的.mat文件
  9. centos7 下修改网络配置
  10. RVC使用指南(三)-对象管理