diff --git a/lib/compiler/passes/generate-js.js b/lib/compiler/passes/generate-js.js index 594a7c9..0a9a8ee 100644 --- a/lib/compiler/passes/generate-js.js +++ b/lib/compiler/passes/generate-js.js @@ -9,8 +9,7 @@ var arrays = require("../../utils/arrays"), function generateJS(ast, options) { /* These only indent non-empty lines to avoid trailing whitespace. */ function indent2(code) { return code.replace(/^(.+)$/gm, ' $1'); } - function indent4(code) { return code.replace(/^(.+)$/gm, ' $1'); } - function indent8(code) { return code.replace(/^(.+)$/gm, ' $1'); } + function indent6(code) { return code.replace(/^(.+)$/gm, ' $1'); } function indent10(code) { return code.replace(/^(.+)$/gm, ' $1'); } function generateTables() { @@ -755,416 +754,434 @@ function generateJS(ast, options) { return parts.join('\n'); } - var parts = [], - startRuleIndices, startRuleIndex, - startRuleFunctions, startRuleFunction, - ruleNames; - - parts.push([ - '(function() {', - ' "use strict";', - '', - ' /*', - ' * Generated by PEG.js 0.9.0.', - ' *', - ' * http://pegjs.org/', - ' */', - '', - ' function peg$subclass(child, parent) {', - ' function ctor() { this.constructor = child; }', - ' ctor.prototype = parent.prototype;', - ' child.prototype = new ctor();', - ' }', - '', - ' function peg$SyntaxError(message, expected, location) {', - ' this.message = message;', - ' this.expected = expected;', - ' this.location = location;', - ' this.name = "SyntaxError";', - '', - ' if (typeof Error.captureStackTrace === "function") {', - ' Error.captureStackTrace(this, peg$SyntaxError);', - ' }', - ' }', - '', - ' peg$subclass(peg$SyntaxError, Error);', - '' - ].join('\n')); - - if (options.trace) { + function generateToplevel() { + var parts = [], + startRuleIndices, startRuleIndex, + startRuleFunctions, startRuleFunction, + ruleNames; + parts.push([ - ' function peg$DefaultTracer() {', - ' this.indentLevel = 0;', - ' }', - '', - ' peg$DefaultTracer.prototype.trace = function(event) {', - ' var that = this;', - '', - ' function log(event) {', - ' function repeat(string, n) {', - ' var result = "", i;', - '', - ' for (i = 0; i < n; i++) {', - ' result += string;', - ' }', - '', - ' return result;', - ' }', - '', - ' function pad(string, length) {', - ' return string + repeat(" ", length - string.length);', - ' }', - '', - ' if (typeof console === "object") {', // IE 8-10 - ' console.log(', - ' event.location.start.line + ":" + event.location.start.column + "-"', - ' + event.location.end.line + ":" + event.location.end.column + " "', - ' + pad(event.type, 10) + " "', - ' + repeat(" ", that.indentLevel) + event.rule', - ' );', - ' }', - ' }', - '', - ' switch (event.type) {', - ' case "rule.enter":', - ' log(event);', - ' this.indentLevel++;', - ' break;', + 'function peg$subclass(child, parent) {', + ' function ctor() { this.constructor = child; }', + ' ctor.prototype = parent.prototype;', + ' child.prototype = new ctor();', + '}', '', - ' case "rule.match":', - ' this.indentLevel--;', - ' log(event);', - ' break;', + 'function peg$SyntaxError(message, expected, location) {', + ' this.message = message;', + ' this.expected = expected;', + ' this.location = location;', + ' this.name = "SyntaxError";', '', - ' case "rule.fail":', - ' this.indentLevel--;', - ' log(event);', - ' break;', + ' if (typeof Error.captureStackTrace === "function") {', + ' Error.captureStackTrace(this, peg$SyntaxError);', + ' }', + '}', '', - ' default:', - ' throw new Error("Invalid event type: " + event.type + ".");', - ' }', - ' };', + 'peg$subclass(peg$SyntaxError, Error);', '' ].join('\n')); - } - parts.push([ - ' function peg$parse(input, options) {', - ' options = options !== void 0 ? options : {};', - '', - ' var parser = this,', - '', - ' peg$FAILED = {},', - '' - ].join('\n')); - - if (options.optimize === "size") { - startRuleIndices = '{ ' - + arrays.map( - options.allowedStartRules, - function(r) { return r + ': ' + asts.indexOfRule(ast, r); } - ).join(', ') - + ' }'; - startRuleIndex = asts.indexOfRule(ast, options.allowedStartRules[0]); + if (options.trace) { + parts.push([ + 'function peg$DefaultTracer() {', + ' this.indentLevel = 0;', + '}', + '', + 'peg$DefaultTracer.prototype.trace = function(event) {', + ' var that = this;', + '', + ' function log(event) {', + ' function repeat(string, n) {', + ' var result = "", i;', + '', + ' for (i = 0; i < n; i++) {', + ' result += string;', + ' }', + '', + ' return result;', + ' }', + '', + ' function pad(string, length) {', + ' return string + repeat(" ", length - string.length);', + ' }', + '', + ' if (typeof console === "object") {', // IE 8-10 + ' console.log(', + ' event.location.start.line + ":" + event.location.start.column + "-"', + ' + event.location.end.line + ":" + event.location.end.column + " "', + ' + pad(event.type, 10) + " "', + ' + repeat(" ", that.indentLevel) + event.rule', + ' );', + ' }', + ' }', + '', + ' switch (event.type) {', + ' case "rule.enter":', + ' log(event);', + ' this.indentLevel++;', + ' break;', + '', + ' case "rule.match":', + ' this.indentLevel--;', + ' log(event);', + ' break;', + '', + ' case "rule.fail":', + ' this.indentLevel--;', + ' log(event);', + ' break;', + '', + ' default:', + ' throw new Error("Invalid event type: " + event.type + ".");', + ' }', + '};', + '' + ].join('\n')); + } parts.push([ - ' peg$startRuleIndices = ' + startRuleIndices + ',', - ' peg$startRuleIndex = ' + startRuleIndex + ',' + 'function peg$parse(input, options) {', + ' options = options !== void 0 ? options : {};', + '', + ' var parser = this,', + '', + ' peg$FAILED = {},', + '' ].join('\n')); - } else { - startRuleFunctions = '{ ' - + arrays.map( - options.allowedStartRules, - function(r) { return r + ': peg$parse' + r; } - ).join(', ') - + ' }'; - startRuleFunction = 'peg$parse' + options.allowedStartRules[0]; - parts.push([ - ' peg$startRuleFunctions = ' + startRuleFunctions + ',', - ' peg$startRuleFunction = ' + startRuleFunction + ',' - ].join('\n')); - } + if (options.optimize === "size") { + startRuleIndices = '{ ' + + arrays.map( + options.allowedStartRules, + function(r) { return r + ': ' + asts.indexOfRule(ast, r); } + ).join(', ') + + ' }'; + startRuleIndex = asts.indexOfRule(ast, options.allowedStartRules[0]); - parts.push(''); + parts.push([ + ' peg$startRuleIndices = ' + startRuleIndices + ',', + ' peg$startRuleIndex = ' + startRuleIndex + ',' + ].join('\n')); + } else { + startRuleFunctions = '{ ' + + arrays.map( + options.allowedStartRules, + function(r) { return r + ': peg$parse' + r; } + ).join(', ') + + ' }'; + startRuleFunction = 'peg$parse' + options.allowedStartRules[0]; - parts.push(indent8(generateTables())); + parts.push([ + ' peg$startRuleFunctions = ' + startRuleFunctions + ',', + ' peg$startRuleFunction = ' + startRuleFunction + ',' + ].join('\n')); + } - parts.push([ - '', - ' peg$currPos = 0,', - ' peg$savedPos = 0,', - ' peg$posDetailsCache = [{ line: 1, column: 1 }],', - ' peg$maxFailPos = 0,', - ' peg$maxFailExpected = [],', - ' peg$silentFails = 0,', // 0 = report failures, > 0 = silence failures - '' - ].join('\n')); + parts.push(''); + + parts.push(indent6(generateTables())); - if (options.cache) { parts.push([ - ' peg$resultsCache = {},', + '', + ' peg$currPos = 0,', + ' peg$savedPos = 0,', + ' peg$posDetailsCache = [{ line: 1, column: 1 }],', + ' peg$maxFailPos = 0,', + ' peg$maxFailExpected = [],', + ' peg$silentFails = 0,', // 0 = report failures, > 0 = silence failures '' ].join('\n')); - } - if (options.trace) { - if (options.optimize === "size") { - ruleNames = '[' - + arrays.map( - ast.rules, - function(r) { return '"' + js.stringEscape(r.name) + '"'; } - ).join(', ') - + ']'; + if (options.cache) { + parts.push([ + ' peg$resultsCache = {},', + '' + ].join('\n')); + } + + if (options.trace) { + if (options.optimize === "size") { + ruleNames = '[' + + arrays.map( + ast.rules, + function(r) { return '"' + js.stringEscape(r.name) + '"'; } + ).join(', ') + + ']'; + + parts.push([ + ' peg$ruleNames = ' + ruleNames + ',', + '' + ].join('\n')); + } parts.push([ - ' peg$ruleNames = ' + ruleNames + ',', + ' peg$tracer = "tracer" in options ? options.tracer : new peg$DefaultTracer(),', '' ].join('\n')); } parts.push([ - ' peg$tracer = "tracer" in options ? options.tracer : new peg$DefaultTracer(),', + ' peg$result;', '' ].join('\n')); - } - parts.push([ - ' peg$result;', - '' - ].join('\n')); + if (options.optimize === "size") { + parts.push([ + ' if ("startRule" in options) {', + ' if (!(options.startRule in peg$startRuleIndices)) {', + ' throw new Error("Can\'t start parsing from rule \\"" + options.startRule + "\\".");', + ' }', + '', + ' peg$startRuleIndex = peg$startRuleIndices[options.startRule];', + ' }' + ].join('\n')); + } else { + parts.push([ + ' if ("startRule" in options) {', + ' if (!(options.startRule in peg$startRuleFunctions)) {', + ' throw new Error("Can\'t start parsing from rule \\"" + options.startRule + "\\".");', + ' }', + '', + ' peg$startRuleFunction = peg$startRuleFunctions[options.startRule];', + ' }' + ].join('\n')); + } - if (options.optimize === "size") { parts.push([ - ' if ("startRule" in options) {', - ' if (!(options.startRule in peg$startRuleIndices)) {', - ' throw new Error("Can\'t start parsing from rule \\"" + options.startRule + "\\".");', + '', + ' function text() {', + ' return input.substring(peg$savedPos, peg$currPos);', + ' }', + '', + ' function location() {', + ' return peg$computeLocation(peg$savedPos, peg$currPos);', + ' }', + '', + ' function expected(description, location) {', + ' location = location !== void 0 ? location : peg$computeLocation(peg$savedPos, peg$currPos)', + '', + ' throw peg$buildException(', + ' null,', + ' [{ type: "other", description: description }],', + ' location', + ' );', + ' }', + '', + ' function error(message, location) {', + ' location = location !== void 0 ? location : peg$computeLocation(peg$savedPos, peg$currPos)', + '', + ' throw peg$buildException(message, null, location);', + ' }', + '', + ' function peg$computePosDetails(pos) {', + ' var details = peg$posDetailsCache[pos], p;', + '', + ' if (details) {', + ' return details;', + ' } else {', + ' p = pos - 1;', + ' while (!peg$posDetailsCache[p]) {', + ' p--;', ' }', '', - ' peg$startRuleIndex = peg$startRuleIndices[options.startRule];', - ' }' - ].join('\n')); - } else { - parts.push([ - ' if ("startRule" in options) {', - ' if (!(options.startRule in peg$startRuleFunctions)) {', - ' throw new Error("Can\'t start parsing from rule \\"" + options.startRule + "\\".");', + ' details = peg$posDetailsCache[p];', + ' details = {', + ' line: details.line,', + ' column: details.column', + ' };', + '', + ' while (p < pos) {', + ' if (input.charCodeAt(p) === 10) {', + ' details.line++;', + ' details.column = 1;', + ' } else {', + ' details.column++;', + ' }', + '', + ' p++;', + ' }', + '', + ' peg$posDetailsCache[pos] = details;', + ' return details;', + ' }', + ' }', + '', + ' function peg$computeLocation(startPos, endPos) {', + ' var startPosDetails = peg$computePosDetails(startPos),', + ' endPosDetails = peg$computePosDetails(endPos);', + '', + ' return {', + ' start: {', + ' offset: startPos,', + ' line: startPosDetails.line,', + ' column: startPosDetails.column', + ' },', + ' end: {', + ' offset: endPos,', + ' line: endPosDetails.line,', + ' column: endPosDetails.column', + ' }', + ' };', + ' }', + '', + ' function peg$fail(expected) {', + ' if (peg$currPos < peg$maxFailPos) { return; }', + '', + ' if (peg$currPos > peg$maxFailPos) {', + ' peg$maxFailPos = peg$currPos;', + ' peg$maxFailExpected = [];', + ' }', + '', + ' peg$maxFailExpected.push(expected);', + ' }', + '', + ' function peg$buildException(message, expected, location) {', + ' function cleanupExpected(expected) {', + ' var i, j;', + '', + ' expected.sort(function(a, b) {', + ' if (a.description < b.description) {', + ' return -1;', + ' } else if (a.description > b.description) {', + ' return 1;', + ' } else {', + ' return 0;', + ' }', + ' });', + '', + /* + * This works because the bytecode generator guarantees that every + * expectation object exists only once, so it's enough to use |===| instead + * of deeper structural comparison. + */ + ' if (expected.length > 0) {', + ' for (i = 1, j = 1; i < expected.length; i++) {', + ' if (expected[i - 1] !== expected[i]) {', + ' expected[j] = expected[i];', + ' j++;', + ' }', + ' }', + ' expected.length = j;', + ' }', + ' }', + '', + ' function buildMessage(expected) {', + ' var expectedDescs = new Array(expected.length),', + ' expectedDesc, i;', + '', + ' for (i = 0; i < expected.length; i++) {', + ' expectedDescs[i] = expected[i].description;', ' }', '', - ' peg$startRuleFunction = peg$startRuleFunctions[options.startRule];', - ' }' + ' expectedDesc = expected.length > 1', + ' ? expectedDescs.slice(0, -1).join(", ")', + ' + " or "', + ' + expectedDescs[expected.length - 1]', + ' : expectedDescs[0];', + '', + ' return "Expected " + expectedDesc + ".";', + ' }', + '', + ' if (expected !== null) {', + ' cleanupExpected(expected);', + ' }', + '', + ' return new peg$SyntaxError(', + ' message !== null ? message : buildMessage(expected),', + ' expected,', + ' location', + ' );', + ' }', + '' ].join('\n')); - } - parts.push([ - '', - ' function text() {', - ' return input.substring(peg$savedPos, peg$currPos);', - ' }', - '', - ' function location() {', - ' return peg$computeLocation(peg$savedPos, peg$currPos);', - ' }', - '', - ' function expected(description, location) {', - ' location = location !== void 0 ? location : peg$computeLocation(peg$savedPos, peg$currPos)', - '', - ' throw peg$buildException(', - ' null,', - ' [{ type: "other", description: description }],', - ' location', - ' );', - ' }', - '', - ' function error(message, location) {', - ' location = location !== void 0 ? location : peg$computeLocation(peg$savedPos, peg$currPos)', - '', - ' throw peg$buildException(message, null, location);', - ' }', - '', - ' function peg$computePosDetails(pos) {', - ' var details = peg$posDetailsCache[pos], p;', - '', - ' if (details) {', - ' return details;', - ' } else {', - ' p = pos - 1;', - ' while (!peg$posDetailsCache[p]) {', - ' p--;', - ' }', - '', - ' details = peg$posDetailsCache[p];', - ' details = {', - ' line: details.line,', - ' column: details.column', - ' };', - '', - ' while (p < pos) {', - ' if (input.charCodeAt(p) === 10) {', - ' details.line++;', - ' details.column = 1;', - ' } else {', - ' details.column++;', - ' }', - '', - ' p++;', - ' }', - '', - ' peg$posDetailsCache[pos] = details;', - ' return details;', - ' }', - ' }', - '', - ' function peg$computeLocation(startPos, endPos) {', - ' var startPosDetails = peg$computePosDetails(startPos),', - ' endPosDetails = peg$computePosDetails(endPos);', - '', - ' return {', - ' start: {', - ' offset: startPos,', - ' line: startPosDetails.line,', - ' column: startPosDetails.column', - ' },', - ' end: {', - ' offset: endPos,', - ' line: endPosDetails.line,', - ' column: endPosDetails.column', - ' }', - ' };', - ' }', - '', - ' function peg$fail(expected) {', - ' if (peg$currPos < peg$maxFailPos) { return; }', - '', - ' if (peg$currPos > peg$maxFailPos) {', - ' peg$maxFailPos = peg$currPos;', - ' peg$maxFailExpected = [];', - ' }', - '', - ' peg$maxFailExpected.push(expected);', - ' }', - '', - ' function peg$buildException(message, expected, location) {', - ' function cleanupExpected(expected) {', - ' var i, j;', - '', - ' expected.sort(function(a, b) {', - ' if (a.description < b.description) {', - ' return -1;', - ' } else if (a.description > b.description) {', - ' return 1;', - ' } else {', - ' return 0;', - ' }', - ' });', - '', - /* - * This works because the bytecode generator guarantees that every - * expectation object exists only once, so it's enough to use |===| instead - * of deeper structural comparison. - */ - ' if (expected.length > 0) {', - ' for (i = 1, j = 1; i < expected.length; i++) {', - ' if (expected[i - 1] !== expected[i]) {', - ' expected[j] = expected[i];', - ' j++;', - ' }', - ' }', - ' expected.length = j;', - ' }', - ' }', - '', - ' function buildMessage(expected) {', - ' var expectedDescs = new Array(expected.length),', - ' expectedDesc, i;', - '', - ' for (i = 0; i < expected.length; i++) {', - ' expectedDescs[i] = expected[i].description;', - ' }', - '', - ' expectedDesc = expected.length > 1', - ' ? expectedDescs.slice(0, -1).join(", ")', - ' + " or "', - ' + expectedDescs[expected.length - 1]', - ' : expectedDescs[0];', - '', - ' return "Expected " + expectedDesc + ".";', - ' }', - '', - ' if (expected !== null) {', - ' cleanupExpected(expected);', - ' }', - '', - ' return new peg$SyntaxError(', - ' message !== null ? message : buildMessage(expected),', - ' expected,', - ' location', - ' );', - ' }', - '' - ].join('\n')); - - if (options.optimize === "size") { - parts.push(indent4(generateInterpreter())); - parts.push(''); - } else { - arrays.each(ast.rules, function(rule) { - parts.push(indent4(generateRuleFunction(rule))); + if (options.optimize === "size") { + parts.push(indent2(generateInterpreter())); parts.push(''); - }); - } + } else { + arrays.each(ast.rules, function(rule) { + parts.push(indent2(generateRuleFunction(rule))); + parts.push(''); + }); + } - if (ast.initializer) { - parts.push(indent4(ast.initializer.code)); - parts.push(''); - } + if (ast.initializer) { + parts.push(indent2(ast.initializer.code)); + parts.push(''); + } + + if (options.optimize === "size") { + parts.push(' peg$result = peg$parseRule(peg$startRuleIndex);'); + } else { + parts.push(' peg$result = peg$startRuleFunction();'); + } - if (options.optimize === "size") { - parts.push(' peg$result = peg$parseRule(peg$startRuleIndex);'); - } else { - parts.push(' peg$result = peg$startRuleFunction();'); + parts.push([ + '', + ' if (peg$result !== peg$FAILED && peg$currPos === input.length) {', + ' return peg$result;', + ' } else {', + ' if (peg$result !== peg$FAILED && peg$currPos < input.length) {', + ' peg$fail({ type: "end", description: "end of input" });', + ' }', + '', + ' throw peg$buildException(', + ' null,', + ' peg$maxFailExpected,', + ' peg$computeLocation(peg$maxFailPos, peg$maxFailPos)', + ' );', + ' }', + '}' + ].join('\n')); + + return parts.join('\n'); } - parts.push([ - '', - ' if (peg$result !== peg$FAILED && peg$currPos === input.length) {', - ' return peg$result;', - ' } else {', - ' if (peg$result !== peg$FAILED && peg$currPos < input.length) {', - ' peg$fail({ type: "end", description: "end of input" });', - ' }', - '', - ' throw peg$buildException(', - ' null,', - ' peg$maxFailExpected,', - ' peg$computeLocation(peg$maxFailPos, peg$maxFailPos)', - ' );', - ' }', - ' }', - '', - ' return {' - ].join('\n')); - - if (options.trace) { + function generateWrapper(toplevelCode) { + var parts = []; + parts.push([ - ' SyntaxError: peg$SyntaxError,', - ' DefaultTracer: peg$DefaultTracer,', - ' parse: peg$parse' + '(function() {', + ' "use strict";', + '', + ' /*', + ' * Generated by PEG.js 0.9.0.', + ' *', + ' * http://pegjs.org/', + ' */', + '' ].join('\n')); - } else { + + parts.push(indent2(toplevelCode)); + parts.push([ - ' SyntaxError: peg$SyntaxError,', - ' parse: peg$parse' + '', + ' return {' + ].join('\n')); + + if (options.trace) { + parts.push([ + ' SyntaxError: peg$SyntaxError,', + ' DefaultTracer: peg$DefaultTracer,', + ' parse: peg$parse' + ].join('\n')); + } else { + parts.push([ + ' SyntaxError: peg$SyntaxError,', + ' parse: peg$parse' + ].join('\n')); + } + + parts.push([ + ' };', + '})()' ].join('\n')); - } - parts.push([ - ' };', - '})()' - ].join('\n')); + return parts.join('\n'); + } - ast.code = parts.join('\n'); + ast.code = generateWrapper(generateToplevel()); } module.exports = generateJS;