How to match a optional repetition character(eg. '?' ) with input when defining macro?

How to match a optional repetition character(eg. '?' ) in input using macro?


macro_rules! g4 {
    ($rule:ident: $($sub_rule:ident$(?)?) +) => {
        $(
            print!("{}", $sub_rule);
            $(print!("optional??");)?
        )+
    };
}

fn main() {
    let s = "MyRule";
    let s1 = "s1";
    let s2 = "s2";

    g4!(s: s1? s2); 

I want to expand g4!(s: s1? s2) => {
print!("{}", s1);
print!("optional??");
print!("{}", s2);
}

complier compliant that:

error: attempted to repeat an expression containing no syntax variables matched as repeating at this depth
 --> src/main.rs:4:37
  |
4 |         $(print!("{}", $sub_rule); $(print!("optional??");)?)+
  |                              

A possible solution is to add an extra pattern for ident?.

macro_rules! g4 {
    ($rule:ident: $sub_rule:ident ? $($tt:tt)*) => {
        println!("optional {}", $sub_rule);
        g4!($rule: $($tt)*);
    };

    ($rule:ident: $sub_rule:ident $($tt:tt)*) => {
        println!("{}", $sub_rule);
        g4!($rule: $($tt)*);
    };

    ($rule:ident:) => {};
}

fn main() {
    let s = "MyRule";
    let s1 = "A";
    let s2 = "B";
    let s3 = "C";
    let s4 = "D";

    g4!(s: s1 s2? s3? s4);
}

Output:

A
optional B
optional C
D
1 Like

I change the macro_rule a little, but it cannot work.

macro_rules! g4 {
    ($rule:ident: $($tt:tt)*) => {
        print!("{}: ", $rule);
        g4!($($tt)*);
    };

    // ($rule:ident: $sub_rule:tt $($tt:tt)*) => {
    //     print!("{}", $sub_rule);
    //     g4!($($tt)*);
    // };
    
    ($sub_rule:tt $($tt:tt)*) => {
        print!("{}", $sub_rule);
        g4!($($tt)*);
    };

    ($sub_rule:tt ? $($tt:tt)*) => {
        print!("({})?", $sub_rule);
        g4!($($tt)*);
    };

    () => {};

}



fn main() {
    let s = "MyRule";
    let s1 = "s1";
    let s2 = "s2";
    let s3 = "s3";

    g4!(s: s1 s2? s3 s1 s2);
}
error: expected expression, found `?`
  --> src/main.rs:35:17
     |
35 |     g4!(s: s1 s2? s3 s1 s2);
     |               ^ expected expression

the order of pattern arms in macros matters; try putting the one with the ? before the one without it.

I believe your second rule is matching, so it's passing the ? into the println!

3 Likes

Thanks, it works

Another option is to use a dummy metavariable:

macro_rules! g4 {
    ($rule:ident: $($sub_rule:ident$(? $([$($_:tt)* $optional:tt])?)?) +) => {
        $(
            print!("{}", $sub_rule);
            $($($optional)?; print!("optional??");)?
        )+
    };
}

fn main() {
    let s = "MyRule";
    let s1 = "s1";
    let s2 = "s2";
    g4!(s: s1? s2);
}

The $optional metavar will never be matched, but it allows you to give a name to the optional part of the input.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.