closure method

bool closure(
  1. CharStream input,
  2. LexerATNConfig config,
  3. ATNConfigSet configs,
  4. bool currentAltReachedAcceptState,
  5. bool speculative,
  6. bool treatEofAsEpsilon,
)

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 config, all other (potentially reachable) states for this rule would have a lower priority.

@return true if an accept state is reached, otherwise false.

Implementation

bool closure(
    CharStream input,
    LexerATNConfig config,
    ATNConfigSet configs,
    bool currentAltReachedAcceptState,
    bool speculative,
    bool treatEofAsEpsilon) {
  if (debug) {
    log('closure(' + config.toString(recog, true) + ')',
        level: Level.FINE.value);
  }

  if (config.state is RuleStopState) {
    if (debug) {
      log('closure at ${recog.ruleNames[config.state.ruleIndex]} rule stop $config\n',
          level: Level.FINE.value);
    }

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

    if (config.context != null && !config.context!.isEmpty) {
      for (var i = 0; i < config.context!.length; i++) {
        if (config.context!.getReturnState(i) !=
            PredictionContext.EMPTY_RETURN_STATE) {
          final newContext =
              config.context!.getParent(i); // "pop" return state
          final returnState = atn.states[config.context!.getReturnState(i)]!;
          final c =
              LexerATNConfig.dup(config, returnState, context: newContext);
          currentAltReachedAcceptState = closure(
            input,
            c,
            configs,
            currentAltReachedAcceptState,
            speculative,
            treatEofAsEpsilon,
          );
        }
      }
    }

    return currentAltReachedAcceptState;
  }

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

  final p = config.state;
  for (var i = 0; i < p.numberOfTransitions; i++) {
    final t = p.transition(i);
    final c = getEpsilonTarget(
      input,
      config,
      t,
      configs,
      speculative,
      treatEofAsEpsilon,
    );
    if (c != null) {
      currentAltReachedAcceptState = closure(input, c, configs,
          currentAltReachedAcceptState, speculative, treatEofAsEpsilon);
    }
  }

  return currentAltReachedAcceptState;
}