Mercurial > lbo > hg > lazycall
view src/lib.rs @ 0:7401c615042c
Initial commit
author | Lewin Bormann <lbo@spheniscida.de> |
---|---|
date | Fri, 20 Nov 2020 22:15:20 +0100 |
parents | |
children | babb2f9cb5ad |
line wrap: on
line source
use std::iter::IntoIterator; use proc_macro; use syn::parse_macro_input; fn tuple_type_from_sig<'a, Iter: IntoIterator<Item = &'a syn::FnArg>>( args: Iter, ) -> syn::TypeTuple { let types = args .into_iter() .map(|a| match &a { &syn::FnArg::Receiver(_) => panic!("unexpected self in function argument list"), &syn::FnArg::Typed(ref typ) => typ.ty.clone(), }) .collect::<Vec<Box<syn::Type>>>(); if types.len() == 1 { let mut types = types; let typ = types.pop().unwrap(); syn::parse_quote! { (#typ,) } } else { syn::parse_quote! { (#(#types),*) } } } #[proc_macro_attribute] pub fn lazy(_: proc_macro::TokenStream, item: proc_macro::TokenStream) -> proc_macro::TokenStream { let parsedfn = parse_macro_input!(item as syn::ItemFn); let sig = &parsedfn.sig; let fnname = &sig.ident; let returntype = match &sig.output { syn::ReturnType::Default => { let emptytuplets = proc_macro::TokenStream::from(quote::quote! { () }); let emptytuple: syn::Type = parse_macro_input!(emptytuplets as syn::Type); syn::ReturnType::Type(<syn::Token![->]>::default(), Box::new(emptytuple)) } r => r.clone(), }; let newname = syn::Ident::new((fnname.to_string() + "_lazy").as_str(), fnname.span()); let mut newsig = sig.clone(); newsig.ident = newname.clone(); match sig.inputs.first() { Some(syn::FnArg::Receiver(_)) => { // this is an impl function // Skip `self` argument. let mut args_iter = (&newsig.inputs).into_iter(); let receiver = args_iter.next().unwrap(); let tuty = tuple_type_from_sig(args_iter); // We are not evaluating for the receiver, thus one fewer argument. let args_ixs: Vec<usize> = (0..newsig.inputs.len()-1).collect(); (quote::quote! { #parsedfn fn #newname<'lazyvallt>(#receiver, lzy: ::lazycall_support::LazyVal<'lazyvallt, #tuty>) #returntype { use ::lazycall_support::Evaluate; let evald = lzy.eval(); self.#fnname(#(evald.#args_ixs),*) } }).into() } Some(syn::FnArg::Typed(_)) => { // This is a freestanding function let tuty = tuple_type_from_sig(&newsig.inputs); let args_ixs: Vec<usize> = (0..newsig.inputs.len()).collect(); (quote::quote! { #parsedfn fn #newname<'lazyvallt>(lzy: ::lazycall_support::LazyVal<'lazyvallt, #tuty>) #returntype { use ::lazycall_support::Evaluate; let evald = lzy.eval(); #fnname(#(evald.#args_ixs),*) } }).into() } None => { // This is a freestanding function without arguments. // fn f() -> ResultT { ... } => fn f_lazy() { ... } (no body modification) let mut updatedfn = parsedfn.clone(); updatedfn.sig = newsig; (quote::quote! { #updatedfn #parsedfn }) .into() } } }