文章目录

  • 前言
  • 一、项目简介
  • 二、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

 

antlr java 例子 antlr入门_java

  • 1、ArrayInit.tokens:ANTLR会给每个我们定义的语法符号指定一个数字形式的类型,然后将它们的对应关系存储与该文件中
  • 2、ArrayInitListener.java,ArrayInitBaseListener:默认情况下,ANTLR生成的语法分析器能将输入文本转换为一棵语法分析树,在遍历该树时,遍历器能够触发一些列回调,并通知监听器对象,第一个文件接口给出了这些回调方法的定义,第二个文件包含该接口的默认实现类
  • 3、ArrayInitLexer.java:ANTLR能够自动识别出我们的语法中的文法规则和词法规则,这个文件包含的是词法分析器的定义
  • 4、ArrayInitParser.java:该文件包含语法分析器类的定义

测试1:

antlr java 例子 antlr入门_学习_02

每行输出代表一个词法符号,其中包含该词法符号的全部信息
以第二行[@1,1:2='99',<INT>,1:1]为例:

  • @1表示他是第一个词法符号
  • 1:2表示由第1到第2个字符组成
  • ='99’表示包含的文本是99
  • < INT >表示类型是INT
  • 最后的1:1表示位于输入文本的第一行第一个字符处

查看语法分析树

antlr java 例子 antlr入门_开发语言_03


antlr java 例子 antlr入门_开发语言_04


antlr java 例子 antlr入门_java_05


测试2:

antlr java 例子 antlr入门_开发语言_06


两组测试均符合预期

三、将生成的语法分析器与Java程序集成

下面将演示在IDEA中的操作:

需先下载好antlr插件

antlr java 例子 antlr入门_java_07


按照个人习惯,我的项目最终的结构如图:

antlr java 例子 antlr入门_学习_08

  • 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的版本相同

若出现爆红,一般情况下点击重新加载项目即可

antlr java 例子 antlr入门_java_09

  • 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);
        }
    }
}

运行结果

antlr java 例子 antlr入门_antlr java 例子_10


若我们设置个错误的格式,将testString改为

String[] testString = {
       "{1,2"
};

运行结果:

antlr java 例子 antlr入门_开发语言_11


这里可以发现,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();
            }
        }
    }
}

运行结果:

antlr java 例子 antlr入门_antlr java 例子_12


结果符合预期