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