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.

271 lines
7.1 KiB
JavaScript

#!/usr/bin/env node
"use strict";
var fs = require("fs");
var path = require("path");
var PEG = require("../lib/peg");
/* Helpers */
function printVersion() {
console.log("PEG.js " + PEG.VERSION);
}
function printHelp() {
console.log("Usage: pegjs [options] [--] [<input_file>] [<output_file>]");
console.log("");
console.log("Generates a parser from the PEG grammar specified in the <input_file> and writes");
console.log("it to the <output_file>.");
console.log("");
console.log("If the <output_file> is omitted, its name is generated by changing the");
console.log("<input_file> extension to \".js\". If both <input_file> and <output_file> are");
console.log("omitted, standard input and output are used.");
console.log("");
console.log("Options:");
console.log(" -e, --export-var <variable> name of the variable where the parser");
console.log(" object will be stored (default:");
console.log(" \"module.exports\")");
console.log(" --cache make generated parser cache results");
console.log(" --allowed-start-rules <rules> comma-separated list of rules the generated");
console.log(" parser will be allowed to start parsing");
console.log(" from (default: the first rule in the");
console.log(" grammar)");
console.log(" -o, --optimize <goal> select optimization for speed or size");
console.log(" (default: speed)");
console.log(" --trace enable tracing in generated parser");
console.log(" --plugin <plugin> use a specified plugin (can be specified");
console.log(" multiple times)");
console.log(" --extra-options <options> additional options (in JSON format) to pass");
console.log(" to PEG.buildParser");
console.log(" --extra-options-file <file> file with additional options (in JSON");
console.log(" format) to pass to PEG.buildParser");
console.log(" -v, --version print version information and exit");
console.log(" -h, --help print help and exit");
}
function exitSuccess() {
process.exit(0);
}
function exitFailure() {
process.exit(1);
}
function abort(message) {
console.error(message);
exitFailure();
}
function addExtraOptions(options, json) {
var extraOptions;
try {
extraOptions = JSON.parse(json);
} catch (e) {
if (!(e instanceof SyntaxError)) { throw e; }
abort("Error parsing JSON: " + e.message);
}
if (typeof extraOptions !== "object") {
abort("The JSON with extra options has to represent an object.");
}
for (var key in extraOptions) {
if (extraOptions.hasOwnProperty(key)) {
options[key] = extraOptions[key];
}
}
}
/*
* Extracted into a function just to silence JSHint complaining about creating
* functions in a loop.
*/
function trim(s) {
return s.trim();
}
/* Arguments */
var args = process.argv.slice(2); // Trim "node" and the script path.
function isOption(arg) {
return (/^-/).test(arg);
}
function nextArg() {
args.shift();
}
/* Files */
function readStream(inputStream, callback) {
var input = "";
inputStream.on("data", function(data) { input += data; });
inputStream.on("end", function() { callback(input); });
}
/* Main */
/* This makes the generated parser a CommonJS module by default. */
var exportVar = "module.exports";
var options = {
cache: false,
output: "source",
optimize: "speed",
trace: false,
plugins: []
};
while (args.length > 0 && isOption(args[0])) {
switch (args[0]) {
case "-e":
case "--export-var":
nextArg();
if (args.length === 0) {
abort("Missing parameter of the -e/--export-var option.");
}
exportVar = args[0];
break;
case "--cache":
options.cache = true;
break;
case "--allowed-start-rules":
nextArg();
if (args.length === 0) {
abort("Missing parameter of the -e/--allowed-start-rules option.");
}
options.allowedStartRules = args[0]
.split(",")
.map(trim);
break;
case "--trace":
options.trace = true;
break;
case "-o":
case "--optimize":
nextArg();
if (args.length === 0) {
abort("Missing parameter of the -o/--optimize option.");
}
if (args[0] !== "speed" && args[0] !== "size") {
abort("Optimization goal must be either \"speed\" or \"size\".");
}
options.optimize = args[0];
break;
case "--plugin":
nextArg();
if (args.length === 0) {
abort("Missing parameter of the --plugin option.");
}
var id = /^(\.\/|\.\.\/)/.test(args[0]) ? path.resolve(args[0]) : args[0];
var mod;
try {
mod = require(id);
} catch (e) {
if (e.code !== "MODULE_NOT_FOUND") { throw e; }
abort("Can't load module \"" + id + "\".");
}
options.plugins.push(mod);
break;
case "--extra-options":
nextArg();
if (args.length === 0) {
abort("Missing parameter of the --extra-options option.");
}
addExtraOptions(options, args[0]);
break;
case "--extra-options-file":
nextArg();
if (args.length === 0) {
abort("Missing parameter of the --extra-options-file option.");
}
try {
var json = fs.readFileSync(args[0]);
} catch(e) {
abort("Can't read from file \"" + args[0] + "\".");
}
addExtraOptions(options, json);
break;
case "-v":
case "--version":
printVersion();
exitSuccess();
break;
case "-h":
case "--help":
printHelp();
exitSuccess();
break;
case "--":
nextArg();
break;
default:
abort("Unknown option: " + args[0] + ".");
}
nextArg();
}
switch (args.length) {
case 0:
process.stdin.resume();
var inputStream = process.stdin;
var outputStream = process.stdout;
break;
case 1:
case 2:
var inputFile = args[0];
var inputStream = fs.createReadStream(inputFile);
inputStream.on("error", function() {
abort("Can't read from file \"" + inputFile + "\".");
});
var outputFile = args.length === 1
? args[0].replace(/\.[^.]*$/, ".js")
: args[1];
var outputStream = fs.createWriteStream(outputFile);
outputStream.on("error", function() {
abort("Can't write to file \"" + outputFile + "\".");
});
break;
default:
abort("Too many arguments.");
}
readStream(inputStream, function(input) {
var source;
try {
source = PEG.buildParser(input, options);
} catch (e) {
if (e.location !== undefined) {
abort(e.location.start.line + ":" + e.location.start.column + ": " + e.message);
} else {
abort(e.message);
}
}
outputStream.write(
exportVar !== "" ? exportVar + " = " + source + ";\n" : source + "\n"
);
if (outputStream !== process.stdout) {
outputStream.end();
}
});