2015-05-12 38 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); 
}