macro_rules!
è sia intelligente e più stupido quanto si possa realizzare.
Inizialmente, tutto l'input per una macro inizia la vita come zuppa di token indifferenziata. Uno Ident
qui, StrLit
, ecc. Tuttavia, quando si abbina e si acquisisce un bit dell'input, generalmente l'input verrà analizzato in un nodo Abstract Syntax Tree; questo è il caso di expr
.
Il bit "intelligente" è che quando si sostituisce questa cattura (per esempio, $expression
), non basta sostituire i gettoni che sono stati originariamente trovati: si sostituisce l'intero nodo AST come un unico token. Quindi ora c'è questo strano non-veramente-un-token nell'output che è un intero elemento di sintassi.
Il bit "stupido" è che questo processo è fondamentalmente irreversibile e per lo più totalmente invisibile. Quindi cerchiamo di prendere il tuo esempio:
outer!(test);
Noi usiamo questo attraverso un livello di espansione, e diventa questo:
println!("{}", inner!(test));
Salvo, che è non quello che sembra. Per rendere le cose più chiare, ho intenzione di inventare una sintassi non standard:
println!("{}", inner!($(test):expr));
finta che $(test):expr
è un singolo token: è un'espressione che può essere rappresentato dalla sequenza di token test
. È non semplicemente la sequenza di token test
. Questo è importante, perché quando l'interprete macro va a espandere tale inner!
macro, verifica la prima regola:
($test:ident) => { stringify!($test) };
Il problema è che $(test):expr
è espressione, non un identificatore. Sì, lo contiene un identificatore, ma l'interprete macro non è così profondo. Vede un'espressione e solo restituisce.
Non riesce a soddisfare la seconda regola per lo stesso motivo.
Quindi cosa fai? ... Beh, questo dipende. Se outer!
non fa alcun tipo di lavorazione sul suo ingresso, è possibile utilizzare un matcher tt
invece:
macro_rules! outer {
($($tts:tt)*) => {
println!("{}", inner!($($tts)*));
}
}
tt
corrisponderà qualsiasi albero di token (vedi la Macros chapter of the Rust Book). $($tts:tt)*
corrisponderà a qualsiasi sequenza di token, senza modificarli. Questo è un modo per inoltrare in sicurezza un gruppo di token a un'altra macro.
Se è necessario eseguire l'elaborazione sull'ingresso e inoltrarlo alla macro inner!
... probabilmente si dovrà ripetere le regole.