Replies: 8 comments
-
Just to make sure I'm understanding the issue correctly, what you need is that if you start a parsing run with, say, two states s1 and s2, and you use state<> to change the state to s, that parsing continues with states s and s2, rather than only s? |
Beta Was this translation helpful? Give feedback.
-
That's right. If you have two state classes paren_state and record_state, and protect a branch of grammar with state<paren_state, R> then R can no longer see record_state. The assumption seems to be that all parser state will be globbed in to a super object, which kind of breaks composability. |
Beta Was this translation helpful? Give feedback.
-
It looks to me that
Note that I haven't tested the above, it might need some small weaks. @nlyan: Would you think that the above can work in your case? Or is your use-case more difficult? |
Beta Was this translation helpful? Give feedback.
-
d-frey, the problem with that approach is that it saves and restores (i.e. copies) all state. Here's the approach I'm currently taking. struct paren_state {
unsigned long depth = 0;
paren_state() noexcept = default;
template <typename... States>
paren_state (pegtl::input const&, States&&... states) noexcept
: paren_state(select<paren_state>(std::forward<States>(states)...)) {
}
template <typename... States>
void
rollback (pegtl::input const&, States&&... states) const noexcept {
auto& ps = select<paren_state>(std::forward<States>(states)...);
ps = *this;
}
}; select<T>(...) is a simply a helper which selects the first T, by reference, from the arguments passed to it. Inside state<paren_state,...>, all the active states are first passed to the paren_state constructor, which, in this case, simply ignores everything except the active paren_state and delegates to the copy-constructor. This establishes the backup of paren_state. Now the original state set ("st..." in state<>::match()) is passed to the grammar. With my rollback() patch, if it fails, paren_state::rollback() is then called on the backup, which again filters out everything except the active (but now possibly modified) paren_state, and delegates to the assignment operator to restore the original state. For extra efficiency, std::move can even be used here. A variant of this code was working with master until I realised state<>::match was only passing the copy to the grammar. You might be right that moving the rollback code in to a new state combinator is better than handling it like paren_state above, but nonetheless, as it stands, something like seq<Rule1, Rule2> can compile, whereas state<SomeState, Rule1, Rule2> can easily not unless you're willing to change Rule1 and Rule2 invasively. |
Beta Was this translation helpful? Give feedback.
-
Concerning your last paragraph "nonetheless...": The use-case That said, let's concentrate on how to create a generally useful and generic helper that works for your use-case. I will think about it some more and discuss it with Colin. |
Beta Was this translation helpful? Give feedback.
-
We have occasionally looked into adding more general or powerful variants of |
Beta Was this translation helpful? Give feedback.
-
Thanks guys. Once I'm done with the parser I'm writing, I'll also put some more thought in to this. |
Beta Was this translation helpful? Give feedback.
-
So, we had a good chat about this issue, but at the moment we can't see a solution that is noticeably more general than the included In other words, with our current experience you are already doing the right thing: Take the supplied code as example, and implement your state-changing class that exactly fits your use case... But please do continue to show us exactly at which points you need to go beyond the supplied solutions, and how exactly. That might help us when we revisit this subject further down the road. For now we will close this issue. |
Beta Was this translation helpful? Give feedback.
-
The state combinator, given state<S, G>, currently only passes a copy of state S to grammar Gs match() function. When multiple states, S1 and S2, are passed in to PEGTL, this results in G not seeing S2. I'm assuming this is an unindented oversight, as you're using a variadic param pack for state.
My simplistic workaround can be seen in my tree[0]. I've merely inverted the logic such that S1 is saved, and G is passed the original state pack in its entirety. If matching fails I call rollback(), rather than success(). This is obviously a breaking change, but I've found it incredibly useful to be able to handle granular state, so I'm interested in your thoughts.
Afaik this is a lot easier than doing the TMP dance to replace S1 in the state pack with a copy before calling match(), but maybe I'm missing your intent.
[0] nlyan@d16bc80
Beta Was this translation helpful? Give feedback.
All reactions