I'm a bit confused what is the intended usage of elm-tools/parser to generate custom error messages. In particular, when creating a Parser value, but before calling run, there's no way to specify what sorts of errors can happen and messages to generate in response.
The only interface in the module with errors seems to happen when calling the Parser.run function, which happens just once in client code.
For instance, suppose I build up a big Parser hierarchically out of several smaller parsers:
fullParser : Parser ...
fullParser =
succeed identity
|. keyword "key1"
|= (inContext "subparser 1" <| subparser1)
|. keyword "key2"
|= (inContext "subparser 2" <| subparser2)
|. P.keyword "key3"
|= (inContext "subparser 3" <| subparser3)
and perhaps those are hierarchically constructed as well:
subparser1 : Parser ...
subparser1 =
succeed identity
|. keyword "subkey1"
|= (inContext "subsubparser 1:1" <| subparser11)
|. keyword "subkey2"
|= (inContext "subsubparser 1:2" <| subparser12)
And at the base level, perhaps I call library code, for instance from Parser.LanguageKit:
subparser11 : P.Parser (List Int)
subparser11 =
sequence
{ start = "{"
, separator = ","
, end = "}"
, spaces = spaces
, item = int
, trailing = Forbidden
}
Now, to run the parser, I call
case run fullParser source of
Ok parsedValue ->
parsedValue
Err { row, col, source, problem, context } ->
<generate some error message>
Suppose a problem happened in subparser11 because Parser.LanguageKit.sequence detected an error such as a missing comma ("{34, 56 78}"). So in the Err, the information we have access to is the Problem generated by Parser.LanguageKit.sequence, which will be something like
BadOneOf ([ExpectingSymbol ",",ExpectingSymbol "}"])
That's not a terrible thing to print, but we can do better. I also have access to the Context value with description "subsubparser 1:1" and a row and col.
It seems that to create a custom error message that says something like "I was expecting either , or } at this point in the 'subparser 1:1' set",
I can use the context description string, but it seems necessary to use a big case statement after the call to run
that uses context strings and the type of Errors that are returned to tell where in the parsing the problem failed, and then customize the error message.
In a language with exceptions, I would do something like this at the point in the parser code itself, where the Problem is generated
def fullParser(source):
subparser1(source)
...
def subparser1(source):
subparser11(source)
...
def subparser11(source):
try:
Parser.LanguageKit.sequence(source)
except BadOneOf:
raise CustomException()
In other words, I could catch the BadOneOf exception generated by the library code, and based on the local context in which I caught that exception,
create a custom Exception of my own.
But I don't see a way to do that sort of "Problem transforming" with elm-tools/parser,
i.e., put the error-transforming code at the point in the definition of the parser where it is relevant.
It seems the only option is to put lots of information into the context string at that point, and then have a big case statement after calling Parser.run
to see where exactly in the parser hierarchy the problem occurred. But this anti-pattern of using Strings as poor man's Enums seems very un-Elm to me.