@lezer/lr
Runtime incremental GLR parser engine that powers Lezer grammars and CodeMirror's syntax trees
Repository Health
Technical Analysis
@lezer/lr is the run-time parsing engine of the Lezer system: it consumes the compact parser tables produced by @lezer/generator and executes them to build syntax trees incrementally, prioritizing speed and small tree size over a highly labeled parse tree. Nodes are minimal blobs of start offset, end offset, tag, and children, which keeps both the generated parser tables and the resulting trees compact enough to re-parse on every keystroke in an editor.
As a GLR (Generalized LR) parser, it can explore multiple parse paths simultaneously to handle ambiguous grammars and recover gracefully from syntax errors mid-edit, making it well suited to live code editors rather than one-shot batch compilers. It is the parsing core underneath CodeMirror 6’s language support packages (@codemirror/lang-*) and any other project that needs fast, incremental, error-tolerant parsing in JavaScript.
What You Get
- An
LRParserruntime that executes generated parser tables to produce syntax trees - GLR (Generalized LR) parsing that explores multiple parse paths to handle ambiguous grammars and recover from syntax errors
- Incremental re-parsing designed to run efficiently on every keystroke in an editor
ContextTrackerandExternalTokenizer/LocalTokenGrouphooks for context-sensitive or hand-written tokenization alongside the generated grammar- Compact, minimally-labeled syntax trees (start, end, tag, children) that keep memory and re-parse cost low
Common Use Cases
- Powering a CodeMirror 6 language extension (e.g.
@codemirror/lang-javascript) that needs live syntax highlighting and folding - Building a custom code editor or IDE-like tool that requires fast incremental reparsing as the user types
- Implementing error-tolerant parsing for a domain-specific language embedded in an interactive tool
- Consuming grammar tables generated by
@lezer/generatorat build time to parse a custom file format at runtime
Under The Hood
Architecture - The engine is organized around four focused modules: parse.ts (966 lines) implements LRParser, the core GLR driving loop that walks generated action/goto tables and manages competing parse stacks; stack.ts (536 lines) implements the Stack data structure representing one in-progress parse path, supporting the fork/merge operations GLR parsing needs for ambiguity; token.ts (402 lines) implements the tokenizer runtime, including InputStream, ExternalTokenizer, and LocalTokenGroup for grammars that need custom or context-sensitive tokenization beyond what the generated tables encode; and constants.ts centralizes the encoding scheme shared with @lezer/generator’s table format. index.ts is a minimal three-line public surface re-exporting LRParser, Stack, and the tokenizer primitives. Tech Stack - Pure TypeScript with a single runtime dependency, @lezer/common (the shared tree/node type definitions used across the Lezer ecosystem), built via a custom build.js script and the author’s own @marijn/buildtool, emitting both ESM (dist/index.js) and CJS (dist/index.cjs) bundles with type declarations. Code Quality - The package’s own test script explicitly defers to @lezer/generator’s test suite (‘Tests are in @lezer/generator’), reflecting that this runtime is validated end-to-end against real generated grammars rather than in isolation; the small, single-purpose module boundaries (parse/stack/token/constants) keep each piece independently reasoned about despite the algorithmic complexity of GLR parsing. API Design - The public API is deliberately narrow — LRParser, Stack, ContextTracker, and tokenizer helpers — because almost all day-to-day usage goes through a generated parser object rather than this package’s exports directly; documentation for the actual parsing interface lives on the Lezer website rather than in inline JSDoc, reflecting its role as an internal engine for generated parsers rather than a general-purpose end-user API.