/// <summary> /// Execute a parser and catch any unhandled exceptions which may be thrown by it. On /// receiving an exception, the input sequence is rewound to the location where Try started /// and options are provided to determine what actions to take. /// </summary> /// <typeparam name="TOutput"></typeparam> /// <param name="parser"></param> /// <param name="examine"></param> /// <param name="bubble"></param> /// <returns></returns> public static IMultiParser <TInput, TOutput> Try <TOutput>(IMultiParser <TInput, TOutput> parser, Action <Exception>?examine = null, bool bubble = false) => new Function <TInput, TOutput> .MultiParser(args => { var cp = args.Input.Checkpoint(); try { return(parser.Parse(args.State)); } catch (ControlFlowException) { // These exceptions are used within the library for non-local control flow, and // should not be caught or modified here. throw; } catch (Exception e) { cp.Rewind(); examine?.Invoke(e); if (bubble) { throw; } return(new MultiResult <TOutput>(parser, cp.Location, cp, Array.Empty <IResultAlternative <TOutput> >(), data: new[] { e })); } }, "TRY {child}", new[] { parser });
/// <summary> /// Attempts a parse but does not consume any input. Instead it returns a boolean true if /// the parser would succeed or false otherwise. /// </summary> /// <typeparam name="TInput"></typeparam> /// <param name="parser"></param> /// <param name="input"></param> /// <returns></returns> public static bool CanMatch <TInput>(this IMultiParser <TInput> parser, ISequence <TInput> input) { var state = new ParseState <TInput>(input, Defaults.LogMethod); var result = parser.Parse(state); result.StartCheckpoint.Rewind(); return(result.Success); }
/// <summary> /// Expect the IMultiResult to contain exactly 1 alternative, and select that to continue. /// </summary> /// <typeparam name="TInput"></typeparam> /// <typeparam name="TOutput"></typeparam> /// <param name="multiParser"></param> /// <returns></returns> public static IParser <TInput, TOutput> Single <TInput, TOutput>(this IMultiParser <TInput, TOutput> multiParser) => multiParser.Select(args => { if (args.Result.Results.Count == 1) { return(args.Success(args.Result.Results[0])); } return(args.Failure()); });
/// <summary> /// Returns the first successful alternative which matches a predicate to continue the /// parse with. /// </summary> /// <typeparam name="TInput"></typeparam> /// <typeparam name="TOutput"></typeparam> /// <param name="multiParser"></param> /// <param name="predicate"></param> /// <returns></returns> public static IParser <TInput, TOutput> First <TInput, TOutput>(this IMultiParser <TInput, TOutput> multiParser, Func <IResultAlternative <TOutput>, bool> predicate) { Assert.ArgumentNotNull(predicate, nameof(predicate)); return(multiParser.Select(args => { var selected = args.Result.Results.FirstOrDefault(predicate); return selected != null ? args.Success(selected) : args.Failure(); })); }
/// <summary> /// Select the result alternative which consumed the most amount of input and use that to /// continue the parse. If there are no alternatives, returns failure. If there are ties, /// the first is selected. /// </summary> /// <typeparam name="TInput"></typeparam> /// <typeparam name="TOutput"></typeparam> /// <param name="multiParser"></param> /// <returns></returns> public static IParser <TInput, TOutput> Longest <TInput, TOutput>(this IMultiParser <TInput, TOutput> multiParser) => multiParser.Select(args => { var longest = args.Result.Results .Where(r => r.Success) .OrderByDescending(r => r.Consumed) .FirstOrDefault(); return(longest != null ? args.Success(longest) : args.Failure()); });
/// <summary> /// Convenience method for parsers which act on character sequences. Attempts a parse but /// does not consume any input. Returns true if the parse would succeed, false otherwise. /// </summary> /// <param name="parser"></param> /// <param name="input"></param> /// <param name="options"></param> /// <returns></returns> public static bool CanMatch(this IMultiParser <char> parser, string input, StringCharacterSequence.Options options = default) { // Don't need to .Checkpoint()/.Rewind() because the sequence is private and we don't // reuse it var sequence = new StringCharacterSequence(input, options); var state = new ParseState <char>(sequence, Defaults.LogMethod); var result = parser.Parse(state); return(result.Success); }
public SingleReplaceResult SetParser(IParser parser) { var previous = _value; if (parser is IMultiParser <TInput, TOutput> typed) { _value = typed; } return(new SingleReplaceResult(this, previous, _value)); }
public MultiParser(IMultiParser <TInput, TMiddle> inner, Func <IParser <TInput, TMiddle>, IMultiParser <TInput, TOutput> > getParser, string name = "") { Assert.ArgumentNotNull(inner, nameof(inner)); Assert.ArgumentNotNull(getParser, nameof(getParser)); _inner = inner; _left = new LeftValue(name); _right = getParser(_left); _getParser = getParser; Name = name; }
/// <summary> /// Creates a new contextual data frame to store data if the data store supports frames. /// Execute the inner parser. When the inner parser concludes, pop the data frame off the /// data store. /// </summary> /// <typeparam name="TOutput"></typeparam> /// <typeparam name="TData"></typeparam> /// <param name="inner"></param> /// <param name="values"></param> /// <returns></returns> public static IMultiParser <TInput, TOutput> DataContext <TOutput, TData>(IMultiParser <TInput, TOutput> inner, Dictionary <string, TData> values) => Context(inner, state => { (state.Data as CascadingKeyValueStore)?.PushFrame(); foreach (var value in values) { state.Data.Set(value.Key, value.Value); } }, state => (state.Data as CascadingKeyValueStore)?.PopFrame() );
public static IMultiParser <TInput, TOutput> Context <TOutput>(IMultiParser <TInput, TOutput> parser, Action <IParseState <TInput> > setup, Action <IParseState <TInput> > cleanup) => new Function <TInput, TOutput> .MultiParser(args => { var state = args.State; try { setup(state); return(parser.Parse(state)); } finally { cleanup(state); } }, string.Empty, new[] { parser });
/// <summary> /// Parse a string using the given character multiparser. /// </summary> /// <typeparam name="TOutput"></typeparam> /// <param name="p"></param> /// <param name="s"></param> /// <returns></returns> public static IMultiResult <TOutput> Parse <TOutput>(this IMultiParser <char, TOutput> p, string s) => p.Parse(new ParseState <char>(new StringCharacterSequence(s, default), Defaults.LogMethod));
/// <summary> /// Transform one multi result into another multi result. Allows modifying the result /// values and all result metadata. /// </summary> /// <typeparam name="TMiddle"></typeparam> /// <typeparam name="TOutput"></typeparam> /// <param name="parser"></param> /// <param name="transform"></param> /// <returns></returns> public static IMultiParser <TInput, TOutput> TransformResultMulti <TMiddle, TOutput>(IMultiParser <TInput, TMiddle> parser, Func <Transform <TInput, TMiddle, TOutput> .MultiArguments, IMultiResult <TOutput> > transform) => new Transform <TInput, TMiddle, TOutput> .MultiParser(parser, transform);
/// <summary> /// Push a recursive data frame before executing the given parser, and then pop the data /// frame when the parser completes. /// </summary> /// <typeparam name="TInput"></typeparam> /// <typeparam name="TOutput"></typeparam> /// <typeparam name="TData"></typeparam> /// <param name="p"></param> /// <param name="name"></param> /// <param name="value"></param> /// <returns></returns> public static IMultiParser <TInput, TOutput> WithDataContext <TInput, TOutput, TData>(this IMultiParser <TInput, TOutput> p, string name, TData value) where TData : notnull => ParserMethods <TInput> .DataContext(p, name, value);
/// <summary> /// Push a recursive data frame before executing the given parser, and then pop the data /// frame when the parser completes. /// </summary> /// <typeparam name="TInput"></typeparam> /// <typeparam name="TOutput"></typeparam> /// <param name="p"></param> /// <returns></returns> public static IMultiParser <TInput, TOutput> WithDataContext <TInput, TOutput>(this IMultiParser <TInput, TOutput> p) => ParserMethods <TInput> .DataContext(p);
/// <summary> /// Transforms the output value of the parser. /// </summary> /// <typeparam name="TMiddle"></typeparam> /// <typeparam name="TOutput"></typeparam> /// <param name="parser"></param> /// <param name="transform"></param> /// <returns></returns> public static IMultiParser <TInput, TOutput> Transform <TMiddle, TOutput>(IMultiParser <TInput, TMiddle> parser, Func <TMiddle, TOutput> transform) => TransformResultMulti <TMiddle, TOutput>(parser, args => args.Result.Transform(transform));
/// <summary> /// Creates a new contextual data frame to store data if the data store supports frames. /// Execute the inner parser. When the inner parser concludes, pop the data frame off the /// data store. /// </summary> /// <typeparam name="TOutput"></typeparam> /// <param name="inner"></param> /// <returns></returns> public static IMultiParser <TInput, TOutput> DataContext <TOutput>(IMultiParser <TInput, TOutput> inner) => Context(inner, state => (state.Data as CascadingKeyValueStore)?.PushFrame(), state => (state.Data as CascadingKeyValueStore)?.PopFrame() );
/// <summary> /// Convenience method to invoke a parser which does not return a value. Creates the /// necessary ParseState. /// </summary> /// <typeparam name="TInput"></typeparam> /// <param name="parser"></param> /// <param name="input"></param> /// <param name="log"></param> /// <returns></returns> public static IMultiResult Parse <TInput>(this IMultiParser <TInput> parser, ISequence <TInput> input, Action <string>?log = null) => parser.Parse(new ParseState <TInput>(input, log ?? Defaults.LogMethod));
/// <summary> /// Invoke a special callback to attempt to select a single alternative and turn it into /// an IResult. /// </summary> /// <typeparam name="TInput"></typeparam> /// <typeparam name="TOutput"></typeparam> /// <param name="multiparser"></param> /// <param name="select"></param> /// <returns></returns> public static IParser <TInput, TOutput> Select <TInput, TOutput>(this IMultiParser <TInput, TOutput> multiparser, Func <Select <TInput, TOutput> .Arguments, IOption <IResultAlternative <TOutput> > > select) => new Select <TInput, TOutput> .Parser(multiparser, select);
/// <summary> /// Make this parser replaceable. /// </summary> /// <typeparam name="TInput"></typeparam> /// <typeparam name="TOutput"></typeparam> /// <param name="p"></param> /// <returns></returns> public static IMultiParser <TInput, TOutput> Replaceable <TInput, TOutput>(this IMultiParser <TInput, TOutput> p) => ParserMethods <TInput> .Replaceable(p);
/// <summary> /// Make this parser replaceable. Gives the parser a name so that it can be easily found /// and replaced. /// </summary> /// <typeparam name="TInput"></typeparam> /// <typeparam name="TOutput"></typeparam> /// <param name="p"></param> /// <param name="name"></param> /// <returns></returns> public static IMultiParser <TInput, TOutput> Replaceable <TInput, TOutput>(this IMultiParser <TInput, TOutput> p, string name) => ParserMethods <TInput> .Replaceable(p).Named(name);
/// <summary> /// Selects the first successful alternative to continue the parse with. /// </summary> /// <typeparam name="TInput"></typeparam> /// <typeparam name="TOutput"></typeparam> /// <param name="multiParser"></param> /// <returns></returns> public static IParser <TInput, TOutput> First <TInput, TOutput>(this IMultiParser <TInput, TOutput> multiParser) => First(multiParser, r => r.Success);
/// <summary> /// Continue the parse with each alternative separately. /// </summary> /// <typeparam name="TInput"></typeparam> /// <typeparam name="TMiddle"></typeparam> /// <typeparam name="TOutput"></typeparam> /// <param name="multiParser"></param> /// <param name="getParser"></param> /// <returns></returns> public static IMultiParser <TInput, TOutput> ContinueWith <TInput, TMiddle, TOutput>(this IMultiParser <TInput, TMiddle> multiParser, Func <IParser <TInput, TMiddle>, IMultiParser <TInput, TOutput> > getParser) => new ContinueWith <TInput, TMiddle, TOutput> .MultiParser(multiParser, getParser);
public MultiParser(IMultiParser <TInput, TOutput> defaultValue, string name = "") { Assert.ArgumentNotNull(defaultValue, nameof(defaultValue)); _value = defaultValue; Name = name; }
/// <summary> /// Continue the parse with all the given parsers. /// </summary> /// <typeparam name="TInput"></typeparam> /// <typeparam name="TMiddle"></typeparam> /// <typeparam name="TOutput"></typeparam> /// <param name="parser"></param> /// <param name="getParsers"></param> /// <returns></returns> public static IMultiParser <TInput, TOutput> ContinueWithEach <TInput, TMiddle, TOutput>(this IMultiParser <TInput, TMiddle> parser, Func <IParser <TInput, TMiddle>, IEnumerable <IParser <TInput, TOutput> > > getParsers) => ContinueWith(parser, left => new EachParser <TInput, TOutput>(getParsers(left).ToArray()));
/// <summary> /// Transform the values of all result alternatives. /// </summary> /// <typeparam name="TInput"></typeparam> /// <typeparam name="TMiddle"></typeparam> /// <typeparam name="TOutput"></typeparam> /// <param name="multiParser"></param> /// <param name="transform"></param> /// <returns></returns> public static IMultiParser <TInput, TOutput> Transform <TInput, TMiddle, TOutput>(this IMultiParser <TInput, TMiddle> multiParser, Func <TMiddle, TOutput> transform) => ParserMethods <TInput> .Transform(multiParser, transform);
/// <summary> /// Transform the values of all result alternatives. /// </summary> /// <typeparam name="TInput"></typeparam> /// <typeparam name="TMiddle"></typeparam> /// <typeparam name="TOutput"></typeparam> /// <param name="multiParser"></param> /// <param name="transform"></param> /// <returns></returns> public static IMultiParser <TInput, TOutput> Transform <TInput, TMiddle, TOutput>(this IMultiParser <TInput, TMiddle> multiParser, Func <Transform <TInput, TMiddle, TOutput> .MultiArguments, IMultiResult <TOutput> > transform) => ParserMethods <TInput> .TransformResultMulti(multiParser, transform);
/// <summary> /// Creates a new contextual data frame to store data if the data store supports frames. /// Execute the inner parser. When the inner parser concludes, pop the data frame off the /// data store. /// </summary> /// <typeparam name="TOutput"></typeparam> /// <typeparam name="TData"></typeparam> /// <param name="inner"></param> /// <param name="name"></param> /// <param name="value"></param> /// <returns></returns> public static IMultiParser <TInput, TOutput> DataContext <TOutput, TData>(IMultiParser <TInput, TOutput> inner, string name, TData value) where TData : notnull => DataContext(inner, new Dictionary <string, object> {
/// <summary> /// Push a recursive data frame before executing the given parser, and then pop the data /// frame when the parser completes. /// </summary> /// <typeparam name="TInput"></typeparam> /// <typeparam name="TOutput"></typeparam> /// <typeparam name="TData"></typeparam> /// <param name="p"></param> /// <param name="values"></param> /// <returns></returns> public static IMultiParser <TInput, TOutput> WithDataContext <TInput, TOutput, TData>(this IMultiParser <TInput, TOutput> p, Dictionary <string, TData> values) => ParserMethods <TInput> .DataContext(p, values);
/// <summary> /// Cache result values of this parser so on the next call to Parse from the same location /// an existing result value can be used. /// </summary> /// <typeparam name="TInput"></typeparam> /// <typeparam name="TOutput"></typeparam> /// <param name="p"></param> /// <returns></returns> public static IMultiParser <TInput, TOutput> Cache <TInput, TOutput>(this IMultiParser <TInput, TOutput> p) => ParserMethods <TInput> .Cache(p);
/// <summary> /// Serves as a placeholder in the parser tree where an in-place replacement can be made. /// </summary> /// <typeparam name="TOutput"></typeparam> /// <param name="defaultParser"></param> /// <returns></returns> public static IMultiParser <TInput, TOutput> Replaceable <TOutput>(IMultiParser <TInput, TOutput> defaultParser) => new Replaceable <TInput, TOutput> .MultiParser(defaultParser ?? new FailParser <TInput, TOutput>());