ANTLR4规则解析生成器(一):入门

文章目录

      • 1 什么是ANTLR4
      • 2 为什么需要ANTLR4
      • 3 环境搭建
      • 4 官方示例
        • 4.1 编写语法规则文件
        • 4.2 生成语法解析器
        • 4.3 基于SDK实现逻辑
      • 5 总结

1 什么是ANTLR4

ANTLR是ANother Tool for Language Recognition的缩写,它是一个强大的用于读取、处理、执行和翻译结构化文本或二进制文件的语法分析器生成器,广泛用于构建语言、工具和框架,通过语法描述规则,它能够生成一个可以遍历解析树的解析器。ANTLR4是ANTLR的第4个版本。

2 为什么需要ANTLR4

以一个计算器的例子来说明,当我们需要开发一个计算器程序时,第一步就是要确认支持的边界,也就是要确认支持哪些运算,例如,假设只需要支持整数的四则运算,且不支持括号,也就是只支持1+2*3等简单的计算。然后就可以开始开发,开发的重点就变成对算式的解析,还需要处理运算符的优先级。在通常的书籍中,会基于栈和队列实现,并且需要自行处理运算符的优先级:复杂计算器——四则运算表达式求值(中缀转后缀表达式。而使用ANTLR4就可以将算式的解析和实现分离,ANTLR4会将算式解析为语法树,然后提供遍历的机制去实现运算,因此,我们的代码就只需要实现运算即可。

简单来说,ANTLR4就是一个生成词法分析器和语法分析器的生成器,能够解析文本和二进制,解析后生成语法树,然后基于不同语言的SDK遍历该语法树,实现对应的逻辑。

使用ANTLR4通常分成三步:

  • 编写语法规则文件(规则文件以g4为后缀),在规则文件中使用自顶向下的形式描述要解析的语法的格式
  • 使用antlr4将规则文件转换成对应语言的语法解析代码
  • 使用对应语言的SDK提供的函数,遍历语法树

3 环境搭建

  • 安装java:建议安装比较高的版本,这里安装的是jdk17
  • 安装虚拟环境:pip3 install virtualenv
  • 创建虚拟环境并进入:virtualenv myenv && . myenv/bin/activate
  • 安装antlr4:pip install antlr4-tools
  • 安装对应语言的运行时库:对于python而言,只支持python3,安装antlr4-python3-runtime

这里面主要要注意的就是java的版本,不能用1.6或者1.8等比较低的版本。

如果使用vscode进行开发,可以安装ANTLR4 grammar syntax support插件;如果使用pycharm开发,可以安装ANTLR v4插件。

4 官方示例

4.1 编写语法规则文件
grammar expr;
prog:   (expr NEWLINE)* ;
expr:   expr ('*'|'/') expr|   expr ('+'|'-') expr|   INT|   '(' expr ')';
NEWLINE : [\r\n]+ ;
INT     : [0-9]+ ;

语法规则文件是基于正则表达式并且从上到下的语法描述文件,很类似于编译原理里面的词法分析和语法分析。

  • 除了grammer所在的行,每个分号结尾的部分都是描述一个规则
  • grammer:声明一个语法的名称,名称为expr,该名称与文件名一致
  • prog:整个规则的总体的描述,prog在这里也只是个名字,没有什么特殊含义,该规则的含义是,若干个(expr NEWLINE)
  • expr:描述prog中的expr表达式,它是一种递归的形式,表达式有4种情况:表达式之间的乘除、表达式之间的加减、INT、表达式可以使用括号
  • NEWLINE:若干换行符
  • INT:若干数字组成

因此,上面就是一个计算器的语法描述文件,该计算器只支持整数的四则运算,并且可以通过括号调整优先级。

4.2 生成语法解析器

将上述语法文件保存为expr.g4,然后使用antlr4工具生成语法解析器:

antlr4 -Dlanguage=Python3 expr.g4

就会在当前目录下生成一些python程序和文件:

  • exprLexer.py:词法分析
  • exprListener.py:继承自ParseTreeListener的空类exprListener
  • exprParser.py:语法分析
4.3 基于SDK实现逻辑

基于上面生成的类,然后结合antlr4提供的api,就可以得到antlr4为我们生成的AST(抽象语法树),相当于我们只使用antlr4为我们解析表达式,但是具体的计算逻辑是需要编写代码去遍历AST。antlr4提供了两种方式遍历AST,一种是listener,另一种是visitor,默认是listener。

例如,当给定表达式为1+2*3时,会生成如下的一棵AST树:

在这里插入图片描述

# Listener.py
from grammer.exprListener import exprListener
from grammer.exprParser import exprParserclass Listener(exprListener):def __init__(self):self.result = {}# Enter a parse tree produced by exprParser#prog.def enterProg(self, ctx:exprParser.ProgContext):pass# Exit a parse tree produced by exprParser#prog.def exitProg(self, ctx:exprParser.ProgContext):pass# Enter a parse tree produced by exprParser#expr.def enterExpr(self, ctx:exprParser.ExprContext):pass# Exit a parse tree produced by exprParser#expr.def exitExpr(self, ctx:exprParser.ExprContext):if ctx.getChildCount() == 3:if ctx.getChild(0).getText() == "(":self.result[ctx.getText()] = self.result[ctx.getChild(1).getText()]else:opc = ctx.getChild(1).getText()v1 = self.result[ctx.getChild(0).getText()]v2 = self.result[ctx.getChild(2).getText()]if opc == "+":self.result[ctx.getText()] = v1 + v2elif opc == "-":self.result[ctx.getText()] = v1 - v2elif opc == "*":self.result[ctx.getText()] = v1 * v2elif opc == "/":self.result[ctx.getText()] = v1 / v2else:ctx.result[ctx.getText()] = 0elif ctx.getChildCount() == 2:opc = ctx.getChild(0).getText()if opc == "+":v = self.result[ctx.getChild(1).getText()]self.result[ctx.getText()] = velif opc == "-":v = self.result[ctx.getChild(1).getText()]self.result[ctx.getText()] = - velif ctx.getChildCount() == 1:self.result[ctx.getText()] = int(ctx.getChild(0).getText())

继承exprListener创建我们自己的Listener,需要基于该Listener类遍历生成的AST,在这里只修改了exitExpr函数,从字面意思理解,该函数就是在遍历AST时离开某个节点时执行的函数,此时可以根据当前节点的孩子的个数执行不同的计算逻辑。

from antlr4 import CommonTokenStream
from antlr4 import ParseTreeWalker
from antlr4.InputStream import InputStream
from antlr4.Token import CommonTokenfrom grammer.exprParser import exprParser
from grammer.exprLexer import exprLexer
from Listener import Listenerif __name__ == '__main__':input_stream = InputStream("1+2*3\n")lexer = exprLexer(input_stream)token_stream = CommonTokenStream(lexer)parser = exprParser(token_stream)tree = parser.prog()listener = Listener()walker = ParseTreeWalker()walker.walk(listener, tree)print(listener.result)

5 总结

在实现一种语言或者规则时,首先需要解析语言或者规则,然后再对其中的单词或者语句进行处理,因此,在实际开发过程中,需要对输入进行分割然后再分析语义,而通过antlr4,可以自定义语言或者规则的构成,然后就可以通过antlr4的库得到一个AST的树,再利用antlr4的api遍历该树实现其他的业务逻辑,因此,基于antlr4可以简化我们的程序,帮助实现词法和语法的分析。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/668938.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据库性能监控 ,数据库可用性监控 #mysql##oracle##SQLserver#_

当谈到监控数据库的性能和可用性时,涉及的方面多种多样。数据库是许多组织业务中的关键组成部分,因此确保其高性能和不间断可用性对于业务的成功至关重要。因此建立一个全面的监控系统至关重要。让我们深入探讨数据库性能和可用性监控的各个方面。 数据…

深刻理解树状数组--树状数组构造定义与动态维护区间和的合理性证明

文章目录 一.树状数组概览二.树状数组构造定义lowbit运算树状数组的结点值的定义树状数组结点层次的定义树状数组父子结点关系定义 三.关于树状数组结构的重要证明引理1引理2树状数组模板题 一.树状数组概览 树状数组的下标从1开始标识,其物理结构是线性表,逻辑结构是一颗多叉…

C语言——O/动态内存管理

目录 一、为什么要有动态内存分配 二、malloc 和 free 1、malloc 2、free 三、calloc和realloc 1、calloc 2、realloc 四、常见的动态内存的错误 1、对NULL指针的解引用操作 2、对动态开辟空间的越界访问 3、对非动态开辟内存使用 free 释放 4、使用free释放一块动…

详细关于如何解决mfc140.dll丢失的步骤,有效修复mfc140.dll文件丢失的问题。

mfc140.dll文件是Microsoft Visual Studio 2015程序集之一,它包含用于支持多种功能的代码和库。当这个mfc140.dll文件丢失时,可能会导致相关程序运行出错甚至无法运行。很多用户可能会遇到mfc140.dll丢失的问题,但是这并不是不可解决的困难。…

Linux中判断文件系统的方法

文章目录 Linux中判断文件系统的方法1.使用mount命令2.使用blkid命令3.使用file命令4.使用fstab文件5.使用df命令(这个用的比较多)6.使用fsck命令7.使用lsblk命令(推荐-简单好用) Linux中判断文件系统的方法 1.使用mount命令 # 这样查看的只有已经挂载…

浅谈连续逆F类的基础理论-波形、最佳阻抗、输出功率

浅谈连续逆F类的基础理论 各种逆类型的功放好像都少引人关注,因为很多人学完正的连续B/J类和连续F类,想当然的类推到了连续逆F类上面去。貌似连续逆F类就是连续F类的电压电流交换一下而已,无需额外的注意,实际并非那么简单的。 浅…

2024年全球手机市场复苏 传音打响出海品牌进阶之战

2024年智能手机将迎来新一轮“增长季”。根据市场研究机构TechInsights的最新预测,2024年全球智能手机市场将恢复低个位数的增长。对广大手机厂商来说,这无疑是个好消息,但如何在逐渐回暖却竞争激烈的市场中站稳脚跟就需要他们“各显神通”了…

邮件群发软件有效果吗?邮箱如何群发邮件?

邮件群发软件如何选择比较好?推荐的EDM邮件批发系统? 许多人开始寻找提高工作效率和推广业务的方法。其中,一种备受关注的工具是邮件群发软件。那么,究竟邮件群发软件是否有效呢?蜂邮EDM将深入探讨这个问题&#xff0…

如何在Vue应用程序中使用Vue-Router来实现路由嵌套动画效果

Vue-Router是Vue.js官方的路由管理插件,可以帮助我们轻松管理应用程序的路由。除了基本的路由功能外,Vue-Router还允许我们在切换路由时添加动画效果,提升用户体验。本文将介绍如何使用Vue-Router来实现路由嵌套动画效果,并提供具…

nginx初学者指南

一、启动、停止和重新加载配置 前提:先要启动nginx 在Windows上启动nginx的步骤如下: 1. 下载并安装nginx。可以从nginx官网下载适合自己操作系统的版本,一般是zip压缩包,解压到指定目录中。 2. 进入nginx的安装目录&#xff…

基于Java (spring-boot)的实验室管理系统

一、项目介绍 基于Java (spring-boot)的交通管理系统功能:注册登录、个人信息管理、驾驶证业务类型管理、机动车业务类型管理、新闻类型管理、违法处理业务类型管理、驾驶证业务管理、机动车业务管理、新闻管理、违法处理业务管理、用户管理。 二、作品包含 ​ 三、…

(源码版)2024美国大学生数学建模E题财产保险的可持续模型详解思路+具体代码季节性时序预测SARIMA天气预测建模

本篇文章是: 2024美国大学生数学建模E题财产保险的可持续模型详解思路+具体代码季节性时序预测SARIMA天气预测建模的源码版本,包含具体建模代码到生成模型步骤。那么废话不多说直接开始展示建模过程建模: 数据预处理 之前我给大家提供的一年的风暴数据是远远不够的,要做时…

Redis核心技术与实战【学习笔记】 - 14.Redis 旁路缓存的工作原理及如何选择应用系统的缓存类型

概述 我们知道,Redis 提供了高性能的数据存取功能,广泛应用在缓存场景中,既可以提升业务的响应速度,又可以避免把高并发的请求发送到数据库。 如果 Redis 做缓存时出现了问题,比如说缓存失效,那么&#x…

C++进阶--搜索二叉树

概念 搜索二叉树是一种特殊的二叉树,其具有以下特点: 1.对于每个结点,它的左子树中的所有节点的值都小于该节点的值,而右子树中的所有节点的值都大于该节点的值。 2.左子树和右子树都是搜索二叉树。 这个 特性使得搜索二叉树可…

2024初始Spring(并使用idea创建springweb项目)

前言 spring呢,以前一直是简单的了解,并没有利用空闲时间去进行对应的深入的学习,今天呢原本是打算好好学的,然后后来呢感觉还是太早了接触,打算把前面知识在过一编之后再开始 Spring介绍 Spring | Home 大家想要访…

SQL必知必会笔记(13~16章)

第十三章 创建高级联结 1、别名:缩短SQL语句,允许在一条Select语句中多次使用相同的表 SELECT A.Name, A.Maths, A.English FROM transcript AS A INNER JOIN student_id As B ON A.Name B.Name; 2、Oracle数据库中没有AS关键字,直接在表名或…

英码科技携手昇腾共建算力底座:推出EA500I超强AI处理能力边缘计算盒子!

在数字经济浪潮中,算力已成为不可或缺的驱动力,为各行各业的数字化转型提供了强大的推动力。面对多元化和供需不平衡的挑战,需要实现从理论架构到软硬件实现的质的飞跃,以满足持续增长的算力需求,华为昇腾在这一方面展…

VS Code+Live Server插件开发游戏并结合内网穿透分享好友异地访问

文章目录 前言1. 编写MENJA小游戏2. 安装cpolar内网穿透3. 配置MENJA小游戏公网访问地址4. 实现公网访问MENJA小游戏5. 固定MENJA小游戏公网地址 前言 本篇教程,我们将通过VS Code实现远程开发MENJA小游戏,并通过cpolar内网穿透发布到公网,分…

人脸识别基本流程

人脸识别一般包括:人脸检测、人脸对齐、人脸特征提取和人脸比对四个步骤 人脸检测:检测到人脸的位置人脸对齐:同一个人采集到的不同图像可能呈现出不同的姿态和表情等,这种情况是不利于人脸特征提取的。所以有必要将人脸图像都变…

ubuntu系统下c++ cmakelist vscode debug(带传参的debug)的详细示例

c和cmake的debug,网上很多都需要配置launch.json,cpp.json啥的,记不住也太复杂了,我这里使用cmake插件带有的设置,各位可以看一看啊✌(不知不觉,竟然了解了vscode中配置文件的生效逻辑🤣) 克隆…