statemachine!() { /* proc-macro */ }Expand description
Creates a state machine.
§The Statemachine Struct
The statemachine struct that is generated (the first item in the macro body) has methods with the following signatures.
use statemachine_macro::*;
statemachine! {
struct Foo;
enum FooState consumes [char] from Start;
}
/*impl Foo {
/// Changes the statemachine to the given state.
fn reset(&mut self, state: FooState) { ... }
/// Returns true if the statemachine is in an accepting state.
fn is_accepting(&self) -> bool { ... }
/// Performs a transition for the given input symbol.
fn consume<T: ...>(&mut self, val: T) { ... }
}*/These methods are currently not provided as a trait implementation. This may change in a future major version.
§Examples
Basic consuming:
use statemachine_macro::*;
statemachine! {
pub struct Foo;
enum FooState consumes [char] from Even accepts [Odd];
Even => {
_ => Odd
},
Odd => {
_ => Even
}
}
let mut foo = statemachine_new!(Foo{});
assert!(!foo.is_accepting());
foo.consume(' ');
assert!(foo.is_accepting());
foo.consume(' ');
assert!(!foo.is_accepting());
foo.consume(' ');
assert!(foo.is_accepting());Resetting the state machine:
use statemachine_macro::*;
#[derive(Debug)]
struct Money;
statemachine! {
pub struct Foo;
enum FooState consumes [Money] from Unpaid accepts [Paid];
}
let mut foo = statemachine_new!(Foo{});
assert!(!foo.is_accepting());
foo.reset(FooState::Paid); // mwahahaha free real estate
assert!(foo.is_accepting());Advanced consuming with multiple types:
use statemachine_macro::*;
statemachine! {
pub struct Foo {
cheater: bool
}
enum FooState consumes [u32, i32] from Even accepts [Odd];
Even => {
u32 match x => {
if x % 2 == 0 {
Even
} else {
Odd
}
},
i32 match v => if *v < 0 {
self.cheater = true;
Even
},
i32 match x => panic!("Hey! Are you trying to cheat?")
},
Odd => {
u32 match x => {
if x % 2 == 0 {
Even
} else {
Odd
}
},
i32 match v => if *v < 0 {
self.cheater = true;
Odd
},
i32 match x => panic!("Hey! Are you trying to cheat?")
}
}
let mut foo = statemachine_new!(Foo{ cheater: false });
assert!(!foo.cheater);
assert!(!foo.is_accepting());
foo.consume(5u32);
assert!(!foo.cheater);
assert!(foo.is_accepting());
foo.consume(4u32);
assert!(!foo.cheater);
assert!(!foo.is_accepting());
foo.consume(4u32);
assert!(!foo.cheater);
assert!(!foo.is_accepting());
foo.consume(-3i32);
assert!(foo.cheater);
assert!(!foo.is_accepting());§Syntax
The following is the syntax of the macro contents. The starting nonterminal is ‘statemachine’.
statemachine ::= struct-item state-description ( state-behaviors )?
state-description ::=
"enum" ident "consumes" "[" type ( "," type )* "]"
( "accepts" "[" ident ( "," ident )* "]" )? ";"
state-behaviors ::= state-behavior ( "," state-behavior )*
state-behavior ::= ident "=>" "{" ( state-transitions )? "}"
state-transitions ::= state-transition ( "," state-transition )*
state-transition ::= transition-trigger | transition-pattern | transition-catchall
transition-pattern ::= type "match" pattern "=>" ( transition-guard )? expr
transition-guard ::= "if" expr
transition-catchall ::= "_" "=>" expr
transition-trigger ::= "@" ( enter-trigger | leave-trigger | loop-trigger )
enter-trigger ::= "enter" "=>" expr
leave-trigger ::= "leave" "=>" expr
loop-trigger ::= "loop" "=>" expr