CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-parsimmon

A monadic LL(infinity) parser combinator library

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

combinators.mddocs/

Parser Combinators

Functions that combine multiple parsers into more complex parsing patterns. Combinators are the core building blocks for creating sophisticated parsers from simpler components.

Capabilities

Sequence Combinators

Combinators that parse multiple elements in sequence.

/**
 * Parses all given parsers in sequence, returns array of results
 * @param {...Parser} parsers - Parsers to run in sequence
 * @returns {Parser} Parser that returns array of all results
 */
Parsimmon.seq(p1, p2, ...pn);

/**
 * Parses all parsers in sequence, applies mapper function to results
 * @param {...Parser} parsers - Parsers to run in sequence
 * @param {Function} mapper - Function applied to results array
 * @returns {Parser} Parser that returns mapped result
 */
Parsimmon.seqMap(p1, p2, ...pn, mapper);

/**
 * Parses parsers in sequence, returns object with named results
 * @param {...(Parser|Array)} args - Parsers or [name, parser] pairs
 * @returns {Parser} Parser that returns object with named properties
 */
Parsimmon.seqObj(...args);

Usage Examples:

// Basic sequence - returns array
const pair = Parsimmon.seq(
  Parsimmon.digits,
  Parsimmon.string(","),
  Parsimmon.digits
);
pair.parse("12,34"); // { status: true, value: ["12", ",", "34"] }

// Sequence with transformation
const coordinate = Parsimmon.seqMap(
  Parsimmon.digits.map(Number),
  Parsimmon.string(","),
  Parsimmon.digits.map(Number),
  (x, comma, y) => ({ x, y })
);
coordinate.parse("10,20"); // { status: true, value: { x: 10, y: 20 } }

// Named sequence - returns object
const point = Parsimmon.seqObj(
  ["x", Parsimmon.digits.map(Number)],
  Parsimmon.string(","),
  ["y", Parsimmon.digits.map(Number)]
);
point.parse("15,25"); // { status: true, value: { x: 15, y: 25 } }

Alternative Combinators

Combinators that try multiple parsing alternatives.

/**
 * Tries parsers in order, returns result of first success
 * @param {...Parser} parsers - Parsers to try in order
 * @returns {Parser} Parser that succeeds with first successful alternative
 */
Parsimmon.alt(p1, p2, ...pn);

Usage Examples:

// Parse different number formats
const number = Parsimmon.alt(
  Parsimmon.regexp(/0x[0-9a-fA-F]+/).map(s => parseInt(s, 16)), // hex
  Parsimmon.regexp(/0b[01]+/).map(s => parseInt(s.slice(2), 2)), // binary
  Parsimmon.regexp(/[0-9]+/).map(Number) // decimal
);

number.parse("42"); // { status: true, value: 42 }
number.parse("0x2A"); // { status: true, value: 42 }
number.parse("0b101010"); // { status: true, value: 42 }

// Parse keywords or identifiers
const keywordOrId = Parsimmon.alt(
  Parsimmon.string("if"),
  Parsimmon.string("else"),
  Parsimmon.string("while"),
  Parsimmon.regexp(/[a-zA-Z][a-zA-Z0-9]*/)
);

Separated Combinators

Combinators for parsing lists with separators.

/**
 * Parses zero or more content items separated by separator
 * @param {Parser} content - Parser for list items
 * @param {Parser} separator - Parser for separator between items
 * @returns {Parser} Parser that returns array of content items
 */
Parsimmon.sepBy(content, separator);

/**
 * Parses one or more content items separated by separator
 * @param {Parser} content - Parser for list items
 * @param {Parser} separator - Parser for separator between items
 * @returns {Parser} Parser that returns array of content items
 */
Parsimmon.sepBy1(content, separator);

Usage Examples:

// Parse comma-separated numbers
const numberList = Parsimmon.sepBy(
  Parsimmon.digits.map(Number),
  Parsimmon.string(",").trim(Parsimmon.optWhitespace)
);

numberList.parse("1,2,3"); // { status: true, value: [1, 2, 3] }
numberList.parse("42"); // { status: true, value: [42] }
numberList.parse(""); // { status: true, value: [] }

// Parse at least one item
const nonEmptyList = Parsimmon.sepBy1(
  Parsimmon.letters,
  Parsimmon.string("|")
);

nonEmptyList.parse("a|b|c"); // { status: true, value: ["a", "b", "c"] }
nonEmptyList.parse(""); // { status: false, ... }

// Parse function arguments
const args = Parsimmon.string("(")
  .then(Parsimmon.sepBy(
    Parsimmon.regexp(/[^,)]+/).trim(Parsimmon.optWhitespace),
    Parsimmon.string(",")
  ))
  .skip(Parsimmon.string(")"));

args.parse("(a, b, c)"); // { status: true, value: ["a", "b", "c"] }

Lazy Evaluation

Combinators for handling recursive grammars and forward references.

/**
 * Creates a lazy parser for recursive grammars
 * @param {Function} fn - Function that returns a parser
 * @returns {Parser} Parser that evaluates lazily
 */
Parsimmon.lazy(fn);

/**
 * Creates a lazy parser with description
 * @param {string} description - Description for error messages
 * @param {Function} fn - Function that returns a parser
 * @returns {Parser} Parser that evaluates lazily
 */
Parsimmon.lazy(description, fn);

Usage Examples:

// Recursive JSON-like structure
const value = Parsimmon.lazy(() => Parsimmon.alt(
  Parsimmon.regexp(/"[^"]*"/).map(s => s.slice(1, -1)), // string
  Parsimmon.digits.map(Number), // number
  array,
  object
));

const array = Parsimmon.string("[")
  .then(Parsimmon.sepBy(value, Parsimmon.string(",").trim(Parsimmon.optWhitespace)))
  .skip(Parsimmon.string("]"));

const object = Parsimmon.string("{")
  .then(Parsimmon.sepBy(
    Parsimmon.seqMap(
      Parsimmon.regexp(/"[^"]*"/).map(s => s.slice(1, -1)),
      Parsimmon.string(":").trim(Parsimmon.optWhitespace),
      value,
      (key, colon, val) => [key, val]
    ),
    Parsimmon.string(",").trim(Parsimmon.optWhitespace)
  ))
  .skip(Parsimmon.string("}"))
  .map(pairs => Object.fromEntries(pairs));

// Arithmetic expressions with precedence
const expr = Parsimmon.lazy("expression", () => addition);

const factor = Parsimmon.alt(
  Parsimmon.digits.map(Number),
  Parsimmon.string("(").then(expr).skip(Parsimmon.string(")"))
);

const term = Parsimmon.sepBy1(factor, Parsimmon.string("*"))
  .map(factors => factors.reduce((a, b) => a * b));

const addition = Parsimmon.sepBy1(term, Parsimmon.string("+"))
  .map(terms => terms.reduce((a, b) => a + b));

Language Creation

Framework for creating complete parsing languages with interdependent rules.

/**
 * Creates a language object with interdependent parsers
 * @param {Object} parsers - Object with parser-generating functions
 * @returns {Object} Language object with parser properties
 */
Parsimmon.createLanguage(parsers);

Usage Examples:

// Create a simple expression language
const MathLang = Parsimmon.createLanguage({
  // Basic tokens
  number: () => Parsimmon.regexp(/[0-9]+/).map(Number),
  operator: () => Parsimmon.oneOf("+-*/"),
  
  // Expressions using other rules
  factor: (r) => Parsimmon.alt(
    r.number,
    Parsimmon.string("(").then(r.expr).skip(Parsimmon.string(")"))
  ),
  
  term: (r) => Parsimmon.seqMap(
    r.factor,
    Parsimmon.seq(Parsimmon.oneOf("*/"), r.factor).many(),
    (first, rest) => rest.reduce((acc, [op, val]) => 
      op === "*" ? acc * val : acc / val, first)
  ),
  
  expr: (r) => Parsimmon.seqMap(
    r.term,
    Parsimmon.seq(Parsimmon.oneOf("+-"), r.term).many(),
    (first, rest) => rest.reduce((acc, [op, val]) => 
      op === "+" ? acc + val : acc - val, first)
  )
});

// Use the language
MathLang.expr.parse("2 + 3 * 4"); // { status: true, value: 14 }

// JSON parser using createLanguage
const JsonLang = Parsimmon.createLanguage({
  value: (r) => Parsimmon.alt(
    r.string, r.number, r.boolean, r.null, r.array, r.object
  ),
  
  string: () => Parsimmon.regexp(/"((?:\\.|[^"\\])*)"/,1),
  number: () => Parsimmon.regexp(/-?(0|[1-9][0-9]*)([.][0-9]+)?([eE][+-]?[0-9]+)?/).map(Number),
  boolean: () => Parsimmon.alt(Parsimmon.string("true"), Parsimmon.string("false")).map(x => x === "true"),
  null: () => Parsimmon.string("null").result(null),
  
  array: (r) => Parsimmon.string("[")
    .then(r.value.sepBy(Parsimmon.string(",").trim(Parsimmon.optWhitespace)))
    .skip(Parsimmon.string("]")),
    
  object: (r) => Parsimmon.string("{")
    .then(Parsimmon.seqMap(
      r.string, 
      Parsimmon.string(":").trim(Parsimmon.optWhitespace), 
      r.value,
      (key, _, value) => [key, value]
    ).sepBy(Parsimmon.string(",").trim(Parsimmon.optWhitespace)))
    .skip(Parsimmon.string("}"))
    .map(pairs => Object.fromEntries(pairs))
});

Install with Tessl CLI

npx tessl i tessl/npm-parsimmon

docs

binary-parsing.md

combinators.md

core-parsers.md

index.md

language-creation.md

string-parsers.md

transformation.md

tile.json