[英]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
的情況。
“聰明”位是當你替換這個捕獲(例如, $expression
)時,你不只是替換最初匹配的標記:你將整個AST節點替換為單個標記 。 所以現在輸出中的這個奇怪的非真正的標記就是整個語法元素。
“愚蠢”的一點是,這個過程基本上是不可逆轉的,而且大多是完全不可見的 。 讓我們舉個例子:
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
將匹配任何令牌樹(請參閱Rust Book的宏章章節 )。 $($tts:tt)*
將匹配任何序列的標記,而不更改它們。 這是將一堆令牌安全地轉發到另一個宏的方法。
如果你需要對輸入進行處理並將其轉發到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),+)
})
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.