use proc_macro2::{Span, TokenStream}; use quote::quote; use syn::parse::{Parse, ParseStream, Result}; use syn::punctuated::Punctuated; use syn::token::{Mut, SelfValue}; use syn::visit_mut::VisitMut; use syn::{ parse_macro_input, Expr, ExprCall, FnArg, Ident, ItemFn, Path, PathSegment, Receiver, Token, Type, }; #[proc_macro_attribute] pub fn program( attr: proc_macro::TokenStream, item: proc_macro::TokenStream, ) -> proc_macro::TokenStream { let item = parse_macro_input!(item as syn::ItemFn); let mut args = parse_macro_input!(attr as Args); if args.name.is_none() { let i = first_letter_to_uppper_case(item.sig.ident.to_string()); args.name = Some(Ident::new(&i, Span::call_site())); } let out = if !args.effects.is_empty() { rewrite_item_into_struct(item, args) } else { quote!(item) }; proc_macro::TokenStream::from(out) } fn first_letter_to_uppper_case(s1: String) -> String { let mut c = s1.chars(); match c.next() { None => String::new(), Some(f) => f.to_uppercase().collect::() + c.as_str(), } } #[derive(Debug)] struct Args { name: Option, effects: Vec, } #[derive(Debug)] struct Effect { name: Ident, path: Path, functions: Vec, } #[derive(Debug)] struct EffectFunction { ident: Ident, alias: Option, } fn get_name_from_args(input: &mut ParseStream) -> Result { let name: Ident = input.parse()?; input.parse::]>()?; Ok(name) } impl Parse for Args { fn parse(mut input: ParseStream) -> Result { let name = get_name_from_args(&mut input).ok(); let effects: Vec<_> = Punctuated::::parse_terminated(input)? .into_iter() .collect(); let effects: Vec = effects .into_iter() .flat_map(|e| { Some(Effect { name: name_from_expr_call(&e)?, path: if let Expr::Path(p) = &*e.func { Some(p.path.clone()) } else { None }?, functions: effects_from_expr_call(&e), }) }) .collect(); Ok(Args { name, effects }) } } fn name_from_expr_call(e: &ExprCall) -> Option { if let Expr::Path(e) = &*e.func { Some(e.path.get_ident()?.clone()) } else { None } } /// returns the list of functions, with their optional alias fn effects_from_expr_call(e: &ExprCall) -> Vec { e.args .iter() .cloned() .flat_map(|p| match p { Expr::Path(e) => Some(EffectFunction { ident: e.path.get_ident().unwrap().clone(), // TODO remove this unwrap alias: None, }), Expr::Cast(cast) => match (*cast.expr, *cast.ty) { (Expr::Path(expr), Type::Path(ty)) => Some(EffectFunction { ident: expr.path.get_ident().unwrap().clone(), alias: Some(ty.path.get_ident()?.clone()), }), _ => None, }, _ => None, }) .collect() } #[derive(Clone, Copy)] struct LettersIter { idx: u32, } impl LettersIter { fn new() -> Self { Self { idx: 'A' as u32 - 1, } } } impl Iterator for LettersIter { type Item = char; fn next(&mut self) -> Option { for _ in 0..100 { self.idx += 1; if let Some(c) = char::from_u32(self.idx) { return Some(c); } } None } } /// takes in the function contents, and returns the structs n stuff fn rewrite_item_into_struct(func: ItemFn, args: Args) -> TokenStream { let prog_name = (&args.name).clone().unwrap(); let intermediate_structs = intermediate_structs(&args, &prog_name); // structs for the builder pattern let inters_tokens = intermediate_structs .iter() .map(|i| i.tokens.clone()) .collect::(); // implementations for the builder pattern let impls = impls(&prog_name, &intermediate_structs); // make the last impl, which contains the run method let final_impl = final_impl(intermediate_structs.last().unwrap(), func, &args); let out = quote! { struct #prog_name; #inters_tokens #impls #final_impl }; TokenStream::from(out) } struct IntermediateStruct { tokens: TokenStream, id: Ident, traits: Vec, letters: Vec, generics: TokenStream, } impl IntermediateStruct { fn new( tokens: TokenStream, id: Ident, traits: Vec, letters: Vec, generics: TokenStream, ) -> Self { Self { tokens, id, traits, letters, generics, } } } fn intermediate_structs(args: &Args, prog_name: &Ident) -> Vec { let struct_with = format!("{}With", prog_name); args.effects .iter() .fold( (vec![], struct_with, vec![]), |(mut structs, name, mut traits), eff| { let name = format!("{}{}", &name, &eff.name); traits.push(eff.name.clone()); // kinda messy let letters = LettersIter::new() .take(traits.len()) .map(|c| Ident::new(&c.to_string(), Span::call_site())); let generics = traits .iter() .zip(letters.clone()) .map(|(t, c)| quote!(#c: #t,)) .collect::(); let id = Ident::new(&name, Span::call_site()); let last = if let Some(&IntermediateStruct { ref id, ref letters, .. }) = &structs.last() { let gen = letters.iter().map(|l| quote!(#l,)).collect::(); quote!(#id<#gen>) } else { quote!(#prog_name) }; let last_letter = letters.clone().last(); structs.push(IntermediateStruct::new( quote! { struct #id<#generics>(#last, #last_letter); }, id, traits.clone(), letters.collect::>(), generics, )); (structs, name, traits) }, ) .0 } fn impls(prog_name: &Ident, intermediate_structs: &Vec) -> TokenStream { let mut impls = vec![]; let mut id = quote!(#prog_name); let mut impl_token = quote!(impl); for inter in intermediate_structs { let next = inter.id.clone(); let gen = inter .letters .iter() .map(|l| quote!(#l,)) .collect::(); let ret = quote!(#next<#gen>); // lmao what a mess // need to get the last trait/letter, then replace P: Printer and p: P let t = inter.traits.last().unwrap(); let l = inter.letters.last().unwrap(); let func_generics = quote!(#l: #t); // and need to destructure self impls.push(quote! { #impl_token #id { fn add<#func_generics>(self, t: #l) -> #ret { #next(self, t) } } }); id = ret; let generics = &inter.generics; impl_token = quote!(impl<#generics>); } impls.into_iter().collect::() } fn final_impl(last: &IntermediateStruct, func: ItemFn, args: &Args) -> TokenStream { let id = &last.id; let full_gen = &last.generics; let gen = last .letters .iter() .map(|l| quote!(#l,)) .collect::(); let func = rewrite_func(func, args); quote! { impl<#full_gen> #id<#gen> { #func } } } fn rewrite_func(mut func: ItemFn, args: &Args) -> TokenStream { func.sig.ident = Ident::new("run", Span::call_site()); // add `mut self` as a parameter func.sig.inputs.insert( 0, FnArg::Receiver(Receiver { attrs: vec![], reference: None, mutability: Some(Mut { span: Span::call_site(), }), self_token: SelfValue { span: Span::call_site(), }, }), ); FuncRewriter { args }.visit_item_fn_mut(&mut func); quote! { #func } } struct FuncRewriter<'a> { args: &'a Args, } impl<'a> syn::visit_mut::VisitMut for FuncRewriter<'a> { fn visit_expr_call_mut(&mut self, node: &mut ExprCall) { let eff_len = self.args.effects.len(); // check if the function name is in args // if it is, replace it with the correct name if let Expr::Path(path) = &mut *node.func { for (i, effect) in self.args.effects.iter().enumerate() { for func in &effect.functions { let ident = func.alias.clone().unwrap_or(func.ident.clone()); if path.path.is_ident(&ident) { // get the effect trait path's, and append the function as a segment let mut effect_path = effect.path.clone(); effect_path.segments.push(PathSegment { ident: func.ident.clone(), arguments: syn::PathArguments::None, }); path.path = effect_path; // then change the parameters so the handler is the first // get the effect's index, and add the inverse num of `.0`s let idx = eff_len - (i + 1); let s = format!("&mut self{}.1", ".0".repeat(idx)); let expr: Expr = syn::parse_str(&s).unwrap(); node.args.insert(0, expr); } } } } } }