/// <summary> /// Matches the specified <paramref name="parser"/> or yields the specified default result if there are /// no matches. /// </summary> /// <typeparam name="TSource">The type of the source elements.</typeparam> /// <typeparam name="TResult">The type of the elements that are generated from parsing the source elements.</typeparam> /// <param name="parser">The parser that might produce matches.</param> /// <param name="defaultResult">The value that is yielded if the specified <paramref name="parser"/> does not match.</param> /// <returns>A parser that yields matches from the specified <paramref name="parser"/> or the specified default result /// if the <paramref name="parser"/> does not match.</returns> public static IObservableParser <TSource, TResult> WithDefault <TSource, TResult>( this IObservableParser <TSource, TResult> parser, TResult defaultResult) { Contract.Requires(parser != null); Contract.Ensures(Contract.Result <IObservableParser <TSource, TResult> >() != null); return(parser.Yield( "WithDefault", (source, observer) => { bool hasResult = false; return parser.Parse(source).SubscribeSafe( result => { hasResult = true; observer.OnNext(result); }, observer.OnError, () => { if (!hasResult) { observer.OnNext(ParseResult.Create(defaultResult, length: 0)); } observer.OnCompleted(); }); })); }
/// <summary> /// Matches the specified <paramref name="parser"/> or yields success without a value when it does not match. /// </summary> /// <typeparam name="TSource">The type of the source elements.</typeparam> /// <typeparam name="TResult">The type of the elements of the result sequences that are generated from parsing the source elements.</typeparam> /// <param name="parser">The parser that might produce matches.</param> /// <returns>A parser that yields matches from the specified <paramref name="parser"/> or /// an empty observable sequence to indicate success when it does not match.</returns> public static IObservableParser <TSource, IObservable <TResult> > Maybe <TSource, TResult>( this IObservableParser <TSource, IObservable <TResult> > parser) { Contract.Requires(parser != null); Contract.Ensures(Contract.Result <IObservableParser <TSource, IObservable <TResult> > >() != null); return(parser.Yield( "Maybe", (source, observer) => { bool hasResult = false; return parser.Parse(source).SubscribeSafe( result => { hasResult = true; observer.OnNext(result); }, observer.OnError, () => { if (!hasResult) { observer.OnNext(ObservableParseResult.SuccessMany <TResult>(length: 0)); } observer.OnCompleted(); }); })); }
/// <summary> /// Yields success when the specified <paramref name="parser"/> does not match. /// </summary> /// <typeparam name="TSource">The type of the source elements.</typeparam> /// <typeparam name="TResult">The type of the elements that are generated from parsing the source elements.</typeparam> /// <param name="parser">The parser for which any match results in failure.</param> /// <returns>A parser that yields failure when the specified <paramref name="parser"/> matches or /// an empty sequence to indicate success when it does not match.</returns> public static IObservableParser <TSource, IObservable <TResult> > None <TSource, TResult>( this IObservableParser <TSource, TResult> parser) { Contract.Requires(parser != null); Contract.Ensures(Contract.Result <IObservableParser <TSource, IObservable <TResult> > >() != null); if (parser is IObservableParserCursor <TSource> ) { return(parser.AtEndOfSequence()); } else { return(parser.Yield <TSource, TResult, IObservable <TResult> >( "None", (source, observer) => { return parser.Parse(source).Any().SubscribeSafe( any => { if (!any) { observer.OnNext(ObservableParseResult.SuccessMany <TResult>(length: 0)); } }, observer.OnError, observer.OnCompleted); })); } }
/// <summary> /// Indicates a successful parse operation without actually parsing by yielding a single result containing an empty sequence. /// </summary> /// <typeparam name="TSource">The type of the source elements.</typeparam> /// <typeparam name="TResult">The type of the elements in the sequences that are generated from parsing the source elements.</typeparam> /// <param name="parser">The parser for which a single empty result sequence is returned to indicate success.</param> /// <remarks> /// <see cref="Success{TSource,TResult}(IObservableParser{TSource,IObservable{TResult}})"/> is required as an explicit overload /// because the meaning of the parser's result sequence is special and must not be compounded into a sequence of sequences, /// which would happen if the <see cref="Success{TSource,TResult}(IObservableParser{TSource,TResult})"/> overload were to be called /// instead. /// </remarks> /// <returns>A parser that returns a single result containing an empty sequence with a length /// of zero, starting from the index at which the specified <paramref name="parser"/> starts.</returns> public static IObservableParser <TSource, IObservable <TResult> > Success <TSource, TResult>( this IObservableParser <TSource, IObservable <TResult> > parser) { Contract.Requires(parser != null); Contract.Ensures(Contract.Result <IObservableParser <TSource, IObservable <TResult> > >() != null); return(parser.Yield("Success", source => ObservableParseResult.ReturnSuccessMany <TResult>(length: 0))); }
/// <summary> /// Converts greedy matches from the specified <paramref name="parser"/> into matches that /// have a length of zero. /// </summary> /// <typeparam name="TSource">The type of the source elements.</typeparam> /// <typeparam name="TResult">The type of the elements that are generated from parsing the source elements.</typeparam> /// <param name="parser">The greedy parser to be made into a non-greedy parser.</param> /// <returns>A parser that converts the greedy matches from the specified <paramref name="parser"/> into /// matches that have a length of zero.</returns> public static IObservableParser <TSource, TResult> NonGreedy <TSource, TResult>( this IObservableParser <TSource, TResult> parser) { Contract.Requires(parser != null); Contract.Ensures(Contract.Result <IObservableParser <TSource, TResult> >() != null); return(parser.Yield("NonGreedy", source => parser.Parse(source).Select(result => result.Yield(length: 0)))); }
/// <summary> /// Projects each match from the specified <paramref name="parser"/> into a singleton observable sequence /// that contains the match's value. /// </summary> /// <typeparam name="TSource">The type of the source elements.</typeparam> /// <typeparam name="TResult">The type of the elements that are generated from parsing the source elements.</typeparam> /// <param name="parser">The parser from which matches will be projected into singleton observable sequences.</param> /// <returns>A parser that yields matches from the specified <paramref name="parser"/> projected into singleton /// observable sequences.</returns> public static IObservableParser <TSource, IObservable <TResult> > Amplify <TSource, TResult>( this IObservableParser <TSource, TResult> parser) { Contract.Requires(parser != null); Contract.Ensures(Contract.Result <IObservableParser <TSource, IObservable <TResult> > >() != null); return(parser.Yield("Amplify", source => parser.Parse(source).Select(result => result.YieldMany()))); }
/// <summary> /// Indicates a failure to parse without actually parsing by returning an empty sequence of parse results. /// </summary> /// <typeparam name="TSource">The type of the source elements.</typeparam> /// <typeparam name="TResult">The type of the elements that are generated from parsing the source elements.</typeparam> /// <param name="parser">The parser that is to fail.</param> /// <returns>A parser that always returns an empty sequence of parse results, starting from the index at which /// the specified <paramref name="parser"/> starts.</returns> public static IObservableParser <TSource, TResult> Failure <TSource, TResult>( this IObservableParser <TSource, TResult> parser) { Contract.Requires(parser != null); Contract.Ensures(Contract.Result <IObservableParser <TSource, TResult> >() != null); return(parser.Yield("Failure", source => ObservableParseResult.ReturnFailure <TResult>())); }
/// <summary> /// Invokes the specified <paramref name="action"/> on each result for its side-effects. /// </summary> /// <typeparam name="TSource">The type of the source elements.</typeparam> /// <typeparam name="TResult">The type of the elements that are generated from parsing the source elements.</typeparam> /// <param name="parser">The parser from which results will be supplied to the specified <paramref name="action"/>.</param> /// <param name="action">The method that will be called for each parser result.</param> /// <returns>A new parser that is the same as the specified parser and also invokes the specified /// <paramref name="action"/> with each result for its side-effects.</returns> public static IObservableParser <TSource, TResult> OnSuccess <TSource, TResult>( this IObservableParser <TSource, TResult> parser, Action <IParseResult <TResult> > action) { Contract.Requires(parser != null); Contract.Requires(action != null); Contract.Ensures(Contract.Result <IObservableParser <TSource, TResult> >() != null); return(parser.Yield("OnSuccess", source => parser.Parse(source).Do(action))); }
/// <summary> /// Projects matches from the specified <paramref name="parser"/> into a new form. /// </summary> /// <typeparam name="TSource">The type of the source elements.</typeparam> /// <typeparam name="TIntermediate">The type of the elements that are generated from parsing the source elements.</typeparam> /// <typeparam name="TResult">The type of the elements that are projected from the matches of the specified <paramref name="parser"/>.</typeparam> /// <param name="parser">The parser from which matches will be projected by the specified <paramref name="selector"/> function.</param> /// <param name="selector">A transform function to apply to each match.</param> /// <returns>A parser that projects matches from the specified <paramref name="parser"/> into a new form.</returns> public static IObservableParser <TSource, TResult> Select <TSource, TIntermediate, TResult>( this IObservableParser <TSource, TIntermediate> parser, Func <TIntermediate, TResult> selector) { Contract.Requires(parser != null); Contract.Requires(selector != null); Contract.Ensures(Contract.Result <IObservableParser <TSource, TResult> >() != null); return(parser.Yield("Select", source => parser.Parse(source).Select(result => result.Yield(selector)))); }
/// <summary> /// Matches everything in between the specified <paramref name="open"/> and <paramref name="close"/> parsers, /// yielding the first unambiguous match as well as everything in between any sub-groups and overlapping groups, /// extending past the unambiguous match of the <paramref name="close"/> parser, that match the same grammar. /// </summary> /// <typeparam name="TSource">The type of the source elements.</typeparam> /// <param name="open">The parser after which the group begins.</param> /// <param name="close">The parser at which the group ends.</param> /// <remarks> /// The same <paramref name="open"/> or <paramref name="close"/> parser may produce multiple matches at the same index. /// </remarks> /// <returns>A parser with a grammar that matches the <paramref name="open"/> parser, followed by everything up to the first /// match of the <paramref name="close"/> parser, yielding the results in between as well as the results of all ambiguous /// matches of the group grammar.</returns> public static IObservableParser <TSource, IObservable <TSource> > AmbiguousGroup <TSource>( this IObservableParser <TSource, TSource> open, IObservableParser <TSource, TSource> close) { Contract.Requires(open != null); Contract.Requires(close != null); Contract.Ensures(Contract.Result <IObservableParser <TSource, IObservable <TSource> > >() != null); return(open.Yield("AmbiguousGroup", source => AmbiguousGroupInternal(source, open, close))); }
/// <summary> /// Matches all results from the specified <paramref name="parser"/> for which the specified /// <paramref name="predicate"/> returns <see langword="true"/>. /// </summary> /// <typeparam name="TSource">The type of the source elements.</typeparam> /// <typeparam name="TResult">The type of the elements that are generated from parsing the source elements.</typeparam> /// <param name="parser">The parser from which matches will be filtered by the specified <paramref name="predicate"/>.</param> /// <param name="predicate">A function that returns <see langword="true"/> to indicate when a match should be yielded and /// <see langword="false"/> when a match should be ignored.</param> /// <returns>A parser that matches only those results from the specified <paramref name="parser"/> for which /// the specified <paramref name="predicate"/> returns <see langword="true"/>.</returns> public static IObservableParser <TSource, TResult> Where <TSource, TResult>( this IObservableParser <TSource, TResult> parser, Func <TResult, bool> predicate) { Contract.Requires(parser != null); Contract.Requires(predicate != null); Contract.Ensures(Contract.Result <IObservableParser <TSource, TResult> >() != null); return(parser.Yield("Where", source => parser.Parse(source).Where(result => predicate(result.Value)))); }
/// <summary> /// Indicates a successful parse operation without actually parsing by yielding the specified scalar <paramref name="result"/> /// with the specified length. /// </summary> /// <typeparam name="TSource">The type of the source elements.</typeparam> /// <typeparam name="TIntermediate">The type of the elements that are generated from parsing the source elements.</typeparam> /// <typeparam name="TResult">The type of the <paramref name="result"/>.</typeparam> /// <param name="parser">The parser for which the specified <paramref name="result"/> indicates success.</param> /// <param name="result">The value of the created parser's result.</param> /// <param name="length">The length of the created parser's result.</param> /// <returns>A parser that always returns the specified scalar <paramref name="result"/> with the specified /// length, starting from the index at which the specified <paramref name="parser"/> starts.</returns> public static IObservableParser <TSource, TResult> Success <TSource, TIntermediate, TResult>( this IObservableParser <TSource, TIntermediate> parser, TResult result, int length) { Contract.Requires(parser != null); Contract.Requires(length >= 0); Contract.Ensures(Contract.Result <IObservableParser <TSource, TResult> >() != null); return(parser.Yield("Success", source => ObservableParseResult.Return(result, length))); }
/// <summary> /// Projects each match from the specified <paramref name="parser"/> into another parser, merges all of the results /// and transforms them with the result selector function. /// </summary> /// <typeparam name="TSource">The type of the source elements.</typeparam> /// <typeparam name="TFirstResult">The type of the elements that are generated from parsing the source elements.</typeparam> /// <typeparam name="TSecondResult">The type of the elements that are generated from the projected parsers.</typeparam> /// <typeparam name="TResult">The type of the elements that are projected from the matches of the projected parsers.</typeparam> /// <param name="parser">The parser from which each match is passed to the specified parser selector function to create /// the next parser.</param> /// <param name="parserSelector">A transform function to apply to each match from the first <paramref name="parser"/>.</param> /// <param name="resultSelector">A transform function to apply to each match from the projected parsers.</param> /// <returns>A parser that projects each match from the specified <paramref name="parser"/> into another parser, /// merges all of the results and transforms them with the result selector function.</returns> public static IObservableParser <TSource, TResult> SelectMany <TSource, TFirstResult, TSecondResult, TResult>( this IObservableParser <TSource, TFirstResult> parser, Func <TFirstResult, IObservableParser <TSource, TSecondResult> > parserSelector, Func <TFirstResult, TSecondResult, TResult> resultSelector) { Contract.Requires(parser != null); Contract.Requires(parserSelector != null); Contract.Requires(resultSelector != null); Contract.Ensures(Contract.Result <IObservableParser <TSource, TResult> >() != null); return(parser.Yield("SelectMany", source => SelectManyInternal(source, parser, parserSelector, resultSelector))); }
/// <summary> /// Throws a <see cref="ParseException"/> returned by the specified function if the specified /// <paramref name="parser"/> does not match. /// </summary> /// <typeparam name="TSource">The type of the source elements.</typeparam> /// <typeparam name="TResult">The type of the elements that are generated from parsing the source elements.</typeparam> /// <param name="parser">The parser that must succeed otherwise a <see cref="ParseException"/> is thrown.</param> /// <param name="exceptionFactory">A function that returns the <see cref="ParseException"/> to be thrown describing the /// failed expectation.</param> /// <returns>A parser that yields the matches from the specified <paramref name="parser"/> or throws /// a <see cref="ParseException"/> returned by the specified functions if there are no matches.</returns> public static IObservableParser <TSource, TResult> Required <TSource, TResult>( this IObservableParser <TSource, TResult> parser, Func <int, Exception> exceptionFactory) { Contract.Requires(parser != null); Contract.Requires(exceptionFactory != null); Contract.Ensures(Contract.Result <IObservableParser <TSource, TResult> >() != null); return(parser.Yield( "Required", (source, observer) => { int index = source.CurrentIndex; bool hasResult = false; return parser.Parse(source).SubscribeSafe( result => { hasResult = true; observer.OnNext(result); }, observer.OnError, () => { if (hasResult) { observer.OnCompleted(); } else { Exception exception = exceptionFactory(index); if (exception == null) { exception = new ParseException(index); } else if (!(exception is ParseException)) { exception = new ParseException(index, exception); } #if !SILVERLIGHT && !PORT_45 && !PORT_40 ParserTraceSources.Input.TraceEvent(System.Diagnostics.TraceEventType.Error, 0, "{0}", exception); #endif observer.OnError(exception); } }); })); }
/// <summary> /// Projects each match from the specified <paramref name="parser"/> into an observable sequence, merges all of the results /// and transforms them with the result selector function. /// </summary> /// <typeparam name="TSource">The type of the source elements.</typeparam> /// <typeparam name="TFirstResult">The type of the elements that are generated from parsing the source elements.</typeparam> /// <typeparam name="TCollection">The type of the elements in the sequences that are projected from the matches.</typeparam> /// <typeparam name="TResult">The type of the elements that are projected from the projected sequences.</typeparam> /// <param name="parser">The parser from which each match is passed to the specified collection selector function.</param> /// <param name="collectionSelector">A transform function to apply to each match from the first <paramref name="parser"/>.</param> /// <param name="resultSelector">A transform function to apply to each element from the projected sequences.</param> /// <param name="lengthSelector">A function that returns the length for each pair of projected values.</param> /// <returns>A parser that projects each match from the specified <paramref name="parser"/> into an observable sequence, /// merges all of the results and transforms them with the result selector function.</returns> public static IObservableParser <TSource, TResult> SelectMany <TSource, TFirstResult, TCollection, TResult>( this IObservableParser <TSource, TFirstResult> parser, Func <TFirstResult, IObservable <TCollection> > collectionSelector, Func <TFirstResult, TCollection, TResult> resultSelector, Func <IParseResult <TFirstResult>, TCollection, int> lengthSelector) { Contract.Requires(parser != null); Contract.Requires(collectionSelector != null); Contract.Requires(resultSelector != null); Contract.Requires(lengthSelector != null); Contract.Ensures(Contract.Result <IObservableParser <TSource, TResult> >() != null); return(parser.Yield("SelectMany", source => SelectManyInternal(source, parser, collectionSelector, resultSelector, lengthSelector))); }
/// <summary> /// Yields success if the specified parser starts at the end of the input sequence. /// </summary> /// <typeparam name="TSource">The type of the source elements.</typeparam> /// <typeparam name="TResult">The type of the elements that are generated from parsing the source elements.</typeparam> /// <typeparam name="TSuccess">The type of the success value.</typeparam> /// <param name="parser">The parser that provides the context in which to check whether the cursor is at the end of the input sequence.</param> /// <param name="successResult">The value that is yielded if the specified parser starts at the end of the input sequence.</param> /// <returns>A new parser that yields success without parsing if the cursor is positioned at the end of the input sequence; otherwise, yields no results.</returns> public static IObservableParser <TSource, TSuccess> AtEndOfSequence <TSource, TResult, TSuccess>( this IObservableParser <TSource, TResult> parser, TSuccess successResult) { Contract.Requires(parser != null); Contract.Ensures(Contract.Result <IObservableParser <TSource, TSuccess> >() != null); return(parser.Yield <TSource, TResult, TSuccess>( "AtEndOfSequence", (source, observer) => { IDisposable disposable; if (source.IsSequenceTerminated) { if (source.AtEndOfSequence) { observer.OnNext(ParseResult.Create(successResult, length: 0)); } observer.OnCompleted(); disposable = Disposable.Empty; } else { bool hasResult = false; disposable = source.Subscribe( Observer.Create <TSource>( result => hasResult = true, observer.OnError, () => { if (!hasResult) { Contract.Assume(source.AtEndOfSequence); observer.OnNext(ParseResult.Create(successResult, length: 0)); } observer.OnCompleted(); }), count: 1); } return disposable; })); }
/// <summary> /// Matches the specified <paramref name="parser"/> the specified number of times. /// </summary> /// <typeparam name="TSource">The type of the source elements.</typeparam> /// <typeparam name="TResult">The type of the elements that are generated from parsing the source elements.</typeparam> /// <param name="parser">The parser to be matched.</param> /// <param name="count">The specified number of times to match the specified <paramref name="parser"/>.</param> /// <returns>A parser that matches the specified <paramref name="parser"/> the specified number of times.</returns> public static IObservableParser <TSource, IObservable <TResult> > Exactly <TSource, TResult>( this IObservableParser <TSource, TResult> parser, int count) { Contract.Requires(parser != null); Contract.Requires(count >= 0); Contract.Ensures(Contract.Result <IObservableParser <TSource, IObservable <TResult> > >() != null); if (count == 0) { return(parser.Yield(_ => ObservableParseResult.ReturnSuccessMany <TResult>(length: 0))); } else if (count == 1) { // Profiling has shown this to be about 50% faster than Repeat(parser, 1).All() return(parser.Amplify()); } else if (parser is IObservableParserCursor <TSource> ) { /* Profiling has shown this to be exponentially faster in next.Exactly(largeN) queries for Ix. * It hasn't been profiled in Rx, but I'm assuming that for similar reasons as Ix it would prove * to be exponentially faster. Furthermore, due to the extra plumbing in Rx that's being avoided * by this optimization, it may have even greater gains than Ix. */ return(parser.Yield <TSource, TResult, IObservable <TResult> >( "Exactly", source => from list in source.Take(count).ToList() where list.Count == count select ParseResult.Create(list.Cast <TResult>().ToObservable(Scheduler.Immediate), count))); } else { return(System.Linq.Enumerable.Repeat(parser, count).All()); } }
/// <summary> /// Applies an <paramref name="accumulator"/> function over each result sequence from the /// specified <paramref name="parser"/> and yields a sequence of accumulated results. /// </summary> /// <typeparam name="TSource">The type of the source elements.</typeparam> /// <typeparam name="TIntermediate">The type of the elements that are generated from parsing the source elements.</typeparam> /// <typeparam name="TAccumulate">The type of the accumulation.</typeparam> /// <typeparam name="TResult">The type of the elements that are generated from projecting the accumulation.</typeparam> /// <param name="parser">The parser that produces a sequence of result sequences to be aggregated.</param> /// <param name="seed">A function that returns the initial value of the accumulation for each parse result.</param> /// <param name="accumulator">A function to be invoked on each element of each parse result.</param> /// <param name="selector">A function that projects the final aggregation of each parse result.</param> /// <returns>A parser that returns the aggregated results.</returns> public static IObservableParser <TSource, TResult> Aggregate <TSource, TIntermediate, TAccumulate, TResult>( this IObservableParser <TSource, IObservable <TIntermediate> > parser, Func <TAccumulate> seed, Func <TAccumulate, TIntermediate, TAccumulate> accumulator, Func <TAccumulate, TResult> selector) { Contract.Requires(parser != null); Contract.Requires(accumulator != null); Contract.Requires(selector != null); Contract.Ensures(Contract.Result <IObservableParser <TSource, TResult> >() != null); return(parser.Yield( "Aggregate", source => from result in parser.Parse(source) from acc in result.Value.Aggregate(seed(), accumulator) select result.Yield(selector(acc)))); }
public static IObservableParser <TSource, TResult> OnFailure <TSource, TResult>( this IObservableParser <TSource, TResult> parser, Action action) { Contract.Requires(parser != null); Contract.Requires(action != null); Contract.Ensures(Contract.Result <IObservableParser <TSource, TResult> >() != null); return(parser.Yield( "OnFailure", (source, observer) => { bool hasResult = false; return parser.Parse(source).Subscribe( result => { hasResult = true; observer.OnNext(result); }, observer.OnError, () => { if (!hasResult) { try { action(); } catch (Exception ex) { observer.OnError(ex); return; } } observer.OnCompleted(); }); })); }
/// <summary> /// Matches the single element from the ambiguous result sequence in each match that is yielded by the specified /// <paramref name="parser"/> and fails for any match in which there is zero or more than one element. /// </summary> /// <typeparam name="TSource">The type of the source elements.</typeparam> /// <typeparam name="TResult">The type of the elements of the result sequences that are generated from parsing the source elements.</typeparam> /// <param name="parser">The parser from which the single result element is yielded for each match.</param> /// <returns>A parser that matches the single element from the result sequence of each match that is /// yielded by the specified <paramref name="parser"/> and fails for any match in which there is zero /// or more than one element.</returns> public static IObservableParser <TSource, TResult> Single <TSource, TResult>( this IObservableParser <TSource, IObservable <TResult> > parser) { Contract.Requires(parser != null); Contract.Ensures(Contract.Result <IObservableParser <TSource, TResult> >() != null); return(parser.Yield <TSource, IObservable <TResult>, TResult>( "Single", source => parser.Parse(source).SelectMany( result => Observable.Create <IParseResult <TResult> >( observer => { bool hasResult = false; TResult firstResult = default(TResult); return result.Value.Take(2).Subscribe( innerResult => { if (!hasResult) { firstResult = innerResult; hasResult = true; } else { hasResult = false; } }, observer.OnError, () => { if (hasResult) { observer.OnNext(result.Yield(firstResult)); } observer.OnCompleted(); }); })))); }
private static IObservableParser <TSource, IObservable <TResult> > AtLeast <TSource, TSeparator, TResult>( this IObservableParser <TSource, TResult> parser, string name, int count, int maximum = -1, IObservableParser <TSource, TSeparator> separator = null, bool nonGreedy = false) { Contract.Requires(parser != null); Contract.Requires(!string.IsNullOrEmpty(name)); Contract.Requires(count >= 0); Contract.Requires(maximum == -1 || maximum >= count); Contract.Requires(maximum != 0); Contract.Ensures(Contract.Result <IObservableParser <TSource, IObservable <TResult> > >() != null); // 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 AllObservableParser 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. */ return(parser.Yield <TSource, TResult, IObservable <TResult> >( name, (source, observer) => { Contract.Requires(source.IsForwardOnly); var branch = source.Branch(); var list = new List <TResult>(); int total = 0; int totalLength = 0; Action onCompleted = () => { if (total >= count) { observer.OnNext(new ParseResult <IObservable <TResult> >(list.ToObservable(Scheduler.Immediate), totalLength)); } observer.OnCompleted(); }; var subscription = new SerialDisposable(); Func <IDisposable> start = () => Scheduler.Immediate.Schedule(self => { bool hasResult = false; bool hasSeparatorResult = false; int length = 0; int separatorLength = 0; Action currentCompleted = () => { if (hasResult) { totalLength += length + separatorLength; if (total < (maximum == -1 ? count : maximum)) { total++; } if (total != maximum && (separator == null || hasSeparatorResult)) { if (nonGreedy && total >= count) { var lookAhead = new LookAheadParseResult <IObservable <TResult> >(list.ToObservable(Scheduler.Immediate), totalLength); subscription.SetDisposableIndirectly(() => new CompositeDisposable( lookAhead, lookAhead.Subscribe(success => { if (success) { onCompleted(); } else { self(); } }))); observer.OnNext(lookAhead); return; } else { self(); return; } } } onCompleted(); }; subscription.SetDisposableIndirectly(() => parser.Parse(branch).SubscribeSafe( result => { hasResult = true; length = Math.Max(length, result.Length); list.Add(result.Value); }, observer.OnError, () => { branch.Move(length); if (separator == null) { currentCompleted(); } else { subscription.SetDisposableIndirectly(() => separator.Parse(branch).SubscribeSafe( separatorResult => { hasSeparatorResult = true; separatorLength = Math.Max(separatorLength, separatorResult.Length); }, observer.OnError, () => { branch.Move(separatorLength); currentCompleted(); })); } })); }); if (nonGreedy && count == 0) { var startSubscription = new SingleAssignmentDisposable(); var lookAhead = new LookAheadParseResult <IObservable <TResult> >(Observable.Empty <TResult>(), length: 0); var lookAheadSubscription = lookAhead.Subscribe(success => { if (success) { onCompleted(); } else { startSubscription.Disposable = start(); } }); observer.OnNext(lookAhead); return new CompositeDisposable(branch, subscription, lookAhead, lookAheadSubscription, startSubscription); } else { var startSubscription = start(); return new CompositeDisposable(branch, subscription, startSubscription); } })); }