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()
+        }
+    }
+}