Можно ли написать цепочечный макрос сравнения в Rust? - PullRequest
3 голосов
/ 28 января 2020

В ржавчине можно передавать > или <= et c внутри аргументов макроса, если аргументы ident с.

Можно ли создать макрос, который позволит вам связывать операторы сравнения?

let x = 3;
let y = 1;
let z = -3;

assert_eq!(cond!(z <= x > y), true);

Ответы [ 3 ]

2 голосов
/ 28 января 2020

Я думаю, что следующее делает то, что вы ожидаете , если вы осторожны с аргументами cond.

. Использует tt (для аргумента operator0) для соответствует <, <=, >=, et c. чтобы избежать повторения большого количества случаев, но tt, конечно, совпадает и с другими токенами.

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);
}
2 голосов
/ 28 января 2020

Да, вы можете. Вам нужно использовать tt для типа оператора:

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

Детская площадка

Вы также можете прочитать Маленькая книга макросов Rust для более сложных шаблонов макросов.

1 голос
/ 28 января 2020

Я думаю, что это технически возможно, но я не уверен, что сделал бы это лично. чтобы обрабатывать все операторы без сопоставления с некоторыми вещами, которые не должны работать, например, cond!(a + b), cond!(a) или cond!(), я должен был быть довольно многословным и использовать рекурсивный макрос. Можно было бы упростить начальные (не @recur) случаи, но я волновался, что неправильное выполнение приведет к бесконечной рекурсии.

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

...