Mercurial > lbo > hg > memoize
changeset 1:df9526623f04
Initial working version!
author | Lewin Bormann <lbo@spheniscida.de> |
---|---|
date | Thu, 15 Oct 2020 12:52:23 +0200 |
parents | 49c550904d88 |
children | 112b5335a4a0 |
files | Cargo.toml examples/test1.rs src/lib.rs |
diffstat | 3 files changed, 106 insertions(+), 5 deletions(-) [+] |
line wrap: on
line diff
--- a/Cargo.toml Wed Oct 14 19:42:54 2020 +0200 +++ b/Cargo.toml Thu Oct 15 12:52:23 2020 +0200 @@ -10,3 +10,6 @@ [dependencies] lazy_static = "1.4" +proc-macro2 = "1.0" +quote = "1.0" +syn = { version = "1.0", features = ["full"] }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/test1.rs Thu Oct 15 12:52:23 2020 +0200 @@ -0,0 +1,12 @@ +use memoize::memoize; + +#[memoize] +fn hello(a: i32) -> bool { + println!("HELLO"); + a%2 == 0 +} + +fn main() { + println!("result: {}", hello(32)); + println!("result: {}", hello(32)); +}
--- a/src/lib.rs Wed Oct 14 19:42:54 2020 +0200 +++ b/src/lib.rs Thu Oct 15 12:52:23 2020 +0200 @@ -1,16 +1,103 @@ #![crate_type = "proc-macro"] -use proc_macro::TokenStream; +use syn; +use syn::{parse_macro_input, spanned::Spanned, ItemFn}; use lazy_static::lazy_static; +use proc_macro::TokenStream; +use proc_macro2::Span; +use quote::{self, ToTokens}; use std::collections::HashMap; use std::sync::Mutex; +/* + * TODO: + * - Create static map for memoized arguments/results + * - Create memoized version of function + * - Rename original function to memoized_original_{fn} + * + */ + #[proc_macro_attribute] -pub fn memoize(attr: TokenStream, item: TokenStream) -> TokenStream { - let func = parse_macro_input - item +pub fn memoize(_attr: TokenStream, item: TokenStream) -> TokenStream { + let func = parse_macro_input!(item as ItemFn); + let sig = &func.sig; + + let original_name = &func.sig.ident; + let fn_name = &func.sig.ident.to_string(); + let renamed_name = format!("memoized_original_{}", fn_name); + let map_name = format!("memoized_mapping_{}", fn_name); + println!("{}", fn_name); + + let mut type_in = None; + let mut name_in = None; + let type_out; + + // Only one argument + if sig.inputs.len() == 1 { + if let syn::FnArg::Typed(ref arg) = sig.inputs[0] { + type_in = Some(arg.ty.clone()); + if let syn::Pat::Ident(_) = &*arg.pat { + name_in = Some(arg.pat.clone()); + } else { + return syn::Error::new( + sig.span(), + "Cannot memoize method (self-receiver) without arguments!", + ) + .to_compile_error() + .into(); + } + } else { + return TokenStream::from( + syn::Error::new( + sig.span(), + "Cannot memoize method (self-receiver) without arguments!", + ) + .to_compile_error(), + ); + } + } + // TODO: Cache methods too + match &sig.output { + syn::ReturnType::Default => type_out = quote::quote! { () }, + syn::ReturnType::Type(_, ty) => type_out = ty.to_token_stream(), + } + + let type_in = type_in.unwrap(); + let map_ident = syn::Ident::new(&map_name, sig.span()); + let store = quote::quote! { + lazy_static::lazy_static! { + static ref #map_ident : std::sync::Mutex<std::collections::HashMap<#type_in, #type_out>> = + std::sync::Mutex::new(std::collections::HashMap::new()); + } + }; + + let mut renamed_fn = func.clone(); + renamed_fn.sig.ident = syn::Ident::new(&renamed_name, func.sig.span()); + + let name_in = name_in.unwrap(); + let memoized_id = &renamed_fn.sig.ident; + let memoizer = quote::quote! { + #sig { + let mut hm = &mut #map_ident.lock().unwrap(); + if let Some(r) = hm.get(&#name_in) { + return *r; + } + let r = #memoized_id(#name_in); + hm.insert(#name_in, r); + r + } + }; + + (quote::quote! { + #store + + #renamed_fn + + #memoizer + }) + .into() } lazy_static! { @@ -30,5 +117,4 @@ #[cfg(test)] mod tests { use super::*; - }