- 编程语言
- 编译器
- 开发工具
【免费下载链接】grammars-v4
Grammars written for ANTLR v4; expectation that the grammars are free of actions.
导读
本文以 grammars-v4 仓库的 python/python3_14 目录为研究对象,全面剖析这套基于 CPython 官方 PEG 文法(Python 3.14.6)移植而来的 ANTLR4 语法工程。你将看到:它如何通过PythonParser.g4完整复刻官方语法规则,如何依靠PythonLexerBase辅助类解决 Python 独有的缩进(INDENT/DEDENT)、编码声明(ENCODING)与 f-string / t-string 插值字符串分词难题,以及如何做到同一语法同时面向 CSharp、Java、Python3、JavaScript、TypeScript 五种运行时。读完本文,你可以直接复用这套语法搭建自己的 Python 3.14 解析工具链,并理解 ANTLR 处理缩进敏感语言的核心套路。
一、语法工程概览:一个目录、两套文法、五种运行时
python/python3_14 目录内部结构与 README 描述完全一致,核心文件包括:
| 文件 | 作用 |
|---|---|
| PythonParser.g4 | ANTLR4 语法(parser)文法,逐条对齐官方 Python 3.14 参考语法(PEG) |
| PythonLexer.g4 | ANTLR4 词法(lexer)文法,处理字面量、关键字、软关键字与插值字符串的词法模式 |
| PythonLexerBase(各运行时版) | 词法辅助类:处理缩进、产生 ENCODING token、tokenize f-string / t-string、BOM 跳过等 |
| Python3_14_6_official_grammar.peg | CPython 官方 PEG 文法的原始副本,作为移植对照基准 |
| examples | 取自 Python 3.14 标准库的真实源文件,用于验证语法正确性 |
| desc.xml | 声明 ANTLR 版本、目标语言与测试入口 |
| pom.xml | Maven 构建配置 |
从 desc.xml 可以看到工程的能力边界:
<antlr-version>^4.13.2</antlr-version> <targets>CSharp;Java;Python3;JavaScript;TypeScript</targets> <test> <targets>CSharp;Java;Python3;JavaScript;TypeScript</targets> <entry-point>file_input</entry-point> <inputs>examples</inputs> </test>这意味着:语法本身不含任何 action(符合 grammars-v4 全仓库"grammars free of actions"的约定),测试以file_input为入口,对examples目录下的全部标准库源文件进行解析验证,且五种目标语言共享同一套文法与辅助类逻辑(Java/PythonLexerBase.java、CSharp/PythonLexerBase.cs、JavaScript/PythonLexerBase.js、Python3/PythonLexerBase.py、TypeScript/PythonLexerBase.ts)。
二、PythonParser.g4:把官方 PEG 文法"翻译"成 ANTLR4
2.1 起始规则与顶层结构
PythonParser.g4 的头部注释明确标注"based on the official PEG grammar",并在 PythonParser.g4 第 30 行 注明依据是 Python 3.14.2 的官方完整文法规格。起始规则与官方file_input、interactive、eval、func_type一一对应:
file_input: statements? EOF; interactive: statement_newline; eval: expressions NEWLINE* EOF; func_type: '(' type_expressions? ')' '->' expression NEWLINE* EOF;在此基础上,statements/statement/simple_stmts/compound_stmt构成了"简单语句 + 复合语句"的层级骨架。一个值得注意的细节在assignment规则上方的注释:
// NOTE: assignment MUST precede expression, else parsing a simple assignment // will throw a SyntaxError.这说明在 ANTLR 的 LL(*) 架构下,规则的排列顺序(而不是 PEG 的优先级)直接决定了语法是否能正确工作——这是从 PEG 移植到 ANTLR 时最常见的坑之一。
2.2 软关键字:用独立 token 替代语义谓词
Python 的type、match、case、_属于软关键字:它们在某些上下文中是标识符,在另一些上下文中是关键字。从 changes.md 可以看到,2025 年 1 月对该工程做过一次"软关键字重构":
- 不再在 parser 语法中使用内嵌代码(语义谓词)判断软关键字;
- 因此不再需要
PythonParserBase类,也不再需要transformGrammar.py生成脚本; - 破坏性变更:为软关键字分配了专用 token,而不是复用
NAME:NAME_OR_TYPE(对应type)NAME_OR_MATCH(对应match)NAME_OR_CASE(对应case)NAME_OR_WILDCARD(对应_)
在 PythonLexer.g4 第 139-144 行 可以看到这四个 token 的实际定义,其注释明确指出"parser grammar determines whether it is an identifier or a keyword depending on the source code context",即由 parser 规则依据上下文决定把它们当标识符还是关键字用。例如type_alias规则(PythonParser.g4):
type_alias : 'type' name type_params? '=' expression;2.3 Python 3.14 特性在规则中的落点
从源码可以确认这套语法覆盖了 Python 3.12+ 引入、3.14 继续演进的新语法,例如:
- 类型参数语法(PEP 695):
type_params规则(PythonParser.g4 第 491-499 行)支持[T]、[*Ts]、[**P]以及带默认值、带 bound 的类型参数; - 模式匹配(PEP 634):
match_stmt、case_block、patterns、mapping_pattern、class_pattern等完整规则族(PythonParser.g4); - 异常组(PEP 654):
try_stmt中单独列出except_star_block(except*)分支(PythonParser.g4 第 316-332 行)。
这些规则的存在,意味着如果你需要为 Python 3.14 代码做静态分析、AST 抽取或 IDE 支持,这套语法已经具备与官方 PEG 文法对标的完整覆盖度。
三、PythonLexerBase:Python 缩进与插值字符串的"幕后大脑"
3.1 为什么必须有辅助类
Python 的缩进语法、编码声明和 f-string 插值都无法用纯上下文无关文法干净地表达。因此 PythonLexer.g4 第 33 行 通过superClass=PythonLexerBase把"脏活"外包给辅助类。README 概括了它的四大职责:
- 处理 Python 缩进(indentations);
- 创建编码 token(encoding token);
- tokenize f-string 与 t-string 字面量;
- 管理许多其他细节。
3.2 缩进处理:INDENT/DEDENT 与隐式续行
以 Python3/PythonLexerBase.py 为例,核心机制是一套"挂起 token 队列 + 缩进长度栈":
_handle_NEWLINE_token():遇到换行时检查上下文——- 若在插值字符串的多行模式下,直接放行;
- 若
_open_paren_bracket_brace_count > 0,说明处于括号内的隐式续行,当前 NEWLINE 被隐藏到 HIDDEN 通道; - 否则根据下一行前导空白计算缩进长度,决定插入
INDENT还是若干DEDENT。
_get_indentation_length():空格计 1,制表符按"每 8 列对齐"规则(TAB_LENGTH = 8)折算,换页符(\f)重置为 0;若同一缩进里混用 tab 与空格,返回INVALID_LENGTH (-1),触发"inconsistent use of tabs and spaces in indentation"诊断——这与 CPython 的行为一致。- 文件开头若是缩进的首个语句(
first statement indented),会主动插入一个带错误文本的INDENTtoken,让 parser 层顺势报出"unexpected indent"。 - 到达 EOF 时(
_insert_trailing_tokens)补齐缺失的尾部 NEWLINE,并弹出栈中剩余的全部缩进层级,产生成串的DEDENTtoken,保证每个 block 正确收口。
Java 版 PythonLexerBase.java 实现了完全相同的状态机(indentationLengthStack、pendingTokenQueue、openParenBracketBraceCount等),且注释注明以 Java 8 实现以兼容 ANTLR4 Java 运行时。
3.3 编码声明:ENCODING token 与 BOM 跳过
PythonLexer.g4 第 48 行 定义了BOM : '\uFEFF';。README 与 changes.md 特别强调:
- BOM(对 Python 而言即 UTF-8 的
EF BB BF字节序列)不是 Python 源码的一部分,必须在词法阶段跳过——_handle_start_of_input()在首个 token 为 BOM 时直接越过它; - 编码声明按 PEP 263 处理:
set_encoding_name()设置编码名(如"utf-8")后,_insert_ENCODING_token()会在 token 流最前端插入一个 HIDDEN 通道的ENCODINGtoken,其文本即编码名;不设置则完全不产生该 token(例如直接解析内存字符串时)。 - 架构变化:README 明确指出,"moved encoding detection from PythonLexerBase to a separate component (grun4py)"——即编码探测逻辑已从词法辅助类中剥离,交给独立的组件(
grun4py)在调用词法器之前完成,PythonLexerBase只负责"接收并排放"编码名。
3.4 f-string 与 t-string 的词法模式
t-string是 Python 3.14 的新特性(模板字符串,对应 PEP 750),也是 README "Recent changes" 中列出的重点更新项。词法层面(PythonLexer.g4 第 175-176 行):
FSTRING_START : FSTRING_PREFIX STRING_QUOTES; // pushMode(...._FSTRING_MODE) is called in PythonLexerBase TSTRING_START : TSTRING_PREFIX STRING_QUOTES; // pushMode(...._TSTRING_MODE) is called in PythonLexerBase前缀支持大小写不敏感的f/fr/rf与t/tr/rt(PythonLexer.g4 第 493-494 行)。其后按引号风格拆出 24 个词法模式(单引号/双引号 × 短/长 × 原始/非原始 × f/t),命名规则如:
SQ1__FSTRING_MODE:f'...'DQ3R_TSTRING_MODE:rt"""..."""- 每个插值字符串模式还配一个
..._FORMAT_SPECIFICATION_MODE,专门处理格式说明符区域(:之后的部分)。
token 层面定义了FSTRING_START/FSTRING_MIDDLE/FSTRING_END与TSTRING_START/TSTRING_MIDDLE/TSTRING_END(PythonLexer.g4 第 39-40 行),分别对应 PEP 701 与 PEP 750 的规范。
PythonLexerBase 承担了模式切换与边界处理:
_set_lexer_mode_by_ISTRING_START_token():根据前缀文本(如f'、rt""")查表LEXER_MODES_FOR_ISTRING_START决定进入哪个模式;_handle_ISTRING_MIDDLE_token_with_double_brace():把{{/}}拆分出一个 HIDDEN 通道的{/}(对应转义花括号);_handle_ISTRING_MIDDLE_token_with_quote_and_lbrace():处理"{、'{、\{三种边界,将{单独切出成 DEFAULT 通道的LBRACEtoken,进入表达式解析;_process_brace_expression():把{...}内的表达式逐 token 累积成字符串(追加到_brace_expression_stack),以便后续判断;_handle_COLONEQUAL_token_in_istring():处理 walrus 运算符在 f/t-string 中的特例——插值字符串内:=只允许出现在括号内,括号外则把COLONEQUAL拆成COLON(格式说明符)并把=移到下一个 MIDDLE token 的头部;_is_valid_dictionary_or_set_comprehension_expression():调用文法自身(dictcomp/setcomp规则)回代检验最外层花括号表达式是否字典/集合推导式,从而落实 Python 规范"最外层 f-string 表达式不能是推导式"的限制;_handle_FORMAT_SPECIFICATION_MODE():在缺失格式说明(如f"{x:}")时补插一个空的FSTRING_MIDDLE/TSTRING_MIDDLEtoken。
需要特别说明的是:PythonLexerBase 重新实现了 mode/stack 管理(_push_lexer_mode/_pop_lexer_mode,对应 Java 的pushLexerMode/popLexerMode),而不是直接依赖 ANTLR 内部的_mode/_modeStack,因为并非所有运行时都暴露了这些内部状态——这是保证五种语言行为一致的关键工程决策。
3.5 错误报告
辅助类内部通过_report_lexer_error统一走 ANTLR 的错误监听器分发;对于缩进不一致、未知插值前缀、f-string 中孤立}("f-string: single '}' is not allowed")等场景,会额外向 token 流插入一个带" ERROR: "前缀文本的ERRORTOKEN(见 PythonLexer.g4 第 179 行 的ERRORTOKEN : .;),从而同时触发 parser 级错误,便于错误恢复。
四、词法细节:关键字、字面量与 Unicode 标识符
PythonLexer.g4 的默认模式覆盖了 Python 词法分析文档定义的全部元素:
- 关键字:
False/True/None及全部 35 个硬关键字均有独立 token(第 103-137 行),如ASYNC、AWAIT、YIELD; - 运算符与分隔符:从
LPAR到EXCLAMATION的完整 token 表(第 53-100 行),含COLONEQUAL :=、ELLIPSIS ...、DOUBLESLASH //等; - 数字字面量:
NUMBER由INTEGER | FLOAT_NUMBER | IMAG_NUMBER组成,底层 fragment 支持十进制、二进制、八进制、十六进制、指数、虚数以及下划线分隔(1_000_000、0x_FF),见 PythonLexer.g4 第 554-575 行; - 字符串/字节串:
STRING_LITERAL、BYTES_LITERAL及其长字符串、原始字符串、转义序列 fragment(第 429-490 行); - 隐藏通道元素:
COMMENT与WS进 HIDDEN 通道,EXPLICIT_LINE_JOINING(反斜杠续行\\\n)同样被隐藏(第 163-172 行); - Unicode 标识符:
ID_START/ID_CONTINUEfragment 是按 Python 3.14.2 的 Unicode 版本手工生成的(第 577 行起),其中ID_CONTINUE覆盖了从\u{0030}到\u{1D7D8}等数以千计的码位区间,与 CPython 的标识符规则保持一致。
同时,类型注释 token(TYPE_COMMENT)仅作为兼容占位保留(PythonLexer.g4 第 38 行)。从 changes.md 可知,自 2024 年 9 月起类型注释不再生成专用 token,而是按普通注释处理;字符串字面量中的"反斜杠+换行"续行也不再在词法层消解。
五、示例与验证:真实标准库源码当测试集
examples 目录存放的是从 Python 3.14 标准库(Lib目录)摘取的源文件,例如_ast_unparse.py、_pydecimal.py、_compat_pickle.py、_colorize.py等。这些文件本身蕴含了极其丰富的语法现象:f-string 嵌套插值、match语句、except*、类型别名、Self/ClassVar注解等。
例如 examples/_colorize.py 中包含了if False:中的类型注解代码块、dataclass 装饰器、collections.abc泛型别名导入(Callable, Iterator, Mapping)等现代语法组合。用这类真实生产代码而非人工构造的最小用例来跑file_input入口测试,能最大程度暴露语法移植中的边界问题——这也是 desc.xml 中entry-point=file_input、inputs=examples配置的意义所在。
六、版本演进脉络(依据 changes.md)
changes.md 记录了该语法持续跟进的轨迹,是理解工程演进的重要参考:
- 2024-09:类型注释 token 取消,改为普通注释;字符串字面量不再消解"反斜杠+换行"续行;
- 2024-10:修复
case [a, *_] if a == 0:触发软关键字_语义谓词失败的缺陷(此后该谓词被重构移除); - 2025-01(Python 3.13.1):加入
ENCODINGtoken;完整重写 f-string tokenizer(词法文法 + PythonLexerBase),使其能正确 tokenize 转义序列、walrus 运算符、字典推导式与集合推导式;软关键字改为专用 token(NAME_OR_TYPE等),移除PythonParserBase与transformGrammar.py; - 2026-07(Python 3.14.6):parser 文法跟进到 Python 3.14.6;新增 t-string tokenize;BOM 字符在文件开头被跳过、不进入 token 流;编码探测从
PythonLexerBase迁移到独立组件grun4py。
七、如何上手使用
仓库是只读的,你可以在自己的工程中按以下方式复用:
- 获取文法:复制 PythonParser.g4、PythonLexer.g4 以及所选运行时对应的
PythonLexerBase(如 Python3/PythonLexerBase.py)到你的项目; - 生成解析器:使用 ANTLR 4.13.2 及以上版本(见 desc.xml 的
<antlr-version>^4.13.2</antlr-version>)生成 lexer/parser 代码,并确保生成的 lexer 继承(或 superClass 指向)PythonLexerBase; - 组装解析流程:以
file_input作为入口规则;用CommonTokenStream连接 lexer 与 parser;如需要 ENCODING token,先调用set_encoding_name("utf-8")(Python 版为set_encoding_name,Java 版为setEncodingName),否则传入空串即可跳过; - 验证:可直接把 examples 下的标准库源文件作为输入,配合
grun(或仓库根目录的 grun.sh)打印 token 流与解析树,检查缩进 token(INDENT/DEDENT)、插值字符串 token(FSTRING_* / TSTRING_*)是否符合预期。
结语
grammars-v4 的python/python3_14是一个"以官方 PEG 文法为基准、以辅助类补齐词法短板"的典型 ANTLR4 移植样本。它把 Python 最难啃的三块硬骨头——缩进、编码声明、f/t-string 插值——全部收敛到PythonLexerBase一个辅助类中,同时让 parser 文法保持纯净(无 action、无内嵌谓词),从而获得了五语言运行时的一致行为。对任何需要为 Python 3.14 代码构建分析器、转换器或编辑器的开发者而言,这套语法既是可直接使用的工具,也是学习"如何用 ANTLR4 处理缩进敏感与插值字符串语言"的优秀范本。
- 编程语言
- 编译器
- 开发工具
【免费下载链接】grammars-v4
Grammars written for ANTLR v4; expectation that the grammars are free of actions.
相关推荐
如何让老Mac免费安装最新版macOS:OpenCore Legacy Patcher完整指南
如何让老Mac免费安装最新版macOS:OpenCore Legacy Patcher完整指南 OpenCore Legacy Patcher(简称 OCLP)
编程语言编译器开发工具grammars-v4 中的 GDScript ANTLR4 语法实现:从 Godot EBNF 到可运行的词法/语法分析器
grammars v4 中的 GDScript ANTLR4 语法实现:从 Godot EBNF 到可运行的词法/语法分析器 本篇文章基于 grammars v
编程语言编译器开发工具深入解析 grammars-v4 中的 Python 3.14 ANTLR4 文法更新:t-string、f-string 词法重构与软关键字变革
深入解析 grammars v4 中的 Python 3.14 ANTLR4 文法更新:t string、f string 词法重构与软关键字变革 Python
编程语言编译器开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考