我想得到ANTLR4解析器的具体错误信息。我发现有两种处理错误的方法:errorListener 和 errorHandler。
// set error handler
parser.removeErrorListeners();
parser.addErrorListener(new QueryErrorListener());
parser.setErrorHandler(new BailErrorStrategy());
Run Code Online (Sandbox Code Playgroud)
但我对它们之间的区别感到困惑。
我发现,errorListener可以获取具体的错误信息,但是只能打印或者记录,不能抛出异常。
errorListener 的实现如下:
public class QueryErrorListener extends BaseErrorListener {
private static final Logger LOGGER = LoggerFactory.getLogger(QueryDispatcher.class);
@Override
public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol,
int line, int charPositionInLine, String msg,
RecognitionException e)
{
List<String> stack = ((Parser)recognizer).getRuleInvocationStack(); Collections.reverse(stack);
String errorMessage = "line "+line+":"+charPositionInLine+" at "+
offendingSymbol+": "+msg;
LOGGER.error("rule stack: "+stack);
LOGGER.error(errorMessage);
QueryParseErrorStrategy queryParseErrorStrategy = new QueryParseErrorStrategy();
}
}
Run Code Online (Sandbox Code Playgroud)
同时errorHandler只能抛出异常ParseCancellationException,没有任何具体的消息。
public class BailErrorStrategy extends DefaultErrorStrategy {
/** Instead of recovering from exception {@code e}, re-throw it wrapped
* in a {@link ParseCancellationException} so it is not caught by the
* rule function catches. Use {@link Exception#getCause()} to get the
* original {@link RecognitionException}.
*/
@Override
public void recover(Parser recognizer, RecognitionException e) {
for (ParserRuleContext context = recognizer.getContext(); context != null; context = context.getParent()) {
context.exception = e;
}
throw new ParseCancellationException(e);
}
/** Make sure we don't attempt to recover inline; if the parser
* successfully recovers, it won't throw an exception.
*/
@Override
public Token recoverInline(Parser recognizer)
throws RecognitionException
{
InputMismatchException e = new InputMismatchException(recognizer);
for (ParserRuleContext context = recognizer.getContext(); context != null; context = context.getParent()) {
context.exception = e;
}
throw new ParseCancellationException(e);
}
/** Make sure we don't attempt to recover from problems in subrules. */
@Override
public void sync(Parser recognizer) { }
}
Run Code Online (Sandbox Code Playgroud)
我试图找到一个解决方案,添加一个传输方法来从 ParseCancellationException 获取详细消息,如下所示。
我发现我可以从 RecognitionException 的 Token 对象获取一些消息,但是我只能找到 line/charPositionInLine/offendingSymbol 消息,我不知道详细消息在哪里,例如“缺少 'xxx',期望 'yyy' ”
public class ANTLRExceptionTransfer {
public static SemanticException transfer(RecognitionException re) {
String errorMsg = "";
Recognizer<?, ?> recognizer = re.getRecognizer();
Token offendingSymbol = re.getOffendingToken();
int line = offendingSymbol.getLine();
int charPositionInLine = offendingSymbol.getCharPositionInLine();
// ????????
String msg = "";
List<String> stack = ((Parser)recognizer).getRuleInvocationStack();
Collections.reverse(stack);
String errorMessage = "rule stack: "+stack;
errorMessage = "\nline "+line+":"+charPositionInLine+" at "+
offendingSymbol+": "+msg;
return new SemanticException(errorMessage);
}
}
Run Code Online (Sandbox Code Playgroud)
这是使用 errorHandler 的正确方法吗?如何获得带有特定错误消息的异常?
我觉得这个setErrorHandler名字有点混乱。它应该与您可以在那里设置的内容一致。它用于设置错误策略(这当然也是某种处理......)。
错误侦听器和错误策略都是应用程序处理解析错误的手段。每个遇到的错误都会调用错误侦听器,并允许应用程序收集它们(例如,在 GUI 中显示它们)。您将收到一条预先生成的错误消息,或者可以根据传入的参数创建自己的错误消息。
错误策略是一个类,用于确定发现错误后如何继续。默认状态是尝试同步到输入流并继续解析。然而,有时您希望解析器在发现错误后立即停止并避免冗长的操作。这个所谓的救助策略是ANTLR4中的另一个类,通常用于SLL解析。请参阅我的一个项目,了解如何使用它。
ParseCancellationException救助错误策略中抛出的异常是没有任何附加信息的异常。它不是用于错误处理(在将其发送给应用程序/用户的意义上,您有错误处理程序),而是抛出一个异常,这不是通常的解析器异常之一,以绕过所有错误捕获并尽快找到摆脱正在进行的解析运行的方法。您必须在自己的代码中捕获此异常,否则它会冒泡到应用程序的根上下文(并可能导致应用程序退出,具体取决于目标语言)。