Move primary implementation of getExpectedTokens to ATN, fixes #191

This commit is contained in:
Sam Harwell 2013-03-21 13:52:36 -05:00
parent 4f29c2fe3d
commit bc17cd4a28
3 changed files with 63 additions and 28 deletions

View File

@ -550,32 +550,16 @@ public abstract class Parser extends Recognizer<Token, ParserATNSimulator> {
return false; return false;
} }
/** Compute the set of valid tokens reachable from the current /**
* position in the parse. * Computes the set of input symbols which could follow the current parser
* state and context, as given by {@link #getState} and {@link #getContext},
* respectively.
*
* @see ATN#getExpectedTokens(int, RuleContext)
*/ */
public IntervalSet getExpectedTokens() { public IntervalSet getExpectedTokens() {
ATN atn = getInterpreter().atn; return getATN().getExpectedTokens(getState(), getContext());
ParserRuleContext ctx = _ctx; }
ATNState s = atn.states.get(getState());
IntervalSet following = atn.nextTokens(s);
// System.out.println("following "+s+"="+following);
if ( !following.contains(Token.EPSILON) ) return following;
IntervalSet expected = new IntervalSet();
expected.addAll(following);
expected.remove(Token.EPSILON);
while ( ctx!=null && ctx.invokingState>=0 && following.contains(Token.EPSILON) ) {
ATNState invokingState = atn.states.get(ctx.invokingState);
RuleTransition rt = (RuleTransition)invokingState.transition(0);
following = atn.nextTokens(rt.followState);
expected.addAll(following);
expected.remove(Token.EPSILON);
ctx = (ParserRuleContext)ctx.parent;
}
if ( following.contains(Token.EPSILON) ) {
expected.add(Token.EOF);
}
return expected;
}
public IntervalSet getExpectedTokensWithinCurrentRule() { public IntervalSet getExpectedTokensWithinCurrentRule() {
ATN atn = getInterpreter().atn; ATN atn = getInterpreter().atn;

View File

@ -92,10 +92,10 @@ public class RecognitionException extends RuntimeException {
} }
public IntervalSet getExpectedTokens() { public IntervalSet getExpectedTokens() {
// TODO: do we really need this type check? if (recognizer != null) {
if ( recognizer!=null && recognizer instanceof Parser) { return recognizer.getATN().getExpectedTokens(offendingState, ctx);
return ((Parser) recognizer).getExpectedTokens();
} }
return null; return null;
} }

View File

@ -155,4 +155,55 @@ public class ATN {
public int getNumberOfDecisions() { public int getNumberOfDecisions() {
return decisionToState.size(); return decisionToState.size();
} }
/**
* Computes the set of input symbols which could follow ATN state number
* {@code stateNumber} in the specified full {@code context}. This method
* considers the complete parser context, but does not evaluate semantic
* predicates (i.e. all predicates encountered during the calculation are
* assumed true). If a path in the ATN exists from the starting state to the
* {@link RuleStopState} of the outermost context without matching any
* symbols, {@link Token#EOF} is added to the returned set.
* <p/>
* If {@code context} is {@code null}, it is treated as
* {@link ParserRuleContext#EMPTY}.
*
* @param stateNumber the ATN state number
* @param context the full parse context
* @return The set of potentially valid input symbols which could follow the
* specified state in the specified context.
* @throws IllegalArgumentException if the ATN does not contain a state with
* number {@code stateNumber}
*/
@NotNull
public IntervalSet getExpectedTokens(int stateNumber, @Nullable RuleContext context) {
if (stateNumber < 0 || stateNumber >= states.size()) {
throw new IllegalArgumentException("Invalid state number.");
}
RuleContext ctx = context;
ATNState s = states.get(stateNumber);
IntervalSet following = nextTokens(s);
if (!following.contains(Token.EPSILON)) {
return following;
}
IntervalSet expected = new IntervalSet();
expected.addAll(following);
expected.remove(Token.EPSILON);
while (ctx != null && ctx.invokingState >= 0 && following.contains(Token.EPSILON)) {
ATNState invokingState = states.get(ctx.invokingState);
RuleTransition rt = (RuleTransition)invokingState.transition(0);
following = nextTokens(rt.followState);
expected.addAll(following);
expected.remove(Token.EPSILON);
ctx = ctx.parent;
}
if (following.contains(Token.EPSILON)) {
expected.add(Token.EOF);
}
return expected;
}
} }