Add an analog of `delimited_by` with better error generation
I would like to have an analog of delimited_by, which would allow to pass to the error that occurred when the closing parser worked, information about how the opening parser worked, most often span, the dumbest way to do it -
inner.xxx(just(“(”, |span| just(”)”).map_err(|e: Error| Error::new(Expected::TupleClose(OpenBracket::new(span.clone())), e.found(), e.span()). But clearly there is a more elegant solution using the abstractions we already have.
So this actually existed in 0.9: Error had a dedicated method for unclosed delimiters and the nested_delimiters recovery strategy would make use of it.
We could do exactly the same thing in 1.0: perhaps if this is something you'd be happy with, we can go right ahead and just implement that. My feeling is that there might be space to add something that allows for more fine-grained control over the final error though, perhaps via the labelling system, allowing the unclosed delimiter to become an extra annotation on top of the existing error (i.e: a new method that sits alongside LabelError::in_context).
Interested to know your thoughts!
There are some thoughts about adding a trait that allows to create an error based on context or expand an existing trait in a similar way. In other words for this we need a trait with additional parameters - type of context, in case of expanding the current one all actual realizations will be for context (). The method will be something like expected_found_with_ctx.
Forget what I wrote above, I seem to have sorted it all out now.
I suggest adding CloseDelimiterExpected<T>, which would be a label. It should contain an instance of type T and span. Then there should be an analog of delimited_by (The name should be thought about, it's easier for you to do since you've already thought along these lines). It looks like the following: a.xxx(b, c). It executes the b parser first, saves its output and span. Then it executes a. Then executes c, if it fails, it uses the one described above as the error label, which is created from the output of parser b and its span.
I like the idea of moving this to a label, that seems very forward-thinking.
The thing that makes this a bit more complex is that just pinning it to delimited_by isn't sufficient: to be able to generate errors like "expected closing delimiter here" we have to know the set of delimiters that we should reject. This is what nested_delimiters is doing, and it's required for us to generate proper errors for nested inputs like
{ { ] }
Here, the error should look like "expected closing } for block defined at column 3 before ] at column 5" but right now the error won't mention the opening brace because it doesn't know that the braces/brackets are structural patterns in their own right, independent of whatever happens to be inside them.
I don't know if this will help, but I've assumed that the type of T in this case will be just enum containing delimiters. In that case we could rely on the fact that all the places we are interested in have a common type T.
For example, (), [], and {} would be in the same enam, which is a label. But "" would be in another, since the 3 above cannot occur in them (assuming that this is the case in the language in question).
The first idea how to implement what you wrote is to require the user to first use a generic parser for any of the brackets, and then a validator to check that the necessary one is encountered. Or require a parser for the required grid and a parser for all others. But the first option is more convenient, because the general parser can be reused.
Something like: a.xxx(just("("), one_of("")]}""), |i| i == ")").