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}