Trait ReduceCallback
pub trait ReduceCallback {
// Required method
fn reduce(
&mut self,
cx: &mut ReduceCx<'_>,
rule_display_id: DisplayId,
children: Vec<NodeValue>,
) -> NodeValue;
// Provided methods
fn reduce_expand1(
&mut self,
cx: &mut ReduceCx<'_>,
rule_display_id: DisplayId,
children: Vec<NodeValue>,
) -> NodeValue { ... }
fn failed(&self) -> bool { ... }
fn routes(&self) -> Option<Arc<RouteSet>> { ... }
fn token_shifted(&mut self, _token: &Token) -> Option<Handle> { ... }
fn stamp_passthrough(&mut self, _slot: u32, _plan: PendingSpan) { ... }
fn flush(&mut self) { ... }
}Expand description
The public reduce-time transform hook — Lark’s embedded transformer=
(create_callback), the one core seam every
binding (Python Transformer, WASM JS fn, C fn-ptr) boxes as
Box<dyn ReduceCallback + Send>. Object-safe (no generics, no Self return)
so it is dyn-compatible; Send at the box site (an owned PyObject/JS
handle is Send). One trait serves both engines: LALR
drives it from apply_shape, Earley from the per-rule chain’s node builder.
Fires once per reduction, for every rule — named, _-splice, and the
__/___*_star/_plus synthetics alike (a _addop
splice rule’s method fires; the ___addop_star_0 synthetic fires
__default__). Lark has no “default vs. override” split at this seam: the
binding’s impl does that dispatch (named method, else __default__, else the
tree builder) and returns one value. The return becomes the reduced node’s
value on the parse stack; a _-splice parent then drains it (see the
Foreign doc + ReduceCx::build_tree).
ExpandSingleChild (?rule, no alias) short-circuits a lone shaped child
before the hook (Lark wraps the method innermost) — the hook is not
called for a collapsed single child. v_args-style call adaptation is
binding-specific and lives above this seam.
Required Methods§
fn reduce(
&mut self,
cx: &mut ReduceCx<'_>,
rule_display_id: DisplayId,
children: Vec<NodeValue>,
) -> NodeValue
fn reduce( &mut self, cx: &mut ReduceCx<'_>, rule_display_id: DisplayId, children: Vec<NodeValue>, ) -> NodeValue
Transform one reduction. cx resolves token leaves and builds arena-backed
trees (the drainable default a _-splice parent expects); rule_display_id
indexes ParseTable::display_names (Lark’s user_callback_name);
children are the shaped children (filter_out dropped, placeholders
inserted, _-child splices already drained).
Provided Methods§
fn reduce_expand1(
&mut self,
cx: &mut ReduceCx<'_>,
rule_display_id: DisplayId,
children: Vec<NodeValue>,
) -> NodeValue
fn reduce_expand1( &mut self, cx: &mut ReduceCx<'_>, rule_display_id: DisplayId, children: Vec<NodeValue>, ) -> NodeValue
Self::reduce for an ExpandSingleChild (?rule) reduction whose
final child count the engine cannot determine: a batching hook deferred
a _-splice child (a NodeValue::Handle with
Handle::with_splice_flag), so Lark’s collapse-if-single-child decision
(ExpandSingleChild wraps the callback innermost) must happen at the
hook’s drain, after the splice expansion: exactly one final child →
that child is the value, callback skipped; otherwise the callback
fires. Only a batching hook can receive this call — a synchronous hook
never returns handles, so the engine resolves the count itself and
calls plain Self::reduce. The default delegates to reduce.
fn failed(&self) -> bool
fn failed(&self) -> bool
true once a host-side callback failure has been latched: the engine
polls this after every Self::reduce and aborts the parse with
crate::ParseError::Callback (the binding re-raises its latched
exception in place of that error), bounding run-ahead past a failing
callback — a batching hook discovers failures only at a drain, so
without this poll the engine would consume the rest of the input.
Default false: a hook without an internal error latch never aborts.
fn routes(&self) -> Option<Arc<RouteSet>>
fn routes(&self) -> Option<Arc<RouteSet>>
Per-display reduce routing for a hybrid transform parse:
None (the default) = every reduce
fires the hook, today’s behavior for every existing hook; Some routes
per RouteSet (un-hooked displays build natively, no hook call).
Fetched once at parse start. LALR-only — Earley extraction ignores routes
(an embedded transformer is LALR-only in every binding).
fn token_shifted(&mut self, _token: &Token) -> Option<Handle>
fn token_shifted(&mut self, _token: &Token) -> Option<Handle>
The shift anchor — lark’s callbacks[token.type](token) on the SHIFT
branch (lalr_parser_state.py:88), the terminal-callback half of the one
callback dict lark keys by both rule and token type. The engine calls this
from [ParserState::shift_token] for every shifted token whose terminal
the fetched RouteSet::hooked_tokens marks — once per shifted token, in
stream order, before the token can be consumed, filtered, or collapsed —
so it reaches the three shapes reduce-time application cannot (a ?rule
that collapses to its single child, a collapsing root, a filter_out
token that is shifted then dropped).
Returns the value’s stand-in on the parse stack:
Some(handle)— a batching hook enqueues the callback (materialize the token, call the user fn, store the result inhandle’s slot at the drain) and returns a plainHandle(neverHandle::with_splice_flag; the collapse logic distinguishes token Handles from deferred_-splice children by that flag). The engine pushesNodeValue::Handlein place of the rawNodeValue::Token; the result flows through every existing Handle arm (native build/splice,?rulecollapse, spanSlotsource, thefinish_okHandle→Foreignflush).None— apply nothing; the engine pushes the raw token. The default. A hook that has no slot table cannot express an inline transformed value through this interface, so synchronous token callbacks are unsupported here; the only implementor is the batching binding, andhooked_tokensis all-false for hooks (WASM/C) that don’t provide one.
After it, the engine polls Self::failed (mirroring the reduce poll) so
a raised callback aborts within batch_size shifts.
fn stamp_passthrough(&mut self, _slot: u32, _plan: PendingSpan)
fn stamp_passthrough(&mut self, _slot: u32, _plan: PendingSpan)
A native ?rule collapse (transform + propagate_positions) returned a
bare batching Handle: lark’s PropagatePositions still wraps that
reduce, so if the slot’s eventual value is a Tree its meta must be
updated from this reduce’s raw span (plan) — plain fields kept,
container widened. A batching hook queues a stamp-only step (ordered
after the slot’s own drain); the default no-op serves synchronous
hooks, whose values the engine already stamped via the returned plan.
fn flush(&mut self)
fn flush(&mut self)
Control is about to leave the engine for caller code mid-parse (the
on_error callback): a batching hook runs its queued work now, so that
code observes every reduction before it. The engine polls
Self::failed right after and does not call out if it is set. Default
no-op: a synchronous hook has nothing queued.
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".