generateRuleBypassTransition method

void generateRuleBypassTransition(
  1. ATN atn,
  2. int idx
)

Implementation

void generateRuleBypassTransition(ATN atn, int idx) {
  final bypassStart = BasicBlockStartState(idx);
  atn.addState(bypassStart);

  final bypassStop = BlockEndState(idx);
  atn.addState(bypassStop);

  bypassStart.endState = bypassStop;
  atn.defineDecisionState(bypassStart);

  bypassStop.startState = bypassStart;

  ATNState? endState;
  Transition? excludeTransition;
  if (atn.ruleToStartState[idx].isLeftRecursiveRule) {
    // wrap from the beginning of the rule to the StarLoopEntryState
    endState = null;
    for (var state in atn.states) {
      if (state == null) {
        continue;
      }
      if (state.ruleIndex != idx) {
        continue;
      }

      if (state is! StarLoopEntryState) {
        continue;
      }

      final maybeLoopEndState =
          state.transition(state.numberOfTransitions - 1).target;
      if (maybeLoopEndState is! LoopEndState) {
        continue;
      }

      if (maybeLoopEndState.epsilonOnlyTransitions &&
          maybeLoopEndState.transition(0).target is RuleStopState) {
        endState = state;
        break;
      }
    }

    if (endState == null) {
      throw UnsupportedError(
        "Couldn't identify final state of the precedence rule prefix section.",
      );
    }

    excludeTransition =
        (endState as StarLoopEntryState).loopBackState!.transition(0);
  } else {
    endState = atn.ruleToStopState[idx];
  }

  // all non-excluded transitions that currently target end state need to target blockEnd instead
  for (var state in atn.states) {
    if (state == null) {
      continue;
    }
    for (var transition in state.transitions) {
      if (transition == excludeTransition) {
        continue;
      }

      if (transition.target == endState) {
        transition.target = bypassStop;
      }
    }
  }

  // all transitions leaving the rule start state need to leave blockStart instead
  while (atn.ruleToStartState[idx].numberOfTransitions > 0) {
    final transition = atn.ruleToStartState[idx]
        .removeTransition(atn.ruleToStartState[idx].numberOfTransitions - 1);
    bypassStart.addTransition(transition);
  }

  // link the new states
  atn.ruleToStartState[idx].addTransition(EpsilonTransition(bypassStart));
  bypassStop.addTransition(EpsilonTransition(endState));

  ATNState matchState = BasicState(idx);
  atn.addState(matchState);

  matchState.addTransition(AtomTransition(
    bypassStop,
    atn.ruleToTokenType[idx],
  ));
  bypassStart.addTransition(EpsilonTransition(matchState));
}