quantifiable_derive/lib.rs
1
2extern crate proc_macro;
3//#[macro_use]
4extern crate quote;
5extern crate syn;
6extern crate synstructure;
7
8use syn::{parse_macro_input, DeriveInput};
9use syn::spanned::Spanned;
10use quote::{quote, quote_spanned};
11
12
13// Look https://github.com/dtolnay/syn/blob/master/examples/heapsize/heapsize_derive/src/lib.rs for a more recent similar derive.
14
15//WITH proc_macro
16// #[proc_macro_derive(Quantifiable)]
17// pub fn quantifiable_macro_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
18// //let ast = syn::parse(input).unwrap();
19// let mut ast = syn::parse_macro_input(&input.to_string()).unwrap();
20// //let mut ast = syn::parse_macro_input!(input);
21// let style = synstructure::BindStyle::Ref.into();
22// //Collect all the fields into a total_memory method.
23// let total_memory_body = synstructure::each_field(&mut ast, &style, |binding| {
24// Some(quote! {
25// sum += ::quantify::Quantifiable::total_memory(#binding);
26// })
27// });
28// //Collect all the fields into a forecast_total_memory method.
29// let forecast_total_memory_body = synstructure::each_field(&mut ast, &style, |binding| {
30// Some(quote! {
31// sum += ::quantify::Quantifiable::forecast_total_memory(#binding);
32// })
33// });
34// //The name of the type for which we are implementing Quantifiable.
35// let name = &ast.ident;
36// let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
37// let mut where_clause = where_clause.clone();
38// //We added the where condition member_name:Quantifiable for each member.
39// for param in &ast.generics.ty_params {
40// where_clause.predicates.push(syn::WherePredicate::BoundPredicate(syn::WhereBoundPredicate {
41// bound_lifetimes: Vec::new(),
42// bounded_ty: syn::Ty::Path(None, param.ident.clone().into()),
43// //bounds: vec![syn::TypeParamBound::Trait(
44// bounds: vec![syn::TyParamBound::Trait(
45// syn::PolyTraitRef {
46// bound_lifetimes: Vec::new(),
47// trait_ref: syn::parse_path("::quantifiable::Quantifiable").unwrap(),
48// },
49// syn::TraitBoundModifier::None
50// )],
51// }))
52// //where_clause.predicates.push(syn::WherePredicate::Type(syn::PredicateType {
53// // lifetimes: None,
54// // bounded_ty: syn::Ty::Path(None, param.ident.clone().into()),
55// // bounds: syn::parse_path("::quantifiable::Quantifiable").unwrap(),
56// //}))
57// }
58// //Build the token sequence.
59// let tokens = quote! {
60// impl #impl_generics ::quantify::Quantifiable for #name #ty_generics #where_clause {
61// #[inline]
62// #[allow(unused_variables, unused_mut, unreachable_code)]
63// fn total_memory(&self) -> usize {
64// let mut sum = 0;
65// match *self {
66// #total_memory_body
67// }
68// sum
69// }
70// fn print_memory_breakdown(&self)
71// {
72// unimplemented!();
73// }
74// fn forecast_total_memory(&self) -> usize
75// {
76// let mut sum = 0;
77// match *self {
78// #forecast_total_memory_body
79// }
80// sum
81// }
82// }
83// };
84// //tokens
85// tokens.to_string().parse().unwrap()
86// }
87
88//WITH proc_macro2 and syn-1.0
89// https://doc.rust-lang.org/proc_macro/index.html
90// https://docs.rs/proc-macro2/1.0.24/proc_macro2/index.html
91// https://docs.rs/syn/1.0.44/syn/index.html
92#[proc_macro_derive(Quantifiable)]
93pub fn quantifiable_macro_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
94 //let ast = syn::parse(input).unwrap();
95 //let mut ast = syn::parse_macro_input(&input.to_string()).unwrap();
96 //let input = proc_macro2::TokenStream::from(input);
97 let ast = parse_macro_input!(input as DeriveInput);
98 //let mut ast = syn::parse_macro_input!(input);
99 //let style = synstructure::BindStyle::Ref.into();
100 //Collect all the fields into a total_memory method.
101 //let total_memory_body = synstructure::each_field(&mut ast, &style, |binding| {
102 // Some(quote! {
103 // sum += ::quantify::Quantifiable::total_memory(#binding);
104 // })
105 // });
106 ////Collect all the fields into a forecast_total_memory method.
107 //let forecast_total_memory_body = synstructure::each_field(&mut ast, &style, |binding| {
108 // Some(quote! {
109 // sum += ::quantify::Quantifiable::forecast_total_memory(#binding);
110 // })
111 // });
112 let total_memory_body = quantifiable_total_memory_expression(&ast.data);
113 let forecast_total_memory_body = quantifiable_forecast_total_memory_expression(&ast.data);
114 //The name of the type for which we are implementing Quantifiable.
115 let name = &ast.ident;
116 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
117 let where_clause = where_clause.clone();
118 //We added the where condition member_name:Quantifiable for each member.
119 // for param in &ast.generics.ty_params {
120 // where_clause.predicates.push(syn::WherePredicate::BoundPredicate(syn::WhereBoundPredicate {
121 // bound_lifetimes: Vec::new(),
122 // bounded_ty: syn::Ty::Path(None, param.ident.clone().into()),
123 // //bounds: vec![syn::TypeParamBound::Trait(
124 // bounds: vec![syn::TyParamBound::Trait(
125 // syn::PolyTraitRef {
126 // bound_lifetimes: Vec::new(),
127 // trait_ref: syn::parse_path("::quantifiable::Quantifiable").unwrap(),
128 // },
129 // syn::TraitBoundModifier::None
130 // )],
131 // }))
132 // //where_clause.predicates.push(syn::WherePredicate::Type(syn::PredicateType {
133 // // lifetimes: None,
134 // // bounded_ty: syn::Ty::Path(None, param.ident.clone().into()),
135 // // bounds: syn::parse_path("::quantifiable::Quantifiable").unwrap(),
136 // //}))
137 // }
138 //Build the token sequence.
139 let tokens = quote! {
140 impl #impl_generics crate::quantify::Quantifiable for #name #ty_generics #where_clause {
141#[inline]
142#[allow(unused_variables, unused_mut, unreachable_code)]
143 fn total_memory(&self) -> usize {
144 #total_memory_body
145 }
146 fn print_memory_breakdown(&self)
147 {
148 unimplemented!();
149 }
150 fn forecast_total_memory(&self) -> usize
151 {
152 #forecast_total_memory_body
153 }
154 }
155 };
156 //tokens
157 //tokens.to_string().parse().unwrap()
158 proc_macro::TokenStream::from(tokens)
159}
160
161///Create an expression for the total_memory method.
162fn quantifiable_total_memory_expression(data: &syn::Data) -> proc_macro2::TokenStream
163{
164 match *data
165 {
166 syn::Data::Struct(ref data) =>
167 {
168 match data.fields
169 {
170 syn::Fields::Named(ref fields) =>
171 {
172 //An struct with named fields. Blah{name1:type1, name2:type2, ...}
173 let fit=fields.named.iter().map(|field|{
174 let name=&field.ident;
175 quote_spanned!{ field.span() =>
176 crate::quantify::Quantifiable::total_memory(&self.#name)
177 }
178 });
179 quote!{ 0 #(+ #fit)* }
180 }
181 syn::Fields::Unnamed(ref fields) =>
182 {
183 let fit=fields.unnamed.iter().enumerate().map(|(i,field)|{
184 let index=syn::Index::from(i);
185 quote_spanned!{ field.span() =>
186 crate::quantify::Quantifiable::total_memory(&self.#index)
187 }
188 });
189 quote!{ 0 #(+ #fit)* }
190 }
191 syn::Fields::Unit => quote!(0),
192 }
193 },
194 syn::Data::Enum(ref data) =>
195 {
196 //https://docs.rs/syn/1.0.44/syn/struct.DataEnum.html
197 let vit = data.variants.iter().map(|variant|{
198 let vname = &variant.ident;
199 match variant.fields
200 {
201 syn::Fields::Named(ref fields) =>
202 {
203 //An struct with named fields. Blah{name1:type1, name2:type2, ...}
204 let fit=fields.named.iter().map(|field|{
205 let name=&field.ident;
206 quote_spanned!{ field.span() =>
207 crate::quantify::Quantifiable::total_memory(#name)
208 }
209 });
210 let sum =quote!{ 0 #(+ #fit)* };
211 let fit=fields.named.iter().map(|field|{
212 let name=&field.ident;
213 quote_spanned!{ field.span() =>
214 #name
215 }
216 });
217 let args = quote!{ #(#fit,)* };
218 quote_spanned!{ variant.span() =>
219 Self::#vname{#args} => #sum,
220 }
221 },
222 syn::Fields::Unnamed(ref fields) =>
223 {
224 let fit=fields.unnamed.iter().enumerate().map(|(i,field)|{
225 //let index=syn::Index::from(i);
226 let arg=quote::format_ident!("u{}",i);
227 quote_spanned!{ field.span() =>
228 crate::quantify::Quantifiable::total_memory(#arg)
229 }
230 });
231 let sum = quote!{ 0 #(+ #fit)* };
232 let fit=fields.unnamed.iter().enumerate().map(|(i,field)|{
233 //let index=syn::Index::from(i);
234 let arg=quote::format_ident!("u{}",i);
235 quote_spanned!{ field.span() =>
236 #arg
237 }
238 });
239 let args = quote!{ #(#fit,)* };
240 quote_spanned!{ variant.span() =>
241 Self::#vname(#args) => #sum,
242 }
243 },
244 syn::Fields::Unit =>
245 {
246 //quote!(0),
247 quote!(Self::#vname => 0,)
248 },
249 }
250 });
251 //FIXME: should be max instead of plus.
252 let tokens=quote!{ match self { #( #vit)* } };
253 //quote!{compile_error!(stringify!{#tokens});#tokens}
254 tokens
255 },
256 syn::Data::Union(_) => unimplemented!(),
257 }
258}
259
260fn quantifiable_forecast_total_memory_expression(data: &syn::Data) -> proc_macro2::TokenStream
261{
262 match *data
263 {
264 syn::Data::Struct(ref data) =>
265 {
266 match data.fields
267 {
268 syn::Fields::Named(ref fields) =>
269 {
270 //An struct with named fields. Blah{name1:type1, name2:type2, ...}
271 let fit=fields.named.iter().map(|field|{
272 let name=&field.ident;
273 quote_spanned!{ field.span() =>
274 crate::quantify::Quantifiable::forecast_total_memory(&self.#name)
275 }
276 });
277 quote!{ 0 #(+ #fit)* }
278 }
279 syn::Fields::Unnamed(ref fields) =>
280 {
281 let fit=fields.unnamed.iter().enumerate().map(|(i,field)|{
282 let index=syn::Index::from(i);
283 quote_spanned!{ field.span() =>
284 crate::quantify::Quantifiable::forecast_total_memory(&self.#index)
285 }
286 });
287 quote!{ 0 #(+ #fit)* }
288 }
289 syn::Fields::Unit => quote!(0),
290 }
291 },
292 syn::Data::Enum(ref data) =>
293 {
294 //https://docs.rs/syn/1.0.44/syn/struct.DataEnum.html
295 let vit = data.variants.iter().map(|variant|{
296 let vname = &variant.ident;
297 match variant.fields
298 {
299 syn::Fields::Named(ref fields) =>
300 {
301 //An struct with named fields. Blah{name1:type1, name2:type2, ...}
302 let fit=fields.named.iter().map(|field|{
303 let name=&field.ident;
304 quote_spanned!{ field.span() =>
305 crate::quantify::Quantifiable::total_memory(#name)
306 }
307 });
308 let sum =quote!{ 0 #(+ #fit)* };
309 let fit=fields.named.iter().map(|field|{
310 let name=&field.ident;
311 quote_spanned!{ field.span() =>
312 #name
313 }
314 });
315 let args = quote!{ #(#fit,)* };
316 quote_spanned!{ variant.span() =>
317 Self::#vname{#args} => #sum,
318 }
319 },
320 syn::Fields::Unnamed(ref fields) =>
321 {
322 let fit=fields.unnamed.iter().enumerate().map(|(i,field)|{
323 //let index=syn::Index::from(i);
324 let arg=quote::format_ident!("u{}",i);
325 quote_spanned!{ field.span() =>
326 crate::quantify::Quantifiable::total_memory(#arg)
327 }
328 });
329 let sum = quote!{ 0 #(+ #fit)* };
330 let fit=fields.unnamed.iter().enumerate().map(|(i,field)|{
331 //let index=syn::Index::from(i);
332 let arg=quote::format_ident!("u{}",i);
333 quote_spanned!{ field.span() =>
334 #arg
335 }
336 });
337 let args = quote!{ #(#fit,)* };
338 quote_spanned!{ variant.span() =>
339 Self::#vname(#args) => #sum,
340 }
341 },
342 syn::Fields::Unit =>
343 {
344 //quote!(0),
345 quote!(Self::#vname => 0,)
346 },
347 }
348 });
349 //FIXME: should be max instead of plus.
350 let tokens=quote!{ match self { #( #vit)* } };
351 //quote!{compile_error!(stringify!{#tokens});#tokens}
352 tokens
353 },
354 syn::Data::Union(_) => unimplemented!(),
355 }
356}
357
358#[cfg(test)]
359mod tests {
360 #[test]
361 fn it_works() {
362 assert_eq!(2 + 2, 4);
363 }
364}