One Grammar. Every Parser.
Never write a parser again.
Describe your language once. PEGO builds the parser: fast, safe and wherever you need it. Here is a complete calculator, operator precedence included:
calc.pego
Edit in the playground →
// calc.pego
type Num = node[string]
type Bin = { Left Expr; Op node[string]; Right Expr }
type Expr ~ Bin | Num
@start
rule main Expr = #operators {
skip " "*
operand num
operand "(" e:main ")" => $e
level additive { infix left "+" / "-" => Bin{Left: $lhs, Op: $op, Right: $rhs} }
level multiplicative { infix left "*" / "/" => Bin{Left: $lhs, Op: $op, Right: $rhs} }
level power { infix right "^" => Bin{Left: $lhs, Op: $op, Right: $rhs} }
}
rule num Num = ([0-9]+) #atomic
pego parse -g calc.pego -f sexpr -i '1 + 2 * 3 ^ 2'
Bin{Left: Num("1"), Op: "+", Right: Bin{Left: Num("2"), Op: "*", Right: Bin{Left: Num("3"), Op: "^", Right: Num("2")}}}
pego parse -g calc.pego -i '1 + * 2'
pego: 1:5: syntax error: expected "(", [0-9]
Note
PEGO is pre-release: the language, the Go API and the file formats may change. Generated Go is not yet as fast as Go's hand-written parsers, and closing that gap is the current work. Why PEGO has every measured number, the current limits and how PEGO compares with other tools.
Why PEGO
Runs anywhere, any way
Any target language, any engine, any optimization. One grammar becomes generated code, a VM or an interpreter, tuned for speed, memory or size, and every one of them returns the same tree, the same positions and the same errors. Whatever you build, PEGO fits.
Unmatched expressive power
Build values of any type you define, carry state for context-sensitive syntax and write a whole operator precedence table as one rule. Recover from errors with a complete tree, and parse endless streams as they arrive. If you can describe a language, you can parse it.
Performance pushed to the limit
A multi-stage IR optimizes at every level, from grammar-wide rewrites down to the generated code. Incremental parsing reuses everything an edit did not touch, so a fresh tree is ready between keystrokes.
Safe on any input
PEGO analyzes your grammar to bound the time and memory a parse can take, and built-in limits on work and nesting mean hostile input can never hang the parser or blow its stack.
Complete support for building your language
A built-in language server brings completion, diagnostics, navigation and formatting to any editor. Trace, profile, lint and debug show exactly what the parser does, so you never have to guess.
#Install
go get github.com/ornew/pego # library
go install github.com/ornew/pego/cmd/pego@latest # command-line tool
Go 1.27.1 or later. No dependencies outside the standard library.
#Use It
From Go
p, err := pego.CompileSource(grammarSource, "main") // grammar errors come with positions
node, err := p.Parse(input) // positions in code points by default
p.Parse(input, pego.WithUnit(pego.Bytes)) // ... or in UTF-8 bytes
p.Parse(input, pego.WithBackend(pego.Bytecode)) // pick a backend
p.Parse(input, pego.RecognizeOnly()) // validate without building a tree
json.Marshal(node) // nodes encode with types and positions
err = p.ParseStream(r, func(n *pego.Node) error { return nil }) // one #stream element at a time
doc, _ := p.NewDocument(text)
doc.Edit(10, 12, "new text") // replace [10, 12)
node, err = doc.Parse() // reuses rule results the edit did not reach
data, _ := p.MarshalBinary() // save; pego.LoadParser(data) skips parsing and checking
From the command line
pego parse -g grammar.pego -i 'abc=12' # parse and print the tree (JSON, or -f sexpr)
pego parse -g grammar.pego -check < input.txt # validate only
pego explain -g grammar.pego < input.txt # which rule calls make up a syntax error
pego trace -g grammar.pego -i 'abc=12' # every rule call as an indented tree (-f json for tools)
pego profile -g grammar.pego < input.txt # cost per rule, with hints on wasted work
pego lint -g grammar.pego # likely mistakes: dead alternatives, unused captures, ...
pego lsp # language server for editors (errors, formatting, rename, ...)
pego fmt -w grammar.pego # format in place, comments kept
pego fix -w grammar.pego # rewrite older syntax to the current one
pego compile -g grammar.pego -o grammar.pegoc # precompile
pego gen -g grammar.pego -module example.com/calc -o calc # generate a standalone Go parser
pego gen -g grammar.pego -module example.com/calc -types -o calc # ... with Go types for the grammar's types
pego gen -g grammar.pego -module example.com/calc -recognize -o calc # ... with Recognize, which validates without a tree
pego gen -lang ts -g grammar.pego -o ts-parser # generate a TypeScript parser into a directory
pego gen -lang ts -g grammar.pego -types -o ts-parser # also emit types.ts and parseAST
pego gen -g grammar.pego -module example.com/calc -max-depth 600000 -o calc # set its nested rule-call default
pego sample -g grammar.pego -n 20 -coverage # generate inputs the grammar accepts (tests, fuzz seeds)
pego edit-fuzz -g grammar.pego -minimize -save-failures out # check Document edits against fresh parses
Run pego without arguments for every command and flag.
#Ready-Made Parsers
parsers/ has parsers for common languages, written in PEGO, generated into Go and tested against the
languages' conformance suites and reference implementations. Each is a module of its own that depends only on the
standard library and PEGO's runtime package. Until PEGO publishes a release, use them from a checkout of the
repository; go get does not fetch them from another module yet (see Ready-Made Parsers):
import "github.com/ornew/pego/parsers/json"
v, err := json.Decode(`{"name": "pego", "tags": ["peg", "operators-rule"]}`)
| Language | Module | Specification |
|---|---|---|
| CEL | github.com/ornew/pego/parsers/cel |
The CEL language definition |
| CSV | github.com/ornew/pego/parsers/csv |
RFC 4180 |
| CUE | github.com/ornew/pego/parsers/cue |
The CUE language specification |
| DuckDB SQL | github.com/ornew/pego/parsers/duckdb |
DuckDB 1.5.6's SQL dialect |
| Go | github.com/ornew/pego/parsers/golang |
The Go specification (go1.27), as go/parser parses it |
| JSON | github.com/ornew/pego/parsers/json |
RFC 8259 |
| Python | github.com/ornew/pego/parsers/python |
Python 3.14 |
| TypeScript | github.com/ornew/pego/parsers/typescript |
TypeScript 5.9 and TSX |
| XML | github.com/ornew/pego/parsers/xml |
XML 1.0 (Fifth Edition), non-validating |
| YAML | github.com/ornew/pego/parsers/yaml |
YAML 1.2.2 |
#Examples
Small grammars with tests that show the features live in examples/: a calculator (Operators Rule and left-recursive), an indentation-based outline format and a small programming language. The complete parsers of parsers/, such as the ones for Go and Python 3, are the larger examples.
#Contributing
Bug reports, ideas and pull requests are welcome. Run go test ./... before sending changes, and follow the
commit message guidelines. Report security vulnerabilities privately, as the
security policy describes.
#License
Documentation
Getting Started With PEGO
From a first grammar to typed trees and operator precedence, step by step.
PEGO Guides
Task-oriented guides to trees, expressions, errors, context, runtimes and code generation.
The PEGO Specifications
The normative definition of the PEGO grammar language.
Go API Reference
The Go API and the command-line tool, generated from the source.
Examples
Small grammars that show the features: a calculator, an outline and a small language.
Design Records
The decisions behind PEGO and their rationale.