elm-tools/parser: how to transform library-generated Problems into custom Problems?

163 views
Skip to first unread message

Dave Doty

unread,
Aug 3, 2017, 3:03:34 PM8/3/17
to Elm Discuss
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.

Dave Doty

unread,
Nov 26, 2017, 6:42:10 PM11/26/17
to Elm Discuss
I thought I'd re-ask this question to see if anyone has any ideas. My original post is a bit long so I'll be briefer:

TL/DR: how does one replace an error message generated by elm-tools/parser with a custom error message?

If an error condition is detected by directly my code while parsing, then I can use

P.fail "my custom error message"

But what if the problem is found not directly in my code, but in elm-tools/parser code that I call? For example,

Parser.list spaces Parser.int

If the current text doesn't look like a list of integers, then Parser.list will generate a Problem. But suppose I want to output a custom error message instead of simply reporting the error message contained in the Problem generated by Parser.list. How can I do this?

It's analogous to (in imperative languages with exceptions) catching an exception thrown by a called function and throwing a new, more informative exception.
Reply all
Reply to author
Forward
0 new messages