proc-macro-workshop:seq-4

审题

// One of the big things callers will want to do with the sequential indices N
// is use them as part of an identifier, like f0 f1 f2 etc.
//
// Implement some logic to paste together any Ident followed by `~` followed by
// our loop variable into a single concatenated identifier.
//
// The invocation below will expand to:
//
//     fn f1() -> u64 { 1 * 2 }
//     fn f2() -> u64 { 2 * 2 }
//     fn f3() -> u64 { 3 * 2 }
//
// Optionally, also support more flexible arrangements like `f~N~_suffix` ->
// f0_suffix f1_suffix etc, though the test suite only requires `prefix~N` so
// you will need to add your own tests for this feature.
//
//
// Resources:
//
//     - Example of creating a new Ident from a string:
//       https://docs.rs/syn/1.0/syn/struct.Ident.html

use seq::seq;

seq!(N in 1..4 {
    fn f~N () -> u64 {
        N * 2
    }
});

// This f0 is written separately to detect whether your macro correctly starts
// with the first iteration at N=1 as specified in the invocation. If the macro
// incorrectly started at N=0 like in the previous tests cases, the first
// generated function would conflict with this one and the program would not
// compile.
fn f0() -> u64 {
    100
}

fn main() {
    let sum = f0() + f1() + f2() + f3();

    assert_eq!(sum, 100 + 2 + 4 + 6);
}

这里要解析的是?~N的形式,我们需要预读一些token
然后将数值带入,把?~N转换为?n的形式。

题解

// solution4.rs
impl crate::parser::SeqParser {
    pub(crate) fn process_prefix(
        &self,
        idx: &mut usize,
        n: usize,
        prefix: &syn::Ident,
        buf: &Vec<proc_macro2::TokenTree>,
    ) -> std::option::Option<proc_macro2::TokenStream> {
        // 还有解析的空间
        if *idx + 2 < buf.len() {
            if let proc_macro2::TokenTree::Punct(p) = &buf[*idx + 1] {
                // 核对标识符
                if p.as_char() == '~' {
                    if let proc_macro2::TokenTree::Ident(ident) = &buf[*idx + 2] {
                        // 核对循环变量,并且需要紧密联系
                        if ident == &self.variable_ident
                            && prefix.span().end() == p.span().start()
                            && p.span().end() == ident.span().start()
                        {
                            // 新生成标识符进行替换
                            let combine_ident_litral = format!("{}{}", prefix.to_string(), n);
                            let combine_ident =
                                syn::Ident::new(&combine_ident_litral, prefix.span());
                            *idx += 3;
                            return std::option::Option::Some(quote::quote! {
                                #combine_ident
                            });
                        }
                    }
                }
            }
        }
        std::option::Option::None
    }
}

不过,这里还算是原有逻辑的一个补充,因此,需要在seq-3中进行添加,也就是注释部分。

整体

整体逻辑保持不变

#[proc_macro]
pub fn seq(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let parser = syn::parse_macro_input!(input as crate::parser::SeqParser);
    return parser.expend_repeat().into();
}
本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!