/*******************************************************************************
* Copyright (c) 2007, 2008 Edgar Espina.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
*******************************************************************************/
package org.deved.antlride.core.interpreter;
import org.antlr.runtime.EarlyExitException;
import org.antlr.runtime.FailedPredicateException;
import org.antlr.runtime.MismatchedNotSetException;
import org.antlr.runtime.MismatchedSetException;
import org.antlr.runtime.MismatchedTokenException;
import org.antlr.runtime.MismatchedTreeNodeException;
import org.antlr.runtime.MissingTokenException;
import org.antlr.runtime.NoViableAltException;
import org.antlr.runtime.RecognitionException;
import org.antlr.runtime.Token;
import org.antlr.runtime.UnwantedTokenException;
import org.antlr.tool.Grammar;
import org.deved.antlride.core.model.IGrammar;
import org.deved.antlride.core.model.evaluation.EvalElementFactory;
import org.deved.antlride.core.model.evaluation.IEvalElement;
import org.deved.antlride.core.model.evaluation.IResultEvalElement;
class AntlrEvalElementBuilder extends
org.antlr.runtime.debug.BlankDebugEventListener {
private int inDecision = 0;
private IEvalElement element;
private IEvalElement current;
private String resultDescription;
private IGrammar g;
private Grammar nativeG;
public AntlrEvalElementBuilder(IGrammar g) {
this.g = g;
}
public void commence(Grammar nativeG) {
this.nativeG = nativeG;
}
public IResultEvalElement getResult() {
IResultEvalElement resultEvalElement = EvalElementFactory
.createResultEvalElement(g, resultDescription != null,
resultDescription, element);
current = null;
return resultEvalElement;
}
@Override
public void enterDecision(int decisionNumber) {
super.enterDecision(decisionNumber);
inDecision++;
}
@Override
public void exitDecision(int decisionNumber) {
inDecision--;
super.exitDecision(decisionNumber);
}
@Override
public void enterRule(String s, String ruleName) {
createRuleEvalElement(ruleName);
}
@Override
public void exitRule(String s, String ruleName) {
if (current != null)
current = current.getParent();
}
@Override
public void consumeToken(Token token) {
if (inDecision == 0) {
String tokenDisplayName = getTokenName(token.getType());
boolean isLexerRule = tokenDisplayName.charAt(0) != '\'';
if (isLexerRule) {
createRuleEvalElement(tokenDisplayName);
}
createTokenEvalElement(token.getText(), token.getLine(), token
.getCharPositionInLine());
if (isLexerRule) {
current = current.getParent();
}
}
}
@Override
public void recognitionException(RecognitionException e) {
String hdr = getErrorHeader(e);
String msg = getErrorMessage(e);
createExceptionEvalElement(e.getClass().getSimpleName(), hdr + " "
+ msg, e.line, e.charPositionInLine);
}
private void createRuleEvalElement(String elementName) {
IEvalElement evalElement = EvalElementFactory.createRuleEvalElement(g,
current, elementName);
addElement(evalElement);
// set current node
current = evalElement;
}
private void createTokenEvalElement(String text, int line, int column) {
IEvalElement evalElement = EvalElementFactory.createTokenEvalElement(g,
current, text, line, column);
addElement(evalElement);
}
private void createExceptionEvalElement(String exception, String text,
int line, int column) {
IEvalElement evalElement = EvalElementFactory
.createExceptionEvalElement(g, current, exception, text, line,
column);
this.resultDescription = text;
addElement(evalElement);
}
private void addElement(IEvalElement evalElement) {
if (element == null) {
element = evalElement;
}
}
/**
* 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.
*/
private String getTokenErrorName(Token t) {
if (t == null) {
return "UNKNOW";
}
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 + "'";
}
@Override
public void terminate() {
nativeG = null;
}
private String getTokenName(int type) {
return nativeG.getTokenDisplayName(type);
}
/**
* What is the error header, normally line/character position information?
*
* @see org.antlr.runtime.BaseRecognizer#getErrorHeader(RecognitionException)
* */
private String getErrorHeader(RecognitionException e) {
return "line " + e.line + ":" + e.charPositionInLine;
}
/**
* What error message should be generated for the various exception types?
*
* Not very object-oriented code, but I like having all error message
* generation within one method rather than spread among all of the
* exception classes. This also makes it much easier for the exception
* handling because the exception classes do not have to have pointers back
* to this object to access utility routines and so on. Also, changing the
* message for an exception type would be difficult because you would have
* to subclassing exception, but then somehow get ANTLR to make those kinds
* of exception objects instead of the default. This looks weird, but trust
* me--it makes the most sense in terms of flexibility.
*
* For grammar debugging, you will want to override this to add more
* information such as the stack frame with getRuleInvocationStack(e,
* this.getClass().getName()) and, for no viable alts, the decision
* description and state etc...
*
* Override this to change the message generated for one or more exception
* types.
*
* @see org.antlr.runtime.BaseRecognizer#getErrorMessage(RecognitionException,
* String[])
*/
private String getErrorMessage(RecognitionException e) {
String msg = e.getMessage();
if (e instanceof UnwantedTokenException) {
UnwantedTokenException ute = (UnwantedTokenException) e;
String tokenName = "<unknown>";
if (ute.expecting == Token.EOF) {
tokenName = "EOF";
} else {
tokenName = getTokenName(ute.expecting);
}
msg = "extraneous input "
+ getTokenErrorName(ute.getUnexpectedToken())
+ " expecting " + tokenName;
} else if (e instanceof MissingTokenException) {
MissingTokenException mte = (MissingTokenException) e;
String tokenName = "<unknown>";
if (mte.expecting == Token.EOF) {
tokenName = "EOF";
} else {
tokenName = getTokenName(mte.expecting);
}
msg = "missing " + tokenName + " at " + getTokenErrorName(e.token);
} else if (e instanceof MismatchedTokenException) {
MismatchedTokenException mte = (MismatchedTokenException) e;
String tokenName = "<unknown>";
if (mte.expecting == Token.EOF) {
tokenName = "EOF";
} else {
tokenName = getTokenName(mte.expecting);
}
msg = "mismatched input " + getTokenErrorName(e.token)
+ " expecting " + tokenName;
} else if (e instanceof MismatchedTreeNodeException) {
MismatchedTreeNodeException mtne = (MismatchedTreeNodeException) e;
String tokenName = "<unknown>";
if (mtne.expecting == Token.EOF) {
tokenName = "EOF";
} else {
tokenName = getTokenName(mtne.expecting);
}
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 "
+ getTokenErrorName(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 "
+ getTokenErrorName(e.token);
} else if (e instanceof MismatchedSetException) {
MismatchedSetException mse = (MismatchedSetException) e;
msg = "mismatched input " + getTokenErrorName(e.token)
+ " expecting set " + mse.expecting;
} else if (e instanceof MismatchedNotSetException) {
MismatchedNotSetException mse = (MismatchedNotSetException) e;
msg = "mismatched input " + getTokenErrorName(e.token)
+ " expecting set " + mse.expecting;
} else if (e instanceof FailedPredicateException) {
FailedPredicateException fpe = (FailedPredicateException) e;
msg = "rule " + fpe.ruleName + " failed predicate: {"
+ fpe.predicateText + "}?";
}
return msg;
}
}