bird-machine-macros 0.0.1

proc macros for bird-machine
Documentation
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(&regex_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(&regex_ir);
    let dfa = dfa::DFA::from(built_nfa);

    quote! {
        fn #fn_name(input: &str) -> bool {
            #dfa
        }
    }
}