public IObservable <IParseResult <IObservable <TResult> > > Parse(IObservableCursor <TSource> source)
        {
            return(Observable.Defer(() =>
            {
                firstParser = null;

                bool first = true;
                var any = new AnyObservableParser <TSource, TResult>(parsers);
                var except = new List <IObservableParser <TSource, TResult> >();

                // See the AllObservableParser.Parse method for an explanation of the optimization that is provided by SelectMany's TContext argument
                var root = source.Branch();
                var rootDisposable = new RefCountDisposable(root);

                return ObservableParseResult.ReturnSuccessMany <TResult>(0)
                .SelectMany(
                    Tuple.Create(root, rootDisposable),
                    parsers.Select(
                        parser => (Func <Tuple <IObservableCursor <TSource>, RefCountDisposable>, Tuple <IParseResult <IObservable <TResult> >, bool>, Tuple <Tuple <IObservableCursor <TSource>, RefCountDisposable>, IObservable <IParseResult <IObservable <TResult> > > > >)
                            ((context, value) =>
                {
                    if (first)
                    {
                        first = false;
                        firstParser = parser;
                    }

                    var branch = context.Item1;
                    var disposable = context.Item2;
                    var refDisposable = disposable.GetDisposable();

                    IObservable <IParseResult <IObservable <TResult> > > results;

                    // Item2 is only true when value.Item1 is the last element of its sequence.
                    if (value.Item2)
                    {
                        branch.Move(value.Item1.Length);

                        results = any.Parse(except, branch)
                                  .Select(result => result.YieldMany())
                                  .Finally(refDisposable.Dispose);
                    }
                    else
                    {
                        branch = branch.Remainder(value.Item1.Length);

                        disposable = new RefCountDisposable(new CompositeDisposable(branch, refDisposable));

                        results = any.Parse(except, branch)
                                  .Select(result => result.YieldMany())
                                  .Finally(disposable.Dispose);
                    }

                    return Tuple.Create(Tuple.Create(branch, disposable), results);
                })),
                    (firstResult, otherResults) => firstResult.Concat(otherResults))
                .Finally(rootDisposable.Dispose);
            }));
        }
Beispiel #2
0
        public IObservable <IParseResult <IObservable <TResult> > > Parse(IObservableCursor <TSource> source)
        {
            return(Observable.Defer(() =>
            {
                firstParser = null;

                bool first = true;

                /* The TContext 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
                 * slightly 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 root = source.Branch();
                var rootDisposable = new RefCountDisposable(root);

                return ObservableParseResult.ReturnSuccessMany <TResult>(0)
                .SelectMany(
                    Tuple.Create(root, rootDisposable),
                    parsers.Select(
                        parser => (Func <Tuple <IObservableCursor <TSource>, RefCountDisposable>, Tuple <IParseResult <IObservable <TResult> >, bool>, Tuple <Tuple <IObservableCursor <TSource>, RefCountDisposable>, IObservable <IParseResult <IObservable <TResult> > > > >)
                            ((context, value) =>
                {
                    if (first)
                    {
                        first = false;
                        firstParser = parser;
                    }

                    var branch = context.Item1;
                    var disposable = context.Item2;
                    var refDisposable = disposable.GetDisposable();

                    IObservable <IParseResult <IObservable <TResult> > > results;

                    // Item2 is only true when value.Item1 is the last element of its sequence.
                    if (value.Item2)
                    {
                        branch.Move(value.Item1.Length);

                        results = parser.Parse(branch)
                                  .Select(result => result.YieldMany())
                                  .Finally(refDisposable.Dispose);
                    }
                    else
                    {
                        branch = branch.Remainder(value.Item1.Length);

                        disposable = new RefCountDisposable(new CompositeDisposable(branch, refDisposable));

                        results = parser.Parse(branch)
                                  .Select(result => result.YieldMany())
                                  .Finally(disposable.Dispose);
                    }

                    return Tuple.Create(Tuple.Create(branch, disposable), results);
                })),
                    (firstResult, otherResults) => firstResult.Concat(otherResults))
                .Finally(rootDisposable.Dispose);
            }));
        }
        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);
            }));
        }
        private IObservableParser <byte, int> Yield(int value)
        {
            Contract.Ensures(Contract.Result <IObservableParser <byte, int> >() != null);

            return(this.Yield(_ => ObservableParseResult.Return(value, 0)));
        }