Macro repetition with multiple rules

I have a macro rule that defines two kinds of rules.

macro_rules! export_functions {
    ($(($FUNC:ident,  $($arg:ident)*, $DOC:expr)),*) => {
        pub mod expr_fn {
            $(
                #[doc = $DOC]
                /// Return $name(arg)
                pub fn $FUNC($($arg: datafusion_expr::Expr),*) -> datafusion_expr::Expr {
                    super::$FUNC().call(vec![$($arg),*],)
                }
            )*
        }
    };
    ($(($FUNC:ident, $DOC:expr)),*) => {
        pub mod expr_fn {
            $(
                #[doc = $DOC]
                /// Return $name(arg)
                pub fn $FUNC(args: Vec<datafusion_expr::Expr>) -> datafusion_expr::Expr {
                    super::$FUNC().call(args)
                }
            )*
        }

    }
}

I have two macro rules with repetitions allowed, it only works if all the rule is the same type, but I don't know how can I mix these two rules in the group.

I would like this caller to work

export_functions!(
    (nullif, arg_1 arg_2, "returns NULL if value1 equals value2; otherwise it returns value1. This can be used to perform the inverse operation of the COALESCE expression."),
    (coalesce, "Returns `coalesce(args...)`, which evaluates to the value of the first expr which is not NULL"),
);

Is mixing rules in repetition possible to do?

compiler complains with

no rules expected the token `"Returns `coalesce(args...)`, which evaluates to the value of the first expr which is not NULL"`
no rules expected this token in macro call

I think what you want can be achieved by splitting the macro into two macro: one to do the switch between the different kind of rules, and one to repeat multiple inputs.

Here is a minimal example showing what I mean:

fn main() {
    foo();
    bar();
    baz();
}

macro_rules! switch {
    ({ $name:ident, $doc:literal }) => {
        #[doc = $doc]
        fn $name() {}
    };
    ($name:ident) => {
        fn $name() {}
    };
}

macro_rules! repeat {
    ($($tokens:tt),*) => {
        $(
            switch!($tokens);
        )*
    }
}

repeat!(foo, {baz, "baz func"}, bar);
1 Like

Thanks! Although we still need two macro, but we can maintain switch only, it is better than maintaining two separate macro rules.

1 Like

Found a small pattern-matching hack that embeds switch into repeat

fn main() {
    foo();
    bar();
    baz();
}

macro_rules! repeat {
    ($($tokens:tt),*) => {
        $(
            // Move to one of the two cases below
            repeat!(switch $tokens);
        )*
    };

    (switch { $name:ident, $doc:literal }) => {
        #[doc = $doc]
        fn $name() {}
    };

    (switch $name:ident) => {
        fn $name() {}
    };
}

repeat!(foo, {baz, "baz func"}, bar);

Note that switch is just a token, and can be replaced with any valid token of your choice.

1 Like

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.