项目流程:
(1)定义 .g4
语法文件;
(2)使用 ANTLR 4 工具,生成词法分析器(Lexer)和语法分析器(Parser)目标编程语言代码
(3)遍历 AST(抽象语法树):访问者模式(Visitor)和监听器模式(Listener)
文章目录:
一、安装 Antlr 4
二、基本流程 CMD
1、编写文法
2、生成分析器
3、编译分析器,测试文法
三、IDEA Java 项目
1、安装插件>新建项目>添加依赖
2、编写文法,生成分析器
3、集成到 Java 程序
4、可视化查看,语法解析树
5、遍历并操作语法分析树
四、IDEA TS 项目
1、初始化>添加依赖>新建项目
2、编写文法,生成分析器
3、集成到 TS 程序
4、可视化查看 (同 Java 项目)
5、遍历并操作语法分析树
参考:
ANTLR4安装(WIndows版,适合小白or新手)_antlr安装-CSDN博客
ANTLR学习(二):ANTLR入门项目-CSDN博客
入门 · ANTLR 4简明教程 (gitbooks.io)
一、安装 Antlr 4
(1)官网下载 ANTLR 4,放到某个文件夹中,最终效果如下图
(2)新建两个 txt 文件,输入内容后改后缀为 bat 文件类型,最终效果如下图
(3)配置环境变量,最终效果如下图
(4)检验安装
二、基本流程 CMD
1、编写文法
新建文件夹,编写一段用于识别 hello world 短语的文法,其文件类型保存为 Hello.g4
grammar Hello; // 声明名为 Hello
的语法
r : 'hello' ID ;
ID : [a-z]+ ; // 小写开头称为词法规则,大写开头称为语法规则
WS : [ \t\r\n]+ -> skip;
注:第二句:定义名为 r
的情况:先出现 "hello" ,后面紧跟着一个小写字母组成的字符串
第三句:定义名为ID
的情况:至少由一个小写字母组成的字符串,都能被叫做 ID
第四句:定义名为ws的情况:出现空格、制表符、回车和换行时,直接跳过解析,不管它
2、生成分析器
利用 ANTLR 工具,生成词法分析器(Lexer)和语法分析器(Parser)目标编程语言的代码
指定目标语言,是为了确保 ANTLR 生成的分析器的代码,适配我们的项目的代码。
由于 ANTLR 是 Java 写的,若不指定目标语言时,它会默认生成 Java 的分析器代码。
指定语言的方式有两种:
法一:在 .g4
文件的头部添加指定的目标语言
法二:在使用 ANTLR 工具的命令行参数中,指定目标语言
若指定为 Java:antlr4 -Dlanguage=Java Hello.g4
=Python、=C、=Cpp、=CSharp、=Go、=JavaScript、=TypeScript、=PHP ......
该命令会在相同目录下,生成后缀名为 tokens、interp 和 java 的 8 个源代码文件。
3、编译分析器,测试文法
编译由 ANTLR 生成的识别器的 Java 源文件,进而生成对应的类文件:javac Hello*.java
此时,就差一个主程序去加载这个类文件,就能启动语言识别器的程序了
ANTLR 运行库提供了 TestRig 的测试工具,可以不创建主程序,就能测试文法。
使用 TestRig 工具,打印识别期间创建的记号,像关键字 hello 和标识符 world 的词汇符号。
法一:以列表的方式,查看记号,每一行输出表示一个记号以及它的相关信息
grun Hello r -tokens // Hello 是文法的名字,r 是开始的规则名字
hello world // 输入并按回车
EOF // Windows 系统输入 Ctrl+Z 并按回车
>> [@0,0:4='hello',<1>,1:0] .... [@2,13:12='<EOF>',<-1>,2:0]
含义: @0 :记号索引(从 0 开始计数)
0:4 :记号开始与结束的位置(从 0 开始)
<1>:记号类型,具体数值和类型,存储在后缀名为 tokens 的文件中
1: 0:记号在第1行(从1开始)从第6个字符开始(从0开始,制表符按单字符计算
法二:以 LISP 风格的文本形式,查看记号:
grun Hello r -tree
>> (r hello world)
含义:识别到一个词法规则 r ,匹配到的文本是 hello world
法三:以可视化的方式,查看语法分析树:
grun Hello r -gui ,返回一张图片
注:再次强调一下,r 是本篇文法中,最开始的规则的名字,不是所有文法的最开始规则都叫 r
三、IDEA Java 项目
1、安装插件>新建项目>添加依赖
(1)安装 ANTLR v4 插件,最终效果如下图
(2)新建 Maven 项目,最终效果如下图
(3)添加 ANTLR 依赖。在 pom.xml 文件中的 </properties> 后,加上如下代码
添加完后,记得要下载依赖(点击右侧的 m 图标,即可启动加载 or 重新加载)
<dependencies>
<dependency>
<groupId>org.antlr</groupId>
<artifactId>antlr4-runtime</artifactId>
<version>4.13.1</version>
</dependency> // 版本号与下面的要一致
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.antlr</groupId>
<artifactId>antlr4-maven-plugin</artifactId>
<version>4.13.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>
2、编写文法,生成分析器
(1)编写文法,保存为 .g4 文件类型
(2)g4 文件右键,配置 ANTLR 工具(这一步了解即可)
(3)g4 文件右键,生成解析器代码(生成前会自动检查语法,若有问题会直接报错)
(1)Lexer 词法分析器:它根据定义的词法规则,将输入的文本,分割成一个个的词法单元。
(2)Parser 语法分析器:它基于词法单元 (tokens),按照定义的语法规则来构建语法分析树。
(3)令牌流:由一系列的令牌 [词法单元] (token),按照特定的顺序,组成的序列。
---------------------------------------- 遍历语法分析树,操作节点元素 --------------------------------------
(3)xxxBaseVisitor.java,xxxVisitor.java(访问者模式):后者是一个基类,它给每种可能的语法结构(比如句子、单词、数字等等)都准备了一个处理的方法。但是这些方法是一开始都是空的方法。我们可以继承这个基类,来创建自己的 Visitor
类,并重写某些行为的具体方法。
(4)xxxBaseListener.java,xxxListener.java(监听者模式):构建语法分析树之后,在遍历该树时,遍历器能够触发一些列回调,并通知监听器对象,第一个文件接口给出了这些回调方法的定义,第二个文件包含该接口的默认实现类
3、集成到 Java 程序
package demo2; import demo2.antlr.modelicaLexer; // 导入自定义的词法分析器 import demo2.antlr.modelicaParser; // 导入自定义的语法分析器 import org.antlr.v4.runtime.*; // 导入 ANTLR 运行时的相关类 import org.antlr.v4.runtime.tree.*; // 导入与解析树相关的类 public class Main { // 定义主类 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(); // 调用 Parser 的 `init` 方法获取解析树 ...... } public static void main(String[] args) throws Exception { ... run(xxx);... }}
4、可视化查看,语法解析树
5、遍历并操作语法分析树
使用 ANTLR 内置的语法分析树遍历器
进行深度优先遍历,然后在它触发的一系列回调函数中,进行适当的操作。
四、IDEA TS 项目
1、初始化>添加依赖>新建项目
(1)初始化:npm init -y
将在当前目录自动创建 package.json
文件,包含项目基本信息,管理项目的依赖和配置。
(2)安装 TS 编译器:npm install --save-dev typescript
配置 TS 编译器:npx tsc --init
注:若下载失败,输入" npm config set proxy null "重置,然后再进行安装。
(3)安装 ANTLR 4:npm install --save antlr4
安装 ANTLR 4 的包,并自动将其作为依赖项添加到 package.json
的 dependencies
中 注:若下载失败,输入" npm config set proxy null "重置,然后再进行安装。
注:若仍然失败,请关闭防火墙,再试一试
(4)新建主代码目录、新建 g4 文件、新建 ts 程序、新建用于存放分析器的目录(如下图)
2、编写文法,生成分析器
(1)g4 文件右键,配置 ANTLR 工具
(2)g4 文件右键,生成分析器代码(生成前会自动检查语法,若有问题会直接报错)
3、集成到 TS 程序
import { BufferedTokenStream, CharStream, CommonTokenStream } from "antlr4"; import ALangLexer from "./antlr/ALangLexer"; import ALangParser from "./antlr/ALangParser"; //导入自定义语法分析器 function run(str: string): void { const input = new CharStream(str); // 将输入的字符串,转换为 ANTLR 输入流 const lexer = new ALangLexer(input); // 使用输入流,创建词法分析器 const tokens: BufferedTokenStream = new CommonTokenStream(lexer);//用Lexer创建令牌流 const parser = new ALangParser(tokens); // 使用令牌流,创建语法分析器 const tree = parser.prog(); // 调用语法分析器的`prog`方法获取解析树 console.log(tree.toStringTree(null,parser)); // 打印解析树的字符串表示 } function main(): void {... run(s); ...} main();