bird_machine_macros/
lib.rs

1extern crate proc_macro;
2use proc_macro::TokenStream;
3
4use proc_macro2::Ident;
5use quote::{format_ident, quote, ToTokens};
6use syn::{parse_macro_input, DeriveInput, LitStr};
7
8mod dfa;
9mod nfa;
10
11#[proc_macro_attribute]
12pub fn bird_machine(args: TokenStream, input: TokenStream) -> TokenStream {
13    let input = parse_macro_input!(input as DeriveInput);
14    let input_regex = parse_macro_input!(args as LitStr);
15
16    let input_type_name = &input.ident;
17    let input_lifetimes: Vec<_> = input.generics.lifetimes().collect();
18    let lifetime = match input_lifetimes.as_slice() {
19        [] => quote! { 'unrestricted },
20        [lt] => quote! { #lt },
21        _ => panic!("multiple lifetime generics, what is this, pls to halp"),
22    };
23
24    let machine_fn = format_ident!("bird_machine_for_{}", input_type_name);
25    let machine = build_machine(&input_regex, &machine_fn);
26    eprintln!("{}", &machine);
27
28    let (_, ty_generics, where_clause) = input.generics.split_for_impl();
29
30    let impl_decl = quote! {
31        impl<#lifetime> ::bird_machine::Machine<#lifetime> for #input_type_name #ty_generics #where_clause
32    };
33    let original_regex = quote! {
34        const ORIGINAL_REGEX: &'static str = #input_regex;
35    };
36    let captures = quote! {
37        fn captures(text: &#lifetime str) -> Option<Self> {
38            todo!()
39        }
40    };
41    let captures_iter = quote! {
42        type CaptureIterator = ::std::iter::Empty<Self>;
43        fn captures_iter(text: &#lifetime str) -> Self::CaptureIterator {
44            todo!()
45        }
46    };
47    let find = quote! {
48        fn find(text: &#lifetime str) -> Option<::bird_machine::Match<#lifetime>> {
49            todo!()
50        }
51    };
52    let find_at = quote! {
53        fn find_at(text: &#lifetime str, start: usize) -> Option<::bird_machine::Match<#lifetime>> {
54            todo!()
55        }
56    };
57    let find_iter = quote! {
58        type FindIterator = ::std::iter::Empty<::bird_machine::Match<#lifetime>>;
59        fn find_iter(text: &#lifetime str) -> Self::FindIterator {
60            todo!()
61        }
62    };
63    let is_match = quote! {
64        fn is_match(text: &#lifetime str) -> bool {
65            #machine_fn(text)
66        }
67    };
68    let is_match_at = quote! {
69        fn is_match_at(text: &#lifetime str, start: usize) -> bool {
70            todo!()
71        }
72    };
73    let replace = quote! {
74        fn replace(
75            text: &#lifetime str,
76            rep: impl ::bird_machine::Replacer<#lifetime, Self>,
77        ) -> ::std::borrow::Cow<#lifetime, str> {
78            todo!()
79        }
80    };
81    let replace_all = quote! {
82        fn replace_all(
83            text: &#lifetime str,
84            rep: impl ::bird_machine::Replacer<#lifetime, Self>,
85        ) -> ::std::borrow::Cow<#lifetime, str> {
86            todo!()
87        }
88    };
89    let replacen = quote! {
90        fn replacen(
91            text: &#lifetime str,
92            limit: usize,
93            rep: impl ::bird_machine::Replacer<#lifetime, Self>,
94        ) -> ::std::borrow::Cow<#lifetime, str> {
95            todo!()
96        }
97    };
98    let split = quote! {
99        type SplitIterator = ::std::iter::Empty<&#lifetime str>;
100        fn split(text: &#lifetime str) -> Self::SplitIterator {
101            todo!()
102        }
103    };
104    let splitn = quote! {
105        type SplitNIterator = ::std::iter::Empty<&#lifetime str>;
106        fn splitn(text: &#lifetime str, limit: usize) -> Self::SplitNIterator {
107            todo!()
108        }
109    };
110
111    let tokens = quote! {
112        #input
113
114        #machine
115
116        #impl_decl {
117            #original_regex
118            #captures
119            #captures_iter
120            #find
121            #find_at
122            #find_iter
123            #is_match
124            #is_match_at
125            #replace
126            #replace_all
127            #replacen
128            #split
129            #splitn
130        }
131    };
132
133    eprintln!(
134        "{impl_decl} {{\n\n\
135            {original_regex}\n\n\
136            {captures}\n\n\
137            {captures_iter}\n\n\
138            {find}\n\n\
139            {find_at}\n\n\
140            {find_iter}\n\n\
141            {is_match}\n\n\
142            {is_match_at}\n\n\
143            {replace}\n\n\
144            {replace_all}\n\n\
145            {replacen}\n\n\
146            {split}\n\n\
147            {splitn}\n\n\
148        }}",
149        impl_decl = impl_decl,
150        original_regex = original_regex,
151        captures = captures,
152        captures_iter = captures_iter,
153        find = find,
154        find_at = find_at,
155        find_iter = find_iter,
156        is_match = is_match,
157        is_match_at = is_match_at,
158        replace = replace,
159        replace_all = replace_all,
160        replacen = replacen,
161        split = split,
162        splitn = splitn,
163    );
164
165    tokens.into()
166}
167
168fn build_machine(regex: &LitStr, fn_name: &Ident) -> proc_macro2::TokenStream {
169    let regex_text = regex.value();
170    let regex_ir = regex_syntax::Parser::new().parse(&regex_text);
171    let regex_ir = match regex_ir {
172        Ok(x) => x,
173        Err(err) => panic!("error compiling regex {}: {}", regex.to_token_stream(), err),
174    };
175
176    let built_nfa = nfa::NFA::from(&regex_ir);
177    let dfa = dfa::DFA::from(built_nfa);
178
179    quote! {
180        fn #fn_name(input: &str) -> bool {
181            #dfa
182        }
183    }
184}