Condensing repetetive macro

macro_rules! btree {
    ([$s: expr, (,)]) => {
        BTree::Branch {
            s: $s,
            t0: Box::new(btree!()),
            t1: Box::new(btree!()),
        }
    };
    ([$s: expr, ($t0: tt,)]) => {
        BTree::Branch {
            s: $s,
            t0: Box::new(btree!($t0)),
            t1: Box::new(btree!()),
        }
    };
    ([$s: expr, (,$t1: tt)]) => {
        BTree::Branch {
            s: $s,
            t0: Box::new(btree!()),
            t1: Box::new(btree!($t1)),
        }
    };
    ([$s: expr, ($t0: tt, $t1: tt)]) => {
        BTree::Branch {
            s: $s,
            t0: Box::new(btree!($t0)),
            t1: Box::new(btree!($t1)),
        }
    };
    () => {
        BTree::Null
    };
}

Is it possible to turn the first 4 branches into one
This:

    ([$s: expr, ($($t0: tt)?, $($t1: tt)?)]) => {
        BTree::Branch {
            s: $s,
            t0: Box::new(btree!($($t0)?)),
            t1: Box::new(btree!($($t1)?)),
        }
    };

Is counted as ambiguous becuase , counts as a tt.

An example usage would be:

        let b_tree = btree!([1, ([2, ([12, ([13, ([6, (,)],)], [14, (, [7, (,)])])], [12, ([13, (,)], [14, (,)])])], [5, (,)])]);

Feel free to share a playground with the working implementation and the test-case, together with the definition (or a simplified definition) of the BTree type, as that simplifies experimentation. (For example, I can’t write macros, even less so bug-free, without being able to ask the compiler “does this work”.)

1 Like

This works for the test case. (Making the []s explicit in the macro matching.)

macro_rules! btree_alt {
    //             vvv      vvv      vvv      vvv
    ([$s: expr, ($([$($t0:tt)+])?, $([$($t1:tt)+])?)]) => {
        BTree::Branch {
            s: $s,
            //                        vvv   vvv
            t0: Box::new(btree_alt!($([$($t0)+])?)),
            t1: Box::new(btree_alt!($([$($t1)+])?)),
        }
    };
    () => {
        BTree::Null
    };
}
3 Likes

it's not possible for the syntax you are using, and this is the exact reason, the tt meta variable is a sledgehammer, it will capture anything, so you cannot use the optional modifier on it (e.g. $($x:tt)? will never work unless at the tail position)

that said, if you can accept a different syntax, the macro can be simplified a little bit. what I'd suggest is to ditch the comma separator and use a s-expression like syntax instead, which is easier to parse.

example:

let nil = tree!();
let example = tree!(1 (2 () ()) (3 (4 () ()) ()));
// possible shorter convenient syntax
let tree1 = tree!(1); // short for `tree!(1 () ())`
let tree2 = tree!(1 2 3); // short for `tree!(1 (2) (3))`, which is short for `tree!(1 (2 () ()) (3 () ()))`
let tree3 = tree!(1 () 3); // you get the idea
let tree4 = tree!(1 2 ()); // or maybe just `tree!(1 2)`

btw, round parenthesizes, square brackets, and curly braces are all tt group tokens, you can mix and match them, the s-expr doesn't have to be use single type of brackets.