What's the difference between ANTLR4's errorListener and errorHandler?

Viewed 1949

I want to get the specific error message of ANTLR4's parser. And I found that there are two way to handle error: errorListener and errorHandler.

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

parser.setErrorHandler(new BailErrorStrategy());

But I'm confused about the difference between them.

I found that, errorListener can get the specific error message, but it can only print it or log it, can't throw a exception.

The implemention of errorListener as bellow:

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();

    }
}

At the same time, the errorHandler can only throw a exception ParseCancellationException without any specific message.

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

I've try to find a solution, add a transfer method to get detail message from ParseCancellationException, as bellow.

I found that I can get some message from a Token object of RecognitionException, but I can only find the line/charPositionInLine/offendingSymbol message, I don't know where is the detail message, like "missing 'xxx', expect '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);


    }
}

Is it the right way to use errorHandler? How can I get a exception with specific error message?

1 Answers
Related