I demonstrate in the MAXMATCH example how UPARSE manages to "roll back" the influence of parsers that fail (when that's the semantic you want):
https://rebol.metaeducation.com/t/maxmatch-parser-combinator-with-without-rollback/2648
But all of the rollback examples we have so far follow a pattern... where results are being accrued, and the "rollback" is just discarding the accrued results before they can have an impact.
But What If You Want To Undo An Impact?
Let's say maybe you wanted a version of assignment that would get undone:
parse [<foo> "bad-arg-for-foo" ...] [
mode: (null)
[
'<foo> mode: ('foo-mode) ["good-arg-for-foo" ...]
|
'<bar> mode: ('bar-mode) ["good-arg-for-bar" ...]
|
(assert [mode = null]) ; let's say we want this
...
]
...
]
Today, if the parse sees <foo> you'll hit the mode: ('foo-mode) and then the rule after will fail. So when you reach the assert you'll see that impact on mode.
You might imagine a combinator for it. Let's frame it very simply in a way that doesn't invoke magic or variadics...
I'll call it UNDOABLE for lack of a better word. It could assume it takes a BLOCK! rule literally, and only accepts the block if it reads it and goes "yeah, that's the kind of thing I can undo":
parse [<foo> "bad-arg-for-foo" ...] [
mode: (null)
[
'<foo> undoable [mode: ('foo-mode)] ["good-arg-for-foo" ...]
|
'<bar> undoable [mode: ('bar-mode)] ["good-arg-for-bar" ...]
|
(assert [mode = null]) ; let's say we want this
...
]
...
]
So it could see that it's a SET-WORD! and a GROUP! and go "yeah, that's understandable enough... I can record the old value of the variable, assign the group eval result to the variable, and if the rule I'm in fails we can just put the value back"
You Can't Do This in UPARSE
In order for this to work, there'd have to be a callback that happened on rollback.
But UPARSE's mechanism of rollback isn't based on callbacks, it's just a decision on the part of combinators to not preserve accrued information. No one finds out about this decision because it's something that didn't happen... not something that did.
There are some wacky things that might be possible. A variable might be set up as an ALIAS that actually looks for its current value in the pending items list... for instance. So whatever you last assigned would be what that variable thought it was...
...which is kind of crazy, because although it would roll back on failure it would just keep accruing a history of values on success, while only looking at the last one. This is a good argument for not implementing scope yourself. ![]()
This Isn't Too Important, But Wanted To Write It Up
Just a thought-experiment. I wondered if it would be easy to write a "roll back assignments" combinator, and I do think it is possible with my ALIAS trick and looking in the pending list. You just have to have some kind of scoping container to say "I want to toss the history of assignments now, if success was accomplished".