为何ANTLR解析器对Java中无效数值输入不报错?如何修复?
问题描述
我基于antlr4-runtime-4.13.0编写了如下用于简单条件判断的ANTLR语法:
grammar Condition; @header { package expression; } condition :(expression)('OR' expression)* ; expression : IDENT '=' NUM ; IDENT : ('a'..'z' | 'A'..'Z')+; NUM : [0-9]+; WS : [ \t\r\n]+ -> skip;
并使用如下Java主类进行测试:
public class TestANTLRGrammar extends ConditionBaseListener { public static void main(String[] args) { String entry = "id = 889xx88 OR y = 7"; ConditionLexer lexer = new ConditionLexer(CharStreams.fromString(entry)); TokenStream tokens = new CommonTokenStream(lexer); ConditionParser parser = new ConditionParser(tokens); parser.condition(); System.out.println(parser.getNumberOfSyntaxErrors()); } }
输入内容为id = 889xx88 OR y = 7时,我预期解析器会因889xx88不是有效数值而报错,但解析器仅识别id = 889后便停止,getNumberOfSyntaxErrors()返回0,请问该如何修复此问题?
问题原因与修复方案
核心原因
- ANTLR解析器默认仅匹配部分符合规则的输入,不会主动检查是否处理完所有文本。你的
condition规则没有要求必须消耗到输入末尾,所以解析到id = 889就已经满足规则,不会处理后续的xx88 OR y = 7。 - Lexer遇到无法匹配的字符时,默认不会抛出错误,而是生成无效Token并继续解析,不会触发语法错误计数。
修复方案
1. 强制解析完整输入
在语法的顶层规则末尾添加EOF,要求解析器必须处理完所有输入内容,否则判定为语法错误。修改后的condition规则:
condition : (expression ('OR' expression)*) EOF ;
2. 启用严格的错误检测
默认的错误监听器不会主动抛出异常,只会静默计数。你可以自定义错误监听器,让Lexer和Parser遇到错误时立即抛出异常,或者确保错误被正确计数。
修改测试代码,添加自定义错误监听器:
public class TestANTLRGrammar extends ConditionBaseListener { public static void main(String[] args) { String entry = "id = 889xx88 OR y = 7"; ConditionLexer lexer = new ConditionLexer(CharStreams.fromString(entry)); // 替换默认错误监听器,遇到Lexer错误直接抛出异常 lexer.removeErrorListeners(); lexer.addErrorListener(new BaseErrorListener() { @Override public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) { throw new IllegalStateException(String.format("Lexer错误: 行%d 位置%d - %s", line, charPositionInLine, msg)); } }); TokenStream tokens = new CommonTokenStream(lexer); ConditionParser parser = new ConditionParser(tokens); // 替换默认错误监听器,遇到Parser错误直接抛出异常 parser.removeErrorListeners(); parser.addErrorListener(new BaseErrorListener() { @Override public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) { throw new IllegalStateException(String.format("Parser错误: 行%d 位置%d - %s", line, charPositionInLine, msg)); } }); try { parser.condition(); System.out.println("解析成功,语法错误数: " + parser.getNumberOfSyntaxErrors()); } catch (IllegalStateException e) { System.err.println(e.getMessage()); } } }
3. 验证效果
修改后,当输入id = 889xx88 OR y = 7时:
- Lexer会先匹配
889为NUM,然后遇到x无法匹配任何Token规则,触发Lexer错误。 - 即使Lexer没有报错,添加
EOF后的Parser会发现解析完id = 889后还有未处理的输入,触发Parser错误,确保无效输入被检测到。
内容的提问来源于stack exchange,提问作者S. DAN
相关产品推荐
相关产品推荐

