简体   繁体   中英

How to provide an enumerated index to a macro interpolation function in quote with proc_macro?

I've implemented the following proc_macro that takes

builtin_method!(hello_world(a, b, c) {
    println!("{} {} {}", a, b, c);
}

and should generate

pub fn hello_world(args: Vec<String>) {
    let a = args.get(0).unwrap();
    let b = args.get(1).unwrap();
    let c = args.get(2).unwrap();

    println!("{} {} {}", a, b, c);
}

Here's my current code.

use proc_macro::TokenStream;
use quote::quote;
use syn;

struct BuiltinDef {
    function: syn::Ident,
    arguments: Vec<syn::Ident>,
    body: syn::Block,
}

impl syn::parse::Parse for BuiltinDef {
    fn parse(stream: syn::parse::ParseStream) -> syn::Result<Self> {
        let function: syn::Ident = stream.parse()?;
        let content;
        let _: syn::token::Paren = syn::parenthesized!(content in stream);
        let arguments: syn::punctuated::Punctuated<syn::Ident, syn::Token![,]> =
            content.parse_terminated(syn::Ident::parse)?;
        let body: syn::Block = stream.parse()?;

        Ok(BuiltinDef {
            function,
            arguments: arguments.into_iter().collect(),
            body,
        })
    }
}

#[proc_macro]
pub fn builtin_method(input: TokenStream) -> TokenStream {
    let def = syn::parse_macro_input!(input as BuiltinDef);

    let function = def.function;
    let arguments = def.arguments;
    let body = def.body;

    let gen = quote! {
        pub fn #function(args: Vec<String>) {
            let mut _i = 0;
            #(
                let mut #arguments = args.get(_i).unwrap().clone();
                _i += 1;
            )*
            #body
        }
    };

    TokenStream::from(gen)
}

Inside the variable interpolation, I need some kind of enumerating variable counting up. According to the docs there is not such way.

How can I implement this better instead of counting up _i ?

let mut _i = 0;
#(
    let mut #arguments = args.get(_i).unwrap().clone();
    _i += 1;
)*

Use standard Iterator::enumerate() :

let arguments = arguments.into_iter().enumerate().map(|(index, arg)| quote! {
    let mut #arg = args.get(#index).unwrap().clone();
});
let gen = quote! {
    pub fn #function(args: Vec<String>) {
        let mut _i = 0;
        #(#arguments)*
        #body
    }
};

Ok after following https://stackoverflow.com/a/70939071/694705 I was able to implement it this way by collecting the arguments back into a Vec<proc_macro2::TokenStream> .

Here's my solution:

let arguments: Vec<proc_macro2::TokenStream> =
        arguments
        .into_iter()
        .enumerate()
        .map(|(idx, arg)| {
            quote! {
                let mut #arg = args.get(#idx).unwrap().clone();
            }
        })
        .collect();

let gen = quote! {
    pub fn #function(args: Vec<String>) {
        #(#arguments)*
        #body
    }
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM