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

别再手动解析字符串了!用ANTLR4在IDEA里快速搞定一个四则运算计算器(附完整.g4文件)

告别手写解析器用ANTLR4在IDEA中构建智能计算器的实战指南每当需要处理复杂文本解析时开发者们往往陷入手写递归下降解析器或调试晦涩正则表达式的泥潭。这种低效的开发方式不仅耗时耗力还难以维护和扩展。想象一下当你需要解析配置文件中的条件表达式、处理自定义查询语法或实现领域特定语言(DSL)时如果能像搭积木一样快速构建解析器会是怎样的体验ANTLR4正是为解决这类问题而生的利器。作为当前最强大的解析器生成工具它已经成功应用于Hive、Spark、Elasticsearch等知名项目中。不同于传统的手工编写解析器ANTLR4允许开发者通过声明式的语法规则描述语言结构自动生成高效可靠的解析代码。这种描述即实现的方式将我们从繁琐的解析细节中解放出来专注于业务逻辑本身。1. 开发环境准备与ANTLR4插件配置在开始构建计算器之前我们需要搭建高效的开发环境。IntelliJ IDEA作为Java生态中最智能的IDE配合ANTLR4插件可以带来极佳的开发体验。首先确保已安装JDK 8或更高版本然后按以下步骤操作安装ANTLR4插件打开IDEA进入File Settings Plugins搜索ANTLR v4并安装重启IDEA使插件生效创建Maven项目mvn archetype:generate -DgroupIdcom.example -DartifactIdantlr-calculator -DarchetypeArtifactIdmaven-archetype-quickstart -DinteractiveModefalse添加ANTLR4运行时依赖 在pom.xml中添加dependencies dependency groupIdorg.antlr/groupId artifactIdantlr4-runtime/artifactId version4.10.1/version /dependency /dependencies提示建议同时配置ANTLR4工具作为Maven插件这样可以在构建时自动生成解析器代码。在pom.xml的build部分添加antlr4-maven-plugin配置。安装完成后我们可以在IDEA中右键点击.g4文件使用Configure ANTLR设置代码生成选项如输出目录、包名等。ANTLR插件最强大的功能是其实时预览功能允许我们在编写语法规则时立即看到解析结果极大提升了开发效率。2. 设计计算器语法规则ANTLR4的核心在于语法规则的定义。我们将创建一个完整的四则运算计算器支持加减乘除、括号和浮点数运算。新建文件Arithmetic.g4开始设计语法规则。2.1 基础语法结构设计grammar Arithmetic; /* 解析入口表示一个完整的表达式 */ parse : expr EOF ; /* 表达式规则处理加减法 */ expr : term ((|-) term)* ; /* 项规则处理乘除法 */ term : factor ((*|/) factor)* ; /* 因子规则处理数字和括号表达式 */ factor : NUMBER | ( expr ) ;这个结构体现了运算符优先级括号内的表达式优先级最高其次是乘除法最后是加减法。ANTLR4会自动处理左递归问题让我们可以自然地表达这种优先级关系。2.2 词法规则定义词法规则定义了如何将输入文本转换为标记(token)/* 词法规则 */ NUMBER : DIGIT (. DIGIT)? ; DIGIT : [0-9] ; WHITESPACE : [ \t\n\r] - skip ;这里定义了三种词法规则NUMBER匹配整数或浮点数DIGIT匹配单个数字字符WHITESPACE匹配空白字符并跳过注意ANTLR4的词法规则有严格的匹配顺序。当多个规则可以匹配相同输入时先定义的规则优先。因此通常将更具体的规则放在前面。2.3 使用ANTLR Preview实时调试ANTLR插件的实时预览功能是开发过程中的利器。在编写语法规则时右键点击.g4文件选择Test Rule parse在ANTLR Preview窗口输入测试表达式如(12)*3-4即时查看生成的语法树这个功能让我们可以快速验证语法规则是否正确发现并修复歧义或错误。例如输入12*3时应该看到乘法节点在加法节点下方表明乘法的优先级更高。3. 实现表达式求值器生成解析器代码后我们需要实现表达式的求值逻辑。ANTLR4生成的解析器会构建语法分析树(Parse Tree)我们需要通过遍历这棵树来计算表达式值。3.1 创建Visitor实现ANTLR4提供了两种遍历语法树的方式Visitor模式和Listener模式。我们选择Visitor模式因为它更符合表达式求值的自然流程。public class EvalVisitor extends ArithmeticBaseVisitorDouble { Override public Double visitParse(ArithmeticParser.ParseContext ctx) { return visit(ctx.expr()); } Override public Double visitExpr(ArithmeticParser.ExprContext ctx) { Double result visit(ctx.term(0)); for (int i 1; i ctx.term().size(); i) { String op ctx.getChild(2*i - 1).getText(); Double term visit(ctx.term(i)); switch (op) { case : result term; break; case -: result - term; break; } } return result; } Override public Double visitTerm(ArithmeticParser.TermContext ctx) { Double result visit(ctx.factor(0)); for (int i 1; i ctx.factor().size(); i) { String op ctx.getChild(2*i - 1).getText(); Double factor visit(ctx.factor(i)); switch (op) { case *: result * factor; break; case /: result / factor; break; } } return result; } Override public Double visitFactor(ArithmeticParser.FactorContext ctx) { if (ctx.NUMBER() ! null) { return Double.parseDouble(ctx.NUMBER().getText()); } else { return visit(ctx.expr()); } } }这个Visitor实现了对每种语法节点的访问逻辑按照运算优先级递归计算表达式的值。每个方法返回该节点对应的计算结果。3.2 处理语法错误健壮的解析器需要妥善处理错误输入。ANTLR4提供了默认的错误处理策略但我们也可以自定义public class ThrowingErrorListener extends BaseErrorListener { Override public void syntaxError(Recognizer?, ? recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) { throw new RuntimeException(语法错误 at line line : charPositionInLine msg); } } // 使用自定义错误处理器 ArithmeticLexer lexer new ArithmeticLexer(input); lexer.removeErrorListeners(); lexer.addErrorListener(new ThrowingErrorListener());3.3 构建测试用例完善的测试是保证计算器正确性的关键。我们可以使用JUnit编写测试类public class CalculatorTest { private double calculate(String expression) { ArithmeticLexer lexer new ArithmeticLexer(CharStreams.fromString(expression)); ArithmeticParser parser new ArithmeticParser(new CommonTokenStream(lexer)); ParseTree tree parser.parse(); return new EvalVisitor().visit(tree); } Test public void testBasicOperations() { assertEquals(3.0, calculate(12), 0.001); assertEquals(6.0, calculate(2*3), 0.001); assertEquals(1.5, calculate(3/2), 0.001); } Test public void testPrecedence() { assertEquals(7.0, calculate(12*3), 0.001); assertEquals(9.0, calculate((12)*3), 0.001); } Test(expected RuntimeException.class) public void testInvalidInput() { calculate(1); } }4. 高级功能扩展基础计算器完成后我们可以进一步扩展功能使其更加强大和实用。4.1 添加变量支持扩展语法规则以支持变量赋值和使用parse : (assignment ;)* expr EOF ; assignment : ID expr ; expr : term ((|-) term)* | ID ; // 添加ID词法规则 ID : [a-zA-Z_][a-zA-Z0-9_]* ;更新Visitor以处理变量public class EvalVisitor extends ArithmeticBaseVisitorDouble { private MapString, Double variables new HashMap(); Override public Double visitAssignment(ArithmeticParser.AssignmentContext ctx) { String id ctx.ID().getText(); Double value visit(ctx.expr()); variables.put(id, value); return value; } Override public Double visitExpr(ArithmeticParser.ExprContext ctx) { if (ctx.ID() ! null) { String id ctx.ID().getText(); if (!variables.containsKey(id)) { throw new RuntimeException(未知变量: id); } return variables.get(id); } // 原有逻辑... } }现在可以处理如a1;b2;ab这样的表达式了。4.2 添加数学函数支持扩展语法以支持函数调用factor : NUMBER | ( expr ) | ID ( expr ) ;更新VisitorOverride public Double visitFactor(ArithmeticParser.FactorContext ctx) { if (ctx.NUMBER() ! null) { return Double.parseDouble(ctx.NUMBER().getText()); } else if (ctx.expr() ! null) { if (ctx.ID() ! null) { // 函数调用 String func ctx.ID().getText(); Double arg visit(ctx.expr()); switch (func) { case sqrt: return Math.sqrt(arg); case sin: return Math.sin(arg); // 添加更多函数... default: throw new RuntimeException(未知函数: func); } } else { // 括号表达式 return visit(ctx.expr()); } } return null; }现在可以计算如sqrt(9)sin(0)这样的表达式了。4.3 性能优化技巧当处理大量或复杂表达式时可以考虑以下优化重用解析器实例ANTLR4的Lexer和Parser实例可以重用减少对象创建开销预编译常用表达式对于重复计算的相同表达式可以缓存语法树使用ThreadLocal在多线程环境中使用ThreadLocal存储解析器实例public class Calculator { private static final ThreadLocalArithmeticParser PARSER ThreadLocal.withInitial(() - { ArithmeticLexer lexer new ArithmeticLexer(CharStreams.fromString()); return new ArithmeticParser(new CommonTokenStream(lexer)); }); public static double calculate(String expression) { ArithmeticLexer lexer new ArithmeticLexer(CharStreams.fromString(expression)); ArithmeticParser parser PARSER.get(); parser.setTokenStream(new CommonTokenStream(lexer)); return new EvalVisitor().visit(parser.parse()); } }在实际项目中ANTLR4不仅能用于计算器还能处理各种复杂文本解析场景。我曾在一个数据分析项目中用它解析自定义的过滤表达式将原本需要一周开发的解析器缩短到一天完成且更易于维护和扩展。

相关文章:

别再手动解析字符串了!用ANTLR4在IDEA里快速搞定一个四则运算计算器(附完整.g4文件)

告别手写解析器:用ANTLR4在IDEA中构建智能计算器的实战指南 每当需要处理复杂文本解析时,开发者们往往陷入手写递归下降解析器或调试晦涩正则表达式的泥潭。这种低效的开发方式不仅耗时耗力,还难以维护和扩展。想象一下,当你需要解…...

5个高级技巧:在React应用中构建专业级JSON编辑器

5个高级技巧:在React应用中构建专业级JSON编辑器 【免费下载链接】jsoneditor-react react wrapper implementation for https://github.com/josdejong/jsoneditor 项目地址: https://gitcode.com/gh_mirrors/js/jsoneditor-react JSONEditor-React是一个基于…...

Phi-3.5-mini-instruct效果展示:表格数据理解+自然语言解释+趋势预测三合一输出

Phi-3.5-mini-instruct效果展示:表格数据理解自然语言解释趋势预测三合一输出 1. 模型简介 Phi-3.5-mini-instruct 是一个轻量级但功能强大的开放模型,属于Phi-3模型家族。这个模型基于高质量的训练数据构建,特别擅长处理推理密集型任务。它…...

Argo CD 实战:从零构建你的第一个 GitOps 应用

1. 为什么你需要Argo CD? 如果你正在管理Kubernetes应用,肯定遇到过这样的场景:每次代码变更后,都要手动执行kubectl apply来更新集群状态。这种操作不仅容易出错,还很难追踪谁在什么时候改了什么东西。我在实际项目中…...

《采购与招标商品详情页前端性能优化实战》

📄 《采购与招标商品详情页前端性能优化实战》背景:政府采购与招标平台的商品详情页实际上是招标公告详情页,包含公告信息、采购需求、资格要求、评分标准、投标文件、澄清公告、开标记录等多个复杂模块。页面特点是信息权威性强、格式标准化…...

别再纠结Flannel和Calico了!手把手教你根据业务场景选对K8s网络插件

别再纠结Flannel和Calico了!手把手教你根据业务场景选对K8s网络插件 当你在Kubernetes生产环境中面临网络插件选择时,Flannel和Calico这两个名字总会反复出现。就像站在十字路口的旅行者,左边是平坦快捷的柏油马路,右边是功能丰富…...

思源宋体TTF:下一代开源中文字体架构与应用范式

思源宋体TTF:下一代开源中文字体架构与应用范式 【免费下载链接】source-han-serif-ttf Source Han Serif TTF 项目地址: https://gitcode.com/gh_mirrors/so/source-han-serif-ttf Source Han Serif TTF作为Google与Adobe联合打造的开源Pan-CJK字体&#xf…...

API 网关怎么设计?一次讲清鉴权、路由、限流、灰度、日志与统一治理

API 网关怎么设计?一次讲清鉴权、路由、限流、灰度、日志与统一治理 大家好,我是一名有 4 年工作经验的 Java 后端开发。 微服务项目里,API 网关几乎是最容易被说成“就转发一下请求”的组件。 但真正做起来你会发现,很多稳定性、…...

终极指南:5分钟打造Windows便携Python开发环境的完整教程

终极指南:5分钟打造Windows便携Python开发环境的完整教程 【免费下载链接】winpython A free Python-distribution for Windows platform, including prebuilt packages for Scientific Python. 项目地址: https://gitcode.com/gh_mirrors/wi/winpython WinP…...

如何快速掌握网盘直链下载助手:八大网盘下载加速终极教程

如何快速掌握网盘直链下载助手:八大网盘下载加速终极教程 【免费下载链接】Online-disk-direct-link-download-assistant 一个基于 JavaScript 的网盘文件下载地址获取工具。基于【网盘直链下载助手】修改 ,支持 百度网盘 / 阿里云盘 / 中国移动云盘 / 天…...

如何打破B站缓存视频的格式枷锁?m4s-converter让你重获观看自由

如何打破B站缓存视频的格式枷锁?m4s-converter让你重获观看自由 【免费下载链接】m4s-converter 一个跨平台小工具,将bilibili缓存的m4s格式音视频文件合并成mp4 项目地址: https://gitcode.com/gh_mirrors/m4/m4s-converter 当你在B站缓存了珍贵…...

实战:用WRF-Chem V3.9.1.1模拟一次华北雾霾过程(附完整namelist配置与排放数据处理心得)

实战:用WRF-Chem V3.9.1.1模拟华北雾霾的完整技术指南 华北地区秋冬季雾霾问题一直是环境科学研究的重点。本文将基于WRF-Chem V3.9.1.1版本,详细介绍如何从零搭建一个针对华北雾霾事件的数值模拟系统。不同于基础教程,我们聚焦于实际科研项目…...

PLM、ERP、MES、CRM:解码企业数字化转型的四大核心引擎

1. 四大系统如何构建企业数字化闭环 想象一下你经营一家汽车制造厂。从设计师在电脑上画出一个新车型的3D模型,到最终客户开着这辆车离开4S店,整个过程就像一场接力赛。PLM、ERP、MES、CRM就是四位关键选手,他们传递的不是接力棒,…...

5分钟解决AutoCAD字体缺失问题:FontCenter智能字体管理插件完整指南

5分钟解决AutoCAD字体缺失问题:FontCenter智能字体管理插件完整指南 【免费下载链接】FontCenter AutoCAD自动管理字体插件 项目地址: https://gitcode.com/gh_mirrors/fo/FontCenter 还在为AutoCAD图纸中出现的问号和乱码文字而烦恼吗?FontCente…...

nli-MiniLM2-L6-H768效果展示:开源项目README与Issue描述的蕴含关系质量评估

nli-MiniLM2-L6-H768效果展示:开源项目README与Issue描述的蕴含关系质量评估 1. 模型核心能力解析 nli-MiniLM2-L6-H768是一个专为文本关系判断设计的轻量级自然语言推理模型。与常见的生成式AI不同,它的核心价值在于精准评估两段文本之间的逻辑关系。…...

南北阁 Nanbeige 4.1-3B 开源模型应用:政务问答系统本地化部署实践

南北阁 Nanbeige 4.1-3B 开源模型应用:政务问答系统本地化部署实践 想体验一个能流畅对话、还能“看见”它思考过程的本地AI助手吗?今天,我们就来聊聊如何将南北阁(Nanbeige)4.1-3B这个轻量又聪明的国产模型&#xff…...

15N70-ASEMI中大功率场景的能效新王者15N70

15N70-ASEMI中大功率场景的能效新王者15N70型号:16N65沟道:NPN品牌:ASEMI封装:TO-220F批号:最新导通内阻:0.6Ω漏源电流:15A漏源电压:700V引脚数量:3特性:N沟…...

从vector的push_back到emplace_back:聊聊C++11如何让容器操作更‘现代’

从vector的push_back到emplace_back:C11如何重构容器操作范式 当你在现代C代码库中看到emplace_back频繁出现时,这不仅仅是一个语法糖的替换——它标志着C语言设计哲学的一次重大转向。作为从C98/03时代走过来的开发者,理解这种变化背后的深层…...

网盘直链下载助手:八大主流网盘全速下载的完整解决方案

网盘直链下载助手:八大主流网盘全速下载的完整解决方案 【免费下载链接】Online-disk-direct-link-download-assistant 一个基于 JavaScript 的网盘文件下载地址获取工具。基于【网盘直链下载助手】修改 ,支持 百度网盘 / 阿里云盘 / 中国移动云盘 / 天翼…...

深度解析:如何通过软件协议逆向工程实现iOS 15-16 iCloud绕过

深度解析:如何通过软件协议逆向工程实现iOS 15-16 iCloud绕过 【免费下载链接】applera1n icloud bypass for ios 15-16 项目地址: https://gitcode.com/gh_mirrors/ap/applera1n 探索基于checkm8漏洞的硬件级解锁技术方案 在iOS设备安全领域,iC…...

LattePanda打造Steam Machine:硬件选型与系统优化指南

1. 从零打造一台LattePanda驱动的Steam Machine去年Valve宣布推迟新一代Steam Machine发布时,作为一名硬件改装爱好者,我决定自己动手复刻这个经典设备。经过三个月的设计和调试,这台基于LattePanda单板机的IOTA版本不仅完美运行Bazzite系统&…...

GPEN效果边界再定义:非正面人脸(俯仰角>30°)修复能力实测报告

GPEN效果边界再定义:非正面人脸(俯仰角>30)修复能力实测报告 1. 测试背景与目的 GPEN作为一款智能面部增强系统,在常规正面人像修复方面已经表现出色。但在实际应用中,我们经常会遇到各种非标准角度的人脸照片&am…...

LeaguePrank:5分钟打造你的专属英雄联盟形象

LeaguePrank:5分钟打造你的专属英雄联盟形象 【免费下载链接】LeaguePrank 项目地址: https://gitcode.com/gh_mirrors/le/LeaguePrank LeaguePrank是一款基于英雄联盟官方LCU API开发的游戏个性化工具,让你在不影响账号安全的前提下&#xff0c…...

避坑指南:用vprbs做SerDes链路仿真时,你的PRBS序列真的设对了吗?

高速SerDes验证实战:vprbs参数配置与PRBS序列生成深度解析 在高速串行接口(SerDes)验证领域,PRBS(伪随机二进制序列)测试堪称链路性能评估的"黄金标准"。作为芯片验证工程师,我们经常需要在Cadence仿真环境中使用analogLib库的vprb…...

Fastboot Enhance:让Android刷机从命令行到图形界面的革命性转变

Fastboot Enhance:让Android刷机从命令行到图形界面的革命性转变 【免费下载链接】FastbootEnhance A user-friendly Fastboot ToolBox & Payload Dumper for Windows 项目地址: https://gitcode.com/gh_mirrors/fa/FastbootEnhance Fastboot Enhance 是…...

混合系统设计:连续与离散的动态融合

1. 混合系统概述:时间与离散的桥梁混合系统(Hybrid Systems)是嵌入式系统设计中的关键建模框架,它巧妙地将两种看似对立的建模范式融为一体:基于时间的连续动态系统与基于离散事件的状态机模型。这种融合不是简单的叠加…...

从零开始构建水下机器人仿真环境:UUV Simulator实战指南

从零开始构建水下机器人仿真环境:UUV Simulator实战指南 【免费下载链接】uuv_simulator Gazebo/ROS packages for underwater robotics simulation 项目地址: https://gitcode.com/gh_mirrors/uu/uuv_simulator UUV Simulator是一个基于Gazebo和ROS的开源水…...

手把手教你配置RK3588单/双PMIC方案:从硬件选型到DTS避坑指南

RK3588电源方案实战指南:单双PMIC设计与DTS配置深度解析 当工程师拿到RK3588这颗性能怪兽时,电源设计往往是第一个需要攻克的堡垒。不同于普通SoC的简单供电需求,RK3588的异构多核架构(4xCortex-A76 4xCortex-A55 NPU GPU&…...

免费创建Windows虚拟游戏手柄:vJoy完整配置与实战指南

免费创建Windows虚拟游戏手柄:vJoy完整配置与实战指南 【免费下载链接】vJoy Virtual Joystick 项目地址: https://gitcode.com/gh_mirrors/vj/vJoy 想要在Windows电脑上体验专业游戏手柄的功能,但又不想投资昂贵的硬件设备?vJoy虚拟摇…...

SMUDebugTool:AMD Ryzen处理器调试的完整实用指南

SMUDebugTool:AMD Ryzen处理器调试的完整实用指南 【免费下载链接】SMUDebugTool A dedicated tool to help write/read various parameters of Ryzen-based systems, such as manual overclock, SMU, PCI, CPUID, MSR and Power Table. 项目地址: https://gitcod…...