Quick start
Section titled “Quick start”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 algebraic effects handle control flow, backtracking, and streaming input. Designed for performance with zero-copy span APIs and fused operations.
Features
Section titled “Features”- Build parsers with direct-style and compose with
Parseffcombinators - API is designed to be expressive enough to not need monadic operators (
>>=,>>|,*>), nor binding operators (let*,let+,and+) - Typed domain errors via polymorphic variants, raise with
Parseff.error. Parseff also adds`Expected of string,`Unexpected_end_of_input, and`Depth_limit_exceeded of stringas possible parsing failures - Automatic backtracking with
Parseff.or_ - Minimal dependency footprint: only
refor regex support - Streaming support with
Source.of_string,Source.of_channel,Source.of_function - Domain-safe: each
Parseff.parse/Parseff.parse_sourcecall is self-contained with no global mutable state, so independent parses can run in parallel across domains - Zero-copy span APIs for low-allocation parsing (
Parseff.take_while_span,Parseff.sep_by_take_span,Parseff.fused_sep_take,Parseff.skip_while_then_char) - Fused operations for hot paths (
Parseff.sep_by_take,Parseff.skip_while_then_char)
Performance
Section titled “Performance”Parseff is faster than Angstrom and MParser by a factor of 2 to 4x. In case of equal implementations, Parseff is ~2x faster than Angstrom and MParser. With an optimized version using zero-copy span APIs, that gap widens to ~4x. See the full comparison for details and bench/bench_vs_angstrom.ml for the benchmark.
Installation
Section titled “Installation”with opam
Section titled “with opam”opam install parseff -ywith dune package management
Section titled “with dune package management”Add parseff to your dune-project:
(package (name my_project) (depends (ocaml (>= 5.3)) (parseff (>= 0.1))))Then lock and build:
dune pkg lockdune buildFrom source
Section titled “From source”git clone https://github.com/davesnx/parseff.gitcd parseffmake initmake builddune installAdd to your dune file
Section titled “Add to your dune file”(executable (name my_parser) (libraries parseff))Example
Section titled “Example”Here’s a small parser that validates IPv4 addresses 192.168.1.1 or 0.0.0.0. A detailed explanation of the IPv4 addresses parser:
let number () = let digits = Parseff.many1 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 = `Expected msg } -> Printf.printf "Error at %d: %s\n" pos msgAPI organization
Section titled “API organization”| Section | Combinators |
|---|---|
| Core | consume, char, satisfy, take_while, take_while1, skip_while, match_regex, fail, error, end_of_input |
| Combinators | or_, one_of, one_of_labeled, optional, look_ahead, expect, rec_ |
| Repetition and separation | many, many1, count, sep_by, sep_by1, between, end_by, end_by1, chainl, chainl1, chainr, chainr1 |
| Convenience | digit, letter, alphanum, any_char, is_whitespace, whitespace, whitespace1, skip_whitespace |
| Errors | fail, error, expect |
| Diagnostics | warn, warn_at, parse_until_end, parse_source_until_end |
| Zero-copy and fused operations | take_while_span, sep_by_take_span, sep_by_take, fused_sep_take, skip_while_then_char |
| Streaming | Source.of_string, Source.of_channel, Source.of_function, parse_source, parse_source_until_end |