@ -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 ;