Peggy/PEG.js Utility Library
pegjs-util is a utility library for the Peggy parser generator (formerly PEG.js), currently at stable version 2.0.2. It enhances Peggy's core `parse` function by injecting convenient utilities directly into grammar actions. The library provides three main features: Parser Tree Token Unrolling, which simplifies common patterns of extracting tokens from repeated grammar rule matches; Abstract Syntax Tree (AST) Node Generation, which assists in building structured ASTs directly within grammar rules; and improved, "cooked" Error Reporting, offering more user-friendly diagnostics than Peggy's default output. Releases appear to follow the development of Peggy itself, with the latest versions published as needed. It differentiates itself by streamlining common parser generator tasks, reducing boilerplate in `.peggy` grammar files, and providing a more robust parsing and error reporting experience.
Common errors
-
ERROR: Parsing Failure:\nERROR: line X (column Y): ...\nERROR: ---^\nERROR: Expected "Z" or end of input but "A" found.
cause The input string does not conform to the defined Peggy grammar, leading to a parsing error reported by pegjs-util's enhanced error handling.fixExamine the error message, specifically the expected tokens and the found character at the indicated line and column, and correct the input string or adjust the grammar definition. -
TypeError: Cannot read properties of undefined (reading 'util')
cause This error occurs within a Peggy grammar action (e.g., when calling `options.util.makeUnroll` or `options.util.makeAST`) if the parser was not invoked using `PEGUtil.parse`. `PEGUtil.parse` is responsible for injecting the `util` object into the options passed to grammar actions.fixEnsure that your parser execution uses `PEGUtil.parse(parser, input, options)` instead of Peggy's direct `parser.parse(input, options)`. -
ReferenceError: asty is not defined
cause This error typically occurs in the `makeAST` callback provided to `PEGUtil.parse` if the `asty` library is used within that callback but has not been properly imported or installed.fixInstall the `asty` package (`npm install asty`) and ensure it is correctly imported (`const ASTY = require('asty');`) in the file where the `makeAST` callback is defined.
Warnings
- gotcha pegjs-util is published as a CommonJS module. It does not officially support native ES module (ESM) imports (e.g., `import PEGUtil from 'pegjs-util'`) in its current version (2.0.2). Users attempting ESM imports may encounter errors.
- gotcha The `makeUnroll` and `makeAST` utilities are only injected into the grammar's `options.util` object when parsing via `PEGUtil.parse`. If you use Peggy's standard `PEG.parse` method directly, these utilities will be undefined within your grammar rules, leading to runtime errors like `options.util is undefined`.
- gotcha While `pegjs-util`'s examples extensively use `asty` for Abstract Syntax Tree (AST) generation, `asty` is not a direct runtime dependency of `pegjs-util`. Users who wish to utilize the AST generation features shown in the examples must explicitly install `asty` (i.e., `npm install asty`) in their project.
- breaking The underlying parser generator transitioned from 'PEG.js' to 'Peggy'. While `pegjs-util` is compatible with 'Peggy', older projects or grammars specifically tied to 'PEG.js' (pre-1.0 versions) might require updates to `peggy` and potentially slight grammar adjustments.
Install
-
npm install pegjs-util -
yarn add pegjs-util -
pnpm add pegjs-util
Imports
- PEGUtil
import PEGUtil from 'pegjs-util';
const PEGUtil = require('pegjs-util'); - makeUnroll
import { makeUnroll } from 'pegjs-util';var unroll = options.util.makeUnroll(location, options);
- makeAST
import { makeAST } from 'pegjs-util';var ast = options.util.makeAST(location, options);
Quickstart
const fs = require("fs");
const ASTY = require("asty"); // Companion library, install separately if needed
const PEG = require("peggy");
const PEGUtil = require("pegjs-util");
const pegjsGrammar = `
{
var unroll = options.util.makeUnroll(location, options);
var ast = options.util.makeAST(location, options);
}
start
= _ seq:id_seq _ {
return ast("Sample").add(seq);
}
id_seq
= id:id ids:(_ "," _ id)* {
return ast("IdentifierSequence").add(unroll(id, ids, 3));
}
id
= id:$([a-zA-Z_][a-zA-Z0-9_]*) {
return ast("Identifier").set("name", id);
}
_ "blank"
= (co / ws)*
co "comment"
= "//" (![\r\n] .)*
/ "/*" (!"*/" .)* "*/"
ws "whitespaces"
= [ \t\r\n]+
`;
const asty = new ASTY();
const parser = PEG.generate(pegjsGrammar);
// Simulate input for parsing
const sampleInputOk = "/* some ok input */\nfoo, bar, quux";
const sampleInputBad = "/* some bad input */\nfoo, bar, quux baz";
function parseInput(inputString, fileName) {
console.log(`\n--- Parsing ${fileName || 'input'} ---`);
const result = PEGUtil.parse(parser, inputString, {
startRule: "start",
makeAST: function (line, column, offset, args) {
// This callback is what the 'ast' helper in the grammar calls
return asty.create.apply(asty, args).pos(line, column, offset);
}
});
if (result.error !== null) {
console.error("ERROR: Parsing Failure:\n" +
PEGUtil.errorMessage(result.error, true).replace(/^/mg, "ERROR: "));
} else {
console.log(result.ast.dump().replace(/\n$/, ""));
}
}
// To run this code, ensure you have installed:
// npm install peggy asty pegjs-util
parseInput(sampleInputOk, "sample-input-ok.txt");
parseInput(sampleInputBad, "sample-input-bad.txt");