The compile-time codegen backend for @engine lr grammars: builds the
SLR(1) table via Grammar.LRTable once, at Elixir-compile-time (inside
use Ichor's macro expansion), and requires it be conflict-free --
same requirement Grammar.LR.compile/1 enforces at runtime, just
checked once here instead of on every call.
Since LR has no forking at all, everything compiles away into direct
calls: one generated function per automaton state
(lr_state_<N>/4), each a compiled case on the current lookahead
terminal (Grammar.LRTable.current_terminal/3) deciding shift, reduce,
or accept -- mirroring how Grammar.Native.RuleCompiler gives every
PEG IR node its own function calling other nodes' functions directly,
never an interpreted instruction loop.
A shift's target state is always compile-time-known (fixed by the
current state + the matched terminal), so it's a direct call to
that state's own function -- zero lookup, the actual hot path (this
runs once per input token). A reduce's GOTO target is not
compile-time-known -- it depends on whichever state popping the
production's RHS happens to expose, which varies with the parse's own
history -- so every reduce goes through lr_dispatch/5, one compiled
clause per state (a jump table, not a Map.get), and lr_goto/2, one
compiled clause per {nonterminal, state} pair. Capture-building
itself stays the shared, unchanged Grammar.LRTable.Captures.build/3
(via Grammar.LR.Stack.reduce/4, Macro.escape'd one production per
reduce clause) -- a small, data-driven fold over a handful of RHS
positions, not the hot per-token loop this module actually compiles
away.
Summary
Functions
Generates the full quoted body (lexer + compiled LR parser + parse/1,2 + run/1,2) for grammar, dispatching to actions_module.
Functions
@spec generate(Aether.Grammar.t(), module()) :: Macro.t()
Generates the full quoted body (lexer + compiled LR parser + parse/1,2 + run/1,2) for grammar, dispatching to actions_module.