/*******************************************************************************
* Copyright (c) 2008 SAP
* see https://research.qkal.sap.corp/mediawiki/index.php/CoMONET
*
* Date: $Date: 2009-04-23 14:54:43 +0200 (Do, 23 Apr 2009) $
* Revision: $Revision: 6272 $
* Author: $Author: c5106462 $
*******************************************************************************/
package com.sap.furcas.runtime.parser.impl;
import org.antlr.runtime.CharStream;
import org.antlr.runtime.EarlyExitException;
import org.antlr.runtime.FailedPredicateException;
import org.antlr.runtime.MismatchedNotSetException;
import org.antlr.runtime.MismatchedRangeException;
import org.antlr.runtime.MismatchedSetException;
import org.antlr.runtime.MismatchedTokenException;
import org.antlr.runtime.MismatchedTreeNodeException;
import org.antlr.runtime.NoViableAltException;
import org.antlr.runtime.RecognitionException;
import org.antlr.runtime.Token;
import com.sap.furcas.runtime.parser.ANTLR3LocationToken;
import com.sap.furcas.runtime.parser.IStreamIndexProvider;
import com.sap.furcas.runtime.parser.ParsingError;
import com.sap.furcas.runtime.parser.TextLocation;
/**
* Deciphers ANTLR3 error messages and creates an Error class. TODO use
* externalized Strings an i18n.
*
* @author C5107456
*/
public class ErrorMessageGenerator {
/**
* Gets the parsing error.
*
* @param e the exception
* @param tokenNames the token names
*
* @return the parsing error
*/
public static ParsingError getParsingError(RecognitionException e, String[] tokenNames) {
// e.printStackTrace();
String msg = null;
ANTLR3LocationToken token = null;
if (e.token instanceof ANTLR3LocationToken) {
token = (ANTLR3LocationToken) e.token;
}
if (e.input instanceof CharStream) { // Lexer Exception
if (e instanceof MismatchedTokenException) {
MismatchedTokenException mte = (MismatchedTokenException) e;
msg = "Mismatched character " + getCharErrorDisplay(e.c) + " expecting " + getCharErrorDisplay(mte.expecting);
} else if (e instanceof NoViableAltException) {
// NoViableAltException nvae = (NoViableAltException) e;
// for development, can add
// "decision=<<"+nvae.grammarDecisionDescription+">>"
// and "(decision="+nvae.decisionNumber+") and
// "state "+nvae.stateNumber
msg = "No viable alternative at character " + getCharErrorDisplay(e.c);
} else if (e instanceof EarlyExitException) {
// EarlyExitException eee = (EarlyExitException) e;
// for development, can add "(decision="+eee.decisionNumber+")"
msg = "Required (...)+ loop did not match anything at character " + getCharErrorDisplay(e.c);
} else if (e instanceof MismatchedNotSetException) {
MismatchedNotSetException mse = (MismatchedNotSetException) e;
msg = "Mismatched character " + getCharErrorDisplay(e.c) + " expecting set " + mse.expecting;
} else if (e instanceof MismatchedSetException) {
MismatchedSetException mse = (MismatchedSetException) e;
msg = "mismatched character " + getCharErrorDisplay(e.c) + " expecting set " + mse.expecting;
} else if (e instanceof MismatchedRangeException) {
MismatchedRangeException mre = (MismatchedRangeException) e;
msg = "Mismatched character " + getCharErrorDisplay(e.c) + " expecting set " + getCharErrorDisplay(mre.a) + ".."
+ getCharErrorDisplay(mre.b);
} else {
msg = "Unknown Exception";
}
} else { // Parser Exception
if (e instanceof MismatchedTokenException) {
MismatchedTokenException mte = (MismatchedTokenException) e;
String tokenName = "<unknown>";
if (mte.expecting == Token.EOF) {
tokenName = "EOF";
} else {
if (tokenNames != null) {
tokenName = tokenNames[mte.expecting];
} else {
tokenName = "<Bug>";
}
}
msg = "Mismatched input " + getTokenErrorDisplay(e.token) + " expecting " + tokenName;
} else if (e instanceof MismatchedTreeNodeException) {
MismatchedTreeNodeException mtne = (MismatchedTreeNodeException) e;
String tokenName = "<unknown>";
if (mtne.expecting == Token.EOF) {
tokenName = "EOF";
} else {
if (tokenNames != null) {
tokenName = tokenNames[mtne.expecting];
} else {
tokenName = "<Bug>";
}
}
msg = "Mismatched tree node: " + mtne.node + " expecting " + tokenName;
} else if (e instanceof NoViableAltException) {
// NoViableAltException nvae = (NoViableAltException) e;
// for development, can add
// "decision=<<"+nvae.grammarDecisionDescription+">>"
// and "(decision="+nvae.decisionNumber+") and
// "state "+nvae.stateNumber
msg = "No viable alternative at input " + getTokenErrorDisplay(e.token);
} else if (e instanceof EarlyExitException) {
// EarlyExitException eee = (EarlyExitException) e;
// for development, can add "(decision="+eee.decisionNumber+")"
msg = "Required (...)+ loop did not match anything at input " + getTokenErrorDisplay(e.token);
} else if (e instanceof MismatchedSetException) {
MismatchedSetException mse = (MismatchedSetException) e;
msg = "Mismatched input " + getTokenErrorDisplay(e.token) + " expecting set " + mse.expecting;
} else if (e instanceof MismatchedNotSetException) {
MismatchedNotSetException mse = (MismatchedNotSetException) e;
msg = "Mismatched input " + getTokenErrorDisplay(e.token) + " expecting set " + mse.expecting;
} else if (e instanceof FailedPredicateException) {
FailedPredicateException fpe = (FailedPredicateException) e;
msg = "Rule " + fpe.ruleName + " failed predicate: {" + fpe.predicateText + "}?";
} else {
msg = "Unknown Exception";
}
}
if (token != null) {
return new ParsingError(msg, token);
} else {
// Happens for lexer errors. Need to reconstruct the absolut index
// when we are running
// in the incremental lexing mode
int index = e.index;
if (e.input instanceof IStreamIndexProvider) {
index = ((IStreamIndexProvider) e.input).makeIndexAbsolute(index);
}
TextLocation location = new TextLocation(index, index, e.line, e.charPositionInLine, e.line, e.charPositionInLine);
return new ParsingError(msg, location);
}
}
/**
* How should a token be displayed in an error message? The default is to
* display just the text, but during development you might want to have a
* lot of information spit out. Override in that case to use t.toString()
* (which, for CommonToken, dumps everything about the token). This is
* better than forcing you to override a method in your token objects
* because you don't have to go modify your lexer so that it creates a new
* Java type.
*
* @param t the t
*
* @return the token error display
*/
public static String getTokenErrorDisplay(Token t) {
if (t != null) {
String s = t.getText();
if (s == null) {
if (t.getType() == Token.EOF) {
s = "<EOF>";
} else {
s = "<" + t.getType() + ">";
}
}
s = s.replaceAll("\n", "\\\\n");
s = s.replaceAll("\r", "\\\\r");
s = s.replaceAll("\t", "\\\\t");
return "'" + s + "'";
} else {
return "<Bug2>";
}
}
/**
* Gets the char error display.
*
* @param c the c
*
* @return the char error display
*/
public static String getCharErrorDisplay(int c) {
String s = String.valueOf((char) c);
switch (c) {
case Token.EOF:
s = "<EOF>";
break;
case '\n':
s = "\\n";
break;
case '\t':
s = "\\t";
break;
case '\r':
s = "\\r";
break;
}
return "'" + s + "'";
}
}