使用ANTLR解析Java表达式时的解析报错及错误信息获取问题
问题1:表达式解析不完整/链式方法调用报错的解决
你最初调用parser.expression()仅能解析到部分内容,核心原因是ANTLR默认不会强制匹配全部输入流,只要匹配到规则的合法前缀就会终止解析。你把表达式包装成return语句的方案本质是想强制全匹配,但这个方案触发了Java8官方语法规则的隐含边界问题,才会出现三级链式方法调用报错的异常。
你之前包装return语句后出现的三级链式调用报错,是因为Java8语法中return语句后的表达式规则隐含了上下文限制,部分场景下会错误截断方法调用的规则匹配,用带EOF的自定义顶层规则可以完全规避这个问题。
最优解决方案是自定义顶层解析规则强制匹配全输入,不需要修改原有Java8语法逻辑,只需要做如下调整:
- 如果你用的是官方标准Java8.g4语法文件,直接在语法文件末尾新增一条顶层规则:
fullExpression: expression EOF;
重新生成Lexer和Parser类之后,直接调用该规则即可完成完整表达式解析:
Java8Lexer lexer = new Java8Lexer(CharStreams.fromString(text)); CommonTokenStream tokens = new CommonTokenStream(lexer); Java8Parser parser = new Java8Parser(tokens); // 关闭默认错误监听器,避免输出到stderr lexer.removeErrorListeners(); parser.removeErrorListeners(); // 调用新增的顶层规则,自动强制匹配全输入 ParseTree result = parser.fullExpression(); // 实际表达式节点是result的第一个子节点 result = result.getChild(0);
这个方案可以完美支持所有合法Java表达式的解析,包括多级链式方法调用,不需要再做return语句包装,也不会出现你遇到的匹配异常。
问题2:捕获解析错误信息
ANTLR默认会把所有语法错误输出到标准错误流,你可以通过自定义错误监听器的方式收集所有错误信息,步骤如下:
- 先定义自己的错误收集监听器:
public class SyntaxErrorListener extends BaseErrorListener { private final List<String> errors = new ArrayList<>(); @Override public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) { errors.add(String.format("第%d行第%d列: %s", line, charPositionInLine, msg)); } public List<String> getErrors() { return errors; } }
- 在解析前把自定义监听器注册到Lexer和Parser上:
SyntaxErrorListener errorListener = new SyntaxErrorListener(); lexer.addErrorListener(errorListener); parser.addErrorListener(errorListener);
- 解析完成后,直接调用
errorListener.getErrors()就能拿到所有语法错误信息,不需要从ParseTree中提取,也不会有多余的控制台输出。
内容的提问来源于stack exchange,提问作者Andrew B
相关产品推荐
相关产品推荐

