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?