Direct-style parser combinator library for OCaml 5
  • OCaml 91.9%
  • Astro 2.9%
  • JavaScript 2.5%
  • Shell 1.2%
  • Makefile 0.9%
  • Other 0.5%
Find a file
David Sancho d3ce6ef748
Merge pull request #9 from davesnx/dune-release-action-v0.4.0
Update dune-release-action to v0.4.0
2026-09-08 00:29:03 +02:00
.github/workflows Update dune-release-action to v0.4.0 2026-09-03 10:18:10 +00:00
bench Store results in commit hashes 2026-03-26 15:51:13 +00:00
doc Fix formatting in some mld docs 2026-04-02 19:49:30 +00:00
examples Format cram test 2026-04-04 09:46:44 +00:00
lib Add implementation and docs 2026-03-26 14:31:04 +00:00
test Add implementation and docs 2026-03-26 14:31:04 +00:00
website Generate .md files 2026-04-15 09:57:22 +00:00
.gitignore Add implementation and docs 2026-03-26 14:31:04 +00:00
.ocamlformat Update ocamlformat to 0.29.0 2026-03-19 16:28:53 +00:00
CHANGES.md Fix formatting in some mld docs 2026-04-02 19:49:30 +00:00
dune Remove CI and make an env var 2026-03-17 18:30:16 +00:00
dune-project Remove effects from docs, website and keep Performance section honest 2026-03-26 10:01:06 +00:00
LICENSE Initial commit: baseline before optimization 2026-02-25 11:00:05 +00:00
Makefile Add automatic changelog 2026-03-23 00:40:28 +00:00
parseff.opam Remove effects from docs, website and keep Performance section honest 2026-03-26 10:01:06 +00:00
parseff.opam.template Install dune.3.22 with-doc 2026-03-19 16:05:00 +00:00
README.md Generate .md files 2026-04-15 09:57:22 +00:00
README.mld Remove effects from docs, website and keep Performance section honest 2026-03-26 10:01:06 +00:00

Parseff

Parseff is a direct-style parser combinator library for OCaml 5 where parsers are plain functions (unit -> 'a), errors are typed via polymorphic variants, and the runtime handles control flow, backtracking, and streaming input. Designed for performance with zero-copy span APIs and fused operations.

Documentation

Installation

$ opam install parseff -y

Example

let number () =
  let digits = Parseff.many ~at_least:1 Parseff.digit () in
  let n = List.fold_left (fun acc d -> (acc * 10) + d) 0 digits in
  if n >= 0 && n <= 255 then n
  else Parseff.error (`Out_of_range n)

let ip_address () =
  let a = number () in
  let _ = Parseff.char '.' in
  let b = number () in
  let _ = Parseff.char '.' in
  let c = number () in
  let _ = Parseff.char '.' in
  let d = number () in
  Parseff.end_of_input ();
  (a, b, c, d)

let () =
  match Parseff.parse "192.168.1.1" ip_address with
  | Ok ((a, b, c, d)) ->
      Printf.printf "Parsed: %d.%d.%d.%d\n" a b c d
  | Error { pos; error = `Out_of_range n } ->
      Printf.printf "Error at %d: %d out of range (0-255)\n" pos n
  | Error { pos; error = `Unexpected_end_of_input } ->
      Printf.printf "Error at %d: unexpected end of input\n" pos
  | Error { pos; error = `Expected msg } ->
      Printf.printf "Error at %d: %s\n" pos msg
  | Error { pos; error = `Failure msg } ->
      Printf.printf "Error at %d: %s\n" pos msg
  | Error { pos; error = `Depth_limit_exceeded msg } ->
      Printf.printf "Error at %d: %s\n" pos msg

Features

Performance

Parseff is built for throughput and low allocation: in the current benchmark suite, the generic Parseff parsers are up to ~1.5x faster than Angstrom's baselines, and its fused zero-copy paths roughly ~3x faster.

Even against Angstrom's optimized JSON parser, Parseff's optimized path is still ~1.5x faster while cutting minor allocations from ~11.1 GB to ~1.3 GB; against Angstrom's generic JSON parser, the generic / optimized Parseff paths land at ~800 MB / ~1.3 GB versus ~5.9 GB.

See the full comparison for the methodology and results, bench/bench_json.ml for the JSON benchmark, and bench/ for the full suite.

Documentation

Contributing

  • Open an issue to discuss proposed changes
  • Write tests for new features
  • Run make fmt before submitting
  • Ensure all tests pass with make test

License

MIT. See LICENSE for details.