Context-sensitive error handling
The Default error handling mechanisms that does single token insertion and deletion works really well in many cases. The one area where I think ANTLR need some work is during no viable alternative exceptions and loops around rule references. For example, what happens if you have a fragment like:
d : decl+ ;
decl: 'foo'
| 'bar'
;
If you have input ")) foo bar foo", which is valid except for the crazy "))" at the front, rule d never enters the decl+ because that input is not consistent with the start of a decl. You will get an early exit exception from the loop, which forces d to track the exception and consume until what follows d. In other words, it consumes the entire input. Not very interesting recovery, eh? The problem here is that we cannot force d to enter the loop starting with bad input. It's almost as if we need error recovery at the start of a (...)+ loop (but not(...)*). That would essentially scant characters until it found the start of a decl. Of course, that could go too far as well. We would have to allow people to specify how to consume.
What about when the bad input is after one decl: "foo )) bar foo"? In this case, d enters decl+ loop and then immediately enters decl. It matches, returns, and then d's loop decides whether to continue. Again, we see the crazy "))" input. We cannot continue the loop and we get no exception (because we've matched at least one element for the + loop). d finishes without giving an error (unless we had EOF on the end). Still bad. Maybe what we need is an error recovery mechanism at the end of the loop as well as the beginning. We need something that will allow us to stay in the loop. We also need to allow the user to specify how to recover. The current exception mechanism is not valid on some rules, only on rules themselves after the ';'.
Sometimes you can do a good job with context-sensitive error handling if you can use the rule level granularity:
d : decl (';' decl)* ;
decl: 'foo'
| 'bar'
;
catch[NoViableAltException nvae] {
reportError(nvae);
// instead of usual: recover(input,re); do our own context-
// sensitive search for re-synchronization
BitSet startOfDecl = new BitSet(new ArrayList(){{
add(FOO); add(BAR);
}});
consumeUntil(input, startOfDecl);
}