Reputation: 4052
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?
Upvotes: 0
Views: 321
Reputation: 59817
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));
}
Upvotes: 4