如何调试宏?
How do I debug macros?
所以我得到了以下我正在尝试调试的宏代码。我从 Rust Book under the section "The deep end". I renamed the variables within the macro to more closely follow this post.
我的目标是让程序打印出 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);
}
调试很有趣。我从尽可能简单的输入开始,然后从那里开始。我发现我在使用打印功能的过程中遇到了问题(重写以便它只打印输入而不循环返回!)。
我还添加了更明确的规则,然后在一切正常后将其删除(当然是一个接一个地进行测试)。一旦我知道每个单独的部分都在编译,并且打印功能在工作,我就能够验证宏的输出。下面的宏有时会在不应该运行的时候运行,但它会编译、打印并且是可调试的。我对目前的状态很满意 post 在这里。
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);
}
有两种主要方法可以调试无法展开的宏:
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
。这对于检查其他宏调用中的内容最有用。
(一旦你的扩展开始工作,调试生成代码中任何问题的最佳工具是使用 --pretty expanded
参数到 rustc
。注意。这需要 -Z unstable-options
要传递的标志以激活它。)
用于轻松查看扩展的另一个好工具是 cargo-expand。
可以安装:
cargo install cargo-expand
然后非常简单地用作:
cargo expand
或更精确地定位特定测试文件(例如tests/simple.rs):
cargo expand --test simple
一定要查看 --help
,有很多选项可以缩小扩展的范围。您甚至可以针对个人 items (structs, fns etc.) 进行扩展!
所以我得到了以下我正在尝试调试的宏代码。我从 Rust Book under the section "The deep end". I renamed the variables within the macro to more closely follow this post.
我的目标是让程序打印出 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);
}
调试很有趣。我从尽可能简单的输入开始,然后从那里开始。我发现我在使用打印功能的过程中遇到了问题(重写以便它只打印输入而不循环返回!)。
我还添加了更明确的规则,然后在一切正常后将其删除(当然是一个接一个地进行测试)。一旦我知道每个单独的部分都在编译,并且打印功能在工作,我就能够验证宏的输出。下面的宏有时会在不应该运行的时候运行,但它会编译、打印并且是可调试的。我对目前的状态很满意 post 在这里。
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);
}
有两种主要方法可以调试无法展开的宏:
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
。这对于检查其他宏调用中的内容最有用。
(一旦你的扩展开始工作,调试生成代码中任何问题的最佳工具是使用 --pretty expanded
参数到 rustc
。注意。这需要 -Z unstable-options
要传递的标志以激活它。)
用于轻松查看扩展的另一个好工具是 cargo-expand。
可以安装:
cargo install cargo-expand
然后非常简单地用作:
cargo expand
或更精确地定位特定测试文件(例如tests/simple.rs):
cargo expand --test simple
一定要查看 --help
,有很多选项可以缩小扩展的范围。您甚至可以针对个人 items (structs, fns etc.) 进行扩展!