statemachine_macro/
lib.rs

1//! Provides the statemachine!() macro.
2//!
3//! # Examples
4//!
5//! ```
6//! use statemachine_macro::*;
7//!
8//! statemachine! {
9//!     #[derive(Default)]
10//!     pub struct Foo {
11//!         pub allow_x: bool
12//!     }
13//!
14//!     enum FooState consumes [char, i32] from Start accepts [Done];
15//!
16//!     Start => {
17//!         @enter => {
18//!             println!("Entering Start!");
19//!         },
20//!         @leave => {
21//!             println!("Leaving Start!");
22//!         },
23//!         @loop => {
24//!             println!("Looping inside Start!");
25//!         },
26//!         char match 'a' => {
27//!             println!("Got 'a'... Going to 'Done'!");
28//!             Done
29//!         },
30//!         char match 'b' => {
31//!             println!("Got 'b'... allowing 'x'!");
32//!             self.allow_x = true;
33//!             Start
34//!         },
35//!         char match 'x' => if self.allow_x {
36//!             println!("Got authorized 'x'.");
37//!             Done
38//!         },
39//!         char match 'x' => if !self.allow_x {
40//!             println!("Got unauthorized 'x'.");
41//!             Error
42//!         },
43//!         i32 match 42 => {
44//!             println!("It is the answer!");
45//!             Error
46//!         },
47//!         i32 match val => {
48//!             println!("Got {}", val);
49//!             Error
50//!         },
51//!         _ => Error
52//!     },
53//!
54//!     Error => {
55//!         _ => Error
56//!     },
57//!
58//!     Done => {
59//!         _ => Error
60//!     }
61//! }
62//!
63//! let mut foo: Foo = Default::default();
64//! foo.consume('a');
65//! assert!(foo.is_accepting());
66//! assert!(!foo.allow_x);
67//! foo.reset(FooState::Start);
68//!
69//! foo.consume('b');
70//! assert!(!foo.is_accepting());
71//! assert!(foo.allow_x);
72//! foo.consume('x');
73//! assert!(foo.is_accepting());
74//! ```
75
76mod model;
77mod util;
78
79use proc_macro;
80use quote::ToTokens;
81use syn::{parse_macro_input, parse_quote};
82
83/// Creates a state machine.
84///
85/// # The Statemachine Struct
86///
87/// The statemachine struct that is generated (the first item in the macro body) has methods with the following signatures.
88///
89/// ```
90/// use statemachine_macro::*;
91///
92/// statemachine! {
93///     struct Foo;
94///     enum FooState consumes [char] from Start;
95/// }
96///
97/// /*impl Foo {
98///     /// Changes the statemachine to the given state.
99///     fn reset(&mut self, state: FooState) { ... }
100///
101///     /// Returns true if the statemachine is in an accepting state.
102///     fn is_accepting(&self) -> bool { ... }
103///
104///     /// Performs a transition for the given input symbol.
105///     fn consume<T: ...>(&mut self, val: T) { ... }
106/// }*/
107/// ```
108///
109/// These methods are currently not provided as a trait implementation. This may change in a future major version.
110///
111/// # Examples
112///
113/// Basic consuming:
114/// ```
115/// use statemachine_macro::*;
116///
117/// statemachine! {
118///     pub struct Foo;
119///
120///     enum FooState consumes [char] from Even accepts [Odd];
121///
122///     Even => {
123///         _ => Odd
124///     },
125///
126///     Odd => {
127///         _ => Even
128///     }
129/// }
130///
131/// let mut foo = statemachine_new!(Foo{});
132/// assert!(!foo.is_accepting());
133/// foo.consume(' ');
134/// assert!(foo.is_accepting());
135/// foo.consume(' ');
136/// assert!(!foo.is_accepting());
137/// foo.consume(' ');
138/// assert!(foo.is_accepting());
139/// ```
140///
141/// Resetting the state machine:
142/// ```
143/// use statemachine_macro::*;
144///
145/// #[derive(Debug)]
146/// struct Money;
147///
148/// statemachine! {
149///     pub struct Foo;
150///
151///     enum FooState consumes [Money] from Unpaid accepts [Paid];
152/// }
153///
154/// let mut foo = statemachine_new!(Foo{});
155/// assert!(!foo.is_accepting());
156/// foo.reset(FooState::Paid); // mwahahaha free real estate
157/// assert!(foo.is_accepting());
158/// ```
159///
160/// Advanced consuming with multiple types:
161/// ```
162/// use statemachine_macro::*;
163///
164/// statemachine! {
165///     pub struct Foo {
166///         cheater: bool
167///     }
168///
169///     enum FooState consumes [u32, i32] from Even accepts [Odd];
170///
171///     Even => {
172///         u32 match x => {
173///             if x % 2 == 0 {
174///                 Even
175///             } else {
176///                 Odd
177///             }
178///         },
179///         i32 match v => if *v < 0 {
180///             self.cheater = true;
181///             Even
182///         },
183///         i32 match x => panic!("Hey! Are you trying to cheat?")
184///     },
185///
186///     Odd => {
187///         u32 match x => {
188///             if x % 2 == 0 {
189///                 Even
190///             } else {
191///                 Odd
192///             }
193///         },
194///         i32 match v => if *v < 0 {
195///             self.cheater = true;
196///             Odd
197///         },
198///         i32 match x => panic!("Hey! Are you trying to cheat?")
199///     }
200/// }
201///
202/// let mut foo = statemachine_new!(Foo{ cheater: false });
203/// assert!(!foo.cheater);
204/// assert!(!foo.is_accepting());
205/// foo.consume(5u32);
206/// assert!(!foo.cheater);
207/// assert!(foo.is_accepting());
208/// foo.consume(4u32);
209/// assert!(!foo.cheater);
210/// assert!(!foo.is_accepting());
211/// foo.consume(4u32);
212/// assert!(!foo.cheater);
213/// assert!(!foo.is_accepting());
214/// foo.consume(-3i32);
215/// assert!(foo.cheater);
216/// assert!(!foo.is_accepting());
217/// ```
218///
219/// # Syntax
220///
221/// The following is the syntax of the macro contents. The starting nonterminal is 'statemachine'.
222///
223/// ```txt
224/// statemachine ::= struct-item state-description ( state-behaviors )?
225///
226/// state-description ::=
227///     "enum" ident "consumes" "[" type ( "," type )* "]"
228///                  ( "accepts" "[" ident ( "," ident )* "]" )? ";"
229///
230/// state-behaviors ::= state-behavior ( "," state-behavior )*
231///
232/// state-behavior ::= ident "=>" "{" ( state-transitions )? "}"
233///
234/// state-transitions ::= state-transition ( "," state-transition )*
235///
236/// state-transition ::= transition-trigger | transition-pattern | transition-catchall
237///
238/// transition-pattern ::= type "match" pattern "=>" ( transition-guard )? expr
239///
240/// transition-guard ::= "if" expr
241///
242/// transition-catchall ::= "_" "=>" expr
243///
244/// transition-trigger ::= "@" ( enter-trigger | leave-trigger | loop-trigger )
245///
246/// enter-trigger ::= "enter" "=>" expr
247/// leave-trigger ::= "leave" "=>" expr
248/// loop-trigger ::= "loop" "=>" expr
249/// ```
250#[proc_macro]
251pub fn statemachine(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
252    let statemachine = parse_macro_input!(input as model::StateMachine);
253    statemachine.to_stream().into()
254}
255
256/// Creates a statemachine.
257///
258/// Because statemachines do some magic processing to the underlying struct, regular struct literals do not work. By wrapping your struct literals in `statemachine_new!`, you can circumvent this restriction.
259///
260/// Note that you can alternatively derive `Default` for your statemachine struct.
261///
262/// # Examples
263///
264/// Without `statemachine_new!`:
265/// ```compile_fail
266/// use statemachine_macro::*;
267///
268/// statemachine! {
269///     struct Foo {
270///         bar: i32
271///     }
272///
273///     enum FooState consumes [char] from Start;
274/// }
275///
276/// let _foo = Foo { bar: 3 };
277/// ```
278///
279/// With `statemachine_new!`:
280/// ```
281/// use statemachine_macro::*;
282///
283/// statemachine! {
284///     struct Foo {
285///         bar: i32
286///     }
287///
288///     enum FooState consumes [char] from Start;
289/// }
290///
291/// let _foo = statemachine_new!(Foo { bar: 3 });
292/// ```
293///
294/// By deriving `Default`:
295/// ```
296/// use statemachine_macro::*;
297///
298/// statemachine! {
299///     #[derive(Default)]
300///     struct Foo {
301///         bar: i32
302///     }
303///
304///     enum FooState consumes [char] from Start;
305/// }
306///
307/// let _foo: Foo = Foo { bar: 3, ..Default::default() };
308/// let _baz: Foo = Default::default();
309/// ```
310#[proc_macro]
311pub fn statemachine_new(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
312    let mut expr: syn::ExprStruct = syn::parse(input).expect("Note: Did you mean to use Foo{} for a statemachine Foo with no fields?");
313    expr.fields.push(parse_quote! {
314        _statemachine_state: Default::default()
315    });
316    expr.into_token_stream().into()
317}