uwurandom_proc_macros/
lib.rs1use convert_case::{Case, Casing};
2use proc_macro::{Span, TokenStream};
3use quote::quote;
4use syn::{Ident, LitChar};
5
6use crate::json::MarkovArr;
7
8mod json;
9
10#[proc_macro]
11pub fn gen_fsm(item: TokenStream) -> TokenStream {
12 let input: Vec<MarkovArr> = serde_json::from_str(&format!("[{}]", item)).unwrap();
13 let mut match_arms = quote!();
14 let mut variants = quote!();
15 let mut random_selection_match_arms = quote!();
16 for (idx, state) in input.iter().enumerate() {
17 let name = to_ident(&state.name);
18 let name_lit = &state.name;
19 let idx = idx as u32;
20 random_selection_match_arms = quote!(
21 #random_selection_match_arms
22 #idx => (Self::#name, #name_lit),
23 );
24 variants = quote!(
25 #variants
26 #name,
27 );
28 let mut inner_match_arms = quote!();
29 if state.total_probability == 1 {
30 let choice = &state.choices[0];
31 let next_state = to_ident(&input[choice.next_ngram].name);
32 let next_char = LitChar::new(choice.next_char, Span::call_site().into());
33 match_arms = quote!(
34 #match_arms
35 Self::#name => (Self::#next_state, #next_char),
36 );
37 continue;
38 }
39 for choice in &state.choices {
40 let next_state = to_ident(&input[choice.next_ngram].name);
41 let cumulative_probability = choice.cumulative_probability - 1;
42 let next_char = LitChar::new(choice.next_char, Span::call_site().into());
43 inner_match_arms = quote!(
44 #inner_match_arms
45 0..=#cumulative_probability => (Self::#next_state, #next_char),
46 )
47 }
48 let total_probability = state.total_probability;
49 match_arms = quote!(
50 #match_arms
51 Self::#name => match rng.next_u32() % #total_probability {
52 #inner_match_arms
53 _ => unreachable!(),
54 },
55 );
56 }
57 let variant_count = input.len() as u32;
58 quote!(
59 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
60 pub enum StateMachine {
61 #variants
62 }
63 impl StateMachine {
64 pub fn generate(self, mut rng: impl ::rand_core::RngCore) -> (Self, char) {
65 match self {
66 #match_arms
67 }
68 }
69 pub fn new_random(mut rng: impl ::rand_core::RngCore) -> (Self, &'static str) {
70 match rng.next_u32() % #variant_count {
71 #random_selection_match_arms
72 _ => unreachable!()
73 }
74 }
75 }
76 )
77 .into()
78}
79
80fn to_ident(name: &str) -> Ident {
81 Ident::new(
82 &name
83 .replace(' ', " space ")
84 .replace(';', " semicolon ") .replace('!', " exclamation ")
86 .replace(',', " comma ")
87 .replace('.', " period ") .to_case(Case::Pascal),
89 Span::call_site().into(),
90 )
91}