#!/usr/bin/env node
var util = require(“util”); var fs = require(“fs”); var path = require(“path”); var PEG = require(“../lib/peg”);
/* Helpers */
function printVersion() {
util.puts("PEG.js " + PEG.VERSION);
}
function printHelp() {
util.puts("Usage: pegjs [options] [--] [<input_file>] [<output_file>]"); util.puts(""); util.puts("Generates a parser from the PEG grammar specified in the <input_file> and writes"); util.puts("it to the <output_file>."); util.puts(""); util.puts("If the <output_file> is omitted, its name is generated by changing the"); util.puts("<input_file> extension to \".js\". If both <input_file> and <output_file> are"); util.puts("omitted, standard input and output are used."); util.puts(""); util.puts("Options:"); util.puts(" -e, --export-var <variable> name of the variable where the parser"); util.puts(" object will be stored (default:"); util.puts(" \"module.exports\")"); util.puts(" --cache make generated parser cache results"); util.puts(" --allowed-start-rules <rules> comma-separated list of rules the generated"); util.puts(" parser will be allowed to start parsing"); util.puts(" from (default: the first rule in the"); util.puts(" grammar)"); util.puts(" -o, --optimize <goal> select optimization for speed or size"); util.puts(" (default: speed)"); util.puts(" --plugin <plugin> use a specified plugin (can be specified"); util.puts(" multiple times)"); util.puts(" --extra-options <options> additional options (in JSON format) to pass"); util.puts(" to PEG.buildParser"); util.puts(" --extra-options-file <file> file with additional options (in JSON"); util.puts(" format) to pass to PEG.buildParser"); util.puts(" -v, --version print version information and exit"); util.puts(" -h, --help print help and exit");
}
function exitSuccess() {
process.exit(0);
}
function exitFailure() {
process.exit(1);
}
function abort(message) {
util.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", plugins: []
};
while (args.length > 0 && isOption(args)) {
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 "-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.line !== undefined && e.column !== undefined) { abort(e.line + ":" + e.column + ": " + e.message); } else { abort(e.message); } } outputStream.write( exportVar !== "" ? exportVar + " = " + source + ";\n" : source + "\n" ); if (outputStream !== process.stdout) { outputStream.end(); }
});