UMD parsers: Generate parser wrapper separately from its toplevel code

Code which was at the toplevel of the "generateJS" function in the code
generator is now split into "generateToplevel" (which genreates parser
toplevel code) and "generateWrapper" (which generates a wrapper around
it). This is pure refactoring, generated parser code is exactly the same
as before.

This change will make it easier to modifiy the code genreator to produce
UMD modules.

Part of work on #362.
redux
David Majda 8 years ago
parent 5e702b5901
commit 7f8b3f7012

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

Loading…
Cancel
Save