@uml
Override this parser interpreters normal decision-making process
at a particular decision and input token index. Instead of
allowing the adaptive prediction mechanism to choose the
first alternative within a block that leads to a successful parse,
force it to take the alternative, 1..n for n alternatives.
As an implementation limitation right now, you can only specify one
override. This is sufficient to allow construction of different
parse trees for ambiguous input. It means re-parsing the entire input
in general because you're never sure where an ambiguous sequence would
live in the various parse trees. For example, in one interpretation,
an ambiguous input sequence would be matched completely in expression
but in another it could match all the way back to the root.
s : e '!'? ;
e : ID
| ID '!'
;
Here, x! can be matched as (s (e ID) !) or (s (e ID !)). In the first
case, the ambiguous sequence is fully contained only by the root.
In the second case, the ambiguous sequences fully contained within just
e, as in: (e ID !).
Rather than trying to optimize this and make
some intelligent decisions for optimization purposes, I settled on
just re-parsing the whole input and then using
{link Trees#getRootOfSubtreeEnclosingRegion} to find the minimal
subtree that contains the ambiguous sequence. I originally tried to
record the call stack at the point the parser detected and ambiguity but
left recursive rules create a parse tree stack that does not reflect
the actual call stack. That impedance mismatch was enough to make
it it challenging to restart the parser at a deeply nested rule
invocation.
Only parser interpreters can override decisions so as to avoid inserting
override checking code in the critical ALL(*) prediction execution path.
@uml Override this parser interpreters normal decision-making process at a particular decision and input token index. Instead of allowing the adaptive prediction mechanism to choose the first alternative within a block that leads to a successful parse, force it to take the alternative, 1..n for n alternatives.
As an implementation limitation right now, you can only specify one override. This is sufficient to allow construction of different parse trees for ambiguous input. It means re-parsing the entire input in general because you're never sure where an ambiguous sequence would live in the various parse trees. For example, in one interpretation, an ambiguous input sequence would be matched completely in expression but in another it could match all the way back to the root.
s : e '!'? ; e : ID | ID '!' ;
Here, x! can be matched as (s (e ID) !) or (s (e ID !)). In the first case, the ambiguous sequence is fully contained only by the root. In the second case, the ambiguous sequences fully contained within just e, as in: (e ID !).
Rather than trying to optimize this and make some intelligent decisions for optimization purposes, I settled on just re-parsing the whole input and then using {link Trees#getRootOfSubtreeEnclosingRegion} to find the minimal subtree that contains the ambiguous sequence. I originally tried to record the call stack at the point the parser detected and ambiguity but left recursive rules create a parse tree stack that does not reflect the actual call stack. That impedance mismatch was enough to make it it challenging to restart the parser at a deeply nested rule invocation.
Only parser interpreters can override decisions so as to avoid inserting override checking code in the critical ALL(*) prediction execution path.