@lezer/lr

Runtime incremental GLR parser engine that powers Lezer grammars and CodeMirror's syntax trees

Library
npm
v1.4.11
685 stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum. How we score it →
34 /100 Needs Attention
Development Activity 4
Maintenance 0
Community 44
Maturity 60
Momentum 28

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation. How we score it →
75 /100 Good
Architecture 85
Code Quality 78
Innovation 82
Learning Curve 55

@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 LRParser runtime 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
  • ContextTracker and ExternalTokenizer/LocalTokenGroup hooks 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/generator at 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.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers