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}