public virtual string ToDotString() { #if COMPACT throw new NotImplementedException("The current platform does not provide RuntimeHelpers.GetHashCode(object)."); #else StringBuilder builder = new StringBuilder(); builder.Append("digraph G {\n"); builder.Append("rankdir=LR;\n"); HashSet <PredictionContext> visited = new HashSet <PredictionContext>(); Stack <PredictionContext> workList = new Stack <PredictionContext>(); workList.Push(Context); visited.Add(Context); while (workList.Count > 0) { PredictionContext current = workList.Pop(); for (int i = 0; i < current.Size; i++) { builder.Append(" s").Append(System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(current)); builder.Append("->"); builder.Append("s").Append(System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(current.GetParent(i))); builder.Append("[label=\"").Append(current.GetReturnState(i)).Append("\"];\n"); if (visited.Add(current.GetParent(i))) { workList.Push(current.GetParent(i)); } } } builder.Append("}\n"); return(builder.ToString()); #endif }
public static Antlr4.Runtime.Atn.PredictionContext GetCachedContext(Antlr4.Runtime.Atn.PredictionContext context, ConcurrentDictionary <Antlr4.Runtime.Atn.PredictionContext, Antlr4.Runtime.Atn.PredictionContext> contextCache, PredictionContext.IdentityHashMap visited) { if (context.IsEmpty) { return(context); } Antlr4.Runtime.Atn.PredictionContext existing; if (visited.TryGetValue(context, out existing)) { return(existing); } if (contextCache.TryGetValue(context, out existing)) { visited[context] = existing; return(existing); } bool changed = false; Antlr4.Runtime.Atn.PredictionContext[] parents = new Antlr4.Runtime.Atn.PredictionContext[context.Size]; for (int i = 0; i < parents.Length; i++) { Antlr4.Runtime.Atn.PredictionContext parent = GetCachedContext(context.GetParent(i), contextCache, visited); if (changed || parent != context.GetParent(i)) { if (!changed) { parents = new Antlr4.Runtime.Atn.PredictionContext[context.Size]; for (int j = 0; j < context.Size; j++) { parents[j] = context.GetParent(j); } changed = true; } parents[i] = parent; } } if (!changed) { existing = contextCache.GetOrAdd(context, context); visited[context] = existing; return(context); } // We know parents.length>0 because context.isEmpty() is checked at the beginning of the method. Antlr4.Runtime.Atn.PredictionContext updated; if (parents.Length == 1) { updated = new SingletonPredictionContext(parents[0], context.GetReturnState(0)); } else { ArrayPredictionContext arrayPredictionContext = (ArrayPredictionContext)context; updated = new ArrayPredictionContext(parents, arrayPredictionContext.returnStates, context.cachedHashCode); } existing = contextCache.GetOrAdd(updated, updated); visited[updated] = existing; visited[context] = existing; return(updated); }
/// <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 /// <code>config</code> /// , all other (potentially reachable) states for /// this rule would have a lower priority. /// </remarks> /// <returns> /// /// <code>true</code> /// if an accept state is reached, otherwise /// <code>false</code> /// . /// </returns> protected internal virtual bool Closure(ICharStream input, ATNConfig config, ATNConfigSet configs, bool speculative) { if (config.State is RuleStopState) { PredictionContext context = config.Context; if (context.IsEmpty) { configs.AddItem(config); return(true); } else { if (context.HasEmpty) { configs.AddItem(config.Transform(config.State, PredictionContext.EmptyFull)); return(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 = ATNConfig.Create(returnState, config.Alt, newContext); if (Closure(input, c, configs, speculative)) { return(true); } } return(false); } // optimization if (!config.State.OnlyHasEpsilonTransitions) { configs.AddItem(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); if (c != null) { if (Closure(input, c, configs, speculative)) { return(true); } } } return(false); }
/// <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); }
public virtual bool Contains(Antlr4.Runtime.Atn.ATNConfig subconfig) { if (this.State.stateNumber != subconfig.State.stateNumber || this.Alt != subconfig .Alt || !this.SemanticContext.Equals(subconfig.SemanticContext)) { return(false); } Stack <PredictionContext> leftWorkList = new Stack <PredictionContext>(); Stack <PredictionContext> rightWorkList = new Stack <PredictionContext>(); leftWorkList.Push(Context); rightWorkList.Push(subconfig.Context); while (leftWorkList.Count > 0) { PredictionContext left = leftWorkList.Pop(); PredictionContext right = rightWorkList.Pop(); if (left == right) { return(true); } if (left.Size < right.Size) { return(false); } if (right.IsEmpty) { return(left.HasEmpty); } else { for (int i = 0; i < right.Size; i++) { int index = left.FindReturnState(right.GetReturnState(i)); if (index < 0) { // assumes invokingStates has no duplicate entries return(false); } leftWorkList.Push(left.GetParent(index)); rightWorkList.Push(right.GetParent(i)); } } } return(false); }
public virtual string[] ToStrings(IRecognizer recognizer, Antlr4.Runtime.Atn.PredictionContext stop, int currentState) { List <string> result = new List <string>(); for (int perm = 0; ; perm++) { int offset = 0; bool last = true; Antlr4.Runtime.Atn.PredictionContext p = this; int stateNumber = currentState; StringBuilder localBuffer = new StringBuilder(); localBuffer.Append("["); while (!p.IsEmpty && p != stop) { int index = 0; if (p.Size > 0) { int bits = 1; while ((1 << bits) < p.Size) { bits++; } int mask = (1 << bits) - 1; index = (perm >> offset) & mask; last &= index >= p.Size - 1; if (index >= p.Size) { goto outer_continue; } offset += bits; } if (recognizer != null) { if (localBuffer.Length > 1) { // first char is '[', if more than that this isn't the first rule localBuffer.Append(' '); } ATN atn = recognizer.Atn; ATNState s = atn.states[stateNumber]; string ruleName = recognizer.RuleNames[s.ruleIndex]; localBuffer.Append(ruleName); } else { if (p.GetReturnState(index) != EmptyFullStateKey) { if (!p.IsEmpty) { if (localBuffer.Length > 1) { // first char is '[', if more than that this isn't the first rule localBuffer.Append(' '); } localBuffer.Append(p.GetReturnState(index)); } } } stateNumber = p.GetReturnState(index); p = p.GetParent(index); } localBuffer.Append("]"); result.Add(localBuffer.ToString()); if (last) { break; } outer_continue :; } return(result.ToArray()); }
internal static Antlr4.Runtime.Atn.PredictionContext Join(Antlr4.Runtime.Atn.PredictionContext context0, Antlr4.Runtime.Atn.PredictionContext context1, PredictionContextCache contextCache) { if (context0 == context1) { return(context0); } if (context0.IsEmpty) { return(IsEmptyLocal(context0) ? context0 : AddEmptyContext(context1)); } else { if (context1.IsEmpty) { return(IsEmptyLocal(context1) ? context1 : AddEmptyContext(context0)); } } int context0size = context0.Size; int context1size = context1.Size; if (context0size == 1 && context1size == 1 && context0.GetReturnState(0) == context1 .GetReturnState(0)) { Antlr4.Runtime.Atn.PredictionContext merged = contextCache.Join(context0.GetParent (0), context1.GetParent(0)); if (merged == context0.GetParent(0)) { return(context0); } else { if (merged == context1.GetParent(0)) { return(context1); } else { return(merged.GetChild(context0.GetReturnState(0))); } } } int count = 0; Antlr4.Runtime.Atn.PredictionContext[] parentsList = new Antlr4.Runtime.Atn.PredictionContext [context0size + context1size]; int[] returnStatesList = new int[parentsList.Length]; int leftIndex = 0; int rightIndex = 0; bool canReturnLeft = true; bool canReturnRight = true; while (leftIndex < context0size && rightIndex < context1size) { if (context0.GetReturnState(leftIndex) == context1.GetReturnState(rightIndex)) { parentsList[count] = contextCache.Join(context0.GetParent(leftIndex), context1.GetParent (rightIndex)); returnStatesList[count] = context0.GetReturnState(leftIndex); canReturnLeft = canReturnLeft && parentsList[count] == context0.GetParent(leftIndex ); canReturnRight = canReturnRight && parentsList[count] == context1.GetParent(rightIndex ); leftIndex++; rightIndex++; } else { if (context0.GetReturnState(leftIndex) < context1.GetReturnState(rightIndex)) { parentsList[count] = context0.GetParent(leftIndex); returnStatesList[count] = context0.GetReturnState(leftIndex); canReturnRight = false; leftIndex++; } else { System.Diagnostics.Debug.Assert(context1.GetReturnState(rightIndex) < context0.GetReturnState (leftIndex)); parentsList[count] = context1.GetParent(rightIndex); returnStatesList[count] = context1.GetReturnState(rightIndex); canReturnLeft = false; rightIndex++; } } count++; } while (leftIndex < context0size) { parentsList[count] = context0.GetParent(leftIndex); returnStatesList[count] = context0.GetReturnState(leftIndex); leftIndex++; canReturnRight = false; count++; } while (rightIndex < context1size) { parentsList[count] = context1.GetParent(rightIndex); returnStatesList[count] = context1.GetReturnState(rightIndex); rightIndex++; canReturnLeft = false; count++; } if (canReturnLeft) { return(context0); } else { if (canReturnRight) { return(context1); } } if (count < parentsList.Length) { parentsList = Arrays.CopyOf(parentsList, count); returnStatesList = Arrays.CopyOf(returnStatesList, count); } if (parentsList.Length == 0) { // if one of them was EMPTY_LOCAL, it would be empty and handled at the beginning of the method return(EmptyFull); } else { if (parentsList.Length == 1) { return(new SingletonPredictionContext(parentsList[0], returnStatesList[0])); } else { return(new ArrayPredictionContext(parentsList, returnStatesList)); } } }
private static PredictionContext AppendContext(PredictionContext context, PredictionContext suffix, PredictionContext.IdentityHashMap visited) { if (suffix.IsEmpty) { if (IsEmptyLocal(suffix)) { if (context.HasEmpty) { return(EmptyLocal); } throw new NotSupportedException("what to do here?"); } return(context); } if (suffix.Size != 1) { throw new NotSupportedException("Appending a tree suffix is not yet supported."); } PredictionContext result; if (!visited.TryGetValue(context, out result)) { if (context.IsEmpty) { result = suffix; } else { int parentCount = context.Size; if (context.HasEmpty) { parentCount--; } PredictionContext[] updatedParents = new PredictionContext[parentCount]; int[] updatedReturnStates = new int[parentCount]; for (int i = 0; i < parentCount; i++) { updatedReturnStates[i] = context.GetReturnState(i); } for (int i_1 = 0; i_1 < parentCount; i_1++) { updatedParents[i_1] = AppendContext(context.GetParent(i_1), suffix, visited); } if (updatedParents.Length == 1) { result = new SingletonPredictionContext(updatedParents[0], updatedReturnStates[0]); } else { System.Diagnostics.Debug.Assert(updatedParents.Length > 1); result = new Antlr4.Runtime.Atn.ArrayPredictionContext(updatedParents, updatedReturnStates); } if (context.HasEmpty) { result = PredictionContext.Join(result, suffix); } } visited[context] = result; } return(result); }
/// <summary> /// Compute set of tokens that can follow /// <code>s</code> /// in the ATN in the /// specified /// <code>ctx</code> /// . /// <p/> /// If /// <code>ctx</code> /// is /// <see cref="PredictionContext.EmptyLocal">PredictionContext.EmptyLocal</see> /// and /// <code>stopState</code> /// or the end of the rule containing /// <code>s</code> /// is reached, /// <see cref="TokenConstants.Epsilon"/> /// is added to the result set. If /// <code>ctx</code> /// is not /// <see cref="PredictionContext.EmptyLocal">PredictionContext.EmptyLocal</see> /// and /// <code>addEOF</code> /// is /// <code>true</code> /// and /// <code>stopState</code> /// 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">BlockEndState</see> /// to detect epsilon paths through a closure. /// </param> /// <param name="ctx"> /// The outer context, or /// <see cref="PredictionContext.EmptyLocal">PredictionContext.EmptyLocal</see> /// 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 /// <code>new HashSet<ATNConfig></code> /// 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 /// <code>new BitSet()</code> /// for this argument. /// </param> /// <param name="seeThruPreds"> /// /// <code>true</code> /// to true semantic predicates as /// implicitly /// <code>true</code> /// and "see through them", otherwise /// <code>false</code> /// to treat semantic predicates as opaque and add /// <see cref="HitPred">HitPred</see> /// 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 /// <code>ctx</code> /// is /// <see cref="PredictionContext.EmptyLocal">PredictionContext.EmptyLocal</see> /// . /// </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 = ATNConfig.Create(s, 0, ctx); if (!lookBusy.Add(c)) { return; } if (s == stopState) { if (PredictionContext.IsEmptyLocal(ctx)) { look.Add(TokenConstants.Epsilon); return; } else { if (ctx.IsEmpty && addEOF) { look.Add(TokenConstants.Eof); return; } } } if (s is RuleStopState) { if (PredictionContext.IsEmptyLocal(ctx)) { look.Add(TokenConstants.Epsilon); return; } else { if (ctx.IsEmpty && addEOF) { look.Add(TokenConstants.Eof); return; } } for (int i = 0; i < ctx.Size; i++) { if (ctx.GetReturnState(i) != PredictionContext.EmptyFullStateKey) { ATNState returnState = atn.states[ctx.GetReturnState(i)]; // System.out.println("popping back to "+retState); for (int j = 0; j < ctx.Size; j++) { bool removed = calledRuleStack.Get(returnState.ruleIndex); try { calledRuleStack.Clear(returnState.ruleIndex); Look(returnState, stopState, ctx.GetParent(j), 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.GetType() == typeof(RuleTransition)) { if (calledRuleStack.Get(((RuleTransition)t).target.ruleIndex)) { continue; } PredictionContext newContext = ctx.GetChild(((RuleTransition)t).followState.stateNumber ); try { calledRuleStack.Set(((RuleTransition)t).target.ruleIndex); Look(t.target, stopState, newContext, look, lookBusy, calledRuleStack, seeThruPreds , addEOF); } finally { calledRuleStack.Clear(((RuleTransition)t).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.GetType() == typeof(WildcardTransition)) { look.AddAll(IntervalSet.Of(TokenConstants.MinUserTokenType, atn.maxTokenType)); } else { // System.out.println("adding "+ t); IntervalSet set = t.Label; if (set != null) { if (t is NotSetTransition) { set = set.Complement(IntervalSet.Of(TokenConstants.MinUserTokenType, atn.maxTokenType )); } look.AddAll(set); } } } } } } }
/// <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 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); }