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.
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).
Summary
Types
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).
Functions
Builds the raw-captures list for production, given its popped RHS entries in order.
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.
Types
@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).
Functions
@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.
@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.