grammars-v4 项目 PDN(Portable Draughts Notation)ANTLR4 文法解析实战指南

原创2026-09-23 12:21:571,458 阅读
文章标签:编程语言编译器开发工具

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.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 完整配置了该文法的生成与测试流程,是仓库内各模块的标准做法:

  1. 解析器生成:使用 org.antlr:antlr4-maven-plugin,sourceDirectory 指向模块根目录,includes 限定只编译 pdn.g4,并同时开启 visitor 与 listener,保证生成的解析代码既支持监听器模式也支持访问器模式;
  2. 自动化测试:使用 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 文件的典型流程如下:

  1. 获取 ANTLR 工具:参照仓库 grun.sh 的做法,下载 ANTLR4 完整 JAR 并设置 ANTLR_JAVA_LIB 环境变量(export ANTLR_JAVA_LIB=<path/to/antlr/jar/file>);
  2. 生成解析代码:对 pdn/pdn.g4 执行 ANTLR 工具生成词法/语法分析器源码,再按所选目标语言编译;
  3. 调用解析入口:以 game 为起点规则,将 examples/ 中的棋谱文本送入解析器;
  4. 遍历解析树:通过生成的 Listener/Visitor 提取标签、着法与结果——由于文法不含动作,所有数据处理逻辑都应在解析树遍历阶段自行实现;
  5. 回归验证:在仓库根目录执行 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 及其示例都是可以直接上手研读与复用的起点。

登录后查看全文
grammars-v4