0

Is it possible to write a declarative macro in Rust that takes a tree like structure of blocks and creates a combined assortment of enums? I'm trying to determine if this is only possible with procedural macros, or if I can get away with using TT munchers. Recursion isn't the only challenge, as I would also need to combine the identifiers used to label each block, like Exposure and Mode becoming ExposureMode, which I can't seem to find a means of achieving.

For example, given a statement like:

enum_tree! {
  Exposure {
    Mode {
      FullAuto,
      Manual,
      ShutterPriority,
      IrisPriority,
      GainPriority,
    },
    Iris {
      Reset,
      Up,
      Down,
      Direct(u8),
    },
  },
  Focus {
    In,
    Out,
    Stop,
  }
}

would yield a result like:

enum ExposureMode {
    FullAuto,
    Manual,
    ShutterPriority,
    IrisPriority,
    GainPriority,
}
enum ExposureIris {
    Reset,
    Up,
    Down,
    Direct(u8),
}
enum Focus {
    In,
    Out,
    Stop,
}
Arbiter
  • 3
  • 1

1 Answers1

0

If I remember correctly, tt-munchers are turing-complete and thus almost equivalent to proc macros. Even if they are not in general, this problem is solvable with them.

The only thing they cannot do is to manipulate atoms... Like concatenating identifiers. There is the concat_ident!() macro for that, but as explained in its documentation:

Also, as a general rule, macros are only allowed in item, statement or expression position. That means while you may use this macro for referring to existing variables, functions or modules etc, you cannot define a new one with it.

So it is not applicable in this case. However, that doesn't mean you cannot use a macro_rules, because people have already wrote crates with proc macros that can concatenate identifiers. A popular example is paste.

However, if the macro is complex, you may find it much easier to understand and write as a proc macro.

See also Is it possible to declare variables procedurally using Rust macros?.

Chayim Friedman
  • 47,971
  • 5
  • 48
  • 77
  • One fundamental difference between proc macros and declarative macros is that proc macros are note hygienic, while declarative macros are partially hygienic. – Sven Marnach May 03 '22 at 09:30
  • Instead of using the paste crate, I'd suggest using nested modules here – that makes the whole macro implementation a lot easier. – Sven Marnach May 03 '22 at 09:31
  • @SvenMarnach Changing hygiene is manipulating identifiers. And using modules may be better, but it depends on the concrete use-case. – Chayim Friedman May 03 '22 at 09:34
  • Ah dang, that concat_ident macro looks perfect, but if creating new identifiers goes against good hygiene I should probably avoid it. Unless of course using paste gets the job done, but I like to avoid adding more dependencies if I can avoid it, but it's better than using a feature-gated macro that might just disappear. It also seems like the docs mention that I could get a tt-muncher macro working, given that it's turing-complete, but it might not be the best for compile-time performance. – Arbiter May 03 '22 at 16:30
  • @Arbiter Using proc macros you can get hygiene, but also can not. And if all you do is generate enums, hygiene doesn't matter at all. I think `paste` do it correctly in presence of only one identifier, though. And I didn't say this is good for performance, though proc macros have their own issues :) (e.g. their own compilation time). In [The Little Book of Rust Macros](https://veykril.github.io/tlborm/) there are few chapters about macro-by-example's performance. – Chayim Friedman May 03 '22 at 20:34