文章目录
- 前言
- 一、项目简介
- 二、ANTLR工具、运行库以及自动生成的代码
- 三、将生成的语法分析器与Java程序集成
- 四、构建一个语言类应用程序
前言
通过一个入门项目学习到:
- 一些ANTLR语法的语义元素定义
- ANTLR根据语法自动生成代码的机制
- 如何讲自动生成的语法分析器和Java程序集成
- 如何使用语法分析树监听器辨析一个代码翻译工具
一、项目简介
构造一个语法,它为C语言或其继承者Java语法的一个很小的子集,我们将识别包括花括号或者嵌套的花括号的一些整数,例如{1,2,3}
和{1,{2,3},4}
二、ANTLR工具、运行库以及自动生成的代码
步骤:
- 对一个语法运行ANTLR
- 将生成的代码与运行库一起编译
- 将编译好的代码和运行库放在一起运行
//语法文件同从以grammar关键字开头
//这是一个名为ArrayInit的语法,必须以文件名ArrayInit.g4相匹配
grammar ArrayInit;
//名为init的规则,它匹配一对花括号中的、逗号分隔的value
init : '{' value (',' value)* '}';
//一个value可以是嵌套的花括号结构,也可以是一个简单的整数,即INT语法符号
value : init
| INT
;
//语法分析器的规则必须以小写字母开头,语法分析器的规则必须用大写字母开头
INT : [0-9]+ ; //定义语法符号INT,它由一个或多个数字组成
WS : [ \t\r\n]+ -> skip ; //定义语法规则“空白符号”,skip
- 1、ArrayInit.tokens:ANTLR会给每个我们定义的语法符号指定一个数字形式的类型,然后将它们的对应关系存储与该文件中
- 2、ArrayInitListener.java,ArrayInitBaseListener:默认情况下,ANTLR生成的语法分析器能将输入文本转换为一棵语法分析树,在遍历该树时,遍历器能够触发一些列回调,并通知监听器对象,第一个文件接口给出了这些回调方法的定义,第二个文件包含该接口的默认实现类
- 3、ArrayInitLexer.java:ANTLR能够自动识别出我们的语法中的文法规则和词法规则,这个文件包含的是词法分析器的定义
- 4、ArrayInitParser.java:该文件包含语法分析器类的定义
测试1:
每行输出代表一个词法符号,其中包含该词法符号的全部信息
以第二行[@1,1:2='99',<INT>,1:1]
为例:
- @1表示他是第一个词法符号
- 1:2表示由第1到第2个字符组成
- ='99’表示包含的文本是99
- < INT >表示类型是INT
- 最后的1:1表示位于输入文本的第一行第一个字符处
查看语法分析树
测试2:
两组测试均符合预期
三、将生成的语法分析器与Java程序集成
下面将演示在IDEA中的操作:
需先下载好antlr插件
按照个人习惯,我的项目最终的结构如图:
- 1、新建Maven项目
一直点next(可根据自身需求修改) - 2、在pom.xml中设置antlr依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>test2</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.antlr</groupId>
<artifactId>antlr4-runtime</artifactId>
<version>4.10.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.antlr</groupId>
<artifactId>antlr4-maven-plugin</artifactId>
<version>4.10.1</version>
<executions>
<execution>
<id>antlr</id>
<goals>
<goal>antlr4</goal>
</goals>
<phase>none</phase>
</execution>
</executions>
<configuration>
<outputDirectory>src/test/java</outputDirectory>
<listener>true</listener>
<treatWarningsAsErrors>true</treatWarningsAsErrors>
</configuration>
</plugin>
</plugins>
</build>
</project>
注意version中的版本要与自身ANTLR的版本相同
若出现爆红,一般情况下点击重新加载项目即可
- 3、在src\main\java中创建文件夹,在文件夹下新建antlr文件,文件内容在第二部分开头已给出
- 4、设置参数并运行
- 5、在项目文件中新建demoMain.java文件
内容为:
package demo.test;
import demo.ArrayInitLexer;
import demo.ArrayInitParser;
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.*;
public class demoMain {
public static void run(String str) throws Exception {
ANTLRInputStream input = new ANTLRInputStream(str);
ArrayInitLexer lexer = new ArrayInitLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
ArrayInitParser parser = new ArrayInitParser(tokens);
ParseTree tree = parser.init();
System.out.println(tree.toStringTree(parser));
}
public static void main(String[] args) throws Exception {
String[] testString = {
"{1,{2,3},4}",
"{1,2,3,4}",
};
for(String s:testString){
System.out.println("Input: " + s);
run(s);
}
}
}
运行结果
若我们设置个错误的格式,将testString改为
String[] testString = {
"{1,2"
};
运行结果:
这里可以发现,antlr可以自动报告语法错误并从语法错误中恢复
测试结果符合预期
- 6、如果想要实现可视化,可以在
.g4
文件中右键,点击Test Rule init - 输入测试用例,即可看到对应的结果
四、构建一个语言类应用程序
在第三部分我们知道了如何对语法运行ANTLR工具,以及如何将自动生成的语法分析器和一个Java程序集成,在这一部分我们继续完成能够处理数组初始化语句的示例程序,让程序不仅能够识别初始化语句,还能够翻译他们。
例如:在Java中将类似{99,3,415}的short数组翻译为"\u0063\u0003\u01c3"
,的这种十六进制表示的结果
翻译,其实就是一系列的映射过程
对程序的要求:程序能从语法分析树中提取数据
方案:使用antlr内置的语法分析树遍历器进行深度优先遍历,然后再它触发的一系列回调函数中进行适当的操作。
步骤:
- 1、新建shortToUnicodeString.java文件,文件内容为:
package demo.test;
import demo.ArrayInitBaseListener;
import demo.ArrayInitParser;
public class shortToUnicodeString extends ArrayInitBaseListener {
//将“{”翻译为 “
@Override
public void enterInit(ArrayInitParser.InitContext ctx){
System.out.print('"');
}
//将”}“翻译为 ”
@Override
public void exitInit(ArrayInitParser.InitContext ctx){
System.out.print('"');
}
//其他翻译
@Override
public void enterValue(ArrayInitParser.ValueContext ctx){
int value = Integer.valueOf(ctx.INT().getText());
System.out.printf("\\u%04x",value);
}
}
- 2、新建Translate.java文件,文件内容为:
package demo.test;
import demo.ArrayInitLexer;
import demo.ArrayInitParser;
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.*;
public class Translate {
public static void run(String str) throws Exception{
ANTLRInputStream input = new ANTLRInputStream(str);
ArrayInitLexer lexer = new ArrayInitLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
ArrayInitParser parser = new ArrayInitParser(tokens);
ParseTree tree = parser.init();
ParseTreeWalker walker = new ParseTreeWalker();
walker.walk(new shortToUnicodeString(),tree);
System.out.println();
}
public static void main(String[] args) {
String[] testStr={
"{99,3,451}"
};
for(String s:testStr){
System.out.println("Input: " + s);
System.out.print("Translate: ");
try {
run(s);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
运行结果:
结果符合预期