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