1extern crate proc_macro;
2
3mod elision;
4
5use proc_macro::*;
6use syn::fold::Fold;
7
8#[proc_macro_attribute]
9pub fn generator(_: TokenStream, input: TokenStream) -> TokenStream {
10 if let Ok(item_fn) = syn::parse(input) {
11 let item_fn = Generator { outer_fn: true, lifetimes: vec![] }.fold_item_fn(item_fn);
12 quote::quote!(#item_fn).into()
13 } else {
14 panic!("#[generator] atribute can only be applied to functions");
15 }
16}
17
18struct Generator {
19 outer_fn: bool,
20 lifetimes: Vec<syn::Lifetime>,
21}
22
23impl Fold for Generator {
24 fn fold_item_fn(&mut self, mut i: syn::ItemFn) -> syn::ItemFn {
25 if !self.outer_fn { return i }
26
27 let inputs = elision::unelide_lifetimes(&mut i.sig.generics.params, i.sig.inputs);
28 self.lifetimes = i.sig.generics.lifetimes().map(|l| l.lifetime.clone()).collect();
29 let output = self.fold_return_type(i.sig.output);
30 let sig = syn::Signature { output, inputs, ..i.sig };
31
32 self.outer_fn = false;
33
34 let inner = self.fold_block(*i.block);
35 let block = Box::new(make_fn_block(&inner));
36
37 syn::ItemFn { sig, block, ..i }
38 }
39
40 fn fold_return_type(&mut self, i: syn::ReturnType) -> syn::ReturnType {
41 use syn::{ReturnType, Token};
42 use proc_macro2::Span;
43 if !self.outer_fn { return i; }
44 let (arrow, ret) = match i {
45 ReturnType::Default => (Token), syn::parse_str("()").unwrap()),
46 ReturnType::Type(arrow, ty) => (arrow, *ty),
47 };
48 let lifetimes = std::mem::replace(&mut self.lifetimes, vec![]);
49 let ret = syn::parse2(quote::quote!(
50 (impl Iterator<Item = #ret> #(+ #lifetimes )*)
51 )).unwrap();
52 ReturnType::Type(arrow, Box::new(ret))
53 }
54
55 fn fold_expr(&mut self, i: syn::Expr) -> syn::Expr {
56 if let syn::Expr::Try(syn::ExprTry { expr, .. }) = i {
57 syn::parse2(quote::quote!(propane::gen_try!(#expr))).unwrap()
58 } else {
59 syn::fold::fold_expr(self, i)
60 }
61 }
62}
63
64fn make_fn_block(inner: &syn::Block) -> syn::Block {
65 syn::parse2(quote::quote! {{
66 let __ret = || {
67 #inner;
68 #[allow(unreachable_code)]
69 {
70 return;
71 yield panic!();
72 }
73 };
74
75 #[allow(unreachable_code)]
76 propane::__internal::GenIter(__ret)
77 }}).unwrap()
78}