1#![crate_name = "modppl_macros"]
2#![crate_type = "proc-macro"]
3#![warn(non_camel_case_types)]
4
5extern crate proc_macro;
6
7
8use syn::parse_macro_input;
9use syn::{Pat,PatType,ItemFn,FnArg,ReturnType};
10use syn::visit_mut::VisitMut;
11use quote::quote;
12
13mod address;
14use address::ReplaceAddressedCalls;
15
16mod proposal;
17use proposal::ty_is_weak_trace_ref;
18
19
20#[proc_macro]
21pub fn dyngen(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
22 let input_fn = parse_macro_input!(input as ItemFn);
24
25 let arg_details: Vec<_> = input_fn.sig.inputs.iter().map(|fn_arg| {
27 match fn_arg {
28 FnArg::Typed(PatType { pat, ty, .. }) => {
29 let (ident, is_mut) = match **pat {
31 Pat::Ident(ref pat_ident) => {
32 (pat_ident.ident.clone(), pat_ident.mutability.is_some())
33 },
34 _ => panic!("Expected function arguments to have identifiers"),
35 };
36
37 let arg_type = ty.clone();
39
40 (ident, is_mut, arg_type)
41 },
42 _ => panic!("Expected typed arguments"),
43 }
44 }).collect();
45
46 let (arg_idents, mutabilities, arg_tys): (Vec<_>, Vec<_>, Vec<_>) = arg_details.into_iter()
48 .fold((vec![], vec![], vec![]), |mut acc, (ident, is_mut, ty)| {
49 acc.0.push(ident);
50 acc.1.push(is_mut);
51 acc.2.push(ty);
52 acc
53 });
54
55 let args_idents_tuple: proc_macro2::TokenStream;
56 let args_ty_tuple: proc_macro2::TokenStream;
57 if arg_tys.len() > 0 && ty_is_weak_trace_ref(&arg_tys[0]) {
58 let trace_ident = &arg_idents[0];
59 let trace_ty = &arg_tys[0];
60 let mut trace_ident_token = quote! { #trace_ident };
61 if mutabilities[0] {
62 trace_ident_token = quote! { mut #trace_ident };
63 }
64
65 let proposal_arg_details = arg_idents.iter().zip(mutabilities.iter()).skip(1).map(|(ident, &is_mut)| {
66 if is_mut {
67 quote! { mut #ident }
68 } else {
69 quote! { #ident }
70 }
71 }).collect::<Vec<_>>();
72
73 args_idents_tuple = quote! { (#trace_ident_token, (#(#proposal_arg_details),*)) };
74 let proposal_arg_tys = &arg_tys[1..].iter().collect::<Vec<_>>();
75 args_ty_tuple = quote! { (#trace_ty, (#(#proposal_arg_tys),*)) };
76 } else {
77 let arg_idents_tokens = arg_idents.iter().zip(mutabilities.iter()).map(|(ident, &is_mut)| {
78 if is_mut {
79 quote! { mut #ident }
80 } else {
81 quote! { #ident }
82 }
83 }).collect::<Vec<_>>();
84
85 args_idents_tuple = quote! { (#(#arg_idents_tokens),*) };
86 args_ty_tuple = quote! { (#(#arg_tys),*) };
87 }
88
89 let ret_ty = match input_fn.sig.output {
91 ReturnType::Default => quote! { () },
92 ReturnType::Type(_, ref ty) => quote! { #ty },
93 };
94
95 let original_ident = &input_fn.sig.ident;
97 let new_ident = syn::Ident::new(&format!("__{}", original_ident), original_ident.span());
98
99 let mut fn_body = input_fn.block;
100
101 let handler_type = quote! { DynGenFnHandler<#args_ty_tuple, #ret_ty> };
102 let genfn_type = quote! { DynGenFn<#args_ty_tuple, #ret_ty> };
103
104 ReplaceAddressedCalls.visit_block_mut(&mut fn_body);
105
106 quote! {
108 fn #new_ident(__g: &mut #handler_type, __args: #args_ty_tuple) -> #ret_ty {
109 let #args_idents_tuple: #args_ty_tuple = __args;
110 #fn_body
111 }
112 pub const #original_ident: #genfn_type = DynGenFn { func: #new_ident };
113 }.into()
114}