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 协议》,转载必须注明作者和本文链接
推荐文章: