All Projects → VKlayd → Rust_fsm_macros

VKlayd / Rust_fsm_macros

Licence: mit
FSM in Rust's macros.

Programming Languages

rust
11053 projects

Projects that are alternatives of or similar to Rust fsm macros

pastafarian
A tiny event-based finite state machine
Stars: ✭ 20 (+0%)
Mutual labels:  fsm, state-machine
statemachine-go
🚦 Declarative Finite-State Machines in Go
Stars: ✭ 47 (+135%)
Mutual labels:  fsm, state-machine
kstatemachine
KStateMachine is a Kotlin DSL library for creating finite state machines (FSM) and hierarchical state machines (HSM).
Stars: ✭ 63 (+215%)
Mutual labels:  fsm, state-machine
FiniteStateMachine
This project is a finite state machine designed to be used in games.
Stars: ✭ 45 (+125%)
Mutual labels:  fsm, state-machine
fea state machines
A Buffet Of C++17 State Machines
Stars: ✭ 19 (-5%)
Mutual labels:  fsm, state-machine
xstate
State machines and statecharts for the modern web.
Stars: ✭ 21,286 (+106330%)
Mutual labels:  fsm, state-machine
UnityHFSM
A simple yet powerful class based hierarchical finite state machine for Unity3D
Stars: ✭ 243 (+1115%)
Mutual labels:  fsm, state-machine
flow
A Statically Type Checked State Machine DSL for Kotlin
Stars: ✭ 74 (+270%)
Mutual labels:  fsm, state-machine
qm
QM model-based design tool and code generator based on UML state machines
Stars: ✭ 54 (+170%)
Mutual labels:  fsm, state-machine
xstate-cpp-generator
C++ State Machine generator for Xstate
Stars: ✭ 33 (+65%)
Mutual labels:  fsm, state-machine
qp-arduino
QP real-time embedded frameworks/RTOS for Arduino (AVR and SAM)
Stars: ✭ 37 (+85%)
Mutual labels:  fsm, state-machine
Fsm As Promised
A finite state machine library using ES6 promises
Stars: ✭ 446 (+2130%)
Mutual labels:  state-machine, fsm
remachine
[WIP] Reason pattern matching viz
Stars: ✭ 44 (+120%)
Mutual labels:  fsm, state-machine
use-state-machine
Use Finite State Machines with React Hooks
Stars: ✭ 28 (+40%)
Mutual labels:  fsm, state-machine
stateless
Finite State Machine porting from Stateless C#
Stars: ✭ 25 (+25%)
Mutual labels:  fsm, state-machine
simple-state-machine
A simple Java state machine for Spring Boot projects
Stars: ✭ 25 (+25%)
Mutual labels:  fsm, state-machine
Stateless
Go library for creating state machines
Stars: ✭ 247 (+1135%)
Mutual labels:  state-machine, fsm
kuafu
This is a tool library that includes log, fsm, state machine...
Stars: ✭ 83 (+315%)
Mutual labels:  fsm, state-machine
ember-fsm
[Maintenance Mode] A promise-aware finite state machine implementation for Ember
Stars: ✭ 37 (+85%)
Mutual labels:  fsm, state-machine
Statecharts.github.io
There is no state but what we make. Feel free to pitch in.
Stars: ✭ 265 (+1225%)
Mutual labels:  state-machine, fsm

Finite State Machine generator in Rust's macro

Overview

With this macro you can easily implement Finite State Machine in declarative way.

State machine consists of:

  • Name
  • Initial state
  • List of states
  • List of commands
  • List of state nodes

Each state node contains:

  • State
  • Context (optional)
  • List of command reactions

Each command reaction contains:

  • Command to react on
  • User-defined code of reaction (optional)
  • Next state of machine (optional)

Working example to begin with

Let's say we'd like to implement such machine:

FSM example

Corresponding code will look like:

#[macro_use] extern crate macro_machine;
declare_machine!(
    MyMachine(A {counter: 0}) // Name and initial state with initial value
    states[A,B] // List of states
    commands[Next] // List of commands
    (A context{counter: i16}: // State node and this state context description with name binding
        >> { // Executed on state A enter
            println!("Enter A: {:?}", context);
            context.counter = context.counter + 1;
        }
        << { // Executed on state A leave
            println!("Leave A: {:?}", context);
            context.counter = context.counter + 1;
        }
        Next {
            println!("Next in A: {:?}", context);
            context.counter = context.counter + 1;
        } => B {counter: context.counter}; // Command Reaction. Now on command Next we add 1 to our context. Also we change state to B and init it with our counter value.
    )
    (B context{counter: i16}:
        >> {
            println!("Enter B: {:?}", context);
            context.counter = context.counter + 1;
        }
        << {
            println!("Leave B: {:?}", context);
            context.counter = context.counter + 1;
        }
        Next {
            println!("Next in B: {:?}", context);
            context.counter = context.counter + 1;
        } => A {counter: context.counter};
    )
);

fn main() {
    use MyMachine::*;
    let mut machine = MyMachine::new();
    machine.execute(&MyMachine::Commands::Next).unwrap();
    machine.execute(&MyMachine::Commands::Next).unwrap();
}

Longer explanation

Simplest state machine example:

#[macro_use] extern crate macro_machine;
declare_machine!(
    Simple(A) // Name and initial State
    states[A,B] // list of States
    commands[Next] // list of Commands
    (A: // State Node
        Next => B; // Command Reaction. Just change state to B
    )
    (B:
        Next => A; // And back to A
    )
);

So, now you can use state machine:

fn main() {
    use Simple::*;
    let mut machine = Simple::new();
    machine.execute(&Simple::Commands::Next).unwrap();
    machine.execute(&Simple::Commands::Next).unwrap();
}

You can add some intelligence to machine.

Each state can hold some data. On State change you can transmit some data between states. It looks like you just create struct with some fields initialization:

#[macro_use] extern crate macro_machine;
declare_machine!(
    Simple(A{counter:0}) // Name and initial State with initial value
    states[A,B] // list of States
    commands[Next] // list of Commands
    (A context{counter:i16}: // State Node and this state context description with binding name
        Next {context.counter=context.counter+1}=> B{counter:context.counter}; // Command Reaction. Now on command Next we add 1 to our context. Also we change state to B and init it with our x value.
    )
    (B context{counter:i16}:
        Next {context.counter=context.counter+1}=> A{counter:context.counter};
    )
);

Let's check our state transmission:

fn main() {
    use Simple::*;
    let mut machine = Simple::new();

    // We are in state A and have our initial value 0
    assert!(match machine.get_current_state(){
        States::A{context}=> if context.counter == 0 {true} else {false},
        _=>false
    });
    machine.execute(&Simple::Commands::Next).unwrap();

    // We are in state B and have counter == 1
    assert!(match machine.get_current_state(){
        States::B{context}=> if context.counter == 1 {true} else {false},
        _=>false
    });
    machine.execute(&Simple::Commands::Next).unwrap();

    // Again in state A and have counter == 2
    assert!(match machine.get_current_state(){
        States::A{context}=> if context.counter == 2 {true} else {false},
        _=>false
    });
}

Also there is callbacks on each entrance and each leave of state.

#[macro_use] extern crate macro_machine;
declare_machine!(
    Simple(A{counter:0}) // Name and initial State with initial value
    states[A,B] // list of States
    commands[Next] // list of Commands
    (A context{counter:i16}: // State Node and this state context description with binding name
        >> {context.counter = context.counter+1;} // Execute when enter state A
        << {context.counter = context.counter+1;} // Execute when leave state A
        Next {context.counter=context.counter+1;} => B{counter:context.counter}; // Command Reaction. Now on command Next we add 1 to our context. Also we change state to B and init it with our x value.
    )
    (B context{counter:i16}:
        Next {context.counter=context.counter+1} => A{counter:context.counter};
    )
);
fn main() {
    use Simple::*;
    let mut machine = Simple::new();
    assert!(match machine.get_current_state(){
        // We are in state A and have value 1. Because Enter State callback executed.
        States::A{context}=> if context.counter == 1 {true} else {false},
        _=>false
    });
    machine.execute(&Simple::Commands::Next).unwrap();
    assert!(match machine.get_current_state(){
        // We are in state B and have counter == 3. Increment happen on User Code execution and execution of Leave state callback.
        States::B{context}=> {println!("context counter: {}", context.counter);if context.counter == 3 {true} else {false}},
        _=>false
    });
    machine.execute(&Simple::Commands::Next).unwrap();
    assert!(match machine.get_current_state(){
        // Again in state A and have counter == 5. Increment happen on User Code execution and on state A enter.
        States::A{context}=> if context.counter == 5 {true} else {false},
        _=>false
    });
}

Example of Machine-scoped context. This context exist in machine life-time.

Let's count machine's state changes:

#[macro_use] extern crate macro_machine;
declare_machine!(
    Simple machine_context{counter: i16} (A) // Declare machine scoped context
    states[A,B]
    commands[Next]
    (A :
        >> {machine_context.counter=machine_context.counter+1;} // Add 1 when enter in state
        Next => B; // Just switch to other state
    )
    (B :
        >> {machine_context.counter=machine_context.counter+1;}
        Next => A;
    )
);
fn main() {
    use Simple::*;
    let mut machine = Simple::new(0); // Create machine and initiate machine context by 0
    let context = machine.get_inner_context();
    assert!(context.counter == 1);
    machine.execute(&Simple::Commands::Next).unwrap();
    let context = machine.get_inner_context();
    assert!(context.counter == 2);
    machine.execute(&Simple::Commands::Next).unwrap();
    let context = machine.get_inner_context();
    assert!(context.counter == 3);
}

Changelog

0.2.0

  • Changed behavior of Leave action. Now it execute before new State context creation.
  • Add machine-scoped context. It can be used by all callbacks inside machine. Data in this context have machine's life-time.
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].