grammars-v4 项目 PDN(Portable Draughts Notation)ANTLR4 文法解析实战指南
grammars-v4 项目 PDN(Portable Draughts Notation)ANTLR4 文法解析实战指南
导读
本文基于 grammars-v4 仓库中的 pdn 模块,系统讲解其 ANTLR4 文法实现:从 PDN(Portable Draughts Notation,便携式跳棋记谱格式)的文件结构出发,逐条拆解 pdn.g4 中的语法规则与词法规则,并结合仓库自带的三个真实棋谱示例与 Maven 测试配置,说明如何生成解析器、如何运行解析测试。读完本文,你将能够独立使用该文法解析国际跳棋 / 西洋跳棋的 PDN 棋谱文件,并理解其规则设计背后的格式语义。
PDN 格式与本仓库文法的定位
PDN 是一种用于记录跳棋(Draughts / Checkers)对局的便携式文本记谱格式,以方括号标签(tag)描述棋局元信息、以带编号的着法序列描述行棋过程。grammars-v4 仓库中的 pdn 模块即为此格式提供了一份"无动作(action-free)"的 ANTLR4 文法——整个文法文件只包含纯语法与词法声明,不含任何目标语言代码,因此可被 ANTLR4 的多种目标语言(Java、C#、C++、Go、JavaScript、Python3、TypeScript 等)直接复用。
模块目录结构如下:
- pdn/pdn.g4:文法本体(语法规则 + 词法规则);
- pdn/examples/:三个真实 PDN 棋谱示例文件;
- pdn/pom.xml:Maven 构建与自动化解析测试配置;
- pdn/desc.xml:声明该文法支持的目标语言列表;
- pdn/README.md:模块说明文档。
文法文件总览:整体结构与顶层规则
pdn.g4 以 BSD 许可证发布,声明语法名为 pdn,整个文件的解析入口是 game 规则:
grammar pdn;
game
: tags moves EOF
;
game 规则清晰勾勒出 PDN 文件的两段式结构:先是一组标签(tags),然后是一段着法序列(moves),最后以文件结束符(EOF)收尾。这种"头部元信息 + 主体棋谱"的组织方式与 PDN 格式本身一一对应,也直接决定了 pom.xml 中测试入口点被配置为 game。
标签段:tags 与 tag 规则
PDN 文件的头部标签段由 tags 与 tag 两条规则描述:
tags
: tag*
;
tag
: '[' text string ']'
;
tags允许出现零个或多个tag,即标签段可为空;- 每条
tag的形态为[ 名称 值 ],其中text对应标签名(如Event、Site、White),string对应带引号的值。
对照仓库示例 example1.txt 的前几行,标签的解析结果一目了然:
[Event "itsyourturn.com USA vs. World 8/04"]
[Site ""]
[Date "2004.08.23"]
[Round "1"]
[Black "Lindus Edwards"]
[White "Anthony Perez"]
[Result "1/2-1/2"]
在 example2.txt 中还能看到 PDN 格式常见的扩展标签,如 <a href="https://link.gitcode.com/i/798560872afc2a9d1cd8d503263c5d21" target="_blank">Setup "1"] 与 [FEN "W:W27,19,18,11,7,6,5:B28,26,25,20,17,10,9,4,3,2."]——后者以 FEN 串记录棋盘初始局面;[example3.txt 中则出现了 [GameType "20"] 这样的棋类变体标签。由于 tag 规则对标签名与值不做白名单约束,这些扩展标签均能被正常解析。
着法段:moves、move 与 movespec
主体着法序列由 moves、move、movespec 三级规则构成:
moves
: move+ (result | '*')+
;
move
: movenum movespec+
;
movespec
: (MOVE1 | MOVE2) (result | '*')?
;
movenum
: number '.'
;
着法编号:movenum
movenum 由"数字 + 句点"组成,如示例中的 1.、2.、3.……,用于标记回合序号。注意它只负责语法形状(数字加句点),并不校验编号是否连续、是否从 1 递增——这是从源码结构可以看出的设计取舍。
单步着法:movespec
movespec 是着法的最小单元,必须匹配 MOVE1 或 MOVE2 两种词法形式,并可选地跟随一个 result 或 '*' 后缀:
movespec
: (MOVE1 | MOVE2) (result | '*')?
;
result 规则定义了三种对局结果标记:
result
: '1/2-1/2'
| '1-0'
| '0-1'
;
分别对应和棋(平局)、白方胜、黑方胜。'*' 则通常表示"结果未定 / 其他情况"。示例 example1.txt 中 9. 10-15 17-10 10. 7-14 26-22* 1/2-1/2 里的 22* 即为 MOVE2 后跟 '*' 的典型形态。
着法的两种记谱形式:MOVE1 与 MOVE2
movespec 依赖的词法规则 MOVE1 与 MOVE2 是理解 PDN 记谱的关键:
MOVE1
: ('0' .. '9')+ 'x' ('0' .. '9')+
;
MOVE2
: ('0' .. '9')+ '-' ('0' .. '9')+
;
- MOVE1(吃子着法):以字母
x连接两个数字,如10x19、5x32,表示"从 10 跳到 19 并吃掉途经棋子";示例 example2.txt 中7. 5x32 {5-32 ...} 1-0一次跳吃九子的组合技正是这种形式; - MOVE2(普通着法):以连字符
-连接两个数字,如11-15、23-18,表示棋子从一格移动到另一格。
两条规则都以"纯数字 + 分隔符 + 纯数字"的紧凑形态匹配,中间不允许出现空白,这是 PDN 记谱中着法与棋谱注释能明确区分的基础。
词法规则详解:数字、文本、字符串、注释与空白
除上述着法相关规则外,pdn.g4 还定义了 5 条基础词法规则:
NUMBER
: ('0' .. '9')+
;
TEXT
: [a-zA-Z] [a-zA-Z0-9_]+
;
STRING
: '"' ('""' | ~ '"')* '"'
;
COMMENT
: '{' .*? '}' -> skip
;
WS
: [ \t\r\n]+ -> skip
;
逐条说明:
- NUMBER:一个或多个数字,用于
movenum的编号部分; - TEXT:以字母开头、后可跟字母/数字/下划线的标识符,用于标签名(
Event、FEN等); - STRING:双引号包裹的字符串,支持
""转义表示字面双引号,用于标签值(如"Anthony Perez")。注意[Site ""]这类空字符串也能被('""' | ~ '"')*匹配为空值; - COMMENT:花括号
{...}包裹的注释,通过-> skip直接丢弃。PDN 棋谱中大量的人类可读评注(如 example1 中的{Crescent Cross}、{Perez' cook; ...})都由它吸收,不会干扰语法分析; - WS:空白(空格、制表符、换行)一律跳过,因此着法与标签可以自由换行排版。
由于 COMMENT 与 WS 都被标记为 skip,生成的词法分析器在进入语法分析阶段前就会把它们过滤干净,解析树中只会保留标签与着法结构,这与仓库中"文法不含动作"的定位一致,也方便后续为任意目标语言生成独立的监听器(Listener)或访问器(Visitor)来处理棋谱数据。
用真实棋谱验证文法:三个示例解析
仓库 pdn/examples/ 提供了三个覆盖不同场景的棋谱,可作为文法正确性的直接验证样本:
| 示例文件 | 棋局背景 | 覆盖要点 |
|---|---|---|
| example1.txt | 2004 年 itsyourtown 网站"USA vs. World"友谊赛 | 常规着法 11-15、带星号着法 27-20*、注释 {...}、双结果标记 1/2-1/2 ... 1/2-1/2 |
| example2.txt | "The Royal Tour"排局 | Setup/FEN 扩展标签、吃子着法 10x19、九子连跳 5x32、单结果标记 1-0 |
| example3.txt | 2003 年国际跳棋世锦赛(WK 2003)对局 | 40 回合完整对局、GameType 标签、大量 x 吃子与 - 移动混合、多行连续着法 |
以 example1 的第 5 回合为例:
5. 15-24 27-20* {28-19 loses PP} 6. 4-8 ...
解析路径为:movenum(5.)→ 两个 movespec(15-24 匹配 MOVE2;27-20* 匹配 MOVE2 后跟 '*')→ 花括号注释被 COMMENT 丢弃 → 继续进入第 6 回合。整个文件的结尾 1/2-1/2 {a very popular position} 1/2-1/2 则对应 moves 规则末尾 (result | '*')+ 允许多个结果标记的写法。
构建与自动化测试:Maven 配置解读
pom.xml 完整配置了该文法的生成与测试流程,是仓库内各模块的标准做法:
- 解析器生成:使用
org.antlr:antlr4-maven-plugin,sourceDirectory指向模块根目录,includes限定只编译pdn.g4,并同时开启visitor与listener,保证生成的解析代码既支持监听器模式也支持访问器模式; - 自动化测试:使用
com.khubla.antlr:antlr4test-maven-plugin对示例文件跑解析回归测试,关键参数:entryPoint:game——即以game为解析入口;grammarName:pdn;exampleFiles:examples/——测试样本目录即模块自带的示例目录。
这意味着在仓库根目录执行 Maven 构建时,pdn 模块会先由 ANTLR 生成 Java 解析器,再逐个解析 examples/ 下的棋谱文件,任何一个示例解析失败都会导致构建失败,从而持续守护文法的正确性。
desc.xml 则声明了本文法面向的目标语言:CSharp;Cpp;Go;Java;JavaScript;PHP;Python3;TypeScript;Antlr4ng,即除 Java 外,还可用于生成 C#、C++、Go、JavaScript、PHP、Python3、TypeScript 以及 ANTLR4ng 运行时对应的解析代码。
在本仓库中运行与使用
使用本文法解析 PDN 文件的典型流程如下:
- 获取 ANTLR 工具:参照仓库 grun.sh 的做法,下载 ANTLR4 完整 JAR 并设置
ANTLR_JAVA_LIB环境变量(export ANTLR_JAVA_LIB=<path/to/antlr/jar/file>); - 生成解析代码:对 pdn/pdn.g4 执行 ANTLR 工具生成词法/语法分析器源码,再按所选目标语言编译;
- 调用解析入口:以
game为起点规则,将 examples/ 中的棋谱文本送入解析器; - 遍历解析树:通过生成的 Listener/Visitor 提取标签、着法与结果——由于文法不含动作,所有数据处理逻辑都应在解析树遍历阶段自行实现;
- 回归验证:在仓库根目录执行 Maven 构建(或单独构建
pdn模块),利用antlr4test-maven-plugin对三个示例自动跑通解析测试。
局限性与扩展方向(基于源码结构的推断)
从 pdn.g4 的现有结构可以推断出若干值得注意的设计边界,供使用者在二次开发时参考:
- 标签名/值不做语义校验:
TEXT与STRING是通用形态,[White "a"]、[Date "??"]这类非常规值也能通过解析,语义合法性需在应用层校验; - 着法编号不校验连续性:
movenum只认"数字 + 句点"形态,编号跳号或重复不会被发现; - MOVE1/MOVE2 不校验棋盘合法性:如
99x99这类越界坐标同样能匹配词法,是否合法落子属于语义层职责; - 无变体(variation)支持:PDN 中常见的括号变体着法(如
1. 11-15 (10-14) 23-18)在本文法中未被覆盖,若需支持需自行扩展语法规则。
这些边界恰恰说明本模块的定位是"忠实、纯净的语法层解析",为上层棋谱分析工具提供稳定基础;而如何在语义层校验、扩展变体语法,则留给了使用该文法的开发者。
结语
pdn 模块以一份不到 120 行的纯 ANTLR4 文法,完整覆盖了 Portable Draughts Notation 的标签段、着法段、结果标记、注释与空白处理,并配套三个真实棋谱与 Maven 自动化测试,是 grammars-v4 仓库中"小而完整"的语法实现范例。无论你是要解析国际跳棋棋谱、构建棋谱数据库,还是学习如何为一门小格式编写 ANTLR4 文法,pdn.g4 及其示例都是可以直接上手研读与复用的起点。