Beispiel #1
0
        /// <summary>
        /// Constructs a parser that will fail if the given parser succeeds,
        /// and will succeed if the given parser fails. In any case, it won't
        /// consume any input. It's like a negative look-ahead in a regular expression.
        /// </summary>
        /// <typeparam name="T">The result type of the given parser.</typeparam>
        /// <typeparam name="TKind">The kind of token being parsed.</typeparam>
        /// <param name="parser">The parser to wrap</param>
        /// <returns>A parser that is the negation of the given parser.</returns>
        public static TokenListParser <TKind, Unit> Not <TKind, T>(TokenListParser <TKind, T> parser)
        {
            if (parser == null)
            {
                throw new ArgumentNullException(nameof(parser));
            }

            return(input =>
            {
                var result = parser(input);

                if (result.HasValue)
                {
                    // This is usually a success case for Not(), so the allocations here are a bit of a pity.

                    var current = input.ConsumeToken();
                    var last = result.Remainder.ConsumeToken();
                    if (current.HasValue)
                    {
                        var span = last.HasValue ?
                                   current.Value.Span.Source.Substring(current.Value.Position.Absolute, last.Value.Position.Absolute - current.Value.Position.Absolute) :
                                   current.Value.Span.Source.Substring(current.Value.Position.Absolute);
                        return TokenListParserResult.Empty <TKind, Unit>(input, $"unexpected successful parsing of {Presentation.FormatLiteral(Friendly.Clip(span, 12))}");
                    }

                    return TokenListParserResult.Empty <TKind, Unit>(input, "unexpected successful parsing");
                }

                return TokenListParserResult.Value(Unit.Value, input, input);
            });
        }
        /// <summary>
        /// Construct a parser that returns <paramref name="name"/> as its "expectation" if <paramref name="parser"/> fails.
        /// </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>
        /// <param name="name">The name given to <paramref name="parser"/>.</param>
        /// <returns>The resulting parser.</returns>
        public static TokenListParser <TKind, T> Named <TKind, T>(this TokenListParser <TKind, T> parser, string name)
        {
            if (parser == null)
            {
                throw new ArgumentNullException(nameof(parser));
            }
            if (name == null)
            {
                throw new ArgumentNullException(nameof(name));
            }

            return(input =>
            {
                var result = parser(input);
                if (result.HasValue || result.Remainder != input)
                {
                    return result;
                }

                // result.IsSubTokenError?
                if (result.ErrorPosition.HasValue)
                {
                    return TokenListParserResult.Empty <TKind, T>(result.Remainder, result.ErrorPosition, result.FormatErrorMessageFragment());
                }

                return TokenListParserResult.Empty <TKind, T>(result.Remainder, new[] { name });
            });
        }
        /// <summary>
        /// Construct a parser that evaluates the result of a previous parser and fails if <paramref name="predicate"/> returns false
        /// for the result.
        /// </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>
        /// <param name="predicate">The predicate to apply.</param>
        /// <returns>The resulting parser.</returns>
        public static TokenListParser <TKind, T> Where <TKind, T>(this TokenListParser <TKind, T> parser, Func <T, bool> predicate)
        {
            if (parser == null)
            {
                throw new ArgumentNullException(nameof(parser));
            }
            if (predicate == null)
            {
                throw new ArgumentNullException(nameof(predicate));
            }

            return(input =>
            {
                var rt = parser(input);
                if (!rt.HasValue)
                {
                    return rt;
                }

                if (predicate(rt.Value))
                {
                    return rt;
                }

                return TokenListParserResult.Empty <TKind, T>(input, "unsatisfied condition");
            });
        }
        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 token of the kind <typeparamref name="TKind"/>.
        /// </summary>
        /// <typeparam name="TKind">The type of the token being matched.</typeparam>
        /// <param name="kind">The kind of token to match.</param>
        /// <returns>The matched token.</returns>
        public static TokenListParser <TKind, Token <TKind> > EqualTo <TKind>(TKind kind)
        {
            var expectations = new[] { Presentation.FormatExpectation(kind) };

            return(input =>
            {
                var next = input.ConsumeToken();
                if (!next.HasValue || !next.Value.Kind.Equals(kind))
                {
                    return TokenListParserResult.Empty <TKind, Token <TKind> >(input, expectations);
                }

                return next;
            });
        }
Beispiel #6
0
        public void MessageWithExpectedTokensUsesTokenPresentation()
        {
            // Composing a complex parser which does not fit a LALR(1) grammar, one might need
            // to have multiple look-ahead tokens. While it is possible to compose parsers with back-tracking,
            // manual generated parsers are some times easier to construct. These parsers would like
            // to report expectations using tokens, but still benefit from the annotations put on
            // the tokens, to generated nicely formatted error messages. The following construct
            // shows how to generate an empty result, which indicates which tokens are expected.
            var emptyParseResult = TokenListParserResult.Empty <ArithmeticExpressionToken, string>(
                new TokenList <ArithmeticExpressionToken>(),
                new [] { ArithmeticExpressionToken.Times, ArithmeticExpressionToken.Zero });

            // Empty result represent expectations using nice string representation taken from
            // annotations of enum values of tokens
            Assert.Equal(2, emptyParseResult.Expectations !.Length);
            Assert.Equal("`*`", emptyParseResult.Expectations ![0]);
Beispiel #7
0
        public static TokenListParser <TKind, Token <TKind> > NotEqualTo <TKind>(this IEnumerable <TKind> kinds)
        {
            var expectations = new[]
            {
                kinds.Select(k => k?.ToString() ?? string.Empty)
                .Where(k => k.Length > 0)
                .Join(", ")
            };

            return(input =>
            {
                var next = input.ConsumeToken();
                if (!next.HasValue || kinds.Any(k => next.Value.Kind != null && next.Value.Kind.Equals(k)))
                {
                    return TokenListParserResult.Empty <TKind, Token <TKind> >(input, expectations);
                }

                return next;
            });
        }
        /// <summary>
        /// Construct a parser that fails with error message <paramref name="errorMessage"/> when <paramref name="parser"/> fails.
        /// </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>
        /// <param name="errorMessage">The error message.</param>
        /// <returns>The resulting parser.</returns>
        public static TokenListParser <TKind, T> Message <TKind, T>(this TokenListParser <TKind, T> parser, string errorMessage)
        {
            if (parser == null)
            {
                throw new ArgumentNullException(nameof(parser));
            }
            if (errorMessage == null)
            {
                throw new ArgumentNullException(nameof(errorMessage));
            }

            return(input =>
            {
                var result = parser(input);
                if (result.HasValue)
                {
                    return result;
                }

                return TokenListParserResult.Empty <TKind, T>(result.Remainder, result.ErrorPosition, errorMessage);
            });
        }
Beispiel #9
0
        private static TokenListParser <TKind, Token <TKind> > Matching <TKind>(Func <TKind, bool> predicate, string[] expectations)
        {
            if (predicate == null)
            {
                throw new ArgumentNullException(nameof(predicate));
            }
            if (expectations == null)
            {
                throw new ArgumentNullException(nameof(expectations));
            }

            return(input =>
            {
                var next = input.ConsumeToken();
                if (!next.HasValue || !predicate(next.Value.Kind))
                {
                    return TokenListParserResult.Empty <TKind, Token <TKind> >(input, expectations);
                }

                return next;
            });
        }
        /// <summary>
        /// Construct a parser that succeeds only if the source is at the end of input.
        /// </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>
        public static TokenListParser <TKind, T> AtEnd <TKind, T>(this TokenListParser <TKind, T> parser)
        {
            if (parser == null)
            {
                throw new ArgumentNullException(nameof(parser));
            }

            return(input =>
            {
                var result = parser(input);
                if (!result.HasValue)
                {
                    return result;
                }

                if (result.Remainder.IsAtEnd)
                {
                    return result;
                }

                return TokenListParserResult.Empty <TKind, T>(result.Remainder);
            });
        }