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);
            }));
        }