使用Antlr4解析PGN走子报错:输入'Ng'匹配Moving_Piece失败
错误原因分析
ANTLR词法分析器遵循最长匹配优先原则:当多个规则都能匹配输入时,优先选择匹配长度最长的;若长度相同,则优先选择先定义的规则。
你的Chars规则定义为[a-zA-Z]*,可以匹配任意长度的字母序列(比如输入Ng时,会被完整匹配为一个Chars token),而Moving_Piece仅能匹配单个大写字母。当输入Ng4时,词法分析器会优先将Ng识别为Chars token,而非拆分为Moving_Piece(N)和Moving_File(g)。后续语法解析时,standard_move规则期望第一个token是Moving_Piece,但实际收到的是Chars,因此触发mismatched input 'Ng' expecting Moving_Piece错误。
另外,Chars规则使用*(允许0个字符)不符合PGN标签的规范(标签名不能为空),但这不是当前错误的直接原因。
解决方案
使用ANTLR的**词法模式(Lexer Modes)**区分标签上下文和走子上下文,让Chars等标签相关规则仅在[...]内部生效,避免与走子规则冲突。修改后的语法如下:
grammar PGN; // 解析规则 annotations: annotation+; annotation : Open_Bracket tag value Close_Bracket Newline ; value: String; tag: Chars; move_time: Move_time Newline; move_number: Move_Number Newline; standard_move: moving_Piece moving_File moving_Rank Newline; moving_Piece: Moving_Piece ; moving_File: Moving_File; moving_Rank: Moving_Rank; // 词法规则 - 默认模式(走子相关规则) Moving_Piece: ('K'|'Q'|'R'|'B'|'N') ; Moving_File: [a-h]; Moving_Rank: [1-8]; Move_Number: [0-9]+ '.'; Move_time: '{[%emt' ~']'* ']}'; // 精准匹配时间注释,避免非贪婪匹配歧义 Open_Bracket : '[' -> pushMode(TAG_MODE); // 进入标签模式 Newline: [\n]; WHITESPACE : ' ' -> skip ; // 词法模式 - 标签内部 mode TAG_MODE; Chars: [a-zA-Z]+; // 修改为+,确保标签名非空 String : '"' ~["]* '"'; Close_Bracket : ']' -> popMode; // 回到默认模式 TAG_WHITESPACE : ' ' -> skip ;
修改说明
- 新增
TAG_MODE词法模式:仅在遇到[时进入该模式,遇到]时退出,确保标签相关规则仅在[...]内部生效。 - 调整
Chars规则为[a-zA-Z]+:符合PGN标签名非空的规范。 - 优化
Move_time匹配规则:使用~']'*替代.*?,避免ANTLR词法规则中非贪婪匹配的歧义,精准匹配{[%emt...]}格式的时间注释。
修改后,解析Ng4时,词法分析器会在默认模式下将N识别为Moving_Piece、g识别为Moving_File、4识别为Moving_Rank,符合预期解析结果。
内容的提问来源于stack exchange,提问作者user2220139
相关产品推荐
相关产品推荐

