Antlr4入门学习及实用案例(一)
ANTLR4 (ANother Tool for Language Recognition) 是一个功能强大的解析器生成器,可以用来读取、处理、执行或格式化结构化文本或二进制文件。它被广泛用于构建语言、工具和框架。
工作流程及基础理论
学习 ANTLR4,首先要理解其工作流程和基础理论。
1. ANTLR4 的工作流程
整个流程可以概括为三步:
- 定义语法 (Grammar):使用 ANTLR4 的特殊语法(保存在
.g4
文件中)来描述自然语言或数据格式的结构。 - 生成代码 (Generate):使用 ANTLR4 工具(一个
.jar
文件 或 antlr4的maven插件)来处理.g4
语法文件,它会自动生成一套源代码(支持 Java, Python, C# 等),包括词法分析器、语法分析器、监听器和访问者。 - 集成与实现 (Implement):将这些生成的代码集成到项目中,并编写自己定义的代码来 "遍历" ANTLR4生成的语法分析树(Parse Tree),从而实现具体的业务逻辑(如计算、转义、格式化等)。
2. 核心概念详解
语法文件 (.g4
File)
- 这是 ANTLR 的核心,所有规则都在这里定义。
- 一个
.g4
文件可以同时包含词法规则和语法规则。
词法分析器 (Lexer)
-
作用:读取字符流(如:输入的文本),并将其分解成一个个有意义的"单词",这些"单词"被称为 词法单元(Token)。
例如,对于输入
100 + 200
,Lexer 会生成三个 Token:INT(100)
、PLUS(+)
、INT(200)
,空格通常会被丢弃。 -
规则定义:在
.g4
文件中,词法规则通常以大写字母开头。例如:INT : [0-9]+ ;
。
语法分析器 (Parser)
-
作用:接收 Lexer 生成的 Token 流,并根据定义的语法规则来检查这些 Token 的排列顺序是否"合乎语法"。如果合法,它会构建一个树形结构来表示输入的层级关系,这个树就是 语法分析树(Parse Tree)。
例如,对于 Token 流
INT
,PLUS
,INT
,Parser 会根据规则expr : INT PLUS INT;
构建一个expr
节点,该节点下有三个子节点。 -
规则定义:在
.g4
文件中,语法规则通常以小写字母开头。例如:expr : term ( '+' term )* ;
。
语法分析树 (Parse Tree)
- 这是 Parser 工作的结果,是输入文本的结构化表示。树的根节点是语法的起始规则,内部节点是其他语法规则,叶子节点是词法单元(Token)。而自定义的业务逻辑就是通过" 走遍" 这棵树来实现的。
- 语法分析树的递归逻辑基于 递归下降解析(Recursive Descent Parsing) 实现,其核心是一种 深度优先、左递归优先 的树构建策略。

监听器 (Listener) vs. 访问者 (Visitor)
ANTLR4 提供了两种遍历 Parse Tree 的设计模式,以实现与分析结果进行交互的能力。
监听器 (Listener):
- 工作方式:由 ANTLR 的
ParseTreeWalker
(树遍历器)来驱动。当遍历器进入(enter
)或退出(exit
)树的某个节点时,会自动调用实现的监听器中对应的方法(如enterExpression
,exitExpression
)。 - 特点:被动式。不需要控制遍历过程,只需要在特定事件发生时做出响应。方法没有返回值。
- 适用场景:当需要对所有节点执行某些操作,且操作之间没有复杂的返回值依赖时,比如字节码生成、代码格式化。

访问者 (Visitor):
- 工作方式:由开发者自己控制遍历。需要显式地在实现的方法中调用
visit(child)
来访问子节点。 - 特点:主动式。可以自由控制遍历的顺序,甚至可以不访问某些子树,方法可以有返回值。
- 适用场景:当子节点的计算结果是父节点计算的一部分时,比如表达式求值、构建抽象语法树(AST)、编译器。

环境准备及入门使用
1. 环境准备
-
安装 Java JDK: ANTLR4 工具及其生成的 Java 代码都需要 Java 环境。请确保已安装 JDK 8 或更高版本。
-
下载 ANTLR4 工具:
-
访问 ANTLR 官网下载页面。
-
下载
antlr-4.x.x-complete.jar
文件(例如antlr-4.13.1-complete.jar
)。 -
为了方便,可以将其放在所在项目目录下,或者一个固定的位置,并设置一个别名方便在命令行中使用。
-
命令行别名设置 (可选但推荐):
-
macOS/Linux (
.bashrc
or.zshrc
):iniexport CLASSPATH=".:/path/to/your/antlr-4.13.1-complete.jar:$CLASSPATH" alias antlr4='java -jar /path/to/your/antlr-4.13.1-complete.jar' alias grun='java org.antlr.v4.gui.TestRig'
-
Windows (CMD/PowerShell): 可以创建一个
.bat
文件,或者直接在命令行中使用完整命令。
-
-
-
项目管理: 创建一个项目文件夹,例如
AntlrCalculator
。
less
AntlrCalculator/
├── antlr-4.13.1-complete.jar
├── src/
│ └── main/
│ ├── antlr4/ // .g4 文件存放处
│ └── java/ // .java 文件存放处
└── pom.xml // 如果使用 Maven
对于项目管理:更推荐使用 Maven 或 Gradle。这样可以非常方便地管理依赖和自动生成代码。
可在 Maven 的 pom.xml
中添加 antlr4 依赖及其插件:
xml
<dependencies>
<dependency>
<groupId>org.antlr</groupId>
<artifactId>antlr4-runtime</artifactId>
<version>${antlr.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.antlr</groupId>
<artifactId>antlr4-maven-plugin</artifactId>
<version>${antlr.version}</version>
<executions>
<execution>
<goals>
<goal>antlr4</goal>
</goals>
</execution>
</executions>
<configuration>
<visitor>true</visitor>
<listener>true</listener>
<sourceDirectory>./src/main/resources</sourceDirectory>
</configuration>
</plugin>
</plugins>
</build>
注:推荐安装 IntelliJ IDEA 的 ANTLR4 插件,可以高亮显示语法,调试语法规则,提供语法树的可视化工具。

2. 定义语法 (.g4
)
可知在 Spring AI 框架中采用了基于 ANTLR 的开源库 StringTemplate 作为提示词模板实现的技术方案,使用 ANTLR4 实现文本规则提取十分简便,接下来让我们动手实现一个简单的提示词模板功能。
在项目目录下创建一个名为 PromptTemplate.g4
的文件,并填写以下内容:
ini
grammar PromptTemplate; // 语法文件的头部,定义语法名称,必须与文件名一致
template: segment+ ; // 起始规则(节点):定义一个模板(template)由一个或多个片段(segment)组成
segment: variable | text ; // 片段规则(节点):由变量(variable)、普通文本(text)组成
variable: LBRACE ID RBRACE ; // 变量规则(节点):由左花括号、标识符(ID)、右花括号组成
text: TEXT_CHUNK+ ; // 文本规则(节点):一个或多个连续的非花括号字符组成
LBRACE: '{' ;
RBRACE: '}' ;
ID: [a-zA-Z_] [a-zA-Z0-9_]* ;
TEXT_CHUNK: ~[{}] + ;
通过 IDEA 的 ANTLR4 插件,输入像 "你好,我的名字是{name}。" 这样的短语,可查看 ANTLR 创建的解析树:

3. 生成代码
打开命令行,进入项目目录,执行以下命令:
shell
# -Dlanguage=Java 是可选的,因为java是默认生成语言
# -o <dir> 可以指定输出目录,默认生成在当前目录
java -jar antlr-4.13.1-complete.jar PromptTemplate.g4
执行后,目录下就会生成了一堆 .java
文件和 .tokens
文件(xxx为语法文件的文件名):
xxxLexer.java
:词法分析器xxxParser.java
:语法分析器xxxListener.java
:监听器接口xxxBaseListener.java
:监听器的空实现,方便继承重写方法xxxVisitor.java
:访问者接口xxxBaseVisitor.java
:访问者的空实现,方便继承重写方法
如果是 maven 项目,建议使用 antlr4 的maven插件,当构建项目时(例如运行 mvn clean install
或 直接执行 antlr4:antlr4
插件的goals),ANTLR4 插件会自动在 target/generated-sources/antlr4
目录下生成对应的解析文件。
4. 编写代码遍历语法树(使用 Visitor)
Visitor 模式是开发时大多数的选择,因为这里我们需要从子表达式匹配出结果,然后返回给父表达式使用。
- 创建一个名为
PromptTemplateEvalVisitor.java
的文件,这个类将继承PromptTemplateBaseVisitor
并重写我们关心的方法。
typescript
public class PromptTemplateEvalVisitor extends PromptTemplateBaseVisitor<String> {
private final Map<String, Object> context; // 提示词模板的上下文数据存储
public PromptTemplateEvalVisitor(Map<String, Object> context) {
this.context = context;
}
@Override
public String visitTemplate(PromptTemplateParser.TemplateContext ctx) {
StringBuilder sb = new StringBuilder();
for (org.antlr.v4.runtime.tree.ParseTree child : ctx.children) {
sb.append(visit(child)); // 递归处理子节点并将结果拼接到字符串
}
return sb.toString();
}
@Override
public String visitText(PromptTemplateParser.TextContext ctx) {
return ctx.getText(); // 文本节点直接返回其原始字符串
}
@Override
public String visitVariable(PromptTemplateParser.VariableContext ctx) {
String varName = ctx.ID().getText(); // 从匹配到的标识符(ID)中提取变量名
Object value = context.get(varName); // 从上下文中获取此变量名的值
return value != null ? value.toString() : ""; // 变量节点值存在则转换字符串直接返回
}
}
- 编写测试类
PromptTemplateEvalVisitorTest.java
来验证提示词模板功能的流程准确性。
ini
@Test
public void testPromptTemplateEval() {
String template = "Go go go {word_a} 黑咖啡{word_b}有多浓 我只要汽水的{word_c}";
CharStream input = CharStreams.fromString(template); // CharStream流 ↓
PromptTemplateLexer lexer = new PromptTemplateLexer(input); // Lexer词法分析器 ↓
TokenStream tokens = new CommonTokenStream(lexer); // Token流 ↓
PromptTemplateParser parser = new PromptTemplateParser(tokens); // Parser语法分析器 ↓
ParseTree tree = parser.template(); // 从template起始规则开始分析,生成语法分析树
Map<String, Object> context = new HashMap<>();
context.put("word_a", "出发喽~");
context.put("word_b", "品味");
context.put("word_c", "轻松~!");
PromptTemplateEvalVisitor renderer = new PromptTemplateEvalVisitor(context);
String result = renderer.visit(tree); // 遍历语法树,执行自定义的业务逻辑
System.out.println(result);
assert "Go go go 出发喽~ 黑咖啡品味有多浓 我只要汽水的轻松~!".equals(result);
}
测试验证成功,我们已经成功构建了一个基于 ANTLR4 的简单提示词模板实现:

小总结
我们在这篇文章中了解到 ANTLR4 的基础流程概念及其入门使用方法,并通过一个简单的提示词模板案例,逐步展示了如何使用 ANTLR 解析语法的完整过程。
由于文章篇幅限制,让我们在下一篇文章中使用一些实用的语法特性,并实现其他更通用的语法案例,进一步了解和学习 ANTLR4,帮助我们能够处理更复杂的语言解析任务,探索 ANTLR4 的更多可能性。
简介 - ANTLR 4 简明教程 - 开发文档 - 文江博客
antlr4/doc/parser-rules.md at master · antlr/antlr4 · GitHub
文章案例项目代码:antlr4-simple-demo