Skip to content

Repository files navigation

Parsec

CI Downloads Contributors Forks Stargazers Issues License

Golang parser combinator library inspired by haskell parsec.

But what is parser combinator?

In the parse combinatorial framework, a "parser" is a function that takes some semistructured input and produces some structured output, and "combinator" is a function that allows combining / composing things. So "parser combinators" is a way of expressing a system where you write a lot of small parsing functions and compose then together.

Installation

Requires Go 1.26 or newer.

go get github.com/okneniz/parsec

Quick start

package main

import (
	"fmt"

	"github.com/okneniz/parsec/strings"
)

func main() {
	// parse an unsigned integer, allowing surrounding whitespaces
	parser := strings.Padded(
		strings.Try(strings.Space("whitespace")),
		strings.Unsigned[int](),
	)

	result, err := strings.ParseString(" 42 ", parser)
	fmt.Println(result, err) // 42 <nil>
}

Small parsing functions are combined into bigger ones: Padded wraps Unsigned with optional whitespace skipping, Try makes backtracking possible, and ParseString feeds the input text through the resulting combinator.

Documentation

GoDoc documentation

Module layout

The root package is the generic core: the Combinator function type, the Buffer input abstraction, parse errors with positions, and every combinator implemented once for an arbitrary item type. The satellite packages build on it:

Package Purpose
parsec generic core: combinators, buffers, errors
parsec/strings rune input, line/column positions, text helpers
parsec/bytes binary input, big/little-endian readers
parsec/tokens token streams for two-stage parsers
parsec/lang lexers and expression machinery from a declarative Definition

Backtracking

Greedy combinators (Satisfy, Eq, Range, OneOf and others) consume one input item even when they fail. Combinators that try alternatives (Choice, Or) or repeat parsing in a loop (Many and similar) do not restore the buffer position on their own. So whenever a combinator can fail inside such a branch, wrap it in Try — it is the backtracking primitive that rewinds the buffer on failure:

choice := strings.Choice(
	"expected number or parens",
	strings.Try(parseNumber()),
	strings.Try(strings.Parens(parseExpr)),
)

Examples

  • text
    • json
    • xml - a streaming SAX parser: the document is a lazy iter.Seq2 of events, one combinator step per event, with a shape-checking Parse on top
    • timestamps
    • credit cards
  • binary
  • lang
    • tiny ml - a minimal Standard ML dialect (let, recursive functions, if-then-else, numbers and booleans): the two-stage parsing pipeline in miniature, with a Hindley-Milner typechecker and an interpreter on top

Projects using parsec

  • assembly - low-level toolkit to assemble and disassemble machine code (ARM64, RISC-V, LoongArch); its self-contained ELF and Mach-O container parsers are built with parsec.
  • cliche - regular expressions engine for batch processing; it compiles many patterns into a shared tree and matches them in a single pass, with regex syntax parsed by parsec.

Development

make test    # run all tests
make lint    # format code and run golangci-lint + style checks
make fmt     # format code only

Formatting conventions beyond gofmt, enforced by make lint:
no one-line function bodies, and a blank line between declarations.

The linter version is pinned in go.mod as a Go tool dependency, so make lint uses exactly the same version locally and in CI. To bump it:

make install-linter # or: go get -tool github.com/golangci/golangci-lint/v2/cmd/golangci-lint@<version>

Roadmap

See the open issues for a list of proposed features (and known issues).

Contributing

Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are greatly appreciated.

  • If you have suggestions for adding or removing projects, feel free to open an issue to discuss it, or directly create a pull request after you edit the README.md file with necessary changes.
  • Please make sure you check your spelling and grammar.
  • Create individual PR for each suggestion.

Creating A Pull Request

  1. Fork the Project
  2. Create your Feature Branch (git checkout -b feature/AmazingFeature)
  3. Commit your Changes (git commit -m 'Add some AmazingFeature')
  4. Push to the Branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request

About

Golang parser combinator library inspired by haskell parsec

Topics

Resources

Stars

18 stars

Watchers

1 watching

Forks

Releases

Used by

Contributors

Languages