-
-[](https://www.npmjs.com/package/unitas)
-[](https://coveralls.io/github/sovrin/unitas?branch=master)
-[](https://opensource.org/licenses/MIT)
-
-A lightweight, TypeScript-first parser combinator library for building expressive and composable parsers.
-
-## Features
-
-- **Parser Combinators**: Compose small parsers into complex ones using combinators like `many`, `choice`, `sequence`, and more
-- **Terminals**: Factory functions for common patterns (`char`, `string`, `regex`, etc.)
-- **Primitives**: Pre-built parser instances ready to use (`digit`, `letter`, `whitespace`, etc.)
-- **TypeScript**: Full TypeScript support with generic types and inference
-- **Tree-shakeable**: ESM-only with separate exports for `combinators`, `terminals`, `primitives`, and `utils`
-- **No dependencies**: Zero external runtime dependencies
-
-> **Note:** This library is in active development. The API may change before v1.0.0.
-
-## Installation
-
-```bash
-npm install unitas
-```
-
-## Quick Start
-
-**CSV parser** — parsing comma-separated values with quoted fields
-
-```typescript
-import { grammar, run } from 'unitas';
-import { choice, inner, separatedBy } from 'unitas/combinators';
-import { char, regex } from 'unitas/terminals';
-import { letters } from 'unitas/primitives';
-
-const csv = grammar({
- row: (p) => separatedBy(p.value, char(',')),
- value: (p) => choice(p.quoted, p.unquoted),
- quoted: () => inner(char('"'), regex(/^[^"]*/), char('"')),
- unquoted: () => letters,
-});
-
-run(csv.row, 'a,b,c'); // ['a', 'b', 'c']
-run(csv.row, '"a,b",c'); // ['a,b', 'c']
-```
-
-**JSON value parser** — parsing simple json values
-
-```typescript
-import { grammar, run } from 'unitas';
-import { choice, map, quoted } from 'unitas/combinators';
-import { string } from 'unitas/terminals';
-import { bool, digits, letters } from 'unitas/primitives';
-
-const json = grammar({
- value: (p) => choice(p.string, p.number, p.bool, p.null),
- string: () => quoted(letters),
- number: () => digits,
- bool: () => bool,
- null: () => map(string('null'), () => null),
-});
-
-run(json.value, '"hello"'); // 'hello'
-run(json.value, '42'); // 42
-run(json.value, 'true'); // true
-run(json.value, 'null'); // null
-```
-
-**Query string parser** — parsing URL query parameters
-
-```typescript
-import { grammar, run } from 'unitas';
-import { map, outer, separatedBy } from 'unitas/combinators';
-import { char } from 'unitas/terminals';
-import { letters } from 'unitas/primitives';
-
-type Query = {
- params: Recordfailure — Creates a failed result with an optional error message.grammar — Creates a recursive grammar where rules can reference each other.label — Labels a parser with a custom error message on failure.lazy — Defers parser creation, useful for recursive grammars.match — Pattern matching on a Result to handle success and failure cases.memoize — Memoizes a parser to cache results by input string. Useful for expensive parsers and recursive grammars to avoid exponential backtracking.parser — Creates a parser from a parser function.run — Runs a parser and returns the value, throws on failure or unconsumed input.success — Creates a successful result with a value and remaining input.char — Parse a specific character.charOf — Parse any character from a set.noneOf — Parse any character not in the set.oneOf — Parse one string from a set of strings (longest match wins).regex — Parse with a regular expression.satisfy — Parse a character satisfying a predicate.string — Parse a specific string.stringOf — Parse first character that exists in string (like charOf but for a string).take — Take n characters.takeWhile — Takes characters while the predicate returns true.token — Parses a string as a token, skipping trailing whitespace. Unlike word, it does not enforce word boundaries - useful for parsing strings that might be followed by any character.word — Parses a specific word and ensures it is not followed by word characters. Use this when parsing keywords that should not be part of a longer identifier.alphaNum — Parse a single alphanumeric character.alphaNums — Parse one or more alphanumeric characters.anyChar — Parse any single character.bool — Parse a boolean literal.crlf — Parse CRLF line ending.digit — Parse a single digit and return as number.digits — Parse one or more digits and return as number.eof — Parse end of file (succeeds only on empty input).eol — Parse end of line (\\n, \\r\\n, or EOF).float — Parse a floating point number.hexDigit — Parse a single hexadecimal digit.hexDigits — Parse one or more hexadecimal digits.identifier — Parse an identifier — starts with letter or underscore, no leading digit, no hyphen.integer — Parse a signed integer.letter — Parse a single letter.letters — Parse one or more letters.line — Parse until end of line.literal — Parse a word-like value including hyphens.lowercase — Parse a single lowercase letter.lowercases — Parses one or more lowercase letters.nl — Parse a newline character.number — Parse an integer or float.octDigit — Parse a single octal digit.octDigits — Parse one or more octal digits.position — Get current position (remaining input length).rest — Parse the rest of the input.space — Parse a single space character.spaces — Parse one or more space characters.tab — Parse tab character.uppercase — Parses a single uppercase letter.uppercases — Parses one or more uppercase letters.whitespace — Parses a single whitespace character.whitespaces — Parses one or more whitespace characters.attempt — Attempt wraps a parser to handle backtracking on failure.bind — Chain parsers where the second parser depends on the first result.braced — Parse content surrounded by braces.bracketed — Parse content surrounded by brackets.chainLeft — Chain left-associative operations (right-to-left for same precedence).chainLeft1 — Chain left-associative operations (fails on empty input).chainRight — Chain right-associative operations (right-to-left grouping).chainRight1 — Chain right-associative operations (fails on empty input).choice — Try each parser in order, return first success.concat — Join string array parser result into a single string.consume — Consume input but discard the result (return null).endBy — Zero or more items separated and ending with terminator.endBy1 — One or more items separated and ending with terminator.exactly — Parse exactly n occurrences.first — Extract the first element from a parser result array.flag — Return true if parser succeeds, false otherwise. Always succeeds without consuming input on failure.fold — Parse zero or more and fold into a single value.fold1 — Parse one or more and fold into a single value.foldRight — Parse zero or more and fold right-to-left.foldRight1 — Parse one or more and fold right-to-left.fuse — Fuse multiple string parsers into a single one. The fused parser concatenates all string results.guard — Conditionally apply parser based on a condition.inner — Extract inner value from surrounded content (like inner of braced).interleaved — Parse items with interleaved separators.last — Extract the last element from a parser result array.left — Keep only the left result from a sequence.lexeme — Parser that consumes trailing whitespace.many — Zero or more occurrences (never fails).many1 — One or more occurrences (fails if no matches).manyAtLeast — Parse at least n occurrences.manyAtMost — Parse at most n occurrences.manyBetween — Parse between min and max occurrences.manyTill — Parse zero or more until terminator matches.map — Transform the parsed value.node — Create a node from parser fields.not — Succeed if parser fails (without consuming input).nth — Extract the nth element from a parser result array.optional — Make parser optional (return null on failure, without consuming input).optionalConsume — Optionally consume input (always succeeds, returns void).optionalSeparatedBy — Zero or more items separated by a separator, with optional null values.outer — Extract outer values from a sequence of 3 parsers (skip middle).padded — Parse content with optional whitespace on both sides.parenthesized — Parse content surrounded by parentheses.peek — Parse without consuming input.postfix — Parse postfix operators (chains atom with operators that return functions).prefix — Parse prefix operators (like - in -5).pure — Always return a value without consuming input.quoted — Parse content surrounded by single or double quotes.recover — Use fallback value when parser fails.right — Keep only the right result from a sequence.separatedBy — Zero or more items separated by a separator.separatedBy1 — One or more items separated by a separator.separatedEndBy — Zero or more items separated by and ending with a terminator.separatedEndBy1 — One or more items separated by and ending with a terminator.separatedUntil — Parse items separated by separator until terminator matches.sequence — Parse a sequence of parsers and return all results as an array.skip — Skip a parser n times.skipMany — Skip zero or more occurrences (never fails, returns null).skipMany1 — Skip one or more occurrences (fails if no matches).surrounded — Parse content surrounded by delimiters.unless — Parse unless condition is true (inverse of guard).until — Parse until terminator matches (fails if terminator never matches).validate — Validate parsed value with a predicate.value — Replace parsed value with a constant.when — Branch on a boolean parser result.filter — Exclude values from array.flatten — Flatten nested arrays.join — Join array elements into a string.pick — Pick elements from an array by index.pipe — Pipe parser functions together.pop — Get the last element of an array.shift — Get the first element of an array.spread — Collect spread arguments into an array.
+
+**Composing parsers into a unified whole.** A lightweight, TypeScript-first parser combinator library — write small parsers, combine them into big ones, get typed results back.
+
+[](https://www.npmjs.com/package/unitas)
+[](https://coveralls.io/github/sovrin/unitas?branch=master)
+[](https://opensource.org/licenses/MIT)
+
+```bash
+npm install unitas
+```
+
+```typescript
+import { grammar, run } from 'unitas';
+import { choice, inner, separatedBy } from 'unitas/combinators';
+import { char, regex } from 'unitas/terminals';
+import { letters } from 'unitas/primitives';
+
+const csv = grammar({
+ row: (p) => separatedBy(p.value, char(',')),
+ value: (p) => choice(p.quoted, p.unquoted),
+ quoted: () => inner(char('"'), regex(/^[^"]*/), char('"')),
+ unquoted: () => letters,
+});
+
+run(csv.row, 'a,b,c'); // ['a', 'b', 'c']
+run(csv.row, '"a,b",c'); // ['a,b', 'c']
+```
+
+- **Zero dependencies**, ESM-only, fully typed
+- **Tree-shakeable** — five entry points, import only what you touch
+- **126 composable exports** — grouped by what they do, indexed below
+- **Mutual recursion out of the box** via `grammar`, no forward declarations
+
+> **Note:** This library is in active development. The API may change before v1.0.0.
+
+## Contents
+
+- [Entry points](#entry-points)
+- [Which function do I need?](#which-function-do-i-need)
+- [Core concepts](#core-concepts)
+- [More examples](#more-examples)
+- [API index](#api-index)
+
+## Entry points
+
+Nothing is re-exported across entry points, so your bundler only ever sees what you import.
+
+| Import from | Contains |
+| -------------------- | ------------------------------------------------------------------ |
+| `unitas` | Types, `success`/`failure`, `run`, `grammar`, `lazy`, `memoize` |
+| `unitas/terminals` | Factories that match input directly — `char`, `string`, `regex`, … |
+| `unitas/primitives` | Ready-made parsers — `digit`, `letters`, `whitespace`, … |
+| `unitas/combinators` | Parsers that take parsers — `map`, `choice`, `sequence`, `many`, … |
+| `unitas/utils` | Helpers for `map` callbacks — `pick`, `join`, `pipe`, … |
+
+## Which function do I need?
+
+The library is broad on purpose: each function does one small thing, so grammars read like the format they parse. This table is the fastest way in — start from the intent, then look the name up in the [API index](#api-index).
+
+### Matching a single thing
+
+| I want to match… | Reach for |
+| ------------------------------------------- | ------------------------------------------------------------------------------------------ |
+| an exact character or string | `char`, `string` |
+| one of several strings (longest wins) | `oneOf` |
+| any character in / not in a set | `charOf`, `stringOf`, `noneOf` |
+| a character passing a predicate | `satisfy` |
+| a regular expression | `regex` |
+| a fixed number of characters, or a run | `take`, `takeWhile` |
+| a keyword not glued to more word characters | `word` |
+| a token, eating trailing whitespace | `token`, `lexeme` |
+| a value — no need to build it | `number`, `integer`, `float`, `bool`, `identifier`, `literal`, `line`, `rest` |
+| a character class | `letter`, `digit`, `alphaNum`, `hexDigit`, `octDigit`, `lowercase`, `uppercase`, `anyChar` |
+| whitespace or a line break | `whitespace`, `space`, `tab`, `nl`, `crlf`, `eol`, `eof` |
+
+Most primitives come in a singular and a plural form: `letter` matches one, `letters` matches a run of them. The same holds for `digit`/`digits`, `alphaNum`/`alphaNums`, `hexDigit`/`hexDigits`, `octDigit`/`octDigits`, `lowercase`/`lowercases`, `uppercase`/`uppercases`, `space`/`spaces` and `whitespace`/`whitespaces`. Need to know how much input is left? `position`.
+
+### Repeating
+
+| I want to… | Reach for |
+| ----------------------------------------- | ----------------------------------------------------- |
+| repeat zero or more / one or more times | `many`, `many1` |
+| repeat a bounded number of times | `exactly`, `manyAtLeast`, `manyAtMost`, `manyBetween` |
+| repeat with a separator between items | `separatedBy`, `separatedBy1`, `optionalSeparatedBy` |
+| …and allow a trailing separator | `separatedEndBy`, `separatedEndBy1` |
+| …where every item ends with the separator | `endBy`, `endBy1`, `interleaved` |
+| repeat until something else shows up | `manyTill`, `until`, `separatedUntil` |
+| repeat but throw the values away | `skip`, `skipMany`, `skipMany1` |
+| reduce the repetitions as I go | `fold`, `fold1`, `foldRight`, `foldRight1` |
+
+### Choosing and branching
+
+| I want to… | Reach for |
+| ------------------------------------------- | ------------------------------------------------ |
+| take the first alternative that matches | `choice` |
+| succeed either way | `optional`, `optionalConsume`, `recover`, `flag` |
+| succeed only when something _doesn't_ match | `not` |
+| gate on a boolean I already have | `guard`, `unless`, `when` |
+| look ahead without consuming input | `peek` |
+
+### Sequencing and pulling values out
+
+| I want to… | Reach for |
+| ------------------------------------------ | ------------------------------------------------------------------------ |
+| run parsers in order and keep every result | `sequence` |
+| keep only one side of a pair | `left`, `right` |
+| keep specific positions of a sequence | `first`, `last`, `nth`, `pick` |
+| drop the delimiters, keep the middle | `inner`, `outer` |
+| glue string results together | `concat`, `fuse`, `join` |
+| match content inside delimiters | `surrounded`, `parenthesized`, `braced`, `bracketed`, `quoted`, `padded` |
+
+### Shaping the result
+
+| I want to… | Reach for |
+| ------------------------------------------ | ----------------------------------------------------- |
+| transform the parsed value | `map`, `pipe` |
+| replace it with a constant | `value`, `pure`, `consume` |
+| build a typed AST node | `node` |
+| reject a value after parsing it | `validate` |
+| choose the next parser from the last value | `bind` |
+| tidy up arrays inside a `map` | `pick`, `filter`, `flatten`, `shift`, `pop`, `spread` |
+
+### Grammars, recursion and errors
+
+| I want to… | Reach for |
+| ------------------------------------------ | ------------------------------------------------------ |
+| let rules reference each other by name | `grammar` |
+| refer to a parser defined further down | `lazy` |
+| cache results and avoid exponential blowup | `memoize` |
+| attach a readable error message | `label` |
+| parse infix operators with precedence | `chainLeft`, `chainLeft1`, `chainRight`, `chainRight1` |
+| parse prefix / postfix operators | `prefix`, `postfix` |
+| handle both outcomes of a `Result` | `match` |
+
+## Core concepts
+
+### The `Parser` type
+
+A `Parser