当解析器不知道该做什么时,默认行为是将消息打印到终端,如:
第1:23行“}”处缺少小数点
这是一个好消息,但在错误的地方。我宁愿把这当作一个例外
我尝试过使用BailErrorStrategy,但这会抛出一个没有消息的ParseCancellationException(由输入不匹配异常引起,也没有消息)
我有没有办法让它通过异常报告错误,同时在消息中保留有用的信息
以下是我真正想要的——我通常使用规则中的操作来构建对象:
dataspec返回[DataExtractor-extractor]
@初始化{
DataExtractorBuilder=新的DataExtractorBuilder(布局);
}
@之后{
$extractor=builder.create();
}
:first=expr{builder.addAll($first.values);}(逗号next=expr{builder.addAll($next.values);})*EOF
;
expr返回[List<;ValueExtractor>;值]
:a=atom{$values=Arrays.asList($a.val);}
|fields=fieldrange{$values=values($fields.fields);}
|“%”{$values=null;}
|星号{$values=值(布局);}
;
然后,当我调用解析器时,我会执行如下操作:
公共静态数据提取器创建(字符串数据规范){
CharStream=新的AntlInputStream(dataspec);
DataSpecificationLexer=新的DataSpecificationLexer(流);
CommonTokenStream令牌=新的CommonTokenStream(lexer);
DataSpecificationParser=新的DataSpecificationParser(令牌);
返回parser.dataspec().extractor;
}
我真正想要的是
- 对于
dataspec()调用,当无法解析输入时抛出异常(理想情况下是选中的异常) - 使该异常具有有用的消息,并提供对发现问题的行号和位置的访问
然后,我会让这个异常在调用堆栈中冒泡,直到最适合向用户显示有用消息的地方——就像我处理断开的网络连接、读取损坏的文件等一样
我确实看到,在ANTLR4中,动作现在被认为是“高级的”,所以我可能会以一种奇怪的方式来处理事情,但我还没有研究“非高级”的方式是什么,因为这种方式已经很好地满足了我们的需要
由于我对现有的两个答案有点纠结,我想分享一下我最终得到的解决方案
首先,我创建了自己版本的ErrorListener,就像Sam Harwell建议的那样:
公共类ThrowingerListener扩展了BaseErrorListener{
公共静态最终ThrowingerListener实例=新ThrowingerListener();
@凌驾
public void syntaxError(识别器<;?,?>;识别器,对象违规符号,int-line,int-charPositionInLine,字符串消息,识别异常e)
抛出ParseCancellationException异常{
抛出新的ParseCancellationException(“行”+line+:“+charPositionInLine+”+msg);
}
}
注意使用ParseCancellationException而不是RecognitionException,因为DefaultErrorStrategy将捕获后者,并且它永远不会到达您自己的代码
没有必要像Brad Mace建议的那样创建一个全新的ErrorStrategy,因为默认情况下DefaultErrorStrategy会生成非常好的错误消息
然后在解析函数中使用自定义ErrorListener:
公共静态字符串解析(字符串文本)抛出ParseCancellationException{
MyLexer lexer=新的MyLexer(新的antlInputStream(text));
lexer.removeErrorListeners();
addErrorListener(throwingererrListener.INSTANCE);
CommonTokenStream令牌=新的CommonTokenStream(lexer);
MyParser=新的MyParser(令牌);
removeErrorListeners();
addErrorListener(throwingerErrorListener.INSTANCE);
ParserRuleContext树=parser.expr();
MyParseRules提取器=新建MyParseRules();
返回提取器。访问(树);
}
(有关MyParseRules功能的更多信息,请参见此处。)
这将提供与默认情况下打印到控制台相同的错误消息,只是以适当的异常形式