当前位置: 首页 > news >正文

Antlr4入门学习及实用案例(一)

Antlr4入门学习及实用案例(一)

ANTLR4 (ANother Tool for Language Recognition) 是一个功能强大的解析器生成器,可以用来读取、处理、执行或格式化结构化文本或二进制文件。它被广泛用于构建语言、工具和框架。

工作流程及基础理论

学习 ANTLR4,首先要理解其工作流程和基础理论。

1. ANTLR4 的工作流程

整个流程可以概括为三步:

  1. 定义语法 (Grammar):使用 ANTLR4 的特殊语法(保存在 .g4 文件中)来描述自然语言或数据格式的结构。
  2. 生成代码 (Generate):使用 ANTLR4 工具(一个.jar文件 或 antlr4的maven插件)来处理 .g4 语法文件,它会自动生成一套源代码(支持 Java, Python, C# 等),包括词法分析器、语法分析器、监听器和访问者。
  3. 集成与实现 (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. 环境准备

  1. 安装 Java JDK: ANTLR4 工具及其生成的 Java 代码都需要 Java 环境。请确保已安装 JDK 8 或更高版本。

  2. 下载 ANTLR4 工具:

    • 访问 ANTLR 官网下载页面。

    • 下载 antlr-4.x.x-complete.jar 文件(例如 antlr-4.13.1-complete.jar)。

    • 为了方便,可以将其放在所在项目目录下,或者一个固定的位置,并设置一个别名方便在命令行中使用。

    • 命令行别名设置 (可选但推荐):

      • macOS/Linux (.bashrc or .zshrc):

        export 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 文件,或者直接在命令行中使用完整命令。

  3. 项目管理:
    创建一个项目文件夹,例如 AntlrCalculator

   AntlrCalculator/├── antlr-4.13.1-complete.jar├── src/│   └── main/│       ├── antlr4/  // .g4 文件存放处│       └── java/    // .java 文件存放处└── pom.xml          // 如果使用 Maven

对于项目管理:更推荐使用 Maven 或 Gradle。这样可以非常方便地管理依赖和自动生成代码。

可在 Maven 的 pom.xml 中添加 antlr4 依赖及其插件:

   <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 的文件,并填写以下内容:

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. 生成代码

打开命令行,进入项目目录,执行以下命令:

# -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 模式是开发时大多数的选择,因为这里我们需要从子表达式匹配出结果,然后返回给父表达式使用。

  1. 创建一个名为 PromptTemplateEvalVisitor.java 的文件,这个类将继承 PromptTemplateBaseVisitor 并重写我们关心的方法。
public class PromptTemplateEvalVisitor extends PromptTemplateBaseVisitor<String> {private final Map<String, Object> context; // 提示词模板的上下文数据存储public PromptTemplateEvalVisitor(Map<String, Object> context) {this.context = context;}@Overridepublic 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();}@Overridepublic String visitText(PromptTemplateParser.TextContext ctx) {return ctx.getText(); // 文本节点直接返回其原始字符串}@Overridepublic String visitVariable(PromptTemplateParser.VariableContext ctx) {String varName = ctx.ID().getText(); // 从匹配到的标识符(ID)中提取变量名Object value = context.get(varName); // 从上下文中获取此变量名的值return value != null ? value.toString() : ""; // 变量节点值存在则转换字符串直接返回}
}
  1. 编写测试类 PromptTemplateEvalVisitorTest.java 来验证提示词模板功能的流程准确性。
@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 的更多可能性。

————————————————

Interpreter 1 - Google 文档

简介 - ANTLR 4 简明教程 - 开发文档 - 文江博客

Antlr4系列(一):语法分析器学习 - 知乎

antlr4/doc/parser-rules.md at master · antlr/antlr4 · GitHub

文章案例项目代码:antlr4-simple-demo

  • https://github.com/Cyanty/antlr4-simple-demo
http://www.sczhlp.com/news/3020/

相关文章:

  • LVGL + ESP-Brookesia 嵌入式模拟桌面应用创建
  • Qt中MVP架构与抽象DAO层的结合示例
  • 7 月做题总结
  • 23
  • MoveIt Transfer漏洞引发更多受害者数据泄露,联邦机构也未能幸免
  • centos安装EPEL源失败
  • ceph:麒麟v10仓库版本的ceph-12.2.8-24.p02.ky10.x86_64,代号:luminous,基本的配置ceph.conf
  • 多肽文库:高效筛选活性肽的科研利器​
  • 第三章 控制器和动作方法
  • 2
  • 深入解析:2025年自动化工程与计算机网络国际会议(ICAECN 2025)
  • LangChain 架构入门指南
  • 2025.8.1 贪心
  • vue3中的watch详细讲解保证看的明明白白
  • thinkphp6.0 项目,使用原生session 很正常,但是使用think 的 session 不正常,在本类中存储,本类中读取可以,其他类中读取 是空值是怎么回事
  • AI 时代,为什么我们还有必要写博客?
  • 阿里云OSS对象存储简单上手(超级详细,针对小白)
  • 一种CAN接口电路的隔离方案
  • 热卷 2510
  • P5405 [CTS2019] 氪金手游 题解
  • 阿里云OSS对象存储简单使用(超级详细,针对小白上手!!)_阿里云oss使用教程-CSDN博客-2025-08-01 08_06_12
  • 浅谈后缀自动机
  • 多Agent协作入门:移交编排模式
  • Oxygen Forensic Detective 17.3.1 新增功能简介 - 一体化数字取证软件
  • ARM物联网漏洞利用实验室在Blackhat USA 2017首次亮相
  • 谁在给字节赚钱:⌈巨量⌋一出,谁与争锋?
  • 大模型时代,这才是AI的“隐形心脏”
  • nodejs第一天
  • 题解 P8883 幻想中成为原神
  • 薄膜铌酸锂量子光子学:回顾与展望