A monadic LL(infinity) parser combinator library
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
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.
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
});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)
);
});When using createLanguage, organize your grammar rules logically:
Expr, Program, or similar).desc() calls for better error messagesFor recursive structures:
lazy() when a parser needs to reference itself before it's fully definedcreateLanguage() when you have multiple interdependent rulesBoth 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 failsInstall with Tessl CLI
npx tessl i tessl/npm-parsimmon