mrkeyoor.com_
Sat 08 Aug 17:43 UTC
npmCLI & Toolingupdated 08 Aug 2026

nearley

nearley is an Earley parser toolkit for JavaScript. You describe a grammar in a .ne file, compile it to a JavaScript module with nearleyc, then feed text to a Parser and read every valid result. Unlike parser generators limited to a narrower grammar class, nearley can accept ambiguous and left-recursive grammars. It also ships command-line tools for interactive tests, random valid-input generation, and railroad diagrams, plus optional Moo lexer integration for faster token-based parsing. That flexibility is useful for small languages and file formats, but it also makes ambiguity and grammar performance the developer's responsibility.

Verdict

nearley remains unusually capable when Earley parsing, streaming feeds, or grammar fuzzing is the actual requirement. For a new deterministic language tool, its aging release and ambiguity burden make Peggy or Chevrotain the safer starting point.

API stability4/5Version 2.20.1 has kept the central contract simple for years: compile a .ne grammar, build Grammar.fromCompiled, create Parser, call feed, and inspect results. Grammar postprocessors and the command names are also well established. Stability here partly reflects low change rather than an active compatibility program, and the docs still note a TypeScript import-format change, so generated grammar imports deserve a compile test during upgrades.
Docs4/5nearley.js.org documents the grammar language, parser lifecycle, Moo integration, ambiguity, token errors, TypeScript preprocessing, unparse, testing, and railroad tools with runnable examples. It is candid that reject is slow, ambiguous parsing is inefficient, and feed cannot recover after an offending token. Some pages show their age and the short repository README delegates almost everything to the site, but the core workflow remains well explained.
Maintenance2/5The npm latest tag remains 2.20.1, the canonical GitHub repository was last pushed on 2024-11-14, and GitHub reports 199 open issues and pull requests. The repository is not archived and its pages were still receiving attention in 2026, but the gap between current use and code activity is material for a parser generator. Teams adopting it should expect to diagnose grammar and runtime compatibility problems themselves.
Ecosystem3/5nearley recorded 7,349,393 npm downloads for the week ending 2026-08-06 and ships a useful family of compiler, test, unparse, and diagram commands. Moo provides the recommended lexer path, and compiled grammars can run in Node or browsers. The broader ecosystem is smaller and older than those numbers suggest: integrations listed in the docs include aging editor and build-tool plugins, and modern TypeScript workflows need extra handling.

Use it if

  • You need a JavaScript parser generator that accepts left-recursive or ambiguous context-free grammars
  • You want to stream input through repeated feed calls rather than parse one complete string at once
  • You value grammar tooling such as nearley-test, nearley-unparse, and nearley-railroad
  • You want to pair a grammar with Moo tokens and JavaScript postprocessors for a custom AST
Skip it if

Setup reality

Install nearley locally and keep grammar compilation in project scripts instead of relying on a global binary. A normal project stores grammar.ne, runs nearleyc grammar.ne -o grammar.js, and imports the generated module at runtime. That generated file is an artifact, so decide whether to commit it or compile it in every build and package step. The parser returns an array because a grammar may have zero, one, or many results. An empty array after the final feed means the input is incomplete or invalid; more than one result means your grammar is ambiguous, and blindly taking results[0] hides the defect. Postprocessors receive child values, a zero-based location, and a reject sentinel. They are where you shape the AST, but returning reject is documented as a frequent performance trap. For serious grammars, add Moo and use token references; nearley's bundled character-class grammar helpers are convenient for prototypes but the docs say a lexer is faster. Streaming means feed can be called repeatedly, not that syntax errors are recoverable. If feed throws on a bad token, create a fresh parser. TypeScript postprocessors are supported through @preprocessor typescript, but the docs call out default-import behavior for compiled grammars. Tooling also needs care in CI: nearley-unparse generates valid random strings but does not prove semantic correctness, and nearley-test needs keepHistory when you want the internal parse table. The package declares no Node engine range in its current npm metadata, so pin and test your actual runtime rather than assuming a documented floor.

Patterns

Compile a grammar into JavaScriptcompile-grammar

npx nearleyc src/expression.ne -o src/expression.js

The runtime consumes the generated JavaScript module, not the .ne file. Put this command in the build so generated code cannot go stale.

Parse input and require one resultparse-input

const nearley = require('nearley');
const grammar = require('./expression.js');

const parser = new nearley.Parser(nearley.Grammar.fromCompiled(grammar));
parser.feed('2 + 3');

if (parser.results.length !== 1) {
  throw new Error('Expected one parse, got ' + parser.results.length);
}
const ast = parser.results[0];

Never silently take results[0]. Zero results means incomplete or invalid final input; multiple results expose grammar ambiguity.

Build an AST in a postprocessordefine-postprocessor

@{%
function binary(data) {
  return { type: 'add', left: data[0], right: data[2] };
}
%}

expression -> number _ "+" _ number {% binary %}
number -> [0-9]:+ {% d => Number(d[0].join('')) %}
_ -> [\s]:*

Postprocessors receive an array containing each symbol's parsed value. Keep AST shaping here so application code does not depend on nested grammar arrays.

Express repetition with EBNF modifiersuse-ebnf

word -> [a-z]:+ {% d => d[0].join('') %}
words -> word ("," _ word):* {%
  d => [d[0], ...d[1].map(part => part[2])]
%}
_ -> [\s]:*

The :+ and :* expansions create nested arrays. Add a postprocessor immediately instead of leaking that generated shape into consumers.

Feed a parser in chunksstream-input

const parser = new nearley.Parser(nearley.Grammar.fromCompiled(grammar));
parser.feed('if (ready) {');
parser.feed('run()');
parser.feed('}');

const [program] = parser.results;

Streaming allows repeated feed calls, but a thrown syntax error is terminal for that parser. Start a new Parser to recover.

Tokenize with Moointegrate-moo

@{%
const moo = require('moo');
const lexer = moo.compile({
  ws: { match: /\s+/, lineBreaks: true },
  number: /0|[1-9][0-9]*/,
  plus: '+',
});
%}
@lexer lexer

sum -> %number %ws:? %plus %ws:? %number {%
  d => Number(d[0].value) + Number(d[4].value)
%}

With a lexer, reference tokens as %name and read token.value. The docs recommend a tokenizer over reject-heavy or character-by-character production grammars.

Split grammar files with includeimport-grammar

@include "./number.ne"

main -> number _ "items" {% d => ({ count: d[0] }) %}
_ -> [\s]:*

Included files are expanded at compile time. Resolve paths from the grammar file and keep rule names from colliding.

Inspect a grammar from the command linetest-from-cli

npx nearleyc src/expression.ne -o /tmp/expression.js
printf '2 + 3' | npx nearley-test /tmp/expression.js

nearley-test prints parse results and can inspect parser state. It is an exploration tool, not a replacement for assertions in your test runner.

Generate valid random examplesgenerate-fuzz-input

npx nearley-unparse -s expression src/expression.js -n 25

Generated strings exercise what the grammar accepts, but they do not prove the postprocessed AST or application semantics are correct.

Generate a railroad diagramdraw-railroad-diagram

npx nearley-railroad src/expression.ne -o docs/expression-grammar.html

The output is an HTML document containing SVG diagrams. Regenerate it when the source grammar changes.

Alternatives

PackageRegistryPick it when
peggynpmYou want an actively maintained PEG generator with deterministic ordered choices and good browser support
chevrotainnpmYou prefer a fast code-defined parser toolkit with explicit tokens, recovery, and TypeScript-oriented tooling
ohm-jsnpmYou want readable PEG-style grammars separated from semantic operations