1个不稳定版本

使用旧Rust 2015

0.1.0 2017年7月26日

#1793 in 进程宏


用于 unrest_codegen

MIT/Apache

430KB
11K SLoC

Rust源代码的Nom解析器

Build Status Latest Version Rust Documentation

不依赖于Syntex解析Rust源代码,旨在与宏1.1一起使用。

设计用于快速编译时间。

  • 从零开始编译syn的时间(包括所有依赖):6秒
  • 编译syntex/quasi/aster堆栈的时间:60+秒

如果您在使用宏1.1时遇到问题,即使问题与syn无关,我也很乐意提供帮助。请在此仓库中提交工单。

宏1.1的使用

[dependencies]
syn = "0.12"
quote = "0.3"

[lib]
proc-macro = true
extern crate proc_macro;
use proc_macro::TokenStream;

extern crate syn;

#[macro_use]
extern crate quote;

#[proc_macro_derive(MyMacro)]
pub fn my_macro(input: TokenStream) -> TokenStream {
    let source = input.to_string();

    // Parse the string representation into a syntax tree
    let ast = syn::parse_derive_input(&source).unwrap();

    // Build the output, possibly using quasi-quotation
    let expanded = quote! {
        // ...
    };

    // Parse back to a token stream and return it
    expanded.parse().unwrap()
}

完整示例

假设我们有一个以下简单的特质,该特质返回结构体中的字段数

trait NumFields {
    fn num_fields() -> usize;
}

基于synquote的宏1.1完整实现如下

extern crate proc_macro;
use proc_macro::TokenStream;

extern crate syn;

#[macro_use]
extern crate quote;

#[proc_macro_derive(NumFields)]
pub fn num_fields(input: TokenStream) -> TokenStream {
    let source = input.to_string();

    // Parse the string representation into a syntax tree
    let ast = syn::parse_derive_input(&source).unwrap();

    // Build the output
    let expanded = expand_num_fields(&ast);

    // Return the generated impl as a TokenStream
    expanded.parse().unwrap()
}

fn expand_num_fields(ast: &syn::DeriveInput) -> quote::Tokens {
    let n = match ast.body {
        syn::Body::Struct(ref data) => data.fields().len(),
        syn::Body::Enum(_) => panic!("#[derive(NumFields)] can only be used with structs"),
    };

    // Used in the quasi-quotation below as `#name`
    let name = &ast.ident;

    // Helper is provided for handling complex generic types correctly and effortlessly
    let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();

    quote! {
        // The generated impl
        impl #impl_generics ::mycrate::NumFields for #name #ty_generics #where_clause {
            fn num_fields() -> usize {
                #n
            }
        }
    }
}

有关涉及特质界限、枚举和不同类型结构的更复杂的示例,请参阅DeepClonedeep-clone-derive

测试

宏1.1有一个限制,即您的进程宏crate必须导出除proc_macro_derive函数之外的所有内容,并且proc_macro_derive进程宏不能在定义它们的同一crate中使用。这些限制可能在将来被解除,但到目前为止,这使得编写测试比其他类型的代码更困难。

特别是,您将无法像以下代码一样编写测试函数:#[test] fn it_works() { ... }与您的代码一致。相反,您可以将测试放在一个tests目录中,或者完全在另一个crate中。

此外,如果您的过程宏实现了特定的trait,则该trait必须在与过程宏分开的crate中定义。

作为一个具体的例子,假设您的过程宏crate名为my_derive,并实现了一个名为my_crate::MyTrait的trait。您的过程宏单元测试可以放在my_derive/tests/test.rs中,或者放入一个单独的crate my_tests/tests/test.rs中。无论哪种方式,测试看起来可能如下所示

#[macro_use]
extern crate my_derive;

extern crate my_crate;
use my_crate::MyTrait;

#[test]
fn it_works() {
    #[derive(MyTrait)]
    struct S { /* ... */ }

    /* test the thing */
}

调试

在开发过程宏时,查看生成的代码可能会有所帮助。使用cargo rustc -- -Zunstable-options --pretty=expandedcargo expand子命令。

要显示使用您的过程宏的某个crate的展开代码,在该crate中运行cargo expand。要显示您自己的测试用例的展开代码,运行cargo expand --test the_test_case,其中最后一个参数是不带.rs扩展名的测试文件名。

Brandon W Maister的这篇文档详细讨论了调试:调试Rust的新自定义Derive系统

可选功能

Syn在许多功能上都使用了可选功能,以优化常见用例的编译时间。以下是可以用的功能及其对编译时间的影响。依赖关系包括在编译时间中。

功能 编译时间 功能
(none) 3秒 表示Rust结构体、枚举和类型AST的数据结构。
解析 6秒 解析包含结构体和枚举的Rust源代码到AST。
打印 4秒 将结构体和枚举的AST打印成Rust源代码。
解析,打印 6秒 这是默认的。解析和打印Rust结构体和枚举。这通常是实现Macros 1.1自定义Derive时想要的。
full 4秒 表示所有可能Rust代码的完整AST的数据结构。
full,解析 9秒 将任何有效的Rust源代码解析到AST。
full,打印 6秒 将AST转换为Rust源代码。
full,解析,打印 11秒 解析和打印任何Rust语法。

许可证

许可在以下之一下

根据您的选择。

贡献

除非您明确声明,否则根据Apache-2.0许可证的定义,您有意提交给本crate的任何贡献,应按照上述条款双许可,不附加任何额外条款或条件。

依赖项

约230KB