FEATURED · 精选文章

ANTLR4 文法结构完全指南:grammar 声明、规则组织、导入合并与 tokens/channels 详解

发布时间 / 2026/9/20 22:23:20
来源 / 创域科博编辑部
栏目 / 资讯中心
ANTLR4 文法结构完全指南:grammar 声明、规则组织、导入合并与 tokens/channels 详解 开发工具编程语言编译器【免费下载链接】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 中文法grammar是一切解析工作的起点它定义了词法lexer与语法parser规则并通过import、tokens、channels、命名动作等机制实现模块化复用。本文以仓库 doc/grammars.md 为骨架结合 tool 模块的工具源码与 antlr4-maven-plugin 的真实测试工程完整讲解文法文件的结构、三种文法类型、导入合并的底层原理、tokens 与 channels 的用法以及文法级动作的注入方式帮助你写出结构清晰、可复用的 ANTLR4 文法。文法文件的基本骨架一个 ANTLR 文法本质上是一段文法声明加上一组规则列表其通用形式如下/** 可选的 javadoc 风格注释 */ grammar Name; ① options {...} import ... ; tokens {...} channels {...} // 仅词法文法可用 actionName {...} rule1 // 解析规则与词法规则可以交错排列 ... ruleN其中 ① 处的grammar Name;头声明和至少一条规则是唯一必需的元素其余options、import、tokens、channels、命名动作都是可选的且可以按任意顺序书写。约束是options、import、tokens各自最多只能出现一次。规则的基本形式为ruleName : alternative1 | ... | alternativeN ;命名约定解析规则parser rule必须以小写字母开头词法规则lexer rule必须以大写字母开头。这条规则被 ANTLR 工具在语义检查阶段严格执行相关检查位于 BasicSemanticChecks.java同时也是区分两类规则最直观的依据。文件名约定包含文法X的文件必须命名为X.g4。ANTLR 工具在 Tool.java 中根据文件名解析文法名如果文件名与grammar头声明不一致会直接报错。三种文法类型grammar头的写法决定了文法包含哪些规则头声明允许的规则典型用途grammar Name;词法 解析规则组合文法词法解析一体最常见parser grammar Name;仅解析规则与独立词法文法搭配使用lexer grammar Name;仅词法规则独立词法、供tokenVocab或import复用parser grammar Name; ...lexer grammar Name; ...两点硬性约束只有词法文法可以包含mode规格。词法模式lexical mode允许把单个词法文法拆成多个子词法器例如 XML 的标签内/标签外上下文。组合文法中不允许出现mode这是文档与 ANTLRParser.g 语法定义共同确认的规则。只有词法文法可以自定义 channels。通道channel用于把词法符号分流到不同的管道最典型的用法是把注释和空白符放入隐藏通道使解析规则无需处理它们。自定义 channels在词法文法中可以用枚举风格声明自定义通道channels { WHITESPACE_CHANNEL, COMMENTS_CHANNEL }声明之后这些通道名可以像枚举值一样在词法规则中使用WS : [ \r\t\n] - channel(WHITESPACE_CHANNEL) ;通道本质上是在词法符号上打的一个整数标记。从源码看Grammar类维护了channelNameToValueMap与channelValueToNameList两张表并通过 defineChannelName 注册通道名通道编号从 1 开始分配0 保留给默认通道Token.DEFAULT_CHANNEL与Token.HIDDEN_CHANNEL为内置通道。词法规则末尾的- channel(X)是 ANTLR 的词法命令lexer command之一除了channel(x)还有skip、more、popMode、mode(x)、pushMode(x)、type(x)等。更多词法命令与字符集语法的细节参见 doc/lexer-rules.md。文法导入Grammar Imports导入的语义继承与覆盖import让文法可以被拆分成逻辑清晰、可复用的模块。ANTLR 处理导入的方式与面向对象语言中的父类极其相似主文法main grammar继承被导入文法中的全部规则、tokens 规格与命名动作主文法中定义的规则会覆盖override被导入文法中的同名规则从而实现继承式的覆写。可以把import理解为一条智能 include语句它不会重复引入已经定义过的规则。所有导入的最终结果被合并成一个完整的组合文法ANTLR 代码生成器看到的只是一份完整文法完全不知道背后存在多个导入文件。其合并流程在 GrammarTransformPipeline.integrateImportedGrammars 中有完整实现工具先将每个导入文法加载为独立的Grammar对象见 Grammar.loadImportedGrammars再把通道、tokens、命名动作、模式、规则逐项拷贝进主文法的 AST得到一棵根文法树。注释中明确写道The goal is a complete combined grammar so we can ignore subordinate grammars目标是完整的组合文法从而可以忽略从属文法。下面这张图展示了MyELang导入ELang后的合并效果MyELang继承了ELang的stat、WS、ID规则覆盖了expr并新增了INT。下面是一次完整的构建与测试运行可以看到MyELang能识别整数表达式而原来的ELang不能第三条错误输入触发的报错信息也证明解析器查找的是MyELang的expr而非ELang的$ antlr4 MyELang.g4 $ javac MyELang*.java $ grun MyELang stat 34; a; ; EOF line 3:0 extraneous input ; expecting {INT, ID}模式mode与导入的交互如果主文法或任一导入文法中存在mode导入过程会把这些模式一并引入并将其中未被覆盖的规则合并进来。假如某个模式的所有规则都被模式外的规则覆盖而变空该模式会被丢弃。这一逻辑在 integrateImportedGrammars 的COPY MODES阶段实现同名模式合并到已有模式节点新模式仅在确实新增了规则addedRules 0时才会被加入合并后的文法空模式不会残留。tokens、channels 与命名动作的合并如果存在tokens规格主文法会合并 token 集合如果存在channel规格主文法会合并通道集合命名动作如members也会被合并。需要注意应避免在被导入文法中使用命名动作和规则内动作因为这会限制文法的可复用性。此外ANTLR 会忽略被导入文法中的任何 options。事实上当被导入文法的 options 与主文法不一致时工具会报OPTIONS_IN_DELEGATE错误见 GrammarTransformPipeline.java所以也不要指望通过导入来传递 options。从源码层面看Grammar.importVocabGrammar.java负责把被导入文法的 token 名、字符串字面量与通道值合并进主文法并同步扩张typeToTokenList与channelValueToNameList两张索引表importTokensFromTokensFileGrammar.java则负责处理options { tokenVocab... }情形下的 tokens 文件导入。嵌套导入与深度优先遍历被导入的文法还可以再导入其他文法。ANTLR 以深度优先方式遍历全部导入链Grammar.loadImportedGrammars在加载每个导入文法后递归调用自身Grammar.java同时用visited集合防止循环导入。如果两个或更多被导入文法定义了同名规则rANTLR 选择最先遇到的那份r。在上图Nested、G1、G3、G2的例子中ANTLR 按Nested→G1→G3→G2的顺序考察因此Nested采用来自G3的r规则它在G2的r之前被看到。导入组合的兼容性矩阵并非任意文法都能互相导入词法文法可以导入词法文法包括含mode的词法文法解析文法可以导入解析文法组合文法可以导入解析文法或导入不含mode的词法文法。另外ANTLR 把导入的规则追加到主词法文法规则列表的末尾这意味着主文法的词法规则优先于导入规则。例如若主文法定义了IF : if ;而被导入文法定义了ID : [a-z] ;同样能匹配if导入的ID不会遮蔽主文法的IF定义——因为IF在规则列表中更靠前词法分析器优先匹配它。真实的导入示例仓库 antlr4-maven-plugin 的 Maven 插件测试工程 importsStandard 给出了标准的多文件导入布局。以src/main/antlr4为根imports/TestBaseLexer.g4 定义基础词法文法声明tokens { Name }并定义Comment、CDSect与多个fragment辅助规则imports/TestBaseLexer2.g4 提供Digit片段规则test/TestLexer.g4 通过import TestBaseLexer, TestBaseLexer2;一次性导入两个词法文法并补充WS、TEXT规则test/TestParser.g4 则是典型的parser grammar配合options { tokenVocabTestLexer; }复用词法器产出的 tokens 文件。这个布局完整演示了基础词法文法可复用→ 具体词法文法import 合并→ 解析文法tokenVocab 关联的三层架构与本文档描述的导入机制完全对应。仓库中 importsCustom 与 importTokens 还提供了自定义导入路径与 tokens 导入的变体示例。Tokens 段显式声明词法记号类型tokens段的用途是为没有对应词法规则的 token 类型预先定义类型编号。基本语法tokens { Token1, ..., TokenN }最常见的场景是配合文法内动作使用——例如关键字集合不固定、需要在词法动作中动态分配 token 类型对应书中Recognizing Languages whose Keywords Arent Fixed一节// 显式定义关键字 token 类型避免隐式定义警告 tokens { BEGIN, END, IF, THEN, WHILE } lexer::members { // 词法器中用于分配 token 类型的关键字映射表 MapString,Integer keywords new HashMapString,Integer() {{ put(begin, KeywordsParser.BEGIN); put(end, KeywordsParser.END); ... }}; }tokens段本质上只是向文法的总体 token 集合追加一组 token。下面用一个最小示例验证其效果a : X ;中的X没有对应词法规则会触发隐式定义警告但 token 编号仍然按序分配$ cat Tok.g4 grammar Tok; tokens { A, B, C } a : X ; $ antlr4 Tok.g4 warning(125): Tok.g4:3:4: implicit definition of token X in parser $ cat Tok.tokens A1 B2 C3 X4从生成的Tok.tokens可以看到tokens { A, B, C }占用了 1、2、3隐式定义的X顺延为 4。tokens文件正是options { tokenVocab... }机制的数据来源——ANTLR 为每个文法生成一个.tokens文件解析文法可以通过 importTokensFromTokensFile 读取它来复用独立的词法文法的 token 编号完整用法参见 doc/options.md 中的tokenVocab一节。文法级动作header 与 members目前Java target文法规则之外只定义了两种命名动作header和members。header把代码注入生成的识别器类文件顶部、类定义之前常用于package声明与 importmembers把代码注入识别器类定义内部作为字段和方法常用于辅助变量、回调方法。对于组合文法ANTLR 会把这组动作同时注入解析器和词法器如果只想作用于其中之一用作用域限定写法parser::name或lexer::name。下面是一个完整的组合文法示例它为生成的代码指定了包名并在members中维护计数器grammar Count; header { package foo; } members { int count 0; } list after {System.out.println(count ints);} : INT {count;} (, INT {count;} )* ; INT : [0-9] ; WS : [ \r\t\n] - skip ;使用此文法时Count.g4应放在目录foo下这样 ANTLR在不使用-o选项时会把生成代码输出到同一个foo目录与 Java 的包目录约定一致$ cd foo $ antlr4 Count.g4 # 在当前目录foo生成代码 $ ls Count.g4 CountLexer.java CountParser.java Count.tokens CountLexer.tokens CountBaseListener.java CountListener.java $ javac *.java $ cd .. $ grun foo.Count list 9, 10, 11 EOF 3 ints运行输出中的3 ints正是after动作在list规则结束后打印的结果——可见文法级动作与规则动作配合可以完成跨规则的上下文统计。从源码看命名动作在合并阶段会按作用域 动作名去重同一文法内同名动作重复定义会报ACTION_REDEFINITION错误而来自不同导入文法的同名动作则会被拼接为一个动作体见 integrateImportedGrammars 的COPY ACTIONS阶段。这也是文档建议避免在被导入文法中放置命名动作的原因之一——拼接语义容易带来意外的代码组合。规则结构速览与进阶阅读本文档聚焦于文法文件的整体组织规则内部的完整语法EBNF 子规则、?/*/量词、左递归处理、options { ... }、token 选项assoc、语义谓词等分布在仓库的专题文档中词法规则、词法模式、字符集与 Unicode 属性doc/lexer-rules.md解析规则与左递归doc/parser-rules.md、doc/left-recursion.md文法/规则/规则元素级选项superClass、language、tokenVocab、TokenLabelType、contextSuperClass、caseInsensitive等doc/options.md词法与解析动作的完整语义、$x.y属性翻译doc/actions.md工具命令行选项含-D覆盖文法内 optionsdoc/tool-options.md。此外ANTLR 官方参考书《The Definitive ANTLR 4 Reference》的第 15 章15.5 Lexer Rules、15.3 Parser Rules、15.8 Options、15.4 Actions and Attributes对上述内容有更详细的展开本文档正文与之对应。小结把握 ANTLR4 文法结构的关键点可归纳为文法 头声明 可选段落options/import/tokens/channels/命名动作 至少一条规则文件名必须与文法名一致且扩展名为.g4解析规则小写开头、词法规则大写开头mode与自定义channels仅存在于词法文法import是智能 include深度优先加载、同名规则前者优先、主文法规则覆盖导入规则最终合并为单一组合文法后交给代码生成器导入文法的 options 被忽略tokens段为无词法规则的 token 预留编号并通过.tokens文件支撑tokenVocab复用header/members是 Java target 下仅有的两种文法级命名动作可配合parser::/lexer::作用域限定实现精确注入。掌握了这些结构要素你就能像仓库测试工程 importsStandard 那样把大型文法拆分为基础可复用层 具体业务层 解析层实现真正的模块化语言工具开发。赞分享开发工具编程语言编译器【免费下载链接】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点击查看免费下载相关推荐claude-desktop-debian 文档体系规范结构、写作规则与页面组织指南claude desktop debian 文档体系规范结构、写作规则与页面组织指南 本篇技术指南以 docs/styleguides/docs_styleg开发工具桌面应用TypeScript 编译器 SymbolFlags 详解binder 语义系统中符号标志位的设计、组合与声明合并规则TypeScript 编译器 SymbolFlags 详解binder 语义系统中符号标志位的设计、组合与声明合并规则 导读 本文围绕 binder symb教程Redux Reducer 结构组织完全指南从单一 reducer 到 reducer 组合与规范化状态Redux Reducer 结构组织完全指南从单一 reducer 到 reducer 组合与规范化状态 Redux 的核心是一个简单的设计模式所有写逻前端创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻