2015-05-12 45 views
7

所以我有我想要調試的以下宏代碼。我已經從Rust Book的「深度」一節中看到它。我將宏中的變量重命名爲更緊密地遵循this後。如何調試宏?

我的目標是讓程序打印出BCT程序的每一行。我很清楚這是非常重要的編譯器。

的唯一錯誤rustc是給我的是:

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

我可以採取什麼步驟來找出其中的問題是來自宏碁

這裏是我的代碼:

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

回答

16

有兩種主要的方式來調試宏都未能擴大。

  • trace_macros!
  • log_syntax!

(NB都是有門,下的功能同名,所以需要夜間編譯器工作,multirust可以很容易地在這類工作的版本之間切換。)

trace_macros!(...)採用一個布爾參數來開啓或關閉宏觀跟蹤(即,它是有狀態的),如果它已打開,編譯器將在擴展它們的參數時打印每個宏調用。通常只需要在包裝箱頂部打一個trace_macros!(true);的電話,如果一個adds以下到您的代碼的頂部:

#![feature(trace_macros)] 

trace_macros!(true); 

那麼輸出的樣子:

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 

它希望縮小了問題:bct_p!呼叫在某種程度上是無效的。仔細觀察它可以發現問題,bct_p的第二條臂的左側使用data:tt,當它應該使用$data:tt時,即缺少$

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

修復,允許編譯取得進展。

log_syntax!在這種情況下並不是很有用,但它仍然是一個整潔的工具:它可以接受任意參數並在展開時將其打印出來。

#![feature(log_syntax)] 

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

fn main() {} 

將打印"hello" , 1 2 3,因爲它編譯。這對於檢查其他宏調用中的內容非常有用。

(一旦你得到了擴展工作,調試在生成的代碼的任何問題的最佳工具是使用--pretty expanded參數rustc。NB。這需要-Z unstable-options標誌傳遞來激活它。)

+0

爲什麼宏編譯步驟不會抱怨'$(not_al_variable),*'?在什麼情況下,它會自行生效?另外,不要忘記--pretty擴展的衛生,當宏外部的變量與宏內部的變量具有相同的名稱(至少,這就是我向他解釋的)時,這是很有用的。 – Nashenas

+2

吃一大堆確切的東西可能是有意義的,例如,人們可以用'$(0),*'去掉尾隨零,這隻會匹配'0,0,0'(等等)。這就是說,這似乎相當罕見,這將是非常有用的。 – huon

0

調試很有意思。我從最簡單的輸入入手,並從那裏開始工作。我發現我在印刷功能方面遇到了問題(重寫,因此它只是打印輸入並且不能循環回來!)。

我還添加了更明確的規則,然後一旦所有工作都正常工作(當然,一路測試)就將其刪除。一旦我知道每個單獨的部分正在編譯,並且打印功能正在工作,我就能夠驗證宏的輸出。下面的宏有時候不應該運行,但它編譯,打印並且是可調試的。我很滿意現在的狀態在這裏發佈。

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