1
1
use super :: * ;
2
2
3
3
/// Memoize a function.
4
- pub fn expand ( mut func : syn:: ItemFn ) -> Result < proc_macro2:: TokenStream > {
4
+ pub fn expand ( item : & syn:: ItemFn ) -> Result < proc_macro2:: TokenStream > {
5
+ // Preprocess and validate the function.
6
+ let function = prepare ( & item) ?;
7
+
8
+ // Rewrite the function's body to memoize it.
9
+ process ( & function)
10
+ }
11
+
12
+ /// Details about a function that should be memoized.
13
+ struct Function {
14
+ item : syn:: ItemFn ,
15
+ name : syn:: Ident ,
16
+ args : Vec < syn:: Ident > ,
17
+ types : Vec < syn:: Type > ,
18
+ output : syn:: Type ,
19
+ }
20
+
21
+ /// Preprocess and validate a function.
22
+ fn prepare ( function : & syn:: ItemFn ) -> Result < Function > {
5
23
let mut args = vec ! [ ] ;
6
24
let mut types = vec ! [ ] ;
7
- for input in & func. sig . inputs {
25
+
26
+ for input in & function. sig . inputs {
8
27
let typed = match input {
9
28
syn:: FnArg :: Typed ( typed) => typed,
10
29
syn:: FnArg :: Receiver ( _) => {
11
- bail ! ( input , "methods are not supported" )
30
+ bail ! ( function , "methods are not supported" )
12
31
}
13
32
} ;
14
33
@@ -19,32 +38,53 @@ pub fn expand(mut func: syn::ItemFn) -> Result<proc_macro2::TokenStream> {
19
38
ident,
20
39
subpat : None ,
21
40
..
22
- } ) => ident,
41
+ } ) => ident. clone ( ) ,
23
42
pat => bail ! ( pat, "only simple identifiers are supported" ) ,
24
43
} ;
25
44
26
- let ty = typed. ty . as_ref ( ) ;
45
+ let ty = typed. ty . as_ref ( ) . clone ( ) ;
27
46
args. push ( name) ;
28
47
types. push ( ty) ;
29
48
}
30
49
50
+ let output = match & function. sig . output {
51
+ syn:: ReturnType :: Default => {
52
+ bail ! ( function. sig, "function must have a return type" )
53
+ }
54
+ syn:: ReturnType :: Type ( _, ty) => ty. as_ref ( ) . clone ( ) ,
55
+ } ;
56
+
57
+ Ok ( Function {
58
+ item : function. clone ( ) ,
59
+ name : function. sig . ident . clone ( ) ,
60
+ args,
61
+ types,
62
+ output,
63
+ } )
64
+ }
65
+
66
+ /// Rewrite a function's body to memoize it.
67
+ fn process ( function : & Function ) -> Result < TokenStream > {
31
68
// Construct a tuple from all arguments.
69
+ let args = & function. args ;
32
70
let arg_tuple = quote ! { ( #( #args, ) * ) } ;
33
71
34
72
// Construct assertions that the arguments fulfill the necessary bounds.
35
- let bounds = types. iter ( ) . map ( |ty| {
73
+ let bounds = function . types . iter ( ) . map ( |ty| {
36
74
quote ! {
37
75
:: comemo:: internal:: assert_hashable_or_trackable:: <#ty>( ) ;
38
76
}
39
77
} ) ;
40
78
41
79
// Construct the inner closure.
42
- let body = & func. block ;
43
- let closure = quote ! { |#arg_tuple| #body } ;
80
+ let output = & function. output ;
81
+ let body = & function. item . block ;
82
+ let closure = quote ! { |#arg_tuple| -> #output #body } ;
44
83
45
84
// Adjust the function's body.
46
- let name = func. sig . ident . to_string ( ) ;
47
- func. block = parse_quote ! { {
85
+ let mut wrapped = function. item . clone ( ) ;
86
+ let name = function. name . to_string ( ) ;
87
+ wrapped. block = parse_quote ! { {
48
88
#( #bounds; ) *
49
89
:: comemo:: internal:: cached(
50
90
#name,
@@ -53,5 +93,5 @@ pub fn expand(mut func: syn::ItemFn) -> Result<proc_macro2::TokenStream> {
53
93
)
54
94
} } ;
55
95
56
- Ok ( quote ! { #func } )
96
+ Ok ( quote ! { #wrapped } )
57
97
}
0 commit comments