1个不稳定版本
使用旧的Rust 2015
0.15.30 | 2019年5月13日 |
---|
#1551 在 进程宏
用于 sonic_spin
1MB
22K SLoC
Rust源代码解析器
Syn是一个解析库,可以将Rust标记流解析为Rust源代码的语法树。
目前这个库主要面向Rust进程宏的使用,但包含一些可能在更广泛范围内有用的API。
-
数据结构 — Syn提供了一个完整的语法树,可以表示任何有效的Rust源代码。语法树以
syn::File
为根,它表示一个完整的源文件,但还有一些其他可能对进程宏有用的入口点,包括syn::Item
、syn::Expr
和syn::Type
。 -
自定义衍生 — 对于自定义衍生特别重要的是
syn::DeriveInput
,它是衍生宏的三个合法输入项之一。以下示例展示了在一个可以派生自定义特实现库中使用此类型。 -
解析 — Syn中的解析围绕具有以下签名的解析函数构建:
fn(ParseStream) -> Result<T>
。Syn定义的每个语法树节点都可以单独解析,可以用作自定义语法的构建块,或者你可以设计出自己的全新语法,而不涉及任何我们的语法树类型。 -
位置信息 — Syn解析的每个标记都与一个
Span
相关联,该Span跟踪行和列信息,回溯到该标记的源。这些跨度允许过程宏显示指向用户代码中所有正确位置的详细错误消息。以下是一个示例。 -
功能标志 — 功能被积极功能门控,因此您的过程宏只能启用它们所需的,而不必为所有其他功能在编译时付费。
如果您在Rust中的过程宏方面遇到任何问题,我很乐意提供帮助,即使问题与Syn无关。请在本存储库中提交工单。
版本要求:Syn支持Rust 1.15.0首次支持过程宏以来的任何编译器版本。一些功能,特别是关于错误报告的功能,仅在较新的编译器或夜间通道中可用。
自定义 derive 示例
使用 Syn 的规范自定义 derive 看起来像这样。我们编写一个带有 proc_macro_derive
属性和我们要推导的特名称的普通 Rust 函数。每当该 derive 出现在用户的代码中时,Rust 编译器将它们的数据结构作为标记传递给我们的宏。我们可以执行任意 Rust 代码来确定如何处理这些标记,然后将一些标记返回给编译器,以便将其编译成用户的包。
[dependencies]
syn = "0.15"
quote = "0.6"
[lib]
proc-macro = true
extern crate proc_macro;
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};
#[proc_macro_derive(MyMacro)]
pub fn my_macro(input: TokenStream) -> TokenStream {
// Parse the input tokens into a syntax tree
let input = parse_macro_input!(input as DeriveInput);
// Build the output, possibly using quasi-quotation
let expanded = quote! {
// ...
};
// Hand the output tokens back to the compiler
TokenStream::from(expanded)
}
heapsize
示例目录展示了自定义 derive 的完整工作 Macros 1.1 实现。它在任何 Rust 编译器 1.15+ 上工作。示例推导出一个 HeapSize
特性,该特性计算一个值拥有的堆内存估计量。
pub trait HeapSize {
/// Total number of bytes of heap memory owned by `self`.
fn heap_size_of_children(&self) -> usize;
}
自定义 derive 允许用户在他们的程序数据结构上编写 #[derive(HeapSize)]
。
#[derive(HeapSize)]
struct Demo<'a, T: ?Sized> {
a: Box<T>,
b: u8,
c: &'a str,
d: String,
}
跨度与错误报告
基于标记的过程宏 API 为编译器错误消息在用户代码中的显示位置提供了很好的控制。考虑如果用户的字段类型没有实现 HeapSize
时用户会看到的错误。
#[derive(HeapSize)]
struct Broken {
ok: String,
bad: std::thread::Thread,
}
如 heapsize
示例中所示,通过跟踪过程宏的展开过程中的跨度信息,基于标记的 Syn 宏能够触发直接指向问题源的错误。
error[E0277]: the trait bound `std::thread::Thread: HeapSize` is not satisfied
--> src/main.rs:7:5
|
7 | bad: std::thread::Thread,
| ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `HeapSize` is not implemented for `std::thread::Thread`
解析自定义语法
lazy-static
示例目录展示了使用 Syn 的解析 API 解析输入标记的 functionlike!(...)
过程宏的实现。
示例重新实现了 crates.io 上的流行 lazy_static
包,作为一个过程宏。
lazy_static! {
static ref USERNAME: Regex = Regex::new("^[a-z0-9_-]{3,16}$").unwrap();
}
实现展示了如何在宏输入上触发自定义警告和错误消息。
warning: come on, pick a more creative name
--> src/main.rs:10:16
|
10 | static ref FOO: String = "lazy_static".to_owned();
| ^^^
调试
在开发过程宏时,查看生成的代码可能很有帮助。使用 cargo rustc -- -Zunstable-options --pretty=expanded
或 cargo expand
子命令。
要显示使用您的过程宏的某些crate的展开代码,请在该crate中运行cargo expand
。要显示您自己的测试用例之一的展开代码,请运行cargo expand --test the_test_case
,其中最后一个参数是不带.rs
扩展名的测试文件名。
Brandon W Maister的这篇文档详细讨论了调试:[调试Rust的新自定义Derive系统](https://quodlibetor.github.io/posts/debugging-rusts-new-custom-derive-system/)
可选功能
Syn通过将许多功能放在可选功能之后来优化最常见用例的编译时间。以下功能可用。
derive
(默认启用)—— 表示自定义derive可能输入的数据结构,包括结构体、枚举和类型。full
—— 表示所有有效Rust源代码语法树的的数据结构,包括项目和表达式。parsing
(默认启用)—— 将输入令牌解析为所选类型的语法树节点的能力。printing
(默认启用)—— 将语法树节点打印为Rust源代码令牌的能力。visit
—— 用于遍历语法树的trait。visit-mut
—— 用于遍历和就地修改语法树的trait。fold
—— 用于转换拥有语法树的trait。clone-impls
(默认启用)—— 为所有语法树类型实现Clone。extra-traits
—— 为所有语法树类型实现Debug、Eq、PartialEq、Hash。proc-macro
(默认启用)—— 对rustc工具链中动态库libproc_macro的运行时依赖。
过程宏shim
Syn使用proc-macro2 crate以稳定的方式模拟编译器的过程宏API,适用于从Rust 1.15.0开始的所有版本。这个shim使得可以编写不关心当前编译器版本是否支持我们使用的功能的代码。
通常,所有代码都应针对proc-macro2编写,而不是proc-macro。一个例外是过程宏入口点的签名,语言要求使用proc_macro::TokenStream
。
proc-macro2 crate将在足够新的编译器上自动检测并使用编译器的数据结构。
许可证
根据以下任一许可证授权:
- Apache License,版本2.0([LICENSE-APACHE](https://github.com/swfsql/syn/blob/81c3383e477e586496b26a2980d35de95a675448/LICENSE-APACHE) 或 [https://apache.ac.cn/licenses/LICENSE-2.0](https://apache.ac.cn/licenses/LICENSE-2.0))
- MIT许可证([LICENSE-MIT](https://github.com/swfsql/syn/blob/81c3383e477e586496b26a2980d35de95a675448/LICENSE-MIT) 或 [https://open-source.org.cn/licenses/MIT](https://open-source.org.cn/licenses/MIT))
由您选择。
贡献
除非您明确声明,否则根据Apache-2.0许可证定义的,您提交给此crate的任何有意包含的贡献,将根据上述许可证双授权,没有任何额外的条款或条件。