# `Grammar.LRTable.Captures`
[🔗](https://github.com/joetjen/ichor_runtime/blob/v0.6.0/lib/grammar/lr_table/captures.ex#L1)

Builds the raw-capture list `Ichor.Actions` expects (`{:token,...}`/
`{:rule,...}`/`{:text,...}`, see `Ichor.Capture.raw_captures/0`) from
one reduced production's own RHS entries -- shared by `Grammar.LR` (a
single linear stack) and `Grammar.GLR` (a graph-structured one): both
reduce the same kind of production, against the same kind of token
stream, needing the exact same per-position classification (see
`Grammar.LRTable.Production`'s own moduledoc for what each capture
kind means), so this logic has exactly one place to live rather than
two copies drifting apart.

Every value it builds records the token range it matched
(`Ichor.Capture.at/3`), for `Ichor.Actions` to turn into its span. A
helper's captures, spliced in, were recorded when the helper was
reduced and keep their ranges.

Built as a list, in `production.captures`' own RHS-position order,
rather than a map -- a name's position (first occurrence, for one
captured more than once) is real information `Ichor.Actions` relies
on (`Ichor.Capture.position/0`) to evaluate sibling captures in true
source order, which a plain map's own iteration order can't be
trusted to reconstruct (it isn't guaranteed to match insertion order,
and has been observed to differ across Erlang/OTP versions).

# `entry`

```elixir
@type entry() ::
  {value :: Grammar.VM.Token.t() | Ichor.Capture.raw_captures(),
   start_pos :: non_neg_integer(), end_pos :: non_neg_integer()}
```

One popped RHS position: `value` is the shifted `%Token{}` (a
terminal position) or the already-built raw-captures list of a
reduced nonterminal; `start_pos`/`end_pos` are the token-stream span
it covers, needed for a `:text`-kind capture regardless of which RHS
position it sits at (not just the whole production's own span).

# `build`

```elixir
@spec build(Grammar.LRTable.Production.t(), [entry()], tuple()) ::
  Ichor.Capture.raw_captures()
```

Builds the raw-captures list for `production`, given its popped RHS `entries` in order.

# `merge`

```elixir
@spec merge(Ichor.Capture.raw_captures(), atom(), term()) ::
  Ichor.Capture.raw_captures()
```

The first/second/third+ list-promotion rule every raw-capture builder
in Ichor uses (`Grammar.Native.Runtime.Parser.merge_captures/2`,
mirrored here for the LR/GLR side): a name captured more than once
naturally becomes a list without knowing the eventual count in
advance. A new name is appended at the end, landing at its
first-occurrence RHS position relative to whatever's already there
(`build/3` calls this in RHS order); a repeat name updates its
existing entry in place, not moving it.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
