/// <summary> /// Refer to another parser indirectly. This allows circular compile-time dependency between parsers. /// </summary> /// <typeparam name="TToken"></typeparam> /// <typeparam name="T"></typeparam> /// <param name="reference"></param> /// <returns></returns> public static Parser <TToken, T> Ref <TToken, T>(Func <Parser <TToken, T> > reference) { if (reference == null) { throw new ArgumentNullException(nameof(reference)); } Parser <TToken, T> p = null; return((i, n) => { if (p == null) { p = reference(); } if (i.Memos.ContainsKey(p)) { throw new ParsingException(i.Memos[p].ToString()); } i.Memos[p] = ParserResult <TToken, T> .Failure(i, new string[0], "Left recursion in the grammar." ); var result = p(i, n); i.Memos[p] = result; return result; }); }
internal static Parser <char, T> TryConvert <T>(string str, Func <string, T> converter) { return((i, n) => { try { return ParserResult <char, T> .Success(i, converter(str)); } catch (FormatException) { return ParserResult <char, T> .Failure(i, $"Failed to parse {str}"); } }); }
public ParserResult <TToken, U> IfSuccess <U>(Func <ParserResult <TToken, TValue>, ParserResult <TToken, U> > next) { if (next == null) { throw new ArgumentNullException(nameof(next)); } if (this.IsSuccess) { return(next(this)); } return(ParserResult <TToken, U> .Failure(this.Rest, this.Expected, this.Description)); }
public static Parser <TToken, T> End <TToken, T>(this Parser <TToken, T> parser) { if (parser == null) { throw new ArgumentNullException(nameof(parser)); } return((i, n) => parser(i, n).IfSuccess(s => s.Rest.AtEnd ? s : ParserResult <TToken, T> .Failure( s.Rest, new[] { "end of input" }, string.Format("unexpected '{0}'", s.Rest.GetCurrent()) ) )); }
public static Parser <ScriptToken, ScriptToken> ScriptToken(Func <int, bool> predicate, string expected) { return(i => { if (i.AtEnd) { return ParserResult <ScriptToken, ScriptToken> .Failure(i, new[] { expected }, "Unexpected end of input"); } if (predicate(i.GetCurrent().Tag)) { return ParserResult <ScriptToken, ScriptToken> .Success(i.Advance(), i.GetCurrent()); } return ParserResult <ScriptToken, ScriptToken> .Failure(i, $"Unexpected {i.GetCurrent()}"); }); }
public static Parser <TToken, IEnumerable <T> > Repeat <TToken, T>( this Parser <TToken, T> parser, int minimumCount, int maximumCount ) { if (parser == null) { throw new ArgumentNullException(nameof(parser)); } return((i, net) => { var remainder = i; var result = new List <T>(); for (var n = 0; n < maximumCount; ++n) { var r = parser(remainder, net); if (!r.IsSuccess && n < minimumCount) { var what = r.Rest.AtEnd ? "end of input" : r.Rest.GetCurrent().ToString(); var msg = $"Unexpected '{what}'"; var exp = minimumCount == maximumCount ? $"'{string.Join(", ", r.Expected)}' {minimumCount} times, but found {n}" : $"'{string.Join(", ", r.Expected)}' between {minimumCount} and {maximumCount} times, but found {n}"; return ParserResult <TToken, IEnumerable <T> > .Failure(i, new[] { exp }, msg); } if (!ReferenceEquals(remainder, r.Rest)) { result.Add(r.Value); } remainder = r.Rest; } return ParserResult <TToken, IEnumerable <T> > .Success(remainder, result); }); }
/// <summary> /// Succeed if the parsed value matches predicate. /// </summary> /// <typeparam name="TToken"></typeparam> /// <typeparam name="T"></typeparam> /// <param name="parser"></param> /// <param name="predicate"></param> /// <returns></returns> public static Parser <TToken, T> Where <TToken, T>(this Parser <TToken, T> parser, Func <T, bool> predicate) { if (parser == null) { throw new ArgumentNullException(nameof(parser)); } if (predicate == null) { throw new ArgumentNullException(nameof(predicate)); } return((i, n) => parser(i, n).IfSuccess(s => predicate(s.Value) ? s : ParserResult <TToken, T> .Failure(i, new string[0], string.Format("Unexpected {0}.", s.Value) ) )); }
public static ParserResult <TToken, T> DetermineBestError <TToken, T>( ParserResult <TToken, T> firstF, ParserResult <TToken, T> secondF ) { if (secondF.Rest.Position > firstF.Rest.Position) { return(secondF); } if (secondF.Rest.Position == firstF.Rest.Position) { return(ParserResult <TToken, T> .Failure( firstF.Rest, firstF.Expected.Union(secondF.Expected), firstF.Description )); } return(firstF); }
/// <summary> /// Attempt parsing only if the <paramref name="except"/> parser fails. /// </summary> /// <typeparam name="TToken"></typeparam> /// <typeparam name="T"></typeparam> /// <typeparam name="U"></typeparam> /// <param name="parser"></param> /// <param name="except"></param> /// <returns></returns> public static Parser <TToken, T> Except <TToken, T, U>(this Parser <TToken, T> parser, Parser <TToken, U> except) { if (parser == null) { throw new ArgumentNullException(nameof(parser)); } if (except == null) { throw new ArgumentNullException(nameof(except)); } // Could be more like: except.Then(s => s.Fail("..")).XOr(parser) return((i, n) => { var r = except(i, n); if (r.IsSuccess) { return ParserResult <TToken, T> .Failure(i, new[] { "other than the excepted input" }, "Excepted parser succeeded."); } return parser(i, n); }); }
public static Parser <char, char> Char(Func <char, bool> predicate, string expected) { if (predicate == null) { throw new ArgumentNullException(nameof(predicate)); } return((i, n) => { if (i.AtEnd) { return ParserResult <char, char> .Failure(i, new[] { expected }, "Unexpected end of input"); } if (predicate(i.GetCurrent())) { return ParserResult <char, char> .Success(i.Advance(), i.GetCurrent()); } return ParserResult <char, char> .Failure(i, new[] { expected }, $"Unexpected '{i.GetCurrent()}'"); }); }
internal static ParserResult <ScriptToken, T> TryParse <T>(this Parser <ScriptToken, T> parser, Network network, Script input) { if (parser == null) { throw new System.ArgumentNullException(nameof(parser)); } if (input == null) { throw new System.ArgumentNullException(nameof(input)); } try { return(parser(new ScriptInput(input), network)); } // Catching exception here is bit ugly, but converting `Script` to `ScriptToken` is itself unsafe // so this is good for assuring purity of this method. catch (ParsingException e) { return(ParserResult <ScriptToken, T> .Failure(new ScriptInput(new ScriptToken[] { }), e.Message)); } }