Skip to content

Commit 87b9786

Browse files
committed
Add documentation for meta variable expressions
1 parent e356977 commit 87b9786

File tree

1 file changed

+270
-0
lines changed

1 file changed

+270
-0
lines changed

src/macros-by-example.md

+270
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,276 @@ compiler knows how to expand them properly:
196196
not have the same number. This requirement applies to every layer of nested
197197
repetitions.
198198

199+
## Metavariable expressions
200+
201+
Metavariable expressions live within a special braced syntax `${...}` and are function-like operators used to access "meta" information about how macros get matched and expanded, such as how many items get matched in a repetition group. This information is typically difficult or even
202+
impossible to otherwise obtain.
203+
204+
### count($ident, depth=0)
205+
206+
Expands to an unsuffixed integer literal representing the number of times a ***repetition*** repeats in total. That is, this will be the number of times that a repetition would be expanded.
207+
208+
The output of `count` depends on where it is placed as well the provided index. If no index is provided, then it will always start at the innermost level.
209+
210+
```rust
211+
macro_rules! count_idents {
212+
( $( $i:ident ),* ) => {
213+
${count($i)}
214+
};
215+
}
216+
217+
fn main() {
218+
assert_eq!(count_idents!(a, b, c), 3);
219+
}
220+
```
221+
222+
If repetitions are nested, then the optional depth parameter can be used to
223+
count the repetitions of a parent repetition group.
224+
225+
Let `M` be the number of repetition groups in the matcher, `T` the depth placement in the transcriber and `C` the written depth in `count`, then it is possible to infer that `M > T + C`.
226+
227+
We are counting how often the repetition group `C` up from a metavariable in the matcher occurs inside the current iteration of the `T` outer repetitions in the transcriber, effectively making the metavariable a reference or starting point.
228+
229+
```rust
230+
macro_rules! count_value {
231+
( $( $name:ident: $( $value:literal ),* );+ ) => {
232+
// Count the total number of times that the (innermost) group
233+
// containing `$value` gets matched.
234+
${count($value)}
235+
// This is the same as `${count($value, 0)}`
236+
};
237+
}
238+
macro_rules! count_name1 {
239+
( $( $name:ident: $( $value:literal ),* );+ ) => {
240+
// This is one way to get the number of times that the group
241+
// containing `$name` gets matched. Alternatively...
242+
${count($name)}
243+
};
244+
}
245+
macro_rules! count_name2 {
246+
( $( $name:ident: $( $value:literal ),* );+ ) => {
247+
// ...`$value` can be used again with a depth specifier of 1,
248+
// indicating that repetitions of the 1st parent group of
249+
// `$value` should be counted, rather than `$value`'s innermost group.
250+
//
251+
// `1` is the maximum value here since `$value`'s group has a single
252+
// parent.
253+
${count($value, 1)}
254+
};
255+
}
256+
macro_rules! count_value_nested {
257+
( $( $name:ident: $( $value:literal ),* );+ ) => {
258+
[ $(
259+
// using `count` within a repetition group will return the number
260+
// of times `$value` is matched _within that group_.
261+
${count($value)},
262+
)+ ]
263+
};
264+
}
265+
fn main() {
266+
// all instances of `$value` counted: count(1, 2, 3, 4, 5) = 5
267+
assert_eq!(count_value!(a: 1, 2, 3; b: 4, 5), 5);
268+
// count(1, 2, 3, ... 11) = 11
269+
assert_eq!(
270+
count_value!(a: 1, 2, 3; b: 4, 5; c: 6, 7; d: 8, 9, 10, 11),
271+
11
272+
);
273+
// `$value` is never matched; count() = 0
274+
assert_eq!(count_value!(a:), 0);
275+
// count(a, b) = 2 matches
276+
assert_eq!(count_name1!(a: 1, 2, 3; b: 4, 5), 2);
277+
// count(a, b, c, d) = 4 matches
278+
assert_eq!(
279+
count_name1!(a: 1, 2, 3; b: 4, 5; c: 6, 7; d: 8, 9, 10, 11),
280+
4
281+
);
282+
// count(a) = 1 match
283+
assert_eq!(count_name1!(a:), 1);
284+
// These have the same results as the above
285+
assert_eq!(count_name2!(a: 1, 2, 3; b: 4, 5), 2);
286+
assert_eq!(
287+
count_name2!(a: 1, 2, 3; b: 4, 5; c: 6, 7; d: 8, 9, 10, 11),
288+
4
289+
);
290+
assert_eq!(count_name2!(a:), 1);
291+
// first match: count(1, 2, 3) = 3. second match: count(4, 5) = 2.
292+
assert_eq!(count_value_nested!(a: 1, 2, 3; b: 4, 5), [3, 2]);
293+
// first match: count(1, 2, 3) = 3. second match: count(4, 5) = 2.
294+
// third match: count(6, 7) = 4. fourth match: count(8, 9, 10, 11) = 4.
295+
assert_eq!(
296+
count_value_nested!(a: 1, 2, 3; b: 4, 5; c: 6, 7; d: 8, 9, 10, 11),
297+
[3, 2, 2, 4]
298+
);
299+
// `$value` is never matched; count() = 0
300+
assert_eq!(count_value_nested!(a:), [0]);
301+
}
302+
```
303+
304+
`count` can not be placed inside the repetition depth of its referenced metavariable, otherwise the output would always be 1.
305+
306+
### ignore($ident)
307+
308+
Sometimes it is desired to repeat an expansion the same number of times as a metavariable repeats but without actually expanding the metavariable.
309+
310+
```rust
311+
macro_rules! count {
312+
( $( $i:ident ),* ) => {{
313+
0 $( + 1 ${ignore($i)} )*
314+
}};
315+
}
316+
fn main() {
317+
assert_eq!(count!(a, b, c), 3);
318+
}
319+
```
320+
321+
The `ignore` metavariable acts as if the ident was used for the purposes of repetition, but expands to nothing.
322+
323+
### index(depth=0)
324+
325+
Expands to an unsuffixed integer literal representing the current iteration index of a ***repetition*** at a given depth.
326+
327+
The output of `index` depends on where it is placed as well the provided index. If no index is provided, then it will always start at the innermost level.
328+
329+
```rust
330+
/// The length of all items within a tuple
331+
trait TotalLen {
332+
fn total_len(&self) -> usize;
333+
}
334+
/// Implement `TotalLen` for a n-length tuple
335+
macro_rules! impl_tuple {
336+
( $( $generic:ident ),* ) => {
337+
impl<$( $generic, )*> TotalLen for ($( $generic, )*)
338+
where
339+
$( $generic: AsRef<[u8]>, )*
340+
{
341+
fn total_len(&self) -> usize {
342+
let mut sum: usize = 0;
343+
$({ // repetition
344+
${ignore($generic)}
345+
// `${index()}` will expand to 0, 1, 2... for each tuple element
346+
sum = sum.wrapping_add(self.${index()}.as_ref().len());
347+
})* // end repetition
348+
sum
349+
}
350+
}
351+
};
352+
}
353+
// Implement for length 2 tuple
354+
impl_tuple!(T1, T2);
355+
// Implement for length 3 tuple
356+
impl_tuple!(T1, T2, T3);
357+
358+
fn main() {
359+
assert_eq!(([1, 2, 3], [4, 5]).total_len(), 5);
360+
assert_eq!(([1].as_slice(), vec![2, 3]).total_len(), 3);
361+
}
362+
```
363+
364+
If repetitions are nested, then the optional depth parameter can be used to
365+
count the repetitions of a parent repetition group.
366+
367+
```rust
368+
macro_rules! innermost0 {
369+
( $( $a:ident: $( $b:literal ),* );+ ) => {
370+
// Count the index of `$b`'s group
371+
[$( $( ${ignore($b)} ${index()}, )* )+]
372+
};
373+
}
374+
375+
macro_rules! innermost1 {
376+
( $( $a:ident: $( $b:literal ),* );+ ) => {
377+
// Count the index of `$b`'s parent repetition group, i.e. `$a`'s group
378+
[$( $( ${ignore($b)} ${index(1)}, )* )+]
379+
};
380+
}
381+
382+
macro_rules! outermost {
383+
( $( $a:ident: $( $b:literal ),* );+ ) => {
384+
// // Count the index of `$a`'s group directly
385+
[$( ${ignore($a)} ${index()}, )+]
386+
};
387+
}
388+
389+
fn main() {
390+
// 1 2 3 = 3 elements
391+
// 4 5 = 2 elements
392+
//
393+
// Increasing list from the innermost loop referring innermost indexes
394+
assert_eq!(innermost0!(a: 1, 2, 3; b: 4, 5), [0, 1, 2, 0, 1]);
395+
396+
// a b = 2 elements
397+
//
398+
// Increasing list from the innermost loop referring outermost indexes
399+
assert_eq!(innermost1!(a: 1, 2, 3; b: 4, 5), [0, 0, 0, 1, 1]);
400+
401+
// a b = 2 elements
402+
//
403+
// Increasing list from the outermost loop referring outermost indexes
404+
assert_eq!(outermost!(a: 1, 2, 3; b: 4, 5), [0, 1]);
405+
}
406+
```
407+
408+
### len(depth=0)
409+
410+
Expands to an unsuffixed integer literal representing the sum or length of a ***repetition*** at a given depth.
411+
412+
The output of `len` depends on where it is placed as well the provided index. If no index is provided, then it will always start at the innermost level.
413+
414+
```rust
415+
macro_rules! array {
416+
( $( $i:ident ),* ) => {
417+
[$( ${len()}, )*]
418+
};
419+
}
420+
421+
fn main() {
422+
assert_eq!(array!(A, B, C), [3, 3, 3]);
423+
}
424+
```
425+
426+
If repetitions are nested, then the optional depth parameter can be used to
427+
count the repetitions of a parent repetition group.
428+
429+
```rust
430+
macro_rules! innermost0 {
431+
( $( $a:ident: $( $b:literal ),* );+ ) => {
432+
[$( $( ${ignore($b)} ${len()}, )* )+]
433+
};
434+
}
435+
436+
macro_rules! innermost1 {
437+
( $( $a:ident: $( $b:literal ),* );+ ) => {
438+
[$( $( ${ignore($b)} ${len(1)}, )* )+]
439+
};
440+
}
441+
442+
macro_rules! outermost {
443+
( $( $a:ident: $( $b:literal ),* );+ ) => {
444+
[$( ${ignore($a)} ${len()}, )+]
445+
};
446+
}
447+
448+
fn main() {
449+
// 1 2 3 = 3 elements
450+
// 4 5 = 2 elements
451+
//
452+
// 3 and 2 elements repeating 3 and 2 times in the innermost loop
453+
assert_eq!(innermost0!(a: 1, 2, 3; b: 4, 5), [3, 3, 3, 2, 2]);
454+
455+
// a b = 2 elements
456+
//
457+
// 2 elements repeating 5 times in the innermost loop
458+
assert_eq!(innermost1!(a: 1, 2, 3; b: 4, 5), [2, 2, 2, 2, 2]);
459+
460+
// a b = 2 elements
461+
//
462+
// 2 elements repeating 2 times in the outermost loop
463+
assert_eq!(outermost!(a: 1, 2, 3; b: 4, 5), [2, 2]);
464+
}
465+
```
466+
467+
Unlike `count`, `len` must be placed inside a repetition.
468+
199469
## Scoping, Exporting, and Importing
200470

201471
For historical reasons, the scoping of macros by example does not work entirely

0 commit comments

Comments
 (0)