#state-machine #interpreter #language

shakemyleg

A simple state machine definition language and interpreter

14 stable releases

3.0.0 Sep 19, 2024
2.5.0 Aug 5, 2024
2.2.2 Jul 31, 2024
1.0.2 Jul 16, 2024

#1655 in Algorithms

Download history 283/week @ 2024-07-14 2/week @ 2024-07-21 903/week @ 2024-07-28 270/week @ 2024-08-04 10/week @ 2024-08-11 3/week @ 2024-08-25 4/week @ 2024-09-01 153/week @ 2024-09-15 17/week @ 2024-09-22 249/week @ 2024-09-29

419 downloads per month

MIT license

59KB
1.5K SLoC

tests badge Written up - here!

SML - ShakeMyLeg, is that a State Machine Language?

A simple state machine definition language and interpreter.

A state machine is composed of states - stages of the machine which are run until an exit condition is met and the machine moves to the next stage. State machines in shakemyleg are defined as a series of expressions which are run every time the machine runs - the "head". Along with the head is the body - a list of conditions which, when evaluate true, run a series of expressions and a StateOp (changeto <state>, stay, end). If no condition is true, no action is taken. Conditions are visited in order. Comments start with a #.

A very simple example shakemyleg machine:

# flip_flip.sml

state A:
    always:
        outputs.bar = inputs.bar + 1
        changeto B

state B:
    always:
        outputs.bar = inputs.bar + 1
        changeto A

(Liberal whitespace around operators and brackets is required because the compiler is dumb.) This machine alternates between states A and B, and propagates the value bar from the input object to the output, and increments it.

We can "compile" this and run it:

use shakemyleg::compile;
use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize)]
struct Foo {
    bar: u8
}

let src = r#"
state A:
    always:
        outputs.bar = inputs.bar + 1
        changeto B
state B:
    always:
        outputs.bar = inputs.bar + 1
        changeto A
"#;

let mut machine = compile(src).unwrap();

let i = Foo { bar: 0 };
let o: Foo = machine.run(i).unwrap().unwrap();
// Two unwraps as the rv is Result<Option<Foo>>
// Result<...> checks if any errors occurred while running
// Option<...> checks if the machine is still running

// output.bar is incremented every time the machine is run
if o.bar != 1u8 {
    panic!();
}

Dependencies

~5.5–7.5MB
~135K SLoC