1#![deny(
2 missing_docs,
3 missing_debug_implementations,
4 missing_copy_implementations,
5 trivial_casts,
6 trivial_numeric_casts,
7 unsafe_code,
8 unstable_features,
9 unused_import_braces,
10 unused_qualifications
11)]
12
13use proc_macro::TokenStream;
20use quote::{quote, ToTokens};
21use syn::fold::Fold;
22use syn::{self, parse_macro_input};
23
24#[proc_macro_attribute]
26pub fn token(_unused: TokenStream, input: TokenStream) -> TokenStream {
27 let item_enum = parse_macro_input!(input as syn::ItemEnum);
28
29 let mut token_enum = TokenEnum::new();
30 let item_enum = token_enum.fold_item_enum(item_enum);
31
32 let mut token_stream = TokenStream::from(quote!(#item_enum));
33 token_stream.extend(token_enum.to_token_stream());
34
35 token_stream
36}
37
38enum CharExpr {
39 Char(char),
40 Chars(Vec<char>),
41}
42
43struct TokenEnum {
44 ident: Option<syn::Ident>,
45 exprs: Vec<(syn::Ident, CharExpr)>,
46 fields: Vec<(syn::Ident, syn::Type)>,
47}
48
49impl TokenEnum {
50 fn new() -> Self {
51 Self {
52 ident: None,
53 exprs: Vec::new(),
54 fields: Vec::new(),
55 }
56 }
57
58 fn to_token_stream(&mut self) -> TokenStream {
59 let ident = self.ident.as_ref().unwrap();
60
61 let mut exprs = Vec::new();
62 for expr in &self.exprs {
63 exprs.push(parse_expr(expr));
64 }
65
66 let mut impls = Vec::new();
67 for (_, ty) in &self.fields {
68 impls.push(quote!(#ty: TokenTrait,))
69 }
70 let mut imp = quote!();
71 if impls.len() > 0 {
72 imp = quote!(where #(#impls)*);
73 }
74
75 let fields = parse_fields(&self.fields);
76
77 TokenStream::from(quote!(
78 impl TokenTrait for #ident {
79 fn match_char(c: char) -> Option<Self> #imp {
80 match c {
81 #(#exprs)*
82 #(#fields)*
83 }
84 }
85 }
86 ))
87 }
88}
89
90impl Fold for TokenEnum {
91 fn fold_item_enum(&mut self, mut i: syn::ItemEnum) -> syn::ItemEnum {
92 self.ident = Some(i.ident.clone());
93 for variant in &mut i.variants {
94 handle_variant(variant, &mut self.exprs, &mut self.fields);
95 }
96 i
97 }
98}
99
100fn handle_variant(
101 variant: &mut syn::Variant,
102 exprs: &mut Vec<(syn::Ident, CharExpr)>,
103 fields: &mut Vec<(syn::Ident, syn::Type)>,
104) {
105 match &variant.fields {
106 syn::Fields::Named(_) => panic!("A Token-Enum can't have named fields!"),
107 syn::Fields::Unnamed(field) => {
108 if field.unnamed.len() != 1 {
109 panic!("A Token-Enum can only have one unnamed field!");
110 }
111 fields.push((variant.ident.clone(), field.unnamed[0].ty.clone()));
112 }
113 syn::Fields::Unit => {
114 if let Some((_, expr)) = &variant.discriminant {
115 match expr {
116 syn::Expr::Array(syn::ExprArray {
117 attrs: _,
118 bracket_token: _,
119 elems,
120 }) => {
121 let mut chars: Vec<char> = Vec::new();
122 for expr in elems {
123 match expr {
124 syn::Expr::Lit(syn::ExprLit{ attrs: _, lit: syn::Lit::Char(c) }) => chars.push(c.value()),
125 _ => panic!(
126 "A Token-Enum can only be assigned to a single 'char' or multiple [chars]!"
127 ),
128 }
129 }
130 exprs.push((variant.ident.clone(), CharExpr::Chars(chars)));
131 }
132 syn::Expr::Lit(syn::ExprLit {
133 attrs: _,
134 lit: syn::Lit::Char(c),
135 }) => exprs.push((variant.ident.clone(), CharExpr::Char(c.value()))),
136 _ => panic!(
137 "A Token-Enum can only be assigned to a single 'char' or multiple [chars]!"
138 ),
139 };
140 variant.discriminant = None;
141 } else {
142 panic!("A Token-Enum with no fields needs to be assigned to a single 'char' or multiple [chars]!");
143 }
144 }
145 };
146}
147
148fn parse_expr((ident, expr): &(syn::Ident, CharExpr)) -> impl ToTokens {
149 match expr {
150 CharExpr::Char(c) => quote!(#c => Some(Self::#ident),),
151 CharExpr::Chars(chars) => quote!(#(#chars)|* => Some(Self::#ident),),
152 }
153}
154
155fn parse_fields(fields: &Vec<(syn::Ident, syn::Type)>) -> Vec<impl ToTokens> {
156 let mut output = vec![quote!(_ =>)];
157 if fields.is_empty() {
158 output.push(quote!(None,));
159 } else {
160 for (i, field) in fields.iter().enumerate() {
161 if i != 0 {
162 output.push(quote!(else));
163 }
164 output.push(parse_field(field).to_token_stream());
165 }
166 output.push(quote!(else { None }));
167 }
168 output
169}
170
171fn parse_field((ident, field): &(syn::Ident, syn::Type)) -> impl ToTokens {
172 quote!(
173 if let Some(field) = #field::match_char(c) {
174 Some(Self::#ident(field))
175 }
176 )
177}