Skip to content

Commit

Permalink
allow using proper Rust attributes on generated structs
Browse files Browse the repository at this point in the history
  • Loading branch information
eugene-babichenko committed May 12, 2024
1 parent 890ff8b commit 95bf83a
Show file tree
Hide file tree
Showing 7 changed files with 29 additions and 83 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand All @@ -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`
Expand Down
1 change: 1 addition & 0 deletions rust-fsm-dsl/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@ edition = "2018"
proc-macro = true

[dependencies]
proc-macro2 = "1"
syn = "1"
quote = "1"
25 changes: 7 additions & 18 deletions rust-fsm-dsl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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! {
Expand Down Expand Up @@ -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.
Expand Down
63 changes: 4 additions & 59 deletions rust-fsm-dsl/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand All @@ -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()?;
Expand All @@ -204,8 +150,7 @@ impl Parse for StateMachineDef {
name,
initial_state,
transitions,
derives,
repr_c,
attributes,
})
}
}
8 changes: 6 additions & 2 deletions rust-fsm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@
//! use rust_fsm::*;
//!
//! state_machine! {
//! derive(Debug)
//! #[derive(Debug)]
//! #[repr(C)]
//! circuit_breaker(Closed)
//!
//! Closed(Unsuccessful) => Open [SetupTimer],
Expand All @@ -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`
Expand Down
4 changes: 2 additions & 2 deletions rust-fsm/tests/simple.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use rust_fsm::*;

state_machine! {
derive(Debug)
repr_c(true)
#[derive(Debug)]
#[repr(C)]
door(Open)

Open(Key) => Closed,
Expand Down

0 comments on commit 95bf83a

Please sign in to comment.