Parsimmon Parser Combinator Library
Parsimmon is a compact, monadic LL(infinity) parser combinator library for JavaScript, enabling the construction of complex parsers from smaller, composable units. It is currently at version 1.18.1 and supports both modern Node.js environments and older browser versions (down to IE7). Its release cadence appears to be infrequent, with the last major feature release (1.7.0) being in 2018, though bug fixes and minor updates have occurred more recently. Key differentiators include its inspiration from Haskell's Parsec and Promises/A+ for its API design, its support for binary parsing using Node.js Buffers, and its compatibility with the Fantasy Land specification, implementing several algebraic structures like Semigroup, Apply, Applicative, Functor, Chain, and Monad. It distinguishes itself by offering a functional and declarative approach to parsing, making grammars easy to read and maintain.
Common errors
-
TypeError: Cannot read properties of undefined (reading 'string')
cause Attempting to destructure static methods from the `parsimmon` module in an ESM context (e.g., `import { string } from 'parsimmon';`) when the module is a CJS default export.fixImport the `Parsimmon` object as a default import and access methods via `Parsimmon.string` (e.g., `import Parsimmon from 'parsimmon'; const myParser = Parsimmon.string('hello');`). -
ReferenceError: Parsimmon is not defined
cause In a browser environment, the `Parsimmon` global is not available because the UMD build was not loaded via a script tag, or in a Node.js environment where `require()` or `import` was not used correctly.fixFor browsers, ensure a `<script>` tag loading the UMD build (e.g., from unpkg) is present before attempting to use `Parsimmon`. For Node.js, use `const Parsimmon = require('parsimmon');` or `import Parsimmon from 'parsimmon';` to import the library. -
Error: Expected a character in the range 'x' to 'y' but got "..."
cause A parser combinator like `Parsimmon.range(begin, end)` or `Parsimmon.letter` encountered an unexpected character in the input string, indicating a mismatch between the parser's expectation and the actual data.fixInspect the input string for unexpected characters or malformed data. Adjust the parser definition to correctly handle the expected input format, or add `.or()` combinators to handle alternative valid patterns. The error message from `parser.parse(input)` usually provides line and column details.
Warnings
- gotcha Parsimmon is primarily a CommonJS module with UMD builds for browsers. While Node.js can import CommonJS modules using `import`, it only supports default imports for the entire module. Directly named imports for static methods (e.g., `import { string } from 'parsimmon'`) will not work as expected in an ESM context.
- breaking In version 1.0.0, `parser.empty` was changed from a property to a function (`parser.empty()`), and `f.ap(x)` was changed to `x.ap(f)` to align with Fantasy Land specifications. Code using the old syntax for these specific combinators will break.
- breaking In version 0.9.1, `Parsimmon.seqMap` began throwing an error when called with zero arguments or if the final argument was not a function. This enforces correct usage of the combinator.
- breaking In version 0.9.0, `Parsimmon.regexp` (aliased as `P.regex` previously) was updated to throw an error if the regular expression included flags other than `i` (case-insensitive), `m` (multiline), or `u` (unicode). This might affect parsers relying on unsupported regex flags.
- gotcha The library's development seems less active recently, with the latest significant feature release (v1.7.0) being in 2018, though maintenance updates exist. While stable and well-tested (100% coverage since 1.6.1), rapid introduction of new features or quick resolution of complex issues might not be expected.
Install
-
npm install parsimmon -
yarn add parsimmon -
pnpm add parsimmon
Imports
- Parsimmon
const Parsimmon = require('parsimmon').Parsimmonimport Parsimmon from 'parsimmon'
- Parsimmon.string
import { string } from 'parsimmon';import Parsimmon from 'parsimmon'; const myParser = Parsimmon.string('hello'); - parser.map
import { map } from 'parsimmon'; map(parser, fn);import Parsimmon from 'parsimmon'; const myParser = Parsimmon.string('hello').map(s => s.toUpperCase()); - Parsimmon.createLanguage
const { createLanguage } = require('parsimmon');import Parsimmon from 'parsimmon'; const language = Parsimmon.createLanguage({});
Quickstart
import Parsimmon from 'parsimmon';
// Define basic parsers
const digit = Parsimmon.regexp(/[0-9]/);
const plus = Parsimmon.string('+');
const minus = Parsimmon.string('-');
// Combine parsers to recognize an integer (one or more digits)
const integer = digit.atLeast(1).map(digits => parseInt(digits.join(''), 10));
// Combine parsers to recognize an operator
const operator = plus.or(minus);
// Define a parser for a simple arithmetic expression like '123+45'
const expression = Parsimmon.seq(integer, operator, integer).map(
([left, op, right]) => {
if (op === '+') return left + right;
if (op === '-') return left - right;
return NaN; // Should not happen with defined operators
}
);
// Parse some input strings
const result1 = expression.parse('123+45');
const result2 = expression.parse('99-10');
const result3 = expression.parse('5*2'); // Will fail
console.log('Result for "123+45":', result1);
console.log('Result for "99-10":', result2);
console.log('Result for "5*2" (expected failure):', result3);
// Example of parsing a more complex structure (e.g., a comma-separated list of numbers)
const comma = Parsimmon.string(',');
const listOfNumbers = integer.sepBy(comma).map(nums => nums.join(', '));
const listResult = listOfNumbers.parse('1,2,3,4,5');
console.log('List of numbers for "1,2,3,4,5":', listResult);