/// <summary> /// Compute set of tokens that can follow /// <paramref name="s"/> /// in the ATN in the /// specified /// <paramref name="ctx"/> /// . /// <p/> /// If /// <paramref name="ctx"/> /// is /// <see cref="PredictionContext.EmptyLocal"/> /// and /// <paramref name="stopState"/> /// or the end of the rule containing /// <paramref name="s"/> /// is reached, /// <see cref="TokenConstants.EPSILON"/> /// is added to the result set. If /// <paramref name="ctx"/> /// is not /// <see cref="PredictionContext.EmptyLocal"/> /// and /// <paramref name="addEOF"/> /// is /// <see langword="true"/> /// and /// <paramref name="stopState"/> /// or the end of the outermost rule is reached, /// <see cref="TokenConstants.EOF"/> /// is added to the result set. /// </summary> /// <param name="s">the ATN state.</param> /// <param name="stopState"> /// the ATN state to stop at. This can be a /// <see cref="BlockEndState"/> /// to detect epsilon paths through a closure. /// </param> /// <param name="ctx"> /// The outer context, or /// <see cref="PredictionContext.EmptyLocal"/> /// if /// the outer context should not be used. /// </param> /// <param name="look">The result lookahead set.</param> /// <param name="lookBusy"> /// A set used for preventing epsilon closures in the ATN /// from causing a stack overflow. Outside code should pass /// <c>new HashSet<ATNConfig></c> /// for this argument. /// </param> /// <param name="calledRuleStack"> /// A set used for preventing left recursion in the /// ATN from causing a stack overflow. Outside code should pass /// <c>new BitSet()</c> /// for this argument. /// </param> /// <param name="seeThruPreds"> /// /// <see langword="true"/> /// to true semantic predicates as /// implicitly /// <see langword="true"/> /// and "see through them", otherwise /// <see langword="false"/> /// to treat semantic predicates as opaque and add /// <see cref="HitPred"/> /// to the /// result if one is encountered. /// </param> /// <param name="addEOF"> /// Add /// <see cref="TokenConstants.EOF"/> /// to the result if the end of the /// outermost context is reached. This parameter has no effect if /// <paramref name="ctx"/> /// is /// <see cref="PredictionContext.EmptyLocal"/> /// . /// </param> protected internal virtual void Look(ATNState s, ATNState stopState, PredictionContext ctx, IntervalSet look, HashSet <ATNConfig> lookBusy, BitSet calledRuleStack, bool seeThruPreds, bool addEOF) { // System.out.println("_LOOK("+s.stateNumber+", ctx="+ctx); ATNConfig c = new ATNConfig(s, 0, ctx); if (!lookBusy.Add(c)) { return; } if (s == stopState) { if (ctx == null) { look.Add(TokenConstants.EPSILON); return; } else if (ctx.IsEmpty && addEOF) { look.Add(TokenConstants.EOF); return; } } if (s is RuleStopState) { if (ctx == null) { look.Add(TokenConstants.EPSILON); return; } else if (ctx.IsEmpty && addEOF) { look.Add(TokenConstants.EOF); return; } if (ctx != PredictionContext.EMPTY) { for (int i = 0; i < ctx.Size; i++) { ATNState returnState = atn.states[ctx.GetReturnState(i)]; bool removed = calledRuleStack.Get(returnState.ruleIndex); try { calledRuleStack.Clear(returnState.ruleIndex); Look(returnState, stopState, ctx.GetParent(i), look, lookBusy, calledRuleStack, seeThruPreds, addEOF); } finally { if (removed) { calledRuleStack.Set(returnState.ruleIndex); } } } return; } } int n = s.NumberOfTransitions; for (int i_1 = 0; i_1 < n; i_1++) { Transition t = s.Transition(i_1); if (t is RuleTransition) { RuleTransition ruleTransition = (RuleTransition)t; if (calledRuleStack.Get(ruleTransition.ruleIndex)) { continue; } PredictionContext newContext = SingletonPredictionContext.Create(ctx, ruleTransition.followState.stateNumber); try { calledRuleStack.Set(ruleTransition.target.ruleIndex); Look(t.target, stopState, newContext, look, lookBusy, calledRuleStack, seeThruPreds, addEOF); } finally { calledRuleStack.Clear(ruleTransition.target.ruleIndex); } } else { if (t is AbstractPredicateTransition) { if (seeThruPreds) { Look(t.target, stopState, ctx, look, lookBusy, calledRuleStack, seeThruPreds, addEOF); } else { look.Add(HitPred); } } else { if (t.IsEpsilon) { Look(t.target, stopState, ctx, look, lookBusy, calledRuleStack, seeThruPreds, addEOF); } else { if (t is WildcardTransition) { look.AddAll(IntervalSet.Of(TokenConstants.MinUserTokenType, atn.maxTokenType)); } else { IntervalSet set = t.Label; if (set != null) { if (t is NotSetTransition) { set = set.Complement(IntervalSet.Of(TokenConstants.MinUserTokenType, atn.maxTokenType)); } look.AddAll(set); } } } } } } }
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 GetCachedContext(PredictionContext context, PredictionContextCache contextCache, PredictionContext.IdentityHashMap visited) { if (context.IsEmpty) { return(context); } PredictionContext existing = visited.Get(context); if (existing != null) { return(existing); } existing = contextCache.Get(context); if (existing != null) { visited.Put(context, existing); return(existing); } bool changed = false; PredictionContext[] parents = new PredictionContext[context.Size]; for (int i = 0; i < parents.Length; i++) { PredictionContext parent = GetCachedContext(context.GetParent(i), contextCache, visited); if (changed || parent != context.GetParent(i)) { if (!changed) { parents = new PredictionContext[context.Size]; for (int j = 0; j < context.Size; j++) { parents[j] = context.GetParent(j); } changed = true; } parents[i] = parent; } } if (!changed) { contextCache.Add(context); visited.Put(context, context); return(context); } PredictionContext updated; if (parents.Length == 0) { updated = EMPTY; } else if (parents.Length == 1) { updated = SingletonPredictionContext.Create(parents[0], context.GetReturnState(0)); } else { ArrayPredictionContext arrayPredictionContext = (ArrayPredictionContext)context; updated = new ArrayPredictionContext(parents, arrayPredictionContext.returnStates); } contextCache.Add(updated); visited.Put(updated, updated); visited.Put(context, updated); return(updated); }
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); } }