How to be notified of backtracking? #32
Replies: 16 comments
-
That might be a bug in our Lua grammar, the idea was to eliminate all "major" backtracking while doing the conversion from CFG to PEG, but we might not have caught all places that need to be changed. In general, the PEGTL currently takes a "hands off" approach regarding actions and backtracking, that is, the grammar and actions have to take care of it themselves, either by not performing actions when backtracking is possible, or taking precautions to undo the actions when backtracking occurs. The general idea is that, for performance reasons, it would always be better to design the grammar and actions in a way that minimises backtracking, and eliminates it where actions are involved. If you would find some backtracking "undo"-notification useful we could look into the possibilities... |
Beta Was this translation helpful? Give feedback.
-
Thanks for the explanation; if it is indeed a bug in the grammar, I will look into it and try to provide an improvement. In general I am fine with the grammar having to be carefully designed so as to prevent excessive backtracking. I am using a modified version of that grammar in a personal project and it has proved very useful so far, thank you (actually I’m very pleased with the PEGTL in general). For now since performance is not really an issue, my local quick and dirty workaround will be to replace any problematic |
Beta Was this translation helpful? Give feedback.
-
It is an "interference" between the rules for assignments and function calls. The first time the unary operator is found is when The second time is when the statement is correctly parsed as a complete function call. In other words, it was not enough that I worked across the expression rules to prevent backtracking, I also need to work across some of the statements... |
Beta Was this translation helpful? Give feedback.
-
I'm currently working on a grammar where backtracking cannot be avoided entirely (without the lookahead hack @samhocevar posted of course) and a way to hook in notifications would be nice.
which I use where backtracking is possible like this:
I have an action for the cleanup:
and (not needed) activate the tracer for debugging:
While this seems to work, there is probably room for improvement. |
Beta Was this translation helpful? Give feedback.
-
Would it be possible to see your grammar (and have some indication as to what the actions are doing)? |
Beta Was this translation helpful? Give feedback.
-
The grammar is here: https://github.com/groxxda/nixexpr/blob/master/expr.hh Nix allows function definitions with the following syntax (among others):
At the same time, nix allows variable references like this:
Here's the rule that matches identifiers and the action that simply converts them to an ast node. When the parser encounters the expression
The reason to perform the cleanup when backtracking happens is to make this assertion happy. This is further complicated by the fact that:
|
Beta Was this translation helpful? Give feedback.
-
This is actually rather similar to the issue with the Lua grammar, perhaps unsurprisingly so: Two different kinds of statements or expressions that start with the same rules and are disambiguated only later. The general idea to get rid of this is to push the decision as far down as possible: From Looking at what you write this might be easy in your case - both of the possibilities you outline not only start with the same rule, they also do the same thing - "save However looking at the details, it all looks a bit more complicated. What you are doing with the PEGTL and in general also looks rather impressive! Do you think you can use this idea to defer the decision until you know enough? Or does this not work well with the way you have organised everything with the But the initial question of this issue was "how to be notified of backtracking". The first solution that comes to mind is to let actions to alternatively define an Of course this spreads the work out a bit, but the alternative would make the library more complicated: Let Any other ideas or suggestions? |
Beta Was this translation helpful? Give feedback.
-
Thanks for the response, here are a few quick off-topic thoughts:
I noticed that the
Although I think it is possible to do this, it's likely not worth the effort (partly because of unary prefix operators). The naive approach would be:
Gets:
which means quite some code duplication.. I could use |
Beta Was this translation helpful? Give feedback.
-
Back to topic:
I think your first suggested solution is too limited in it's usefulness to make it worth implementing. Now when So I'd strongly prefer alternative two, where I define cleanup actions for A and B separately and PEGTL decides for me what cleanup actions should be called. I'm not sure if PEGTL really needs support for this, because the approach I mentioned initially (make a dummy rule and associate it with an appropriate action) is a simple solution that already works. |
Beta Was this translation helpful? Give feedback.
-
I just discussed this with Colin and I think the "simple" solution might work like this:
will apply A, then B fails and you need to revert whatever A has done. In order to do this, you would then do this in
This might not be the most efficient solution, but it will clearly work as when B fails you already know that A succeeded. Do you think this approach will also work for your use-cases? |
Beta Was this translation helpful? Give feedback.
-
@d-frey: I think to implement this cleanly, PEGTL has to call the undo function for the action whose apply function caused the state change (as many times as the apply function was called).. |
Beta Was this translation helpful? Give feedback.
-
How about
and
for the first part, where
saves the current value of
"commits" the transaction (discarding the saved state from
uses the information from the saved state to restore the previous value. I know this is all a bit manual and lacks the convenience one would wish for. OTOH it is straight forward, fits nicely into our design space and it is very general. |
Beta Was this translation helpful? Give feedback.
-
We are also looking into giving the whole thing a more transactional feeling, the action class could allow for three functions, |
Beta Was this translation helpful? Give feedback.
-
If one wants to go with saving the state, I think the better approach is a state change for the (possibly failing) intermediate steps. In my opinion an actions undo function is mostly useful if it undoes that same action, especially for cases like For now I'm quite satisfied with the solution I described above which works similar to @d-frey s suggestion and also uses a dummy That said, I want to emphasize that PEGTL already provides perfectly good ways to achieve what I need 👍 |
Beta Was this translation helpful? Give feedback.
-
It probably depends on the state change, but yes, sometimes this is more convenient. Going back to
using in your grammar:
instead of plain
This is not too bad I guess, but of course it works in a very simple way. The PEGTL is still meant to be mean and lean, so a lot of things are possible but sometimes not very convenient. We are, however, always looking for generic, powerful yet simple ways to extend the library (time being our most sparse resource). What do you think about the above helpers and the way of adding |
Beta Was this translation helpful? Give feedback.
-
Actually the additional hooks necessary for backtracking already exist, however they are called on the Given that approach, and the work-arounds in the grammar discussed here, I will now close this issue, but feel free to continue the discussion or re-open if appropriate! |
Beta Was this translation helpful? Give feedback.
-
I have trouble understanding the mechanism to handle backtracking when it cannot be avoided.
For instance, in
examples/lua53_parse.cc
I defined the following simple action to list unary operators:Then parse this simple Lua snippet:
… with this code:
And the result is:
It appears that one action is executed twice because of backtracking when parsing
f(#l)
. Is this the expected behaviour? What mechanism might the PEGTL provide to allow me to be notified that the second call toaction<lua53::unary_operators>::apply()
should be rolled back (and how much)?Beta Was this translation helpful? Give feedback.
All reactions