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

transformation.mddocs/

Parser Transformation

Instance methods for transforming and chaining parsers. These methods enable building complex parsing pipelines by combining and transforming simpler parsers.

Capabilities

Result Transformation

Methods that transform the result of a successful parse.

/**
 * Transform parser result using a function
 * @param {Function} fn - Function to transform the result
 * @returns {Parser} Parser with transformed result
 */
parser.map(fn);

/**
 * Replace parser result with a constant value
 * @param {any} value - Value to use as result
 * @returns {Parser} Parser that returns the constant value
 */
parser.result(value);

/**
 * Transform input before parsing (contravariant map)
 * @param {Function} fn - Function to transform input
 * @returns {Parser} Parser that operates on transformed input
 */
parser.contramap(fn);

/**
 * Transform both input and output
 * @param {Function} f - Function to transform input
 * @param {Function} g - Function to transform output
 * @returns {Parser} Parser with both transformations
 */
parser.promap(f, g);

Usage Examples:

// Basic transformation
const number = Parsimmon.digits.map(Number);
number.parse("42"); // { status: true, value: 42 }

// Transform to constant
const trueParser = Parsimmon.string("yes").result(true);
trueParser.parse("yes"); // { status: true, value: true }

// Transform input
const caseInsensitive = Parsimmon.string("hello")
  .contramap(input => input.toLowerCase());
caseInsensitive.parse("HELLO"); // { status: true, value: "hello" }

// Transform both input and output
const upperDigits = Parsimmon.digits
  .promap(
    input => input.toLowerCase(), // normalize input
    result => result.toUpperCase() // transform output
  );

// Complex transformations
const coordinate = Parsimmon.seqMap(
  Parsimmon.digits.map(Number),
  Parsimmon.string(","),
  Parsimmon.digits.map(Number),
  (x, _, y) => ({ x, y, distance: Math.sqrt(x*x + y*y) })
);

String Manipulation

Methods for working with string results.

/**
 * Concatenate array result to single string
 * @returns {Parser} Parser that returns joined string
 */
parser.tie();

/**
 * Concatenate array result with separator
 * @param {string} separator - String to join array elements
 * @returns {Parser} Parser that returns joined string
 */
parser.tieWith(separator);

Usage Examples:

// Join characters
const letters = Parsimmon.letter.many().tie();
letters.parse("hello"); // { status: true, value: "hello" }

// Join with separator
const words = Parsimmon.letters.sepBy(Parsimmon.whitespace).tieWith(" ");
words.parse("hello world"); // { status: true, value: "hello world" }

// Parse hex bytes
const hexPair = Parsimmon.regexp(/[0-9a-fA-F]/).times(2).tie();
const hexString = hexPair.sepBy(Parsimmon.string(" ")).tieWith("");
hexString.parse("A1 B2 C3"); // { status: true, value: "A1B2C3" }

Parser Chaining

Methods that chain parsers together in sequence or conditionally.

/**
 * Monadic bind - chain dependent parsers
 * @param {Function} fn - Function that returns next parser based on result
 * @returns {Parser} Parser with conditional chaining
 */
parser.chain(fn);

/**
 * Parse this parser then another, return second result
 * @param {Parser} next - Parser to run after this one
 * @returns {Parser} Parser that returns result of next
 */
parser.then(next);

/**
 * Parse this parser then another, return first result
 * @param {Parser} next - Parser to run after this one
 * @returns {Parser} Parser that returns result of this
 */
parser.skip(next);

Usage Examples:

// Conditional parsing with chain
const numberParser = Parsimmon.digits.chain(function(digits) {
  const num = Number(digits);
  if (num > 100) {
    return Parsimmon.fail("number too large");
  }
  return Parsimmon.succeed(num);
});

// Dynamic parsing based on first result
const taggedValue = Parsimmon.letters.chain(function(tag) {
  if (tag === "num") {
    return Parsimmon.string(":").then(Parsimmon.digits.map(Number));
  } else if (tag === "str") {
    return Parsimmon.string(":").then(Parsimmon.regexp(/[^,]*/));
  } else {
    return Parsimmon.fail("unknown tag: " + tag);
  }
});

// Sequence with then/skip
const assignment = Parsimmon.letters
  .skip(Parsimmon.string("=").trim(Parsimmon.optWhitespace))
  .then(Parsimmon.digits.map(Number));

assignment.parse("x = 42"); // { status: true, value: 42 }

// Parse between delimiters
const quoted = Parsimmon.string('"')
  .then(Parsimmon.regexp(/[^"]*/))
  .skip(Parsimmon.string('"'));

Parser Combination

Methods that combine this parser with alternatives or conditions.

/**
 * Try alternative parser if this one fails
 * @param {Parser} alternative - Parser to try if this fails
 * @returns {Parser} Parser that tries alternative on failure
 */
parser.or(alternative);

/**
 * Provide fallback value if parser fails
 * @param {any} value - Value to return on failure
 * @returns {Parser} Parser with fallback value
 */
parser.fallback(value);

/**
 * Assert condition on result, fail with message if false
 * @param {Function} condition - Predicate function for result
 * @param {string} message - Error message if condition fails
 * @returns {Parser} Parser with assertion
 */
parser.assert(condition, message);

Usage Examples:

// Try alternatives
const booleanValue = Parsimmon.string("true")
  .result(true)
  .or(Parsimmon.string("false").result(false));

// Provide defaults
const optionalName = Parsimmon.string("name:")
  .then(Parsimmon.letters)
  .fallback("anonymous");

// Validate results
const positiveNumber = Parsimmon.digits
  .map(Number)
  .assert(n => n > 0, "positive number required");

// Complex validation
const email = Parsimmon.regexp(/[^@]+@[^@]+\.[^@]+/)
  .assert(s => s.includes("."), "email must contain domain")
  .assert(s => s.length < 100, "email too long");

Repetition Methods

Methods that repeat a parser multiple times.

/**
 * Repeat parser zero or more times
 * @returns {Parser} Parser that returns array of results
 */
parser.many();

/**
 * Repeat parser exactly n times, or between min and max times
 * @param {number} min - Minimum repetitions (or exact if max not given)
 * @param {number} [max] - Maximum repetitions
 * @returns {Parser} Parser that returns array of results
 */
parser.times(min, max);

/**
 * Repeat parser at most n times
 * @param {number} n - Maximum repetitions
 * @returns {Parser} Parser that returns array of results
 */
parser.atMost(n);

/**
 * Repeat parser at least n times
 * @param {number} n - Minimum repetitions
 * @returns {Parser} Parser that returns array of results
 */
parser.atLeast(n);

Usage Examples:

// Parse multiple items
const numbers = Parsimmon.digits.map(Number).many();
numbers.parse("123456"); // { status: true, value: [1,2,3,4,5,6] }

// Exact repetition
const hexByte = Parsimmon.regexp(/[0-9a-fA-F]/).times(2).tie();
hexByte.parse("a1"); // { status: true, value: "a1" }

// Range repetition
const identifier = Parsimmon.regexp(/[a-zA-Z_]/)
  .then(Parsimmon.regexp(/[a-zA-Z0-9_]/).times(0, 63))
  .map(([first, rest]) => first + rest.join(""));

// At least/at most
const someDigits = Parsimmon.digit.atLeast(1).tie();
const fewDigits = Parsimmon.digit.atMost(3).tie();

// Parse comma-separated with repetition
const csvRow = Parsimmon.regexp(/[^,\n]*/)
  .sepBy(Parsimmon.string(","))
  .skip(Parsimmon.newline.atMost(1));

Separated Repetition

Instance methods for parsing lists with separators.

/**
 * Parse zero or more of this parser separated by separator
 * @param {Parser} separator - Parser for separator
 * @returns {Parser} Parser that returns array of results
 */
parser.sepBy(separator);

/**
 * Parse one or more of this parser separated by separator
 * @param {Parser} separator - Parser for separator
 * @returns {Parser} Parser that returns array of results
 */
parser.sepBy1(separator);

Usage Examples:

// Parse word lists
const wordList = Parsimmon.letters.sepBy(Parsimmon.string(","));
wordList.parse("apple,banana,cherry"); // { status: true, value: ["apple","banana","cherry"] }

// Parse with required elements
const nonEmptyList = Parsimmon.digits.map(Number).sepBy1(Parsimmon.string("|"));
nonEmptyList.parse("1|2|3"); // { status: true, value: [1,2,3] }
nonEmptyList.parse(""); // { status: false, ... }

// Parse with complex separators
const parameter = Parsimmon.letters;
const paramList = parameter.sepBy(
  Parsimmon.string(",").trim(Parsimmon.optWhitespace)
);

String Manipulation

Methods for working with array results as strings.

/**
 * Join array of strings with separator
 * @param {string} separator - String to join with
 * @returns {Parser} Parser that returns joined string
 */
parser.tieWith(separator);

/**
 * Join array of strings with no separator
 * @returns {Parser} Parser that returns concatenated string
 */
parser.tie();

Usage Examples:

// Collect characters into string
const word = Parsimmon.letter.many().tie();
word.parse("hello"); // { status: true, value: "hello" }

// Join with separator
const spacedWord = Parsimmon.letter.many().tieWith(" ");
spacedWord.parse("hello"); // { status: true, value: "h e l l o" }

// Parse dotted identifiers
const dottedId = Parsimmon.letters.sepBy1(Parsimmon.string(".")).tieWith(".");
dottedId.parse("foo.bar.baz"); // { status: true, value: "foo.bar.baz" }

Position and Context

Methods for working with parsing position and context.

/**
 * Mark result with start and end positions
 * @returns {Parser} Parser that returns {start, value, end}
 */
parser.mark();

/**
 * Create AST node with name and position
 * @param {string} name - Node name
 * @returns {Parser} Parser that returns {name, value, start, end}
 */
parser.node(name);

/**
 * Apply wrapper function to this parser
 * @param {Function} wrapper - Function that takes and returns a parser
 * @returns {Parser} Result of wrapper function
 */
parser.thru(wrapper);

Usage Examples:

// Track positions
const markedNumber = Parsimmon.digits.map(Number).mark();
markedNumber.parse("123"); 
// { status: true, value: { start: {offset:0, line:1, col:1}, value: 123, end: {offset:3, line:1, col:4} } }

// Create AST nodes
const numberNode = Parsimmon.digits.map(Number).node("Number");
numberNode.parse("42");
// { status: true, value: { name: "Number", value: 42, start: {...}, end: {...} } }

// Apply transformations
const trimmed = Parsimmon.string("hello").thru(p => 
  Parsimmon.optWhitespace.then(p).skip(Parsimmon.optWhitespace)
);

// Reusable wrapper
const token = (parser) => parser.trim(Parsimmon.optWhitespace);
const numberToken = Parsimmon.digits.map(Number).thru(token);

Lookahead and Validation

Methods for conditional parsing and validation.

/**
 * Ensure parser is followed by another parser
 * @param {Parser|string|RegExp} x - Parser, string, or regex that should follow
 * @returns {Parser} Parser that checks lookahead
 */
parser.lookahead(x);

/**
 * Ensure parser is not followed by another parser
 * @param {Parser} x - Parser that should not follow
 * @returns {Parser} Parser that checks negative lookahead
 */
parser.notFollowedBy(x);

/**
 * Wrap parser between left and right parsers
 * @param {Parser} left - Parser for left delimiter
 * @param {Parser} right - Parser for right delimiter
 * @returns {Parser} Parser that returns middle value
 */
parser.wrap(left, right);

/**
 * Trim parser with whitespace (or specified parser)
 * @param {Parser} [parser] - Parser to use for trimming (default: optWhitespace)
 * @returns {Parser} Parser with trimming
 */
parser.trim(parser);

/**
 * Provide custom error description
 * @param {string|string[]} description - Error description
 * @returns {Parser} Parser with custom error messages
 */
parser.desc(description);

Usage Examples:

// Lookahead validation
const keyword = Parsimmon.string("if")
  .notFollowedBy(Parsimmon.regexp(/[a-zA-Z0-9_]/));

keyword.parse("if"); // Success
keyword.parse("ifdef"); // Fails

// Wrap between delimiters
const parenthesized = Parsimmon.digits.wrap(
  Parsimmon.string("("),
  Parsimmon.string(")")
);
parenthesized.parse("(123)"); // { status: true, value: "123" }

// Trim whitespace
const trimmedNumber = Parsimmon.digits.trim(Parsimmon.optWhitespace);
trimmedNumber.parse("  42  "); // { status: true, value: "42" }

// Custom error messages
const email = Parsimmon.regexp(/[^@]+@[^@]+/)
  .desc("valid email address");

email.parse("invalid"); // Error mentions "valid email address"

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