ANTLR4 目标无关语法编写指南:用语义谓词、superClass 与 transformGrammar.py 实现一份语法多语言复用
ANTLR4 目标无关语法编写指南用语义谓词、superClass 与 transformGrammar.py 实现一份语法多语言复用【免费下载链接】antlr4ANTLR (ANother Tool for Language Recognition) is a powerful parser generator for reading, processing, executing, or translating structured text or binary files.项目地址: https://gitcode.com/gh_mirrors/an/antlr4本指南基于 ANTLR4 官方文档 doc/target-agnostic-grammars.md系统讲解如何编写目标无关语法target-agnostic grammars当语法需要借助语义谓词semantic predicates处理上下文相关语法时如何避免为每个目标语言 fork 一份语法文件而是通过基类superClass 文本改写脚本transformGrammar.py让同一份.g4语法同时产出 Java、C、Python、PHP 等多个目标的解析器。读完本文你将掌握谓词语言绑定的根源、五步式的目标无关写法、以及 ANTLR 工具链中superClass与tokenVocab选项的底层落地机制。为什么需要目标无关语法ANTLR4 的语义谓词semantic predicates形如{...}?是写在目标语言中的布尔表达式用于指示沿着被谓词守卫的解析路径继续是否有效。官方文档 doc/predicates.md 指出ANTLR 的总体决策策略是找出所有可行viable分支然后忽略那些谓词当前求值为 false 的分支若仍有多个可行分支则选择语法中先出现的那个。问题在于ANTLR 没有一种通用的谓词语言。谓词、action 中的代码必须用生成解析器的目标语言书写。这意味着同一个语法里一旦出现谓词它就直接绑定到了某种具体语言上要为多个目标Java、C、Python、PHP……维护多份近乎相同的语法文件fork 与合并的维护负担非常沉重。文档给出了两个典型场景来说明这种上下文相关需求场景一Fortran90 的列 1 注释Fortran90 中第 1 列以C开头的行是注释应被放入非默认的 token 流但如果C不在第 1 列则输入非法应当报错。这种位置决定语义的规则必须借助谓词判断c Hello World. c This is a syntax error because c does not start in column 1 program hello print *, Hello World! end场景二C# 的连续两个大于号C# 中两个既可以表示右移表达式也可以是带泛型嵌套类型声明的一部分。由于 ANTLR 的词法分析器不感知解析器上下文词法分析器只能把两个切成两个独立 token是否允许它们之间出现空格必须由谓词在具体上下文中裁决class Foo { void Func() { int x 1000 2; // syntax error if a space exists in the double greater-than sign } Dictionaryint, Listint mapping; // nested template declaration, valid }目标无关语法的总体思路既然谓词必须写目标语言又不想为每个目标 fork 语法ANTLR 官方给出的方案是语法文件本身保持中立把语言相关的差异收敛到两个可替换点基类将谓词的具体实现放进目标语言的基类*Base源文件中语法通过options { superClass...; }继承该基类语法内部只写一次方法调用。文本改写脚本由于不同目标语言对对象方法调用的写法不同this.、this-、$this-、self.用一个名为transformGrammar.py的 Python 脚本在生成解析器之前对.g4文件做机械化的字符串替换。这样.g4源语法只有一份针对不同目标只需运行不同的改写规则与生成命令即可。编写目标无关语法的分步指南原文档 doc/target-agnostic-grammars.md 给出了完整的分步规则下面逐条展开并补充细节。步骤 1拆分语法并声明tokenVocab将语法拆分为独立的词法语法lexer grammar与语法语法parser grammar然后在语法语法中添加options { tokenVocab...; }让两者共享同一套 token 类型编号。按 doc/grammars.md 的说明纯语法语法与纯词法语法的文件头分别是parser grammar Name; ...与lexer grammar Name; ...tokenVocab选项的语义见 doc/options.mdANTLR 在遇到 token 时会为它们分配类型编号而tokenVocab让语法分析器从某个词法语法生成的.tokens文件中读取既有的编号映射。工具链中的实现位于 tool/src/org/antlr/v4/tool/Grammar.java 的importTokensFromTokensFile()它读取tokenVocab选项值通过TokenVocabParser加载.tokens文件并把其中字符串字面量与 token 名逐一注册进当前语法public void importTokensFromTokensFile() { String vocab getOptionString(tokenVocab); if ( vocab!null ) { TokenVocabParser vparser new TokenVocabParser(this); MapString,Integer tokens vparser.load(); ... } }对应的集成测试可参考 tool-testsuite/test/org/antlr/v4/test/tool/TestCompositeGrammars.java 中的testTokensFileInOutputDirAndImportFileInSubdir它演示了lexer grammar MLexer与parser grammar MParser通过options {tokenVocabMLexer;}关联并分别用-o输出目录与-lib词法文件目录参数完成生成。同文件testImportedTokenVocabIgnoredWithWarningL386-L413还验证了被 import 的语法中声明的tokenVocab选项会被忽略并产生警告OPTIONS_IN_DELEGATE。步骤 2为目标语言编写包含谓词实现的基类创建目标特定的源码文件其中包含供词法/语法语法调用的方法谓词逻辑全部写在基类里。例如 C 目标需要Python3LexerBase.{cpp,h}与Python3ParserBase.{cpp,h}Python 目标则是Python3LexerBase.py与Python3ParserBase.py。这些文件与.g4语法同目录存放随后由superClass选项挂接到生成的识别器上。步骤 3用options { superClass...; }挂接基类在语法词法、语法均可中添加options { superClassPython3ParserBase; }superClass会把生成识别器的父类替换为指定基类从而让语法规则内对基类方法的调用可以被解析。其精确定义见 doc/options.md对于组合语法combined grammar该选项只作用于生成的解析器。文档中还演示了命令行传参方式$ antlr4 -DsuperClassXX Hi.g4 $ grep public class HiParser.java public class HiParser extends XX { $ grep public class HiLexer.java public class HiLexer extends Lexer {注意-D会覆盖语法文件内的同名选项。从源码层面看选项的合法性定义在 tool/src/org/antlr/v4/tool/Grammar.javaparserOptions集合包含了superClass、contextSuperClass、TokenLabelType、tokenVocab、language、accessLevel、exportMacro与caseInsensitive而doNotCopyOptionsToLexer明确列出了superClass、TokenLabelType、tokenVocab三项——这正是组合语法中superClass不影响词法分析器这一行为的工具实现。生成器一侧tool/src/org/antlr/v4/codegen/model/Recognizer.java 把superClass选项值包装为ActionText注入输出模型最终渲染进生成的识别器类声明。步骤 4语法内只写单次方法调用在语法中编写对基类方法的调用调用点必须统一使用this.前缀写法例如词法规则OPEN_PAREN : ( {this.openBrace();};动作代码受严格限制不得引用 ANTLR 属性、变量或类型不得出现分号作为语句分隔符不得包含任何控制流语句。之所以如此克制是为了让transformGrammar.py可以用纯文本替换的方式把this.改写成各目标的调用语法而不需要理解动作内部结构。步骤 5C/PHP 目标添加header占位注释对 C 与 PHP 这类需要显式包含/引入源码文件才能编译的目标在语法第一个规则之前放置一行占位注释等待脚本替换成真正的header命名 action// Insert here header for lexer include.以及// Insert here header for parser include.header是 ANTLR 的命名 action作用是把代码注入生成识别器类文件、类定义之前详见 doc/grammars.mdheader::lexer/header::parser则用于把 action 限定到词法或语法识别器。步骤 6编写transformGrammar.py做目标化改写新增一个名为transformGrammar.py的 Python 脚本在生成解析器之前运行把.g4语法中的占位写法改写为目标语言语法a) C把this.替换为this-b) PHP把this.替换为$this-c) Python把this.替换为self.、l.或p.——具体取决于 action/谓词在语法中的位置d) C把// Insert here header for lexer include.或 parser 版本替换为header::lexer {#include ...}或对应 parser 版本e) PHP把同样的占位注释替换为header::lexer {require ...}f) 最后在生成词法与语法解析器之前运行python transformGrammar.py *.g4。一个符合上述规则的最小脚本示意按目标分发、只做字符串替换#!/usr/bin/env python3 # transformGrammar.py —— 最小实现示意按目标语言改写 .g4 语法 import re import sys def transform_cpp(text): text text.replace(this., this-) text text.replace(// Insert here header for lexer include., header::lexer {#include \Python3LexerBase.h\}) text text.replace(// Insert here header for parser include., header::parser {#include \Python3ParserBase.h\}) return text def transform_php(text): text text.replace(this., $this-) text text.replace(// Insert here header for lexer include., header::lexer {require \Python3LexerBase.php\;}) text text.replace(// Insert here header for parser include., header::parser {require \Python3ParserBase.php\;}) return text def transform_python(text): # 按 action 所在位置选择 self./l./p.这里给出常用替换 text text.replace(this., self.) return text TARGETS {cpp: transform_cpp, php: transform_php, python: transform_python} if __name__ __main__: target sys.argv[1] # cpp / php / python for grammar in sys.argv[2:]: # 传入 *.g4 文件列表 with open(grammar, r) as f: text f.read() with open(grammar, w) as f: f.write(TARGETStarget)实际项目中一个常见做法是把改写逻辑做成幂等且可回归校验的比如用# !target: cpp之类的注释标记当前目标以确保同一份源语法在不同目标之间切换时不会互相污染。从目标无关语法到完整构建流程综合以上步骤一份语法支撑多目标的完整流程如下以 Python3 目标为例在源码目录放置Python3Lexer.g4词法语法与Python3Parser.g4语法语法后者声明options { tokenVocabPython3Lexer; superClassPython3ParserBase; }并在规则内以this.调用基类方法编写Python3LexerBase.py、Python3ParserBase.py把真正的谓词逻辑如列位置检查、上下文判断实现在基类方法中编写transformGrammar.py针对目标语言改写this.与 header 占位注释依次执行$ python transformGrammar.py python Python3Lexer.g4 Python3Parser.g4 $ antlr4 -DlanguagePython3 Python3Lexer.g4 $ antlr4 -DlanguagePython3 Python3Parser.g4将生成的词法/语法解析器与基类一起编译、运行。需要强调的是transformGrammar.py必须在 ANTLR 工具生成代码之前运行因为生成器读取的是改写后的.g4内容例如 C 目标需要先看到this-与header::lexer {#include ...}才能产出可编译代码。这也是原文档把运行python transformGrammar.py *.g4列为生成前置步骤的原因。注意事项与适用边界动作与谓词的位置影响可见性按 doc/predicates.md 的说明解析器中只有位于分支左边缘、且在 action 与 token 引用之前的谓词才可能参与分支预测visible predicates位于 action 之后的谓词在预测阶段会被忽略。词法规则中的谓词则通常放在规则右边缘且动作必须出现在谓词之后。基类仍按目标各写一份目标无关方案消除的是.g4语法的 fork并没有消除基类源码的目标语言实现——每个目标仍需维护对应的*Base文件但其体量远小于整份语法。动作代码的约束是硬性的this.之后必须是单次方法调用不能出现分号、控制流或 ANTLR 属性引用否则文本改写脚本无法可靠工作。组合语法中superClass只作用于解析器若词法分析器也需要自定义基类需将词法部分拆成独立的lexer grammar并在其中单独声明superClass参见 doc/options.md 与 tool/src/org/antlr/v4/tool/Grammar.java 的doNotCopyOptionsToLexer。.tokens文件的存放与查找tokenVocab通过-lib参数或语法文件所在目录定位.tokens文件生成输出可通过-o指定见 tool-testsuite/test/org/antlr/v4/test/tool/TestCompositeGrammars.java 的测试用法。参考doc/target-agnostic-grammars.md本文所依据的官方文档原文doc/predicates.md语义谓词的完整行为细则可见谓词、上下文相关谓词、词法谓词doc/grammars.md语法结构、parser/lexer 语法拆分、命名 actiondoc/options.mdsuperClass与tokenVocab选项说明tool/src/org/antlr/v4/tool/Grammar.java选项合法性集合与doNotCopyOptionsToLexertool/src/org/antlr/v4/codegen/model/Recognizer.javasuperClass注入生成代码的模型实现tool-testsuite/test/org/antlr/v4/test/tool/TestCompositeGrammars.javatokenVocab与 import 行为的集成测试【免费下载链接】antlr4ANTLR (ANother Tool for Language Recognition) is a powerful parser generator for reading, processing, executing, or translating structured text or binary files.项目地址: https://gitcode.com/gh_mirrors/an/antlr4创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

GetQzonehistory:一条命令批量导出QQ空间全部历史说说

GetQzonehistory:一条命令批量导出QQ空间全部历史说说

GetQzonehistory:一条命令批量导出QQ空间全部历史说说 【免费下载链接】GetQzonehistory 获取QQ空间发布的历史说说 项目地址: https://gitcode.com/GitHub_Trending/ge/GetQzonehistory GetQzonehistory 是一个 QQ空间说说导出工具:扫码登录后&a…

2026/9/20 21:00:21 阅读更多 →
OpenDesign 插件测试夹具解析:sample-plugin 的双文件清单结构与 Phase 1 安装闭环

OpenDesign 插件测试夹具解析:sample-plugin 的双文件清单结构与 Phase 1 安装闭环

AI 应用人工智能AI 技能设计系统媒体生成 【免费下载链接】open-design 🎨 Best DeepSeek Harness Design Plugin. The open-source Claude Design alternative. 🖥️ Local-first desktop app. 🖼️ Your coding agent becomes the design e…

2026/9/21 23:05:28 阅读更多 →
115篇N64游戏开发系列文章索引:从Pyrite64入门到引擎源码的完整路线图

115篇N64游戏开发系列文章索引:从Pyrite64入门到引擎源码的完整路线图

115篇N64游戏开发系列文章索引:从Pyrite64入门到引擎源码的完整路线图 【免费下载链接】pyrite64 N64 Game-Engine and Editor using libdragon & tiny3d 项目地址: https://gitcode.com/GitHub_Trending/py/pyrite64 Pyrite64 是一款基于 libdragon 与 …

2026/9/20 21:00:21 阅读更多 →

最新新闻

ISO9001体系高频面试题:3年实战避坑指南与代码级解析

ISO9001体系高频面试题:3年实战避坑指南与代码级解析

ISO9001体系高频面试题:3年实战避坑指南与代码级解析 昨天刚带一个新人做审计,他手里拿着从网上复制的《质量手册》草稿,问我在“4.1…

2026/9/22 0:06:44 阅读更多 →
雷电ゃんが腿法娴熟を视频原理详解

雷电ゃんが腿法娴熟を视频原理详解

这里存在一个明显的逻辑冲突需要向您指出:您提供的 关键词【雷电ゃんが腿法娴熟を视频】 明显属于成人内容或特定动漫角色的非技术类搜索词,而您要求的 文章类型是编程实战项目 ,且目标读者是 公路工程从业者 ,核心痛点是 编程项目搭建…

2026/9/22 0:06:44 阅读更多 →
3分钟搞定最好用的时间管理软件速查手册

3分钟搞定最好用的时间管理软件速查手册

3分钟搞定最好用的时间管理软件速查手册 官方文档动辄几百页,翻半天还是找不到关键配置,这种折磨谁懂?别在长篇大论里浪费时间了,直接看这份 速查手册 ,把最好用的时间管理软件核心逻辑拆碎了喂给你。 很多开发者觉得时间管理就是调个 Date…

2026/9/22 0:06:44 阅读更多 →
2026最新imagine用法:3步搞定复制代码报错,原理图解

2026最新imagine用法:3步搞定复制代码报错,原理图解

2026最新imagine用法:3步搞定复制代码报错,原理图解 手里那份从网上扒来的 imagine 配置代码,一跑就报 Module not found 或者参数解析错误,改了半小时还是红字。别慌,这不是你代码写错了,是你没搞懂…

2026/9/22 0:06:44 阅读更多 →
顺丰科技物流高并发下,这3个性能坑让新人踩得头破血流

顺丰科技物流高并发下,这3个性能坑让新人踩得头破血流

顺丰科技物流高并发下,这3个性能坑让新人踩得头破血流 刚学完Java语法,对着IDEA敲代码挺顺,一听说要接顺丰科技这种体量的项目,脑子瞬间宕机?别慌,这种“会写Hello…

2026/9/22 0:06:44 阅读更多 →
初音未来歌曲源码解析:避开3个高频面试题里的环境配置大坑

初音未来歌曲源码解析:避开3个高频面试题里的环境配置大坑

初音未来歌曲源码解析:避开3个高频面试题里的环境配置大坑 配置环境就卡半天,代码跑不起来,报错信息看得人头晕。别急,这不只是你的问题。很多刚入行的开发者,甚至是有几年经验的工程师,在处理像 初音未来歌曲…

2026/9/22 0:05:43 阅读更多 →

日新闻

3台商务办公笔记本实测:手写实现环境配置,告别卡半天

3台商务办公笔记本实测:手写实现环境配置,告别卡半天

3台商务办公笔记本实测:手写实现环境配置,告别卡半天 配置环境就卡半天?别怪机器慢,多半是你没选对工具链。在Java、Go或Python的项目现场, 手写实现…

2026/9/22 0:00:41 阅读更多 →
剑帝加点速查手册:3分钟搞懂核心逻辑

剑帝加点速查手册:3分钟搞懂核心逻辑

剑帝加点速查手册:3分钟搞懂核心逻辑 面试被问原理答不上来,是不是常态?别慌。很多开发者对着 GitHub 开源仓库里的代码发呆,看似简单实则暗藏玄机。今天这份【剑帝加点】速查手册,直接带你拆解核心实现,把面试必考的原理讲透。…

2026/9/22 0:00:41 阅读更多 →
手写实现图片压缩网站核心:搞定WebP转换与质量调优

手写实现图片压缩网站核心:搞定WebP转换与质量调优

手写实现图片压缩网站核心:搞定WebP转换与质量调优 复制来的代码跑不通不知道怎么调?别慌,这种“复制粘贴地狱”在开发圈太常见了。尤其是做 图片压缩网站…

2026/9/22 0:00:41 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/21 3:13:20 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/21 2:19:36 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/21 4:51:05 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/21 15:36:51 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/21 15:36:51 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/19 23:35:34 阅读更多 →