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) { ... }
}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.
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".