Finite state machines in rust; bendns fork to add types.
rust_fsm_dsl: move the parser to a separate submodule
| -rw-r--r-- | rust_fsm_dsl/src/lib.rs | 151 | ||||
| -rw-r--r-- | rust_fsm_dsl/src/parser.rs | 147 |
2 files changed, 150 insertions, 148 deletions
diff --git a/rust_fsm_dsl/src/lib.rs b/rust_fsm_dsl/src/lib.rs index 18b9b00..cfa93b3 100644 --- a/rust_fsm_dsl/src/lib.rs +++ b/rust_fsm_dsl/src/lib.rs @@ -7,154 +7,9 @@ extern crate proc_macro; use proc_macro::TokenStream; use quote::quote; use std::collections::HashSet; -use syn::{ - braced, bracketed, parenthesized, - parse::{Error, Parse, ParseStream, Result}, - parse_macro_input, - token::{Bracket, Paren}, - Ident, Token, Visibility, -}; +use syn::{parse_macro_input, Ident}; -/// The output of a state transition -struct Output(Option<Ident>); - -impl Parse for Output { - fn parse(input: ParseStream) -> Result<Self> { - if input.lookahead1().peek(Bracket) { - let output_content; - bracketed!(output_content in input); - Ok(Self(Some(output_content.parse()?))) - } else { - Ok(Self(None)) - } - } -} - -impl Into<Option<Ident>> for Output { - fn into(self) -> Option<Ident> { - self.0 - } -} - -/// Represents a part of state transition without the initial state. The `Parse` -/// trait is implemented for the compact form. -struct TransitionEntry { - input_value: Ident, - final_state: Ident, - output: Option<Ident>, -} - -impl Parse for TransitionEntry { - fn parse(input: ParseStream) -> Result<Self> { - let input_value = input.parse()?; - input.parse::<Token![=>]>()?; - let final_state = input.parse()?; - let output = input.parse::<Output>()?.into(); - Ok(Self { - input_value, - final_state, - output, - }) - } -} - -/// Parses the transition in any of the possible formats. -struct TransitionDef { - initial_state: Ident, - transitions: Vec<TransitionEntry>, -} - -impl Parse for TransitionDef { - fn parse(input: ParseStream) -> Result<Self> { - let initial_state = input.parse()?; - // Parse the transition in the simple format - // InitialState(Input) => ResultState [Output] - let transitions = if input.lookahead1().peek(Paren) { - let input_content; - parenthesized!(input_content in input); - let input_value = input_content.parse()?; - input.parse::<Token![=>]>()?; - let final_state = input.parse()?; - let output = input.parse::<Output>()?.into(); - - vec![TransitionEntry { - input_value, - final_state, - output, - }] - } else { - // Parse the transition in the compact format - // InitialState => { - // Input1 => State1, - // Input2 => State2 [Output] - // } - input.parse::<Token![=>]>()?; - let entries_content; - braced!(entries_content in input); - - let entries: Vec<_> = entries_content - .parse_terminated::<_, Token![,]>(TransitionEntry::parse)? - .into_iter() - .collect(); - if entries.is_empty() { - return Err(Error::new_spanned( - initial_state, - "No transitions provided for a compact representation", - )); - } - entries - }; - Ok(Self { - initial_state, - transitions, - }) - } -} - -/// Parses the whole state machine definition in the following form (example): -/// -/// ```rust,ignore -/// state_machine! { -/// CircuitBreaker(Closed) -/// -/// Closed(Unsuccessful) => Open [SetupTimer], -/// Open(TimerTriggered) => HalfOpen, -/// HalfOpen => { -/// Successful => Closed, -/// Unsuccessful => Open [SetupTimer] -/// } -/// } -/// ``` -struct StateMachineDef { - /// The visibility modifier (applies to all generated items) - visibility: Visibility, - name: Ident, - initial_state: Ident, - transitions: Vec<TransitionDef>, -} - -impl Parse for StateMachineDef { - fn parse(input: ParseStream) -> Result<Self> { - let visibility = input.parse()?; - let name = input.parse()?; - - let initial_state_content; - parenthesized!(initial_state_content in input); - let initial_state = initial_state_content.parse()?; - - let transitions = input - .parse_terminated::<_, Token![,]>(TransitionDef::parse)? - .into_iter() - .collect(); - - Ok(Self { - visibility, - name, - initial_state, - transitions, - }) - } -} +mod parser; /// The full information about a state transition. Used to unify the /// represantion of the simple and the compact forms. @@ -167,7 +22,7 @@ struct Transition<'a> { #[proc_macro] pub fn state_machine(tokens: TokenStream) -> TokenStream { - let input = parse_macro_input!(tokens as StateMachineDef); + let input = parse_macro_input!(tokens as parser::StateMachineDef); if input.transitions.is_empty() { let output = quote! { diff --git a/rust_fsm_dsl/src/parser.rs b/rust_fsm_dsl/src/parser.rs new file mode 100644 index 0000000..0eb3f7c --- /dev/null +++ b/rust_fsm_dsl/src/parser.rs @@ -0,0 +1,147 @@ +use syn::{ + braced, bracketed, parenthesized, + parse::{Error, Parse, ParseStream, Result}, + token::{Bracket, Paren}, + Ident, Token, Visibility, +}; + +/// The output of a state transition +pub struct Output(Option<Ident>); + +impl Parse for Output { + fn parse(input: ParseStream) -> Result<Self> { + if input.lookahead1().peek(Bracket) { + let output_content; + bracketed!(output_content in input); + Ok(Self(Some(output_content.parse()?))) + } else { + Ok(Self(None)) + } + } +} + +impl Into<Option<Ident>> for Output { + fn into(self) -> Option<Ident> { + self.0 + } +} + +/// Represents a part of state transition without the initial state. The `Parse` +/// trait is implemented for the compact form. +pub struct TransitionEntry { + pub input_value: Ident, + pub final_state: Ident, + pub output: Option<Ident>, +} + +impl Parse for TransitionEntry { + fn parse(input: ParseStream) -> Result<Self> { + let input_value = input.parse()?; + input.parse::<Token![=>]>()?; + let final_state = input.parse()?; + let output = input.parse::<Output>()?.into(); + Ok(Self { + input_value, + final_state, + output, + }) + } +} + +/// Parses the transition in any of the possible formats. +pub struct TransitionDef { + pub initial_state: Ident, + pub transitions: Vec<TransitionEntry>, +} + +impl Parse for TransitionDef { + fn parse(input: ParseStream) -> Result<Self> { + let initial_state = input.parse()?; + // Parse the transition in the simple format + // InitialState(Input) => ResultState [Output] + let transitions = if input.lookahead1().peek(Paren) { + let input_content; + parenthesized!(input_content in input); + let input_value = input_content.parse()?; + input.parse::<Token![=>]>()?; + let final_state = input.parse()?; + let output = input.parse::<Output>()?.into(); + + vec![TransitionEntry { + input_value, + final_state, + output, + }] + } else { + // Parse the transition in the compact format + // InitialState => { + // Input1 => State1, + // Input2 => State2 [Output] + // } + input.parse::<Token![=>]>()?; + let entries_content; + braced!(entries_content in input); + + let entries: Vec<_> = entries_content + .parse_terminated::<_, Token![,]>(TransitionEntry::parse)? + .into_iter() + .collect(); + if entries.is_empty() { + return Err(Error::new_spanned( + initial_state, + "No transitions provided for a compact representation", + )); + } + entries + }; + Ok(Self { + initial_state, + transitions, + }) + } +} + +/// Parses the whole state machine definition in the following form (example): +/// +/// ```rust,ignore +/// state_machine! { +/// CircuitBreaker(Closed) +/// +/// Closed(Unsuccessful) => Open [SetupTimer], +/// Open(TimerTriggered) => HalfOpen, +/// HalfOpen => { +/// Successful => Closed, +/// Unsuccessful => Open [SetupTimer] +/// } +/// } +/// ``` +pub struct StateMachineDef { + /// The visibility modifier (applies to all generated items) + pub visibility: Visibility, + pub name: Ident, + pub initial_state: Ident, + pub transitions: Vec<TransitionDef>, +} + +impl Parse for StateMachineDef { + fn parse(input: ParseStream) -> Result<Self> { + let visibility = input.parse()?; + let name = input.parse()?; + + let initial_state_content; + parenthesized!(initial_state_content in input); + let initial_state = initial_state_content.parse()?; + + let transitions = input + .parse_terminated::<_, Token![,]>(TransitionDef::parse)? + .into_iter() + .collect(); + + Ok(Self { + visibility, + name, + initial_state, + transitions, + }) + } +} |