/// <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 regex. /// </summary> /// <typeparam name="T">The result type of the given parser</typeparam> /// <param name="parser">The parser to wrap</param> /// <returns>A parser that is the opposite of the given parser.</returns> public static Parser <object> Not <T>(this Parser <T> parser) { if (parser == null) { throw new ArgumentNullException(nameof(parser)); } return(i => { var result = parser(i); if (result.WasSuccessful) { var msg = $"`{StringExtensions.Join(", ", result.Expectations)}' was not expected"; return Result.Failure <object>(i, msg, new string[0]); } return Result.Success <object>(null, i); }); }
/// <summary> /// /// </summary> /// <param name="parser"></param> /// <param name="minimumCount"></param> /// <param name="maximumCount"></param> /// <typeparam name="T"></typeparam> /// <returns></returns> /// <exception cref="ArgumentNullException"></exception> public static Parser <IEnumerable <T> > Repeat <T>(this Parser <T> parser, int minimumCount, int maximumCount) { if (parser == null) { throw new ArgumentNullException(nameof(parser)); } return(i => { var remainder = i; var result = new List <T>(); for (var n = 0; n < maximumCount; ++n) { var r = parser(remainder); if (!r.WasSuccessful && n < minimumCount) { var what = r.Remainder.AtEnd ? "end of input" : r.Remainder.Current.ToString(); var msg = $"Unexpected '{what}'"; var exp = $"'{StringExtensions.Join(", ", r.Expectations)}' between {minimumCount} and {maximumCount} times, but found {n}"; return Result.Failure <IEnumerable <T> >(i, msg, new[] { exp }); } if (!ReferenceEquals(remainder, r.Remainder)) { result.Add(r.Value); } remainder = r.Remainder; } return Result.Success <IEnumerable <T> >(result, remainder); }); }
/// <summary> /// Parses a single character except for those in the given parameters /// </summary> /// <param name="c"></param> /// <returns></returns> public static Parser <char> CharExcept(IEnumerable <char> c) { var chars = c as char[] ?? c.ToArray(); return(CharExcept(chars.Contains, StringExtensions.Join("|", chars))); }
/// <summary> /// Parses a single character except for those in c /// </summary> /// <param name="c"></param> /// <returns></returns> public static Parser <char> CharExcept(string c) { return(CharExcept(c.ToEnumerable().Contains, StringExtensions.Join("|", c.ToEnumerable()))); }
/// <summary> /// Parse a single character of any in c /// </summary> /// <param name="c"></param> /// <returns></returns> public static Parser <char> Chars(params char[] c) { return(Char(c.Contains, StringExtensions.Join("|", c))); }