Parsers with Error Recovery

I can see recovery is often likely to consist of chomping until one of some list of characters is reached. Using Elm as an example, if we have:

val = [ 1, 2, 3, ]
val = [ 1, 2, 3xy, 4]

Then chomping until ',' or ']' is hit is going to work.

Indeed, I can see in the TolerantParser, this recovery strategy is already coded:

https://github.com/mdgriffith/elm-markup/blob/3.0.1/src/Mark/Internal/TolerantParser.elm#L129

type OnError error
    = FastForwardTo (List Char)
    | Skip
    | StopWith error

Carrying the closure.

But there is another common case, that should come up often during interactive editing. That is when the list has not been closed yet. Suppose I am typing:

val = [ 1, 2, 3, 
                 ^ cursor is here

I didn’t close the list yet, and chomping for the comma or end-of-list is not going to work. An editor could theoretically want to infer type on the list by recovering to get the list [1, 2, 3] and then offer help relevant to a List Int context.

I am thinking this could be done by always pushing the closure of the current AST structure onto the context. By which I mean, if say [ is encountered, the expected closure is ]. Or ( and ) or let and in and so on.

When an error is encountered, in addition to chomping for certain characters, could also try popping the next closure off the context stack and acting as if it was encountered, and then trying to continue from there. If that fails, could rewind and try popping another one, and so on, until none are left.


val =
    [ (1, "one"
    , (2, "two")
    ]

This doesn’t seem like it will always produce good result. I forgot the ) on the first list entry, the parser won’t notice until it hits ], at which point it will pop the ) and parse the expression as [ (1, "one", (2, "two")) ].

I don’t see a function to pop the context stack, only to push onto it with inContext, so I am not yet sure how this will work? I guess I can always push a new context onto it that is always interpreted as overriding the next one, so can modify the context in that way even without a pop operation. :thinking: