ANTLR贪婪规则问题求助:自定义语言解析异常
解决ANTLR语法中的贪婪匹配与空字符串警告问题
我来帮你拆解并解决这两个问题,都是词法规则的定义和优先级导致的:
问题根源分析
- TEXT匹配空字符串的警告:你的
TEXT : ~[\r\n]*;用了*量词,允许零次匹配,遇到空白行时就会匹配空字符串,触发ANTLR的146号警告。 - CMD.NEW被TEXT贪婪匹配:ANTLR词法分析器遵循最长匹配优先原则,且相同长度匹配时先定义的规则优先级更高。你的语法里没有单独定义
.的词法规则,TEXT规则的范围又覆盖了几乎所有非换行字符,所以整个CMD.NEW会被当成一个TEXTtoken,而非拆分成CMD、.、WORD。
分步解决方案
1. 修复TEXT空字符串匹配问题
把TEXT规则的量词从*改成+,要求至少匹配一个字符,彻底避免空字符串匹配:
TEXT : ~[\r\n]+ ;
2. 让命令行元素被正确识别
- 添加
.的词法规则DOT,确保它能被单独识别:DOT : '.' ; - 修改
command_line规则,把硬编码的.替换为DOTtoken:command_line : section DOT command ((COLON WHITESPACE*)? arg)? ; - (可选优化)既然
section固定为CMD,可以直接写CMD代替section,简化规则:command_line : CMD DOT command ((COLON WHITESPACE*)? arg)? ;
3. 处理空白字符(可选但重要)
你的WHITESPACE规则目前会生成单独的token,导致普通行中的空格被拆分,无法被normal_line : TEXT正确匹配。如果希望保留普通行的空格,同时让命令行的空格作为分隔符:
- 可以直接删除
WHITESPACE规则,让空格被包含在TEXT中; - 或者如果命令行的空格需要被忽略,将
WHITESPACE设置为跳过:
这种情况下,WHITESPACE : (' ' | '\t') -> skip ;command_line中的WHITESPACE*可以去掉,因为空格已经被词法分析器跳过了。
修改后的完整语法
grammar MyGrammar; /* Parser Rules */ root : line+ EOF ; line : (comment_line | command_line | normal_line) NEWLINE; comment_line : COMMENT ; // 简化section为固定的CMD,同时调整空白处理逻辑 command_line : CMD DOT command (COLON arg)? ; normal_line : TEXT ; command : WORD ; arg : TEXT ; /* Lexer Rules */ fragment LOWERCASE : [a-z] ; fragment UPPERCASE : [A-Z] ; fragment LETTER : LOWERCASE | UPPERCASE ; fragment DIGIT : [0-9] ; // 先定义关键字和符号,确保优先级高于兜底的TEXT CMD : 'CMD'; DOT : '.' ; COLON : ':' ; COMMENT : '--' ~[\r\n]+; NEWLINE : ('\r'? '\n' | '\r')+; WORD : (LETTER | DIGIT | '_')+ ; // 兜底规则,放在最后,且不匹配空字符串 TEXT : ~[\r\n]+ ; // 如果需要忽略命令行中的空格,启用下面的规则 // WHITESPACE : (' ' | '\t') -> skip ;
测试验证
重新编译并测试CMD.NEW:
$ antlr4 MyGrammar.g4 $ javac MyGrammar*.java $ grun MyGrammar root -tokens CMD.NEW
你会看到正确的token拆分:
[@0,0:2='CMD',<CMD>,1:0] [@1,3:3='.',<DOT>,1:3] [@2,4:6='NEW',<WORD>,1:4] [@3,7:7='\n',<NEWLINE>,1:7] [@4,8:7='<EOF>',<EOF>,2:0]
同时,空字符串的警告也会消失。
内容的提问来源于stack exchange,提问作者sebastian
相关产品推荐
相关产品推荐

