/// <summary> /// 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. /// </summary> /// <remarks> /// 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 /// <paramref name="config"/> /// , all other (potentially reachable) states for /// this rule would have a lower priority. /// </remarks> /// <returns> /// /// <see langword="true"/> /// if an accept state is reached, otherwise /// <see langword="false"/> /// . /// </returns> protected internal virtual bool Closure(ICharStream input, ATNConfig config, ATNConfigSet configs, bool currentAltReachedAcceptState, bool speculative, bool treatEofAsEpsilon) { if (config.State is RuleStopState) { PredictionContext context = config.Context; if (context.IsEmpty) { configs.Add(config); return(true); } else { if (context.HasEmpty) { configs.Add(config.Transform(config.State, PredictionContext.EmptyFull, true)); currentAltReachedAcceptState = true; } } for (int i = 0; i < context.Size; i++) { int returnStateNumber = context.GetReturnState(i); if (returnStateNumber == PredictionContext.EmptyFullStateKey) { continue; } PredictionContext newContext = context.GetParent(i); // "pop" return state ATNState returnState = atn.states[returnStateNumber]; ATNConfig c = config.Transform(returnState, newContext, false); currentAltReachedAcceptState = Closure(input, c, configs, currentAltReachedAcceptState, speculative, treatEofAsEpsilon); } return(currentAltReachedAcceptState); } // optimization if (!config.State.OnlyHasEpsilonTransitions) { if (!currentAltReachedAcceptState || !config.PassedThroughNonGreedyDecision) { configs.Add(config); } } ATNState p = config.State; for (int i_1 = 0; i_1 < p.NumberOfOptimizedTransitions; i_1++) { Transition t = p.GetOptimizedTransition(i_1); ATNConfig c = GetEpsilonTarget(input, config, t, configs, speculative, treatEofAsEpsilon); if (c != null) { currentAltReachedAcceptState = Closure(input, c, configs, currentAltReachedAcceptState, speculative, treatEofAsEpsilon); } } return(currentAltReachedAcceptState); }
/// <summary>Computes the SLL prediction termination condition.</summary> /// <remarks> /// Computes the SLL prediction termination condition. /// <p> /// This method computes the SLL prediction termination condition for both of /// the following cases.</p> /// <ul> /// <li>The usual SLL+LL fallback upon SLL conflict</li> /// <li>Pure SLL without LL fallback</li> /// </ul> /// <p><strong>COMBINED SLL+LL PARSING</strong></p> /// <p>When LL-fallback is enabled upon SLL conflict, correct predictions are /// ensured regardless of how the termination condition is computed by this /// method. Due to the substantially higher cost of LL prediction, the /// prediction should only fall back to LL when the additional lookahead /// cannot lead to a unique SLL prediction.</p> /// <p>Assuming combined SLL+LL parsing, an SLL configuration set with only /// conflicting subsets should fall back to full LL, even if the /// configuration sets don't resolve to the same alternative (e.g. /// <c/> /// /// 1,2}} and /// <c/> /// /// 3,4}}. If there is at least one non-conflicting /// configuration, SLL could continue with the hopes that more lookahead will /// resolve via one of those non-conflicting configurations.</p> /// <p>Here's the prediction termination rule them: SLL (for SLL+LL parsing) /// stops when it sees only conflicting configuration subsets. In contrast, /// full LL keeps going when there is uncertainty.</p> /// <p><strong>HEURISTIC</strong></p> /// <p>As a heuristic, we stop prediction when we see any conflicting subset /// unless we see a state that only has one alternative associated with it. /// The single-alt-state thing lets prediction continue upon rules like /// (otherwise, it would admit defeat too soon):</p> /// <p> /// <c>[12|1|[], 6|2|[], 12|2|[]]. s : (ID | ID ID?) ';' ;</c> /// </p> /// <p>When the ATN simulation reaches the state before /// <c>';'</c> /// , it has a /// DFA state that looks like: /// <c>[12|1|[], 6|2|[], 12|2|[]]</c> /// . Naturally /// <c>12|1|[]</c> /// and /// <c>12|2|[]</c> /// conflict, but we cannot stop /// processing this node because alternative to has another way to continue, /// via /// <c>[6|2|[]]</c> /// .</p> /// <p>It also let's us continue for this rule:</p> /// <p> /// <c>[1|1|[], 1|2|[], 8|3|[]] a : A | A | A B ;</c> /// </p> /// <p>After matching input A, we reach the stop state for rule A, state 1. /// State 8 is the state right before B. Clearly alternatives 1 and 2 /// conflict and no amount of further lookahead will separate the two. /// However, alternative 3 will be able to continue and so we do not stop /// working on this state. In the previous example, we're concerned with /// states associated with the conflicting alternatives. Here alt 3 is not /// associated with the conflicting configs, but since we can continue /// looking for input reasonably, don't declare the state done.</p> /// <p><strong>PURE SLL PARSING</strong></p> /// <p>To handle pure SLL parsing, all we have to do is make sure that we /// combine stack contexts for configurations that differ only by semantic /// predicate. From there, we can do the usual SLL termination heuristic.</p> /// <p><strong>PREDICATES IN SLL+LL PARSING</strong></p> /// <p>SLL decisions don't evaluate predicates until after they reach DFA stop /// states because they need to create the DFA cache that works in all /// semantic situations. In contrast, full LL evaluates predicates collected /// during start state computation so it can ignore predicates thereafter. /// This means that SLL termination detection can totally ignore semantic /// predicates.</p> /// <p>Implementation-wise, /// <see cref="ATNConfigSet"/> /// combines stack contexts but not /// semantic predicate contexts so we might see two configurations like the /// following.</p> /// <p> /// <c/> /// (s, 1, x, /// ), (s, 1, x', {p})}</p> /// <p>Before testing these configurations against others, we have to merge /// <c>x</c> /// and /// <c>x'</c> /// (without modifying the existing configurations). /// For example, we test /// <c>(x+x')==x''</c> /// when looking for conflicts in /// the following configurations.</p> /// <p> /// <c/> /// (s, 1, x, /// ), (s, 1, x', {p}), (s, 2, x'', {})}</p> /// <p>If the configuration set has predicates (as indicated by /// <see cref="ATNConfigSet.hasSemanticContext"/> /// ), this algorithm makes a copy of /// the configurations to strip out all of the predicates so that a standard /// <see cref="ATNConfigSet"/> /// will merge everything ignoring predicates.</p> /// </remarks> public static bool HasSLLConflictTerminatingPrediction(PredictionMode mode, ATNConfigSet configSet) { if (AllConfigsInRuleStopStates(configSet.configs)) { return(true); } // pure SLL mode parsing if (mode == PredictionMode.SLL) { // Don't bother with combining configs from different semantic // contexts if we can fail over to full LL; costs more time // since we'll often fail over anyway. if (configSet.hasSemanticContext) { // dup configs, tossing out semantic predicates ATNConfigSet dup = new ATNConfigSet(); foreach (ATNConfig c in configSet.configs) { dup.Add(new ATNConfig(c, SemanticContext.NONE)); } configSet = dup; } } // now we have combined contexts for configs with dissimilar preds // pure SLL or combined SLL+LL mode parsing ICollection <BitSet> altsets = GetConflictingAltSubsets(configSet.configs); bool heuristic = HasConflictingAltSet(altsets) && !HasStateAssociatedWithOneAlt(configSet.configs); return(heuristic); }
/// <summary>Computes the SLL prediction termination condition.</summary> /// <remarks> /// Computes the SLL prediction termination condition. /// <p> /// This method computes the SLL prediction termination condition for both of /// the following cases.</p> /// <ul> /// <li>The usual SLL+LL fallback upon SLL conflict</li> /// <li>Pure SLL without LL fallback</li> /// </ul> /// <p><strong>COMBINED SLL+LL PARSING</strong></p> /// <p>When LL-fallback is enabled upon SLL conflict, correct predictions are /// ensured regardless of how the termination condition is computed by this /// method. Due to the substantially higher cost of LL prediction, the /// prediction should only fall back to LL when the additional lookahead /// cannot lead to a unique SLL prediction.</p> /// <p>Assuming combined SLL+LL parsing, an SLL configuration set with only /// conflicting subsets should fall back to full LL, even if the /// configuration sets don't resolve to the same alternative (e.g. /// <c/> /// /// 1,2}} and /// <c/> /// /// 3,4}}. If there is at least one non-conflicting /// configuration, SLL could continue with the hopes that more lookahead will /// resolve via one of those non-conflicting configurations.</p> /// <p>Here's the prediction termination rule them: SLL (for SLL+LL parsing) /// stops when it sees only conflicting configuration subsets. In contrast, /// full LL keeps going when there is uncertainty.</p> /// <p><strong>HEURISTIC</strong></p> /// <p>As a heuristic, we stop prediction when we see any conflicting subset /// unless we see a state that only has one alternative associated with it. /// The single-alt-state thing lets prediction continue upon rules like /// (otherwise, it would admit defeat too soon):</p> /// <p> /// <c>[12|1|[], 6|2|[], 12|2|[]]. s : (ID | ID ID?) ';' ;</c> /// </p> /// <p>When the ATN simulation reaches the state before /// <c>';'</c> /// , it has a /// DFA state that looks like: /// <c>[12|1|[], 6|2|[], 12|2|[]]</c> /// . Naturally /// <c>12|1|[]</c> /// and /// <c>12|2|[]</c> /// conflict, but we cannot stop /// processing this node because alternative to has another way to continue, /// via /// <c>[6|2|[]]</c> /// .</p> /// <p>It also let's us continue for this rule:</p> /// <p> /// <c>[1|1|[], 1|2|[], 8|3|[]] a : A | A | A B ;</c> /// </p> /// <p>After matching input A, we reach the stop state for rule A, state 1. /// State 8 is the state right before B. Clearly alternatives 1 and 2 /// conflict and no amount of further lookahead will separate the two. /// However, alternative 3 will be able to continue and so we do not stop /// working on this state. In the previous example, we're concerned with /// states associated with the conflicting alternatives. Here alt 3 is not /// associated with the conflicting configs, but since we can continue /// looking for input reasonably, don't declare the state done.</p> /// <p><strong>PURE SLL PARSING</strong></p> /// <p>To handle pure SLL parsing, all we have to do is make sure that we /// combine stack contexts for configurations that differ only by semantic /// predicate. From there, we can do the usual SLL termination heuristic.</p> /// <p><strong>PREDICATES IN SLL+LL PARSING</strong></p> /// <p>SLL decisions don't evaluate predicates until after they reach DFA stop /// states because they need to create the DFA cache that works in all /// semantic situations. In contrast, full LL evaluates predicates collected /// during start state computation so it can ignore predicates thereafter. /// This means that SLL termination detection can totally ignore semantic /// predicates.</p> /// <p>Implementation-wise, /// <see cref="ATNConfigSet"/> /// combines stack contexts but not /// semantic predicate contexts so we might see two configurations like the /// following.</p> /// <p> /// <c/> /// (s, 1, x, /// ), (s, 1, x', {p})}</p> /// <p>Before testing these configurations against others, we have to merge /// <c>x</c> /// and /// <c>x'</c> /// (without modifying the existing configurations). /// For example, we test /// <c>(x+x')==x''</c> /// when looking for conflicts in /// the following configurations.</p> /// <p> /// <c/> /// (s, 1, x, /// ), (s, 1, x', {p}), (s, 2, x'', {})}</p> /// <p>If the configuration set has predicates (as indicated by /// <see cref="ATNConfigSet.HasSemanticContext()"/> /// ), this algorithm makes a copy of /// the configurations to strip out all of the predicates so that a standard /// <see cref="ATNConfigSet"/> /// will merge everything ignoring predicates.</p> /// </remarks> public static bool HasSLLConflictTerminatingPrediction(PredictionMode mode, [NotNull] ATNConfigSet configs) { /* Configs in rule stop states indicate reaching the end of the decision * rule (local context) or end of start rule (full context). If all * configs meet this condition, then none of the configurations is able * to match additional input so we terminate prediction. */ if (AllConfigsInRuleStopStates(configs)) { return(true); } // pure SLL mode parsing if (mode == PredictionMode.Sll) { // Don't bother with combining configs from different semantic // contexts if we can fail over to full LL; costs more time // since we'll often fail over anyway. if (configs.HasSemanticContext) { // dup configs, tossing out semantic predicates ATNConfigSet dup = new ATNConfigSet(); foreach (ATNConfig c in configs) { ATNConfig config = c.Transform(c.State, SemanticContext.None, false); dup.Add(c); } configs = dup; } } // now we have combined contexts for configs with dissimilar preds // pure SLL or combined SLL+LL mode parsing ICollection <BitSet> altsets = GetConflictingAltSubsets(configs); bool heuristic = HasConflictingAltSet(altsets) && !HasStateAssociatedWithOneAlt(configs); return(heuristic); }
/** * 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}. */ protected bool Closure(ICharStream input, LexerATNConfig config, ATNConfigSet configs, bool currentAltReachedAcceptState, bool speculative, bool treatEofAsEpsilon) { if (debug) { ConsoleWriteLine("closure(" + config.ToString(recog, true) + ")"); } if (config.state is RuleStopState) { if (debug) { if (recog != null) { ConsoleWriteLine("closure at " + recog.RuleNames[config.state.ruleIndex] + " rule stop " + config); } else { ConsoleWriteLine("closure at rule stop " + 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, PredictionContext.EMPTY)); 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[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.NumberOfTransitions; 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); }