protected boolean closure(CharStream input, LexerATNConfig config, ATNConfigSet configs, boolean currentAltReachedAcceptState, boolean speculative, boolean treatEofAsEpsilon) {
		if ( debug ) {
			System.out.println("closure("+config.toString(recog, true)+")");
		}

		if ( config.state instanceof RuleStopState ) {
			if ( debug ) {
				if ( recog!=null ) {
					System.out.format(Locale.getDefault(), "closure at %s rule stop %s\n", recog.getRuleNames()[config.state.ruleIndex], config);
				}
				else {
					System.out.format(Locale.getDefault(), "closure at rule stop %s\n", config);
				}
			}

			if ( config.context == null || config.context.hasEmptyPath() ) {
				if (config.context == null || config.context.isEmpty()) {
					configs.add(config);
					return true;
				}
				else {
					configs.add(new LexerATNConfig(config, config.state, EmptyPredictionContext.Instance));
					currentAltReachedAcceptState = true;
				}
			}

			if ( config.context!=null && !config.context.isEmpty() ) {
				for (int i = 0; i < config.context.size(); i++) {
					if (config.context.getReturnState(i) != PredictionContext.EMPTY_RETURN_STATE) {
						PredictionContext newContext = config.context.getParent(i); // "pop" return state
						ATNState returnState = atn.states.get(config.context.getReturnState(i));
						LexerATNConfig c = new LexerATNConfig(config, returnState, newContext);
						currentAltReachedAcceptState = closure(input, c, configs, currentAltReachedAcceptState, speculative, treatEofAsEpsilon);
					}
				}
			}

			return currentAltReachedAcceptState;
		}

		// optimization
		if ( !config.state.onlyHasEpsilonTransitions() ) {
			if (!currentAltReachedAcceptState || !config.hasPassedThroughNonGreedyDecision()) {
				configs.add(config);
			}
		}

		ATNState p = config.state;
		for (int i=0; i<p.getNumberOfTransitions(); i++) {
			Transition t = p.transition(i);
			LexerATNConfig c = getEpsilonTarget(input, config, t, configs, speculative, treatEofAsEpsilon);
			if ( c!=null ) {
				currentAltReachedAcceptState = closure(input, c, configs, currentAltReachedAcceptState, speculative, treatEofAsEpsilon);
			}
		}

		return currentAltReachedAcceptState;
	}
Since the alternatives within any lexer decision are ordered by
preference, this method stops pursuing the closure as soon as an accept
state is reached. After the first accept state is reached by depth-first
search from {@code config}, all other (potentially reachable) states for
this rule would have a lower priority.

@return {@code true} if an accept state is reached, otherwise
{@code false}.
runtime/Java/src/org/antlr/v4/runtime/atn/LexerATNSimulator.java