2015-05-12 3 views
7

Mam następujący kod makra, który próbuję debugować. Zrobiłem to z Rust Book w dziale "Głęboki koniec". Zmieniono nazwy zmiennych w makrze, aby dokładniej śledzić wpis this.Jak mogę debugować makra?

Moim celem jest, aby program wydrukował każdą linię programu BCT. Mam świadomość, że jest to bardzo ciężki kompilator.

Jedyny błąd rustc daje mi to:

[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); 

Jakie kroki mogę podjąć, aby dowiedzieć gdzie w makro problem jest pochodzących z?

Oto mój kod:

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); 
}    

Odpowiedz

16

Są dwa główne sposoby makr debugowania, że ​​nie udaje się poszerzyć.

  • trace_macros! i
  • log_syntax!

(NB oba są cechą ogrodzony, pod cech to samo imię, a więc wymagają pracy kompilatora nocnego, multirust ułatwia przełączanie między wersjami dla tego rodzaju pracy.)

trace_macros!(...) przyjmuje argument boolowski, który włącza lub wyłącza śledzenie makr (tj. to stateful), jeśli jest włączony, kompilator wydrukuje każde wywołanie makra z jego argumentami podczas ich rozwijania. Zazwyczaj wystarczy rzucić wywołanie trace_macros!(true); na górze skrzynki, np. jeśli jeden adds następujących do góry kodzie:

#![feature(trace_macros)] 

trace_macros!(true); 

Następnie wyjście wygląda następująco:

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 

który miejmy nadzieję zawęża problem: wywołanie bct_p! jest nieważny w jakiś sposób. Patrzenie na to ostrożnie ujawnia problem, lewa strona drugiego ramienia bct_p używa data:tt, kiedy powinna używać $data:tt, tj. Brakującego $.

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

Naprawianie, które pozwala kompilacji na postęp.

log_syntax! nie jest tak natychmiast przydatne w tym przypadku, ale nadal jest dobrym narzędziem: przyjmuje dowolne argumenty i wypisuje je po rozwinięciu, np.

#![feature(log_syntax)] 

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

fn main() {} 

wydrukuje "hello" , 1 2 3 podczas kompilacji. Jest to najbardziej przydatne do sprawdzania rzeczy w innych wywołaniach makr.

(Gdy masz ekspansję do pracy, najlepszym narzędziem do debugowania problemów w wygenerowanego kodu jest użycie --pretty expanded argument rustc. NB. Wymaga to flagę -Z unstable-options mają być przekazane, aby go uaktywnić.)

+0

Dlaczego krok kompilacji makr nie narzeka na '$ (not_al_variable), *'? W jakich sytuacjach byłoby to ważne samo? Nie zapomnij o rozszerzeniu, higieny, która jest przydatna, gdy zmienna znajdująca się poza makrem ma taką samą nazwę jak zmienna wewnątrz makra (przynajmniej tak mi to wyjaśniono). – Nashenas

+2

Może być sens spożywać wiele kopii dokładnej rzeczy, np. można zerwać końcowe zera za pomocą '$ (0), *' które będzie pasowało tylko '0, 0, 0' (itd.). To powiedziawszy, wydaje się stosunkowo rzadkie, że byłoby to bardzo przydatne. – huon

0

debugowanie było interesujące. Zacząłem od najprostszego możliwego wejścia i pracowałem od tego miejsca. Zauważyłem, że miałem problemy z funkcjami drukowania (przepisać, żeby wydrukował tylko dane wejściowe i nie wracał!).

Dodałem również zasady, które były bardziej wyraźne, a następnie usunięto je, gdy wszystko działało (jeden po drugim, oczywiście po drodze). Gdy dowiedziałem się, że każdy element się kompiluje, a funkcje drukowania działały, udało mi się zweryfikować wyniki makr. Poniższe makro uruchamia się czasami, gdy nie powinno, ale kompiluje się, drukuje i jest dostępne do debugowania. Jestem zadowolony z obecnego stanu, aby opublikować go tutaj.

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); 
}