Skip to content

Commit 05c4836

Browse files
committed
feat(macros): Added evaluate macro to evaluate whether a function is worth memo
1 parent 63bda6c commit 05c4836

File tree

2 files changed

+75
-1
lines changed

2 files changed

+75
-1
lines changed

macros/src/lib.rs

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use proc_macro::TokenStream;
22
use quote::{format_ident, quote};
3-
use syn::{Expr, ItemFn, ItemStatic, ReturnType, parse_macro_input};
3+
use syn::{Expr, Ident, ItemFn, ItemStatic, ReturnType, parse_macro_input};
44

55
#[proc_macro]
66
pub fn signal(input: TokenStream) -> TokenStream {
@@ -127,3 +127,53 @@ pub fn effect(input: TokenStream) -> TokenStream {
127127

128128
expanded.into()
129129
}
130+
131+
#[proc_macro_attribute]
132+
pub fn evaluate(attr: TokenStream, item: TokenStream) -> TokenStream {
133+
let print = parse_macro_input!(attr as Ident);
134+
let func = parse_macro_input!(item as ItemFn);
135+
136+
let vis = &func.vis;
137+
let sig = &func.sig;
138+
let block = &func.block;
139+
let ident = &func.sig.ident;
140+
141+
let output_ty = match &sig.output {
142+
ReturnType::Type(_, ty) => ty.clone(),
143+
_ => {
144+
return syn::Error::new_spanned(&sig.output, "Functions must have a return value")
145+
.to_compile_error()
146+
.into();
147+
}
148+
};
149+
150+
if !sig.inputs.is_empty() {
151+
return syn::Error::new_spanned(
152+
&sig.inputs,
153+
"The memo macro can only be used with `get` function without any parameters.",
154+
)
155+
.to_compile_error()
156+
.into();
157+
}
158+
159+
let option_ty = quote! { Option<#output_ty> };
160+
let ident = ident.to_string();
161+
162+
let expanded = quote! {
163+
#vis #sig
164+
where #output_ty: Eq + Clone + 'static
165+
{
166+
let new: #output_ty = (|| #block)();
167+
168+
static mut VALUE: #option_ty = None;
169+
if let Some(old) = unsafe { VALUE } && old == new {
170+
#print(format!("Evaluate: {} not changed, still {:?}\n", #ident, new));
171+
}
172+
unsafe { VALUE = Some(new.clone()) };
173+
174+
new
175+
}
176+
};
177+
178+
expanded.into()
179+
}

macros/tests/eval.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
use macros::evaluate;
2+
3+
static mut PRINT_INVOKED: i32 = 0;
4+
5+
fn print(content: String) {
6+
unsafe { PRINT_INVOKED += 1 };
7+
8+
eprint!("{content}");
9+
}
10+
11+
#[evaluate(print)]
12+
pub fn get_number() -> i32 {
13+
42
14+
}
15+
16+
#[test]
17+
fn evaluate_test() {
18+
let v1 = get_number();
19+
let v2 = get_number();
20+
assert_eq!(v1, v2);
21+
22+
let _ = get_number();
23+
assert_eq!(unsafe { PRINT_INVOKED }, 2);
24+
}

0 commit comments

Comments
 (0)