// Now construct the generated code! Produce the output TokenStream by repeating
// the loop body the correct number of times as specified by the loop bounds and
// replacing the specified identifier with the loop counter.
//
// The invocation below will need to expand to a TokenStream containing:
//
// compile_error!(concat!("error number ", stringify!(0)));
// compile_error!(concat!("error number ", stringify!(1)));
// compile_error!(concat!("error number ", stringify!(2)));
// compile_error!(concat!("error number ", stringify!(3)));
//
// This test is written as a compile_fail test because our macro isn't yet
// powerful enough to do anything useful. For example if we made it generate
// something like a function, every one of those functions would have the same
// name and the program would not compile.
use seq::seq;
seq!(N in 0..4 {
compile_error!(concat!("error number ", stringify!(N)));
});
fn main() {}
之前我們解析的時候,{}
內部是完全的按照rust
語法直接讀取。
從用例(提示)上面可以看到,應該是需要將N
在後續的程式碼片段中替換為對應的數值。
參考錯誤提示
error: error number 0
--> tests/03-expand-four-errors.rs:20:5
|
20 | compile_error!(concat!("error number ", stringify!(N)));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: error number 1
--> tests/03-expand-four-errors.rs:20:5
|
20 | compile_error!(concat!("error number ", stringify!(N)));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: error number 2
--> tests/03-expand-four-errors.rs:20:5
|
20 | compile_error!(concat!("error number ", stringify!(N)));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: error number 3
--> tests/03-expand-four-errors.rs:20:5
|
20 | compile_error!(concat!("error number ", stringify!(N)));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
我們還需要精準的定位到錯誤span
。
只要外部for
之後,找到對應的N
替換為當前的次數即可。
因為前面已經impl
了一個方法,後續題解都使用同樣的方法,直接在結構體內定義。
impl crate::parser::SeqParser {
pub(crate) fn expend_repeat(&self) -> proc_macro2::TokenStream {
let mut res = proc_macro2::TokenStream::new();
// 外層迴圈,遍歷語句並且替換N
for i in self.begin..self.end {
res.extend(self.do_expand_repeat(&self.body, i));
}
res
}
pub(crate) fn do_expand_repeat(
&self,
ts: &proc_macro2::TokenStream,
n: usize,
) -> proc_macro2::TokenStream {
let buf = &ts.clone().into_iter().collect::<Vec<_>>();
let mut res = proc_macro2::TokenStream::new();
let mut idx = 0usize;
while idx < buf.len() {
let node = &buf[idx];
match node {
// group
proc_macro2::TokenTree::Group(group) => {
let recurrence_expand_stream = self.do_expand_repeat(&group.stream(), n);
// 因為解析消耗了括號,我們應該補充回來
let mut wrap_in_group_stream =
proc_macro2::Group::new(group.delimiter(), recurrence_expand_stream);
// 注意span,否則報錯位置對不上
wrap_in_group_stream.set_span(group.clone().span());
res.extend(quote::quote!(#wrap_in_group_stream));
}
// 識別符號
proc_macro2::TokenTree::Ident(ident) => {
// 後續題解,當前無需關注
// if let std::option::Option::Some(token_stream) =
// self.process_prefix(&mut idx, n, ident, buf)
// {
// res.extend(token_stream);
// continue;
// }
// 如果是指定的標記,替換為當前的迴圈數值
// 因為生成程式碼,實際上是數字的字面值
if ident == &self.variable_ident {
let new_ident = proc_macro2::Literal::i64_unsuffixed(n as i64);
res.extend(quote::quote!(#new_ident));
} else { // 非迴圈標誌,直接新增
res.extend(quote::quote!(#node));
}
}
_ => { // 其他情況直接新增
res.extend(quote::quote!(#node));
}
}
idx += 1;
}
res
}
}
這裡利用了proc_macro2::TokenStream
轉化為陣列的能力,方便我們對節點的遍歷。
目前,我們已經自己組裝出了自定義的語法。
#[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 協議》,轉載必須註明作者和本文連結