Skip to content

Commit

Permalink
Fixes #161. Adds a -c CLI option that also accepts a JS module whose …
Browse files Browse the repository at this point in the history
…name ends in .js or .cjs
  • Loading branch information
hildjj committed Jun 14, 2021
1 parent 9f69170 commit 07dfb9d
Show file tree
Hide file tree
Showing 7 changed files with 265 additions and 139 deletions.
26 changes: 24 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,8 @@ You can tweak the generated parser with several options:
parser object is assigned to when no module loader is detected
- `--extra-options <options>` — additional options (in JSON format, as an
object) to pass to `peg.generate`
- `--extra-options-file <file>` — file with additional options (in JSON
format, as an object) to pass to `peg.generate`
- `-c`, `--extra-options-file <file>` — file with additional options (in JSON
or commonjs module format, as an object) to pass to `peg.generate`
- `--format <format>` — format of the generated parser: `amd`, `es`, `commonjs`,
`globals`, `umd` (default: `commonjs`)
- `-o`, `--output <file>` - file to send output to. Defaults to input file
Expand All @@ -128,6 +128,28 @@ You can tweak the generated parser with several options:
- `-v`, `--version` - output the version number
- `-h`, `--help` - display help for command

If you specify options using `-c <file>` or `--extra-options-file <file>`, you
will need to ensure you are using the correct types. In particular, you may
specify "plugin" as a string, or "plugins" as an array of objects that have a
`use` method. Always use the long (two-dash) form of the option. Options
that contain dashes should be specified in camel case. You may also specify an
"input" field instead of using the command line.

For example:

```js
// config.js or config.cjs
module.exports = {
allowedStartRules = ["foo", "bar"],
format: "umd",
exportVar: "foo",
input: "fooGrammar.peggy",
plugins: [require("./plugin.js")],
testFile: "myTestInput.foo",
trace: true
};
```

### JavaScript API

In Node.js, require the Peggy parser generator module:
Expand Down
130 changes: 78 additions & 52 deletions bin/peggy.js
Original file line number Diff line number Diff line change
Expand Up @@ -2237,6 +2237,8 @@ function incrementNodeInspectorPort(args) {

const MODULE_FORMATS = ["amd", "commonjs", "es", "globals", "umd"];
const MODULE_FORMATS_WITH_DEPS = ["amd", "commonjs", "es", "umd"];
const DEFAULT_FORMAT = "COMMON_JS";
let verbose = false;

// Helpers

Expand All @@ -2245,6 +2247,22 @@ function abort(message) {
process.exit(1);
}

function abortError(msg, error) {
console.error(msg);
if (typeof error.format === "function") {
abort(error.format([{
source: testGrammarSource,
text: testText,
}]));
} else {
if (verbose) {
abort(error);
} else {
abort(`Error: ${error.message}`);
}
}
}

function addExtraOptions(options, json) {
let extraOptions;

Expand All @@ -2253,7 +2271,7 @@ function addExtraOptions(options, json) {
} catch (e) {
if (!(e instanceof SyntaxError)) { throw e; }

abort("Error parsing JSON: " + e.message);
abortError("Error parsing JSON:", e);
}
if ((extraOptions === null)
|| (typeof extraOptions !== "object")
Expand All @@ -2263,20 +2281,28 @@ function addExtraOptions(options, json) {
return Object.assign({}, options, extraOptions);
}

// Files

function readStream(inputStream, callback) {
let input = "";
inputStream.on("data", data => { input += data; });
inputStream.on("end", () => { callback(input); });
}

function readFile(name) {
let f = null;
try {
f = fs__default['default'].readFileSync(name, "utf8");
} catch (e) {
abort(`Can't read from file "${name}".`);
abortError(`Can't read from file "${name}".`, e);
}
return f;
}

// Command line processing

const program = new commander.exports.Command();
const options = program
let options = program
.version(peg__default['default'].VERSION, "-v, --version")
.arguments("[input_file]")
.addOption(
Expand Down Expand Up @@ -2306,17 +2332,23 @@ const options = program
(val, prev) => addExtraOptions(prev, val)
)
.option(
"--extra-options-file <file>",
"file with additional options (in JSON format as an object) to pass to peg.generate",
(val, prev) => addExtraOptions(prev, readFile(val))
"-c, --extra-options-file <file>",
"file with additional options (in JSON or commonjs module format as an object) to pass to peg.generate",
(val, prev) => {
if (/\.c?js$/.test(val)) {
return Object.assign({}, prev, require(path__default['default'].resolve(val)));
} else {
return addExtraOptions(prev, readFile(val));
}
}
)
.addOption(
new commander.exports.Option(
"--format <format>",
"format of the generated parser"
)
.choices(MODULE_FORMATS)
.default("commonjs")
.default(DEFAULT_FORMAT, '"commonjs"')
)
.option("-o, --output <file>", "output file")
.option(
Expand All @@ -2342,7 +2374,6 @@ const options = program
.addOption(
new commander.exports.Option("-O, --optimize <style>")
.hideHelp()
.default("speed")
.argParser(() => {
console.error("Option --optimize is deprecated from 1.2.0 and has no effect anymore.");
console.error("It will be deleted in 2.0.");
Expand All @@ -2357,22 +2388,36 @@ if (options.extraOptions) {
Object.assign(options, options.extraOptions);
delete options.extraOptions;
}
if (options.extraOptionsFile) {
Object.assign(options, options.extraOptionsFile);
delete options.extraOptionsFile;
}

const verbose = options.verbose;
delete options.verbose;

// Ensure defaults do not clobber config file
if (options.allowedStartRules.length === 0) {
// [] is an invalid input, as is null
// undefined doesn't work as a default in commander
delete options.allowedStartRules;
}

if (options.extraOptionsFile) {
// Ensure defaults do not clobber config file
const fmt = options.format;
delete options.format;

// Command line overwrites config file
options = Object.assign({}, options.extraOptionsFile, options);
delete options.extraOptionsFile;

if (fmt !== DEFAULT_FORMAT) {
options.format = fmt;
}
}
if (!options.format || (options.format === DEFAULT_FORMAT)) {
options.format = "commonjs";
}

verbose = Boolean(options.verbose);
delete options.verbose;

if (options.plugin) {
options.plugins = options.plugin.map(val => {
options.plugins = (options.plugins || []).concat(options.plugin.map(val => {
// If this is an absolute or relative path (not a module name)
const id = (path__default['default'].isAbsolute(val) || /^\.\.?[/\\]/.test(val))
? path__default['default'].resolve(val)
Expand All @@ -2382,10 +2427,10 @@ if (options.plugin) {
mod = require(id);
} catch (e) {
if (e.code !== "MODULE_NOT_FOUND") { throw e; }
abort("Can't load module \"" + id + "\".");
abortError(`Can't load module "${id}".`, e);
}
return mod;
});
}));
delete options.plugin;
}

Expand All @@ -2409,19 +2454,23 @@ if (options.exportVar !== undefined) {
}
}

let inputFile = null;
let inputFile = options.input;
switch (program.args.length) {
case 0:
inputFile = "-";
inputFile = inputFile ? inputFile : "-";
break;

case 1:
if (inputFile) {
abort("Do not specify input both on command line and in config file.");
}
inputFile = program.args[0];
break;

default:
abort("Too many arguments.");
}

let outputFile = options.output;
if (!outputFile) {
outputFile = ((inputFile === "-") || options.test || options.testFile)
Expand All @@ -2448,14 +2497,13 @@ if (options.testFile) {
}

if (verbose) {
console.error("OPTIONS:", options);
}
// Files

function readStream(inputStream, callback) {
let input = "";
inputStream.on("data", data => { input += data; });
inputStream.on("end", () => { callback(input); });
console.error("OPTIONS:", util__default['default'].inspect(options, {
depth: Infinity,
colors: process.stdout.isTTY,
}));
console.error("INPUT:", inputFile);
console.error("OUTPUT:", outputFile);
console.error("TEST TEXT:", testText);
}

// Main
Expand All @@ -2480,18 +2528,7 @@ readStream(inputStream, input => {
try {
source = peg__default['default'].generate(input, options);
} catch (e) {
if (typeof e.format === "function") {
abort(e.format([{
source: options.grammarSource,
text: input,
}]));
} else {
if (verbose) {
abort(e);
} else {
abort(`Error: ${e.message}`);
}
}
abortError("Error parsing grammar:", e);
}

if (testText) {
Expand All @@ -2500,18 +2537,7 @@ readStream(inputStream, input => {
grammarSource: testGrammarSource,
});
} catch (e) {
if (typeof e.format === "function") {
abort(e.format([{
source: testGrammarSource,
text: testText,
}]));
} else {
if (verbose) {
abort(e);
} else {
abort(`Error: ${e.message}`);
}
}
abortError("Error parsing test:", e);
}
source = util__default['default'].inspect(source, {
depth: Infinity,
Expand Down
Loading

0 comments on commit 07dfb9d

Please sign in to comment.