2个不稳定版本
使用旧的Rust 2015
0.13.0 | 2018年3月5日 |
---|---|
0.12.10 | 2018年2月21日 |
#961 in Rust模式
90 每月下载量
1MB
18K SLoC
Rust源代码的Nom解析器
Syn是一个解析库,可以将Rust令牌流解析为Rust源代码的语法树。
目前该库主要针对自定义推导用例,但包含一些可能对Rust过程宏更有用的API。
-
数据结构 — Syn提供了一个完整的语法树,可以表示任何有效的Rust源代码。语法树以
syn::File
为根,它代表一个完整的源文件,但还有其他可能对过程宏有用的入口点,包括syn::Item
、syn::Expr
和syn::Type
。 -
自定义推导 — 对自定义推导特别有用的是
syn::DeriveInput
,这是推导宏的三个合法输入项之一。以下示例展示了在可以推导你自己的特质的库中使用此类型。 -
解析器组合子 — Syn中的解析基于一系列公开的解析器组合子宏,您可以使用这些宏来解析在
functionlike!(...)
程序性宏中构思的任何基于令牌的语法。Syn定义的每个语法树节点都可以单独解析,可以用作自定义语法的构建块,或者您可以从最基本的令牌开始自行完成。 -
位置信息 — Syn解析的每个令牌都与一个
Span
相关联,该 Span 跟踪行和列信息,直至该令牌的源。这些 Span 允许程序性宏显示指向用户代码中所有正确位置的详细错误消息。以下是一个示例。 -
功能标志 — 功能性是积极受限的,因此您的程序性宏只启用它们需要的功能,并且不会为其他所有功能支付编译时间。
如果您在 Rust 中遇到任何程序性宏相关的问题,即使问题与 Syn 无关,我也很乐意提供帮助。请在本存储库中提交工单。
版本要求:Syn 支持 Rust 1.15.0 中程序性宏的首次支持以来的任何编译器版本。一些功能,特别是错误报告功能,仅在较新的编译器或夜间频道上可用。
自定义 derive 示例
使用 Syn 的规范自定义 derive 看起来像这样。我们编写一个带有 proc_macro_derive
属性和我们要推导的特质名称的普通 Rust 函数。每当该 derive 出现在用户的代码中时,Rust 编译器都会将用户的数据结构作为令牌传递给我们的宏。我们可以执行任意 Rust 代码来确定如何处理这些令牌,然后将一些令牌返回给编译器以编译到用户的包中。
[dependencies]
syn = "0.12"
quote = "0.4"
[lib]
proc-macro = true
extern crate proc_macro;
extern crate syn;
#[macro_use]
extern crate quote;
use proc_macro::TokenStream;
use syn::DeriveInput;
#[proc_macro_derive(MyMacro)]
pub fn my_macro(input: TokenStream) -> TokenStream {
// Parse the input tokens into a syntax tree
let input: DeriveInput = syn::parse(input).unwrap();
// Build the output, possibly using quasi-quotation
let expanded = quote! {
// ...
};
// Hand the output tokens back to the compiler
expanded.into()
}
heapsize
示例目录展示了自定义 derive 的完整工作 Macros 1.1 实现。它在任何 Rust 编译器 >=1.15.0 上工作。该示例推导出一个 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,
}
Span 和错误报告
heapsize2
示例目录是 heapsize
示例的扩展,它演示了 Macros 2.0 的一些卫生和错误报告属性。该示例目前需要 nightly Rust 编译器 >=1.24.0-nightly,但我们正在努力稳定所有相关的 API。
基于令牌的程序性宏 API 为编译器错误消息在用户代码中的显示位置提供了极大的控制。考虑用户看到的错误,如果他们的字段类型未实现 HeapSize
。
#[derive(HeapSize)]
struct Broken {
ok: String,
bad: std::thread::Thread,
}
在 Macros 1.1 基于字符串的程序性宏世界中,生成的错误会无用地指向 derive 宏的调用,而不是指向实际有问题的字段。
error[E0599]: no method named `heap_size_of_children` found for type `std::thread::Thread` in the current scope
--> src/main.rs:4:10
|
4 | #[derive(HeapSize)]
| ^^^^^^^^
如 heapsize2
示例中所示,通过跟踪程序性宏展开过程中的 span 信息,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
展示了一个 functionlike!(...)
过程宏的实现,其中输入标记被使用 nom
风格的解析组合器解析。
示例重新实现了 crates.io 上流行的 lazy_static
crate 作为过程宏。
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 系统。
可选功能
Syn 将许多功能放在可选功能后面,以优化最常见用例的编译时间。以下功能可用。
derive
(默认启用) — 表示自定义 derive 可能有输入的数据结构,包括结构体、枚举和类型。full
— 表示所有有效 Rust 源代码的语法树的 Data 结构,包括项和表达式。parsing
(默认启用) — 能够将输入标记解析为所选类型的语法树节点。printing
(默认启用) — 能够将语法树节点打印为 Rust 源代码的标记。visit
— 用于遍历语法树的 Trait。visit-mut
— 用于遍历和就地修改语法树的 Trait。fold
— 用于转换拥有语法树的 Trait。clone-impls
(默认启用) — 所有语法树类型的 Clone 实现。extra-traits
— 所有语法树类型的 Debug、Eq、PartialEq、Hash 实现。
夜间功能
默认情况下,Syn 使用 proc-macro2
crate 以稳定的方式模拟夜间编译器的过程宏 API,该 API 一直支持到 Rust 1.15.0。这个中间件使得编写代码时无需关心当前编译器版本是否支持我们使用的功能。
在夜间编译器上,为了消除稳定的填充件并直接使用编译器的 proc-macro
,请将 proc-macro2
添加到您的 Cargo.toml 文件中,并设置其 "nightly"
功能,从而绕过稳定的填充件。
[dependencies]
syn = "0.12"
proc-macro2 = { version = "0.2", features = ["nightly"] }
许可证
许可方式任选其一
- Apache 许可证 2.0 版(LICENSE-APACHE 或 https://apache.ac.cn/licenses/LICENSE-2.0)
- MIT 许可证(LICENSE-MIT 或 http://opensource.org/licenses/MIT)
您可自行选择。
贡献
除非您明确声明,否则根据 Apache-2.0 许可证的定义,您提交的任何有意包含在此软件包中的贡献将如上所述双重许可,而不附加任何额外条款或条件。
依赖项
~235KB