Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Additional functionality for user-defined error messages #91

Open
jkaye2012 opened this issue Mar 4, 2017 · 3 comments
Open

Additional functionality for user-defined error messages #91

jkaye2012 opened this issue Mar 4, 2017 · 3 comments

Comments

@jkaye2012
Copy link

First and foremost, thanks for the great library (and for making it Core compatible!). Everything has worked fantastically out of the box as advertised.

A potential feature that could be nice (it would certainly be useful for me) would be the ability to hook error messages into different parsers to make the reasons behind parse failures more human-readable.

For example, if I have a parser that looks something like this:

public static readonly Sprache.Parser<Property> Property =
    from _ in Parse.String("  ")
    from variable in Variable
    from decl in Parse.String(SyntaxElement.TypeDeclaration)
    from typeSpec in TypeSpec
    from end in Parse.String(SyntaxElement.EndLine)
    select new Property(variable, typeSpec);

public static readonly Sprache.Parser<Data> Data =
    from kwyd in Parse.String(ReservedKeywords.Data)
    from _ in Parse.Char(' ')
    from typeSpec in TypeSpec
    from beginBlock in Parse.String(SyntaxElement.BeginBlock)
    from properties in Parse.AtLeastOnce(Property)
    from endBlock in Parse.String(SyntaxElement.EndBlock)
    select new Data(typeSpec, properties);

And I attempt to parse a string that looks like this (missing the expected Property from the AtLeastOnce):

data Example{
}

The error message I currently get is:

Unhandled Exception: Sprache.ParseException: Parsing failure: unexpected '}'; expected    (Line 2, Column 1); recently consumed: a Example {

   at Sprache.ParserExtensions.Parse[T](Parser`1 parser, String input)
   at JKL.Parser.JKLParser.ParseFile(String text) in /Users/jkaye/src/JKL/JKL/Parser/Parser.cs:line 41
   at JKL.Program.Main(String[] args) in /Users/jkaye/src/JKL/JKL/Program.cs:line 16

I think this is a pretty good error message for the developer. I've consumed some token and then hit another one before hitting the expected two spaces from the Property parser; however, it would be awesome if I were able to write the parser like this (or something equivalent):

public static readonly Sprache.Parser<Property> Property =
    from _ in Parse.String("  ")
    from variable in Variable.OnFailure("Property definition must begin with variable name")
    from decl in Parse.String(SyntaxElement.TypeDeclaration)
    from typeSpec in TypeSpec.OnFailure("Missing variable type specification")
    from end in Parse.String(SyntaxElement.EndLine)
    select new Property(variable, typeSpec);

public static readonly Sprache.Parser<Data> Data =
    from kwyd in Parse.String(ReservedKeywords.Data)
    from _ in Parse.Char(' ')
    from typeSpec in TypeSpec.OnFailure("Missing data type name")
    from beginBlock in Parse.String(SyntaxElement.BeginBlock).OnFailure("Expected block begin")
    from properties in Parse.AtLeastOnce(Property).OnFailure("Expected at least one Property definition")
    from endBlock in Parse.String(SyntaxElement.EndBlock).OnFailure("Missing block end")
    select new Data(typeSpec, properties);

And then get an error message like this from the same input:

Parse failure at line 2, column 1: Expected at least one Property definition

I haven't looked at the internal code much to know if this is easily do-able without a lot of effort. I was thinking you could propagate the current exception until you hit one of these OnFailure handles at which point you would replace the error message with the one provided by the first OnFailure that you hit. Do you have an idea of how difficult this would be to accomplish within the Sprache framework as it currently exists? I'd be willing to give it a shot if you think agree that this is something that would be possible and useful.

Thanks again!

@nblumhardt
Copy link
Member

Sounds like a good/feasible improvement, thanks 👍

@Odepax
Copy link

Odepax commented Aug 6, 2019

Hellow there!

Has there been more research on this until now? I'm currently working on what is probably the most naive implementation:

internal static Parser<T> ErrorMessage<T>(this Parser<T> @this, string message) =>
   input =>
   {
      IResult<T> result = @this(input);

      return result.WasSuccessful
         ? result
         : Result.Failure<T>(result.Remainder, message, result.Expectations);
   };

It works fine on simple examples:

internal static readonly Parser<IReadOnlyList<object>> Cat= (
   from c in Parse.Char('c').ErrorMessage("GIVE ME A 'C'!")
   from a in Parse.Char('a').ErrorMessage("GIVE ME A 'A'!")
   from t in Parse.Char('t').ErrorMessage("GIVE ME A 'T'!")
   select new List<object>(0)
).End();

But the error message gets lost when used in combination with Optional() and End().

Sorry for responding to a year-old post. 🥔

@nblumhardt
Copy link
Member

@Odepax thanks for the follow-up, and sorry about the very slow reply, here! We're light on maintainer time in this project and so there isn't a lot of bandwidth to for us to dig in further. A PR with a complete example would still be more than welcome.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants