Ejemplo n.º 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 IParser <TSource, IEnumerable <TResult> > AndUnordered <TSource, TResult>(
            this IParser <TSource, TResult> parser,
            IParser <TSource, IEnumerable <TResult> > otherParser)
        {
            Contract.Requires(parser != null);
            Contract.Requires(otherParser != null);
            Contract.Ensures(Contract.Result <IParser <TSource, IEnumerable <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 }));
        }
Ejemplo n.º 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 IParser <TSource, IEnumerable <TResult> > And <TSource, TResult>(
            this IParser <TSource, TResult> parser,
            IParser <TSource, IEnumerable <TResult> > nextParser)
        {
            Contract.Requires(parser != null);
            Contract.Requires(nextParser != null);
            Contract.Ensures(Contract.Result <IParser <TSource, IEnumerable <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 <IParser <TSource, TResult> >();
            var rightMany       = new List <IParser <TSource, IEnumerable <TResult> > >();
            var nestedRight     = nextParser as AllParser <TSource, TResult>;
            var nestedRightMany = nextParser as AllManyParser <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)));
            }
        }
Ejemplo n.º 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 IParser <TSource, IEnumerable <TResult> > Exactly <TSource, TResult>(
            this IParser <TSource, TResult> parser,
            int count)
        {
            Contract.Requires(parser != null);
            Contract.Requires(count >= 0);
            Contract.Ensures(Contract.Result <IParser <TSource, IEnumerable <TResult> > >() != null);

            if (count == 0)
            {
                return(parser.Yield(_ => ParseResult.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 IParserCursor <TSource> )
            {
                // Profiling has shown this to be exponentially faster in next.Exactly(largeN) queries.
                return(parser.Yield <TSource, TResult, IEnumerable <TResult> >(
                           "Exactly",
                           source =>
                {
                    var list = source.Take(count).Cast <TResult>().ToList().AsReadOnly();

                    if (list.Count == count)
                    {
                        return ParseResult.Return((IEnumerable <TResult>)list, count);
                    }
                    else
                    {
                        return ParseResult.ReturnFailureMany <TResult>();
                    }
                }));
            }
            else
            {
                return(Enumerable.Repeat(parser, count).All());
            }
        }