It is possible to generate a struct from a macro_rules macro, where the fields are supplied by the tokentree input of the macro? I made the following simplified example:
I seems that the macro inside the struct is not expanded.
I also tried adding generated attributes to a struct and generate an inner struct with a #[derive]. I didn't got the attributes connected to the struct.
Macros must expand to a complete syntax element (e.g. item, expression etc). A list of fields is not a complete anything.
The way to do this is with a recursive macro that builds up partial results by passing them to itself, and then generates the output all at once. This is often called a muncher because it methodically munches through its input.
Your macro, converted to a muncher, would look something like this:
macro_rules! new_struct {
// input is empty: time to output
(@munch () -> {$(#[$attr:meta])* struct $name:ident $(($id:ident: $ty:ty))*}) => {
$(#[$attr])* struct $name { $($id: $ty),* }
};
// branch off to generate an inner struct
(@munch ($id:ident: struct $name:ident {$($inner:tt)*} $($next:tt)*) -> {$(#[$attr:meta])* struct $($output:tt)*}) => {
new_struct!(@munch ($($inner)*) -> {$(#[$attr])* struct $name});
new_struct!(@munch ($($next)*) -> {$(#[$attr])* struct $($output)* ($id: $name)});
};
// throw on the last field
(@munch ($id:ident: $ty:ty) -> {$($output:tt)*}) => {
new_struct!(@munch () -> {$($output)* ($id: $ty)});
};
// throw on another field (not the last one)
(@munch ($id:ident: $ty:ty, $($next:tt)*) -> {$($output:tt)*}) => {
new_struct!(@munch ($($next)*) -> {$($output)* ($id: $ty)});
};
// entry point (this is where a macro call starts)
($(#[$attr:meta])* struct $name:ident { $($input:tt)*} ) => {
new_struct!(@munch ($($input)*) -> {$(#[$attr])* struct $name});
// ^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^
// input output
}
}
new_struct! {
#[derive(Debug)]
struct Foo {
foo: i32,
bar: struct Bar {
bar: i32,
foobar: i64
}
}
}
fn main() {
println!("{:#?}", Foo { foo: 1, bar: Bar { bar: 2, foobar: 3 } });
}
I took some liberties with your syntax because a few things didn't make sense: like where did you plan to get field names or type names for the inner structs?
The general strategy is to transform the input, with its nested structs, into a flat structure that looks like #[$attr]... struct $name { $id: $ty, ...}. But this is kept in the $($output:tt)* argument until the macro is ready to output the whole thing. You can put #![feature(trace_macros)] trace_macros!(true); at the top of the file to see this happening.
I'm happy to add more comments to my code if needed. By the way, I highly recommend reading Daniel Keep's The Little Book of Rust Macros, it explains lots of tricks like this.
Thanks for your response. With this information I succeeded in completing the macro.
My objection was to flatten the result struct. So instead of directly outputting the struct, I took the information up to the top level and made the full struct there.
I also find the documentation you suggested very helpfull. It's a pity I didn't recognize that documentation before.