char_lex_macro/
lib.rs

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
13//! # CHAR-LEX-MACRO
14//!
15//! The `proc_macro_attribute` crate for [`Char-lex`].
16//!
17//! [`Char-lex`]: https://docs.rs/char-lex/
18
19use proc_macro::TokenStream;
20use quote::{quote, ToTokens};
21use syn::fold::Fold;
22use syn::{self, parse_macro_input};
23
24/// The `token` attribute macro.
25#[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}