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
133
134
135
136
#[macro_use]
extern crate fstrings;
extern crate proc_macro;

use ::proc_macro::TokenStream;
use ::proc_macro2::{
    Span,
    // TokenStream as TokenStream2,
};
use quote::{
    // ToTokens,
    quote,
    quote_spanned,
};
use ::syn::{*,
    spanned::Spanned,
    // parse::Parse,
    // punctuated::Punctuated,
    // error::Error,
};

#[macro_use]
mod macros;

const IDENT_SUFFIX: &'static str = "__hack__";

#[proc_macro_attribute] pub
fn generator (params: TokenStream, input: TokenStream)
  -> TokenStream
{
    #[allow(unused)]
    const FUNCTION_NAME: &str = "generator";

    let yield_type = {
        let params = parse_macro_input!(params as AttributeArgs);
        match params.len() {
            | 0 => return Error::new(
                Span::call_site(),
                &f!("Missing parameter: expected `#[{FUNCTION_NAME}(<type>)]`"),
            ).to_compile_error().into(),

            | 1 => {
                let arg = {params}.pop().unwrap();
                match arg {
                    | NestedMeta::Meta(Meta::Path(path)) => path,
                    | _ => return Error::new(
                        arg.span(),
                        "Expected a type",
                    ).to_compile_error().into(),
                }
            },

            | _ => return Error::new(
                {params}.pop().unwrap().span(),
                &f!("Too many parameters"),
            ).to_compile_error().into(),
        }
    };

    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 __coroutine____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! {
            __coroutine__: next_gen::Coroutine<'_, #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 = input.ident.to_string();
    let ident = &ident[.. ident.len() - IDENT_SUFFIX.len()];
    let co = Ident::new(ident, input.ident.span());
    TokenStream::from(quote_spanned! { input.span() =>
        macro_rules! yield_ {(
            $value:expr
        ) => (
            #co._yield($value).await
        )}
    })
}