Finite state machines in rust; bendns fork to add types.
| -rw-r--r-- | CHANGELOG.md | 3 | ||||
| -rw-r--r-- | README.md | 8 | ||||
| -rw-r--r-- | rust-fsm-dsl/Cargo.toml | 1 | ||||
| -rw-r--r-- | rust-fsm-dsl/src/lib.rs | 25 | ||||
| -rw-r--r-- | rust-fsm-dsl/src/parser.rs | 63 | ||||
| -rw-r--r-- | rust-fsm/src/lib.rs | 8 | ||||
| -rw-r--r-- | rust-fsm/tests/simple.rs | 4 |
7 files changed, 29 insertions, 83 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 676cb73..6376006 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,9 @@ adheres to [Semantic Versioning][semver]. enum is generated instead of using `()` for the sake of uniformity. No attributes (e.g. `derive` and `repr`) are applied on an empty `Output`, because many of them are simply not designed to work this way. +* It is now possible to use proper Rust attributes like `#[derive(Debug)]`, + `#[repr(C)]`, etc (any attribute you want really). This replaces the original + way this macro had for using derives and specifying representation. ## Added * A type alias `StateMachine` for `rust_fsm::StateMachine<Impl>` is now generated inside the said module. @@ -56,7 +56,8 @@ The DSL is parsed by the `state_machine` macro. Here is a little example. use rust_fsm::*; state_machine! { - derive(Debug) + #[derive(Debug)] + #[repr(C)] circuit_breaker(Closed) Closed(Unsuccessful) => Open [SetupTimer], @@ -71,7 +72,10 @@ state_machine! { This code sample: * Defines a state machine called `circuit_breaker`; -* Derives the `Debug` trait for it (the `derive` section is optional); +* Derives the `Debug` trait for it. All attributes you use here (like + `#[repr(C)]`) will be applied to all types generated by this macro. If you + want to apply attributes or a docstring to the `mod` generated by this macro, + just put it before the macro invocation. * Sets the initial state of this state machine to `Closed`; * Defines state transitions. For example: on receiving the `Successful` input when in the `HalfOpen` state, the machine must move to the `Closed` diff --git a/rust-fsm-dsl/Cargo.toml b/rust-fsm-dsl/Cargo.toml index 63468c2..1155858 100644 --- a/rust-fsm-dsl/Cargo.toml +++ b/rust-fsm-dsl/Cargo.toml @@ -16,5 +16,6 @@ edition = "2018" proc-macro = true [dependencies] +proc-macro2 = "1" syn = "1" quote = "1" diff --git a/rust-fsm-dsl/src/lib.rs b/rust-fsm-dsl/src/lib.rs index 3d82364..ce1e5b5 100644 --- a/rust-fsm-dsl/src/lib.rs +++ b/rust-fsm-dsl/src/lib.rs @@ -5,8 +5,8 @@ extern crate proc_macro; use proc_macro::TokenStream; -use quote::quote; -use std::collections::BTreeSet; +use quote::{quote, ToTokens}; +use std::{collections::BTreeSet, iter::FromIterator}; use syn::{parse_macro_input, Ident}; mod parser; @@ -26,17 +26,11 @@ struct Transition<'a> { pub fn state_machine(tokens: TokenStream) -> TokenStream { let input = parse_macro_input!(tokens as parser::StateMachineDef); - let derives = if let Some(derives) = input.derives { - quote! { #[derive(#(#derives,)*)] } - } else { - quote! {} - }; - - let type_repr = if let Some(true) = input.repr_c { - quote! { #[repr(C)] } - } else { - quote! {} - }; + let attrs = input + .attributes + .into_iter() + .map(ToTokens::into_token_stream); + let attrs = proc_macro2::TokenStream::from_iter(attrs); if input.transitions.is_empty() { let output = quote! { @@ -103,11 +97,6 @@ pub fn state_machine(tokens: TokenStream) -> TokenStream { } } - let attrs = quote! { - #derives - #type_repr - }; - // Many attrs and derives may work incorrectly (or simply not work) for // empty enums, so we just skip them altogether if the output alphabet is // empty. diff --git a/rust-fsm-dsl/src/parser.rs b/rust-fsm-dsl/src/parser.rs index 3685200..8b0d957 100644 --- a/rust-fsm-dsl/src/parser.rs +++ b/rust-fsm-dsl/src/parser.rs @@ -2,7 +2,7 @@ use syn::{ braced, bracketed, parenthesized, parse::{Error, Parse, ParseStream, Result}, token::{Bracket, Paren}, - Ident, Token, Visibility, + Attribute, Ident, Token, Visibility, }; mod kw { @@ -106,58 +106,6 @@ impl Parse for TransitionDef { } } -struct ReprC { - repr_c: Option<bool>, -} - -impl Parse for ReprC { - fn parse(input: ParseStream) -> Result<Self> { - let lookahead = input.lookahead1(); - if lookahead.peek(kw::repr_c) { - let kw_repr_c = input.parse::<kw::repr_c>()?; - let entries_content; - parenthesized!(entries_content in input); - match entries_content.parse::<syn::Lit>() { - Ok(syn::Lit::Bool(b)) => { - return Ok(ReprC { - repr_c: Some(b.value()), - }); - } - _ => { - return Err(Error::new_spanned(kw_repr_c, "Invalid repr_c argument")); - } - } - } - Ok(ReprC { repr_c: None }) - } -} - -struct Derives { - derives: Option<Vec<Ident>>, -} - -impl Parse for Derives { - fn parse(input: ParseStream) -> Result<Self> { - let lookahead = input.lookahead1(); - if lookahead.peek(kw::derive) { - let kw_derive = input.parse::<kw::derive>()?; - let entries_content; - parenthesized!(entries_content in input); - let entries: Vec<_> = entries_content - .parse_terminated::<_, Token![,]>(Ident::parse)? - .into_iter() - .collect(); - if entries.is_empty() { - return Err(Error::new_spanned(kw_derive, "Derive list cannot be empty")); - } - return Ok(Derives { - derives: Some(entries), - }); - } - Ok(Derives { derives: None }) - } -} - /// Parses the whole state machine definition in the following form (example): /// /// ```rust,ignore @@ -178,14 +126,12 @@ pub struct StateMachineDef { pub name: Ident, pub initial_state: Ident, pub transitions: Vec<TransitionDef>, - pub derives: Option<Vec<Ident>>, - pub repr_c: Option<bool>, + pub attributes: Vec<Attribute>, } impl Parse for StateMachineDef { fn parse(input: ParseStream) -> Result<Self> { - let Derives { derives } = input.parse()?; - let ReprC { repr_c } = input.parse()?; + let attributes = Attribute::parse_outer(input)?; let visibility = input.parse()?; let name = input.parse()?; @@ -204,8 +150,7 @@ impl Parse for StateMachineDef { name, initial_state, transitions, - derives, - repr_c, + attributes, }) } } diff --git a/rust-fsm/src/lib.rs b/rust-fsm/src/lib.rs index 445fa2b..337cbaa 100644 --- a/rust-fsm/src/lib.rs +++ b/rust-fsm/src/lib.rs @@ -53,7 +53,8 @@ //! use rust_fsm::*; //! //! state_machine! { -//! derive(Debug) +//! #[derive(Debug)] +//! #[repr(C)] //! circuit_breaker(Closed) //! //! Closed(Unsuccessful) => Open [SetupTimer], @@ -68,7 +69,10 @@ //! This code sample: //! //! * Defines a state machine called `circuit_breaker`; -//! * Derives the `Debug` trait for it (the `derive` section is optional); +//! * Derives the `Debug` trait for it. All attributes you use here (like +//! `#[repr(C)]`) will be applied to all types generated by this macro. If you +//! want to apply attributes or a docstring to the `mod` generated by this +//! macro, just put it before the macro invocation. //! * Sets the initial state of this state machine to `Closed`; //! * Defines state transitions. For example: on receiving the `Successful` //! input when in the `HalfOpen` state, the machine must move to the `Closed` diff --git a/rust-fsm/tests/simple.rs b/rust-fsm/tests/simple.rs index c16dab8..8754b92 100644 --- a/rust-fsm/tests/simple.rs +++ b/rust-fsm/tests/simple.rs @@ -1,8 +1,8 @@ use rust_fsm::*; state_machine! { - derive(Debug) - repr_c(true) + #[derive(Debug)] + #[repr(C)] door(Open) Open(Key) => Closed, |