extern crate proc_macro; use proc_macro::TokenStream; use proc_macro2::Ident; use quote::{format_ident, quote, ToTokens}; use syn::{parse_macro_input, DeriveInput, LitStr}; mod dfa; mod nfa; #[proc_macro_attribute] pub fn bird_machine(args: TokenStream, input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); let input_regex = parse_macro_input!(args as LitStr); let input_type_name = &input.ident; let input_lifetimes: Vec<_> = input.generics.lifetimes().collect(); let lifetime = match input_lifetimes.as_slice() { [] => quote! { 'unrestricted }, [lt] => quote! { #lt }, _ => panic!("multiple lifetime generics, what is this, pls to halp"), }; let machine_fn = format_ident!("bird_machine_for_{}", input_type_name); let machine = build_machine(&input_regex, &machine_fn); eprintln!("{}", &machine); let (_, ty_generics, where_clause) = input.generics.split_for_impl(); let impl_decl = quote! { impl<#lifetime> ::bird_machine::Machine<#lifetime> for #input_type_name #ty_generics #where_clause }; let original_regex = quote! { const ORIGINAL_REGEX: &'static str = #input_regex; }; let captures = quote! { fn captures(text: &#lifetime str) -> Option { todo!() } }; let captures_iter = quote! { type CaptureIterator = ::std::iter::Empty; fn captures_iter(text: &#lifetime str) -> Self::CaptureIterator { todo!() } }; let find = quote! { fn find(text: &#lifetime str) -> Option<::bird_machine::Match<#lifetime>> { todo!() } }; let find_at = quote! { fn find_at(text: &#lifetime str, start: usize) -> Option<::bird_machine::Match<#lifetime>> { todo!() } }; let find_iter = quote! { type FindIterator = ::std::iter::Empty<::bird_machine::Match<#lifetime>>; fn find_iter(text: &#lifetime str) -> Self::FindIterator { todo!() } }; let is_match = quote! { fn is_match(text: &#lifetime str) -> bool { #machine_fn(text) } }; let is_match_at = quote! { fn is_match_at(text: &#lifetime str, start: usize) -> bool { todo!() } }; let replace = quote! { fn replace( text: &#lifetime str, rep: impl ::bird_machine::Replacer<#lifetime, Self>, ) -> ::std::borrow::Cow<#lifetime, str> { todo!() } }; let replace_all = quote! { fn replace_all( text: &#lifetime str, rep: impl ::bird_machine::Replacer<#lifetime, Self>, ) -> ::std::borrow::Cow<#lifetime, str> { todo!() } }; let replacen = quote! { fn replacen( text: &#lifetime str, limit: usize, rep: impl ::bird_machine::Replacer<#lifetime, Self>, ) -> ::std::borrow::Cow<#lifetime, str> { todo!() } }; let split = quote! { type SplitIterator = ::std::iter::Empty<&#lifetime str>; fn split(text: &#lifetime str) -> Self::SplitIterator { todo!() } }; let splitn = quote! { type SplitNIterator = ::std::iter::Empty<&#lifetime str>; fn splitn(text: &#lifetime str, limit: usize) -> Self::SplitNIterator { todo!() } }; let tokens = quote! { #input #machine #impl_decl { #original_regex #captures #captures_iter #find #find_at #find_iter #is_match #is_match_at #replace #replace_all #replacen #split #splitn } }; eprintln!( "{impl_decl} {{\n\n\ {original_regex}\n\n\ {captures}\n\n\ {captures_iter}\n\n\ {find}\n\n\ {find_at}\n\n\ {find_iter}\n\n\ {is_match}\n\n\ {is_match_at}\n\n\ {replace}\n\n\ {replace_all}\n\n\ {replacen}\n\n\ {split}\n\n\ {splitn}\n\n\ }}", impl_decl = impl_decl, original_regex = original_regex, captures = captures, captures_iter = captures_iter, find = find, find_at = find_at, find_iter = find_iter, is_match = is_match, is_match_at = is_match_at, replace = replace, replace_all = replace_all, replacen = replacen, split = split, splitn = splitn, ); tokens.into() } fn build_machine(regex: &LitStr, fn_name: &Ident) -> proc_macro2::TokenStream { let regex_text = regex.value(); let regex_ir = regex_syntax::Parser::new().parse(®ex_text); let regex_ir = match regex_ir { Ok(x) => x, Err(err) => panic!("error compiling regex {}: {}", regex.to_token_stream(), err), }; let built_nfa = nfa::NFA::from(®ex_ir); dbg!("built the NFA"); let dfa = dfa::DFA::from(built_nfa); quote! { fn #fn_name(input: &str) -> bool { #dfa } } }