#!/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] [--] []"); console.log(""); console.log("Options:"); console.log(" --allowed-start-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 use specified dependency (can be specified"); console.log(" multiple times)"); console.log(" -e, --export-var 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 additional options (in JSON format) to pass"); console.log(" to peg.generate"); console.log(" --extra-options-file file with additional options (in JSON"); console.log(" format) to pass to peg.generate"); console.log(" --format format of the generated parser: amd,"); console.log(" commonjs, globals, umd (default: commonjs)"); console.log(" -h, --help print help and exit"); console.log(" -O, --optimize select optimization for speed or size"); console.log(" (default: speed)"); console.log(" -o, --output output file"); console.log(" --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."); } for (let 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 */ 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 }; 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 (args[0] !== "amd" && args[0] !== "commonjs" && args[0] !== "globals" && args[0] !== "umd") { abort("Module format must be one of \"amd\", \"commonjs\", \"globals\", and \"umd\"."); } 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]; 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 "--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 (options.format !== "amd" && options.format !== "commonjs" && options.format !== "umd") { 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; let 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(); } });