macro_loop/
lib.rs

1#![doc = "`macro_loop!` macro crate"]
2#![deny(missing_docs)]
3
4mod expr;
5mod fragment;
6mod name;
7mod value;
8
9mod util;
10use util::*;
11
12#[cfg(test)]
13mod speedtests;
14
15/// `macro_loop!` provides special fragment features using `@`.
16///
17/// # For Loops
18///
19/// Syntax: `@for <item> in <values> { ... }`
20///
21/// For loops emit their body per value:
22///
23/// ```rust
24/// macro_loop! {
25///     @for N in 2..=4 {
26///         struct @[Vec @N];
27///     }
28/// }
29///
30/// // outputs:
31/// // struct Vec2;
32/// // struct Vec3;
33/// // struct Vec4;
34/// ```
35///
36/// The `<item>` needs to be a ***pattern*** - Either:
37/// * an identifier (`Prime`),
38/// * an array of patterns (`[CapeLight, [Sprinter, Truth]]`).
39///
40/// The `<values>` needs to be an array value that matches the `<item>` pattern.
41/// A values is either:
42/// * a literal,
43/// * an identifier,
44/// * an array of values.
45///
46/// Values support operators such as `+`, `..` and `==`.
47///
48/// Declaring a for loop with multiple parameters (`@for a in [...], b in [...]`),
49/// emits the body per value combination.
50///
51/// # If Statements
52///
53/// Syntax: `@if <condition> { ... }`
54///
55/// An if statement emits its body only if its condition is met:
56///
57/// ```rust
58/// macro_rules! not_equal {
59///     ($a:ident $b:ident) => {
60///         macro_loop! {
61///             @if $a != $b {
62///                 println!("{}", stringify!($a != $b))
63///             }
64///         }
65///     };
66/// }
67///
68/// fn main() {
69///     not_equal!(a a); // doesn't print
70///     not_equal!(a b); // prints
71///     not_equal!(b b); // doesn't print
72/// }
73/// ```
74///
75/// The `<condition>` needs to be a bool value.
76///
77/// # Let Statements
78///
79/// Syntax: `@let <name> = <value>;`
80///
81/// Let statements declare names that have a value:
82///
83/// macro_loop! {
84///     @let components = [x, y, z, w];
85///
86///     @for X in @components, Y in @components] {
87///         ...
88///     }
89/// }
90///
91/// The `<name>` needs to be a pattern, and the ~value~ has to match it.
92///
93/// # Identifiers
94///
95/// Syntax: `@[<idents>]`
96///
97/// Concats identifier segments into a single identifier:
98///
99/// ```rust
100/// @let N = 5;
101///
102/// struct @[Struct @N]; // Struct5
103/// ```
104#[proc_macro]
105pub fn macro_loop(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
106    use syn::parse::Parser;
107
108    match macro_loop2.parse2(input.into()) {
109        Ok(stream) => stream,
110        Err(err) => err.into_compile_error(),
111    }
112    .into()
113}
114
115fn macro_loop2(input: syn::parse::ParseStream) -> syn::Result<proc_macro2::TokenStream> {
116    use syn::parse::Parse;
117
118    let name_stream = name::NameStream::parse(input)?;
119
120    name_stream.resolve(&name::Namespace::new())
121}