You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

74 lines
1.7 KiB
JavaScript

"use strict";
const pegjs = require("pegjs");
const { validateOptions, either, required, isString, isPlainObject, allowExtraProperties } = require("validatem");
const fs = require("fs");
const moduleEval = require("eval");
const vm = require("vm");
const asExpression = require("as-expression");
const textParser = require("../text-parser");
module.exports = function createPegParser({ grammar, grammarFile, options }) {
validateOptions(arguments, [
{
grammar: [ isString ],
grammarFile: [ isString ],
options: [ isPlainObject ]
},
// FIXME: require-either
either(
allowExtraProperties({ grammar: [ required ] }),
allowExtraProperties({ grammarFile: [ required ] })
)
]);
if (grammarFile != null) {
// FIXME: cache
grammar = fs.readFileSync(grammarFile, "utf8");
}
let parserCode = pegjs.generate(grammar, {
... options,
output: "source",
format: "commonjs"
});
let parser = asExpression(() => {
if (grammarFile != null) {
return moduleEval(parserCode, grammarFile, {}, true);
} else {
let exports_ = {};
let sandbox = {
exports: exports_,
module: {
exports: exports_,
},
require: function () {
throw new Error("You cannot use require() when loading a grammar as a string; use the `grammarFile` option instead");
}
};
let script = new vm.Script(parserCode.replace(/^\#\!.*/, ''));
script.runInNewContext(sandbox);
return sandbox.module.exports;
}
});
return {
supportsStreams: false,
parse: function (text) {
try {
return parser.parse(text);
} catch (error) {
if (error.name === "SyntaxError") {
throw textParser.NoResult.chain(error, "Parsing output failed");
} else {
throw error;
}
}
}
};
};