【问题标题】:How do I debug macros?如何调试宏?
【发布时间】:2015-07-23 21:18:35
【问题描述】:

所以我有以下我正在尝试调试的宏代码。我从“深端”部分下的Rust Book 中获取了它。我重命名了宏中的变量,以便更紧密地关注this 帖子。

我的目标是让程序打印出 BCT 程序的每一行。我很清楚这是非常繁重的编译器。

rustc 给我的唯一错误是:

user@debian:~/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);
}            

【问题讨论】:

    标签: rust rust-macros


    【解决方案1】:

    有两种主要方法可以调试无法扩展的宏:

    • trace_macros!
    • log_syntax!

    (注意。两者都是功能门控,在同名功能下,因此需要夜间编译器才能工作,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。这对于检查其他宏调用中的内容非常有用。

    (一旦扩展工作,调试生成代码中的任何问题的最佳工具是使用rustc--pretty expanded 参数。注意。这需要将-Z unstable-options 标志传递给激活它。)

    【讨论】:

    • 为什么宏编译步骤没有报错$(not_al_variable),*?在什么情况下它本身是有效的?另外,不要忘记 --pretty expand,hygiene,当宏外部的变量与宏内部的变量同名时,它很有用(至少,我是这么解释的)。
    • 吃很多精确的东西的副本可能是有意义的,例如。可以使用$(0),* 去除尾随零,这将仅匹配0, 0, 0(等)。也就是说,这似乎是相对罕见的,这将是非常有用的。
    【解决方案2】:

    另一个用于轻松查看扩展的好工具是cargo-expand

    可以安装:

    cargo install cargo-expand
    

    然后很简单地用作:

    cargo expand
    

    或者更精确地定位特定的测试文件(例如tests/simple.rs):

    cargo expand --test simple
    

    请务必查看--help,有很多选项可以缩小扩展范围。您甚至可以针对个人 items (structs, fns etc.) 进行扩展!

    【讨论】:

      【解决方案3】:

      调试很有趣。我从最简单的输入开始,然后从那里开始工作。我发现我在打印功能的过程中遇到了问题(重写,所以它只打印输入而不循环回来!)。

      我还添加了更明确的规则,然后在一切正常后将其删除(当然,一个接一个地进行测试)。一旦我知道每个单独的部分都在编译,并且打印功能正在工作,我就能够验证宏的输出。下面的宏有时会在不应该运行的时候运行,但它可以编译、打印并且是可调试的。我对目前的状态很满意,可以在这里发布。

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-10-11
        • 2013-02-26
        • 2010-11-26
        • 1970-01-01
        • 2012-12-24
        • 1970-01-01
        相关资源
        最近更新 更多