1个不稳定版本
使用旧Rust 2015
0.1.0 | 2017年7月26日 |
---|
#1793 in 进程宏
430KB
11K SLoC
Rust源代码的Nom解析器
不依赖于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;
}
基于syn
和quote
的宏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
}
}
}
}
有关涉及特质界限、枚举和不同类型结构的更复杂的示例,请参阅DeepClone
和deep-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=expanded
或cargo 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 License,Version 2.0 (LICENSE-APACHE 或 http://www.apache.org/licenses/LICENSE-2.0)
- 麻省理工学院许可证(LICENSE-MIT 或 http://opensource.org/licenses/MIT)
根据您的选择。
贡献
除非您明确声明,否则根据Apache-2.0许可证的定义,您有意提交给本crate的任何贡献,应按照上述条款双许可,不附加任何额外条款或条件。
依赖项
约230KB