private static IParser <IReadOnlyList <T> > DoRepeat <T>(this IParser <T> parser, int atLeast, int?atMost) { return(Create(position => { List <T> values = new List <T>(capacity: atLeast); TextPosition remainder = position; int repeated = 0; while (true) { if (repeated >= atMost) { break; } IParseResult <T> result = parser.TryParse(remainder); if (!result.Success || result.NextPosition == remainder) { break; } values.Add(result.Value); remainder = result.NextPosition; repeated++; } return repeated >= atLeast ? ParseResult.Success <IReadOnlyList <T> >(values, remainder) : ParseResult.Failure <IReadOnlyList <T> >(position); })); }
/// <summary> /// Parses the specified string using the specified string comparison. /// </summary> public static IParser <string> String(string text, StringComparison comparison) { return(Create(position => { string inputText = position.Text; int inputIndex = position.Index; int textLength = text.Length; if (string.Compare(inputText, inputIndex, text, 0, textLength, comparison) == 0) { return ParseResult.Success(inputText.Substring(inputIndex, textLength), position.WithNextIndex(textLength)); } return ParseResult.Failure <string>(position); })); }
/// <summary> /// Parses a single character if the specified predicate returns true. /// </summary> public static IParser <char> Char(Func <char, bool> test) { return(Create(position => { if (!position.IsAtEnd()) { char current = position.GetCurrentChar(); if (test(current)) { return ParseResult.Success(current, position.WithNextIndex()); } } return ParseResult.Failure <char>(position); })); }
/// <summary> /// Succeeds if the specified regular expression pattern matches the text. /// </summary> /// <remarks>The regular expression pattern is automatically anchored at the beginning /// of the text, but not at the end of the text. The parsed value is the successful Match.</remarks> public static IParser <Match> Regex(string pattern, RegexOptions regexOptions = RegexOptions.None) { // turn off multiline mode for '^'; wrap pattern in non-capturing group to ensure ungrouped '|' works properly var regex = new Regex("(?-m:^)(?:" + pattern + ")", regexOptions); return(Create(position => { int inputIndex = position.Index; string inputText = position.Text; var match = regex.Match(inputText, inputIndex, inputText.Length - inputIndex); if (match.Success) { return ParseResult.Success(match, position.WithNextIndex(match.Length)); } return ParseResult.Failure <Match>(position); })); }
/// <summary> /// Succeeds with the specified value without advancing the text position. /// </summary> public static IParser <T> Success <T>(T value) => Create(position => ParseResult.Success(value, position));
/// <summary> /// Wraps the text position and length around a successfully parsed value. /// </summary> public static IParser <Positioned <T> > Positioned <T>(this IParser <T> parser) { return(Create(position => parser.TryParse(position) .MapSuccess(result => ParseResult.Success(new Positioned <T>(result.Value, position, result.NextPosition.Index - position.Index), result.NextPosition)))); }