How a lossless parser records which rules matched where, for
Ichor.Syntax.tree/3 to weave the tokens into.
The records travel inside the ordinary raw captures, under one
reserved name (key/0), rather than beside them. That is what makes
them correct for free: every backend already discards a failed
alternative's captures and merges a name captured more than once into
one ordered list, so a record of a rule that was tried and abandoned
never survives, and the ones that do stay in source order. The key
can never collide with a grammar's own capture name, since $ is not
a name character in any grammar notation Ichor reads.
A rule's own captures hold its callees' records; the call site takes
them out before the captures go anywhere else (called/4), so
Ichor.Actions never sees one, and records the call as one record in
its place.
Summary
Functions
A call of rule that matched the tokens [from, to), whose own
captures were captures: the captures without the records, for the
ordinary {:rule, rule, captures}, and the call's own record, holding
those records as its children.
The reserved capture name records travel under.
Grammar.LRTable.Captures.build/3, recording: the captures for
production, holding the records of the rules on its right-hand
side. It takes what build/3 takes, so a runtime can be handed
either.
The root's record of a parse that matched [0, to) with captures,
and the captures without the records -- for Ichor.Actions.
The records captures hold, in order, and captures without them.
Functions
@spec called( atom(), non_neg_integer(), non_neg_integer(), Ichor.Capture.raw_captures() ) :: {Ichor.Capture.raw_captures(), Ichor.Syntax.record()}
A call of rule that matched the tokens [from, to), whose own
captures were captures: the captures without the records, for the
ordinary {:rule, rule, captures}, and the call's own record, holding
those records as its children.
@spec key() :: atom()
The reserved capture name records travel under.
@spec reduced( Grammar.LRTable.Production.t(), [Grammar.LRTable.Captures.entry()], tuple() ) :: Ichor.Capture.raw_captures()
Grammar.LRTable.Captures.build/3, recording: the captures for
production, holding the records of the rules on its right-hand
side. It takes what build/3 takes, so a runtime can be handed
either.
A grammar rule on the right-hand side is recorded as one record over
the span it covers. A helper nonterminal LR desugaring made up
($star_1, $grp_2, ... -- every name starting with $) is not a
rule the grammar's author wrote, so its records are spliced into this
production's own. A helper's span is taken whatever kind of capture
the production makes of it: a :text capture drops the helper's
captures but keeps its records, as the PEG backends do.
@spec root(atom(), non_neg_integer(), Ichor.Capture.raw_captures()) :: {Ichor.Capture.raw_captures(), Ichor.Syntax.record()}
The root's record of a parse that matched [0, to) with captures,
and the captures without the records -- for Ichor.Actions.
@spec take(Ichor.Capture.raw_captures()) :: {[Ichor.Syntax.record()], Ichor.Capture.raw_captures()}
The records captures hold, in order, and captures without them.
iex> Ichor.Syntax.Record.take([{:n, {:text, "1"}}])
{[], [{:n, {:text, "1"}}]}