[antlr-interest] executing actions during backtrack
Terence Parr
parrt at cs.usfca.edu
Tue May 9 18:28:08 PDT 2006
On May 9, 2006, at 3:30 PM, Kay Roepke wrote:
>>> I guess you need something like an inverse action that applies when
>>> backtracking fails. So you might need a pair of actions. This way
>>> all
>>> actions executed inside backtracking are undone when backtracking is
>>> done. Maybe it is enough to add an inverse action to indicate it
>>> should be executed inside backtracking?
>>
>> Hi :) Yep, thought about that and decided it's probably very hard
>> to always do an undo. That said, for symbol tables it might be
>> doable. Might have to track stuff in a data structure to manage
>> the undos. What if we did "software transaction" like stuff to
>> auto-rewind?
>
> Perl does something like that, but of course that only applies to
> changes to locally scoped variables when you embed code in regexes.
> Stacking those guessing code blocks and executing the undo blocks
> on the way back would be the way to do, methinks.
> If a user does something that absolutely should only happen once,
> one would assume that he tests for guessing mode anyway. We can't
> second guess them
> there (as much as we wanted to ;))
What if we could auto unroll any object/data-structured you
identified somehow as transactional? You would give me an undo()
method for each object, like a hashtable. I could record the
arguments to pass to undo so it knows how to undo. This would not be
perfect as the arguments could point at stuff that could change (like
a stringbuffer), but would be a poor man's transactions.
We need to keep in mind that MOST of the time, this feature is
unnecessary. It's only needed when you need both syn and sem preds
and they interact.
Let's see. Here's a simple example to chew on:
{
Scope scope = null; // current scope
}
a : (block '.')=>block '.' // dummy rule to force backtrack of block
| block '!'
;
block
: {scope = new LocalScope(scope);} // push new scope
'{' statement* '}'
{scope = scope.parent;} // pop scope
;
statement
: "int" ID ';' {scope.define($ID.text, new VariableSymbol($ID.text));}
| "if" ...
;
First time through on input
{ int i; }.
ANTLR backtracks through block and statement doing no actions. Then
upon success, rewinds, and does it again with feeling. Upon input
{ int i; }!
ANTLR backtracks through block and statement doing no actions. Upon
failure, it rewinds and does 2nd alt, calling block with feeling.
This is the normal case. What if we needed a semantic predicate in
statement that needed to know whether an ID was a var?
statement
: "int" ID ';' {scope.define($ID.text, new VariableSymbol($ID.text));}
| "if" ...
| {isVar(input.LT(1))}? ID ... // some alt predicated upon ID being
a variable name
;
Here, you MUST have the define() function call executed or else the
{...}? sem pred will always fail.
Alright. We need to unroll the define() action for sure but we could
avoid if by throwing out the entire scope. If we executed the push/
pop scope actions, the definitions would actually disappear already
and automatically! Wow, interesting!
Let's keep going with the undo() method though. I'd need to track
the scope and ID.text and VariableSymbol objects so undo would know
what to do. How can I know what to track? Impossible. Users will
have to provide a specific undo action that I can execute:
statement
: "int" ID ';'
{scope.define($ID.text, new VariableSymbol($ID.text));}
@undo {scope.remove($ID.text);}
| "if" ...
| {isVar(input.LT(1))}? ID ... // some alt predicated upon ID being
a variable name
;
That would unroll it no problem. Let's assume that plain {...}
actions always exec when an @undo is provided (heh, I like that).
Where would the undo action get executed though? What if the undo
action references a local variable? It won't even compile if I move
it out to the commit/rollback of the synpred up in a(). The undo
cannot be done in the rule as statement will be called multiple
times. It has t be at the transaction end (synpred fail/success)
where I roll back the input. Oh! Also, if it references input.LT(1)
that will be at some unknown location during the undo action's
rollback! There would have to be lots of restrictions on the undo
actions. For example, I can see already that $ID.text will be
unavailable in the rollback action in a().
Ok, taking the cue from Jeff Barnes, perhaps the undo action actually
creates an object that parameterizes the action and executes it
during rollback in reverse order:
@undo(Scope s=scope, String id=$ID.text) {s.remove(id);}
Here it would evaluate scope and $ID.text in it's original context
but execute the action during rollback. The object could be:
class Action324 extends UndoAction {
Scope s;
String id;
public Action324(Scope s, String id) {...set fields...}
public void undo() { s.remove(id); }
}
The action would be translated to:
undoActions.add(new Action324(scope,$ID.text));
Later we can walk undoActions and undo everything.
Hmm...would this work? Surely in this case. The key is passing all
necessary data to the undo as evaluated in its original context.
Ter
More information about the antlr-interest
mailing list