예제 #1
0
 /// <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--;
     }
 }
예제 #2
0
 private void Init()
 {
     TerminalsCache = new DynamicArray <Terminal>();
     MemoTable      = new MemoEntry[Input.Length + 1, Grammar.Nonterminals.Count];
     IsLogEnabled   = true;
 }
예제 #3
0
        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--;
            }
        }