Finite state machines in rust; bendns fork to add types.
-rw-r--r--CHANGELOG.md3
-rw-r--r--README.md8
-rw-r--r--rust-fsm-dsl/Cargo.toml1
-rw-r--r--rust-fsm-dsl/src/lib.rs25
-rw-r--r--rust-fsm-dsl/src/parser.rs63
-rw-r--r--rust-fsm/src/lib.rs8
-rw-r--r--rust-fsm/tests/simple.rs4
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.
diff --git a/README.md b/README.md
index d93b5b9..3523dbc 100644
--- a/README.md
+++ b/README.md
@@ -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,