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<Self> {
todo!()
}
};
let captures_iter = quote! {
type CaptureIterator = ::std::iter::Empty<Self>;
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);
let dfa = dfa::DFA::from(built_nfa);
quote! {
fn #fn_name(input: &str) -> bool {
#dfa
}
}
}