I want to generate a static array (doing this at runtime is not an option) with help of a macro.
My attempts are
macro_rules! test {
($($data:expr),*) => {
[ test!(@xform $($data),*) ]
};
(@xform) => { };
(@xform $a:expr) => { be32($a) };
(@xform $a:expr, $($data:expr),*) => { be32($a), test!(@xform $($data),*) };
}
// just for simplicity...
const fn be32(v: u32) -> u32 { v + 1 }
static FILE_HEADER_0: [u32;2] = test!(1, 2);
static FILE_HEADER_1: [u32;2] = [be32(1), be32(2)];
but this fails with
error: macro expansion ignores token `,` and any following
--> src/lib.rs:8:52
|
3 | [ test!(@xform $($data),*) ]
| ------------------------- help: you might be missing a semicolon here: `;`
| |
| caused by the macro expansion here
...
8 | (@xform $a:expr, $($data:expr),*) => { be32($a), test!(@xform $($data),*) };
| ^
|
= note: the usage of `test!` is likely invalid in expression context
I expect that FILE_HEADER_0
is generated like FILE_HEADER_1
Is this possible with normal macro_rules!
or do I have to use proc_macro
?
Your macro expands to multiple comma-separated expressions, and this is invalid. It has to generate only one.
The usual solution to that is push-down accumulation: accumulate the resulting array in the macro and keep adding elements to it:
macro_rules! test {
($($data:expr),*) => {
test!(@xform [] $($data),*)
};
(@xform $arr:tt) => { $arr };
(@xform [ $($arr:tt)* ] $a:expr) => {
test!(@xform [ $($arr)* be32($a) ])
};
(@xform [ $($arr:tt)* ] $a:expr, $($data:expr),*) => {
test!(@xform [ $($arr)* be32($a), ] $($data),*)
};
}
In the first expansion we will have test!(@xform [] 1, 2)
. This will reach the last arm and expand to test!(@xform [ be(1), ] 2)
. This will expand (by the third arm) to test!(@xform [ be(1), be(2) ])
which will finally expand to [be(1), be(2)]
by the second arm.
In this case, however, you don't need complex solutions. A simple macro will do:
macro_rules! test {
($($data:expr),*) => {
[ $( be32($data) ),* ]
};
}