# Repetitions & Backtracking

## Syntax

A repetition is `$( … )` followed by an optional separator and a required count suffix:

```
$( … )*        zero or more, no separator
$( … )+        one or more, no separator
$( … )?        zero or one
$( … ),*       zero or more, comma-separated
$( … ),+       one or more, comma-separated
$( … );+       one or more, semicolon-separated
```

**Only `,` and `;` are valid separators.** Anything else is a parse error.

The same syntax works on both sides of the arrow. On the pattern side it captures a sequence; on the
body side it replays one:

```typescript
const $vec = macroRules`
  ($($x:Expr),*) => {
    const __v = [];
    $( __v.push($x); )*
    __v
  }
`;
```

## Body-side separators use a lookahead

On the body side, a `,` or `;` immediately after `$( … )` is only treated as a separator when a `*`,
`+`, or `?` follows it. Otherwise it stays literal text. That is what makes this work as written:

```typescript
$($rest),+     // comma is the separator
$( stmt; )+    // semicolon is inside the group, emitted each time
```

## Length inference

The number of iterations is inferred from the sequence-bound metavariables mentioned _inside_ the
group:

- Mention **none** → `UnanchoredRepetition` error. The expander has no way to know how many times to
  repeat.
- Mention **two of different lengths** → `InconsistentSequenceLength` error.

```typescript
// Error: nothing inside the repetition is sequence-bound.
($($x:Expr),*) => { $( console.log("hi"); )* }
```

Nested repetitions are supported in bodies, and each level infers its own length from the variables
it mentions.

## Bounded backtracking (value position)

Repetitions are greedy, but the matcher will backtrack to let a trailing metavariable match. This
pattern works:

```typescript
const $last = macroRules`
  ($($init:Expr),* $last:Expr) => $last
`;

$last(1, 2, 3); // → 3
```

The matcher first lets `$($init),*` consume everything, fails to bind `$last`, then gives back one
element and succeeds. Backtracking is bounded — it gives back one element at a time rather than
exploring every split — so it's predictable and cheap.

## No backtracking in type position

This is the important limitation: **type-position matching is greedy with no retry.** The identical
pattern in a `kind: "type"` macro cannot match, because the repetition consumes every type argument
and never gives one back:

```typescript
// Works in value position, never matches in type position:
($($t:Type),* $last:Type) => …
```

Restructure type-position patterns so the variable-length group is last, or match a fixed arity per
arm.
