parserator
Version:
An elegant parser combinators library for Typescript
355 lines (252 loc) • 8.09 kB
Markdown
# Parserator
[](https://badge.fury.io/js/parserator)
[](https://opensource.org/licenses/MIT)
An elegant parser combinators library for Typescript.
## Table of contents
* [Introduction](
* [Installation](
* [Basic Usage](
* [Primitive Parsers](
* [Combinators](
* [Generator Syntax](
* [Error Handling](
* [Debugging](
* [Advanced Usage](
## Introduction
Parserator is a TypeScript-first parser combinator library. It allows you to build complex parsers from simple building blocks, with full type inference and a clean, generator-based syntax.
Some key features:
* Zero dependencies
* Written in TypeScript with complete type inference
* Clean, generator-based syntax similar to async/await
* Rich set of built-in parsers and combinators
* Detailed error reporting
* Extensive debugging capabilities
## Installation
```bash
npm install parserator
```
Requirements:
- TypeScript 4.5+
- `strict` mode enabled in tsconfig
## Basic Usage
```typescript
import { Parser, char, many1, digit } from 'parserator'
// Create a simple number parser
const numberParser = many1(digit).map(digits => parseInt(digits.join(""), 10))
// Parse a string
numberParser.parse("123") // => 123
// Handle errors safely
numberParser.safeParse("abc")
// => { success: false, error: ParserError }
```
## Primitive Parsers
```typescript
import { char, string, regex, alphabet, digit } from 'parserator'
// Single character
char("a").parse("abc") // => "a"
// Exact string
string("hello").parse("hello world") // => "hello"
// Regular expression
regex(/[0-9]+/).parse("123abc") // => "123"
// Any letter
alphabet.parse("abc") // => "a"
// Any digit
digit.parse("123") // => "1"
```
## Combinators
### Repetition
```typescript
import { many0, many1, manyN } from 'parserator'
// Zero or more
many0(digit).parse("123abc") // => ["1","2","3"]
// One or more
many1(digit).parse("123abc") // => ["1","2","3"]
// Exact count
manyN(digit, 2).parse("123") // => ["1","2"]
```
### Sequencing
```typescript
import { sequence, or, between, sepBy } from 'parserator'
// Sequence of parsers
sequence([char("a"), char("b")]).parse("abc") // => "b"
// Choice between parsers
or(char("a"), char("b")).parse("abc") // => "a"
// Between delimiters
between(char("("), char(")"), digit).parse("(5)") // => "5"
// Separated values
sepBy(char(","), digit).parse("1,2,3") // => ["1","2","3"]
```
### String Operations
```typescript
import { takeUntil, takeUpto, parseUntilChar } from 'parserator'
// Take until parser succeeds
takeUntil(char(";")).parse("hello;world") // => "hello"
// Take until parser would succeed
takeUpto(char(";")).parse("hello;world") // => "hello"
// Parse until character
parseUntilChar(";").parse("hello;world") // => "hello"
```
## Generator Syntax
Write parsers using a clean, generator-based syntax that feels like async/await:
```typescript
import { parser, char, many1, digit, optional } from 'parserator'
// Parse a floating point number
const float = parser(function* () {
// Parse optional sign
const sign = yield* optional(char("-"))
// Parse integer part
const intPart = yield* many1(digit)
// Parse optional fractional part
const fractionalPart = yield* optional(
parser(function* () {
yield* char(".")
return yield* many1(digit)
})
)
// Parse optional exponent
const exponentPart = yield* optional(
parser(function* () {
yield* char("e")
const expSign = yield* optional(char("+") || char("-"))
const expDigits = yield* many1(digit)
return (expSign ?? "") + expDigits.join("")
})
)
// Combine parts
const numStr =
(sign ?? "") +
intPart.join("") +
(fractionalPart ? "." + fractionalPart.join("") : "") +
(exponentPart ? "e" + exponentPart : "")
return parseFloat(numStr)
})
float.run("123.456e-7") // Right([1.23456e-5, ...])
```
## Error Handling
Customize error messages and add error callbacks:
```typescript
const parser = many1(digit)
.error("Expected at least one digit")
.errorCallback((error, state) => {
return `Error at ${state.pos.line}:${state.pos.column}: ${error.message}`
})
parser.run("abc")
// Left(ParserError: Error at 1:1: Expected at least one digit)
```
## Debugging
Debug tools to inspect parser behavior:
```typescript
import { debug, trace } from 'parserator'
// Add debug output
const debuggedParser = debug(parser, "number-parser")
// Add trace points
const tracedParser = trace("Before parsing number")
.then(parser)
```
## Advanced Usage
### JSON Array Parser
```typescript
const jsonArray = parser(function* () {
yield* char("[")
yield* skipSpaces
const items = yield* sepBy(
char(","),
parser(function* () {
yield* skipSpaces
const value = yield* or(stringParser, numberParser)
yield* skipSpaces
return value
})
)
yield* skipSpaces
yield* char("]")
return items
})
jsonArray.run('["hello", 123, "world"]')
// Right([["hello", 123, "world"], ...])
```
### Recursive Parsers
```typescript
const expr: Parser<number> = Parser.lazy(() =>
parser(function* () {
yield* char("(")
const left = yield* number
const op = yield* or(char("+"), char("-"))
const right = yield* expr
yield* char(")")
return op === "+" ? left + right : left - right
})
)
expr.run("(1+(2-(3+4)))")
// Right([-4, ...])
```
## API Reference
### Parser<T>
The core Parser class that represents a parsing computation.
### Methods
###
Run the parser on an input string
#### parseOrError(input: string): T | ParserError
Run parser and return result or error
#### parseOrThrow(input: string): T
Run parser and throw on error
###
Transform parser result
#### flatMap<B>(f: (a: T) => Parser<B>): Parser<B>
Chain parsers
###
Set error message
#### errorCallback(cb: (error: ParserError, state: ParserState) => string): Parser<T>
Custom error handling
###
Adds a tap point to observe the current state and result during parsing
#### withName(name: string): Parser<T>
Name the parser for better errors
### Static Methods
###
Create parser using generator syntax
#### Parser.succeed<T>(value: T): Parser<T>
Create always-succeeding parser
#### Parser.fail(message: string): Parser<never>
Create always-failing parser
#### Parser.lazy<T>(f: () => Parser<T>): Parser<T>
Creates a new parser that lazily evaluates the given function. This is useful for creating recursive parsers.
### Basic Parsers
###
Creates a parser that matches a single character.
```ts
const parser = char("a")
parser.parse("abc") // => "a"
parser.parse("xyz") // throws error
```
###
Creates a parser that matches an exact string in the input.
```ts
const parser = string("hello")
parser.parse("hello world") // => "hello"
parser.parse("goodbye") // throws error
```
###
Creates a parser that matches input against a regular expression. The regex must match at the start of the input.
```ts
const parser = regex(/[0-9]+/)
parser.parse("123abc") // => "123"
```
###
A parser that matches any single alphabetic character (a-z, A-Z).
```ts
const parser = alphabet
parser.parse("abc") // => "a"
parser.parse("123") // throws error
```
###
A parser that matches any single digit character (0-9).
```ts
const parser = digit
parser.parse("123") // => "1"
parser.parse("abc") // throws error
```
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
## License
MIT