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

language-creation.mddocs/

Language Creation

Framework for creating complete parsing languages with interdependent rules. This enables building complex parsers where rules can reference each other, including recursive and mutually recursive grammars.

Capabilities

Language Creation

Creates a language object with interdependent parsers that can reference each other by name.

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

Usage Examples:

// Simple arithmetic language
const Lang = Parsimmon.createLanguage({
  Expr: function() {
    return Parsimmon.alt(
      this.AddExpr,
      this.Number
    );
  },
  
  AddExpr: function() {
    return Parsimmon.seqMap(
      this.Number,
      Parsimmon.string('+').trim(Parsimmon.optWhitespace),
      this.Expr,
      (left, op, right) => ({ type: 'add', left, right })
    );
  },
  
  Number: function() {
    return Parsimmon.regexp(/[0-9]+/)
      .map(Number)
      .desc('number');
  }
});

// Use the language
const result = Lang.Expr.parse("5 + 3 + 2");
// Returns: { type: 'add', left: 5, right: { type: 'add', left: 3, right: 2 } }

// More complex example with recursive structures
const JsonLang = Parsimmon.createLanguage({
  value: function() {
    return Parsimmon.alt(
      this.string,
      this.number,
      this.object,
      this.array,
      this.bool,
      this.null
    );
  },
  
  object: function() {
    return this.pair
      .sepBy(this.comma)
      .wrap(this.lbrace, this.rbrace)
      .map(pairs => Object.fromEntries(pairs));
  },
  
  array: function() {
    return this.value
      .sepBy(this.comma)
      .wrap(this.lbracket, this.rbracket);
  },
  
  // ... other rules
});

Lazy Parser Creation

Creates lazy parsers for recursive grammars where parsers need to reference themselves or other parsers that aren't defined yet.

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

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

Usage Examples:

// Simple recursive parser
const parenExpr = Parsimmon.lazy(() => {
  return Parsimmon.string('(')
    .then(parenExpr.or(Parsimmon.regexp(/[^()]+/)))
    .skip(Parsimmon.string(')'));
});

// Lazy parser with description
const list = Parsimmon.lazy('list', () => {
  return Parsimmon.string('[')
    .then(
      Parsimmon.alt(
        list,
        Parsimmon.regexp(/[^\[\]]+/)
      )
    )
    .skip(Parsimmon.string(']'));
});

// Mutual recursion example
let expr, term, factor;

expr = Parsimmon.lazy(() => {
  return Parsimmon.alt(
    Parsimmon.seqMap(
      term,
      Parsimmon.string('+'),
      expr,
      (l, op, r) => ({ type: 'add', left: l, right: r })
    ),
    term
  );
});

term = Parsimmon.lazy(() => {
  return Parsimmon.alt(
    Parsimmon.seqMap(
      factor,
      Parsimmon.string('*'),
      term,
      (l, op, r) => ({ type: 'mul', left: l, right: r })
    ),
    factor
  );
});

factor = Parsimmon.lazy(() => {
  return Parsimmon.alt(
    expr.wrap(Parsimmon.string('('), Parsimmon.string(')')),
    Parsimmon.regexp(/[0-9]+/).map(Number)
  );
});

Best Practices

Language Organization

When using createLanguage, organize your grammar rules logically:

  • Start with the top-level rule (usually called Expr, Program, or similar)
  • Define rules in order of precedence (higher precedence rules reference lower ones)
  • Use clear, descriptive names for rules
  • Add .desc() calls for better error messages

Recursive Pattern Handling

For recursive structures:

  • Use lazy() when a parser needs to reference itself before it's fully defined
  • Use createLanguage() when you have multiple interdependent rules
  • Always provide good descriptions for error reporting
  • Be careful about left recursion (can cause infinite loops)

Error Handling

Both createLanguage and lazy support proper error propagation:

const Lang = Parsimmon.createLanguage({
  expr: function() {
    return this.number.desc('expected number');
  },
  
  number: function() {
    return Parsimmon.regexp(/[0-9]+/)
      .map(Number)
      .desc('number');
  }
});

// Provides clear error messages when parsing fails

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