简体   繁体   中英

Can I specify an operator or other syntactic literal within a macro input?

First, I know this is not a good use of macros but I'm learning what I can do.

I have a struct Rational :

pub struct Rational{
    pub n: i128,
    pub d : i128
}

I have a macro that builds these:

macro_rules! rat{
    ($n : expr,  $d : expr) => {
        Rational{n : $n,d:  $d}
    }
}

The syntax for calling that macro looks like: rat,(1, 3) . I would like it to visually show a fractional format, so you would call it like rat!(1 / 3) instead, and the macro would parse that expression to yield the same result. (Note that if it first evaluates 1/3 , even as a float type, it will not exactly correspond as 1/3 does not correspond exactly to any float.)

I'm hoping there's some syntax like:

macro_rules! rat{
    ($n : expr `/` $d : expr) => {
        Rational{n : $n,d:  $d}
    }
}

where I can specify syntax to be used in the call. (That attempt does not compile, of course.)

Again, obviously this is silly and an X/Y problem and all that. For context, I'm considering building an equation wrangling system, and at some point I'd like to be able to parse things from a math-y syntax, which might actually justify the use of macros. This is just a baby step.

Does such a thing exist using declarative macros? If not, is it possible with procedural macros? Finally, I know that in Scala macros there would be no way to make that work for literal values, because the expression 1/3 would be resolved so early in the compilation process the AST would be gone by the time even macros are called. Is that also the case in Rust?

Yes, you can use the / token directly in the rule:

#[derive(Debug)]
struct Rational{
    n: i128,
    d: i128
}

macro_rules! rat {
    ($n:literal / $d:literal) => {
        Rational { n: $n, d: $d }
    };
}

fn main() {
    println!("rat!(1 / 3) = {:?}", rat!(1 / 3));
}
rat!(1 / 3) = Rational { n: 1, d: 3 }

However, notice I have changed the arguments from expr to literal . Your question seems to imply this is fine for your use-case, but I bring it up because at least the first parameter $n cannot be an expr . It would cause a parsing ambiguity because / is a valid continuation of an expression and Rust simply doesn't allow / as a separator after a expr . And this goes for all operators; from the follow-set ambiguity restrictions , only => , , , or ; may follow an expr .

If you do want to allow any expr , a common trick is to require parenthesis:

macro_rules! rat {
    ($n:literal / $d:expr) => {
        Rational { n: $n, d: $d }
    };
    (($n:expr) / $d:expr) => {
        Rational { n: $n, d: $d }
    };
}

fn main() {
    println!("rat!(1 / 3) = {:?}", rat!(1 / 3));
    println!("rat!((1 + 2) / 3) = {:?}", rat!((1 + 2) / 3));
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM