0

I'm trying to write a simple macro like this, to facilitate the writing of my code

enum Term {
    Or(Box<Term>, Box<Term>),
    And(Box<Term>, Box<Term>),
    Value(u8),
}

macro_rules! term {
    ($value_1:expr & $value_2:expr) => {
        Term::And(Box::new($value_1), Box::new($value_2))
    };
    ($value_1:expr | $value_2: expr) => {
        Term::Or(Box::new($value_1), Box::new($value_2))
    };
    ($value: expr) => {
        Term::Value($value)
    };
}

However, I cannot put a & or | after an expr in my pattern. I tried using tt, but it didn't work because I'm not always just putting one token in my $value. pat_param doesn't seem to work with & either.
The only option that seems to work would be to use (($value_1:expr) & ($value_2:expr)), but if I could do without the extra parentheses, it'd be better.
Is there a way to do this?

cafce25
  • 15,907
  • 4
  • 25
  • 31
er1t
  • 59
  • 5

2 Answers2

2

& isn't allowed to avoid ambiguity, there is no way to have & as a token directly after an expr parameter in a declarative macro.

expr and stmt may only be followed by one of: =>, ,, or ;.

cafce25
  • 15,907
  • 4
  • 25
  • 31
  • All my possible values are always `x`, `x.clone()`, `x.method()` or those kind of expressions. Are there any other capture type that might work for those? – er1t Mar 12 '23 at 10:55
  • @er1t Is `x` an arbitrary expression? – Chayim Friedman Mar 12 '23 at 11:27
  • I think either a [procedual macro](https://doc.rust-lang.org/reference/procedural-macros.html) or using parentheses are your best options. – cafce25 Mar 12 '23 at 11:32
  • @ChayimFriedman `x` is a simple variable (of type `Term` or `u8`). It's never used in a complex expression, it's always just one single variable. – er1t Mar 12 '23 at 17:10
  • @cafce25 procedural macro seems a bit overkill in my situation, so I'm gonna stick to the parentheses! Thanks for the advice! – er1t Mar 12 '23 at 17:59
1

If you only want to allow a single variable, optionally with a method call after, you can use the following macro (note it does not handle generic method call syntax):

macro_rules! term {
    (
        $name:ident $( .$method:ident( $($argument:expr),* $(,)? ) )?
        $($rest:tt)+
    ) => {
        term!(@parsed_lhs
            ( $name $( .$method( $($argument),* ) )? )
            $($rest)*
        )
    };
    (@parsed_lhs $value_1:tt & $value_2:expr) => {
        Term::And(Box::new($value_1), Box::new($value_2))
    };
    (@parsed_lhs $value_1:tt | $value_2:expr) => {
        Term::Or(Box::new($value_1), Box::new($value_2))
    };
    ($value:expr) => {
        Term::Value($value)
    };
}

This macro is not perfect, it has some edge cases, but it should be good enough.

Chayim Friedman
  • 47,971
  • 5
  • 48
  • 77