Crate hyperlark
Expand description
Hyperlark core — the pure-Rust parsing library.
The shipped surface is the lexer stack (BasicLexer, Scanner,
ContextualLexer), the LALR and Earley engines, the grammar model, the
in-Rust .lark compiler, the serialized-grammar loader/saver, the custom-
lexer and postlex seams, and the Lark facade every binding wraps.
The differential comparison format lives in the hyperlark-conformance
crate — dev tooling, deliberately not part of this shipped library.
Structs§
- Action
- A LALR action cell, packed into a
u32(POC-proven encoding):Action::NONE=u32::MAX; high bit set = reduce (low 31 bits = rule index); high bit clear = shift/goto target state. Arepr(transparent)newtype so a cell can’t be used as a bare integer (indexed with, arithmetic’d) — the packed word is reachable only through the constructors/queries below. - Arena
Idx - Index into
crate::model::ParsedTree::arena(the child-node store) — acrate::model::Tree::children_start. NOT a length:children_lenis a plain count, not an index into any space, so it stays a bareu32/usize. - Basic
Lexer - Lark’s
BasicLexer: scanner + ignore/newline sets + composed callbacks + the name↔id tables (ids cover all conf terminals, including scanner-removed absorbed literals). - Basic
Token Source - A
TokenSourceover the sharedBasicLexer— the analogue of the Lark test classes’BasicLexer(copy(lexer_conf))wrapper (CustomLexerNew), and the building block the conformance custom-lexer port drives. Owns its cursor (LexerState); ignores the parser-state key like Lark’sBasicLexer.next_token. - Compile
Options - Compile-time options (the facade threads Lark kwargs here; defaults match Lark’s).
- Compiled
Grammar - The compiled grammar — same downstream shape as
json_loader::LoadedGrammar. - Contextual
Lexer - Per-parser-state lexer set.
- Cursor
- A borrowed walk cursor over a
ParseResult— the core tree-walk primitive the deferredVisitor/Interpreter/Transformerclasses will build on (Provisional-but-churn-averse). Resolves display names through the result’s table. - Custom
Token - A token produced by a
TokenSource, typed by terminal NAME — exactly the seam Lark exposes (a custom lexer yieldsToken(type: str, value)); the engine resolves the name through the parse table per token. Positions are the caller’s verbatim (the engines never recompute them;$ENDborrows them as-is). - Display
Id - Index into
crate::model::ParseTable::display_names; aTree’sdatalabel (a rule’s origin, or alias when present). Shares no space withRuleId/NontermId— display names are their own dense id space in loader-preserved order. - Earley
Dynamic Matchers - The dynamic-Earley lexer built once at construction (the seam mirroring Lark’s
EarleyRegexpMatcher.__init__): the per-terminal forward matchers + parallel terminal-priority vector. A zero-width / bad regexp raises theGrammarErrorhere (construction), never mid-parse — every subsequent parse borrows this by reference (seesuper::EarleyLexerSource). - Earley
Parse Job - The invariant inputs to an Earley parse — the peer to the LALR engine’s
crate::lalr::ParseJob, keeping the two entry surfaces parallel (this engine’sparse_withdeliberately mirrors LALR’s). Bundles what to parse and how to lex it; the strategy (transform) stays a separate argument. - Earley
Parse Options - Per-parse Earley options. Mirrors
crate::ParseOptions(LALR) plus theambiguityfield the Earley engine alone honors (Lark’sambiguity=). Earley owns its own options type — LALR’s has noambiguityand lives behind the engine boundary. - Embedded
Stdlib - The embedded Lark stdlib: all four
lark/grammars/*.larkfiles (embed all four —%import python.X/%import lark.Xare working Lark features via its package loader). Baked in viainclude_str!; no filesystem needed in native/WASM/C. - Eval
Node - One node under
ParseResult::eval: dispatch onname, fold children on demand witheval. - FnFold
Foldfrom two closures — for when a one-off fold doesn’t warrant a type. Seefold_fn.- Foreign
Error - The opaque payload a
ParseError::CustomLexcarries out of a failed custom-lexer pull (crate::custom::CustomLexError::Custom): an owned, thread-safeAnya binding downcasts back to its host exception with identity intact (in Lark, the custom lexer’s exception escapesparseunwrapped — a storedPyErrround-trips through here).ArckeepsParseErrorClone; equality is payload identity (Arc::ptr_eq), the only equivalence an opaque value supports. - Foreign
Value - The opaque payload carried by
NodeValue::Foreign: an owned, thread-safeAnya binding downcasts back to its host value.ArckeepsNodeValueClone(the SPPF walk clones freely) andSend + Sync(no core public item is!Send/!Sync; an ownedPyObject/JS handle is itselfSend + Sync). ManualDebug—dyn Anyis notDebug. - Grammar
Error - A grammar-compile failure — Lark’s
GrammarErrorclass surface (message-detail parity deferred; the raise/no-raise decision is v1). - Grammar
Error Report - One entry of
find_grammar_errors’s result: the recordedUnexpectedInputplus Lark’s_error_reprstring. The binding mapserrorto ahyperlark.exceptions.UnexpectedInputsubclass and keepsrepras the tuple’s second element. - Handle
- A batching-callback slot handle: a binding-private slot index (into the
hook’s pending-value table) packed with an optional splice-flag high bit. A
repr(transparent)u32newtype so the packed word — likeAction— is reachable only through its accessors, never as a bare integer. - Immutable
Interactive Parser - Lark’s
ImmutableInteractiveParser: the copy-on-feed wrapper. Some methods are overridden in Lark (feed_token,exhaust_lexer,as_mutable) so they return a new immutable and never mutateself; others are inherited unmodified (resume_parse,feed_eof,pretty,accepts,choices,copy) and keep their base semantics — including the quirk thatresume_parseruns the baseparse_from_stateover the shared state and mutates it in place. This surface mirrors Lark faithfully, documenting each quirk with its citation rather than idealizing it away. - Imported
File - One resolved imported file — Lark’s
used_filesKEY: a filesystem/packagejoined_pathplus whether it came from the embedded stdlib (FromPackageLoader), which the binding surfaces as aPackageResourcerather than a plain path string (list_grammar_imports). - Indenter
- Lark’s
Indenter, configured by terminal names. - Indenter
Session - The live indenter state (Lark’s instance fields, indenter.py:29-34):
paren_level, theindent_levelstack, plus the resolved token ids and the last upstream token’s positions (the EOF flush’s borrow source, gated on that token’s value being non-empty). Fresh per session:paren_level0,indent_level[0], no last token. - Interactive
Parser - The mutable interactive parser. Construction:
crate::lalr::parse_interactive. - Lark
- A compiled grammar ready to parse — Lark’s
Larkobject. Holds the sharedParseTable(behind anArc, soParseResultcan carry a cheap clone) plus the concrete lexer the resolved frontend needs.Send + Syncby construction. - Lark
Options - Construction options — Lark’s
Lark(grammar, **options)kwargs as a flatDefault-able struct (no fluent builder in core; a builder is binding sugar). Field-init the ones you need overDefault::default. - Leaf
Cursor - lark
scan_values: live pre-order over leaf children — every non-Tree child value (Token, None placeholder, Foreign), left-to-right, exactly the recursive generator’s order (design P5). Yields the leaf’s arena slot. - Lex
- Iterator over a lex pass. Yields tokens until exhaustion; a lex error ends the stream after being yielded once.
- Lexer
Conf - Everything
BasicLexer::newconsumes — never global state, so one is built per accept-set from a filtered conf.always_acceptis deliberately absent: it is a ContextualLexer-only param whose effect arrives pre-folded into the terminal subset.Clone(callbacks areArc-shared) — a custom-lexer instance retains a copy for the source to build from, Lark’slexer_type(lexer_conf)handoff. - Lexer
Conf Read Error - A structural problem in the serialized grammar JSON.
- Lexer
State - Mutable lex-pass state (Lark’s
LexerStateminus the text, which callers borrow): the tracker +last_token, which Lark updates only on a returned token and error reporting later consumes. - Line
Counter - Line/column/offset tracker (Lark’s
LineCounter), with both cursors:byte_posis where the scanner reads;char_pos(and every derived position) counts code points, exactly as Pythonlen/rindexdo. - Load
Error - A structural problem in the serialized grammar JSON.
- Load
Options - Loader-time options (grammar-shaping kwargs that Lark applies at build time, not parse time).
- Loaded
Grammar - The loaded grammar: the lexer conf + the parse table (which owns the
rules).
startsymbols come fromparser_conf.start. - Maybe
Pos - A
Poswhose fields may individually be unknown. - Maybe
Span - A
start..endpair ofMaybePos. - MetaIdx
- Index into
crate::model::ParsedTree::meta_arena— acrate::model::Tree::meta. Hand-written rather than [define_idx]: it needs aNONEsentinel (mirrorscrate::model::Action::NONE/crate::model::Handle’s packed-word sentinels), so it does not fit the plain-newtype macro shape. - Nonterm
Id - A nonterminal id: index into
crate::model::ParseTable::nonterm_namesand the COLUMN of agoto_actionsrow. A rule’sorigin_idis one (the LHS nonterminal that a reduce gotos on). - OnError
Context - The context handed to a
parse(on_error=)callback: the escaping error plus a mutable recovery handle onto the live parse. - Owned
Interactive Parser - An interactive LALR parser that owns its backing
Larkand input text, so it can be stored in a binding handle and driven token-by-token across calls. Build one withLark’s interactive entry (bindings callOwnedInteractiveParser::new). - Parse
Job - The invariant inputs to a parse — what to parse and how to lex it — bundled
so the six values that otherwise thread identically through every LALR entry
(
parse_with,parse_discard,parse_interactive, and the private [drive_to_accept]) travel as one. It is deliberately orthogonal to the strategy — what each reduction yields (build a tree / discard / transform) — which stays a separate argument (transformonparse_with, thereducemode inside [drive_to_accept]). That strategy axis is the one thing that actually differs between the entries, so it is the one thing left un-bundled. - Parse
Options - Per-parse options.
- Parse
Result - A finished parse — self-describing: the owned tree plus the
table its
data_ids index, so aCursorresolves display names from the result alone. Both fields areArc(O(1) clone,Send, C-expressible). - Parse
Table - The LALR parse table.
token_actionsis a locked cross-phase contract: per-state rows, dense state ids, carrying both shift and reduce-lookahead terminals — accept-sets andaccepts()read through it; a shift-only row would silently shrink them.goto_actionsstays per-state addressable too:choices()re-merges both maps. Loader-preserved order is canon — state ids, rule order,__ANONids pass through exactly as serialized, never renumbered. - Parsed
Tree - A finished parse: root + arenas (POC-proven layout — children in one
growing buffer, tokens in another;
meta_arenaparallel, empty in off-mode). - Pattern
- A terminal’s pattern: raw
value+ per-terminalflags(chars:i m s x l u, as Lark serializes its frozenset) + the optional raw grammar-source form. - Pending
Meta - A
Meta::Pendingpayload: the endpoint plans plus — for a?rulecollapse onto an already-stamped child — the child’s prior meta (base), whose plain fields survive and whose container fields are the fallback when an endpoint resolves to no contribution (lark’s per-endpointhasattrupdate). - Pending
Span - The raw PRE-FILTER span plan of one transform-mode reduce — what lark’s
PropagatePositions(wrapping outside the child filter) reads: filtered-out anonymous tokens included,_-splice children read through their carried plans. - Pos
- One point in the input.
- Postlex
Error - A postlex configuration/stream error (unknown terminal name, or the
indenter’s dedent-mismatch — Lark’s
DedentError). - Postlex
Source - What a
crate::postlex::PostlexStreampulls upstream tokens from — one token per call, against the LIVE parser state. - Postorder
Cursor - Post-order DFS, children left-to-right — the
Visitor_Recursive.visitand post-parseTransformer.transformorder (design P6). Live: frames descend the arena as they go. - Reduce
Cx - The arena/token context handed to a
ReduceCallback::reducecall. Both engines build one over their live child arena + token arena, so the hook resolvesNodeValue::Token(idx)leaves and constructs arena-backedNodeValue::Trees (the representation a_-splice parent can drain) without engine-specific glue. - Route
Set - Which reductions still call the user’s
ReduceCallbackvs. build natively — a transform pays the callback (host-boundary) cost only for rules the user customized; the rest build as a plain parse. Keyed by display (a rule’s callback identity — aliased alternatives get their own; design A1), not rule id. Ahookeddisplay fires the callback; the rest take the nativeBuildTree/splice arms inapply_shape— no hook call (children still build, into the arena or aSplice). Built once byRouteSet::build; fetched per parse viaReduceCallback::routes(None= all hooked, today’s behavior). - Rule
- A grammar rule. Lark’s
RuleOptionsfields are flattened onto the rule (keep flattened): a rule with multiple BNF expansions would otherwise SHARE one options object, which is exactly the aliasing that makes Lark’spriority='invert'negate a multi-expansion rule once per expansion (a net no-op — see thetp_prio_plus_invertnote in the conformance corpus). Owning the fields by value makes that class of bug unrepresentable. - RuleId
- Index into
crate::model::ParseTable::rules— the reduce target a reduce action decodes to (crate::model::Action::rule_id). NOT a nonterminal id (that isorigin_id, a distinct space). - Rule
Shape - Per-rule reduce-time instructions — one shared
build_rule_shapeis the tree-shaping contract both engines reuse (the POC’s duplicate copy is a named drop). Mirrors Lark’smaybe_create_child_filter - Save
Error - A save-emit failure (mirrors Lark’s
save()NotImplementedErrorfor the non-LALR guard — a table only exists for LALR). - Save
Options - The scalar, compile-invariant options that populate
data.options. The callable/hook options (transformer,postlex,lexer_callbacks,edit_terminals) are deliberately absent — they are re-supplied at load (Lark’s_LOAD_ALLOWED_OPTIONS) and cannot cross a portable JSON boundary; the Python binding layers them into a pickle envelope wrapping this JSON. - Saved
Options - The compile-invariant half of a save’s
data.optionsblock — the options the loader/compiler BAKED into the serialized rules and table, so a faithful load must restore them from the file rather than re-accept them from the caller (Lark’s_LOAD_ALLOWED_OPTIONScomplement, lark.py:248 / :570-575). - Scan
Cache - Per-lex-session scratch for the Lazy fast engine’s lazy DFA, threaded through
LexerStateso the hot scan reuses a cache instead of acquiring a pooled one per token (the pool exists for&selfthread-safe access; a&mut-threaded cache is cheaper on the single-threaded lex path — worth ~3-4% of parse). Keyed by DFA identity because a contextual lexer’s per-state lexers each have their own DFA and a lazyCacheis valid only for the DFA that built it. A no-op for the Meta/Dense engines. Empty until the first Lazy scan; a fresh one costs nothing. - Scan
Match - A match starting exactly at the scan position.
- Scan
Spec - One ranked, compiled-ready terminal: its id + final
to_regexp()output. - Scanner
- The compiled two-partition scanner.
- Sentinel
Option Option<T>niche-packed intoT::NONE.#[repr(transparent)]so it is layout-identical toT(4 B foru32, vs 8 B forOption<u32>). The point is the size, not a compiler niche — all theNone/Somelogic is explicit.- Shape
Error - An
empty_indices/expansion mismatch or other shape-construction problem. - Span
- A
start..endpair ofPos— the unit that actually travels. - StateId
- An LALR state id: the ROW index of a
token_actions/goto_actionstable, and the value carried bycrate::model::ParseTable::start_states/end_statesand the engine’sstate_stack. Bounded dense state numbering from the compiler/loader (canonical BFS renumbering). - Subtrees
Cursor - lark
iter_subtreesorder: process a growing queue (append each processed node’s Tree children reversed), then yield the whole thing reversed. The snapshot is taken eagerly at construction, matching lark’s eager queue walk — later host mutations never change the yielded position sequence. - Terminal
Def - A terminal definition: name + pattern + lexer priority. Priorities may be negative; default 0.
- Token
- A lexed token — Lark’s 8 slots. Positions are code
points: 0-based
*_pos, 1-basedline/column.SentinelOption<u32>(4 B each;u32::MAX= position-less) rather thanOption<u32>(8 B) — a token can be constructed position-less (callbacks,$END), though the lexer always sets all six.BasicLexer::next_tokenguards inputs via [input_exceeds_u32] (LexError::InputTooLarge), which reservesu32::MAX, so every position here is provably< u32::MAXand round-trips through the sentinel losslessly. - Token
Arena Idx - Index into
crate::model::ParsedTree::token_arena— acrate::model::NodeValue::Token’s payload. - TokenId
- A terminal id: index into
crate::model::ParseTable::id_to_token, the COLUMN of atoken_actionsrow, and acrate::lexer::Token’stype_id. One id space shared by the lexer and the table (conf order;$ENDlast). - Topdown
Cursor - lark
iter_subtrees_topdown: live pre-order, left-to-right, no dedup. lark yields a node before reading its children (design P4), so the last-yielded node’s children are pushed lazily at the nextnext()call — the host’s yield window can prune the descent (skip_children) or hand off (stack) without the stale children already being on the stack. - Tree
- A tree node: children live in the shared arena (slice descriptor), meta —
when
propagate_positionsis on — in the parallel meta arena. - Width
Error - A pattern that neither width route can analyze. The lexer build treats this as an uncompilable terminal (validation).
Enums§
- Ambiguity
Lark(..., ambiguity=…). v1 =Resolve(default: pick one derivation by summed priority) |Explicit(wrap every derivation in_ambig/_iambig/_interTrees).'forest'(raw SPPF) is deferred from v1;#[non_exhaustive]letsForestland additively later.- Choice
Action - A merged
choices()entry — Lark returns the rawstates[position]map whose keys include NON-TERMINAL goto names: hyperlark re-merges the splittoken_actions/goto_actionsrows to reproduce it. - Custom
LexError - A
TokenSourcefailure. - Earley
Lexer Source - Which scan strategy drives an Earley parse (Lark’s resolved lexer; the
frontends facade’s
ResolvedLexer). Earley defines its own source enum rather than importing LALR’sLexerSource(engine boundary): the basic path takes a pre-lexingBasicLexertoken stream; the dynamic paths take the construction-built matchers. - Fold
Leaf - A leaf handed to
Fold::leaf: a lexed token, or theNoneplaceholder a[...]optional inserts undermaybe_placeholders(Lark’sNonechild). - Frontend
Error - A construction-time frontend/engine-configuration failure. Lives in the shared
model (beside
ParseError) so both thecrate::frontendsfacade and thecrate::earleyengine can raise it without the engine reaching across the engine boundary into the facade layer (mirrors the sanctionedAMBIG_DATA_IDretrofit).crate::frontendsre-exports it for the public surface. Carries Lark’s exception class so the conformance runner can assert both the class and the verbatim message: the postlex×dynamic and parser×lexer rejects raiseConfigurationError(FrontendError::Configuration); the dynamic×lexer_callbacksreject and the dynamic Earley zero-width/bad-regexp matcher-build reject raiseGrammarError(FrontendError::Grammar). - Lark
Build Error - A construction failure — Lark’s build-time exception classes, carrying the class name + verbatim message so a binding reproduces the Python exception. Provisional richness.
- LexError
- Runtime lex errors. Construction-time problems are
LexerBuildError. - Lexer
Build Error - Construction-time errors, mirroring Lark’s validation,
all gated by
skip_validation. - Lexer
Source - Which lexer drives a parse (contextual becomes the real LALR default;
the basic path stays for bootstrap/diagnostics).
Copy— it holds only immutable references, so a caller can drive the same source through more than one entry (e.g. the tree vs. no-tree parity A/B). - Lexer
Spec - The user-facing lexer choice: Lark’s
lexer=strings, plusCustomfor a caller-supplied lexer. Like Lark, a custom lexer is exempt from the parser×lexer matrix entirely — every parser accepts one. - Meta
- Node position info under
propagate_positions. Not a genericTree<M>. - Node
Kind - What a
Cursorpoints at. Provisional: the deferred visitor classes build on this, so#[non_exhaustive]. - Node
Value - A parse-tree node value.
Spliceis a reduce-time intermediate produced and drained inside the LALR engine (LALR-private). The engine never returns one —finish_parsere-wraps a rootSpliceand adebug_assertguards it (lalr/state.rs). The variant ispubonly because this enum is shared cross-engine; a hand-builtParsedTreecontaining aSpliceis unsupported input that violates the tree walkers’ expectations (they may panic). - Parse
Error - Structured parse failure, mirroring Lark’s
UnexpectedInputsubclasses.$ENDrejection raisesUnexpectedTokenwith the$ENDtoken exactly as Lark’s LALR does —UnexpectedEOFis Earley-only and the POC’s rewrite is a named parity bug we do not replicate. - Parser
Kind - The engine the grammar is configured for. hyperlark implements Lark’s
lalrandearley; Lark’scykparser is not supported. - Pattern
Kind - Which kind of pattern a terminal carries (Lark’s two
Patternsubclasses). - Priority
Literal - Lark’s
priority=literal ('auto' | 'normal' | 'invert') — RustNone(absence) maps to Lark’spriority=None. OnlySelf::Invertand RustNonetrigger the pre-serialize negate/strip;Auto/Normalare the Earley-runtime knob, no-ops at compile time. - Priority
Mode - Lark’s
priority=kwarg values. - Resolved
Lexer - What
auto(or an explicit spec) resolves to.Dynamic/DynamicCompleteare Earley-only; the LALR engine never sees them (the matrix rejects the pairing first). - Scanner
Build Error - Scanner
Engine - Which concrete
regex-automataengine drives the fast (look-around-free) partition. Provisional knob (seeLarkOptions::scanner_engine); the merge semantics (MatchKind::LeftmostFirst, anchored-at-posspan search) and the resulting token stream are byte-identical across all three — only the build-time / warm-scan / resident-memory trade differs. - Span
Plan - One endpoint of a transform-mode raw-children span scan.
- Span
Source - One candidate in a pending endpoint walk, in lark’s
_pp_get_metaorder. - Splice
Span Payload - The boxed
SpliceSpanpayload: the_-rule’s span, the poison marker (seeMeta::Poisoned), or — under a batching transform — the deferred endpoint plans a scanning parent inlines (lark reads the splicedTree(_rule)’s meta, whose value may depend on callback results). - Symbol
- A grammar symbol in a rule expansion (
Terminal/NonTerminal).
Constants§
- AMBIG_
DATA_ ID - Ambiguity tree-
datasentinels. Earleyambiguity='explicit'wraps derivations in_ambig,_iambig/_interTrees whosedatanames are not rule origins, so they have no slot inParseTable::display_names. They intern as reserveddata_ids at the top of theu32-backedDisplayIdrange — the POC used one such sentinel (AMBIG_DATA_ID = usize::MAX, parser.rs:154); this transposes the idea intoDisplayIdspace for all three. Resolve names viaParseTable::data_name, never by indexingdisplay_names. (Sentinels sit far above any realdisplay_id; a grammar withu32::MAX - 2distinct tree names is not representable regardless.) - CURSOR_
ROOT - The position addressing the root node (
ParsedTree::root, which lives outside the arena). Unreachable as a real slot:Tree::children_startisu32, so an arena never has a valid slot atu32::MAX. - IAMBIG_
DATA_ ID - INTER_
DATA_ ID - SAVE_
FORMAT_ VERSION - The canonical-JSON save format version. Bump the (single) integer on any
wire-incompatible change to the envelope the loader consumes. The loader
reads a top-level
hyperlark_format_versionand rejects a value greater than this (a newer save cannot be trusted to parse under an older build); an absent header is accepted — the corpus fixtures and the loader’s own input format predate the header and are, by construction, the current pin’s shape. - WIDTH_
UNBOUNDED - Unbounded-width sentinel: strictly greater than every finite code-point
width. Only the ordering matters — do NOT mirror CPython’s
MAXREPEATor its2**64(width sentinel).
Traits§
- Fold
- A bottom-up fold algebra: what a leaf is worth, and how a rule combines its
children’s values. The Rust sibling of Lark’s
Transformer— both methods dispatch on a display name, with the same shape:ruleis called with the rule’s DISPLAY name (aliases included) and the already-folded children in order;leafwith the token TYPE name ("NUMBER"— or""for the hole a[...]optional inserts undermaybe_placeholders). - Fold
Sink - A monomorphic, UNBOXED reduce-time fold — the Rust-native peer to
ReduceCallbackthat does NOT route values through the type-erasedNodeValue::Foreign(Arc) box the language bindings need. The concrete sink owns its own typed value stack (e.g.Vec<f64>), so a pure-Rust fold pays no per-reduction heap allocation and no per-reductionVec<NodeValue>children shaping:Self::shiftconverts a token leaf to a value and pushes it;Self::reducekeeps the shaped children of one rule (shape.to_include,?-collapse viaexpand1_inline), folds them, and replaces them with the single result. It drives the identical token stream as the tree build (the reduce value never steers the parser), so it accepts/rejects identically — only the value representation differs. Driven bycrate::Lark::parse_fold; the concrete sink exposes its own accessor for the accepted root value. - Grammar
Loader - One
%importsource. Returns(joined_path, text)— the joined path threads back as the base for nested imports — orNone= try the next source (mirrorsexcept IOError: continue). - Interactive
Handle - The recovery handle the on-error callback drives — a lifetime-erased view over
the live
InteractiveParser(so the callback type carries no engine lifetime). The callback feeds corrective tokens through this, then returnstrueto have the façade retryresume_parse. Provisional surface. - Postlex
- A postlex configuration — the factory for per-parse sessions plus the
always_acceptnames the contextual lexer unions into every state and the compiler threads into terminal pruning. - Postlex
Stream - One live postlex stream — THE contract, native or bindings.
- Reduce
Callback - The public reduce-time transform hook — Lark’s embedded
transformer=(create_callback), the one core seam every binding (PythonTransformer, WASM JS fn, C fn-ptr) boxes asBox<dyn ReduceCallback + Send>. Object-safe (no generics, noSelfreturn) so it isdyn-compatible;Sendat the box site (an ownedPyObject/JS handle isSend). One trait serves both engines: LALR drives it fromapply_shape, Earley from the per-rule chain’s node builder. - Sentinel
- A type that reserves one value of its domain to mean
None, soSentinelOption<Self>needs no discriminant word and stayssize_of::<Self>(). - Token
Source - A caller-implemented token source — the Rust custom lexer (Lark’s
LexerABC with__future_interface__ = 2, pulled per token instead of a Python generator).
Functions§
- build_
rule_ shape - Build a
RuleShape. The single shared implementation — the bridge must call this, never re-derive. - compile_
grammar - Compile
.larksource.from_lark_source-style construction also routes through here. - earley_
build_ dynamic_ matchers - Eagerly build the dynamic-Earley matchers at construction, surfacing a
zero-width / bad-regexp
FrontendError::Grammar(Lark’s construction-timeGrammarError) before any parse runs.g_regex_flagsis the grammar-wide bitmask (0 for the common no-g_regex_flagsgrammar). - earley_
parse - Parse
textfromstartwith the basic lexer — thin delegate toparse_with, mirroring the LALR engine’sparse. (Convenience for the basic path; the dynamic paths go throughparse_withdirectly.) - earley_
parse_ with - The Earley parse entry — signature mirrors the LALR engine’s
parse_with(including thepostlexparam) plus Earley’sParseOptionswith itsambiguityfield. Flow (Algorithm Sketch): build recognizer tables from the shared model → run the [recognizer] over the [scan] strategy chosen bylexer(postlex, when present, wraps the basic token stream via the connector) → gate + run the [prioritizer] sum-cascade → [extract] byambiguitythrough the per-rule [chains]. A no-parse maps to the per-lexer error class (basic:UnexpectedToken/UnexpectedEOF; dynamic:UnexpectedCharacters). Dynamic matcher construction (with its zero-width/bad-regexpGrammarError) happens earlier, inbuild_dynamic_matchers; this entry only borrows the built matchers. - find_
grammar_ errors find_grammar_errors(text, start='start'). Parsessource(Lark appends a trailing newline) error-tolerantly over the meta-grammar, collecting(UnexpectedInput, repr)pairs, then keeps the first error per line in ascending-line order.- fold_fn
- Build a
Foldfrom two closures — two matches with one shape:on_leaf(token_type_name, leaf) -> Tandon_rule(rule_name, children) -> T. (A placeholder hole reacheson_leafwith the name"".) - list_
grammar_ imports list_grammar_imports(grammar, import_paths)— see [super::builder::list_grammar_imports].- load_
grammar - Load a full serialized grammar document.
- node_at
- Resolve a cursor position to its node (
CURSOR_ROOT= the root). - optimize_
substitutions - The enabled
(original, substitute)pairs, for the conformance span-fixture and flag-on corpus cross-checks (a compact, stable read of the table without exposing the internal struct). - parse
- Parse
textfromstartusing the basic lexer. Thin delegate toparse_withoverLexerSource::Basicso the shift/reduce loop lives in one place (the basic path stays for bootstrap/diagnostics —Lark.lexis basic — while the contextual lexer is now the real LALR default). - parse_
discard - No-tree parse: drive the same lexer/postlex/feed loop as
parse_withbut under [Reduce::Discard], so every reduce yieldsNodeValue::Nonewith no shaping and no arena write. ReturnsOk(())on accept, the sameParseErroron reject. Because the reduce value never steersfeed_token, this consumes the byte-identical token stream and reaches the identical accept/reject as the tree path — only tree construction is elided. The peer to the POC’sparse_no_arenafor no-tree benches (and a discard-parse API candidate: recognition-only validation without paying for a tree). - parse_
fold - Monomorphic reduce-time fold: drive the same lexer/feed loop as
parse_withbut under [Reduce::Fold], forwarding every shift/reduce tosink(which owns its own typed value stack). No tree and noForeign(Arc)box is built — the Rust-native peer to atransformer=hook, without the per-reduction heap box. ReturnsOk(())on accept (the root value is left on the sink); the sameParseErroron reject. - parse_
interactive - Begin an interactive parse (Lark’s
parse_interactive): returns immediately with aninteractive::InteractiveParserholding the start state and a lazy lexer cursor overtext; nothing is lexed until the caller drives it (exhaust_lexer/resume_parse/manual feeds). The returned parser borrowslexerandtextfor'p.postlex, when present, is started once (its session interposes between lexer and parser, buffering the fan-out); a non-clonable session is dropped oncopy(). - parse_
with - Parse with an explicit lexer source (04: the single shared shift/reduce loop).
- re_
escape - Byte-exact equivalent of CPython’s
re.escape(as behaving on the pinned interpreter): escapes exactly the special-character set Python does — including space,#,&,-,~(escaping) — and passes everything else (incl. all non-ASCII) through untouched. Verify the exact set against the source-of-truth venv, not from memory. - read_
lexer_ conf - Read
data.parser.lexer_confout of a Larktools.serializeJSON document into aLexerConf(callbacks empty,skip_validationfalse). - read_
saved_ options - Read the compile-invariant options a save recorded in
data.options. A separate pass over the document, like [read_lexer_conf] —from_json_strneedsmaybe_placeholdersBEFOREload_grammarruns, because it shapes the rules the loader builds. - regexp_
matches_ newline - True iff
regexp(a finalcrate::Pattern::to_regexpoutput) can match text containing a newline (U+000A) — the exactnewline_typespredicate. - regexp_
width - The static (min, max) match width of
regexpin code points. - resolve_
lexer - Resolve
lexer='auto'and validate theparser×lexerpairing, in Lark’s evaluation order: - resolve_
pending_ metas - Resolve every
Meta::Pendinginmetasin dependency order (MetaRefedges form a forest — each meta is referenced by at most one parent walk; explicit stack, so deep?rulechains cannot recurse out).classifyreads a drained slot’s host value (peek, never consume) and MUST be callable for every slot a plan references — the binding calls this only when drains guarantee those slots are filled (a crossing’s consuming drain; parse finish). Unresolvable plans (opaque residuals) demote toMeta::Poisoned, which surfaces as Empty. - to_
canonical_ json - Serialize a compiled grammar to the canonical envelope, as a pretty-printed (indent-2, key-sorted) string with a trailing newline — matching the corpus fixtures’ shape and byte-stable across repeated saves.
- validate_
dynamic_ lexer_ callbacks - The
dynamic/dynamic_completelexer forbidslexer_callbacks— Lark raisesGrammarError("Earley's dynamic lexer doesn't support lexer_callbacks.")at Earley-parser construction, afterresolve_lexer. Callers pass the resolved lexer; the check is a no-op for any non-dynamic lexer.
Type Aliases§
- Edit
Terminals - The
edit_terminalshook: run over every compiledTerminalDefbetween compile and the priority transform. Boxed+ SendsoLarkOptionsstaysSend. - Lark
Instance - The pre-rename spelling of
Lark, kept so existing embedders keep compiling. Every sibling binding (Python, JS, C) already calls its entry typeLark; the core converged on the same noun. - OnError
- The
on_errorcallback type:return trueto resume,falseto re-raise. - Slot
Contribution - What one host (slot) value contributes to one endpoint of a pending span:
None= lark’s_pp_get_metaskips it (not a Token/Tree/__lark_meta__, or a Tree with empty meta);Some= the container-preferred point, whose individual fields may still be unknown. - Splice
Span - The reduce-time container span a
NodeValue::Splicecarries up to the parent that drains it: a(start, end)pair of(line, column, pos)triples. Lark keeps the spliced_-rule as aTree(_rule)and its parent reads that Tree’scontainer_*meta; our flatSplicecarries the same span here instead.Nonewhen the_-rule has no positioned raw child (an Empty_rulemeta — the parent skips it). Meaningful only mid-reduce: it isNonein propagate_positions=off (zero-cost) and irrelevant once aSpliceis drained. - Token
Callback - A per-terminal token callback (
lexer_callbacks).Arcso one callback can be held by more than one composed chain, unlike the POC.