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

ANTLR4匹配词法规则而非解析规则的问题排查

ANTLR4语法解析问题排查与解决

问题背景

用户提供的ANTLR4语法文件如下:

grammar test;

prog: (decl | expr)+
;
decl: doc | quiz
;

doc: '%doc' paramlist
;

quiz: '%quiz' paramlist STR? '%quiz' ENDL
;
paramlist: '(' VAR '=' PARAMVAL {, VAR '=' PARAMVAL}')'
;
expr:expr '\*' expr
|expr '+' expr
|expr '-' expr
|DOC
;

// tokens
DOC: 'doc';
PERCENT: '%';
VAR:  [a-zA-Z_][a-zA-Z0-9_]* ;
PARAMVAL: [^,]+|'"'[^"]*'"' ;
STR: (~["\\r\n] | EscapeSequence)+ ;
fragment EscapeSequence:
'\\' 'u005c'? [btnfr"'\\]
| '\\' 'u005c'? ([0-3]? [0-7])? [0-7]
| '\\' 'u'+ HexDigit HexDigit HexDigit HexDigit;
fragment HexDigit: [0-9a-fA-F];
ENDL: '\n' ;
WS: [ \t\n]+ -> skip;

遇到的实际问题

  • 输入%doc时能被正常识别,但补充PARAMVAL内容后,解析树会把所有内容识别为STR
  • quiz规则存在完全相同的识别异常
  • 疑惑:STR仅在quiz规则的中间部分被引用,为何会在其他场景中被优先识别

问题核心原因

ANTLR4的词法分析遵循最长匹配+先定义token优先级更高的规则,当前冲突的关键在于:

  • STR的匹配范围过大:(~["\\r\n] | EscapeSequence)+意味着除双引号、反斜杠、换行外的所有字符都能被匹配,覆盖了PARAMVAL、VAR甚至%doc这类内容的匹配范围
  • 即使其他token先定义,只要输入内容能被STR匹配出更长的序列,就会优先被识别为STR

具体修复方案

1. 修正语法错误

原paramlist中的{, VAR '=' PARAMVAL}是错误写法,ANTLR4中需用(',' VAR '=' PARAMVAL)*表示零或多个重复项:

paramlist: '(' VAR '=' PARAMVAL (',' VAR '=' PARAMVAL)* ')'
;

2. 调整词法规则顺序

把匹配范围更具体的token(VAR、PARAMVAL)放在STR之前定义,利用ANTLR4的"先定义token优先级更高"规则,避免被STR覆盖:

// 调整后的token顺序
VAR:  [a-zA-Z_][a-zA-Z0-9_]* ;
PARAMVAL: '"'~'"'*'"' | [a-zA-Z0-9_]+ ;
DOC: 'doc';
PERCENT: '%';
STR: (~[%\\r\n] | EscapeSequence)+ ;

3. 缩小STR匹配范围

为STR添加明确的排除项(比如%),避免和quiz的边界标识冲突,同时限制其仅匹配符合预期的内容:

STR: (~[%\\r\n] | EscapeSequence)+ ; // 排除%,防止误匹配quiz的边界

4. 优化PARAMVAL定义

将原PARAMVAL的模糊匹配[^,]+改为精准匹配,避免和STR的范围重叠:

PARAMVAL: '"'~'"'*'"' | [a-zA-Z0-9_]+ ; // 仅匹配带引号的字符串或标识符

修复后的完整语法示例

grammar test;

prog: (decl | expr)+
;
decl: doc | quiz
;

doc: '%doc' paramlist
;

quiz: '%quiz' paramlist STR? '%quiz' ENDL
;
paramlist: '(' VAR '=' PARAMVAL (',' VAR '=' PARAMVAL)* ')'
;
expr: expr '*' expr
    | expr '+' expr
    | expr '-' expr
    | DOC
    ;

// tokens:按匹配精准度排序
VAR:  [a-zA-Z_][a-zA-Z0-9_]* ;
PARAMVAL: '"'~'"'*'"' | [a-zA-Z0-9_]+ ;
DOC: 'doc';
PERCENT: '%';
STR: (~[%\\r\n] | EscapeSequence)+ ;
fragment EscapeSequence:
'\\' ('u005c'? [btnfr"'\\])
| '\\' ('u005c'? ([0-3]? [0-7])? [0-7])
| '\\' 'u'+ HexDigit HexDigit HexDigit HexDigit;
fragment HexDigit: [0-9a-fA-F];
ENDL: '\n' ;
WS: [ \t]+ -> skip; // 移除\n,避免和ENDL冲突

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 03:12:19