// So far our macro has repeated the entire loop body. This is not sufficient
// for some use cases because there are restrictions on the syntactic position
// that macro invocations can appear in. For example the Rust grammar would not
// allow a caller to write:
//
// enum Interrupt {
// seq!(N in 0..16 {
// Irq~N,
// });
// }
//
// because this is just not a legal place to put a macro call.
//
// Instead we will implement a way for the caller to designate a specific part
// of the macro input to be repeated, so that anything outside that part does
// not get repeated. The repeated part will be written surrounded by #(...)*.
//
// The invocation below should expand to:
//
// #[derive(Copy, Clone, PartialEq, Debug)]
// enum Interrupt {
// Irq0,
// ...
// Irq15,
// }
//
// Optionally, allow for there to be multiple separate #(...)* sections,
// although the test suite does not exercise this case. The #(...)* sections
// will each need to be repeated according to the same loop bounds.
use seq::seq;
seq!(N in 0..16 {
#[derive(Copy, Clone, PartialEq, Debug)]
enum Interrupt {
#(
Irq~N,
)*
}
});
fn main() {
let interrupt = Interrupt::Irq8;
assert_eq!(interrupt as u8, 8);
assert_eq!(interrupt, Interrupt::Irq8);
}
在這裡,我們追尋到了熟悉的味道#(?)*
。
曾經在builder
和debug
我們大量使用它,現在我們需要去實現它。
主要是以#
開始的情況下,接下來解析(?)
,並且以*
結尾的情況下。
將?
內部的邏輯,用seq-4中的邏輯去實現。
impl crate::parser::SeqParser {
pub(crate) fn expand_section(&self) -> std::option::Option<proc_macro2::TokenStream> {
let buffer = syn::buffer::TokenBuffer::new2(self.body.clone());
let (expended, expend_section_stream) = self.do_expand_section(buffer.begin());
if expended {
return std::option::Option::Some(expend_section_stream);
}
std::option::Option::None
}
pub(crate) fn do_expand_section(
&self,
origin_cursor: syn::buffer::Cursor,
) -> (bool, proc_macro2::TokenStream) {
let mut found = false;
let mut res = proc_macro2::TokenStream::new();
// 因為存在# (?) *的解析情況,直接使用陣列的形式解析不夠連貫
// 這裡採用cursor會更加絲滑
// 不便的就是處理的專案需要更加具體的列舉出來
let mut cursor = origin_cursor;
while !cursor.eof() {
// 符號檢測,主要答題邏輯
if let Some((prefix, prefix_next_cursor)) = cursor.punct() {
if prefix.as_char() == '#' {
if let Some((group_cursor, _, group_next_cursor)) =
prefix_next_cursor.group(proc_macro2::Delimiter::Parenthesis)
{
if let Some((suffix, suffix_next_cursor)) = group_next_cursor.punct() {
if suffix.as_char() == '*' {
for i in self.begin..self.end {
// 匹配部分,使用之前的方式進行展開
let t = self.do_expand_repeat(&group_cursor.token_stream(), i);
res.extend(t);
}
cursor = suffix_next_cursor;
found = true;
continue;
}
}
}
}
}
// {},遞迴展開
if let Some((group_cursor, _, group_next_cursor)) =
cursor.group(proc_macro2::Delimiter::Brace)
{
let (sub_found, sub_stream) = self.do_expand_section(group_cursor);
found = sub_found;
// 注意{}還原
res.extend(quote::quote!({#sub_stream}));
cursor = group_next_cursor;
continue;
// [],遞迴展開
} else if let Some((group_cursor, _, group_next_cursor)) =
cursor.group(proc_macro2::Delimiter::Bracket)
{
let (sub_found, sub_stream) = self.do_expand_section(group_cursor);
found = sub_found;
// [] 還原
res.extend(quote::quote!([#sub_stream]));
cursor = group_next_cursor;
continue;
// (),遞迴展開
} else if let Some((group_cursor, _, group_next_cursor)) =
cursor.group(proc_macro2::Delimiter::Parenthesis)
{
let (sub_found, sub_stream) = self.do_expand_section(group_cursor);
found = sub_found;
// ()還原
res.extend(quote::quote!((#sub_stream)));
cursor = group_next_cursor;
continue;
// 其他情況直接新增,不過列舉匹配麻煩
} else if let Some((punct, next)) = cursor.punct() {
res.extend(quote::quote!(#punct));
cursor = next;
continue;
} else if let Some((ident, next)) = cursor.ident() {
res.extend(quote::quote!(#ident));
cursor = next;
continue;
} else if let Some((literal, next)) = cursor.literal() {
res.extend(quote::quote!(#literal));
cursor = next;
continue;
} else if let Some((lifetime, next)) = cursor.lifetime() {
res.extend(quote::quote!(#lifetime));
cursor = next;
continue;
}
}
(found, res)
}
}
在這裡,有一個容易疏忽的點,那就是不一定存在#()*
的結構。
因此我們需要返回一個found
的標記,因為針對section
的展開相容了repeat
。
但是repeat
不相容section
,因此,如果section
展開了,就可以直接返回。
反之,進行repeat
展開。
#[proc_macro]
pub fn seq(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let parser = syn::parse_macro_input!(input as crate::parser::SeqParser);
if let std::option::Option::Some(repeat_section_stream) = parser.expand_section() {
return repeat_section_stream.into();
}
return parser.expend_repeat().into();
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結