/// <summary>
        /// Construct a parser that applies <paramref name="first"/>, provides the value to <paramref name="second"/> and returns the result.
        /// </summary>
        /// <typeparam name="TKind">The kind of the tokens being parsed.</typeparam>
        /// <typeparam name="T">The type of value being parsed.</typeparam>
        /// <typeparam name="U">The type of the resulting value.</typeparam>
        /// <param name="first">The first parser.</param>
        /// <param name="second">The second parser.</param>
        /// <returns>The resulting parser.</returns>
        public static TokenListParser <TKind, U> Then <TKind, T, U>(this TokenListParser <TKind, T> first, Func <T, TokenListParser <TKind, U> > second)
        {
            if (first == null)
            {
                throw new ArgumentNullException(nameof(first));
            }
            if (second == null)
            {
                throw new ArgumentNullException(nameof(second));
            }

            return(input =>
            {
                var rt = first(input);
                if (!rt.HasValue)
                {
                    return TokenListParserResult.CastEmpty <TKind, T, U>(rt);
                }

                var ru = second(rt.Value)(rt.Remainder);
                if (!ru.HasValue)
                {
                    return ru;
                }

                return TokenListParserResult.Value(ru.Value, input, ru.Remainder);
            });
        }
        /// <summary>
        /// Apply the character parser <paramref name="valueParser"/> to the span represented by the parsed token.
        /// </summary>
        /// <typeparam name="TKind">The kind of the tokens being parsed.</typeparam>
        /// <typeparam name="U">The type of the resulting value.</typeparam>
        /// <param name="parser">The parser.</param>
        /// <param name="valueParser">A function that determines which character parser to apply.</param>
        /// <returns>A parser that returns the result of parsing the token value.</returns>
        public static TokenListParser <TKind, U> Apply <TKind, U>(this TokenListParser <TKind, Token <TKind> > parser, Func <Token <TKind>, TextParser <U> > valueParser)
        {
            if (parser == null)
            {
                throw new ArgumentNullException(nameof(parser));
            }
            if (valueParser == null)
            {
                throw new ArgumentNullException(nameof(valueParser));
            }
            return(input =>
            {
                var rt = parser(input);
                if (!rt.HasValue)
                {
                    return TokenListParserResult.CastEmpty <TKind, Token <TKind>, U>(rt);
                }

                var uParser = valueParser(rt.Value);
                var uResult = uParser.AtEnd()(rt.Value.Span);
                if (!uResult.HasValue)
                {
                    var message = $"invalid {Presentation.FormatExpectation(rt.Value.Kind)}, {uResult.FormatErrorMessageFragment()}";
                    return new TokenListParserResult <TKind, U>(input, uResult.Remainder.Position, message, null, uResult.Backtrack);
                }

                return TokenListParserResult.Value(uResult.Value, rt.Location, rt.Remainder);
            });
        }
        /// <summary>
        /// Construct a parser that matches <paramref name="parser"/> zero or more times.
        /// </summary>
        /// <typeparam name="TKind">The kind of the tokens being parsed.</typeparam>
        /// <typeparam name="T">The type of value being parsed.</typeparam>
        /// <param name="parser">The parser.</param>
        /// <returns>The resulting parser.</returns>
        /// <remarks>Many will fail if any item partially matches this. To modify this behavior use <see cref="Try{TKind,T}(TokenListParser{TKind,T})"/>.</remarks>
        public static TokenListParser <TKind, T[]> Many <TKind, T>(this TokenListParser <TKind, T> parser)
        {
            if (parser == null)
            {
                throw new ArgumentNullException(nameof(parser));
            }

            return(input =>
            {
                var result = new List <T>();
                var @from = input;
                var r = parser(input);
                while (r.HasValue)
                {
                    if (@from == r.Remainder) // Broken parser, not a failed parsing.
                    {
                        throw new ParseException($"Many() cannot be applied to zero-width parsers; value {r.Value} at position {r.Location.Position}.");
                    }

                    result.Add(r.Value);
                    @from = r.Remainder;
                    r = parser(r.Remainder);
                }

                if (!r.Backtrack && r.IsPartial(@from))
                {
                    return TokenListParserResult.CastEmpty <TKind, T, T[]>(r);
                }

                return TokenListParserResult.Value(result.ToArray(), input, r.Remainder);
            });
        }
        public static TokenListParser <TTokenKind, U> SelectCatch <TTokenKind, T, U>(this TokenListParser <TTokenKind, T> parser, Func <T, U> trySelector, string errorMessage)
        {
            if (parser == null)
            {
                throw new ArgumentNullException(nameof(parser));
            }
            if (trySelector == null)
            {
                throw new ArgumentNullException(nameof(trySelector));
            }
            if (errorMessage == null)
            {
                throw new ArgumentNullException(nameof(errorMessage));
            }

            return(input =>
            {
                var t = parser(input);
                if (!t.HasValue)
                {
                    return TokenListParserResult.CastEmpty <TTokenKind, T, U>(t);
                }

                try
                {
                    var u = trySelector(t.Value);
                    return TokenListParserResult.Value(u, input, t.Remainder);
                }
                catch
                {
                    return TokenListParserResult.Empty <TTokenKind, U>(input, errorMessage);
                }
            });
        }
Beispiel #5
0
        /// <summary>
        /// Parse a sequence of operands connected by left-associative operators.
        /// </summary>
        /// <typeparam name="T">The type being parsed.</typeparam>
        /// <typeparam name="TOperator">The type of the operator.</typeparam>
        /// <typeparam name="TKind">The kind of token being parsed.</typeparam>
        /// <param name="operator">A parser matching operators.</param>
        /// <param name="operand">A parser matching operands.</param>
        /// <param name="apply">A function combining an operator and two operands into the result.</param>
        /// <returns>The result of calling <paramref name="apply"/> successively on pairs of operands.</returns>
        public static TokenListParser <TKind, T> Chain <TKind, T, TOperator>(
            TokenListParser <TKind, TOperator> @operator,
            TokenListParser <TKind, T> operand,
            Func <TOperator, T, T, T> apply)
        {
            if (@operator == null)
            {
                throw new ArgumentNullException(nameof(@operator));
            }
            if (operand == null)
            {
                throw new ArgumentNullException(nameof(operand));
            }
            if (apply == null)
            {
                throw new ArgumentNullException(nameof(apply));
            }

            return(input =>
            {
                var parseResult = operand(input);
                if (!parseResult.HasValue)
                {
                    return parseResult;
                }

                var result = parseResult.Value;

                var operatorResult = @operator(parseResult.Remainder);
                while (operatorResult.HasValue || operatorResult.IsPartial(parseResult.Remainder))
                {
                    // If operator read any input, but failed to read complete input, we return error
                    if (!operatorResult.HasValue)
                    {
                        return TokenListParserResult.CastEmpty <TKind, TOperator, T>(operatorResult);
                    }

                    parseResult = operand(operatorResult.Remainder);

                    if (!parseResult.HasValue)
                    {
                        return TokenListParserResult.CastEmpty <TKind, T, T>(parseResult);
                    }

                    result = apply(operatorResult.Value, result, parseResult.Value);
                    operatorResult = @operator(parseResult.Remainder);
                }

                return TokenListParserResult.Value(result, input, parseResult.Remainder);
            });
        }
        public static TokenListParser <TKind, TResult> ChainModified <TKind, TResult, TOperator, TModifier>(
            TokenListParser <TKind, TOperator> @operator,
            TokenListParser <TKind, TResult> operand,
            TokenListParser <TKind, TModifier> modify,
            Func <TOperator, TResult, TResult, TModifier, TResult> apply)
        {
            if (@operator == null)
            {
                throw new ArgumentNullException(nameof(@operator));
            }
            if (operand == null)
            {
                throw new ArgumentNullException(nameof(operand));
            }
            if (modify == null)
            {
                throw new ArgumentNullException(nameof(modify));
            }
            if (apply == null)
            {
                throw new ArgumentNullException(nameof(apply));
            }

            return(input =>
            {
                var parseResult = operand(input);
                if (!parseResult.HasValue)
                {
                    return parseResult;
                }

                var result = parseResult.Value;
                var remainder = parseResult.Remainder;

                var operatorResult = @operator(remainder);
                while (operatorResult.HasValue || operatorResult.SubTokenErrorPosition.HasValue || remainder != operatorResult.Remainder)
                {
                    // If operator read any input, but failed to read complete input, we return error
                    if (!operatorResult.HasValue)
                    {
                        return TokenListParserResult.CastEmpty <TKind, TOperator, TResult>(operatorResult);
                    }

                    var operandResult = operand(operatorResult.Remainder);
                    remainder = operandResult.Remainder;

                    if (!operandResult.HasValue)
                    {
                        return operandResult;
                    }

                    var modifierResult = modify(remainder);
                    remainder = modifierResult.Remainder;

                    if (!modifierResult.HasValue)
                    {
                        return TokenListParserResult.CastEmpty <TKind, TModifier, TResult>(modifierResult);
                    }

                    result = apply(operatorResult.Value, result, operandResult.Value, modifierResult.Value);

                    operatorResult = @operator(remainder);
                }

                return TokenListParserResult.Value(result, input, remainder);
            });
        }