/// <summary> /// Invokes the nonterminal, and stores the result in the memo table. /// </summary> /// <param name="nonterminal"></param> /// <param name="position"></param> /// <returns></returns> public virtual IEnumerable <OutputRecord> ApplyNonterminal(Nonterminal nonterminal, int position) { Depth++; try { MemoEntry memoEntry = MemoTable[position, nonterminal.Index]; if (memoEntry == null) { var answer = nonterminal.Eval(this); memoEntry = new MemoEntry(answer, Position); MemoTable[position, nonterminal.Index] = memoEntry; return(answer); } else { Position = memoEntry.Position; return(memoEntry.Answer); } } finally { Depth--; } }
private void Init() { TerminalsCache = new DynamicArray <Terminal>(); MemoTable = new MemoEntry[Input.Length + 1, Grammar.Nonterminals.Count]; IsLogEnabled = true; }
public override IEnumerable <OutputRecord> ApplyNonterminal(Nonterminal nonterminal, int position) { Depth++; try { MemoEntry memoEntry = MemoTable[position, nonterminal.Index]; LeftRecursion leftRecursion = LeftRecursionSet[position]; if (memoEntry == null || (leftRecursion != null && leftRecursion.InvolvedSet.Contains(nonterminal.Index))) { // If we've detected left recursion... if (InvocationSet[position][nonterminal.Index]) { // If there is no left recursion already going on... if (leftRecursion == null) { // Create a new left recursion entry and store it in the LeftRecursionSet for the current position. leftRecursion = new LeftRecursion(nonterminal, Grammar); LeftRecursionSet[position] = leftRecursion; } // If there already is left-recursion going on, we may need to start keeping track of a new thread. Fortunately, // we can know that the new left-recursive state will end before we have to worry about the old one again, so // we can create a linked-list-based stack where each "Next" entry is further up the call stack. The inverse // of this process is near the end. else if (leftRecursion.Rule != nonterminal) { // This is here because we don't ever want to add an entry to the stack that is already being evaluated. if (leftRecursion.EvalSet.Contains(nonterminal.Index)) { return(LeftRecursion); } var nextLeftRecursion = leftRecursion; leftRecursion = new LeftRecursion(nonterminal, nextLeftRecursion, Grammar); } // This ensures that we keep track of those nonterminals that were involved in the left recursion. (i.e. // all the nonterminals in the call stack following the nonterminal responsible for the left-recursion. // This is non-inclusive -- it does not include the offending nonterminal located on both sides.) All such // nonterminals are added to the InvolvedSet of the LeftRecursion record. leftRecursion.Add(CallStack.TakeWhile(o => o != nonterminal)); // Keep track of this LeftRecursion at the current position (possibly overwriting a previous entry -- this // entry will be restored when the current left recursion finishes) LeftRecursionSet[position] = leftRecursion; // Return the special LeftRecursion value that indicates a (special) failed attempt. Below, we check for // this value to ensure that the memo table is not updated with the failure when left-recursion is still // going on (i.e. we will need to make another attempt later and don't want to have it return a failure // because of the memo result) return(LeftRecursion); } else { memoEntry = new MemoEntry(null, position); bool first = true; IEnumerable <OutputRecord> answer; while (true) { // Reset the position Position = position; // Update the invocation set and call stack InvocationSet[position][nonterminal.Index] = true; CallStack.Push(nonterminal); // Evaluate the nonterminal answer = nonterminal.Eval(this); // Reset the invocation set and call stack CallStack.Pop(); InvocationSet[position][nonterminal.Index] = false; // Except for the first iteration, we never want to continue the iteration (or the loop) if the // evaluation failed to increase the Position. (The first time through we do need to finish the // iteration as the rest of the code takes care of bookkeeping required to note a failed attempt.) if (memoEntry.Position >= Position && !first) { // Update the position and answer since we are going to revert back to the state in the memo entry Position = memoEntry.Position; answer = memoEntry.Answer; break; } bool processing = false; // If we have an answer (and it's not the special LeftRecursion failure), then we want to store the // result in the memo table. If it is the special LeftRecursion failure, we don't because that's the // whole reason for the special value. if (answer != null && answer != LeftRecursion) { // We want to catch any case where the result was overwritten, as that fact invalidates the current // nonterminal's parse and will force a new iteration of the parse loop. if (memoEntry.Answer != null && memoEntry.Position < Position) { processing = true; } // Update the memo entry memoEntry.Position = Position; memoEntry.Answer = answer; MemoTable[position, nonterminal.Index] = memoEntry; } first = false; // We want to end the loop as early as possible. For the vast majority of cases, there is no left-recursion going // on at the current context (position+nonterminal). For these cases, we do not want to evaluate the nonterminal more // than once. Therefore, the loop will break if the following conditions are true: // // a) We are not specifically forcing a new iteration as specified above (processing) // b) There is absolutely no left-recursion going on or there is, but this nonterminal is not the one being // recursed. if (!processing && (LeftRecursionSet[position] == null || LeftRecursionSet[position].Rule != nonterminal)) { break; } } // Finally, record a failure if we found nothing even after a left-recursive attempt. if (answer == null && answer != LeftRecursion) { memoEntry.Answer = answer; memoEntry.Position = position; MemoTable[position, nonterminal.Index] = memoEntry; } // Reverses the left-recursion process described above so that the LeftRecursionSet is always kept in an // accurate state: If this was the top of the LeftRecursion stack, the set is cleared, otherwise the // current LeftRecursion is popped and the previous one restored. leftRecursion = LeftRecursionSet[position]; if (leftRecursion != null && leftRecursion.Rule == nonterminal) { leftRecursion = leftRecursion.Next; LeftRecursionSet[position] = leftRecursion; } return(answer); } } else { Position = memoEntry.Position; return(memoEntry.Answer); } } finally { Depth--; } }