1个不稳定版本
使用旧Rust 2015
0.1.0 | 2017年7月26日 |
---|
#2152 in 过程宏
在 2 个crate中使用(通过 unrest_tmp_syn)
75KB
1.5K SLoC
Nom解析器,用于Rust源代码
无需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,它返回结构体中的字段数
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将很多功能放在可选功能之后,以优化最常见的用例的编译时间。以下是可用的功能和它们对编译时间的影响。依赖项包含在编译时间中。
功能 | 编译时间 | 功能 |
---|---|---|
(无) | 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,版本2.0 (LICENSE-APACHE 或 http://www.apache.org/licenses/LICENSE-2.0)
- MIT许可证 (LICENSE-MIT 或 http://opensource.org/licenses/MIT)
at your option.
贡献
除非您明确声明,否则根据Apache-2.0许可证定义,您有意提交以包含在本软件包中的任何贡献,将按上述方式双重许可,不附加任何额外条款或条件。
依赖项
~230KB