Alternatives
hyperlark fills a specific niche: it builds LALR(1) and Earley parse tables
from a Lark .lark grammar at runtime — no code generation — and exposes that
engine natively in Rust, Python, TypeScript/WASM, and C (the C binding is
LALR-only today; the others expose both engines). That shapes where it fits well
and where it doesn’t, and it differs from every other tool in at least one axis
(runtime vs codegen, declarative grammar vs grammar-as-code, LALR vs PEG vs GLR,
one language vs four).
Pick your language below — the tabs are sticky, and stay in sync with the code-example tabs elsewhere in the docs, except WASM: what is measured there is the wasm binding rather than a language, so it has no code-example tab to pair with. Each compares hyperlark to the main parsing libraries in that language. Read them as “pick the right tool,” not “hyperlark wins”: a code-generated LR parser or a hand-tuned combinator will out-run runtime tables on a constant factor, and hyperlark’s Earley engine is its general engine, not its fast one.
In Python, hyperlark is a drop-in for lark — the same grammar and API,
reimplemented in native Rust. So the honest comparison is against the Python
parsing field, where hyperlark’s LALR is the fast option and everyone else
(including hyperlark’s own Earley) sits in the pure-Python-speed tier.
| Parser | Approach | Pros | Cons |
|---|---|---|---|
| hyperlark | Native-Rust core with a Lark-compatible API | Drop-in for lark (same grammar + API); the parse runs in native code; LALR and Earley; no runtime lark dependency | Younger; close but not a full-parity guarantee (documented gaps); ships as a compiled wheel |
| lark | Pure Python — the reference hyperlark reimplements | Very mature and feature-rich (Earley/LALR/CYK, reconstructor, tree templates); large community; trivial to install | Pure Python — the slow baseline |
| PLY | Python lex-yacc (LALR) | Classic yacc/lex model; stable; well documented | yacc-style (grammars in method docstrings); no tree by default; dated |
| sly | PLY successor (yacc-style LALR) | Cleaner than PLY; reduces straight to a value (no tree) | Rule-method grammar (no separate grammar file); no general parse tree; less active |
| pyparsing | Combinators (PEG-ish), pure Python | Expressive; hugely popular; no separate grammar file | Slow; grammar-as-Python-code; PEG-style limits |
| parsimonious | PEG, clean external grammar | Simple declarative PEG grammar | Pure-Python slow; PEG semantics |
| ANTLR4 (Python target) | LL(*) generator (codegen) | Powerful; excellent tooling; one grammar → many targets | Codegen via a Java tool; heavy, verbose runtime; LL(*), not LALR |
Choose hyperlark when you already use (or would use) lark and want the same
trees far faster. Stay on lark if you need a feature hyperlark defers
(reconstructor, tree templates, CYK) or want zero compiled dependencies.
Choose sly for a yacc-style evaluator with no tree. Choose ANTLR if you
need one grammar across many host languages and its tooling.
Rust has the richest field: hand-written combinators (nom, winnow, chumsky), code-generated parsers (pest, lalrpop), and a runtime GLR engine (tree-sitter). hyperlark is the runtime-table option — you trade a constant factor against codegen for the ability to load and change grammars with no build step.
| Parser | Approach | Pros | Cons |
|---|---|---|---|
| hyperlark | Runtime LALR(1) + Earley tables from a .lark grammar | No codegen/build step — grammars load and change at runtime; LALR speed and Earley generality from one grammar; Lark-style tree or reduce-time fold; the same grammar runs in Python/JS/C | A constant factor behind codegen (pays a runtime table build); younger, smaller ecosystem; you write a grammar, not Rust — less inline control than combinators |
| nom | Parser combinators (Rust functions) | Extremely fast; zero-dependency; great for binary/streaming; full control; very mature | The grammar is Rust code (no separate artifact); PEG-style ordered choice (no LALR conflict checks); you build any tree yourself |
| winnow | nom-derived combinators | nom-class speed with an ergonomic, actively-developed API | Same combinator trade-offs — grammar-as-code, no declarative grammar file |
| chumsky | Combinators with first-class error recovery | Excellent errors/recovery; ergonomic; well-suited to language front-ends | Slower than nom; grammar-as-code; heavier |
| pest | PEG, external .pest grammar (derive → codegen) | Clean declarative PEG grammar; good errors; popular | Codegen build step; PEG semantics (ordered choice, no left recursion); yields untyped pairs you walk |
| lalrpop | LR(1) parser generator (codegen) | Real LR(1) with conflict detection; fast; typed reduce actions | Codegen build step; LR(1) authoring + conflict debugging; built-in lexer is slow (pair with logos); Rust-only |
| tree-sitter | Incremental GLR (runtime, generated C tables) | Incremental re-parse + error resilience (editor-grade); huge grammar ecosystem; multi-language | Grammar authored in JS then generated to C (a build step); concrete syntax tree, not a clean AST; tuned for editors, not raw throughput |
Choose hyperlark when you want a declarative grammar you can load or edit at runtime and share across languages, and LALR throughput is enough. Choose nom / winnow for maximum speed and binary formats. Choose lalrpop for a compiled LR(1) parser with conflict guarantees. Choose pest for a clean PEG with a build step. Choose tree-sitter for incremental editor tooling.
hyperlark reaches JS/TS through WASM. The parse result crosses the wasm↔JS boundary once per parse; the remaining cost is the wasm tier tax on the parse itself plus building a JS value per node — so on structure-heavy inputs (many small nodes) a pure-JS parser like chevrotain can win, while on scan-heavy inputs (few nodes, long tokens) hyperlark’s native lexer pulls ahead. See the benchmarks page for the full shape-dependent breakdown.
| Parser | Approach | Pros | Cons |
|---|---|---|---|
| hyperlark (WASM) | Rust core compiled to WASM; Lark grammar | Fast native scan/parse; LALR and Earley; the same grammar as Python/Rust/C; tree or reduce-time fold | A wasm tier tax + per-node JS value building on structure-heavy trees; a WASM bundle to ship; younger |
| chevrotain | Pure-JS parsing DSL (runtime, no codegen) | The fastest JS runtime parser; no build step; great errors; battle-tested (Langium is built on it) | The grammar is imperative JS (rule methods), not a declarative file; JS-only; JIT warm-up sensitive |
| peggy (peg.js successor) | PEG, generates a JS parser | Clean declarative PEG grammar; standalone generated parser; popular | Codegen step; PEG semantics; slower than chevrotain |
| nearley | Earley (runtime), grammar compiled by nearleyc | Any CFG incl. ambiguity; streaming; the fair Earley peer to hyperlark-Earley | Slow (Earley); a grammar build step; lightly maintained |
| ohm | PEG with external grammar + semantics | Nice declarative grammar and semantic actions; good for DSLs/teaching | Slower; smaller ecosystem |
| tree-sitter (wasm) | Incremental GLR | Incremental, error-resilient, editor-grade; many grammars | Grammar codegen (JS→C→wasm); CST not AST; editor-oriented |
Choose hyperlark for scan-heavy inputs, a declarative grammar shared with
other languages, or when you already have a .lark grammar. Choose
chevrotain for the fastest warm pure-JS parser on structure-heavy inputs and no
build step. Choose peggy for a clean PEG with codegen. Choose nearley for
general/ambiguous grammars in pure JS.
C has no shortage of parser generators; what it lacks is a runtime one that needs no build step and produces a tree for you. hyperlark’s C binding links the same Rust core behind a C ABI.
| Parser | Approach | Pros | Cons |
|---|---|---|---|
| hyperlark (C ABI) | Rust core behind a C ABI; Lark grammar | Fast native parse; runtime grammars (no codegen); parse to a tree, or fold to a value (post-parse or reduce-time, no tree) | Links a Rust-built library; younger; lower-level C API; LALR only (Earley is in the Rust/Python/JS bindings, not yet in C) |
| bison + flex | yacc/lex (LALR, codegen) | The ubiquitous classic; fast LALR; decades of hardening; everywhere | Codegen build step; C actions interleaved in the grammar; global-state / reentrancy quirks; you build the tree in actions |
| lemon | LALR generator (codegen; SQLite’s) | Reentrant, no global state; very fast; proven in SQLite | Codegen; C actions; less common; manual tree building |
| tree-sitter (C API) | Incremental GLR (runtime) | Incremental, error-resilient, editor-grade; huge grammar library | Grammar codegen (JS→C); CST, not a clean AST; editor-oriented |
| re2c | Lexer generator | Extremely fast DFA lexer | Lexer only — pair with a parser (not measured here) |
| hand-written recursive descent | Manual C | Full control; fast; no dependencies | All manual; no grammar artifact; error-prone as grammars grow |
Choose hyperlark for a runtime grammar with a ready-made tree and no codegen step. Choose bison/flex or lemon for a compiled LALR parser with no runtime dependency (lemon if you want reentrancy and no global state). Choose tree-sitter for incremental editor tooling.