0

lets say I wan't to generate this with just syntactic macros:

struct Foo {
    bar: [Bar; 100],
    gee: [Gee; 100],
}

with just this macro call:

make_struct!(100; Bar, Gee);

The first and obvious problem is is that lowercase bar/gee which as far as I know is impossible to create on syntactic macros so I made a small utility proc macro.

use proc_macro::{Ident, Span, TokenTree, TokenStream};

/// Take identifier and return it lowercase
#[proc_macro]
pub fn lower(stream: TokenStream) -> TokenStream {
    let tt = stream.into_iter().nth(0).unwrap();
    if let TokenTree::Ident(ident) = tt {
        dbg!(&ident);
        let new_ident = ident.to_string().to_lowercase();
        dbg!(&new_ident);
        TokenTree::Ident(
            Ident::new(&new_ident[..], Span::mixed_site())).into()
    } else {
        panic!("Invalid input to `lower` macro, just accepts one literal");
    }
}

Then how do I use this macro inside a syntactic macro? Because this works:

macro_rules! test {
    ($x:ident) => {
        let lower!($x) = 10;
    }
}

test!(Foo); // -> let foo = 10;

But this fails:

macro_rules! test {
    ($x:ident) => {
        struct Test { lower!($x) : [$x; 100] }
    }
}

test!(Foo); // -> (); ERROR

Is this a compiler error? or I'm doing something wrong?

cdecompilador
  • 178
  • 2
  • 9
  • Can you post the full error message that you're getting? – Dogbert Aug 07 '22 at 13:43
  • There are multriple issues, but the main thing is that the proc-macro is not expanded the right moment so it says that `lower!($x)` for example is not a valid identifier – cdecompilador Aug 07 '22 at 15:44

1 Answers1

3

The problem you are encountering is that macro invocations are not allowed in arbitrary locations in the source code, but only certain contexts (like expressions and items).

There is a workaround, however, and it is already implemented by the paste library which does what your proc macro does as part of its main job of concatenating identifiers. The macro can be invoked from a larger context and process the tokens within it however it likes. With paste:

macro_rules! test {
    ($x:ident) => {
        paste::paste! {
            struct Test { [<$x:lower>] : [$x; 100] }
        }
    }
}

In use, the paste! macro by itself can be placed anywhere a macro invocation can, and it does nothing until it sees the token sequence [< ... >] telling it to process the tokens within those regions in particular. Thus paste bypasses the restriction on macro invocation positions.

Kevin Reid
  • 37,492
  • 13
  • 80
  • 108
  • I didn't wanted to process everything with proc macros because thats quite slow but it makes the job so thanks, if no one knows a way of doing it without full processing I'll mark this as the solution – cdecompilador Aug 07 '22 at 15:42