| #!/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(); |
| } |
| }); |