예제 #1
0
        /// <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);
                }));
            }
        }
예제 #2
0
        /// <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();
                });
            }));
        }
예제 #3
0
        /// <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>
        /// 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>
        /// 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))));
        }
예제 #6
0
        private static IObservable <IParseResult <TResult> > SelectManyInternal <TSource, TFirstResult, TSecondResult, TResult>(
            IObservableCursor <TSource> source,
            IObservableParser <TSource, TFirstResult> firstParser,
            Func <TFirstResult, IObservableParser <TSource, TSecondResult> > secondSelector,
            Func <TFirstResult, TSecondResult, TResult> resultSelector,
            Func <IParseResult <TFirstResult>, IParseResult <TSecondResult>, int> lengthSelector = null)
        {
            Contract.Requires(source != null);
            Contract.Requires(source.IsForwardOnly);
            Contract.Requires(firstParser != null);
            Contract.Requires(secondSelector != null);
            Contract.Requires(resultSelector != null);
            Contract.Ensures(Contract.Result <IObservable <IParseResult <TResult> > >() != null);

            return(from first in firstParser.Parse(source)
                   from second in Observable.Create <IParseResult <TSecondResult> >(
                       observer =>
            {
                var lookAhead = first as ILookAheadParseResult <TFirstResult>;
                bool hasResult = false;

                var remainder = source.Remainder(first.Length);

                return secondSelector(first.Value)
                .Parse(remainder)
                .Finally(remainder.Dispose)
                .Subscribe(
                    second =>
                {
                    hasResult = true;

                    if (lookAhead != null)
                    {
                        lookAhead.OnCompleted(success: true);

                        observer.OnCompleted();
                    }
                    else
                    {
                        observer.OnNext(second);
                    }
                },
                    observer.OnError,
                    () =>
                {
                    if (!hasResult && lookAhead != null)
                    {
                        lookAhead.OnCompleted(success: false);
                    }

                    observer.OnCompleted();
                });
            })
                   select lengthSelector == null
               ? first.Add(second, resultSelector)
               : first.Yield(second, resultSelector, lengthSelector));
        }
예제 #7
0
        /// <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)));
        }
예제 #8
0
파일: Filter.cs 프로젝트: trimonovds/Rxx
        /// <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))));
        }
예제 #9
0
        /// <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))));
        }
예제 #10
0
        /// <summary>
        /// Applies the specified <paramref name="parser"/> to generate 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="source">The observable sequence to parse.</param>
        /// <param name="parser">An object that defines a grammar to be applied to the observable sequence to generate matches.</param>
        /// <returns>An observable sequence of matches.</returns>
        public static IObservable <TResult> Parse <TSource, TResult>(
            this IObservable <TSource> source,
            IObservableParser <TSource, TResult> parser)
        {
            Contract.Requires(source != null);
            Contract.Requires(parser != null);
            Contract.Ensures(Contract.Result <IObservable <TResult> >() != null);

            // enableBranchOptimizations must be false: See the comments in the first interactive Rxx.Parsers.Linq.Parser.Parse method for details.
            return(parser.Parse(source.ToCursor(forwardOnly: true, enableBranchOptimizations: false)).Select(result => result.Value));
        }
예제 #11
0
        /// <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);
                    }
                });
            }));
        }
예제 #12
0
        /// <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))));
        }
예제 #13
0
        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();
                });
            }))));
        }
예제 #15
0
파일: Group.cs 프로젝트: trimonovds/Rxx
        private static IObservable <IParseResult <IObservable <TSource> > > AmbiguousGroupInternal <TSource>(
            IObservableCursor <TSource> source,
            IObservableParser <TSource, TSource> open,
            IObservableParser <TSource, TSource> close)
        {
            Contract.Requires(source != null);
            Contract.Requires(open != null);
            Contract.Requires(close != null);
            Contract.Ensures(Contract.Result <IObservable <IParseResult <IObservable <TSource> > > >() != null);

            return(Observable.Create <IParseResult <IObservable <TSource> > >(
                       observer =>
            {
                var branch = source.Branch();

                var disposables = new CompositeDisposable(branch);

                bool hasOpenResult = false;

                disposables.Add(open.Parse(source).SubscribeSafe(
                                    openResult =>
                {
                    hasOpenResult = true;

                    int openCount = 1;

                    var openSinks = new List <Action <IParseResult <TSource> > >();
                    var closeSinks = new List <Action <IParseResult <TSource> > >();
                    var contentSinks = new List <Action <IParseResult <TSource> > >();
                    var bufferedResults = new List <IEnumerable <IParseResult <IObservable <TSource> > > >();

                    Func <List <IParseResult <IObservable <TSource> > > > addBuffer = () =>
                    {
                        var buffer = new List <IParseResult <IObservable <TSource> > >();
                        bufferedResults.Add(buffer);
                        return buffer;
                    };

                    Action installSinks = () =>
                    {
                        var buffer = addBuffer();
                        var content = new List <TSource>();

                        openSinks.Add(result => content.Add(result.Value));
                        contentSinks.Add(result => content.Add(result.Value));
                        closeSinks.Add(result =>
                        {
                            // copy the content list to create a new branch
                            var branchResult = result.Yield(new List <TSource>(content).ToObservable(Scheduler.Immediate));

                            buffer.Add(branchResult);

                            if (openCount > 0)
                            {
                                content.Add(result.Value);
                            }
                        });
                    };

                    // base sinks must be installed first - openCount must be incremented before other sinks are executed
                    openSinks.Add(_ => { openCount++; installSinks(); });
                    closeSinks.Add(_ => { openCount--; });

                    // now we can install the sinks for the first open (matched in the foreach above)
                    installSinks();

                    var innerBranch = branch.Remainder(openResult.Length);

                    bool hasInnerResult = false;
                    IObservableParser <TSource, TSource> current = open;

                    var recursion = new SingleAssignmentDisposable();

                    disposables.Add(recursion);

                    recursion.Disposable = Scheduler.Immediate.Schedule(
                        self =>
                    {
                        var capturedBranch = innerBranch;

                        var innerParser =
                            open.OnSuccess(innerOpenResult =>
                        {
                            hasInnerResult = true;

                            var clone = openSinks.ToList();

                            // the sinks list is modified when open is matched, so we must run a clone
                            clone.ForEach(sink => sink(innerOpenResult));

                            innerBranch = capturedBranch.Remainder(innerOpenResult.Length);

                            current = open;
                        })
                            .Or(
                                close.OnSuccess(closeResult =>
                        {
                            hasInnerResult = true;

                            closeSinks.ForEach(sink => sink(closeResult));

                            innerBranch = capturedBranch.Remainder(closeResult.Length);

                            current = close;
                        }))
                            .Or(
                                current.Next.OnSuccess(content =>
                        {
                            hasInnerResult = true;

                            contentSinks.ForEach(sink => sink(content));

                            innerBranch = capturedBranch.Remainder(content.Length);
                        }));

                        var innerSubscription = new SingleAssignmentDisposable();

                        disposables.Add(innerSubscription);

                        innerSubscription.Disposable = innerParser.Parse(capturedBranch).SubscribeSafe(
                            _ => { },
                            observer.OnError,
                            () =>
                        {
                            if (openCount > 0 && hasInnerResult)
                            {
                                self();
                            }
                            else
                            {
                                innerBranch.Dispose();

                                disposables.Remove(innerSubscription);
                                disposables.Remove(recursion);

                                if (hasInnerResult)
                                {
                                    try
                                    {
                                        bufferedResults.Concat().ForEach(observer.OnNext);
                                    }
                                    catch (Exception ex)
                                    {
                                        observer.OnError(ex);
                                    }

                                    observer.OnCompleted();
                                }
                                else
                                {
                                    // completing without results is failure
                                    observer.OnCompleted();
                                }
                            }
                        });
                    });
                },
                                    observer.OnError,
                                    () =>
                {
                    if (!hasOpenResult)
                    {
                        observer.OnCompleted();
                    }
                }));

                return disposables;
            }));
        }
예제 #16
0
        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);
                }
            }));
        }
        public IObservable <IParseResult <IObservable <TResult> > > Parse(IObservableCursor <TSource> source)
        {
            return(Observable.Create <IParseResult <IObservable <TResult> > >(
                       observer =>
            {
                int matchCount = 0;
                int remainingLength = 0;

                Action <Action> iterate = moveNext =>
                {
                    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;
                    })
                                 .Do(
                        __ => { },
                        () =>
                    {
                        /* 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--;
                        }

                        moveNext();
                    });

                    observer.OnNext(ParseResult.Create(values, length: 1));
                };

                Action complete = () =>
                {
                    if (remainingLength > 0)
                    {
                        observer.OnNext(ObservableParseResult.SuccessMany <TResult>(remainingLength));
                    }

                    observer.OnCompleted();
                };

                var untilSubscription = new SerialDisposable();

                var schedule = Scheduler.Immediate.Schedule(
                    self =>
                {
                    if (!source.AtEndOfSequence &&
                        (untilCount == unlimitedCount || matchCount < untilCount))
                    {
                        if (untilParser == null)
                        {
                            iterate(self);
                        }
                        else
                        {
                            untilSubscription.SetDisposableIndirectly(() =>
                                                                      untilParser.Parse(source).Any().Subscribe(
                                                                          any =>
                            {
                                if (!any)
                                {
                                    iterate(self);
                                }
                                else
                                {
                                    complete();
                                }
                            },
                                                                          observer.OnError));
                        }
                    }
                    else
                    {
                        complete();
                    }
                });

                return new CompositeDisposable(schedule, untilSubscription);
            }));
        }
예제 #18
0
        private static IObservable <IParseResult <TResult> > SelectManyInternal <TSource, TFirstResult, TCollection, TResult>(
            IObservableCursor <TSource> source,
            IObservableParser <TSource, TFirstResult> parser,
            Func <TFirstResult, IObservable <TCollection> > collectionSelector,
            Func <TFirstResult, TCollection, TResult> resultSelector,
            Func <IParseResult <TFirstResult>, TCollection, int> lengthSelector = null)
        {
            Contract.Requires(source != null);
            Contract.Requires(source.IsForwardOnly);
            Contract.Requires(parser != null);
            Contract.Requires(collectionSelector != null);
            Contract.Requires(resultSelector != null);
            Contract.Ensures(Contract.Result <IObservable <IParseResult <TResult> > >() != null);

            return(from first in parser.Parse(source)
                   from second in Observable.Create <Tuple <TCollection, bool> >(
                       observer =>
            {
                var lookAhead = first as ILookAheadParseResult <TFirstResult>;
                var hasResult = false;
                TCollection previous = default(TCollection);

                return collectionSelector(first.Value).Subscribe(
                    second =>
                {
                    if (lookAhead != null)
                    {
                        hasResult = true;

                        lookAhead.OnCompleted(success: true);

                        observer.OnCompleted();
                    }
                    else
                    {
                        if (hasResult)
                        {
                            observer.OnNext(Tuple.Create(previous, false));
                        }

                        hasResult = true;
                        previous = second;
                    }
                },
                    observer.OnError,
                    () =>
                {
                    if (hasResult && lookAhead == null)
                    {
                        observer.OnNext(Tuple.Create(previous, true));
                    }
                    else if (!hasResult && lookAhead != null)
                    {
                        lookAhead.OnCompleted(success: false);
                    }

                    observer.OnCompleted();
                });
            })
                   select lengthSelector == null
               ? first.Yield(second.Item1, resultSelector, (f, s) => second.Item2?f.Length : 0)
                   : first.Yield(second.Item1, resultSelector, lengthSelector));
        }