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…...

无人机之集群控制及应用
一、无人机集群控制 无人机集群控制是指通过先进的通信、导航和控制算法,实现多架无人机之间的协同、协调和高效的任务执行。其关键技术包括: 通信技术:实现无人机之间的实时数据传输和共享,确保集群控制的准确性和稳定性。 路径…...

前端导出带有合并单元格的列表
// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...
【论文笔记】若干矿井粉尘检测算法概述
总的来说,传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度,通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...
C++ 基础特性深度解析
目录 引言 一、命名空间(namespace) C 中的命名空间 与 C 语言的对比 二、缺省参数 C 中的缺省参数 与 C 语言的对比 三、引用(reference) C 中的引用 与 C 语言的对比 四、inline(内联函数…...
[Java恶补day16] 238.除自身以外数组的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O(n) 时间复杂度…...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...

【开发技术】.Net使用FFmpeg视频特定帧上绘制内容
目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法,当前调用一个医疗行业的AI识别算法后返回…...

USB Over IP专用硬件的5个特点
USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中,从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备(如专用硬件设备),从而消除了直接物理连接的需要。USB over IP的…...

回溯算法学习
一、电话号码的字母组合 import java.util.ArrayList; import java.util.List;import javax.management.loading.PrivateClassLoader;public class letterCombinations {private static final String[] KEYPAD {"", //0"", //1"abc", //2"…...

【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)
本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...

基于IDIG-GAN的小样本电机轴承故障诊断
目录 🔍 核心问题 一、IDIG-GAN模型原理 1. 整体架构 2. 核心创新点 (1) 梯度归一化(Gradient Normalization) (2) 判别器梯度间隙正则化(Discriminator Gradient Gap Regularization) (3) 自注意力机制(Self-Attention) 3. 完整损失函数 二…...