
Product
Introducing Socket Scanning for VS Code Marketplace Extensions
Socket now scans VS Code extensions, giving teams early detection of risky behaviors, hidden capabilities, and supply chain threats in developer tools.
Parser combinators that compile to optimized JavaScript — use as a library or as a build-time macro
Write parsers as ordinary functions. Ship them like hand-written parsers.
Parser combinators are pleasant to write and usually slow. Parser generators are fast and usually mean grammar files, generated code, and extra tooling. Parséman is a combinator library with an optional compiler, and it gives you both.
Parsing to JS values, the runtime-compiled artifact is the fastest general-purpose JS
parser in the suite — ahead of every other library measured, at every grammar and every
input size in that suite.
Every parser in the suite builds real output: objects, row arrays, AST nodes. On a 7.7 kB
GraphQL document compiled Parséman takes 148 µs; Chevrotain takes 336 µs. Only a
purpose-built native edges it out: JSON.parse on JSON.
Two more results. The setup-free interpreter now leads every external parser at every
measured JSON, CSV, and GraphQL size. The compiled CST path beats Lezer on the JSON CST
fixture — 148 µs vs 583 µs at 11.9 kB — while producing a richer tree carrying spans
and trivia. And parseDoc
stores parent-relative spans, so an in-place edit costs a fraction of a full reparse rather
than a multiple of it. Results move with grammar shape, input size and runtime — which is
why the suite ships with the library rather than only its conclusions.
You get there by writing normal code. Add the bundler plugin, mark one import, and the
combinators you already wrote compile to an optimized TableProgram artifact — the compiler
computes first sets, links direct bodies for proven shapes, and left-factors choices for you.
Grammars work the same in plain JavaScript: the macro compiles a .js grammar to the same
output as a .ts one. The package ships one ESM implementation, loadable through either
import or require() on the supported Node versions. Write in TypeScript and result
types are inferred across the whole combinator chain — types you didn't have to write out.
Reach for it when you want CST/AST nodes with spans and trivia, error recovery and incremental re-parsing for editor tooling, or simply a fast parser for a DSL, config language, formatter, or linter.
Parséman is scannerless without giving up token-style routing. Literal or first-set-disjoint
alternatives are a natural fit for choice(...); when several branches share a broad opener,
dispatch(selector, when(...), otherwise(...)) parses that head once, then routes by the
returned value or structural marker. CSS-like grammars use that for at-rules,
identifier-or-function values, media features, and dialect routes where interpolation and
dedicated syntax overlap.
📖 Full documentation: matthew-dean.github.io/parseman
npm install parseman
# pnpm add parseman
Pre-1.0: minor versions may carry breaking changes — check the changelog
before upgrading. Requires Node ^20.19.0 || >=22.13.0.
import { literal, sequence, choice, regex, transform, parse } from 'parseman'
const method = choice(literal('GET'), literal('POST'), literal('PUT'), literal('DELETE'))
const target = regex(/[^\s]+/)
const version = regex(/1\.[01]/)
const requestLine = transform(
sequence(method, literal(' '), target, literal(' HTTP/'), version),
([verb, , path, , ver]) => ({ verb, path, version: `HTTP/${ver}` })
)
parse(requestLine, 'GET /api/v1 HTTP/1.1')
// { ok: true, value: { verb: 'GET', path: '/api/v1', version: 'HTTP/1.1' }, span: ... }
New here? What is Parséman? walks the same ground more slowly.
The same combinator code runs three ways, with identical results:
with { type: 'macro' } disappears entirely; what ships is a compact table artifact
using the shared parseman/table runtime. Executing it still goes through run()/parse(),
so an app keeps Parseman as an ordinary dependency — see
the three modes.)compile() — the same optimizer, on demand at runtime.// Add the plugin (vite.config.ts) and one import attribute — that's the whole change:
import { literal, sequence, choice } from 'parseman' with { type: 'macro' }
See The three modes.
✍️ Writing grammars
literal,
regex, sequence, choice, many, sepBy, token, peek, not, and more.rules() for mutually recursive grammars; fully macro-compilable.choice(a·x, a·y) into a·(x | y)
to satisfy the tool. The compiler does that for you.compose() a dialect onto a base grammar instead of forking it.withCtx / gate without mutating shared state.dispatch parses a broad shared head once and routes at the grammar boundary that matters.🌳 Getting structure out
node()
captures terminals, named fields, and trivia, with policies for wrapping and collapsing.parseDoc re-parses just the edited subtree on each keystroke.🔬 Seeing what your grammar does
toRailroadHtml() and toEBNF() generate the grammar reference from the parser, so
the spec can't drift from what actually parses.diagnoseGrammar() tells you which choice lost its O(1) dispatch, names the
overlapping arms, and says how to fix it. Run it in a test or a CI job — compiling
reports nothing.Full API in the reference; how it stacks up against Peggy, Chevrotain, Lezer, tree-sitter, Parsimmon, Nearley and hand-written parsers in How Parséman compares.
Benchmarked against Peggy, Parsimmon, Chevrotain, Nearley, Jison and Lezer on JSON, CSV and GraphQL, at three input sizes each. Each chart's legend names the libraries measured for that grammar.
Largest fixture of each, runtime compile against the fastest other library on that chart:
GraphQL 147.76 µs vs Chevrotain's 335.75 µs, JSON 122.09 µs vs Chevrotain's
242.86 µs, CSV 75.02 µs vs Peggy's 433.52 µs. Native JSON.parse does JSON large
in 40.61 µs. On the CST chart, runtime compile runs 151.87 µs against Lezer's 582.14 µs
parse-only.
Those are the committed 0.50.2 charts, regenerated on 2026-08-29 on an M4 Pro with Node 24.11.1. They include small, medium, and large rows for JSON, GraphQL, and CST, plus small and large CSV rows.
Per-fixture figures, initialization costs, hardware, grammar provenance and how to reproduce any of it: benchmarks guide. Results move with grammar shape, input size and runtime — which is why the suite ships with the library rather than only its conclusions. Speed levers for your own grammars: performance guide.
The GraphQL fixture is a real grammar, parsing executable documents — queries, mutations, fragments, directives, every value type — into typed AST nodes, so the numbers come from a spec-shaped language rather than a toy.
pnpm install
pnpm test # interpreter + compiled parity, ordered-choice semantics
pnpm typecheck
pnpm build # ESM + .d.ts → dist/
pnpm docs:dev # this documentation site, locally
Benchmark and chart tasks (pnpm bench, bench:svg, bench:parseman, …) are described in
the benchmarks guide.
The frozen 0.48 runtime architecture and release specification is
docs/design/parseman-0.48.md. Detailed measurements
and rejected experiments remain in the linked evidence registers rather than being
treated as current design.
MIT © Matthew Dean
FAQs
Parser combinators that compile to optimized JavaScript — use as a library or as a build-time macro
The npm package parseman receives a total of 1,205 weekly downloads. As such, parseman popularity was classified as popular.
We found that parseman demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Product
Socket now scans VS Code extensions, giving teams early detection of risky behaviors, hidden capabilities, and supply chain threats in developer tools.

Research
/Security News
Socket uncovered two malicious VS Code themes in a GlassWorm-linked cluster with thousands of installs across VS Code Marketplace and Open VSX.

Security News
/Company News
Capital One is partnering with Socket to proactively secure its open source supply chain.