2015-05-12 18 views
7

Quindi ho il seguente codice macro che sto cercando di eseguire il debug. L'ho preso dallo Rust Book nella sezione "The deep end". Ho rinominato le variabili all'interno della macro per seguire più da vicino il post this.Come si esegue il debug delle macro?

Il mio obiettivo è far stampare al programma ogni riga del programma BCT. Sono ben consapevole che questo è molto pesante per il compilatore.

L'unica rustc errore mi sta dando è:

[email protected]:~/rust/macros$ rustc --pretty expanded src/main.rs -Z unstable-options > src/main.precomp.rs 
src/main.rs:151:34: 151:35 error: no rules expected the token `0` 
src/main.rs:151  bct!(0, 1, 1, 1, 0, 0, 0; 1, 0); 

Quali passi posso prendere per capire dove nella macro il problema è venuta da?

Ecco il mio codice:

fn main() { 
{ 
    // "Bitwise Cyclic Tag" automation through macros 
    macro_rules! bct { 
     // cmd 0: 0 ... => ... 
     (0, $($program:tt),* ; $_head:tt) 
      => (bct_p!($($program),*, 0 ;)); 
     (0, $($program:tt),* ; $_head:tt, $($tail:tt),*) 
      => (bct_p!($($program),*, 0 ; $($tail),*)); 

     // cmd 1x: 1 ... => 1 ... x 
     (1, $x:tt, $($program:tt),* ; 1) 
      => (bct_p!($($program),*, 1, $x ; 1, $x)); 
     (1, $x:tt, $($program:tt),* ; 1, $($tail:tt),*) 
      => (bct_p!($($program),*, 1, $x ; 1, $($tail),*, $x)); 

     // cmd 1x: 0 ... => 0 ... 
     (1, $x:tt, $($program:tt),* ; $($tail:tt),*) 
      => (bct_p!($($program),*, 1, $x ; $($tail),*)); 

     // halt on empty data string 
     ($($program:tt),* ;) 
      => (()); 
     } 

    macro_rules! print_bct { 
     ($x:tt ;) 
      => (print!("{}", stringify!($x))); 
     (; $d:tt) 
      => (print!("{}", stringify!($d))); 
     ($x:tt, $($program:tt),* ;) 
      => { 
       print!("{}", stringify!($x)); 
       print_bct!($program ;); 
      }; 
     ($x:tt, $($program:tt),* ; $($data:tt),*) 
      => { 
       print!("{}", stringify!($x)); 
       print_bct!($program ; $data); 
      }; 
     (; $d:tt, $($data:tt),*) 
      => { 
       print!("{}", stringify!($d)); 
       print_bct!(; $data); 
      }; 
    } 

    macro_rules! bct_p { 
     ($($program:tt),* ;) 
      => { 
       print_bct!($($program:tt),* ;); 
       println!(""); 
       bct!($($program),* ;); 
      }; 
     ($($program:tt),* ; $(data:tt),*) 
      => { 
       print_bct!($($program),* ; $($data),*); 
       println!(""); 
       bct!($($program),* ; $($data),*); 
      }; 
    } 

    // the compiler is going to hate me... 
    bct!(0, 1, 1, 1, 0, 0, 0; 1, 0); 
}    

risposta

16

Ci sono due modi principali per le macro di debug che non riescono ad espandersi:.

  • trace_macros! e
  • log_syntax!

(NB entrambi sono caratteristica gated, sotto le caratteristiche del lo stesso nome, e quindi richiede il compilatore notturno per funzionare, multirust rende facile passare da una versione all'altra per questo tipo di lavoro.)

trace_macros!(...) prende un argomento booleano che attiva o disattiva la traccia macro (es. è di stato), se è attivo, il compilatore stamperà ogni invocazione di macro con i suoi argomenti man mano che vengono espansi. Di solito si vuole solo lanciare una chiamata trace_macros!(true); nella parte superiore della cassa, ad es. se uno adds quanto segue per la parte superiore del vostro codice:

#![feature(trace_macros)] 

trace_macros!(true); 

L'uscita si presenta come:

bct! { 0 , 1 , 1 , 1 , 0 , 0 , 0 ; 1 , 0 } 
bct_p! { 1 , 1 , 1 , 0 , 0 , 0 , 0 ; 0 } 
<anon>:68:34: 68:35 error: no rules expected the token `0` 
<anon>:68  bct!(0, 1, 1, 1, 0, 0, 0; 1, 0); 
             ^
playpen: application terminated with error code 101 

che si restringe verso il basso si spera il problema: la chiamata bct_p! non è valido in qualche modo. Guardando attentamente rivela il problema, il lato sinistro del secondo braccio di bct_p utilizza data:tt quando deve usare $data:tt, ovvero uno $ mancante.

($($program:tt),* ; $(data:tt),*) 

Correzione che consente alla compilazione di progredire.

log_syntax! non è immediatamente utile in questo caso, ma è ancora uno strumento preciso: accetta argomenti arbitrari e li stampa quando è espanso, ad es.

#![feature(log_syntax)] 

log_syntax!("hello", 1 2 3); 

fn main() {} 

stampa "hello" , 1 2 3 durante la compilazione. Questo è molto utile per ispezionare le cose all'interno di altre invocazioni di macro.

(Una volta che hai l'espansione a lavorare, lo strumento migliore per eseguire il debug di eventuali problemi nel codice generato è quello di utilizzare l'argomento --pretty expanded a rustc. NB. Ciò richiede la bandiera -Z unstable-options da passare per attivarlo.)

+0

Perché il passo della compilazione della macro non si lamenta di '$ (non_al_variabile), *'? In quali situazioni sarebbe valido da solo? Inoltre, non dimenticare di --pretty espanso, igiene, che è utile quando una variabile esterna alla macro ha lo stesso nome di una variabile all'interno della macro (almeno, è così che mi è stata spiegata). – Nashenas

+2

Potrebbe avere senso mangiare un sacco di copie di una cosa esatta, ad es. uno potrebbe togliere gli zero finali con '$ (0), *' che corrisponderebbe solo a '0, 0, 0' (ecc.). Detto questo, sembra relativamente raro che ciò sia estremamente utile. – huon

0

Debugging era interessante. Ho iniziato con l'input più semplice possibile e ho lavorato da lì. Ho scoperto di avere problemi con le funzioni di stampa (riscrivi in ​​modo che stampi solo gli input e non torni indietro!).

Inoltre ho aggiunto regole che erano più esplicite e quindi le ho rimosse una volta che tutto funzionava (uno a uno, ovviamente, testando lungo la strada). Una volta saputo che ogni singolo pezzo stava compilando e le funzioni di stampa funzionavano, sono stato in grado di verificare l'output dei macro. La macro di seguito a volte viene eseguita quando non dovrebbe, ma viene compilata e stampata ed è possibile eseguire il debugging. Sono abbastanza contento dello stato attuale per postarlo qui.

fn main() { 
    // "Bitwise Cyclic Tag" automation through macros 
    macro_rules! bct { 
     // cmd 0: 0 ... => ... 
     (0, $($program:tt),* ; $_head:tt) 
      => (pbct!($($program),*, 0 ;)); 
     (0, $($program:tt),* ; $_head:tt, $($tail:tt),*) 
      => (pbct!($($program),*, 0 ; $($tail),*)); 

     // cmd 1x: 1 ... => 1 ... x 
     (1, $x:tt, $($program:tt),* ; 1) 
      => (pbct!($($program),*, 1, $x ; 1, $x)); 
     (1, $x:tt, $($program:tt),* ; 1, $($tail:tt),*) 
      => (pbct!($($program),*, 1, $x ; 1, $($tail),*, $x)); 

     // cmd 1x: 0 ... => 0 ... 
     (1, $x:tt, $($program:tt),* ; $($tail:tt),*) 
      => (pbct!($($program),*, 1, $x ; $($tail),*)); 

     // halt on empty data string 
     ($($program:tt),* ;) 
      => (()); 
    } 

    macro_rules! println_bct { 
     () => 
      (println!("")); 
     (;) => 
      (println!(":")); 

     ($d:tt) => 
      (println!("{}", stringify!($d))); 
     ($d:tt, $($data:tt),*) => { 
      print!("{}", stringify!($d)); 
      println_bct!($($data),*); 
     }; 
     (; $($data:tt),*) => { 
      print!(":"); 
      println_bct!($($data),*); 
     }; 

     ($x:tt ; $($data:tt),*) => { 
      print!("{}", stringify!($x)); 
      println_bct!(; $($data),*); 
     }; 
     ($x:tt, $($program:tt),* ; $($data:tt),*) => { 
      print!("{}", stringify!($x)); 
      println_bct!($($program),* ; $($data),*); 
     }; 
    } 

    macro_rules! pbct { 
     ($($program:tt),* ; $($data:tt),*) => { 
      println_bct!($($program),* ; $($data),*); 
      bct!($($program),* ; $($data),*); 
     }; 
    } 

    pbct!(0, 0, 1, 1, 1, 0, 0, 0 ; 1, 0, 1); 

    // This one causes the compiler to hit recursion limits, heh 
    // pbct!(0, 0, 1, 1, 1, 1, 1, 0 ; 1, 0, 1); 
} 
Problemi correlati