A monadic LL(infinity) parser combinator library
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Built-in parsers for common string and character matching patterns. These provide convenient shortcuts for frequently used parsing operations.
Ready-to-use parsers for common character types and patterns.
// Single character parsers
Parsimmon.any; // Matches any single character
Parsimmon.digit; // Matches single digit [0-9]
Parsimmon.letter; // Matches single letter [a-zA-Z]
// Multiple character parsers
Parsimmon.digits; // Matches zero or more digits
Parsimmon.letters; // Matches zero or more letters
Parsimmon.whitespace; // Matches one or more whitespace characters
Parsimmon.optWhitespace; // Matches zero or more whitespace charactersUsage Examples:
// Parse single characters
Parsimmon.digit.parse("5"); // { status: true, value: "5" }
Parsimmon.letter.parse("a"); // { status: true, value: "a" }
Parsimmon.any.parse("@"); // { status: true, value: "@" }
// Parse multiple characters
Parsimmon.digits.parse("123"); // { status: true, value: "123" }
Parsimmon.letters.parse("hello"); // { status: true, value: "hello" }
Parsimmon.whitespace.parse(" \t"); // { status: true, value: " \t" }
// Combining with other parsers
const word = Parsimmon.letters.skip(Parsimmon.optWhitespace);
const number = Parsimmon.digits.map(Number);Parsers for different line ending formats across platforms.
Parsimmon.cr; // Matches carriage return (\r)
Parsimmon.lf; // Matches line feed (\n)
Parsimmon.crlf; // Matches Windows line ending (\r\n)
Parsimmon.newline; // Matches any line ending (CR, LF, or CRLF)
Parsimmon.end; // Matches newline or end of inputUsage Examples:
// Parse different line endings
Parsimmon.newline.parse("\n"); // { status: true, value: "\n" }
Parsimmon.newline.parse("\r\n"); // { status: true, value: "\r\n" }
Parsimmon.crlf.parse("\r\n"); // { status: true, value: "\r\n" }
// Use in line-based parsing
const line = Parsimmon.regexp(/[^\r\n]*/).skip(Parsimmon.newline);
const lines = line.many();
lines.parse("line1\nline2\nline3\n");Parsers for special positions and conditions in the input.
Parsimmon.eof; // Matches end of input
Parsimmon.all; // Consumes all remaining input
Parsimmon.index; // Returns current position informationUsage Examples:
// Ensure complete parsing
const completeNumber = Parsimmon.digits.skip(Parsimmon.eof);
completeNumber.parse("123"); // Success
completeNumber.parse("123abc"); // Fails - not at end
// Get all remaining input
const remaining = Parsimmon.string("start:").then(Parsimmon.all);
remaining.parse("start:everything else"); // { status: true, value: "everything else" }
// Track position
const withPosition = Parsimmon.seq(
Parsimmon.index,
Parsimmon.string("hello"),
Parsimmon.index
).map(([start, value, end]) => ({ value, start, end }));Parsers for matching characters from specific sets or ranges.
/**
* Matches any character from the given string
* @param {string} str - String containing valid characters
* @returns {Parser} Parser that matches any character in str
*/
Parsimmon.oneOf(str);
/**
* Matches any character NOT in the given string
* @param {string} str - String containing invalid characters
* @returns {Parser} Parser that matches any character not in str
*/
Parsimmon.noneOf(str);
/**
* Matches any character in the given range (inclusive)
* @param {string} begin - Start character of range
* @param {string} end - End character of range
* @returns {Parser} Parser that matches characters in range
*/
Parsimmon.range(begin, end);Usage Examples:
// Parse vowels
const vowel = Parsimmon.oneOf("aeiouAEIOU");
vowel.parse("a"); // { status: true, value: "a" }
vowel.parse("x"); // { status: false, ... }
// Parse consonants
const consonant = Parsimmon.noneOf("aeiouAEIOU0123456789 \t\n");
consonant.parse("b"); // { status: true, value: "b" }
// Parse letter ranges
const lowercase = Parsimmon.range("a", "z");
const uppercase = Parsimmon.range("A", "Z");
const hexDigit = Parsimmon.oneOf("0123456789abcdefABCDEF");
// Combine ranges
const alphanumeric = Parsimmon.alt(
Parsimmon.range("a", "z"),
Parsimmon.range("A", "Z"),
Parsimmon.range("0", "9")
);Parsers that use custom functions to test characters.
/**
* Matches a single character/byte that satisfies the predicate
* @param {Function} predicate - Function that tests a character
* @returns {Parser} Parser that matches when predicate returns true
*/
Parsimmon.test(predicate);
/**
* Consumes characters while predicate is true
* @param {Function} predicate - Function that tests each character
* @returns {Parser} Parser that consumes matching characters
*/
Parsimmon.takeWhile(predicate);Usage Examples:
// Parse uppercase letters
const isUppercase = (ch) => ch >= "A" && ch <= "Z";
const uppercase = Parsimmon.test(isUppercase);
uppercase.parse("H"); // { status: true, value: "H" }
// Parse identifiers
const isAlphaNum = (ch) => /[a-zA-Z0-9_]/.test(ch);
const identifier = Parsimmon.test(ch => /[a-zA-Z_]/.test(ch))
.then(Parsimmon.takeWhile(isAlphaNum))
.map((first, rest) => first + rest);
// Parse numbers with custom logic
const isDigit = (ch) => ch >= "0" && ch <= "9";
const naturalNumber = Parsimmon.takeWhile(isDigit)
.assert(str => str.length > 0, "at least one digit")
.map(Number);
// Parse until delimiter
const isNotComma = (ch) => ch !== ",";
const csvField = Parsimmon.takeWhile(isNotComma);Parsers that check ahead without consuming input.
/**
* Succeeds if parser would succeed, but consumes no input
* @param {Parser|string|RegExp} x - Parser, string, or regex to look ahead for
* @returns {Parser} Parser that looks ahead without consuming
*/
Parsimmon.lookahead(x);
/**
* Succeeds if parser would fail, consumes no input
* @param {Parser} parser - Parser that should not match
* @returns {Parser} Parser that succeeds when parser fails
*/
Parsimmon.notFollowedBy(parser);Usage Examples:
// Parse numbers not followed by letters
const numberNotInWord = Parsimmon.digits
.skip(Parsimmon.notFollowedBy(Parsimmon.letter));
numberNotInWord.parse("123"); // Success
numberNotInWord.parse("123abc"); // Fails
// Look ahead for keywords
const keywordIf = Parsimmon.string("if")
.skip(Parsimmon.lookahead(Parsimmon.regexp(/\s/)));
keywordIf.parse("if "); // Success
keywordIf.parse("ifelse"); // Fails
// Conditional parsing based on lookahead
const stringOrNumber = Parsimmon.lookahead(Parsimmon.digit)
.then(Parsimmon.digits.map(Number))
.or(Parsimmon.letters);Install with Tessl CLI
npx tessl i tessl/npm-parsimmon