2017年9月18日
本节主要介绍了解释器中的一些基本概念,然后对ANTLR的执行过程重新进行了梳理,最后对ANTLR语法进行了简要梳理,如有任何错误欢迎斧正。2017年9月22日
对本文页面进行了一些调整
一、 解释器中的基本概念
1.1 解释器(interpreter)
通常来说,如果一个程序能够计算,或者说是“执行”语句,我们就将其称为解释器。
如计算器、python解释器等。
1.2 翻译器(translator)
如果一个程序能够将一种语言翻译成其他语言,我们就将其称之为翻译器。
如Java到C#的转换与编译器。
1.3 编译器(compiler)
编译器就是将“一种语言(通常为高级语言)”翻译为“另一种语言(通常为低级语言)”的程序
1.4 词法分析(lexical analyzer)
将字符划分为单词或token的过程称为词法分析
1.5 语法分析(parser or syntax analyzer)
在词法分析的基础上将单词序列组合成各类语法短语
二、 ANTLR的执行过程
2.1 制定语法规则
就像我们在《解释器构造实践-ANTLR(一)》(以下简称为(一))中所写过的HelloWorld一样,首先我们需要一个grammar文件,这个grammar文件用于描述各种自定义的规则。
这次我们使用一个数组初始化的语法(ArrayInit):
//头信息,grammar后接语法名,与文件名相同
grammar ArrayInit;
//init将匹配在大括号中,由英文逗号分割的值
init : '{' value (',' value)* '}' ;
//值可以是整数,也可以是嵌套的数组。‘|’表示或者。
value : init | INT ;
//匹配整数
INT : [0-9]+ ;
//忽略空格、制表、换行
WS : [ \t\r\n]+ -> skip ;
2.2 生成分析器
在(一)中我们已经配置过了antlr4的别名或者shell脚本,接下来用antlr4
指令生成分析文件:
$ antlr4 ArrayInit.g4
$ ls
ArrayInit.g4 ArrayInitLexer.tokens
ArrayInit.tokens ArrayInitListener.java
ArrayInitBaseListener.java ArrayInitParser.java
ArrayInitLexer.java
ArrayInit
├── ArrayInit.g4 -- 语法文件
├── ArrayInit.tokens --
├── ArrayInitBaseListener.java -- 缺省情况下生成的Listener
├── ArrayInitLexer.java -- 词法分析器
├── ArrayInitLexer.tokens --
├── ArrayInitListener.java -- 缺省情况下生成的Listener
└── ArrayInitParser.java -- 语法分析器
补充说明:
ANTLR对生成语法树有两种遍历方法,分别为listener与visitor,如上默认选择的是listener。
listener:在遍历语法树时,自动回调Listener中回调方法。实现简单,全自动,采用深度优先遍历,只需要实现listener中的各个接口方法即可完成语义表达。
vistior:提供了可以控制遍历过程的一种方式,通过调用visit方法,可以访问子树节点。我们可以采用antlr4 -no-listener -visitor *.g4来生成visit遍历方法而非listener。
2.3 编译源代码
$ javac *.java
2.4 测试语法
在(一)中我们已经配置过了grun的别名,接下来用grun
指令进行测试:
$ grun ArrayInit init -tokens
{99, 3, 451}
EOF
[@0,0:0='{',<1>,1:0]
[@1,1:2='99',<4>,1:1]
[@2,3:3=',',<2>,1:3]
[@3,5:5='3',<4>,1:5]
[@4,6:6=',',<2>,1:6]
[@5,8:10='451',<4>,1:8]
[@6,11:11='}',<3>,1:11]
[@7,13:12='<EOF>',<-1>,2:0]
2.5 添加主函数
//导入antlr的运行时库
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.*;
public class Test {
public static void main(String[] args) throws Exception {
//创建一个读取标准输入的CharStream
ANTLRInputStream input = new ANTLRInputStream(System.in);
//创建一个从指定的CharStream中读取数据的词法分析器
ArrayInitLexer lexer = new ArrayInitLexer(input);
//创建一个词法分析器产生的token缓冲
CommonTokenStream tokens = new CommonTokenStream(lexer);
//创建一个从token缓冲中读取token的语法分析器
ArrayInitParser parser = new ArrayInitParser(tokens);
//开始分析规则init
ParseTree tree = parser.init();
//打印Lisp风格的语法分析树
System.out.println(tree.toStringTree(parser));
}
}
2.6 重新编译
$ javac Array*.java Test.java
2.7 执行
$ java Test
{1,{2,3},4}
EOF
(init { (value 1) , (value (init { (value 2) , (value 3) })) , (value 4) })
三、 简略语法说明
- 语法(grammar)由一系列的规则(rule)组成
- 由小写字母开始的规则组成分析规则
- 由大写字母开始的规则组成词法规则
- 用|符号来表示可选择的规则,用圆括号的形式来表示子规则
- 待补充
四、 问题
- 翻译器(translator)与编译器(compiler)的区别
五、 参考链接
- 百度百科 编译器
https://baike.baidu.com/item/编译器/8853067?fr=aladdin - 《The Definitive ANTLR 4 Reference》
https://pragprog.com/book/tpantlr2/the-definitive-antlr-4-reference - 《Antlr v4入门教程》
http://blog.youkuaiyun.com/dc_726/article/details/45399371