Participle
A dead-simple Go library for building parsers straight from annotated struct tags.
Repository Health
Technical Analysis
Participle lets Go developers define a grammar as a plain annotated struct and get a working parser for free, with no separate grammar file, code generator, or hand-rolled parser-combinator boilerplate required. Struct tags describe an EBNF-like grammar, and parsing directly populates an AST of your own Go types, much like encoding/json unmarshals JSON into a struct.
Version 2 targets Go generics (participle.Build[G]), ships a configurable stateful lexer for handling nested or interpolated tokens, and supports advanced grammar features like union types, custom Capture/Parseable interfaces, positive/negative lookahead, and detailed positional error reporting, making it a practical choice for building DSLs, config-file parsers, and small custom languages entirely in Go.
What You Get
- Struct-tag grammars - Define an EBNF-like grammar directly in Go struct tags and parse straight into typed AST nodes.
- Generics-based API -
participle.Build[G]andMustBuild[G]construct a fully typedParser[G]for your grammar type. - Configurable lexing - Ships with a default
text/scanner-based lexer, or drop in the built-in stateful/modal lexer for nested or interpolated tokens. - Union type support - Model sum types with
participle.Union[T], trying each member production in declaration order. - Custom capture hooks - Implement
Capture,Parseable, orParseTypeWithto hand-roll parsing for individual fields or productions. - Detailed error reporting - Parse errors report the deepest failure position reached during backtracking, not just the first mismatch.
Common Use Cases
- Domain-specific languages - Parsing custom DSLs (query languages, expression languages, config formats) directly into Go structs.
- Config file formats - Building parsers for INI-like, HCL-like, or bespoke configuration syntaxes.
- Toy and teaching languages - Prototyping small interpreters and compilers, as shown in the repo’s SQL, GraphQL, Thrift, and TOML example parsers.
- Structured query parsing - Parsing structured query-style grammars such as JSONPath expressions or precedence-climbing arithmetic.
Under The Hood
Architecture
The parser is built via a two-phase pipeline: Build[G] (parser.go) walks the target grammar type G via reflection inside a generatorContext (grammar.go/context.go), constructing a tree of node implementations (nodes.go, struct.go) that mirror the struct-tag EBNF, and validate.go checks the resulting node tree for well-formedness before parsing begins. At parse time, ParseFromLexer drives tokens from a lexer.PeekingLexer through a parseContext (context.go) into that node tree, which recursively populates the target struct via reflect.Value, while error.go tracks the “deepest” failure position across backtracking branches for reporting, and disjunction-style nodes implement Union[T] alternation. Lexing (the lexer package’s pluggable Definition/Lexer/PeekingLexer interfaces) is cleanly separated from grammar-tree construction (grammar.go) and tree execution (nodes.go), though the design keeps coupling non-trivial: changing the core node interface ripples through nodes.go, struct.go, grammar.go, and validate.go simultaneously.
Tech Stack
A pure Go module (go.mod declares go 1.24) with only two direct third-party dependencies — github.com/alecthomas/assert/v2 (test assertions) and github.com/alecthomas/repr (pretty-printing used in tests and tutorials) — plus github.com/hexops/gotextdiff as an indirect test dependency; there are no runtime dependencies beyond the standard library (text/scanner, regexp, reflect). Lexing defaults to a text/scanner-based lexer with an optional hand-rolled stateful/modal lexer built on compiled regexp.Regexp state machines, plus a lower-level lexer/internal package. A companion cmd/participle Go module hosts a small code-generation CLI for compiling stateful lexers ahead of time, and cmd/railroad renders grammars to railroad-diagram assets. Release tooling uses Hermit for toolchain pinning and GoReleaser for release artifacts.
Code Quality
The repo carries an extensive test suite (33 _test.go files across the root, lexer, and ebnf packages, with parser_test.go alone covering capture semantics, lookahead, error messages, unions, and stateful lexing), using assert/v2 for assertions. A separate _examples/ module is exercised end-to-end by CI’s dedicated “Test Examples” step, giving executable documentation for real grammars (SQL, GraphQL, Thrift, TOML, JSONPath). Error handling is explicit and typed, with dedicated Error/ParseError/UnexpectedTokenError types carrying position info, and CI runs both go test ./... and a golangci-lint job driven by an extensive lint configuration.
API Design
The public API is small and idiomatic: a single generic entry point (Build[G]/MustBuild[G]) turns any annotated Go struct into a typed Parser[G], so the grammar and the AST are the same type, mirroring encoding/json’s tag-based approach that Go developers already know. Getting started requires no boilerplate beyond defining the struct and calling Build; advanced needs (custom captures, union types, custom lexers, case-insensitivity) are opt-in via a compact, well-documented functional-options set rather than expanding the core surface. Naming is consistent across Parse/ParseString/ParseBytes/ParseFromLexer, and documentation is strong, with a dedicated tutorial walking through building an INI parser end-to-end and a wide range of worked examples covering realistic grammars.
Used by 3 apps in this directory
Rill
Analytics · Data Engineering
The fastest BI tool for humans and agents — define metrics, models, and dashboards as code and query them instantly on ClickHouse or DuckDB.
Warrant
Authentication · Security
A Google Zanzibar-inspired authorization service for defining, checking, and auditing fine-grained access control across your applications.
ZITADEL
Authentication
Open-source, API-first identity platform delivering multi-tenancy, Passkeys, OIDC, SAML, and SCIM without vendor lock-in.