/// <summary> /// Matches the left parser and the right parser in any order and yields the matches from both in a concatenated sequence, /// or yields no matches if either the first or second parser has 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">A parser to be matched, before or after the other parser.</param> /// <param name="otherParser">The other parser to be matched, before or after the first parser.</param> /// <returns>A parser that yields a concatenated sequence of matches from the first parser and the second parser in any order, /// or no matches if either the first or second parser has no matches.</returns> public static IObservableParser <TSource, IObservable <TResult> > AndUnordered <TSource, TResult>( this IObservableParser <TSource, TResult> parser, IObservableParser <TSource, IObservable <TResult> > otherParser) { Contract.Requires(parser != null); Contract.Requires(otherParser != null); Contract.Ensures(Contract.Result <IObservableParser <TSource, IObservable <TResult> > >() != null); // Optimization similar to And is inappropriate here because a grammar of sequential AndUnordered parsers actually implies // an order that must be respected between each unordered group. For example, a parser grammar such as "{12}{34}", where // each character within {} is combined using AndUnordered, must match any of the following input sequences: "1234", "2134", // "1243", "2143"; however, it must not match any of the following input sequences: "3124", "4321", "3241", "4123", etc. return(AllUnordered(new[] { parser.Amplify(), otherParser })); }
/// <summary> /// Matches the left parser followed by the right parser and yields the matches from both in a concatenated sequence, /// or yields no matches if either the first or second parser has 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 to be matched first.</param> /// <param name="nextParser">The parser to be matched after the first <paramref name="parser"/>.</param> /// <returns>A parser that yields a concatenated sequence of matches from the first parser followed by the second parser, /// or no matches if either the first or second parser has no matches.</returns> public static IObservableParser <TSource, IObservable <TResult> > And <TSource, TResult>( this IObservableParser <TSource, TResult> parser, IObservableParser <TSource, IObservable <TResult> > nextParser) { Contract.Requires(parser != null); Contract.Requires(nextParser != null); Contract.Ensures(Contract.Result <IObservableParser <TSource, IObservable <TResult> > >() != null); // The following code is an optimization that combines nested Ands (on the right side) into a single And. // It's not possible for this overload to be called with a nested And on the left side because And always // produces IEnumerable<TResult> and this overload defines the left side as a scalar TResult, thus the // compiler will choose a different overload of And when nested on the left. var right = new List <IObservableParser <TSource, TResult> >(); var rightMany = new List <IObservableParser <TSource, IObservable <TResult> > >(); var nestedRight = nextParser as AllObservableParser <TSource, TResult>; var nestedRightMany = nextParser as AllManyObservableParser <TSource, TResult>; if (nestedRight == null && nestedRightMany == null) { rightMany.Add(nextParser); } else if (nestedRight != null) { right.AddRange(nestedRight.Parsers); } else { rightMany.AddRange(nestedRightMany.Parsers); } // If the left or right side has no parsers then the parse operation must fail, but must be evaluated anyway. // e.g., Given the nested combination of someParser.And(All(emptyParserList)), this is gauranteed to fail // on All, so it seems futile to even attempt parsing the left side; however, since there may be side-effects // the left side must be parsed anyway. There are several nested parser combinations that meet this criteria. // Futhermore, all combinations must not throw an exception. // In any possible case where the left or right side doesn't have any parsers, the parse operation must // result in a parse failure (i.e., no results) - not an exception. if (right.Count > 0) { return(All(new[] { parser }.Concat(right))); } else { return(All(new[] { parser.Amplify() }.Concat(rightMany))); } }
/// <summary> /// Matches the specified <paramref name="parser"/> the specified number of times. /// </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 to be matched.</param> /// <param name="count">The specified number of times to match the specified <paramref name="parser"/>.</param> /// <returns>A parser that matches the specified <paramref name="parser"/> the specified number of times.</returns> public static IObservableParser <TSource, IObservable <TResult> > Exactly <TSource, TResult>( this IObservableParser <TSource, TResult> parser, int count) { Contract.Requires(parser != null); Contract.Requires(count >= 0); Contract.Ensures(Contract.Result <IObservableParser <TSource, IObservable <TResult> > >() != null); if (count == 0) { return(parser.Yield(_ => ObservableParseResult.ReturnSuccessMany <TResult>(length: 0))); } else if (count == 1) { // Profiling has shown this to be about 50% faster than Repeat(parser, 1).All() return(parser.Amplify()); } else if (parser is IObservableParserCursor <TSource> ) { /* Profiling has shown this to be exponentially faster in next.Exactly(largeN) queries for Ix. * It hasn't been profiled in Rx, but I'm assuming that for similar reasons as Ix it would prove * to be exponentially faster. Furthermore, due to the extra plumbing in Rx that's being avoided * by this optimization, it may have even greater gains than Ix. */ return(parser.Yield <TSource, TResult, IObservable <TResult> >( "Exactly", source => from list in source.Take(count).ToList() where list.Count == count select ParseResult.Create(list.Cast <TResult>().ToObservable(Scheduler.Immediate), count))); } else { return(System.Linq.Enumerable.Repeat(parser, count).All()); } }