ANTLR解析不匹配输入无报错,如何触发异常或校验解析完整性?
问题描述
我正在使用ANTLR定义一门简单编程语言,目前仅支持在根级别定义方法,大幅简化后的语法如下:
grammar MyLang; root: declaration* ; declaration: Def ..<snip/snap>.. End; Def: 'def'; End: 'end'; ...
我的解析代码如下:
var charStream = CharStreams.fromString(s) var lexer = new MyLangLexer(charStream); var tokenStream = new CommonTokenStream(lexer); var parser = new MyLangParser(tokenStream); var rootContext = parser.root(); var astFactory = new Parser(); var declarations = astFactory.visitRoot(rootContext);
合法输入(如下方代码)可正常解析:
def foobar() command1 end def bazz() command2 command3 end
但提供非法输入时(例如省略def):
foobar() command1 end
程序无异常抛出,也无解析结果;若非法输入前有合法声明,解析会提前静默终止。请问如何让ANTLR在无法匹配解析规则时抛出异常,或获取最后成功解析的token以验证是否为EOF?
解决方案
1. 开启错误抛出机制
ANTLR默认会静默跳过无法匹配的输入,可通过设置解析器的错误策略,让它遇到语法错误直接抛出异常:
var parser = new MyLangParser(tokenStream); // 替换默认错误处理,遇到错误立即终止并抛出异常 parser.setErrorHandler(new BailErrorStrategy());
此时解析过程中只要出现无法匹配的语法,就会抛出ParseCancellationException,你可以在代码中捕获该异常进行错误处理。
2. 验证解析是否到达输入末尾
若要确认解析是否完整处理了所有输入,可在解析完成后检查token流的当前位置:
var rootContext = parser.root(); // 检查解析结束后,token流是否指向EOF if (tokenStream.LA(1) != Token.EOF) { Token unparsedToken = tokenStream.get(tokenStream.index()); throw new RuntimeException("解析终止于非法输入:行" + unparsedToken.getLine() + ", 列" + unparsedToken.getCharPositionInLine()); }
这种方式能识别出解析提前终止、存在未处理输入的情况。
3. 自定义错误监听器
如果需要更精细的错误控制,可自定义ANTLRErrorListener捕获语法错误:
// 移除默认的控制台错误输出监听器 parser.removeErrorListeners(); parser.addErrorListener(new BaseErrorListener() { @Override public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) { throw new RuntimeException("语法错误:行" + line + ", 列" + charPositionInLine + " - " + msg, e); } });
该监听器会在每次语法错误发生时抛出异常,你可以根据需求自定义错误信息格式或处理逻辑。
内容的提问来源于stack exchange,提问作者Thomas S.
相关产品推荐
相关产品推荐

