DeutschGitHub
prototypepackages/parser-combinator/src

Parser Combinator

@ralphschuler/parser-combinator

Composes byte-oriented parsers with slice-aware inputs and progress-checked repetition.

parsingbacktrackingbinary

01 · Problem

What is it for?

Parsing must preserve input position, errors and progress while supporting alternatives and repetition.

02 · Mental model

How should you think about it?

A parser maps a cursor state to either a new state with a result or an explicit error.

In this repository

Composes byte-oriented parsers with slice-aware inputs and progress-checked repetition. The library has a broad combinator surface; the regression suite focuses on composition, view boundaries and termination.

03 · Control flow

What happens in which order?

  1. Create an input view with the correct offset and length.
  2. Run a primitive parser.
  3. Compose results or backtrack on failure.
  4. Require forward progress inside repetition.
  5. Return the final cursor and result.

04 · Building blocks

The important contracts

Parser / map / chain / runCompose and execute parser state transformations.
str / char / regex / sequenceOf / choice / many / sepByBuild primitives, alternatives and progress-checked repetition.

05 · Build it yourself

Implement a small core

Start with this small independent implementation, then use the steps below to make its contract explicit.

  1. Create an input view with the correct offset and length.
  2. Run a primitive parser.
  3. Compose results or backtrack on failure.
  4. Require forward progress inside repetition.
  5. Return the final cursor and result.
Concept sketch · may reference omitted helpers
type State = { input: Uint8Array; offset: number };
type Result<T> =
  | { ok: true; value: T; state: State }
  | { ok: false; offset: number; expected: Set<string> };

class Parser<T> {
  constructor(readonly parse: (state: State) => Result<T>) {}
  map<U>(fn: (value: T) => U) {
    return new Parser<U>(state => {
      const result = this.parse(state);
      return result.ok ? { ...result, value: fn(result.value) } : result;
    });
  }
}

06 · Verify

What should you test?

  • Reject repetition of a parser that consumes no input.
  • Respect sliced typed-array boundaries.
  • Check UTF-8 characters and final cursor positions.

Try it yourself

Turn the first verification claim into a failing test, implement the smallest fix, then add a boundary case.

Reject repetition of a parser that consumes no input.

Compare your approach

Your check should fail when the stated invariant is deliberately broken, and pass again after the fix. Keep setup deterministic and assert an observable result.

07 · Limits

Trade-offs and failure modes

  • Backtracking can become expensive.
  • Character counts and byte counts differ.
  • Errors should retain useful location and context.
Important

The library has a broad combinator surface; the regression suite focuses on composition, view boundaries and termination.

08 · Keep exploring

Source code and related ideas

yarn tsx packages/parser-combinator/examples/basic.ts