ANTLR4的errorListener和errorHandler之间的区别是什么?

时间:2017-07-31 02:57:35

标签: antlr4

我想获得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消息,我不知道详细消息在哪里,比如&#34;缺失&#39; xxx&#39;,期待&#39; yyy&#39;&#34;

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是正确的方法吗? 如何通过特定错误消息获取异常?

1 个答案:

答案 0 :(得分:3)

我发现setErrorHandler这个名字有点令人困惑。它应该与你在那里设置的一致。它是用于设置错误策略(当然也是某种处理......)。

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

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

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