derive_finite_automaton_derive/
macro.rs1mod trie;
2
3use proc_macro::TokenStream;
4use proc_macro2::Span;
5use quote::{format_ident, quote};
6use syn::{parse_macro_input, Arm, DeriveInput, Expr, Ident};
7use trie::Trie;
8
9pub(crate) const NO_STATE_NAME: &str = "None";
10
11#[proc_macro_derive(
12 FiniteAutomataConstructor,
13 attributes(automaton_mappings, automaton_item_type)
14)]
15pub fn stateful_trie_constructor(input: TokenStream) -> TokenStream {
16 let input = parse_macro_input!(input as DeriveInput);
17 let name = input.ident;
18
19 let mut mappings = None::<trie::Mappings>;
20
21 let automaton_mappings = input
22 .attrs
23 .iter()
24 .filter(|attr| attr.path().is_ident("automaton_mappings"));
25
26 for mapping in automaton_mappings {
27 let m = mapping.parse_args::<trie::Mappings>().unwrap();
28 if let Some(em) = mappings.as_mut() {
29 em.extend(m);
30 } else {
31 mappings = Some(m);
32 }
33 }
34
35 let item_type: Ident = input
37 .attrs
38 .iter()
39 .find(|attr| attr.path().is_ident("automaton_item_type"))
40 .map(|attr| attr.parse_args().unwrap())
41 .unwrap_or_else(|| Ident::new("char", Span::call_site()));
42
43 let trie: Trie<crate::trie::Item, Expr> = mappings.unwrap().into();
44
45 let (mut states, mut arms): (Vec<Ident>, Vec<Arm>) = Default::default();
46
47 let no_state_ident = Ident::new(NO_STATE_NAME, Span::call_site());
48
49 let states_ident = format_ident!("{}States", name.to_string().to_uppercase());
50
51 trie::expand_trie(
52 &trie,
53 &mut arms,
54 &mut states,
55 &no_state_ident,
56 &states_ident,
57 );
58
59 let output = quote! {
60 const _: () = {
61 #[derive(PartialEq, Eq, Clone, Debug)]
62 pub enum #states_ident {
63 #no_state_ident,
64 #( #states ),*
65 }
66
67 impl ::derive_finite_automaton::FiniteAutomataConstructor for #name {
68 type FiniteAutomata = #states_ident;
69
70 fn new_automaton() -> #states_ident {
71 #states_ident::#no_state_ident
72 }
73 }
74
75 impl ::derive_finite_automaton::FiniteAutomata<#name> for #states_ident {
76 type State = #states_ident;
77 type Item = #item_type;
78
79 fn get_next(self, item: Self::Item) -> ::derive_finite_automaton::GetNextResult<#name, #states_ident> {
80 match (&self, item) {
81 #( #arms )*
82 }
83 }
84 }
85 };
86 };
87
88 output.into()
89}