【问题标题】:What's the difference between ANTLR4's errorListener and errorHandler?ANTLR4的errorListener和errorHandler有什么区别?
【发布时间】:2018-01-06 11:49:18
【问题描述】:

我想得到 ANTLR4 解析器的具体错误信息。 而且我发现有两种处理错误的方法:errorListener和errorHandler。

// set error handler
parser.removeErrorListeners();
parser.addErrorListener(new QueryErrorListener());

parser.setErrorHandler(new BailErrorStrategy());

但我对它们之间的区别感到困惑。

我发现,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();

    }
}

同时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) { }
}

我正在尝试寻找解决方案,添加一个传输方法以从 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);


    }
}

errorHandler的使用方法正确吗? 如何获得带有特定错误消息的异常?

【问题讨论】:

  • 我现在可以从 RecognitionException 获取基本信息,但仍然不知道如何构建像 errorListener 那样的可读消息 [@1289,4632:4632='}' 的第 1:4632 行, ,1:4632]: 外部输入 '}' 期待 ','] 。 ANTLR4有没有提供可用的方法?

标签: antlr4


【解决方案1】:

我觉得setErrorHandler 这个名字有点混乱。它应该与您可以在那里设置的内容一致。是为了设置错误策略(当然也是某种处理……)。

错误监听器和错误策略都是应用程序处理解析错误的手段。每个遇到的错误都会调用错误侦听器,并允许应用程序收集它们(例如,在 GUI 中显示它们)。您将收到一条预先生成的错误消息,或者可以根据传入的参数创建自己的错误消息。

错误策略是一个类,它决定在发现错误后如何继续。默认策略是尝试同步到输入流并继续解析。但是,有时您希望解析器立即停止并避免在发现错误后进行冗长的操作。这种所谓的bail-out strategy是ANTLR4中的另一个类,通常用于SLL解析。请参阅 one of my projects 了解其使用方法。

救助错误策略中抛出的ParseCancellationException 是一个没有任何附加信息的异常。它不是用于错误处理(在将其发送给应用程序/用户的意义上,您有错误处理程序),而是抛出一个不是常见解析器异常之一的异常,以绕过所有错误捕获并尽快找到摆脱正在进行的解析运行的方法。您必须在自己的代码中捕获此异常,否则它会冒泡到应用程序的根上下文(并可能导致应用程序退出,具体取决于目标语言)。

【讨论】:

  • 能否从 ParseCancellationException 中获取特定的诊断信息?或者是否可以将errorlistener的错误信息发送给errorHandler?而且我发现,如果我同时设置errorHandler和errorListener,有时errorListener不起作用,但有时却起作用。它们有一些冲突吗?
  • 好吧,由于错误策略可以随时取消解析运行(例如,保释策略),因此并非所有错误都通过错误侦听器报告。 ParseCancellationException 没有任何信息,只有一个目标:我在回答中解释的内容(取消当前的解析运行)。
  • RecognitionException 包含了errorListener 可以获取的所有信息。 RecognitionException 有一个成员“recognizer”,我们可以从中获取错误堆栈,另一个成员“offendingToken”,我们可以获取行(错误行)& charPositionInLine(错误位置)& 本身(错误消息)。但是我们似乎需要自己传输错误消息,因为错误消息类似于'[@1,1:12='"selectmust"',,1:1]',我们需要将其传输到更易读的格式。谢谢你回答我的问题~
猜你喜欢
  • 1970-01-01
  • 2021-12-28
  • 1970-01-01
  • 1970-01-01
  • 2010-10-02
  • 2011-12-12
  • 2010-09-16
  • 2012-03-14
  • 2012-02-06
相关资源
最近更新 更多