diff --git a/runtime/Java/src/org/antlr/v4/runtime/atn/LexerATNSimulator.java b/runtime/Java/src/org/antlr/v4/runtime/atn/LexerATNSimulator.java index 2849fa3b3..1f74bb0fe 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/atn/LexerATNSimulator.java +++ b/runtime/Java/src/org/antlr/v4/runtime/atn/LexerATNSimulator.java @@ -351,7 +351,8 @@ public class LexerATNSimulator extends ATNSimulator { lexerActionExecutor = lexerActionExecutor.fixOffsetBeforeMatch(input.index() - startIndex); } - if (closure(input, new LexerATNConfig((LexerATNConfig)c, target, lexerActionExecutor), reach, currentAltReachedAcceptState, true)) { + boolean treatEofAsEpsilon = t == CharStream.EOF; + if (closure(input, new LexerATNConfig((LexerATNConfig)c, target, lexerActionExecutor), reach, currentAltReachedAcceptState, true, treatEofAsEpsilon)) { // any remaining configs for this alt have a lower priority than // the one that just reached an accept state. skipAlt = c.alt; @@ -400,7 +401,7 @@ public class LexerATNSimulator extends ATNSimulator { for (int i=0; i closureBusy = new HashSet(); + boolean treatEofAsEpsilon = t == Token.EOF; for (ATNConfig c : intermediate) { - closure(c, reach, closureBusy, false, fullCtx); + closure(c, reach, closureBusy, false, fullCtx, treatEofAsEpsilon); } } @@ -971,7 +972,7 @@ public class ParserATNSimulator extends ATNSimulator { ATNState target = p.transition(i).target; ATNConfig c = new ATNConfig(target, i+1, initialContext); Set closureBusy = new HashSet(); - closure(c, configs, closureBusy, true, fullCtx); + closure(c, configs, closureBusy, true, fullCtx, false); } return configs; @@ -1218,12 +1219,13 @@ public class ParserATNSimulator extends ATNSimulator { @NotNull ATNConfigSet configs, @NotNull Set closureBusy, boolean collectPredicates, - boolean fullCtx) + boolean fullCtx, + boolean treatEofAsEpsilon) { final int initialDepth = 0; closureCheckingStopState(config, configs, closureBusy, collectPredicates, fullCtx, - initialDepth); + initialDepth, treatEofAsEpsilon); assert !fullCtx || !configs.dipsIntoOuterContext; } @@ -1232,7 +1234,8 @@ public class ParserATNSimulator extends ATNSimulator { @NotNull Set closureBusy, boolean collectPredicates, boolean fullCtx, - int depth) + int depth, + boolean treatEofAsEpsilon) { if ( debug ) System.out.println("closure("+config.toString(parser,true)+")"); @@ -1251,7 +1254,7 @@ public class ParserATNSimulator extends ATNSimulator { if ( debug ) System.out.println("FALLING off rule "+ getRuleName(config.state.ruleIndex)); closure_(config, configs, closureBusy, collectPredicates, - fullCtx, depth); + fullCtx, depth, treatEofAsEpsilon); } continue; } @@ -1265,7 +1268,7 @@ public class ParserATNSimulator extends ATNSimulator { c.reachesIntoOuterContext = config.reachesIntoOuterContext; assert depth > Integer.MIN_VALUE; closureCheckingStopState(c, configs, closureBusy, collectPredicates, - fullCtx, depth - 1); + fullCtx, depth - 1, treatEofAsEpsilon); } return; } @@ -1282,7 +1285,7 @@ public class ParserATNSimulator extends ATNSimulator { } closure_(config, configs, closureBusy, collectPredicates, - fullCtx, depth); + fullCtx, depth, treatEofAsEpsilon); } /** Do the actual work of walking epsilon edges */ @@ -1291,12 +1294,15 @@ public class ParserATNSimulator extends ATNSimulator { @NotNull Set closureBusy, boolean collectPredicates, boolean fullCtx, - int depth) + int depth, + boolean treatEofAsEpsilon) { ATNState p = config.state; // optimization if ( !p.onlyHasEpsilonTransitions() ) { configs.add(config, mergeCache); + // make sure to not return here, because EOF transitions can act as + // both epsilon transitions and non-epsilon transitions. // if ( debug ) System.out.println("added config "+configs); } @@ -1305,7 +1311,7 @@ public class ParserATNSimulator extends ATNSimulator { boolean continueCollecting = !(t instanceof ActionTransition) && collectPredicates; ATNConfig c = getEpsilonTarget(config, t, continueCollecting, - depth == 0, fullCtx); + depth == 0, fullCtx, treatEofAsEpsilon); if ( c!=null ) { int newDepth = depth; if ( config.state instanceof RuleStopState) { @@ -1335,7 +1341,7 @@ public class ParserATNSimulator extends ATNSimulator { } closureCheckingStopState(c, configs, closureBusy, continueCollecting, - fullCtx, newDepth); + fullCtx, newDepth, treatEofAsEpsilon); } } } @@ -1351,7 +1357,8 @@ public class ParserATNSimulator extends ATNSimulator { @NotNull Transition t, boolean collectPredicates, boolean inContext, - boolean fullCtx) + boolean fullCtx, + boolean treatEofAsEpsilon) { switch (t.getSerializationType()) { case Transition.RULE: @@ -1372,6 +1379,19 @@ public class ParserATNSimulator extends ATNSimulator { case Transition.EPSILON: return new ATNConfig(config, t.target); + case Transition.ATOM: + case Transition.RANGE: + case Transition.SET: + // EOF transitions act like epsilon transitions after the first EOF + // transition is traversed + if (treatEofAsEpsilon) { + if (t.matches(Token.EOF, 0, 1)) { + return new ATNConfig(config, t.target); + } + } + + return null; + default: return null; }