Mercurial > lbo > hg > lazycall
changeset 0:7401c615042c
Initial commit
author | Lewin Bormann <lbo@spheniscida.de> |
---|---|
date | Fri, 20 Nov 2020 22:15:20 +0100 |
parents | |
children | babb2f9cb5ad |
files | .hgignore Cargo.lock Cargo.toml examples/test_functions.rs lazycall_support/Cargo.lock lazycall_support/Cargo.toml lazycall_support/src/lib.rs src/lib.rs |
diffstat | 8 files changed, 236 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.hgignore Fri Nov 20 22:15:20 2020 +0100 @@ -0,0 +1,1 @@ +^target/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Cargo.lock Fri Nov 20 22:15:20 2020 +0100 @@ -0,0 +1,49 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "lazycall" +version = "0.1.0" +dependencies = [ + "lazycall_support", + "quote", + "syn", +] + +[[package]] +name = "lazycall_support" +version = "0.1.0" + +[[package]] +name = "proc-macro2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "1.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc371affeffc477f42a221a1e4297aedcea33d47d19b61455588bd9d8f6b19ac" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Cargo.toml Fri Nov 20 22:15:20 2020 +0100 @@ -0,0 +1,22 @@ +[package] +name = "lazycall" +version = "0.1.0" +authors = ["Lewin Bormann <lewin@lewin-bormann.info>"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +proc-macro = true + +[dependencies] +quote = "~1.0" +syn = {version = "~1.0", features = ["full"]} + +lazycall_support = {path = "lazycall_support"} + +[[example]] +name = "test_functions" + +[workspace] +members = ["lazycall_support"]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/test_functions.rs Fri Nov 20 22:15:20 2020 +0100 @@ -0,0 +1,29 @@ +use lazycall::lazy; + +#[lazy] +fn test_function_empty() -> i32 { + 3 +} + +struct TestStruct(i32); + +#[lazy] +fn test_function_someargs(a: i32, b: usize, c: TestStruct) -> bool { + a%2 == 0 +} + +#[lazy] +fn test_function_onearg(a: i32) -> bool { + a%2 == 0 +} + +impl TestStruct { + #[lazy] + fn test_receiving_func(&mut self, a: i32) -> bool { + a%2 == 0 + } +} + +fn main() { + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lazycall_support/Cargo.lock Fri Nov 20 22:15:20 2020 +0100 @@ -0,0 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "lazycall_support" +version = "0.1.0"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lazycall_support/Cargo.toml Fri Nov 20 22:15:20 2020 +0100 @@ -0,0 +1,9 @@ +[package] +name = "lazycall_support" +version = "0.1.0" +authors = ["Lewin Bormann <lewin@lewin-bormann.info>"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lazycall_support/src/lib.rs Fri Nov 20 22:15:20 2020 +0100 @@ -0,0 +1,27 @@ + +pub struct LazyVal<'a, Result>(&'a mut dyn FnMut() -> Result); + +impl<'a, Result> LazyVal<'a, Result> { + fn new<F: 'a + FnMut() -> Result>(f: &'a mut F) -> LazyVal<'a, Result> { + LazyVal(f) + } +} + +pub trait Evaluate<R> { + fn eval(self) -> R; +} + +impl<'a, R> Evaluate<R> for LazyVal<'a, R> { + fn eval(self) -> R { + (self.0)() + } +} + + +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + assert_eq!(2 + 2, 4); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib.rs Fri Nov 20 22:15:20 2020 +0100 @@ -0,0 +1,94 @@ + +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() + } + } +}