next_gen_proc_macros/
mod.rs1#![allow(nonstandard_style, unused_imports)]
5
6use ::core::{
7 mem,
8 ops::Not as _,
9};
10use ::proc_macro::{
11 TokenStream,
12};
13use ::proc_macro2::{
14 Span,
15 TokenStream as TokenStream2,
16 TokenTree as TT,
17};
18use ::quote::{
19 format_ident,
20 quote,
21 quote_spanned,
22 ToTokens,
23};
24use ::syn::{*,
25 parse::{Parse, Parser, ParseStream},
26 punctuated::Punctuated,
27 Result, spanned::Spanned,
29};
30
31mod utils;
32
33#[proc_macro_attribute] pub
37fn generator (
38 attrs: TokenStream,
39 input: TokenStream,
40) -> TokenStream
41{
42 generator_impl(attrs.into(), input.into())
43 .map(|ret| {
44 #[cfg(feature = "verbose-expansions")] {
45 utils::pretty_print_tokenstream(&ret);
46 }
47 ret
48 })
49 .unwrap_or_else(|err| {
50 let mut errors =
51 err .into_iter()
52 .map(|err| Error::new(
53 err.span(),
54 format_args!("`#[next_gen::generator]`: {}", err),
55 ))
56 ;
57 let mut err = errors.next().unwrap();
58 errors.for_each(|cur| err.combine(cur));
59 err.to_compile_error()
60 })
61 .into()
62}
63
64fn generator_impl (
65 params: TokenStream2,
66 input: TokenStream2,
67) -> Result<TokenStream2>
68{
69 struct Params {
70 yield_ty: Type,
71 resume: Option<(Type, Option<Pat>)>,
72 }
73 impl Parse for Params {
74 fn parse (input: ParseStream<'_>)
75 -> Result<Params>
76 {
77 mod kw {
78 ::syn::custom_keyword!(resume);
79 }
80 let mut yield_ty: Option<Type> = None;
81 let mut resume: Option<(Type, Option<Pat>)> = None;
82 while input.is_empty().not() {
83 let snoopy = input.lookahead1();
84 match () {
85 | _case if snoopy.peek(Token![yield]) => {
86 if yield_ty.is_some() {
87 return Err(input.error("already provided"));
88 }
89 let _: Token![yield] = input.parse().unwrap();
90 let parenthesized; parenthesized!(parenthesized in input);
91 yield_ty.replace(parenthesized.parse()?);
92 let _: Option<Token![,]> = parenthesized.parse()?;
93 },
94 | _case if snoopy.peek(kw::resume) => {
95 if resume.is_some() {
96 return Err(input.error("already provided"));
97 }
98 let _: kw::resume = input.parse().unwrap();
99 let resume_ty: Type = {
100 let parenthesized; parenthesized!(parenthesized in input);
101 let it = parenthesized.parse()?;
102 let _: Option<Token![,]> = parenthesized.parse()?;
103 it
104 };
105 let mut resume_pat = None;
106 if input.parse::<Option<Token![as]>>()?.is_some() {
107 resume_pat.replace(input.parse()?);
108 }
109 resume.replace((resume_ty, resume_pat));
110 },
111 | _case if yield_ty.is_some() && resume.is_some() => break,
114 | _default => return Err(snoopy.error()),
115 }
116 let _: Option<Token![,]> = input.parse()?;
117 }
118 let yield_ty = if let Some(it) = yield_ty { it } else {
119 return Err(input.error("missing `yield(<yield type>)`"));
120 };
121 Ok(Self { yield_ty, resume })
122 }
123 }
124
125 let Params {
126 yield_ty: YieldTy @ _,
127 resume,
128 } = parse2(params)?;
129 let mut fun: ItemFn = parse2(input)?;
130 let ItemFn {
131 ref mut block,
132 ref mut sig,
133 ..
134 } = fun;
135
136 let __yield_slot__ = Ident::new(
137 "__yield_slot__",
138 ::proc_macro::Span::mixed_site().into(),
139 );
140
141 let resume_arg_pat = {
143 sig.asyncness = parse_quote!( async );
144
145 if let Some(receiver) = sig.receiver() {
146 return Err(Error::new_spanned(
147 receiver,
148 "`self` receivers are not supported yet",
149 ));
150 }
151
152 let (each_pat, EachTy @ _): (Vec<_>, Vec<_>) =
153 ::core::mem::take(&mut sig.inputs)
154 .into_iter()
155 .map(|fn_arg| match fn_arg {
156 | FnArg::Receiver(_) => unreachable!(),
157 | FnArg::Typed(PatType { pat, ty, .. }) => (*pat, ty),
158 })
159 .unzip()
160 ;
161 let (resume_arg_pat, ResumeArg @ _) = match resume {
162 | Some((ResumeArg @ _, initial_resume_arg_pat)) => (
163 initial_resume_arg_pat.unwrap_or_else(|| parse_quote!( _ )),
164 ResumeArg.into_token_stream(),
165 ),
166 | None => (
167 parse_quote!( _ ),
168 quote!(),
169 ),
170 };
171 sig.inputs = parse_quote!(
172 #__yield_slot__: ::next_gen::__::__Internals_YieldSlot_DoNotUse__<'_, #YieldTy, #ResumeArg>,
173 (
174 #(#each_pat ,)*
175 ): (
176 #(#EachTy ,)*
177 ),
178 );
179 resume_arg_pat
180 };
181
182 {
184 *block = parse_quote!({
185 macro_rules! yield_ {(
186 $value:expr $(,)?
187 ) => (
188 #__yield_slot__.__put($value).await
189 )}
190
191 let #resume_arg_pat = #__yield_slot__.__take_initial_arg();
192
193 #block
194 });
195 }
196
197 Ok(fun.into_token_stream())
198}