如何在不重复规则的情况下为宏编写包装器?
How do I write a wrapper for a macro without repeating the rules?
我正在尝试为宏制作一个包装器。问题是我不想在两个宏中重复相同的规则。有办法吗?
这是我尝试过的:
macro_rules! inner {
($test:ident) => { stringify!($test) };
($test:ident.run()) => { format!("{}.run()", stringify!($test)) };
}
macro_rules! outer {
($expression:expr) => {
println!("{}", inner!($expression));
}
}
fn main() {
println!("{}", inner!(test));
println!("{}", inner!(test.run()));
outer!(test);
outer!(test.run());
}
但我收到以下错误:
src/main.rs:8:31: 8:42 error: expected ident, found test
src/main.rs:8 println!("{}", inner!($expression));
^~~~~~~~~~~
如果我为此更改 outer
宏,代码编译:
macro_rules! outer {
($expression:expr) => {
println!("{}", stringify!($expression));
}
}
我做错了什么?
macro_rules!
比您可能意识到的 聪明 和 笨。
最初,所有 宏输入开始作为未分化的令牌汤开始生活。一个 Ident
这里, StrLit
那里, 等 然而,当你匹配并捕获一些输入时,通常输入将被解析为抽象语法树节点; expr
.
就是这种情况
"clever" 位是当您替换此捕获时(例如,$expression
),您不只是替换最初匹配的标记:您替换 作为单个令牌的整个 AST 节点。所以现在输出中有这个奇怪的不是真正的标记,它是一个完整的语法元素。
"dumb" 位是这个过程基本上是不可逆的,并且大部分 完全不可见 。那么让我们举个例子:
outer!(test);
我们运行这个通过一级展开,就变成了这个:
println!("{}", inner!(test));
只是,不是它的样子。为了让事情更清楚,我将发明一些非标准语法:
println!("{}", inner!( $(test):expr ));
假设 $(test):expr
是单个标记:它是一个可以用标记序列 test
表示的表达式。它 而不是 只是令牌序列 test
。这很重要,因为当宏解释器展开 inner!
宏时,它会检查第一条规则:
($test:ident) => { stringify!($test) };
问题是$(test):expr
是一个表达式,不是标识符。是的,它包含一个标识符,但宏解释器看起来并不那么深。它看到一个表达式,然后放弃。
由于同样的原因,它无法匹配第二条规则。
那你是做什么的? ... 好吧,这取决于。如果 outer!
不对其输入进行 任何 类型的处理,您可以改用 tt
匹配器:
macro_rules! outer {
($($tts:tt)*) => {
println!("{}", inner!($($tts)*));
}
}
tt
将匹配 any 令牌树(参见 Macros chapter of the Rust Book)。 $($tts:tt)*
将匹配 any 个标记序列,而不更改它们。这是一种将一堆令牌安全地转发到另一个宏的方法。
如果您需要对输入 和 进行处理,将其转发给 inner!
宏...您可能不得不重复这些规则.
我在 $($stuff: expr),+
语法方面取得了一些成功。
macro_rules! println {
( $($stuff: expr),+) => {
avr_device::interrupt::free(|cs| {
uwriteln!(unsafe { &SERIAL_STATIC}.borrow(cs).borrow_mut().as_mut().unwrap(),
$($stuff),+)
})
}
}
我正在尝试为宏制作一个包装器。问题是我不想在两个宏中重复相同的规则。有办法吗?
这是我尝试过的:
macro_rules! inner {
($test:ident) => { stringify!($test) };
($test:ident.run()) => { format!("{}.run()", stringify!($test)) };
}
macro_rules! outer {
($expression:expr) => {
println!("{}", inner!($expression));
}
}
fn main() {
println!("{}", inner!(test));
println!("{}", inner!(test.run()));
outer!(test);
outer!(test.run());
}
但我收到以下错误:
src/main.rs:8:31: 8:42 error: expected ident, found test
src/main.rs:8 println!("{}", inner!($expression));
^~~~~~~~~~~
如果我为此更改 outer
宏,代码编译:
macro_rules! outer {
($expression:expr) => {
println!("{}", stringify!($expression));
}
}
我做错了什么?
macro_rules!
比您可能意识到的 聪明 和 笨。
最初,所有 宏输入开始作为未分化的令牌汤开始生活。一个 Ident
这里, StrLit
那里, 等 然而,当你匹配并捕获一些输入时,通常输入将被解析为抽象语法树节点; expr
.
"clever" 位是当您替换此捕获时(例如,$expression
),您不只是替换最初匹配的标记:您替换 作为单个令牌的整个 AST 节点。所以现在输出中有这个奇怪的不是真正的标记,它是一个完整的语法元素。
"dumb" 位是这个过程基本上是不可逆的,并且大部分 完全不可见 。那么让我们举个例子:
outer!(test);
我们运行这个通过一级展开,就变成了这个:
println!("{}", inner!(test));
只是,不是它的样子。为了让事情更清楚,我将发明一些非标准语法:
println!("{}", inner!( $(test):expr ));
假设 $(test):expr
是单个标记:它是一个可以用标记序列 test
表示的表达式。它 而不是 只是令牌序列 test
。这很重要,因为当宏解释器展开 inner!
宏时,它会检查第一条规则:
($test:ident) => { stringify!($test) };
问题是$(test):expr
是一个表达式,不是标识符。是的,它包含一个标识符,但宏解释器看起来并不那么深。它看到一个表达式,然后放弃。
由于同样的原因,它无法匹配第二条规则。
那你是做什么的? ... 好吧,这取决于。如果 outer!
不对其输入进行 任何 类型的处理,您可以改用 tt
匹配器:
macro_rules! outer {
($($tts:tt)*) => {
println!("{}", inner!($($tts)*));
}
}
tt
将匹配 any 令牌树(参见 Macros chapter of the Rust Book)。 $($tts:tt)*
将匹配 any 个标记序列,而不更改它们。这是一种将一堆令牌安全地转发到另一个宏的方法。
如果您需要对输入 和 进行处理,将其转发给 inner!
宏...您可能不得不重复这些规则.
我在 $($stuff: expr),+
语法方面取得了一些成功。
macro_rules! println {
( $($stuff: expr),+) => {
avr_device::interrupt::free(|cs| {
uwriteln!(unsafe { &SERIAL_STATIC}.borrow(cs).borrow_mut().as_mut().unwrap(),
$($stuff),+)
})
}
}