view src/lib.rs @ 1:df9526623f04

Initial working version!
author Lewin Bormann <lbo@spheniscida.de>
date Thu, 15 Oct 2020 12:52:23 +0200
parents 49c550904d88
children 112b5335a4a0
line wrap: on
line source

#![crate_type = "proc-macro"]

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 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! {
    static ref STORE: Mutex<HashMap<i32, bool>> = Mutex::new(HashMap::new());
}

// fn memoizer(a: i32) -> bool {
//    let mut hm = &mut STORE.lock().unwrap();
//    if let Some(r) = hm.get(&a) {
//        return *r;
//    }
//    let r = memoized_function(a);
//    hm.insert(a, r);
//    r
// }

#[cfg(test)]
mod tests {
    use super::*;
}