korken89 / smlang-rs

A State Machine Language DSL procedual macro for Rust
Apache License 2.0
193 stars 28 forks source link

Is there support for multiple guarded transitions for a state and a triggering event ? #71

Closed dkumsh closed 1 month ago

dkumsh commented 2 months ago

Is there support for multiple guarded transitions for a state and a triggering event ?

As it is described here, the guards should enable/disable transitions based on the value of the extended variables and event parameters.

I would expect that guards should allow transitions from (state,event) to different states, same way as it works in Boost SML and many other state machine implementations.

The article referenced above says:

In extended state machines, a transition can have a guard, which means that the transition can "fire" only if the guard evaluates to TRUE. A state can have many transitions in response to the same trigger, as long as they have nonoverlapping guards; however, this situation could create problems in the sequence of evaluation of the guards when the common trigger occurs. The UML specification[1] intentionally does not stipulate any particular order; rather, UML puts the burden on the designer to devise guards in such a way that the order of their evaluation does not matter. Practically, this means that guard expressions should have no side effects, at least none that would alter evaluation of other guards having the same trigger.

Is there support for multiple guarded transitions for a state and a triggering event ? If yes, could you please show how to do it, otherwise would it be possible to implement it?

ryan-summers commented 2 months ago

If the initial state and triggering event are identical, I do not believe we support multiple ending states based on different guards. You would have to use a different event type to accomplish this instead.

Can you clarify what you're looking to do exactly?

dkumsh commented 2 months ago

Something like this: State1 + Event1 [ guard2] / action2 = State2, State1 + Event1 [ guard3] / action_3 = State3, In State1 we receive Event1 and depending on which of the guards returns true, we transition either to State2 or to State3. We can assume that the guards are not-overlapping. Different event type would not work.

Here is one of my examples:
I receive a big message from network. It is fragmented into multiple parts. The goal of this protocol is to assemble the big message from the fragments. I need to wait for the first fragment, and then start assembling the big message. Each fragment has a fragment number field and a flag indicating if it is a last fragment

If the message I got is not the first message, I stay in the same state and ignore the fragment (as I need to wait for the first fragment) If it is the first fragment , the protocol switches to another state, where it processes remaining fragments If is is the first and simultaneously the last message (i.e. we got a complete message), it switches to the third state.

Multiple transitions in a given state and for a given triggering event is exactly what the guards were designed for, as it described in the Wipkipedia article I am refereeing. One guard per state/trigger works more like an assertion rather than a guard

Here is for example you can see a UML visualisation of how guards are used. Here is another tool describing the semantics of the guards. Example from BoostSML supporting this (See transitions for ( s3,e4) )

I think full support of this would be a valuable extension to smlang .

ryan-summers commented 2 months ago

Interesting that BoostSML indeed supports this. I'd be happy to review a PR for it, but this definitely feels non-trivial to add.

Is there support for multiple guarded transitions for a state and a triggering event ?

No, I don't believe this is yet implemented in smlang.

If yes, could you please show how to do it, otherwise would it be possible to implement it?

To implement this, you would need to parse the syntax for it in the proc-macro and update the generated code logic. You'd also need to implement code within the proc-macro to check that the guard logic are non-overlapping implementations as well, which could be somewhat complex to verify. I'd recommend looking at some existing and previous PRs to get a feel as to what the changes look like, for example https://github.com/korken89/smlang-rs/pull/66


By the way, I don't see how your example would require this. It seems like you could probably implement this with different state transitions to work around this, but that's just my take.

dkumsh commented 2 months ago

Thanks for you reply. I made a change on my local clone. Indeed it is not a simple change. I am now trying to use it to implement a state machine in my project. Once I feel comfortable with the change I will push a PR for review (maybe within a week or two). I hope it won't break any existing functionality. It now passes all tests except two tests validating state/event duplicates as I had to allow the duplicates. Will get back to you in a while

dkumsh commented 2 months ago

I have created a draft PR for a review : https://github.com/korken89/smlang-rs/pull/72

dkumsh commented 2 months ago

Current implementation only allows single function guards. In addition to the PR above I implemented support for guard expressions (similar to Boost SML). In the original library you can define a transition like : State + Event [guard] / ... where in square brackets it allows a single boolean guard function. With the addition I describe it is now possible to specify guard boolean expressions, e.g : State + Event [guard1 && ! guard2] / ... I will push that code in a few days to my fork

dkumsh commented 1 month ago

Hello, could someone please review the pull request at their earliest convenience? Thank you!

dkumsh commented 1 month ago

@ryan-summers Tanks for providing the review!