Browse Source
- Split into 3 files: "peg.js", "options.js" and "usage.txt" - Rewrote arguments parser and helpers to be more precise - Any arguments after "--" will be passed to "options['--']" now - Added negation options: "--no-cache" and "--no-trace" - Added "bare" to accepted module formats - Added 2 aliases for "--extra-options-file": "-c" and "--config" - Added short options: "-a", "-f" and "-p" - Reformatted help text in "usage.txt" - Updated documentation related to command line options and normal options - Changed "bin" field in "package.json" from "bin/pegjs" to "bin/peg" - Added documentation note about command line options that are repeated - Updated gulpfile.js, replacing "bin/pegjs" with "bin/*.js" See #429, which was what I intended to fix/solve, but instead pushed back and did this instead.master
7 changed files with 404 additions and 369 deletions
@ -0,0 +1,260 @@ |
|||
"use strict"; |
|||
|
|||
let fs = require("fs"); |
|||
let path = require("path"); |
|||
let peg = require("../"); |
|||
|
|||
// Options
|
|||
|
|||
let inputFile = null; |
|||
let outputFile = null; |
|||
|
|||
let options = { |
|||
"--": [], |
|||
"cache": false, |
|||
"dependencies": {}, |
|||
"exportVar": null, |
|||
"format": "commonjs", |
|||
"optimize": "speed", |
|||
"output": "source", |
|||
"plugins": [], |
|||
"trace": false |
|||
}; |
|||
|
|||
const EXPORT_VAR_FORMATS = ["globals", "umd"]; |
|||
const DEPENDENCY_FORMATS = ["amd", "commonjs", "es", "umd"]; |
|||
const MODULE_FORMATS = ["amd", "bare", "commonjs", "es", "globals", "umd"]; |
|||
const OPTIMIZATION_GOALS = ["size", "speed"]; |
|||
|
|||
// Helpers
|
|||
|
|||
function abort(message) { |
|||
console.error(message); |
|||
process.exit(1); |
|||
} |
|||
|
|||
function addExtraOptions(json) { |
|||
let 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."); |
|||
} |
|||
|
|||
Object |
|||
.keys(extraOptions) |
|||
.forEach(key => { |
|||
options[key] = extraOptions[key]; |
|||
}); |
|||
} |
|||
|
|||
function formatChoicesList(list) { |
|||
list = list.map(entry => `"${entry}"`); |
|||
let lastOption = list.pop(); |
|||
|
|||
return list.length === 0 |
|||
? lastOption |
|||
: list.join(", ") + " or " + lastOption; |
|||
} |
|||
|
|||
function updateList(list, string) { |
|||
string |
|||
.split(",") |
|||
.forEach(entry => { |
|||
entry = entry.trim(); |
|||
if (list.indexOf(entry) === -1) { |
|||
list.push(entry); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
// Arguments
|
|||
|
|||
let args = process.argv.slice(2); |
|||
|
|||
function nextArg(option) { |
|||
if (args.length === 0) { |
|||
abort(`Missing parameter of the ${option} option.`); |
|||
} |
|||
|
|||
return args.shift(); |
|||
} |
|||
|
|||
// Parse Arguments
|
|||
|
|||
while (args.length > 0) { |
|||
let json, mod; |
|||
let argument = args.shift(); |
|||
|
|||
if (argument.indexOf("-") === 0 && argument.indexOf("=") > 1) { |
|||
argument = argument.split("="); |
|||
args.unshift(argument.length > 2 ? argument.slice(1) : argument[1]); |
|||
argument = argument[0]; |
|||
} |
|||
|
|||
switch (argument) { |
|||
|
|||
case "--": |
|||
options["--"] = args; |
|||
args = []; |
|||
break; |
|||
|
|||
case "-a": |
|||
case "--allowed-start-rules": |
|||
if (!options.allowedStartRules) { |
|||
options.allowedStartRules = []; |
|||
} |
|||
updateList(options.allowedStartRules, nextArg("--allowed-start-rules")); |
|||
break; |
|||
|
|||
case "--cache": |
|||
options.cache = true; |
|||
break; |
|||
|
|||
case "--no-cache": |
|||
options.cache = false; |
|||
break; |
|||
|
|||
case "-d": |
|||
case "--dependency": |
|||
argument = nextArg("-d/--dependency"); |
|||
if (argument.indexOf(":") === -1) { |
|||
mod = [argument, argument]; |
|||
} else { |
|||
mod = argument.split(":"); |
|||
if (mod.length > 2) { |
|||
mod[1] = mod.slice(1); |
|||
} |
|||
} |
|||
options.dependencies[mod[0]] = mod[1]; |
|||
break; |
|||
|
|||
case "-e": |
|||
case "--export-var": |
|||
options.exportVar = nextArg("-e/--export-var"); |
|||
break; |
|||
|
|||
case "--extra-options": |
|||
addExtraOptions(nextArg("--extra-options")); |
|||
break; |
|||
|
|||
case "-c": |
|||
case "--config": |
|||
case "--extra-options-file": |
|||
argument = nextArg("-c/--config/--extra-options-file"); |
|||
try { |
|||
json = fs.readFileSync(argument, "utf8"); |
|||
} catch (e) { |
|||
abort(`Can't read from file "${argument}".`); |
|||
} |
|||
addExtraOptions(json); |
|||
break; |
|||
|
|||
case "-f": |
|||
case "--format": |
|||
argument = nextArg("-f/--format"); |
|||
if (MODULE_FORMATS.indexOf(argument) === -1) { |
|||
abort(`Module format must be either ${formatChoicesList(MODULE_FORMATS)}.`); |
|||
} |
|||
options.format = argument; |
|||
break; |
|||
|
|||
case "-h": |
|||
case "--help": |
|||
console.log(fs.readFileSync(path.join(__dirname, "usage.txt"), "utf8").trim()); |
|||
process.exit(); |
|||
break; |
|||
|
|||
case "-O": |
|||
case "--optimize": |
|||
argument = nextArg("-O/--optimize"); |
|||
if (OPTIMIZATION_GOALS.indexOf(argument) === -1) { |
|||
abort(`Optimization goal must be either ${formatChoicesList(OPTIMIZATION_GOALS)}.`); |
|||
} |
|||
options.optimize = argument; |
|||
break; |
|||
|
|||
case "-o": |
|||
case "--output": |
|||
outputFile = nextArg("-o/--output"); |
|||
break; |
|||
|
|||
case "-p": |
|||
case "--plugin": |
|||
argument = nextArg("-p/--plugin"); |
|||
try { |
|||
mod = require(argument); |
|||
} catch (ex1) { |
|||
if (ex1.code !== "MODULE_NOT_FOUND") { throw ex1; } |
|||
|
|||
try { |
|||
mod = require(path.resolve(argument)); |
|||
} catch (ex2) { |
|||
if (ex2.code !== "MODULE_NOT_FOUND") { throw ex2; } |
|||
|
|||
abort(`Can't load module "${argument}".`); |
|||
} |
|||
} |
|||
options.plugins.push(mod); |
|||
break; |
|||
|
|||
case "--trace": |
|||
options.trace = true; |
|||
break; |
|||
|
|||
case "--no-trace": |
|||
options.trace = false; |
|||
break; |
|||
|
|||
case "-v": |
|||
case "--version": |
|||
console.log("PEG.js v" + peg.VERSION); |
|||
process.exit(); |
|||
break; |
|||
|
|||
default: |
|||
if (inputFile !== null) { |
|||
abort(`Unknown option: "${argument}".`); |
|||
} |
|||
inputFile = argument; |
|||
} |
|||
} |
|||
|
|||
// Validation and defaults
|
|||
|
|||
if (Object.keys(options.dependencies).length > 0) { |
|||
if (DEPENDENCY_FORMATS.indexOf(options.format) === -1) { |
|||
abort(`Can't use the -d/--dependency option with the "${options.format}" module format.`); |
|||
} |
|||
} |
|||
|
|||
if (options.exportVar !== null) { |
|||
if (EXPORT_VAR_FORMATS.indexOf(options.format) === -1) { |
|||
abort(`Can't use the -e/--export-var option with the "${options.format}" module format.`); |
|||
} |
|||
} |
|||
|
|||
if (inputFile === null) { |
|||
inputFile = "-"; |
|||
} |
|||
|
|||
if (outputFile === null) { |
|||
if (inputFile === "-") { |
|||
outputFile = "-"; |
|||
} else if (inputFile) { |
|||
outputFile = inputFile.substr(0, inputFile.length - path.extname(inputFile).length) + ".js"; |
|||
} |
|||
} |
|||
|
|||
// Export
|
|||
|
|||
options.inputFile = inputFile; |
|||
options.outputFile = outputFile; |
|||
|
|||
module.exports = options; |
@ -0,0 +1,64 @@ |
|||
#!/usr/bin/env node
|
|||
|
|||
"use strict"; |
|||
|
|||
let fs = require("fs"); |
|||
let peg = require("../lib/peg"); |
|||
let options = require("./options"); |
|||
|
|||
// Helpers
|
|||
|
|||
function readStream(inputStream, callback) { |
|||
let input = ""; |
|||
inputStream.on("data", data => { input += data; }); |
|||
inputStream.on("end", () => { callback(input); }); |
|||
} |
|||
|
|||
function abort(message) { |
|||
console.error(message); |
|||
process.exit(1); |
|||
} |
|||
|
|||
// Main
|
|||
|
|||
let inputStream, outputStream; |
|||
|
|||
if (options.inputFile === "-") { |
|||
process.stdin.resume(); |
|||
inputStream = process.stdin; |
|||
inputStream.on("error", () => { |
|||
abort(`Can't read from file "${options.inputFile}".`); |
|||
}); |
|||
} else { |
|||
inputStream = fs.createReadStream(options.inputFile); |
|||
} |
|||
|
|||
if (options.outputFile === "-") { |
|||
outputStream = process.stdout; |
|||
} else { |
|||
outputStream = fs.createWriteStream(options.outputFile); |
|||
outputStream.on("error", () => { |
|||
abort(`Can't write to file "${options.outputFile}".`); |
|||
}); |
|||
} |
|||
|
|||
readStream(inputStream, input => { |
|||
let location, source; |
|||
|
|||
try { |
|||
source = peg.generate(input, options); |
|||
} catch (e) { |
|||
if (e.location !== undefined) { |
|||
location = e.location.start; |
|||
abort(location.line + ":" + location.column + ": " + e.message); |
|||
} else { |
|||
abort(e.message); |
|||
} |
|||
} |
|||
|
|||
outputStream.write(source); |
|||
if (outputStream !== process.stdout) { |
|||
outputStream.end(); |
|||
} |
|||
}); |
|||
|
@ -1,329 +0,0 @@ |
|||
#!/usr/bin/env node |
|||
|
|||
"use strict"; |
|||
|
|||
let fs = require("fs"); |
|||
let path = require("path"); |
|||
let peg = require("../lib/peg"); |
|||
|
|||
// Helpers |
|||
|
|||
function printVersion() { |
|||
console.log("PEG.js " + peg.VERSION); |
|||
} |
|||
|
|||
function printHelp() { |
|||
console.log("Usage: pegjs [options] [--] [<input_file>]"); |
|||
console.log(""); |
|||
console.log("Options:"); |
|||
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(" --cache make generated parser cache results"); |
|||
console.log(" -d, --dependency <dependency> use specified dependency (can be specified"); |
|||
console.log(" multiple times)"); |
|||
console.log(" -e, --export-var <variable> name of a global variable into which the"); |
|||
console.log(" parser object is assigned to when no module"); |
|||
console.log(" loader is detected"); |
|||
console.log(" --extra-options <options> additional options (in JSON format) to pass"); |
|||
console.log(" to peg.generate"); |
|||
console.log(" --extra-options-file <file> file with additional options (in JSON"); |
|||
console.log(" format) to pass to peg.generate"); |
|||
console.log(" --format <format> format of the generated parser: amd, commonjs"); |
|||
console.log(" es, globals, umd (default: commonjs)"); |
|||
console.log(" -h, --help print help and exit"); |
|||
console.log(" -O, --optimize <goal> select optimization for speed or size"); |
|||
console.log(" (default: speed)"); |
|||
console.log(" -o, --output <file> output file"); |
|||
console.log(" --plugin <plugin> use a specified plugin (can be specified"); |
|||
console.log(" multiple times)"); |
|||
console.log(" --trace enable tracing in generated parser"); |
|||
console.log(" -v, --version print version information and exit"); |
|||
} |
|||
|
|||
function exitSuccess() { |
|||
process.exit(0); |
|||
} |
|||
|
|||
function exitFailure() { |
|||
process.exit(1); |
|||
} |
|||
|
|||
function abort(message) { |
|||
console.error(message); |
|||
exitFailure(); |
|||
} |
|||
|
|||
function addExtraOptions(options, json) { |
|||
let 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."); |
|||
} |
|||
|
|||
Object.keys(extraOptions).forEach(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 |
|||
|
|||
let 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) { |
|||
let input = ""; |
|||
inputStream.on("data", data => { input += data; }); |
|||
inputStream.on("end", () => { callback(input); }); |
|||
} |
|||
|
|||
// Main |
|||
|
|||
let inputFile = null; |
|||
let outputFile = null; |
|||
|
|||
let options = { |
|||
cache: false, |
|||
dependencies: {}, |
|||
exportVar: null, |
|||
format: "commonjs", |
|||
optimize: "speed", |
|||
output: "source", |
|||
plugins: [], |
|||
trace: false |
|||
}; |
|||
|
|||
const MODULE_FORMATS = ["amd", "commonjs", "es", "globals", "umd"]; |
|||
const MODULE_FORMATS_WITH_DEPS = ["amd", "commonjs", "es", "umd"]; |
|||
|
|||
while (args.length > 0 && isOption(args[0])) { |
|||
let json, id, mod; |
|||
|
|||
switch (args[0]) { |
|||
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 "--cache": |
|||
options.cache = true; |
|||
break; |
|||
|
|||
case "-d": |
|||
case "--dependency": |
|||
nextArg(); |
|||
if (args.length === 0) { |
|||
abort("Missing parameter of the -d/--dependency option."); |
|||
} |
|||
if (args[0].indexOf(":") !== -1) { |
|||
let parts = args[0].split(":"); |
|||
options.dependencies[parts[0]] = parts[1]; |
|||
} else { |
|||
options.dependencies[args[0]] = args[0]; |
|||
} |
|||
break; |
|||
|
|||
case "-e": |
|||
case "--export-var": |
|||
nextArg(); |
|||
if (args.length === 0) { |
|||
abort("Missing parameter of the -e/--export-var option."); |
|||
} |
|||
options.exportVar = args[0]; |
|||
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 { |
|||
json = fs.readFileSync(args[0]); |
|||
} catch (e) { |
|||
abort("Can't read from file \"" + args[0] + "\"."); |
|||
} |
|||
addExtraOptions(options, json); |
|||
break; |
|||
|
|||
case "--format": |
|||
nextArg(); |
|||
if (args.length === 0) { |
|||
abort("Missing parameter of the --format option."); |
|||
} |
|||
if (MODULE_FORMATS.indexOf(args[0]) === -1) { |
|||
abort("Module format must be one of " + MODULE_FORMATS.map(format => `"${format}"`).join(", ") + "."); |
|||
} |
|||
options.format = args[0]; |
|||
break; |
|||
|
|||
case "-h": |
|||
case "--help": |
|||
printHelp(); |
|||
exitSuccess(); |
|||
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 "-o": |
|||
case "--output": |
|||
nextArg(); |
|||
if (args.length === 0) { |
|||
abort("Missing parameter of the -o/--output option."); |
|||
} |
|||
outputFile = args[0]; |
|||
break; |
|||
|
|||
case "--plugin": |
|||
nextArg(); |
|||
if (args.length === 0) { |
|||
abort("Missing parameter of the --plugin option."); |
|||
} |
|||
id = /^(\.\/|\.\.\/)/.test(args[0]) ? path.resolve(args[0]) : args[0]; |
|||
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 "--trace": |
|||
options.trace = true; |
|||
break; |
|||
|
|||
case "-v": |
|||
case "--version": |
|||
printVersion(); |
|||
exitSuccess(); |
|||
break; |
|||
|
|||
case "--": |
|||
nextArg(); |
|||
break; |
|||
|
|||
default: |
|||
abort("Unknown option: " + args[0] + "."); |
|||
} |
|||
nextArg(); |
|||
} |
|||
|
|||
if (Object.keys(options.dependencies).length > 0) { |
|||
if (MODULE_FORMATS_WITH_DEPS.indexOf(options.format) === -1) { |
|||
abort("Can't use the -d/--dependency option with the \"" + options.format + "\" module format."); |
|||
} |
|||
} |
|||
|
|||
if (options.exportVar !== null) { |
|||
if (options.format !== "globals" && options.format !== "umd") { |
|||
abort("Can't use the -e/--export-var option with the \"" + options.format + "\" module format."); |
|||
} |
|||
} |
|||
|
|||
let inputStream, outputStream; |
|||
|
|||
switch (args.length) { |
|||
case 0: |
|||
inputFile = "-"; |
|||
break; |
|||
|
|||
case 1: |
|||
inputFile = args[0]; |
|||
break; |
|||
|
|||
default: |
|||
abort("Too many arguments."); |
|||
} |
|||
|
|||
if (outputFile === null) { |
|||
if (inputFile === "-") { |
|||
outputFile = "-"; |
|||
} else { |
|||
outputFile = inputFile.substr(0, inputFile.length - path.extname(inputFile).length) + ".js"; |
|||
} |
|||
} |
|||
|
|||
if (inputFile === "-") { |
|||
process.stdin.resume(); |
|||
inputStream = process.stdin; |
|||
inputStream.on("error", () => { |
|||
abort("Can't read from file \"" + inputFile + "\"."); |
|||
}); |
|||
} else { |
|||
inputStream = fs.createReadStream(inputFile); |
|||
} |
|||
|
|||
if (outputFile === "-") { |
|||
outputStream = process.stdout; |
|||
} else { |
|||
outputStream = fs.createWriteStream(outputFile); |
|||
outputStream.on("error", () => { |
|||
abort("Can't write to file \"" + outputFile + "\"."); |
|||
}); |
|||
} |
|||
|
|||
readStream(inputStream, input => { |
|||
let source; |
|||
|
|||
try { |
|||
source = peg.generate(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(source); |
|||
if (outputStream !== process.stdout) { |
|||
outputStream.end(); |
|||
} |
|||
}); |
@ -0,0 +1,51 @@ |
|||
Usage: pegjs [options] [<input_file>] [--] |
|||
|
|||
Options: |
|||
|
|||
-a, --allowed-start-rules <rules> Comma-separated list of rules the generated |
|||
parser will be allowed to start parsing from |
|||
(default: first rule in the grammar) |
|||
(note: repeatable option) |
|||
|
|||
--cache Make generated parser cache results |
|||
|
|||
-c, --config <file> Aliases for "--extra-options-file" |
|||
|
|||
-d, --dependency <dependency> Use specified dependency |
|||
(note: repeatable option) |
|||
|
|||
-e, --export-var <variable> Name of a global variable into which the |
|||
parser object is assigned to when no |
|||
module loader is detected |
|||
|
|||
--extra-options <options> A string with additional options (in JSON |
|||
format) to pass to peg.generate |
|||
(note: repeatable option) |
|||
|
|||
--extra-options-file <file> File with additional options (in JSON |
|||
format) to pass to peg.generate |
|||
(note: repeatable option) |
|||
|
|||
-f, --format <format> Format of the generated parser: |
|||
amd, bare, commonjs, es, globals, umd |
|||
(default: commonjs) |
|||
|
|||
-h, --help Print help and exit |
|||
|
|||
--no-cache Generated parser doesn't cache results |
|||
(default behavior) |
|||
|
|||
--no-trace Disable tracing in generated parser |
|||
(default behavior) |
|||
|
|||
-O, --optimize <goal> Select optimization for speed or size |
|||
(default: speed) |
|||
|
|||
-o, --output <file> Output file |
|||
|
|||
-p, --plugin <plugin> Use a specified plugin |
|||
(note: repeatable option) |
|||
|
|||
--trace Enable tracing in generated parser |
|||
|
|||
-v, --version Print version information and exit |
Loading…
Reference in new issue