Finite state machines in rust; bendns fork to add types.
Diffstat (limited to 'rust_fsm_dsl/src/parser.rs')
-rw-r--r--rust_fsm_dsl/src/parser.rs147
1 files changed, 147 insertions, 0 deletions
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,
+ })
+ }
+}