前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Rust 过程宏之Syn(crate)

Rust 过程宏之Syn(crate)

原创
作者头像
8菠萝
修改2021-06-22 08:00:00
2.7K0
修改2021-06-22 08:00:00
举报
文章被收录于专栏:菠萝上市没有菠萝上市没有

概念

在编写过程宏时,经常需要对TokenStream 进行解析和处理。 而Syn库就是专门用于对TokenStream进行解析。

Syn is a parsing library for parsing a stream of Rust tokens into a syntax tree of Rust source code.

Syn 用于将Rust tokens 解析为Rust 源码语法树。

实践

在过程宏使用Syn解析的流程:

  1. 定义自己存储结构
  2. 实现syn::parse::Parse
  3. 使用parse_macro_input!()生成源码树。
代码语言:rust
复制
// 定义自己的存储结构
struct ItemStruct {
  pub struct_token: Token![struct],
  pub ident: Ident,
  pub brace_token: token::Brace,
}

// 实现syn::parse::Parse 接口
impl Parse for ItemStruct {
    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
        let content;
        let lookahead = input.lookahead1();
        // 只针对struct类型
        if lookahead.peek(Token![struct]) {
            return Ok(ItemStruct {
                struct_token: input.parse()?,
                ident: input.parse()?,
                brace_token: braced!(content in input),
            });
        }
        Err(lookahead.error())
    }

}

// 在派生宏中使用 parse_macro_input
#[proc_macro_derive(SynTest)]
pub fn syn_test(input: TokenStream)->TokenStream{
    let input = parse_macro_input!(input as ItemStruct);
    println!("MyParse:{:?}", input.struct_token.to_token_stream());
    println!("MyIdent:{:?}", input.ident.to_token_stream());
    TokenStream::new()
}



// main.rs 
// #[derive(SynTest)]
// struct World{}

// 程序输出:
// MyParse:TokenStream [Ident { ident: "struct", span: #0 bytes(290..296) }]
// MyIdent:TokenStream [Ident { ident: "World", span: #0 bytes(297..302) }]

----

当然,Syn已经内置一个DeriveInput类型, 用于struct、enum、unions类型的解析,改写下上面的例子。

代码语言:javascript
复制
/* Struct syn::DeriveInput 

pub struct DeriveInput {
    pub attrs: Vec<Attribute>,
    pub vis: Visibility,
    pub ident: Ident,
    pub generics: Generics,
    pub data: Data,
}
*/

#[proc_macro_derive(SynTest)]
pub fn syn_test(input: TokenStream)->TokenStream{
    // 将ItemStruct => DeriveInput
    let input = parse_macro_input!(input as DeriveInput);
    println!("MyIdent:{:?}", input.ident.to_token_stream());
    TokenStream::new()
}

通过Syn生成语法树后,后面可用使用quote! 处理源码树,实现需要的扩展功能。

使用过程宏为struct添加hello方法:

  1. 使用parse_macro_input 解析到DeriveInput
  2. 获取struct name TokenStream
  3. 使用quote! 扩展改结构体
代码语言:rust
复制
#[proc_macro_derive(Hello)]
pub fn hello_derive(input: TokenStream)->TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    println!("{:?}", input.ident.to_token_stream());
    let name = input.ident.to_token_stream();
    
    // #name 为上面获取的struct name
    let expanded = quote! {
        impl #name {
            pub fn hello(){
                println!("Hello World!");
            }
        }
    };
    expanded.into()
}

// main.rs
#[derive(Hello)]
struct Test{}
fn main() {
    Test::hello();
}

// 输出:
// Hello World!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 概念
    • 实践
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档