예제 #1
0
        /// <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 }));
        }
예제 #2
0
        /// <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)));
            }
        }
예제 #3
0
        /// <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());
            }
        }