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