superpower icon indicating copy to clipboard operation
superpower copied to clipboard

Make SelectCatch() from Sprache available in Superpower

Open ewoutkramer opened this issue 5 years ago • 3 comments

The Serilog parser has two extension methods SelectCatch(,,string error message) that does a select, but if that select throws, instead results in an error with the given message.

I think these are useful enough to introduce more generally in Superpower.

ewoutkramer avatar Mar 27 '19 08:03 ewoutkramer

Great suggestion; I wonder if (since in Superpower we've tried to create more composable building blocks), this could be something like:

p.Catch(e => "some message")

And maybe a generic one that restricts on the Exception type, if we can figure out how to make that sane to call, in the presence of other generic arguments required for parser?

It's also strange that Catch() wouldn't, in some sense, succeed and produce some default value 🤔 ... not sure if there is a better name for it, and/or whether there's some additional/similar parser that could be added for the catch -> default case?

nblumhardt avatar Mar 27 '19 22:03 nblumhardt

p.Catch(e => "some message")

Yes, that would actually be a good way to express it, as that would work in the 'for' part of a LINQ expression too.

I don't know if that's sufficient - since the issue I have is where some of the semantic checks are done in the constructor code of node ypes in my AST. This makes sense in cases where the same check would apply when manually constructing the AST. As these constructors raise exceptions they will be raised in the "select" part of the LINQ query when I construct the AST.

It's also strange that Catch() wouldn't, in some sense, succeed and produce some default value 🤔

You mean that parsing would continue after the catch, a "default" is returned and no message is produced?

ewoutkramer avatar May 21 '19 09:05 ewoutkramer

Something like this?

        /// <summary>
        /// Attempts the parser and invokes the exception handler if the parser throws TException.
        /// </summary>
        /// <typeparam name="TKind">The type of tokens consumed by the parser.</typeparam>
        /// <typeparam name="T">The type of the result.</typeparam>
        /// <typeparam name="TException">The type of exception caught and handled by <see cref="Catch{TKind, T, TException}(TokenListParser{TKind, T}, Func{TException, TokenListParserResult{TKind, T}})"/></typeparam>
        /// <param name="parser">The parser.</param>
        /// <param name="exceptionHandler">A function that handles TException and returns a <see cref="TokenListParserResult{TKind, T}"/>.</param>
        /// <returns>A parser that calls the first parser and handles TException by calling the exception handler.</returns>
        /// <exception cref="ArgumentNullException">Thrown if either the parser or the exceptionHandler is null.</exception>
        public static TokenListParser<TKind, T> Catch<TKind, T, TException>(
            this TokenListParser<TKind, T> parser,
            Func<TException, TokenListParserResult<TKind, T>> exceptionHandler)
            where TException : Exception
        {
            if (parser == null) throw new ArgumentNullException(nameof(parser));
            if (exceptionHandler == null) throw new ArgumentNullException(nameof(exceptionHandler));

            return input =>
            {
                try
                {
                    return parser(input);
                }
                catch (TException ex)
                {
                    return exceptionHandler(ex);
                }
            };
        }

marklauter avatar Jun 13 '24 04:06 marklauter