You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ANTLR为何不遵循语法规则顺序?词法匹配异常求助

解决ANTLR词法匹配优先级与解析器规则冲突问题

问题场景

定义了如下ANTLR语法片段:

zLine
  : Z fahrtnummer verwaltung takanzahl? taktZeitInMinuten? COMMENT ANYTHING NL
  ;

fahrtnummer
  : INT
  ;

verwaltung
  : ASCII
  ;

输入行示例:

*Z 00006 003849                                           % 00006 003849    00

期望00006匹配INT(作为fahrtnummer),003849匹配ASCII(作为verwaltung),但实际运行中:

  • 原语法中INT定义在ASCII前,003849被识别为INT,导致解析器报错missing ASCII
  • 调换INT与ASCII的定义顺序后,所有字母数字串都被识别为ASCII,fahrtnummer等位置也无法匹配INT,同样不符合需求

完整语法、输入文件及测试输出如下:

完整ANTLR语法

grammar FPLAN3;

fplan
  : zLine*;

zLine
  : Z fahrtnummer verwaltung takanzahl? taktZeitInMinuten? COMMENT ANYTHING NL
  ;

fahrtnummer
  : INT
  ;

verwaltung
  : ASCII
  ;

takanzahl
  : INT
  ;

taktZeitInMinuten
  : INT
  ;

Z: '*Z';

INT: [0-9]+;
ASCII: [0-9a-zA-Z]+;
//ASCII: [\P{Cc}\P{Cn}\P{Cs}]+;
COMMENT: '%';
ANYTHING: .*?;
NL: '\r'? '\n' | '\r';

IGNORE_SPACE
 : [ ] -> skip
 ;

输入文件

*Z 00006 003849                                           % 00006 003849    00
*Z 00007 003849                                           % 00007 003849    00
*Z 00008 003849                                           % 00008 003849    00

测试输出

[@0,0:1='*Z',<'*Z'>,1:0]
[@1,3:7='00006',<INT>,1:3]
[@2,9:14='003849',<INT>,1:9]
[@3,58:58='%',<'%'>,1:58]
[@4,60:64='00006',<INT>,1:60]
[@5,66:71='003849',<INT>,1:66]
[@6,76:77='00',<INT>,1:76]
[@7,78:79='\r\n',<NL>,1:78]
[@8,80:81='*Z',<'*Z'>,2:0]
[@9,83:87='00007',<INT>,2:3]
[@10,89:94='003849',<INT>,2:9]
[@11,138:138='%',<'%'>,2:58]
[@12,140:144='00007',<INT>,2:60]
[@13,146:151='003849',<INT>,2:66]
[@14,156:157='00',<INT>,2:76]
[@15,158:159='\r\n',<NL>,2:78]
[@16,160:161='*Z',<'*Z'>,3:0]
[@17,163:167='00008',<INT>,3:3]
[@18,169:174='003849',<INT>,3:9]
[@19,218:218='%',<'%'>,3:58]
[@20,220:224='00008',<INT>,3:60]
[@21,226:231='003849',<INT>,3:66]
[@22,236:237='00',<INT>,3:76]
[@23,238:237='<EOF>',<EOF>,3:78]
line 1:9 missing ASCII at '003849'
line 1:60 mismatched input '00006' expecting ANYTHING
line 2:9 missing ASCII at '003849'
line 2:60 mismatched input '00007' expecting ANYTHING
line 3:9 missing ASCII at '003849'
line 3:60 mismatched input '00008' expecting ANYTHING
(fplan (zLine *Z (fahrtnummer 00006) (verwaltung <missing ASCII>) (takanzahl 003849) % 00006 003849 00 \r\n) (zLine *Z (fahrtnummer 00007) (verwaltung <missing ASCII>) (takanzahl 003849) % 00007 003849 00 \r\n) (zLine *Z (fahrtnummer 00008) (verwaltung <missing ASCII>) (takanzahl 003849) % 00008 003849 00))

核心原因

ANTLR的词法分析遵循最长匹配优先原则,当多个词法规则匹配同一输入时,会选择匹配长度最长的规则;若长度相同,则遵循定义顺序优先,即先定义的规则优先匹配。

你定义的INT和ASCII规则存在重叠:纯数字串同时满足两个规则,因此词法分析阶段会直接按优先级生成对应token,完全独立于解析器的规则顺序——解析器只能使用词法阶段生成的token,无法强制改变词法匹配结果。

此外,ANYTHING: .*?的非贪婪匹配会导致它提前终止,无法正确匹配COMMENT到NL之间的所有内容,进一步引发匹配错误。

解决方案

根据需求选择以下两种方案:

方案1:合并词法规则,用解析器规则区分位置/语义

将重叠的词法规则合并为一个通用规则,在解析器层通过位置或语义谓词区分不同用途的token,适用于verwaltung允许为纯数字的场景:

grammar FPLAN3;

fplan
  : zLine*;

zLine
  : Z fahrtnummer verwaltung takanzahl? taktZeitInMinuten? COMMENT ANYTHING NL
  ;

// 仅匹配纯数字的TOKEN作为fahrtnummer
fahrtnummer
  : TOKEN { $TOKEN.text.matches("\\d+") }?
  ;

// 直接取第二个TOKEN作为verwaltung(允许纯数字)
verwaltung
  : TOKEN
  ;

// 仅匹配纯数字的TOKEN作为takanzahl
takanzahl
  : TOKEN { $TOKEN.text.matches("\\d+") }?
  ;

// 仅匹配纯数字的TOKEN作为taktZeitInMinuten
taktZeitInMinuten
  : TOKEN { $TOKEN.text.matches("\\d+") }?
  ;

Z: '*Z';

// 合并原INT和ASCII规则
TOKEN: [0-9a-zA-Z]+;
COMMENT: '%';
// 修改ANYTHING为匹配换行前的所有字符
ANYTHING: ~[\r\n]*;
NL: '\r'? '\n' | '\r';

IGNORE_SPACE
 : [ ] -> skip
 ;

方案2:修改ASCII规则,消除与INT的重叠

如果verwaltung必须包含字母(不能是纯数字),可调整ASCII规则,确保它仅匹配包含字母的字符串,避免与INT规则冲突:

// 修改词法规则部分
INT: [0-9]+;
// 要求至少包含一个字母,避免纯数字匹配ASCII
ASCII: [a-zA-Z][0-9a-zA-Z]*;
COMMENT: '%';
// 修复ANYTHING规则
ANYTHING: ~[\r\n]*;
NL: '\r'? '\n' | '\r';

效果验证

修改后,输入中的00006会被正确识别为fahrtnummer,003849被识别为verwaltung,COMMENT后的内容也能完整匹配ANYTHING,不再出现缺失或不匹配的错误。

内容的提问来源于stack exchange,提问作者BetaRide

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 12:48:25