Give it a W3C EBNF grammar and an input, and it shows you how that input is parsed: the parse forest, the Earley chart the parser actually built, railroad diagrams of the grammar, and what is wrong with the grammar itself.
It is a grammar comprehension and debugging tool. It does not generate parsers.
ebnf-viewer ./examples/json # opens the workspace in your browser
Status: early.
v0.1.0is a working walking skeleton, not a finished product. The engine is real — Earley with a Scott-style SPPF, so ambiguous and left-recursive grammars are handled properly — but the performance apparatus described in docs/DESIGN.md is not built yet. See What is and is not here.
Most grammar tooling answers "did it parse?" — this answers "how did it parse, and why not?"
- Real grammars are ambiguous and you usually find out much too late. This tool parses with Earley and keeps every derivation in an SPPF, so it can point at the exact span that has two parses and show you both.
- Real grammars are left-recursive, because that is how you write an expression grammar. Earley takes left recursion natively; nothing has to be rewritten to suit the tool.
- When a parse fails, "unexpected token at offset 412" is not an answer. This reports the furthest
position reached, the set of things that could have continued, and which constructs were still
open — the
{on line 2 that never closed.
W3C EBNF is scannerless: terminals are characters, so there is no lexer hiding between you and the grammar. What you see in the tree is what the grammar actually said.
Not published to npm yet. From a clone:
pnpm install
pnpm build
node packages/cli/bin/ebnf-viewer.mjs ./examples/jsonRequires Node ≥ 20.19.
ebnf-viewer [dir] serve the workspace and open a browser
ebnf-viewer parse <grammar> <input> print the parse tree
ebnf-viewer why <grammar> <input> explain why a parse failed
ebnf-viewer check <dir> grammar diagnostics; non-zero exit on error
ebnf-viewer init [dir] write a starter grammar and sample
Flags: --start <Rule>, --port <n>, --no-open, --format text|json, --max-items <n>.
Exit codes: 0 ok, 1 usage, 2 grammar has errors, 3 input failed to parse, 4 internal.
$ ebnf-viewer parse examples/expr/expr.ebnf examples/expr/sample.txt
Expr [0,11) "(1+2)*3-4/2"
Expr [0,7) "(1+2)*3"
Term [0,7) "(1+2)*3"
Term [0,5) "(1+2)"
Factor [0,5) "(1+2)"
'(' [0,1) "("
Expr [1,4) "1+2"
...Note [0-9]+ appears in the tree as [0-9]+ — the synthetic rules that lowering creates to feed
Earley never reach you.
$ ebnf-viewer parse examples/expr/expr-ambiguous.ebnf examples/expr/sample-ambiguous.txt
Expr [0,5) "1+2*3" amb×2
...
2 parses
1 ambiguity observed
A001 Expr [0,5) ×2: "1+2*3"
`Expr` derives "1+2*3" through 2 different alternatives (1, 2).$ ebnf-viewer why examples/json/json.ebnf broken.json
broken.json:1:16: parse failed
1 | {"name":"ebnf" "stars":3}
^
found '"'
expected ',' | '}' | #x9, #xA, #xD or #x20
unclosed object opened at 0 (line 1, col 1)
unclosed members opened at 1 (line 1, col 2)$ ebnf-viewer check examples/diagnostics
broken.ebnf:3:25: error E301: undefined nonterminal 'Footer'
Start ::= Header Body Footer
^^^^^^
hint: add a production 'Footer ::= ...' or remove the reference
broken.ebnf:15:1: error E502: rule 'Loop' is not productive: it cannot derive any finite string
hint: every alternative of 'Loop' reaches 'Loop' again; add a base case
broken.ebnf:19:1: warning W403: unit rule cycle: Unit -> Alias -> Unit
broken.ebnf:15:1: warning W411: rule 'Loop' is directly left-recursive: Loop -> Loop
hint: left recursion is legal and fully supported by the Earley engineebnf-viewer <dir> serves a three-column UI on 127.0.0.1: grammar, input, and a result column
with five views. Hovering anything highlights the corresponding span in the other two columns.
Edit a grammar file in your own editor and the browser updates.
| View | What it is for |
|---|---|
| Tree | the parse forest, collapsed into a readable tree; ambiguous nodes carry a badge and you can pin which derivation to show |
| Diagnostics | everything static analysis found, clickable to the source span |
| Railroad | each rule as a syntax diagram, cross-linked to the grammar text |
| Chart | the Earley sets themselves — which items were predicted, scanned and completed at each position |
| Ambiguity | each ambiguous span with its shortest witness |
Working:
- W3C EBNF frontend:
::=,|, juxtaposition,?*+,( ),'...'/"...",[a-z],[^...],#xNN,/* */, andA - Bexclusion for character sets. Panic recovery, so one typo does not cost you the rest of the file. W3C spec constraint notes ([VC: ...]) are treated as trivia instead of being silently absorbed into the previous rule. - Codepoint alphabet throughout, so
[#x10000-#x10FFFF]means what it says. - Earley recognizer with Aycock–Horspool nullable pre-completion, atomic string literals, and a zero-width iteration guard; Scott-style binarized SPPF with family dedup, reachability sweep and canonical numbering.
- Ambiguity detection and reporting;
ParseCountthat saysinfinitewith a witness cycle rather than lying. - Static analysis: undefined, duplicate, unused, unreachable, non-productive, nullable, unit cycles, and left recursion with the actual cycle path (reported as information, since Earley handles it).
- Failure explanation with expected set, unclosed constructs, and partial literal matches.
- Railroad SVG rendering, byte-stable, with no DOM measurement.
- CLI, local server with watch mode, and the web workspace.
Not built yet (designed in docs/DESIGN.md, deliberately deferred):
- The performance apparatus: struct-of-arrays arenas, Leo optimization, budget ladder with graceful degradation, browser worker, row paging. Large inputs will get slow before they get correct.
- The journal-based step debugger (time travel, breakpoints,
whyNot). The Chart view is the minimum slice. - Railroad path-taken overlay, the xref graph, and SVG export.
- General
A - Bover nonterminal languages. Character-set exclusion is exact; anything else is approximated as the left operand and reported asW505. - Grammar editing in the browser,
.ebnftraceartifacts, static build.
docs/DESIGN.md is the full architecture — the parsing engine specification, the
diagnostic catalogue, the trace model, and the roadmap. It is considerably more thorough than this
MVP and is what the MVP is being grown into.
pnpm install
pnpm typecheck
pnpm test # 87 tests over the named engine fixtures
pnpm buildThe test suite is the interesting part: it pins the cases that catch silent tree bugs — Scott's
hidden left recursion, S ::= S S | 'a' over aaaa giving exactly Catalan(3) = 5 parses, nullable
iteration bodies terminating, and a property test that no synthetic symbol name ever escapes into
anything a user can see.
MIT