Rust:如何允许和检测 macro_rules 中的可选标点符号?
Rust: How to allow and detect optional punctionuation in macro_rules?
我玩 Rust 有一段时间了,决定是时候从宏开始了。我想创建一个宏,允许对无符号整数变量的特定位进行按位和运算。这是我目前正在使用的东西:
macro_rules! AND {
($($val:ident.$bit:literal), *) => {
{
let mut val_out = 0x01;
$(
val_out &= ($val >> $bit);
)*
val_out & 0x01
}
};
}
fn main() {
let x = 0x01;
let y = 0x02;
let z = 0x10;
println!("{}", AND!(x.0, y.1, z.4)); // Prints 1
println!("{}", AND!(x.0, y.1, z.0)); // Prints 0
}
我想做的也是允许否定运算符。这是我编译的内容,但我不知道如何确定感叹号是否匹配。
macro_rules! AND {
($($(!)?$val:ident.$bit:literal), *) => {
{
let mut val_out = 0x01;
$(
val_out &= ($val >> $bit);
)*
val_out & 0x01
}
};
}
fn main() {
let x = 0x01;
let y = 0x02;
let z = 0x10;
println!("{}", AND!(!x.0, !y.1, z.4)); // Prints 1, would like to print 0
println!("{}", AND!(x.0, y.1, !z.0)); // Prints 0, would like to print 1
}
我尝试匹配表达式而不是感叹号,但表达式必须是最后匹配的内容。我也尝试在文字后面匹配一个ident,认为文字后面的下划线可以表示否定,但是当我使用if或match语句来确定ident是否匹配时,我得到一个错误:
macro_rules! AND {
($($val:ident.$bit:literal$($negate:ident)?), *) => {
{
let mut val_out = 0x01;
$(
if $negate == "_" {
val_out &= (!$val >> $bit);
}
else {
val_out &= ($val >> $bit);
}
)*
val_out & 0x01
}
};
}
fn main() {
let x = 0x01;
let y = 0x02;
let z = 0x10;
println!("{}", AND!(x.0, y.1, z.4_));
error: variable 'negate' is still repeating at this depth
--> src/main.rs:8:20
|
8 | if $negate == "_" {
| ^^^^^^^
如有任何想法,我们将不胜感激。谢谢!
你提出的带有前导但可选符号(例如 !
)的宏语法很难用 Rust 的 macro_rules!
表达。简化它的一种方法是对两种情况都使用一个符号(例如 +
表示肯定,-
表示否定)。那么就可以用一个二级宏来一一区分这两种情况:
// Auxiliary macro to distinguish positive and negative cases
macro_rules! and_aux {
($var:ident + $val:ident $bit:literal) => {
$var &= ($val >> $bit);
};
($var:ident - $val:ident $bit:literal) => {
$var &= !($val >> $bit);
};
}
// Main macro
macro_rules! AND {
($($t:tt $val:ident . $bit:literal), *) => {{
let mut val_out = 0x01;
$(
and_aux!(val_out $t $val $bit);
)*
val_out & 0x01
}};
}
fn main() {
let x = 0x01;
let y = 0x02;
let z = 0x10;
println!("{}", AND!(+x.0, +y.1, +z.4)); // Prints 1
println!("{}", AND!(+x.0, +y.1, +z.0)); // Prints 0
println!("{}", AND!(-x.0, -y.1, +z.4)); // Prints 0
println!("{}", AND!(+x.0, +y.1, -z.0)); // Prints 1
}
当然,你也可以使用其他符号,或者你可以使用括号,这可以使macro_rules!
中的很多事情成为可能。这部分是因为方括号(包括 ()
、[]
和 {}
)是唯一可以分隔 tts(标记树)的元素,您通常需要更高级的 macro_rules!
.
但是,如果您喜欢复杂且难以调试的宏,您实际上可以让您的原始宏语法起作用。例如,你可以做一个递归宏,每次递归只解析一点输入,将一些中间表示转发到下一个调用。例如:
// Auxiliary macro, does the heavy lifting
macro_rules! and_inner {
// Finishing rule, assembles the actual output
( @ $var:ident { $( $finished:tt )* } from { $(,)? } ) => {
{
let mut $var = 0x01;
$( $finished )*
$var & 0x01
}
};
// Parse negated case
( @ $var:ident {
$( $finished:tt )*
} from {
! $val:ident . $bit:literal , // only this line is processed here
$( $rest_input:tt )*
}
) => {
and_inner!(@ $var {
$( $finished )*
$var &= !($val >> $bit);
} from {
$( $rest_input )*
})
};
// Parse positive case
( @ $var:ident {
$( $finished:tt )*
} from {
$val:ident . $bit:literal , // only this line is processed here
$( $rest_input:tt )*
}
) => {
and_inner!(@ $var {
$( $finished )*
$var &= ($val >> $bit);
} from {
$( $rest_input )*
})
};
}
// Main macro
macro_rules! AND {
// Entry rule prepares input for internal macro
( $( $input:tt )* ) => {
and_inner!(@ tmp_var { } from { $($input)* , })
};
}
你可以试试所谓的incremental macro muncher:
macro_rules! _AND {
($val_out:ident , $val:ident.$bit:literal $($tail:tt)*) => {
$val_out &= ($val >> $bit);
_AND!{$val_out $($t)*};
};
($val_out:ident , ! $val:ident.$bit:literal $($tail:tt)*) => {
$val_out &= (!$val >> $bit);
_AND!{$val_out $($t)*};
};
($val_out:ident) => { }
}
macro_rules! AND {
($($tail:tt)*) => {
{
let mut val_out = 0x01;
_AND!{val_out , $($tail)*};
val_out & 0x01
}
};
}
fn main() {
let x = 0x01;
let y = 0x02;
let z = 0x10;
println!("{}", AND!(!x.0, !y.1, z.4)); // Prints 1, would like to print 0
println!("{}", AND!(x.0, y.1, !z.0)); // Prints 0, would like to print 1
}
这个想法是将宏的全部内容解析为 令牌树 (tt
) 的列表,基本上可以是任何东西,然后传递它们到递归宏,在该宏的每次迭代中吃掉一些。
我玩 Rust 有一段时间了,决定是时候从宏开始了。我想创建一个宏,允许对无符号整数变量的特定位进行按位和运算。这是我目前正在使用的东西:
macro_rules! AND {
($($val:ident.$bit:literal), *) => {
{
let mut val_out = 0x01;
$(
val_out &= ($val >> $bit);
)*
val_out & 0x01
}
};
}
fn main() {
let x = 0x01;
let y = 0x02;
let z = 0x10;
println!("{}", AND!(x.0, y.1, z.4)); // Prints 1
println!("{}", AND!(x.0, y.1, z.0)); // Prints 0
}
我想做的也是允许否定运算符。这是我编译的内容,但我不知道如何确定感叹号是否匹配。
macro_rules! AND {
($($(!)?$val:ident.$bit:literal), *) => {
{
let mut val_out = 0x01;
$(
val_out &= ($val >> $bit);
)*
val_out & 0x01
}
};
}
fn main() {
let x = 0x01;
let y = 0x02;
let z = 0x10;
println!("{}", AND!(!x.0, !y.1, z.4)); // Prints 1, would like to print 0
println!("{}", AND!(x.0, y.1, !z.0)); // Prints 0, would like to print 1
}
我尝试匹配表达式而不是感叹号,但表达式必须是最后匹配的内容。我也尝试在文字后面匹配一个ident,认为文字后面的下划线可以表示否定,但是当我使用if或match语句来确定ident是否匹配时,我得到一个错误:
macro_rules! AND {
($($val:ident.$bit:literal$($negate:ident)?), *) => {
{
let mut val_out = 0x01;
$(
if $negate == "_" {
val_out &= (!$val >> $bit);
}
else {
val_out &= ($val >> $bit);
}
)*
val_out & 0x01
}
};
}
fn main() {
let x = 0x01;
let y = 0x02;
let z = 0x10;
println!("{}", AND!(x.0, y.1, z.4_));
error: variable 'negate' is still repeating at this depth
--> src/main.rs:8:20
|
8 | if $negate == "_" {
| ^^^^^^^
如有任何想法,我们将不胜感激。谢谢!
你提出的带有前导但可选符号(例如 !
)的宏语法很难用 Rust 的 macro_rules!
表达。简化它的一种方法是对两种情况都使用一个符号(例如 +
表示肯定,-
表示否定)。那么就可以用一个二级宏来一一区分这两种情况:
// Auxiliary macro to distinguish positive and negative cases
macro_rules! and_aux {
($var:ident + $val:ident $bit:literal) => {
$var &= ($val >> $bit);
};
($var:ident - $val:ident $bit:literal) => {
$var &= !($val >> $bit);
};
}
// Main macro
macro_rules! AND {
($($t:tt $val:ident . $bit:literal), *) => {{
let mut val_out = 0x01;
$(
and_aux!(val_out $t $val $bit);
)*
val_out & 0x01
}};
}
fn main() {
let x = 0x01;
let y = 0x02;
let z = 0x10;
println!("{}", AND!(+x.0, +y.1, +z.4)); // Prints 1
println!("{}", AND!(+x.0, +y.1, +z.0)); // Prints 0
println!("{}", AND!(-x.0, -y.1, +z.4)); // Prints 0
println!("{}", AND!(+x.0, +y.1, -z.0)); // Prints 1
}
当然,你也可以使用其他符号,或者你可以使用括号,这可以使macro_rules!
中的很多事情成为可能。这部分是因为方括号(包括 ()
、[]
和 {}
)是唯一可以分隔 tts(标记树)的元素,您通常需要更高级的 macro_rules!
.
但是,如果您喜欢复杂且难以调试的宏,您实际上可以让您的原始宏语法起作用。例如,你可以做一个递归宏,每次递归只解析一点输入,将一些中间表示转发到下一个调用。例如:
// Auxiliary macro, does the heavy lifting
macro_rules! and_inner {
// Finishing rule, assembles the actual output
( @ $var:ident { $( $finished:tt )* } from { $(,)? } ) => {
{
let mut $var = 0x01;
$( $finished )*
$var & 0x01
}
};
// Parse negated case
( @ $var:ident {
$( $finished:tt )*
} from {
! $val:ident . $bit:literal , // only this line is processed here
$( $rest_input:tt )*
}
) => {
and_inner!(@ $var {
$( $finished )*
$var &= !($val >> $bit);
} from {
$( $rest_input )*
})
};
// Parse positive case
( @ $var:ident {
$( $finished:tt )*
} from {
$val:ident . $bit:literal , // only this line is processed here
$( $rest_input:tt )*
}
) => {
and_inner!(@ $var {
$( $finished )*
$var &= ($val >> $bit);
} from {
$( $rest_input )*
})
};
}
// Main macro
macro_rules! AND {
// Entry rule prepares input for internal macro
( $( $input:tt )* ) => {
and_inner!(@ tmp_var { } from { $($input)* , })
};
}
你可以试试所谓的incremental macro muncher:
macro_rules! _AND {
($val_out:ident , $val:ident.$bit:literal $($tail:tt)*) => {
$val_out &= ($val >> $bit);
_AND!{$val_out $($t)*};
};
($val_out:ident , ! $val:ident.$bit:literal $($tail:tt)*) => {
$val_out &= (!$val >> $bit);
_AND!{$val_out $($t)*};
};
($val_out:ident) => { }
}
macro_rules! AND {
($($tail:tt)*) => {
{
let mut val_out = 0x01;
_AND!{val_out , $($tail)*};
val_out & 0x01
}
};
}
fn main() {
let x = 0x01;
let y = 0x02;
let z = 0x10;
println!("{}", AND!(!x.0, !y.1, z.4)); // Prints 1, would like to print 0
println!("{}", AND!(x.0, y.1, !z.0)); // Prints 0, would like to print 1
}
这个想法是将宏的全部内容解析为 令牌树 (tt
) 的列表,基本上可以是任何东西,然后传递它们到递归宏,在该宏的每次迭代中吃掉一些。