ANTLR可以支持生成多种编程语言为载体的Lexer和Parser,可以自动处理LL(*)文法(注意不是LR文法,这里ANTLR实际上是Another Tool for Language Recognition的缩写),目前先在IDEA中用最方便的插件学习一下生成Java版本的。
首先在IDEA中安装插件,从File->Setting->Plugins搜索并安装ANTLR v4 grammer plugin
,并重启IDEA。
创建一个项目模块,如果项目模块不是Maven格式,要将其转换。对于干净的新创建的模块直接右键Add Framework Support->Maven即可。
在main/resource
目录下添加calculator.g4
文件,这是语法规则文件:
grammar calculator;
start:
expression EOF
;
expression:
| INT
| expression (PLUS | MINUS) expression
;
PLUS : '+';
MINUS : '-';
INT : '0'..'9'+;
注意其中grammer
要和.g4
文件名一致。
接下来要检查定义的语法规则,这步不是必须的。右键这个.g4
文件的start
这一块(注意在不同的位置右键,出来的菜单是不同的),选择Test Rule Start,可以看到解析的Parse Tree。在左侧Input
下面的框内提供输入,即可看到实时的Parse Tree:
右键这个.g4
文件,选择Configure ANTLR,对其配置:
配置完成后,右键这个.g4
文件,选择Generate ANTLR Recognizer即可自动生成解析程序:
虽然生成了解析程序,但是还没有安装ANTLR,所以里面导入包的地方就会报错。在pom.xml
中的dependencies
标签下添加:
<dependency>
<groupId>org.antlrgroupId>
<artifactId>antlr4-runtimeartifactId>
<version>4.8-1version>
dependency>
然后自动进行依赖下载即可。注意这里的版本要和ANTLR语法到代码的构建工具版本一致(当然不一致后面运行时会报错回来修改版本号就行)。
这里在Main.java
主方法中调用Lexer和Parser进行解析,并得到Parse Tree:
import antlr.calculatorLexer;
import antlr.calculatorParser;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
public class Main {
public static void main(String[] args) {
// 将输入字符串化为输入流(也可以是ANTLRInputStream)然后传入词法分析器
calculatorLexer lexer = new calculatorLexer(CharStreams.fromString("1+2+5"));
// 转换成Token流传入语法分析器
calculatorParser parser = new calculatorParser(new CommonTokenStream(lexer));
// 因为词法文件里最外层定义了start规则,这里就能从parser.start()解析并获取规则树
calculatorParser.StartContext tree = parser.start();
// 将其输出
System.out.println(tree.toStringTree(parser));
}
}
运行结果:
(start (expression (expression (expression 1) + (expression 2)) + (expression 5)) )
如果只想把Parser做完,只要执行parser.start()
而不使用其返回值就可以,当输入不符合语法时候会报错。
特别注意,这里StartContext
和start()
只是因为当时.g4
文件中定义了start
规则才有的。这个规则名也不是必须叫这个的,如果这个规则改名为sta
,那么这两个东西也会被StaContext
和.sta()
取代。
[1] Interpreter Assignment 1
[2] antlr4操作入门(java版本)
使用ANTLR之类的工具就是为了省去手写编译器/解析器前端的麻烦且低效的工作,如果不是做自己的DSL解析(或者类似的东西),手写.g4
文件也是非常麻烦而且不必要的,在antlr/grammars-v4这个仓库里集成了大家总结的各个语言的语法文件,可以直接拿来用。
一个问题就是这些文件的顶层规则(上文中的start规则)命名不一,而且很多不是放在文件开头的,一个技巧就是直接在IDEA中打开,然后看看右边滚动条:
这里是IDEA的静态分析器报告的,意在说明这条规则没有被使用,那么显然它就是顶层规则了。用这条规则尝试解析项目作业的测试用例:
非常好用。
而且从C.g4
文件后面的Token声明来看,这里面是已经做了对嵌入式C的拓展了的。
发现了一点问题,关于解析C语言#define
的问题见这个issue中的讨论。