Reputation: 679
In rust, it's possible to pass >
or <=
etc inside macro arguments so long as the arguments are ident
s.
Is it possible to create a macro that lets you chain comparison operators?
let x = 3;
let y = 1;
let z = -3;
assert_eq!(cond!(z <= x > y), true);
Upvotes: 4
Views: 2160
Reputation: 73480
I think it is technically possible, but I'm not sure I'd do it personally.
to handle all the operators without matching on some things that shouldn't work like cond!(a + b)
, cond!(a)
or cond!()
I had to be pretty verbose, and use a recursive macro. It might be possible to simplify the initial (non @recur
) cases, but I was worried that doing it wrong would lead to infinite recursion.
macro_rules! cond {
( @recur $x:ident ) => { true };
( @recur $x:ident < $y:ident $($tail:tt)* ) => { ($x < $y) && cond!( @recur $y $($tail)*) };
( @recur $x:ident > $y:ident $($tail:tt)* ) => { ($x > $y) && cond!( @recur $y $($tail)*) };
( @recur $x:ident <= $y:ident $($tail:tt)* ) => { ($x <= $y) && cond!( @recur $y $($tail)*) };
( @recur $x:ident >= $y:ident $($tail:tt)* ) => { ($x >= $y) && cond!( @recur $y $($tail)*) };
( @recur $x:ident == $y:ident $($tail:tt)* ) => { ($x == $y) && cond!( @recur $y $($tail)*) };
( $x:ident < $y:ident $($tail:tt)* ) => { ($x < $y) && cond!( @recur $y $($tail)*) };
( $x:ident > $y:ident $($tail:tt)* ) => { ($x > $y) && cond!( @recur $y $($tail)*) };
( $x:ident <= $y:ident $($tail:tt)* ) => { ($x <= $y) && cond!( @recur $y $($tail)*) };
( $x:ident >= $y:ident $($tail:tt)* ) => { ($x >= $y) && cond!( @recur $y $($tail)*) };
( $x:ident == $y:ident $($tail:tt)* ) => { ($x == $y) && cond!( @recur $y $($tail)*) };
}
fn main() {
let x = 3;
let y = 1;
let z = -3;
println!("(z <= x > y) = {}", cond!(z <= x > y));
}
Upvotes: 2
Reputation: 35983
I think the following does what you expect as long as you are careful with the arguments to cond
.
It uses tt
(for argument operator0
) to match <
, <=
, >=
, etc. to avoid repeating lots of cases, but tt
, of course, matches other tokens, too.
macro_rules! cond{
($x:ident $operator0:tt $x0:ident) => {
($x $operator0 $x0)
};
($x:ident $operator0:tt $x0:ident $($operator1:tt $x1:ident)*) => {
($x $operator0 $x0) && cond!($x0 $($operator1 $x1)*)
};
}
fn main() {
let x = 3;
let y = 1;
let z = -3;
assert_eq!(cond!(z <= x > y), true);
}
Upvotes: 2
Reputation: 23294
Yes you can. You need to use tt
for the operator type:
macro_rules! cond {
(@rec ($head:expr) $last:ident $op:tt $next:ident $($tail:tt)*) => {
cond!(@rec (($head) && ($last $op $next)) $next $($tail)*)
};
(@rec ($head:expr) $last:ident) => { $head };
($first:ident $op:tt $next:ident $($tail:tt)*) => {
cond!(@rec ($first $op $next) $next $($tail)*)
}
}
fn main() {
let x = 3;
let y = 1;
let z = -3;
println!("(z <= x > y) = {}", cond!(z <= x > y));
}
You can also read The little book of Rust Macros for more advanced macros patterns.
Upvotes: 4