8个版本
0.2.0 | 2023年11月13日 |
---|---|
0.1.6 | 2023年11月10日 |
0.1.5 | 2023年10月24日 |
#682 in Rust模式
在 macroex-extras 中使用
92KB
1.5K SLoC
macroex
基于提取器的低级宏解析包,通过 derive 宏提供高级解析支持。
FromMacro
和 Extractor
FromMacro
是这个包的精髓。它提供了 from_one
和 from_many
函数,分别解析 TokenTree
和 TokenStream
。
我们通常假设 from_many
将包含两个或更多的 TokenTrees
。如果不是,则认为提取器存在错误。
FromMacro
的所有实现者都是单个 TokenTree
的 Extractor
。当直接在 TokenStream
迭代器上使用时,它们将消耗一个 TokenTree
并尝试使用 from_one
进行解析。
let mut iter = quote!{45, Hello; true false}.into_iter();
let a: i32 = iter.extract()?;
let b: Punct = iter.extract()?;
// Extracts a string value from an ident
let IdentString(c) = iter.extract()?;
// Validates a semicolon
let d: PunctOf<';'> = iter.extract()?;
let e: bool = iter.extract()?;
// Validates a false literal
let f: LitFalse = iter.extract()?;
这非常棒!因为大多数事情都可以表示为一个单独的 TokenTree
。
// This is a single TokenTree::Group
{
name: "Tom".
age: 45,
children: [
"Tim", "Tam"
],
}
然而,还有其他一些 TokenTree
无法处理的事情。
// This fails because -45 is two tokens
let a: i32 = quote!{-45}.into_iter().extract().unwrap();
将其他 Extractor
包裹在 FromMacro
实现者中,允许它们解析额外的 TokenTrees
,并且如果匹配到多个 TokenTree
,还可以利用 from_many
方法。
// Note -45 is two tokens
let mut iter = quote!{-45}.into_iter();
// All extracts everything from a stream
let All(a) = iter.extract()?;
assert_eq!(a, -45i32);
let mut iter = quote!{-45, 21, 9.5,}.into_iter();
// CommaExtractor extracts until a comma or end of stream is found.
let CommaExtractor(a) = iter.extract()?;
let CommaExtractor(b) = iter.extract()?;
let CommaExtractor(c) = iter.extract()?;
// EndOfStream is a unit struct extractor and this asserts iter is empty
let EndOfStream = iter.extract()?;
assert_eq!(a, -45);
assert_eq!(b, 21);
assert_eq!(c, 9.5);
Derive
我们提供了类似 serde::Deserialize
的 derive 宏 FromMacro
和 FromAttrs
,这使得根据特定数据格式进行结构体和枚举的解析变得更为便捷。
FromMacro
可以解析类似于原生Rust的语法,而FromAttrs
则解析宏属性中常用的语法。
为什么不使用serde_tokenstream
呢?
因为我们没有使用serde
数据模型。在宏的上下文中,我们的数据模型要强大得多。我们可以提取所有FromMacro
实现者,包括TokenStream
、Ident
、Group
等。
FromMacro
FromMacro
可以解析类似于原生Rust的语法。
类型 | from_one |
from_many |
---|---|---|
单元结构体 | 结构体名称 |
-- |
元组结构体 | (元组, ..) |
结构体名称(元组, ..) |
命名结构体 | {字段:值, ..} |
结构体名称{字段:值, ..} |
单元枚举变体 | 变体名称 |
-- |
元组枚举变体 | -- | 变体名称(元组, ..) |
命名枚举变体 | -- | 变体名称{字段:值, ..} |
示例
类型 | Rust类型 | from_one |
from_many |
---|---|---|---|
单元结构体 | struct Red; |
Red |
-- |
元组结构体 | struct Vec2 (i32, i32) |
(4, 5) |
Vec2(4, 5) |
命名结构体 | struct Vec2 {x: i32, y: i32} |
{x: 4,y: 5} |
Vec2{x: 4,y: 5} |
单元变体 | enum Color {Black,White} |
Black |
-- |
元组变体 | enum Animals {Dog(String),Sheep(usize)} |
-- | Dog("Rex") |
命名变体 | enum Shapes {Square{x: f32},Rect{x: f32,y: f32}} |
-- | Rect{x: 4,y: 5} |
用例
由于我们很可能在宏中解析配置,如果找不到字段,我们将提供一个Default::default()
值。
如果你的类型没有实现Default
,则需要使用#[macroex(required)]
来选择退出。
#[derive(FromMacro)]
pub struct Person {
pub name: String,
pub age: i32,
pub height: f32,
// This works as long as Gender implements `FromMacro` and `Default`
pub gender: Gender,
// Using Option is idiomatic to handle the default case.
pub hair_color: Option<NumberList<[f32;4]>>,
// We can extract macro based things
pub do_something: Option<TokenStream>,
}
示例宏输入
person! {
name: "Lina",
age: 23,
gender: Female,
hair_color: [0.7, 0.4, 0],
do_something: {
let w = "world";
println!("Hello, {}!", w)
},
}
属性
FromMacro
宏支持以下属性
#[derive(FromMacro)]
// We use the same casing names as serde.
#[macroex(rename_all="SCREAMING-KEBAB-CASE")]
pub struct Animal {
// Errors if not specified.
#[macroex(required)]
pub name: String,
// Evaluate an expression instead of `Default::default()`
#[macroex(default="0.0")]
pub height: f32,
#[macroex(default=r#""dog".to_owned()"#)]
pub species: String,
// Take strings as inputs, and collects them into a vec.
#[macroex(repeat)]
// and rename "nicknames" to "nickname" during parsing.
#[macroex(rename="nickname")]
pub nicknames: Vec<String>,
}
FromAttrs
FromAttrs
为与宏属性相关的语法生成简单的FromMacro
实现。
此宏仅允许在命名结构体上使用,并支持3种基本语法
.., name, ..
解析为name: true
,这匹配布尔值。.., name = expr, ..
解析为name: expr
.., name(..), ..
转换为name: T{ .. }
其他类型,如无字段的枚举,可以使用 FromMacro
来生成兼容的 FromMacro
实现,以供此宏使用。
示例
我们使用与 FromMacro
相同的属性集。
#[derive(FromAttrs)]
#[macroex(rename_all="snake_case")]
pub struct Animal {
#[macroex(required)]
pub name: String,
#[macroex(default="0.0")]
pub height: f32,
#[macroex(default=r#"Ident::new("Dog", Span::call_site())"#)]
pub species: Ident,
#[macroex(repeat, rename="mascot")]
pub mascot_infos: Vec<MascotInfo>,
}
示例属性
#[animal(name = "Ferris", species = Crab, mascot(language = "Rust"))]
我们可以解析以下内容:
(name = "Ferris", species = Crab, mascot(language = "Rust"))
使用 from_one
,或者
name = "Ferris", species = Crab, mascot(language = "Rust")
使用 from_many
,通常与 syn
一起提取。
宏链和卫生
我们将输入视为字符串类型,并将尝试在解析过程中平铺所有遇到的 None
定界符组。
依赖项
~0.5–1MB
~22K SLoC