Skip to main content

repetitive/
lib.rs

1//! `repetitive!` macro crate.
2
3#![deny(missing_docs)]
4
5mod context;
6mod expr;
7mod fragment;
8mod stages;
9mod tokens;
10use context::*;
11use expr::*;
12use fragment::*;
13use stages::*;
14use tokens::*;
15
16/// The macro!
17///
18/// This macro emits the exact same code it gets,
19/// except for when you use fragments through the `@` prefix.
20///
21/// # Fragments
22///
23/// ### For Loop
24///
25/// Emits the body per value of the specified list.
26///
27/// Syntax:
28/// `@for <pattern> in <list> { <code> }`
29///
30/// Also accepts multiple patterns separated by commas.
31///
32/// ```rust
33/// @for a in [...], b in [...] {
34///     <code>
35/// }
36/// ```
37///
38/// Acts like:
39/// ```rust
40/// @for a in [...] {
41///     @for b in [...] {
42///         <code>
43///     }
44/// }
45/// ```
46///
47/// Example:
48/// ```rust
49/// @for Color in ['Red, 'Green, 'Blue] {
50///     struct @Color;
51/// }
52/// ```
53///
54/// ### Concat
55///
56/// Concats multiple identifiers into one.
57///
58/// Syntax:
59/// `@[<expr> <expr>...]`
60///
61/// Also accepts `@str[...]` which emits a string literal instead of an identifier.
62///
63/// Example:
64/// ```rust
65/// @for N in 2..=4 {
66///     struct @['Vec N];
67/// }
68/// ```
69///
70/// ### Let Statement
71///
72/// Binds a value to a name.
73///
74/// Syntax:
75/// `@let <pattern> = <expr>;`
76///
77/// Example:
78/// ```rust
79/// @for N in 2..=4 {
80///     @let VecN = @['Vec N];
81///
82///     struct @VecN;
83/// }
84/// ```
85///
86/// ### If Statement
87///
88/// Emits the body if the condition is true.
89///
90/// Syntax:
91/// `@if <expr> { <code> }`
92///
93/// Also accepts `@if <expr> { <code> } else { <code> }`.
94///
95/// Example:
96/// ```rust
97/// @for a in 0..5, b in 0..5 {
98///     @if a != b {
99///         println!("{} != {}", a, b);
100///     }
101/// }
102/// ```
103///
104/// ### Expression
105///
106/// Emits the result of an expression.
107///
108/// Syntax:
109/// `@(<expr>)`
110///
111/// Example:
112/// ```rust
113/// @for idx in 0..5 {
114///     example::<@(idx + 1)>();
115/// }
116/// ```
117///
118/// ### Tokens
119///
120/// Stores tokens as a fragment value.
121///
122/// Syntax:
123/// `@{ <token> }`
124///
125/// Example:
126/// ```rust
127/// @let tokens = @{ @frag + 1 };
128///
129/// @for frag in [1, 2, 3] {
130///     println!("{}", @tokens);
131/// }
132/// ```
133///
134/// # Expressions
135///
136/// Expressions can evaluate to these types:
137/// - bool lit,
138/// - int lit,
139/// - float lit,
140/// - char lit,
141/// - string lit,
142/// - ident,
143/// - list,
144/// - tokenstream.
145///
146/// An expression can be:
147/// - a literal: `1`, `1.0`, `'a'`, `"hello"`, `true`, `false`,
148/// - an "ident literal": `'Ident`, `~Ident` (useful for declarative macros. `~$ident_lit_frag`),
149/// - a list: `[... <expr> ...]`,
150/// - a name: `name`,
151/// - a fragment: `@{...}`,
152/// - if else: `@if <expr> { <expr> } else { <expr> }`,
153/// - an operator: `<expr> + <expr>`, `!<expr>`,
154/// - a method call: `<expr>.<method>(<expr>)`.
155///
156/// These operators are supported:
157/// - add `+`, sub `-`, mul `*`, div `/`, rem `%`, neg `-`,
158/// - bitand `&`, bitor `|`, bitxor `^`, shl `<<`, shr `>>`,
159/// - eq `==`, ne `!=`, lt `<`, le `<=`, gt `>`, ge `>=`,
160/// - and `&&`, or `||`, not `!`,
161/// - range `..`, range_inclusive `..=`.
162///
163/// Supported methods are known by auto-completion.
164///
165/// # Match
166///
167/// Can be used as an expression or as a fragment.
168///
169/// Example:
170/// ```rust
171/// @for weird in [
172///     "Weird",
173///     [1, 2, 3],
174///     @{ @frag + 1 },
175/// ] {
176///     @match weird {
177///         "Weird" => { println!("Its a string!") },
178///         [1, 2, _] => { println!("It starts with 1 and 2!") },
179///         something_else => { println!("Its something else!") },
180///     }
181/// }
182/// ```
183#[proc_macro]
184pub fn repetitive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
185    main::repetitive(input.into()).into()
186}
187
188mod main {
189    use proc_macro2::TokenStream;
190    use quote::quote;
191
192    use super::*;
193
194    pub fn repetitive(input: TokenStream) -> TokenStream {
195        let mut ctx = Context::new();
196
197        let tokens = 'tokens: {
198            let tokens = match Tokens::ctx_parse.ctx_parse2(input.into(), &mut ctx) {
199                Ok(tokens) => tokens,
200                Err(err) => {
201                    ctx.push_error(err);
202                    break 'tokens Err(());
203                }
204            };
205
206            let mut output = TokenStream::new();
207            tokens.expand(&mut output, &mut ctx, &mut Namespace::new());
208
209            if ctx.has_errors() {
210                break 'tokens Err(());
211            }
212
213            Ok(output)
214        };
215
216        let tokens = match tokens {
217            Ok(output) => output,
218            Err(()) => ctx
219                .take_errors()
220                .collect::<Vec<_>>()
221                .into_iter()
222                .map(|err| err.into_compile_error(&mut ctx))
223                .collect(),
224        };
225
226        let warnings = ctx
227            .take_warnings()
228            .map(|warning| warning.into_compile_error());
229
230        #[cfg(feature = "doc")]
231        let doc = paste_method_doc(ctx.get_method_calls());
232
233        #[cfg(not(feature = "doc"))]
234        let doc = TokenStream::new();
235
236        quote! {
237            #tokens
238
239            #(#warnings)*
240
241            #doc
242        }
243        .into()
244    }
245}