1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#![cfg_attr(feature = "external_doc",
    feature(external_doc),
)]
#![doc(test(attr(deny(warnings))))]

#[macro_use]
extern crate fstrings;
extern crate proc_macro;

use ::proc_macro::TokenStream;
use quote::{
    quote,
    quote_spanned,
};
use ::syn::{*,
    spanned::Spanned,
};

#[macro_use]
mod macros;

const IDENT_SUFFIX: &'static str = "__hack__";
/// Transforms a function with `yield_!` calls into a generator.
#[cfg_attr(feature = "external_doc",
    doc = "",
    doc = "# Example",
    doc = "",
    doc = "```rust",
    doc = "# macro_rules! ignore {($($tt:tt)*) => ()} ignore! {",
    doc(include = "doc_examples/generator.rs"),
    doc = "# }",
    doc = "```",
)]
///
/// # Expansion
///
/// The above example expands to:
#[cfg_attr(feature = "external_doc",
    doc = "",
    doc = "```rust",
    doc = "# macro_rules! ignore {($($tt:tt)*) => ()} ignore! {",
    doc(include = "doc_examples/generator_desugared.rs"),
    doc = "# }",
    doc = "```",
)]
#[proc_macro_attribute] pub
fn generator (params: TokenStream, input: TokenStream)
  -> TokenStream
{
    #[allow(unused)]
    const FUNCTION_NAME: &str = "generator";

    let yield_type: Type = parse_macro_input!(params as Type);

    debug_input!(&input);
    let mut function: ItemFn = parse_macro_input!(input);
    let ItemFn {
        ref mut block,
        ref mut sig,
        ..
    } = function;

    // Update block to generate `yield_!` macro.
    {
        *block = parse_quote! {
            {
                #[derive(next_gen::next_gen_hack)]
                enum __yield_slot____hack__ {}

                #block
            }
        };
    }

    // Handle the signature
    {
        sig.asyncness = parse_quote! { async };

        match sig
                .inputs
                .iter()
                .find(|&fn_arg| match *fn_arg {
                    | FnArg::Receiver(_) => true,
                    | _ => false,
                })
        {
            | Some(ty) => return {
                Error::new(ty.span(), &f!(
                    "`#[{FUNCTION_NAME}]` does not support `self` receivers yet"
                )).to_compile_error().into()
            },

            | _ => {},
        }

        let (pats, tys): (Vec<_>, Vec<_>) =
            ::core::mem::replace(&mut sig.inputs, Default::default())
                .into_iter()
                .map(|fn_arg| match fn_arg {
                    | FnArg::Receiver(_) => unreachable!(),
                    | FnArg::Typed(PatType { pat, ty, .. }) => (pat, ty),
                })
                .unzip()
        ;
        sig.inputs = parse_quote! {
            __yield_slot__: next_gen::__Internals_YieldSlot_DoNotUse__<'_, #yield_type>,
            ( #(#pats ,)* ): ( #(#tys ,)* ),
        };
    }

    TokenStream::from(debug_output!(quote! {
        #function
    }))
}


#[doc(hidden)]
#[proc_macro_derive(next_gen_hack)] pub
fn hack (input: TokenStream) -> TokenStream
{
    let input: DeriveInput = parse_macro_input!(input);
    let ident: String = input.ident.to_string();
    let ident: &str = &ident[.. ident.len() - IDENT_SUFFIX.len()];
    let yield_slot = Ident::new(ident, input.ident.span());
    TokenStream::from(quote_spanned! { input.span() =>
        macro_rules! yield_ {(
            $value:expr
        ) => (
            let () = #yield_slot.put($value).await;
        )}
    })
}