/** * Adding a new config means merging contexts with existing configs for * {@code (s, i, pi, _)}, where {@code s} is the * {@link ATNConfig#state}, {@code i} is the {@link ATNConfig#alt}, and * {@code pi} is the {@link ATNConfig#semanticContext}. We use * {@code (s,i,pi)} as key. * * <p>This method updates {@link #dipsIntoOuterContext} and * {@link #hasSemanticContext} when necessary.</p> */ public bool Add(ATNConfig config, MergeCache mergeCache) { if (readOnly) { throw new Exception("This set is readonly"); } if (config.semanticContext != SemanticContext.NONE) { hasSemanticContext = true; } if (config.OuterContextDepth > 0) { dipsIntoOuterContext = true; } ATNConfig existing = configLookup.GetOrAdd(config); if (existing == config) { // we added this new one cachedHashCode = -1; configs.Add(config); // track order here return(true); } // a previous (s,i,pi,_), merge with it and save result bool rootIsWildcard = !fullCtx; PredictionContext merged = PredictionContext.Merge(existing.context, config.context, rootIsWildcard, mergeCache); // no need to check for existing.context, config.context in cache // since only way to create new graphs is "call rule" and here. We // cache at both places. existing.reachesIntoOuterContext = Math.Max(existing.reachesIntoOuterContext, config.reachesIntoOuterContext); // make sure to preserve the precedence filter suppression during the merge if (config.IsPrecedenceFilterSuppressed) { existing.SetPrecedenceFilterSuppressed(true); } existing.context = merged; // replace context; no need to alt mapping return(true); }
public virtual int AdaptivePredict(ITokenStream input, int decision, ParserRuleContext outerContext) { if (debug || debug_list_atn_decisions) { Console.WriteLine("adaptivePredict decision " + decision + " exec LA(1)==" + GetLookaheadName(input) + " line " + input.LT(1).Line + ":" + input.LT(1).Column); } this.input = input; startIndex = input.Index; context = outerContext; DFA dfa = decisionToDFA[decision]; thisDfa = dfa; int m = input.Mark(); int index = startIndex; // Now we are certain to have a specific decision's DFA // But, do we still need an initial state? try { DFAState s0; if (dfa.IsPrecedenceDfa) { // the start state for a precedence DFA depends on the current // parser precedence, and is provided by a DFA method. s0 = dfa.GetPrecedenceStartState(parser.Precedence); } else { // the start state for a "regular" DFA is just s0 s0 = dfa.s0; } if (s0 == null) { if (outerContext == null) outerContext = ParserRuleContext.EmptyContext; if (debug || debug_list_atn_decisions) { Console.WriteLine("predictATN decision " + dfa.decision + " exec LA(1)==" + GetLookaheadName(input) + ", outerContext=" + outerContext.ToString(parser)); } bool fullCtx = false; ATNConfigSet s0_closure = ComputeStartState(dfa.atnStartState, ParserRuleContext.EmptyContext, fullCtx); if (dfa.IsPrecedenceDfa) { /* If this is a precedence DFA, we use applyPrecedenceFilter * to convert the computed start state to a precedence start * state. We then use DFA.setPrecedenceStartState to set the * appropriate start state for the precedence level rather * than simply setting DFA.s0. */ dfa.s0.configSet = s0_closure; // not used for prediction but useful to know start configs anyway s0_closure = ApplyPrecedenceFilter(s0_closure); s0 = AddDFAState(dfa, new DFAState(s0_closure)); dfa.SetPrecedenceStartState(parser.Precedence, s0); } else { s0 = AddDFAState(dfa, new DFAState(s0_closure)); dfa.s0 = s0; } } int alt = ExecATN(dfa, s0, input, index, outerContext); if (debug) Console.WriteLine("DFA after predictATN: " + dfa.ToString(parser.Vocabulary)); return alt; } finally { mergeCache = null; // wack cache after each prediction thisDfa = null; input.Seek(index); input.Release(m); } }
protected virtual ATNConfigSet ComputeReachSet(ATNConfigSet closure, int t, bool fullCtx) { if (debug) Console.WriteLine("in computeReachSet, starting closure: " + closure); if (mergeCache == null) { mergeCache = new MergeCache(); } ATNConfigSet intermediate = new ATNConfigSet(fullCtx); /* Configurations already in a rule stop state indicate reaching the end * of the decision rule (local context) or end of the start rule (full * context). Once reached, these configurations are never updated by a * closure operation, so they are handled separately for the performance * advantage of having a smaller intermediate set when calling closure. * * For full-context reach operations, separate handling is required to * ensure that the alternative matching the longest overall sequence is * chosen when multiple such configurations can match the input. */ List<ATNConfig> skippedStopStates = null; // First figure out where we can reach on input t foreach (ATNConfig c in closure.configs) { if (debug) Console.WriteLine("testing " + GetTokenName(t) + " at " + c.ToString()); if (c.state is RuleStopState) { if (fullCtx || t == IntStreamConstants.EOF) { if (skippedStopStates == null) { skippedStopStates = new List<ATNConfig>(); } skippedStopStates.Add(c); } continue; } int n = c.state.NumberOfTransitions; for (int ti = 0; ti < n; ti++) { // for each transition Transition trans = c.state.Transition(ti); ATNState target = GetReachableTarget(trans, t); if (target != null) { intermediate.Add(new ATNConfig(c, target), mergeCache); } } } // Now figure out where the reach operation can take us... ATNConfigSet reach = null; /* This block optimizes the reach operation for intermediate sets which * trivially indicate a termination state for the overall * adaptivePredict operation. * * The conditions assume that intermediate * contains all configurations relevant to the reach set, but this * condition is not true when one or more configurations have been * withheld in skippedStopStates, or when the current symbol is EOF. */ if (skippedStopStates == null && t != TokenConstants.EOF) { if (intermediate.Count == 1) { // Don't pursue the closure if there is just one state. // It can only have one alternative; just add to result // Also don't pursue the closure if there is unique alternative // among the configurations. reach = intermediate; } else if (GetUniqueAlt(intermediate) != ATN.INVALID_ALT_NUMBER) { // Also don't pursue the closure if there is unique alternative // among the configurations. reach = intermediate; } } /* If the reach set could not be trivially determined, perform a closure * operation on the intermediate set to compute its initial value. */ if (reach == null) { reach = new ATNConfigSet(fullCtx); HashSet<ATNConfig> closureBusy = new HashSet<ATNConfig>(); bool treatEofAsEpsilon = t == TokenConstants.EOF; foreach (ATNConfig c in intermediate.configs) { Closure(c, reach, closureBusy, false, fullCtx, treatEofAsEpsilon); } } if (t == IntStreamConstants.EOF) { /* After consuming EOF no additional input is possible, so we are * only interested in configurations which reached the end of the * decision rule (local context) or end of the start rule (full * context). Update reach to contain only these configurations. This * handles both explicit EOF transitions in the grammar and implicit * EOF transitions following the end of the decision or start rule. * * When reach==intermediate, no closure operation was performed. In * this case, removeAllConfigsNotInRuleStopState needs to check for * reachable rule stop states as well as configurations already in * a rule stop state. * * This is handled before the configurations in skippedStopStates, * because any configurations potentially added from that list are * already guaranteed to meet this condition whether or not it's * required. */ reach = RemoveAllConfigsNotInRuleStopState(reach, reach == intermediate); } /* If skippedStopStates is not null, then it contains at least one * configuration. For full-context reach operations, these * configurations reached the end of the start rule, in which case we * only add them back to reach if no configuration during the current * closure operation reached such a state. This ensures adaptivePredict * chooses an alternative matching the longest overall sequence when * multiple alternatives are viable. */ if (skippedStopStates != null && (!fullCtx || !PredictionMode.HasConfigInRuleStopState(reach.configs))) { foreach (ATNConfig c in skippedStopStates) { reach.Add(c, mergeCache); } } if (reach.Empty) return null; return reach; }
public static PredictionContext MergeArrays( ArrayPredictionContext a, ArrayPredictionContext b, bool rootIsWildcard, MergeCache mergeCache) { if (mergeCache != null) { PredictionContext previous = mergeCache.Get(a, b); if (previous != null) { return(previous); } previous = mergeCache.Get(b, a); if (previous != null) { return(previous); } } // merge sorted payloads a + b => M int i = 0; // walks a int j = 0; // walks b int k = 0; // walks target M array int[] mergedReturnStates = new int[a.returnStates.Length + b.returnStates.Length]; PredictionContext[] mergedParents = new PredictionContext[a.returnStates.Length + b.returnStates.Length]; // walk and merge to yield mergedParents, mergedReturnStates while (i < a.returnStates.Length && j < b.returnStates.Length) { PredictionContext a_parent = a.parents[i]; PredictionContext b_parent = b.parents[j]; if (a.returnStates[i] == b.returnStates[j]) { // same payload (stack tops are equal), must yield merged singleton int payload = a.returnStates[i]; // $+$ = $ bool both_dollar = payload == EMPTY_RETURN_STATE && a_parent == null && b_parent == null; bool ax_ax = (a_parent != null && b_parent != null) && a_parent.Equals(b_parent); // ax+ax -> ax if (both_dollar || ax_ax) { mergedParents[k] = a_parent; // choose left mergedReturnStates[k] = payload; } else // ax+ay -> a'[x,y] { PredictionContext mergedParent = Merge(a_parent, b_parent, rootIsWildcard, mergeCache); mergedParents[k] = mergedParent; mergedReturnStates[k] = payload; } i++; // hop over left one as usual j++; // but also skip one in right side since we merge } else if (a.returnStates[i] < b.returnStates[j]) { // copy a[i] to M mergedParents[k] = a_parent; mergedReturnStates[k] = a.returnStates[i]; i++; } else // b > a, copy b[j] to M { mergedParents[k] = b_parent; mergedReturnStates[k] = b.returnStates[j]; j++; } k++; } // copy over any payloads remaining in either array if (i < a.returnStates.Length) { for (int p = i; p < a.returnStates.Length; p++) { mergedParents[k] = a.parents[p]; mergedReturnStates[k] = a.returnStates[p]; k++; } } else { for (int p = j; p < b.returnStates.Length; p++) { mergedParents[k] = b.parents[p]; mergedReturnStates[k] = b.returnStates[p]; k++; } } // trim merged if we combined a few that had same stack tops if (k < mergedParents.Length) { // write index < last position; trim if (k == 1) { // for just one merged element, return singleton top PredictionContext a_ = SingletonPredictionContext.Create(mergedParents[0], mergedReturnStates[0]); if (mergeCache != null) { mergeCache.Put(a, b, a_); } return(a_); } mergedParents = Arrays.CopyOf(mergedParents, k); mergedReturnStates = Arrays.CopyOf(mergedReturnStates, k); } PredictionContext M = new ArrayPredictionContext(mergedParents, mergedReturnStates); // if we created same array as a or b, return that instead // TODO: track whether this is possible above during merge sort for speed if (M.Equals(a)) { if (mergeCache != null) { mergeCache.Put(a, b, a); } return(a); } if (M.Equals(b)) { if (mergeCache != null) { mergeCache.Put(a, b, b); } return(b); } CombineCommonParents(mergedParents); if (mergeCache != null) { mergeCache.Put(a, b, M); } return(M); }
public static PredictionContext MergeSingletons( SingletonPredictionContext a, SingletonPredictionContext b, bool rootIsWildcard, MergeCache mergeCache) { if (mergeCache != null) { PredictionContext previous = mergeCache.Get(a, b); if (previous != null) { return(previous); } previous = mergeCache.Get(b, a); if (previous != null) { return(previous); } } PredictionContext rootMerge = MergeRoot(a, b, rootIsWildcard); if (rootMerge != null) { if (mergeCache != null) { mergeCache.Put(a, b, rootMerge); } return(rootMerge); } if (a.returnState == b.returnState) { // a == b PredictionContext parent = Merge(a.parent, b.parent, rootIsWildcard, mergeCache); // if parent is same as existing a or b parent or reduced to a parent, return it if (parent == a.parent) { return(a); // ax + bx = ax, if a=b } if (parent == b.parent) { return(b); // ax + bx = bx, if a=b } // else: ax + ay = a'[x,y] // merge parents x and y, giving array node with x,y then remainders // of those graphs. dup a, a' points at merged array // new joined parent so create new singleton pointing to it, a' PredictionContext a_ = SingletonPredictionContext.Create(parent, a.returnState); if (mergeCache != null) { mergeCache.Put(a, b, a_); } return(a_); } else // a != b payloads differ // see if we can collapse parents due to $+x parents if local ctx { int[] payloads = new int[2]; PredictionContext[] parents = new PredictionContext[2]; PredictionContext pc; PredictionContext singleParent = null; if (a == b || (a.parent != null && a.parent.Equals(b.parent))) { // ax + bx = [a,b]x singleParent = a.parent; } if (singleParent != null) { // parents are same // sort payloads and use same parent if (a.returnState > b.returnState) { payloads[0] = b.returnState; payloads[1] = a.returnState; } else { payloads[0] = a.returnState; payloads[1] = b.returnState; } parents[0] = singleParent; parents[1] = singleParent; pc = new ArrayPredictionContext(parents, payloads); if (mergeCache != null) { mergeCache.Put(a, b, pc); } return(pc); } // parents differ and can't merge them. Just pack together // into array; can't merge. // ax + by = [ax,by] // sort by payload if (a.returnState > b.returnState) { payloads[0] = b.returnState; payloads[1] = a.returnState; parents[0] = b.parent; parents[1] = a.parent; } else { payloads[0] = a.returnState; payloads[1] = b.returnState; parents[0] = a.parent; parents[1] = b.parent; } pc = new ArrayPredictionContext(parents, payloads); if (mergeCache != null) { mergeCache.Put(a, b, pc); } return(pc); } }
internal static PredictionContext Merge(PredictionContext a, PredictionContext b, bool rootIsWildcard, MergeCache mergeCache) { if (a == b || a.Equals(b)) { return(a); } if (a is SingletonPredictionContext && b is SingletonPredictionContext) { return(MergeSingletons((SingletonPredictionContext)a, (SingletonPredictionContext)b, rootIsWildcard, mergeCache)); } // At least one of a or b is array // If one is $ and rootIsWildcard, return $ as * wildcard if (rootIsWildcard) { if (a is EmptyPredictionContext) { return(a); } if (b is EmptyPredictionContext) { return(b); } } // convert singleton so both are arrays to normalize if (a is SingletonPredictionContext) { a = new ArrayPredictionContext((SingletonPredictionContext)a); } if (b is SingletonPredictionContext) { b = new ArrayPredictionContext((SingletonPredictionContext)b); } return(MergeArrays((ArrayPredictionContext)a, (ArrayPredictionContext)b, rootIsWildcard, mergeCache)); }