Antlr的使用
概念
ANTLR(ANother Tool for Language Recognition)是一个强大的解析器生成工具,用于读取、处理、执行或翻译结构化文本或二进制文件。ANTLR通过定义文法(grammar)来识别、构建和访问语言中的元素。
ANTLR为包括Java、C++、C#在内的多种语言提供了一个通过语法描述来自动构造自定义语言的识别器(recognizer)、编译器(parser)和解释器(translator)的框架。ANTLR使用Adaptive LL(*) 语法分析技术进行语法分析,支持词法分析、语法分析、语义分析以及代码生成等功能。
Antlr 提供了大量的官方Grammar示例,包含了各种场景语言,如SQL、Javascript等。
核心功能
1)自动生成语法分析器:ANTLR可以根据用户定义的语法规则自动生成相应的词法分析器和语法分析器,无需手动编写复杂的语法分析代码;
词法分析器(Lexer):负责将输入文本分割成一个个的标记(Token)。词法分析器不关心所生成的单个Token的语法意义及其与上下文之间的关系;
语法分析器(Parser):使用词法分析器生成的标记来构建语法树。语法分析器关注Token之间的语法关系和上下文信息;
2)语法树构造:ANTLR能够将输入文本转换为语法树(AST,Abstract Syntax Tree),使文本的结构更加清晰易懂;
3)语法错误提示:ANTLR在解析过程中能够发现语法错误,并提供详细的错误提示,帮助用户快速定位和修复问题;
4)自定义语言支持:ANTLR支持自定义语言的识别、解析和翻译,为开发者提供了极大的灵活性;
工作流程
1)定义语法规则:使用ANTLR的语法规范描述语言(通常为.g4文件)定义目标语言的语法规则;
2)生成解析器:使用ANTLR工具,根据语法规则文件,自动生成词法分析器和语法分析器的源码;
3)编译解析器:将自动生成的词法分析器和语法分析器的源代码拷贝到应用项目中,编译成可执行文件或库;
3)使用解析器:在应用程序中调用自动生成的解析器,对输入的文本进行解析,并根据生成的语法树进行其他处理;
ANTLR准备
本篇以IDEA中使用 Antlr 4.x 为例。
4.1 Antlr插件安装
在使用之前,要先安装Antlr插件。安装步骤如下:
打开 File - Settings - Plugins 菜单中,如图:

选择插件市场,搜索antlr。
如果搜索不了,可以修改代理,如图:

在弹出的界面中,选择自动代理,并在url中输入:
https://plugins.jetbrains.com/

如果以上配置还是搜索不了,可以直接在浏览器中访问JetBrains Marketplace,在界面中搜索antlr,并选择安装。如图:

4.2 插件使用
Antlr 插件安装之后,idea 开发工具中才能支持 .g4 文件的创建。
4.2.1 Generate ANTLR Recognizer
在 g4 文件中右击,选择 “Generate ANTLR Recognizer”,在项目的根目录,自动创建一个 gen 目录,自动生成词法分析器和语法分析器的源码。

如 Hello.g4 文件,自动的文件如下:

4.2.2 ANTLR Preview
在 Idea 中打开 “ANTLR Preview”,如下:

选择任意 g4 文件,在左侧输入框中输入满足条件的信息,右侧可以预览生成的解析树。

Hello 示例
5.1 示例
Antlr 支持正则表达式集合表示法。以下示例为使用 hello 开头的短语。
1)创建 Hello.g4 文件,文件内容如下:
grammar Hello; // 定义名字
@header {package com.jingai.antlr; } // java 的packages : 'hello' ID | EOF ; // 匹配关键字hello和标志符
ID: [a-z]+ ; // 标识符由小写字母组成
WS: [ \t\r\n]+ -> skip ; // 跳过空格、制表符、回车符和换行符
a)文件以 grammar 开头,名称跟着文件名 Hello;
b)@header 可以用于指定自动生成的词法分析器等 java 文件的报名;
c)在本例中,s 为解析树的root节点,如上面 4.2.2 所示;
d)ID 节点匹配任意长度的小写字母;
+:匹配一次或多次;*:匹配零次或多次;?:匹配零次或一次;
e)-> skip 用于指定跳过的信息;
f)EOF 结束标识;
2)使用 ANTLR Preview,可以预留生成的解析树,如 4.2.2 所示;
3)使用 Generate ANTLR Recognizer,自动生成词法分析器和语法分析器的源码,如 4.2.1 所示;
4)在项目中创建一个包,名称为 g4 文件中通过 @header 指定。将自动生成的 java 文件拷贝到包中;
5)自定义Visitor,代码如下:
package com.jingai.anltr.hello;import org.antlr.v4.runtime.tree.ParseTree;import java.util.List;public class EvalVisitor extends HelloBaseVisitor<String> {@Overridepublic String visitS(HelloParser.SContext ctx) {List<ParseTree> children = ctx.children;StringBuffer sb = new StringBuffer();for (ParseTree t : children) {System.out.println("visit child : " + t.getText());sb.append(t.getText()).append(" ");}return sb.toString();}}
在 Antlr 中,可以通过实现监听器(Listener)或访问者(Visitor)接口来遍历语法树。在自动生成的代码中,除了词法分析器和语法分析器以外,还生成了对应的 Listener 和 Visitor 接口以及基础的默认实现类。
5)编写测试用例,代码如下:
package com.jingai.anltr.hello;import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;public class HelloTest {public static void main(String[] args) {// 创建词法分析器 hello world 字符串HelloLexer lexer = new HelloLexer(CharStreams.fromString("hello world"));// 获取tokenCommonTokenStream tokens = new CommonTokenStream(lexer);// 解析tokenHelloParser parser = new HelloParser(tokens);// 获取根rootHelloParser.SContext tree = parser.s();EvalVisitor visitor = new EvalVisitor();System.out.println(visitor.visit(tree));}
}
执行以上的测试用例,打印的信息为:

5.2 解析
在上面的自定义Visitor中,在for循环中添加断点,执行信息如下:

1)visitS() 方法访问根节点 s,该根节点存在两个子节点,分别为 hello 和 world;
2)子节点的类型为TerminalNodeImpl,包含 symbol 和 parent 信息。其中symbol为当前节点的信息、parent为父节点信息;
a)symbol 为 CommonToken 类型,打印的信息为:[@1,6:10='world',<2>,1:6] ;
b)CommonToken 的 toString() 源码中,返回的代码如下:
return "[@" + this.getTokenIndex() + "," + // 索引,从0开始this.start + ":" + this.stop + // 开始和结束位置"='" + txt + "', // 对应文本<" + typeString + ">" + // 类型channelStr + "," + // 频道信息,在 g4 文件中可以通过 channel() 指定this.line + ":" + // 所在的行,从1开始this.getCharPositionInLine() + "]"; // 所在行的开始位置,从0开始c)结合以上分析,[@1,6:10='world',<2>,1:6] 表示如下:
@1表示为第1个,即第2个;
6:10表示文本在第6个字符到第10个字符之间;
<2>表示类型为2。可以在自动生成的代码中查看;如:
1:6表示在第1行,从第6个字符开始;
算式计算示例
以下通过计算表达式的解析,计算表达式的值为例。
1)创建Calcultor.g4文件,代码如下:
grammar Calculator;@header {package com.jingai.antlr.calculator; } // java 的packageexpr: INT # int| expr op=('*'|'/') expr # mulDiv| expr op=('+'|'-') expr # addSub| '(' expr ')' # brackets| EOF # e;INT : [0-9]+ ;MUL : '*' ;
DIV : '/' ;
ADD : '+' ;
SUB : '-' ;WS: [ \t\r\n]+ -> skip ; // 跳过空格、制表符、回车符和换行符
a)| 表示或的关系,即 expr 是由 INT 或者乘除表达式或者加减表达式或者括号表达式组成;
b)在每一项后面使用 # 加字母,在自动生成的Visitor或Listener中,会自动生成对应的visit或enter方法。如 # mulDiv,在CalcultorVisiter中,自动生成 visitMulDiv()方法;
二义性处理
所谓的二义性是指解析的信息同时匹配多种语法结构。如上面算式表达式,如果要解析的是
5 + 2 * 3,那么 5 + 2 匹配了加法表达式,2 * 3 匹配了乘法表达式,此时就存在二义性。
在 Antlr 中,通过使输入字符串和语法中第一个指定的规则匹配来解决词法二义性。
如上面的 5 + 2 * 3,因为在语法定义中,乘除是放在加减前面,所以会匹配先匹配乘法。解析树为:
如果在语法中修改乘除和加减的位置,同一个表达式,解析树为:
2)通过 Generate ANTLR Recognizer 自动生成源码,并拷贝到项目;
3)编写Visiter类,进行算式运算,代码如下:
package com.jingai.anltr.calculator;public class MyCalculatorVisitor extends CalculatorBaseVisitor<Integer> {/*** 加减运算*/@Overridepublic Integer visitAddSub(CalculatorParser.AddSubContext ctx) {// 获取加减运算左右的数字Integer left = visit(ctx.expr(0));Integer right = visit(ctx.expr(1));// 根据计算类型,执行相应计算,并返回计算结果if(ctx.op.getType() == CalculatorParser.ADD) {return left + right;}return left - right;}/*** 乘除运算*/@Overridepublic Integer visitMulDiv(CalculatorParser.MulDivContext ctx) {// 获取乘除运算左右的数字Integer left = visit(ctx.expr(0));Integer right = visit(ctx.expr(1));if(ctx.op.getType() == CalculatorParser.MUL) {return left * right;}return left / right;}@Overridepublic Integer visitInt(CalculatorParser.IntContext ctx) {return Integer.parseInt(ctx.INT().getText());}@Overridepublic Integer visitBrackets(CalculatorParser.BracketsContext ctx) {return visit(ctx.expr());}
}
4)编写测试用例,代码如下:
package com.jingai.anltr.calculator;import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;public class CalculatorTest {public static void main(String[] args) {CalculatorLexer lexer = new CalculatorLexer(CharStreams.fromString("12-(3*3)+8-4/2"));CommonTokenStream tokens = new CommonTokenStream(lexer);CalculatorParser parser = new CalculatorParser(tokens);CalculatorParser.ExprContext tree = parser.expr();MyCalculatorVisitor visitor = new MyCalculatorVisitor();System.out.println(visitor.visit(tree));}
}
执行结果如下:

其他语法
1)通过@parser::members{},添加 java 代码,该代码会自动添加到Parser解析器中;
2)使用locals,定义局部变量,添加 java 代码,对应代码会自动添加到Parser解析器对应的标签解析中;
3)默认情况下,Antlr 从左到右结合运算符进行解析,对于特殊情况,需要从右到左的,可以使用assoc手动指定,示例如下:
expr : expr '^'<assoc=right> expr // 运算符是右结合的| INT;
4)支持语义谓词添加,示例如下:
// 示例一
group : INT sequence[$INT.int]; // INT为int类型sequence[int n] locals [int i = 1;] // 在自动生成的Parser类的sequence()方法中,会添加 int n 的参数: ({$i <= $n}? INT {$i++;})* ;INT : [0-9]+ ;// 示例二
predicates: expression predicate[$expression.ctx]? // expression的ctx为ParserRuleContext类型;predicate[ParserRuleContext value] : // 在自动生成的Parser类的predicate()方法中,会添加ParserRuleContext value的参数expression: STR;
小结
本篇分析到这里,以下做一个小结:
1)Antlr 用于按照编写的语法规则,解析文本字符串。语法支持正则表达式规则;
先将文本按词进行分解,而后通过语法进行匹配分析。
2)通过 ANTLR Preview,可以预留解析树;
3)通过 Generate ANTLR Recognizer,可以自动生成词法分析器和语法分析器;
在语法中,可以按照特定格式添加对应语言的代码,代码将自动添加在Parser语法分析器中。(也可在自动生成后的Parser语法分析器代码中进行修改,该种方式不太规范)
4)通过继承自动生成的Visiter或Listener,可以对解析后的信息进行提取即相应处理;
关于本篇内容你有什么自己的想法或独到见解,欢迎在评论区一起交流探讨下吧。
参考:
《ANTLR 4简明教程》 - 书栈网 · BookStack
GitHub - antlr/grammars-v4: Grammars written for ANTLR v4; expectation that the grammars are free of actions.
相关文章:
Antlr的使用
概念 ANTLR(ANother Tool for Language Recognition)是一个强大的解析器生成工具,用于读取、处理、执行或翻译结构化文本或二进制文件。ANTLR通过定义文法(grammar)来识别、构建和访问语言中的元素。 ANTLR为包括Jav…...
HealChat心理大语言模型 丨OPENAIGC开发者大赛高校组AI创作力奖
在第二届拯救者杯OPENAIGC开发者大赛中,涌现出一批技术突出、创意卓越的作品。为了让这些优秀项目被更多人看到,我们特意开设了优秀作品报道专栏,旨在展示其独特之处和开发者的精彩故事。 无论您是技术专家还是爱好者,希望能带给…...
PyQt5整合爬虫制作图片爬取器-幽络源
前言 本篇教程适合对Python爬虫和Python软件制作感兴趣的小伙伴阅读,看完本篇教程,你将能更深入了解PyQt5与实际功能的整合方式。 1.设计界面 首先在pycharm中创建一个新目录,这里我建立的目录名为爬图片,然后按如图打开Qt设计…...
DC00023基于jsp+MySQL新生报到管理系统
1、项目功能演示 DC00023基于jsp新生报到管理系统java webMySQL新生管理系统 2、项目功能描述 基于jspMySQL新生报到管理系统项目分为学生、辅导员、财务处和系统管理员四个角色。 2.1 学生功能 1、系统登录 2、校园新闻、报到流程、学校简介、在线留言、校园风光、入校须知…...
AdaptIoT——制造业中使用因果关系的自我标签系统
0.概述 论文地址:https://arxiv.org/abs/2404.05976 在许多制造应用中,机器学习(ML)已被证明可以提高生产率。针对制造业应用提出了一些软件和工业物联网(IIoT)系统,以接收这些 ML 应用。最近&…...
代码随想录算法训练营Day15
654.最大二叉树 力扣题目链接:. - 力扣(LeetCode) 前序递归、循环不变量 class Solution {public TreeNode constructMaximumBinaryTree(int[] nums) {return findmax(nums,0,nums.length);}public TreeNode findmax(int[] nums,int lefti…...
Thinkphp/Laravel旅游景区预约系统的设计与实现
目录 技术栈和环境说明具体实现截图设计思路关键技术课题的重点和难点:框架介绍数据访问方式PHP核心代码部分展示代码目录结构解析系统测试详细视频演示源码获取 技术栈和环境说明 采用PHP语言开发,开发环境为phpstudy 开发工具notepad并使用MYSQL数据库…...
SpringCloud学习记录|day1
学习材料 2024最新SpringCloud微服务开发与实战,java黑马商城项目微服务实战开发(涵盖MybatisPlus、Docker、MQ、ES、Redis高级等) 学redis讲到微服务就停了,nginx也是。 所以嘛,我终于来到微服务了。 复习MyBatisP…...
Elasticsearch讲解
1.Elasticsearch基本知识 1.基本认识和安装 Elasticsearch是由elastic公司开发的一套搜索引擎技术,它是elastic技术栈中的一部分。完整的技术栈包括: Elasticsearch:用于数据存储、计算和搜索 Logstash/Beats:用于数据收集 Kib…...
Linux嵌入式有发展吗,以及对uboot,kernel,rootfs的领悟
工作多年后,对uboot,kernel,rootfs的领悟,总结 上大学时,51单片机,正点原子的stm32,linux arm开发。对uboot,kernel,rootfs的理解云里雾里,感觉自己很懂了 其…...
基于Springboot+Vue的公寓管理系统(含源码+数据库)
1.开发环境 开发系统:Windows10/11 架构模式:MVC/前后端分离 JDK版本: Java JDK1.8 开发工具:IDEA 数据库版本: mysql5.7或8.0 数据库可视化工具: navicat 服务器: SpringBoot自带 apache tomcat 主要技术: Java,Springboot,mybatis,mysql,vue 2.视频演示地址 3.功能 该系统…...
多功能声学气膜馆:承载梦想与希望的舞台—轻空间
在9月29日上午,苏州大学应用技术学院的2024级新生开学典礼暨开学第一课在轻空间建造的多功能声学气膜馆内盛大举行。这一盛典不仅见证了2849名新生的入学,也展示了气膜馆的独特魅力与优越功能。 卓越的声学表现 声学气膜馆采用高性能材料,确保…...
【线程】线程池
线程池通过一个线程安全的阻塞任务队列加上一个或一个以上的线程实现,线程池中的线程可以从阻塞队列中获取任务进行任务处理,当线程都处于繁忙状态时可以将任务加入阻塞队列中,等到其它的线程空闲后进行处理。 线程池作用: 1.降…...
输出 / 目录下所有目录文件的大小并排序
使用 du -sh /* 输出 / 目录下所有的目录总大小,看下效果: [rootlocalhost ~]# du -sh /* 0 /bin 110M /boot 0 /dev 32M /etc 12K /home 0 /lib 0 /lib64 0 /media 0 /mnt 0 /opt du: cannot access ‘/proc/2731/task/2731/fd/4’: No such file or …...
【hot100-java】【编辑距离】
多维dp篇 class Solution {public int minDistance(String word1, String word2) {char [] sword1.toCharArray();char [] tword2.toCharArray();int ns.length;int mt.length;int [][] fnew int[n1][m1];for (int j1;j<m;j){f[0][j]j;}for(int i0;i<n;i){f[i1][0]i1;for…...
随手记:牛回速归
上周-国庆前:牛回速归 国庆:小心被套住 国庆后:一片迷茫 总结:要是上周到国庆前的基本都能捞到,后面情况不好说 后续持续更新...
UI设计师面试整理-设计过程和方法论
在UI设计师面试中,清晰地阐述你的设计过程和方法论是至关重要的。这不仅可以展示你的专业技能和设计思维,也能让面试官看到你是如何解决实际设计问题的。以下是一个全面的UI设计过程和常用方法论的概述,你可以根据你的经验进行相应调整。 1. 设计过程 a. 研究与发现阶段(Re…...
ACM 纳新每日一题 4329: 三进制
首先我们要学习的是数制转化 这里我找了一篇博客https://blog.csdn.net/weixin_53564801/article/details/123665194 一定要注意0需要单独特判一下,这个点尤其重要 然后关于这道题可以使用递归来实现,如下: 递归的代码比较简洁,但…...
WebGIS包括哪些技术栈?怎么学习?
WebGIS,其实是利用Web开发技术结合地理信息系统(GIS)的产物,它是一种通过Internet实现GIS交互操作和服务的最佳途径。 WebGIS通过图形化界面直观地呈现地理信息和特定数据,具有可扩展性和跨平台性。 它提供交互性&am…...
无人机之集群控制及应用
一、无人机集群控制 无人机集群控制是指通过先进的通信、导航和控制算法,实现多架无人机之间的协同、协调和高效的任务执行。其关键技术包括: 通信技术:实现无人机之间的实时数据传输和共享,确保集群控制的准确性和稳定性。 路径…...
深度学习在微纳光子学中的应用
深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向: 逆向设计 通过神经网络快速预测微纳结构的光学响应,替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...
【Python】 -- 趣味代码 - 小恐龙游戏
文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...
SkyWalking 10.2.0 SWCK 配置过程
SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外,K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案,全安装在K8S群集中。 具体可参…...
TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...
Day131 | 灵神 | 回溯算法 | 子集型 子集
Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣(LeetCode) 思路: 笔者写过很多次这道题了,不想写题解了,大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...
【磁盘】每天掌握一个Linux命令 - iostat
目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat(I/O Statistics)是Linux系统下用于监视系统输入输出设备和CPU使…...
如何为服务器生成TLS证书
TLS(Transport Layer Security)证书是确保网络通信安全的重要手段,它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书,可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...
微信小程序云开发平台MySQL的连接方式
注:微信小程序云开发平台指的是腾讯云开发 先给结论:微信小程序云开发平台的MySQL,无法通过获取数据库连接信息的方式进行连接,连接只能通过云开发的SDK连接,具体要参考官方文档: 为什么? 因为…...
【Linux】Linux安装并配置RabbitMQ
目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的,需要先安…...
前端调试HTTP状态码
1xx(信息类状态码) 这类状态码表示临时响应,需要客户端继续处理请求。 100 Continue 服务器已收到请求的初始部分,客户端应继续发送剩余部分。 2xx(成功类状态码) 表示请求已成功被服务器接收、理解并处…...



