proc-macro-workshop:seq-5

审题

// 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);
}

在这里,我们追寻到了熟悉的味道#(?)*
曾经在builderdebug我们大量使用它,现在我们需要去实现它。
主要是以#开始的情况下,接下来解析(?),并且以*结尾的情况下。
?内部的逻辑,用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 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

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