public IEnumerable <IParseResult <IEnumerable <TResult> > > Parse(ICursor <TSource> source) { firstParser = null; bool first = true; // See the AllParser.Parse method for an explanation of the optimization that is provided by this stack var branches = new Stack <ICursor <TSource> >(); using (var root = source.Branch()) { branches.Push(root); var query = ParseResult.ReturnSuccessMany <TResult>(0) .SelectMany( parsers.Select( parser => (Func <Tuple <IParseResult <IEnumerable <TResult> >, bool>, IEnumerable <IParseResult <IEnumerable <TResult> > > >) (value => { if (first) { first = false; firstParser = parser; } var branch = branches.Peek(); // Item2 is only true when value.Item1 is the last element of its sequence. if (value.Item2) { branch.Move(value.Item1.Length); return(parser.Parse(branch)); } else { var remainder = branch.Remainder(value.Item1.Length); branches.Push(remainder); return(parser.Parse(remainder) .OnErrorOrDisposed(() => { /* Finally() cannot be used here. SelectMany generates each value as a Tuple, indicating whether it's * the last value in the sequence. To gather that info it uses a side-effect that causes it to look-ahead * in the enumerator, which would cause Finally to execute and dispose of the branch before it's used * by the child sequence. SelectMany only calls Dispose when the sequence is no longer in use. */ branches.Pop().Dispose(); })); } })), (firstResult, secondResult) => firstResult.Concat(secondResult)); // Use an iterator block to ensure that the local variables in this method aren't shared between enumerators. foreach (var result in query) { yield return(result); } } }
public ICursor <T> Branch() { var branch = cursor.Branch(); branches.Add(branch); return(new ParserCursorBranch(branch, () => branches.Remove(branch))); }
public ICursor <T> Branch() { var next = branch.Branch(); branches.Add(next); return(new ParserCursorBranch(next, () => branches.Remove(next))); }
public IEnumerable <IParseResult <IEnumerable <TResult> > > Parse(ICursor <TSource> source) { int matchCount = 0; int remainingLength = 0; while (!source.AtEndOfSequence && (untilCount == unlimitedCount || matchCount < untilCount) && (untilPredicate == null || !untilPredicate(source))) { bool hasResult = false; int length = 0; var branch = source.Branch(); var values = parser.Parse(branch) .Finally(branch.Dispose) .Select( result => { if (!hasResult) { matchCount++; hasResult = true; } length = Math.Max(length, result.Length); return(result.Value); }); yield return(ParseResult.Create(values, length: 1)); /* Assume that the results have been iterated by the caller at this point; * otherwise, the cursor's state cannot be determined when the results * are eventually iterated, causing unpredictable results. * * We must respect the greediness of the results unless the length is zero since the * cursor would have already moved to the following element. It is acceptable to ignore * zero-length results because marking an entirely non-greedy parser as ambiguous would * otherwise cause the parser to continously parse the first element indefinitely. */ if (length > 0) { remainingLength = length - 1; } else if (remainingLength > 0) { remainingLength--; } } if (remainingLength > 0) { yield return(ParseResult.SuccessMany <TResult>(remainingLength)); } }
public static ICursor <TSource> Remainder <TSource>(this ICursor <TSource> cursor, int skip) { Contract.Requires(cursor != null); Contract.Requires(skip >= 0); Contract.Ensures(cursor.IsForwardOnly == Contract.OldValue(cursor.IsForwardOnly)); Contract.Ensures(Contract.Result <ICursor <TSource> >() != null); Contract.Ensures(Contract.Result <ICursor <TSource> >().IsForwardOnly == cursor.IsForwardOnly); Contract.Ensures(Contract.Result <ICursor <TSource> >().IsSequenceTerminated == cursor.IsSequenceTerminated); Contract.Ensures(Contract.Result <ICursor <TSource> >().LatestIndex == cursor.LatestIndex); Contract.Ensures(Contract.Result <ICursor <TSource> >().CurrentIndex == (cursor.AtEndOfSequence ? cursor.CurrentIndex : cursor.IsSequenceTerminated ? Math.Min(cursor.CurrentIndex + skip, cursor.LatestIndex + 1) : cursor.CurrentIndex + skip)); var branch = cursor.Branch(); Contract.Assert(branch.IsSequenceTerminated == cursor.IsSequenceTerminated); Contract.Assert(branch.CurrentIndex == cursor.CurrentIndex); Contract.Assert(branch.LatestIndex == cursor.LatestIndex); Contract.Assert(branch.AtEndOfSequence == cursor.AtEndOfSequence); if (!branch.AtEndOfSequence) { int count = skip; if (branch.IsSequenceTerminated && branch.CurrentIndex + count > branch.LatestIndex + 1) { count = (branch.LatestIndex + 1) - branch.CurrentIndex; Contract.Assert(cursor.CurrentIndex + count == cursor.LatestIndex + 1); } branch.Move(count); Contract.Assert(branch.CurrentIndex == cursor.CurrentIndex + count); Contract.Assert(cursor.IsSequenceTerminated || branch.CurrentIndex == cursor.CurrentIndex + skip); Contract.Assert(!cursor.IsSequenceTerminated || cursor.CurrentIndex + skip <= cursor.LatestIndex + 1 || branch.CurrentIndex == cursor.LatestIndex + 1); Contract.Assume(!cursor.IsSequenceTerminated || branch.CurrentIndex == Math.Min(cursor.CurrentIndex + skip, cursor.LatestIndex + 1)); } else { Contract.Assert(branch.CurrentIndex == cursor.CurrentIndex); } return(branch); }
private static IEnumerable <IParseResult <IEnumerable <TResult> > > AtLeastIterator <TSource, TSeparator, TResult>( ICursor <TSource> source, IParser <TSource, TResult> parser, int count, int maximum, IParser <TSource, TSeparator> separator, bool nonGreedy) { Contract.Assume(source != null); Contract.Assume(source.IsForwardOnly); Contract.Assume(parser != null); Contract.Assume(count >= 0); Contract.Assume(maximum == -1 || maximum >= count); Contract.Assume(maximum != 0); // TODO: Update this method to properly support multi-result parsers. /* The current implementation just uses Math.Max for the lengths and aggregates all of the results into a single list. * The correct behavior is more like a SelectMany query, so consider using the new SelectMany overload that AllParser uses. */ /* This method is optimized to prevent stack overflows due to two factors: recursion and using Skip to move the source cursor. * * The previous implementation used recursive calls to NoneOrMore, in which there was a linear relationship between the number * of stack frames and the number of elements in the input sequence. As an input sequence grew and the parser continued matching * elements, the number of calls to the Skip operator (via the Remainder extension) grew linearly, and so did the number of branches * due to NoneOrMore using the Or operator, which not only added the Or operator to the stack but added all of the calls to the * quantified parser between the stack frames that Or added, for every subsequent element in the sequence that the parser matched. */ using (var branch = source.Branch()) { var list = new List <TResult>(); int total = 0; int totalLength = 0; bool iterate = true; if (nonGreedy && count == 0) { using (var lookAhead = new LookAheadParseResult <IEnumerable <TResult> >(Enumerable.Empty <TResult>(), length: 0)) { yield return(lookAhead); Contract.Assume(lookAhead.Succeeded.HasValue); if (lookAhead.Succeeded.Value) { iterate = false; } } } while (iterate) { bool hasResult = false; bool hasSeparatorResult = false; int length = 0; int separatorLength = 0; foreach (var result in parser.Parse(branch)) { hasResult = true; length = Math.Max(length, result.Length); list.Add(result.Value); } branch.Move(length); if (separator != null) { foreach (var separatorResult in separator.Parse(branch)) { hasSeparatorResult = true; separatorLength = Math.Max(separatorLength, separatorResult.Length); } branch.Move(separatorLength); } if (hasResult) { totalLength += length + separatorLength; if (total < (maximum == -1 ? count : maximum)) { total++; if (total == maximum) { break; } } if (separator == null || hasSeparatorResult) { if (nonGreedy && total >= count) { using (var lookAhead = new LookAheadParseResult <IEnumerable <TResult> >(list.AsReadOnly(), totalLength)) { yield return(lookAhead); Contract.Assume(lookAhead.Succeeded.HasValue); if (lookAhead.Succeeded.Value) { break; } } } continue; } } break; } if (total >= count) { yield return(new ParseResult <IEnumerable <TResult> >(list.AsReadOnly(), totalLength)); } } }
public IEnumerable <IParseResult <IEnumerable <TResult> > > Parse(ICursor <TSource> source) { firstParser = null; bool first = true; /* The stack of branches allows for an optimization on top of the previous implementation, which used to create a new * branch for every parse result in every parse result sequence. This new implementation changes that behavior slightly * by not creating a new branch for the last result in each sequence. All parser rules (at the time of writing) in Rxx * generate zero or one result only; therefore, the current branch can be moved forward and reused by child sequences * without affecting the parent result sequence, because it's already completed. This optimization has proven to be * greatly beneficial across normal parser queries that use And, All and Exactly operators. It should also be beneficial * for multi-result queries since most of the individual parser rules in these queries will only generate a single result. * A new branch would only be created for each result in the multi-result sequence. Scalar-result parsers that follow * sequentially in the All query would simply move their shared parent branch instead of creating new branches. */ var branches = new Stack <ICursor <TSource> >(); using (var root = source.Branch()) { branches.Push(root); var query = ParseResult.ReturnSuccessMany <TResult>(0) .SelectMany( parsers.Select( parser => (Func <Tuple <IParseResult <IEnumerable <TResult> >, bool>, IEnumerable <IParseResult <IEnumerable <TResult> > > >) (value => { if (first) { first = false; firstParser = parser; } var branch = branches.Peek(); // Item2 is only true when value.Item1 is the last element of its sequence. if (value.Item2) { branch.Move(value.Item1.Length); return(parser.Parse(branch).Select(result => result.YieldMany())); } else { var remainder = branch.Remainder(value.Item1.Length); branches.Push(remainder); return(parser.Parse(remainder) .Select(result => result.YieldMany()) .OnErrorOrDisposed(() => { /* Finally() cannot be used here. SelectMany generates each value as a Tuple, indicating whether it's * the last value in the sequence. To gather that info it uses a side-effect that causes it to look-ahead * in the enumerator, which would cause Finally to execute and dispose of the branch before it's used * by the child sequence. SelectMany only calls Dispose when the sequence is no longer in use. */ branches.Pop().Dispose(); })); } })), (firstResult, otherResults) => firstResult.Concat(otherResults)); // Use an iterator block to ensure that the local variables in this method aren't shared between enumerators. foreach (var result in query) { yield return(result); } } }