Skip to content

Commit

Permalink
Add documentation for meta variable expressions
Browse files Browse the repository at this point in the history
  • Loading branch information
c410-f3r committed May 17, 2024
1 parent e356977 commit 327c2f8
Showing 1 changed file with 214 additions and 0 deletions.
214 changes: 214 additions & 0 deletions src/macros-by-example.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,220 @@ compiler knows how to expand them properly:
not have the same number. This requirement applies to every layer of nested
repetitions.

## Metavariable expressions

Gives access to additional information about metavariables that otherwise would be difficult or even impossible to obtain manually.

### count($ident, depth)

Expands to an unsuffixed integer literal representing the number of times a ***metavariable*** repeats in total.

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.

```rust
macro_rules! count_idents {
( $( $i:ident ),* ) => {
${count($i)}
};
}

fn main() {
assert_eq!(count_idents!(a, b, c), 3);
}
```

If repetitions are nested, then the optional depth parameter can be used to limit the number of nested repetitions that are counted.

```rust
macro_rules! no_repetition0 {
( $( $a:ident: $( $b:literal ),* );+ ) => {
[${count($b)}]
};
}

macro_rules! no_repetition1 {
( $( $a:ident: $( $b:literal ),* );+ ) => {
[${count($b, 1)}]
};
}

macro_rules! outermost {
( $( $a:ident: $( $b:literal ),* );+ ) => {
[$( ${ignore($a)} ${count($b)}, )+]
};
}

fn main() {
// 1 2 3 4 5 = 5 elements
assert_eq!(no_repetition0!(a: 1, 2, 3; b: 4, 5), [5]);

// a b = 2 elements
assert_eq!(no_repetition1!(a: 1, 2, 3; b: 4, 5), [2]);

// 1 2 3 = 3 elements
// 4 5 = 2 elements
assert_eq!(outermost!(a: 1, 2, 3; b: 4, 5), [3, 2]);
}
```

`count` can not be placed inside the innermost repetition.

### ignore($ident)

Binds a metavariable for repetition, but expands to nothing.

```rust
macro_rules! count {
( $( $i:stmt ),* ) => {{
0 $( + 1 ${ignore($i)} )*
}};
}

fn main() {
assert_eq!(count!(if true {} else {}, let _: () = (), || false), 3);
}
```

Sometimes it is desired to repeat an expansion the same number of times as a metavariable repeats but without actually expanding the metavariable. It may be possible to work around this by expanding the metavariable in an expression like `{ let _ = $x; 1 }`, where the expanded value of `$x` is ignored, but this is only possible if what `$x` expands to is valid in this kind of expression.

The `ignore` metavariable acts as if the ident was used for the purposes of repetition, but expands to nothing.

### index(depth)

Expands to an unsuffixed integer literal representing the current iteration index of a ***repetition*** at the given depth.

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.

```rust
trait Len {
fn len(&self) -> usize;
}

macro_rules! impl_tuple {
( $( $name:ident ),* ) => {
impl<$( $name, )*> Len for ($( $name, )*)
where
$( $name: AsRef<[u8]>, )*
{
fn len(&self) -> usize {
let mut sum: usize = 0;
$({
${ignore($name)}
sum = sum.wrapping_add(self.${index()}.as_ref().len());
})*
sum
}
}
};
}

impl_tuple!(A, B);

fn main() {
assert_eq!((&[1, 2, 3][..], &[4, 5][..]).len(), 5);
}
```

If repetitions are nested, then the optional depth parameter can be used to limit the number of nested repetitions that are counted.

```rust
macro_rules! innermost0 {
( $( $a:ident: $( $b:literal ),* );+ ) => {
[$( $( ${ignore($b)} ${index()}, )* )+]
};
}

macro_rules! innermost1 {
( $( $a:ident: $( $b:literal ),* );+ ) => {
[$( $( ${ignore($b)} ${index(1)}, )* )+]
};
}

macro_rules! outermost {
( $( $a:ident: $( $b:literal ),* );+ ) => {
[$( ${ignore($a)} ${index()}, )+]
};
}

fn main() {
// 1 2 3 = 3 elements
// 4 5 = 2 elements
//
// Increasing list from the innermost loop refering innermost indexes
assert_eq!(innermost0!(a: 1, 2, 3; b: 4, 5), [0, 1, 2, 0, 1]);

// a b = 2 elements
//
// Increasing list from the innermost loop refering outermost indexes
assert_eq!(innermost1!(a: 1, 2, 3; b: 4, 5), [0, 0, 0, 1, 1]);

// a b = 2 elements
//
// Increasing list from the outermost loop refering outermost indexes
assert_eq!(outermost!(a: 1, 2, 3; b: 4, 5), [0, 1]);
}
```

### len(depth)

Expands to an unsuffixed integer literal representing the sum or length of a ***repetition*** at a given depth.

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.

```rust
macro_rules! array {
( $( $i:ident ),* ) => {
[${len()}, )*]
};
}

fn main() {
assert_eq!(array!(A, B, C), [3, 3, 3]);
}
```

If repetitions are nested, then the optional depth parameter can be used to limit the number of nested repetitions that are counted.

```rust
macro_rules! innermost0 {
( $( $a:ident: $( $b:literal ),* );+ ) => {
[$( $( ${ignore($b)} ${len()}, )* )+]
};
}

macro_rules! innermost1 {
( $( $a:ident: $( $b:literal ),* );+ ) => {
[$( $( ${ignore($b)} ${len(1)}, )* )+]
};
}

macro_rules! outermost {
( $( $a:ident: $( $b:literal ),* );+ ) => {
[$( ${ignore($a)} ${len()}, )+]
};
}

fn main() {
// 1 2 3 = 3 elements
// 4 5 = 2 elements
//
// 3 and 2 elements repeating 3 and 2 times in the innermost loop
assert_eq!(innermost0!(a: 1, 2, 3; b: 4, 5), [3, 3, 3, 2, 2]);

// a b = 2 elements
//
// 2 elements repeating 5 times in the innermost loop
assert_eq!(innermost1!(a: 1, 2, 3; b: 4, 5), [2, 2, 2, 2, 2]);

// a b = 2 elements
//
// 2 elements repeating 2 times in the outermost loop
assert_eq!(outermost!(a: 1, 2, 3; b: 4, 5), [2, 2]);
}
```

Contrary to `count`, `len` must be placed inside a repetition.

## Scoping, Exporting, and Importing

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

0 comments on commit 327c2f8

Please sign in to comment.