/// <summary> /// Start a left-recursion record for a rule at a given index. /// </summary> /// <param name="expansion">The production expansion.</param> /// <param name="index">The input position.</param> /// <param name="record">The new left-recursion record.</param> public void StartLRRecord(Expansion expansion, int index, LRRecord <MatchItem <TInput, TResult> > record) { Dictionary <int, LRRecord <MatchItem <TInput, TResult> > > record_dict; if (!current_recursions.TryGetValue(expansion.Name, out record_dict)) { record_dict = new Dictionary <int, LRRecord <MatchItem <TInput, TResult> > >(); current_recursions.Add(expansion.Name, record_dict); } record_dict[index] = record; }
/// <summary> /// Get the left-recursion record for a rule at a given index. /// </summary> /// <param name="expansion">The production expansion.</param> /// <param name="index">The input position.</param> /// <param name="record">The left-recursion record.</param> /// <returns>True if there is a left-recursion record for the rule at the index.</returns> public bool TryGetLRRecord(Expansion expansion, int index, out LRRecord <MatchItem <TInput, TResult> > record) { Dictionary <int, LRRecord <MatchItem <TInput, TResult> > > record_dict; if (current_recursions.TryGetValue(expansion.Name, out record_dict) && record_dict.TryGetValue(index, out record)) { return(true); } record = null; return(false); }
/// <summary> /// Call a grammar production, using memoization and handling left-recursion. /// </summary> /// <param name="memo">The memo for the current match.</param> /// <param name="ruleName">The name of the production.</param> /// <param name="index">The current index in the input stream.</param> /// <param name="production">The production itself.</param> /// <param name="args">Arguments to the production (can be null).</param> /// <returns>The result of the production at the given input index.</returns> protected MatchItem <TInput, TResult> _MemoCall ( MatchState <TInput, TResult> memo, string ruleName, int index, Action <MatchState <TInput, TResult>, int, IEnumerable <MatchItem <TInput, TResult> > > production, IEnumerable <MatchItem <TInput, TResult> > args ) { MatchItem <TInput, TResult> result; var expansion = new Expansion { Name = args == null ? ruleName : ruleName + string.Join(", ", args.Select(arg => arg.ToString()).ToArray()), Num = 0 }; // if we have a memo record, use that if (memo.TryGetMemo(expansion, index, out result)) { memo.Results.Push(result); return(result); } // if we are not handling left recursion, just call the production directly. if (!HandleLeftRecursion || Terminals.Contains(ruleName)) { production(memo, index, args); result = memo.Results.Peek(); memo.Memoize(expansion, index, result); if (result == null) { memo.AddError(index, () => "expected " + ruleName); } return(result); } // check for left-recursion LRRecord <MatchItem <TInput, TResult> > record; if (memo.TryGetLRRecord(expansion, index, out record)) { record.LRDetected = true; var involved = memo.CallStack .TakeWhile(rec => rec.CurrentExpansion.Name != expansion.Name) .Select(rec => rec.CurrentExpansion.Name); if (record.InvolvedRules != null) { record.InvolvedRules.UnionWith(involved); } else { record.InvolvedRules = new HashSet <string>(involved); } if (!memo.TryGetMemo(record.CurrentExpansion, index, out result)) { throw new MatcherException(index, "Problem with expansion " + record.CurrentExpansion); } memo.Results.Push(result); } // no lr information else { record = new LRRecord <MatchItem <TInput, TResult> >(); record.LRDetected = false; record.NumExpansions = 1; record.CurrentExpansion = new Expansion { Name = expansion.Name, Num = record.NumExpansions }; record.CurrentNextIndex = -1; memo.Memoize(record.CurrentExpansion, index, null); memo.StartLRRecord(expansion, index, record); memo.CallStack.Push(record); while (true) { production(memo, index, args); result = memo.Results.Pop(); // do we need to keep trying the expansions? if (record.LRDetected && result != null && result.NextIndex > record.CurrentNextIndex) { record.NumExpansions = record.NumExpansions + 1; record.CurrentExpansion = new Expansion { Name = expansion.Name, Num = record.NumExpansions }; record.CurrentNextIndex = result != null ? result.NextIndex : 0; memo.Memoize(record.CurrentExpansion, index, result); record.CurrentResult = result; } // we are done trying to expand else { if (record.LRDetected) { result = record.CurrentResult; } memo.ForgetLRRecord(expansion, index); memo.Results.Push(result); // if we are not involved in any left-recursion expansions above us, memoize memo.CallStack.Pop(); bool found_lr = memo.CallStack.Any(rec => rec.InvolvedRules != null && rec.InvolvedRules.Contains(expansion.Name)); if (!found_lr) { memo.Memoize(expansion, index, result); } if (result == null) { memo.AddError(index, () => "expected " + expansion.Name); } break; } } } return(result); }