"use strict"; let asts = require("../asts"); let op = require("../opcodes"); let js = require("../js"); // Generates parser JavaScript code. function generateJS(ast, options) { // These only indent non-empty lines to avoid trailing whitespace. function indent2(code) { return code.replace(/^(.+)$/gm, ' $1'); } function indent10(code) { return code.replace(/^(.+)$/gm, ' $1'); } function generateTables() { if (options.optimize === "size") { return [ 'var peg$consts = [', indent2(ast.consts.join(',\n')), '];', '', 'var peg$bytecode = [', indent2(ast.rules.map(rule => 'peg$decode("' + js.stringEscape(rule.bytecode.map( b => String.fromCharCode(b + 32) ).join('')) + '")' ).join(',\n')), '];' ].join('\n'); } else { return ast.consts.map((c, i) => 'var peg$c' + i + ' = ' + c + ';').join('\n'); } } function generateRuleHeader(ruleNameCode, ruleIndexCode) { let parts = []; parts.push(''); if (options.trace) { parts.push([ 'peg$tracer.trace({', ' type: "rule.enter",', ' rule: ' + ruleNameCode + ',', ' location: peg$computeLocation(startPos, startPos)', '});', '' ].join('\n')); } if (options.cache) { parts.push([ 'var key = peg$currPos * ' + ast.rules.length + ' + ' + ruleIndexCode + ';', 'var cached = peg$resultsCache[key];', '', 'if (cached) {', ' peg$currPos = cached.nextPos;', '' ].join('\n')); if (options.trace) { parts.push([ 'if (cached.result !== peg$FAILED) {', ' peg$tracer.trace({', ' type: "rule.match",', ' rule: ' + ruleNameCode + ',', ' result: cached.result,', ' location: peg$computeLocation(startPos, peg$currPos)', ' });', '} else {', ' peg$tracer.trace({', ' type: "rule.fail",', ' rule: ' + ruleNameCode + ',', ' location: peg$computeLocation(startPos, startPos)', ' });', '}', '' ].join('\n')); } parts.push([ ' return cached.result;', '}', '' ].join('\n')); } return parts.join('\n'); } function generateRuleFooter(ruleNameCode, resultCode) { let parts = []; if (options.cache) { parts.push([ '', 'peg$resultsCache[key] = { nextPos: peg$currPos, result: ' + resultCode + ' };' ].join('\n')); } if (options.trace) { parts.push([ '', 'if (' + resultCode + ' !== peg$FAILED) {', ' peg$tracer.trace({', ' type: "rule.match",', ' rule: ' + ruleNameCode + ',', ' result: ' + resultCode + ',', ' location: peg$computeLocation(startPos, peg$currPos)', ' });', '} else {', ' peg$tracer.trace({', ' type: "rule.fail",', ' rule: ' + ruleNameCode + ',', ' location: peg$computeLocation(startPos, startPos)', ' });', '}' ].join('\n')); } parts.push([ '', 'return ' + resultCode + ';' ].join('\n')); return parts.join('\n'); } function generateInterpreter() { let parts = []; function generateCondition(cond, argsLength) { let baseLength = argsLength + 3; let thenLengthCode = 'bc[ip + ' + (baseLength - 2) + ']'; let elseLengthCode = 'bc[ip + ' + (baseLength - 1) + ']'; return [ 'ends.push(end);', 'ips.push(ip + ' + baseLength + ' + ' + thenLengthCode + ' + ' + elseLengthCode + ');', '', 'if (' + cond + ') {', ' end = ip + ' + baseLength + ' + ' + thenLengthCode + ';', ' ip += ' + baseLength + ';', '} else {', ' end = ip + ' + baseLength + ' + ' + thenLengthCode + ' + ' + elseLengthCode + ';', ' ip += ' + baseLength + ' + ' + thenLengthCode + ';', '}', '', 'break;' ].join('\n'); } function generateLoop(cond) { let baseLength = 2; let bodyLengthCode = 'bc[ip + ' + (baseLength - 1) + ']'; return [ 'if (' + cond + ') {', ' ends.push(end);', ' ips.push(ip);', '', ' end = ip + ' + baseLength + ' + ' + bodyLengthCode + ';', ' ip += ' + baseLength + ';', '} else {', ' ip += ' + baseLength + ' + ' + bodyLengthCode + ';', '}', '', 'break;' ].join('\n'); } function generateCall() { let baseLength = 4; let paramsLengthCode = 'bc[ip + ' + (baseLength - 1) + ']'; return [ 'params = bc.slice(ip + ' + baseLength + ', ip + ' + baseLength + ' + ' + paramsLengthCode + ')', ' .map(function(p) { return stack[stack.length - 1 - p]; });', '', 'stack.splice(', ' stack.length - bc[ip + 2],', ' bc[ip + 2],', ' peg$consts[bc[ip + 1]].apply(null, params)', ');', '', 'ip += ' + baseLength + ' + ' + paramsLengthCode + ';', 'break;' ].join('\n'); } parts.push([ 'function peg$decode(s) {', ' return s.split("").map(function(ch) { return ch.charCodeAt(0) - 32; });', '}', '', 'function peg$parseRule(index) {' ].join('\n')); if (options.trace) { parts.push([ ' var bc = peg$bytecode[index];', ' var ip = 0;', ' var ips = [];', ' var end = bc.length;', ' var ends = [];', ' var stack = [];', ' var startPos = peg$currPos;', ' var params;' ].join('\n')); } else { parts.push([ ' var bc = peg$bytecode[index];', ' var ip = 0;', ' var ips = [];', ' var end = bc.length;', ' var ends = [];', ' var stack = [];', ' var params;' ].join('\n')); } parts.push(indent2(generateRuleHeader('peg$ruleNames[index]', 'index'))); parts.push([ // The point of the outer loop and the |ips| & |ends| stacks is to avoid // recursive calls for interpreting parts of bytecode. In other words, we // implement the |interpret| operation of the abstract machine without // function calls. Such calls would likely slow the parser down and more // importantly cause stack overflows for complex grammars. ' while (true) {', ' while (ip < end) {', ' switch (bc[ip]) {', ' case ' + op.PUSH + ':', // PUSH c ' stack.push(peg$consts[bc[ip + 1]]);', ' ip += 2;', ' break;', '', ' case ' + op.PUSH_UNDEFINED + ':', // PUSH_UNDEFINED ' stack.push(undefined);', ' ip++;', ' break;', '', ' case ' + op.PUSH_NULL + ':', // PUSH_NULL ' stack.push(null);', ' ip++;', ' break;', '', ' case ' + op.PUSH_FAILED + ':', // PUSH_FAILED ' stack.push(peg$FAILED);', ' ip++;', ' break;', '', ' case ' + op.PUSH_EMPTY_ARRAY + ':', // PUSH_EMPTY_ARRAY ' stack.push([]);', ' ip++;', ' break;', '', ' case ' + op.PUSH_CURR_POS + ':', // PUSH_CURR_POS ' stack.push(peg$currPos);', ' ip++;', ' break;', '', ' case ' + op.POP + ':', // POP ' stack.pop();', ' ip++;', ' break;', '', ' case ' + op.POP_CURR_POS + ':', // POP_CURR_POS ' peg$currPos = stack.pop();', ' ip++;', ' break;', '', ' case ' + op.POP_N + ':', // POP_N n ' stack.length -= bc[ip + 1];', ' ip += 2;', ' break;', '', ' case ' + op.NIP + ':', // NIP ' stack.splice(-2, 1);', ' ip++;', ' break;', '', ' case ' + op.APPEND + ':', // APPEND ' stack[stack.length - 2].push(stack.pop());', ' ip++;', ' break;', '', ' case ' + op.WRAP + ':', // WRAP n ' stack.push(stack.splice(stack.length - bc[ip + 1], bc[ip + 1]));', ' ip += 2;', ' break;', '', ' case ' + op.TEXT + ':', // TEXT ' stack.push(input.substring(stack.pop(), peg$currPos));', ' ip++;', ' break;', '', ' case ' + op.IF + ':', // IF t, f indent10(generateCondition('stack[stack.length - 1]', 0)), '', ' case ' + op.IF_ERROR + ':', // IF_ERROR t, f indent10(generateCondition( 'stack[stack.length - 1] === peg$FAILED', 0 )), '', ' case ' + op.IF_NOT_ERROR + ':', // IF_NOT_ERROR t, f indent10( generateCondition('stack[stack.length - 1] !== peg$FAILED', 0 )), '', ' case ' + op.WHILE_NOT_ERROR + ':', // WHILE_NOT_ERROR b indent10(generateLoop('stack[stack.length - 1] !== peg$FAILED')), '', ' case ' + op.MATCH_ANY + ':', // MATCH_ANY a, f, ... indent10(generateCondition('input.length > peg$currPos', 0)), '', ' case ' + op.MATCH_STRING + ':', // MATCH_STRING s, a, f, ... indent10(generateCondition( 'input.substr(peg$currPos, peg$consts[bc[ip + 1]].length) === peg$consts[bc[ip + 1]]', 1 )), '', ' case ' + op.MATCH_STRING_IC + ':', // MATCH_STRING_IC s, a, f, ... indent10(generateCondition( 'input.substr(peg$currPos, peg$consts[bc[ip + 1]].length).toLowerCase() === peg$consts[bc[ip + 1]]', 1 )), '', ' case ' + op.MATCH_REGEXP + ':', // MATCH_REGEXP r, a, f, ... indent10(generateCondition( 'peg$consts[bc[ip + 1]].test(input.charAt(peg$currPos))', 1 )), '', ' case ' + op.ACCEPT_N + ':', // ACCEPT_N n ' stack.push(input.substr(peg$currPos, bc[ip + 1]));', ' peg$currPos += bc[ip + 1];', ' ip += 2;', ' break;', '', ' case ' + op.ACCEPT_STRING + ':', // ACCEPT_STRING s ' stack.push(peg$consts[bc[ip + 1]]);', ' peg$currPos += peg$consts[bc[ip + 1]].length;', ' ip += 2;', ' break;', '', ' case ' + op.FAIL + ':', // FAIL e ' stack.push(peg$FAILED);', ' if (peg$silentFails === 0) {', ' peg$fail(peg$consts[bc[ip + 1]]);', ' }', ' ip += 2;', ' break;', '', ' case ' + op.LOAD_SAVED_POS + ':', // LOAD_SAVED_POS p ' peg$savedPos = stack[stack.length - 1 - bc[ip + 1]];', ' ip += 2;', ' break;', '', ' case ' + op.UPDATE_SAVED_POS + ':', // UPDATE_SAVED_POS ' peg$savedPos = peg$currPos;', ' ip++;', ' break;', '', ' case ' + op.CALL + ':', // CALL f, n, pc, p1, p2, ..., pN indent10(generateCall()), '', ' case ' + op.RULE + ':', // RULE r ' stack.push(peg$parseRule(bc[ip + 1]));', ' ip += 2;', ' break;', '', ' case ' + op.SILENT_FAILS_ON + ':', // SILENT_FAILS_ON ' peg$silentFails++;', ' ip++;', ' break;', '', ' case ' + op.SILENT_FAILS_OFF + ':', // SILENT_FAILS_OFF ' peg$silentFails--;', ' ip++;', ' break;', '', ' default:', ' throw new Error("Invalid opcode: " + bc[ip] + ".");', ' }', ' }', '', ' if (ends.length > 0) {', ' end = ends.pop();', ' ip = ips.pop();', ' } else {', ' break;', ' }', ' }' ].join('\n')); parts.push(indent2(generateRuleFooter('peg$ruleNames[index]', 'stack[0]'))); parts.push('}'); return parts.join('\n'); } function generateRuleFunction(rule) { let parts = []; let stackVars = []; let code; function c(i) { return "peg$c" + i; } // |consts[i]| of the abstract machine function s(i) { return "s" + i; } // |stack[i]| of the abstract machine let stack = { sp: -1, maxSp: -1, push: function(exprCode) { let code = s(++this.sp) + ' = ' + exprCode + ';'; if (this.sp > this.maxSp) { this.maxSp = this.sp; } return code; }, pop: function(n) { if (n === undefined) { return s(this.sp--); } else { let values = Array(n); for (var i = 0; i < n; i++) { values[i] = s(this.sp - n + 1 + i); } this.sp -= n; return values; } }, top: function() { return s(this.sp); }, index: function(i) { return s(this.sp - i); } }; function compile(bc) { let ip = 0; let end = bc.length; let parts = []; let value; function compileCondition(cond, argCount) { let baseLength = argCount + 3; let thenLength = bc[ip + baseLength - 2]; let elseLength = bc[ip + baseLength - 1]; let baseSp = stack.sp; let thenCode, elseCode, thenSp, elseSp; ip += baseLength; thenCode = compile(bc.slice(ip, ip + thenLength)); thenSp = stack.sp; ip += thenLength; if (elseLength > 0) { stack.sp = baseSp; elseCode = compile(bc.slice(ip, ip + elseLength)); elseSp = stack.sp; ip += elseLength; if (thenSp !== elseSp) { throw new Error( "Branches of a condition must move the stack pointer in the same way." ); } } parts.push('if (' + cond + ') {'); parts.push(indent2(thenCode)); if (elseLength > 0) { parts.push('} else {'); parts.push(indent2(elseCode)); } parts.push('}'); } function compileLoop(cond) { let baseLength = 2; let bodyLength = bc[ip + baseLength - 1]; let baseSp = stack.sp; let bodyCode, bodySp; ip += baseLength; bodyCode = compile(bc.slice(ip, ip + bodyLength)); bodySp = stack.sp; ip += bodyLength; if (bodySp !== baseSp) { throw new Error("Body of a loop can't move the stack pointer."); } parts.push('while (' + cond + ') {'); parts.push(indent2(bodyCode)); parts.push('}'); } function compileCall() { let baseLength = 4; let paramsLength = bc[ip + baseLength - 1]; let value = c(bc[ip + 1]) + '(' + bc.slice(ip + baseLength, ip + baseLength + paramsLength).map( p => stack.index(p) ).join(', ') + ')'; stack.pop(bc[ip + 2]); parts.push(stack.push(value)); ip += baseLength + paramsLength; } while (ip < end) { switch (bc[ip]) { case op.PUSH: // PUSH c parts.push(stack.push(c(bc[ip + 1]))); ip += 2; break; case op.PUSH_CURR_POS: // PUSH_CURR_POS parts.push(stack.push('peg$currPos')); ip++; break; case op.PUSH_UNDEFINED: // PUSH_UNDEFINED parts.push(stack.push('undefined')); ip++; break; case op.PUSH_NULL: // PUSH_NULL parts.push(stack.push('null')); ip++; break; case op.PUSH_FAILED: // PUSH_FAILED parts.push(stack.push('peg$FAILED')); ip++; break; case op.PUSH_EMPTY_ARRAY: // PUSH_EMPTY_ARRAY parts.push(stack.push('[]')); ip++; break; case op.POP: // POP stack.pop(); ip++; break; case op.POP_CURR_POS: // POP_CURR_POS parts.push('peg$currPos = ' + stack.pop() + ';'); ip++; break; case op.POP_N: // POP_N n stack.pop(bc[ip + 1]); ip += 2; break; case op.NIP: // NIP value = stack.pop(); stack.pop(); parts.push(stack.push(value)); ip++; break; case op.APPEND: // APPEND value = stack.pop(); parts.push(stack.top() + '.push(' + value + ');'); ip++; break; case op.WRAP: // WRAP n parts.push( stack.push('[' + stack.pop(bc[ip + 1]).join(', ') + ']') ); ip += 2; break; case op.TEXT: // TEXT parts.push( stack.push('input.substring(' + stack.pop() + ', peg$currPos)') ); ip++; break; case op.IF: // IF t, f compileCondition(stack.top(), 0); break; case op.IF_ERROR: // IF_ERROR t, f compileCondition(stack.top() + ' === peg$FAILED', 0); break; case op.IF_NOT_ERROR: // IF_NOT_ERROR t, f compileCondition(stack.top() + ' !== peg$FAILED', 0); break; case op.WHILE_NOT_ERROR: // WHILE_NOT_ERROR b compileLoop(stack.top() + ' !== peg$FAILED', 0); break; case op.MATCH_ANY: // MATCH_ANY a, f, ... compileCondition('input.length > peg$currPos', 0); break; case op.MATCH_STRING: // MATCH_STRING s, a, f, ... compileCondition( eval(ast.consts[bc[ip + 1]]).length > 1 ? 'input.substr(peg$currPos, ' + eval(ast.consts[bc[ip + 1]]).length + ') === ' + c(bc[ip + 1]) : 'input.charCodeAt(peg$currPos) === ' + eval(ast.consts[bc[ip + 1]]).charCodeAt(0), 1 ); break; case op.MATCH_STRING_IC: // MATCH_STRING_IC s, a, f, ... compileCondition( 'input.substr(peg$currPos, ' + eval(ast.consts[bc[ip + 1]]).length + ').toLowerCase() === ' + c(bc[ip + 1]), 1 ); break; case op.MATCH_REGEXP: // MATCH_REGEXP r, a, f, ... compileCondition( c(bc[ip + 1]) + '.test(input.charAt(peg$currPos))', 1 ); break; case op.ACCEPT_N: // ACCEPT_N n parts.push(stack.push( bc[ip + 1] > 1 ? 'input.substr(peg$currPos, ' + bc[ip + 1] + ')' : 'input.charAt(peg$currPos)' )); parts.push( bc[ip + 1] > 1 ? 'peg$currPos += ' + bc[ip + 1] + ';' : 'peg$currPos++;' ); ip += 2; break; case op.ACCEPT_STRING: // ACCEPT_STRING s parts.push(stack.push(c(bc[ip + 1]))); parts.push( eval(ast.consts[bc[ip + 1]]).length > 1 ? 'peg$currPos += ' + eval(ast.consts[bc[ip + 1]]).length + ';' : 'peg$currPos++;' ); ip += 2; break; case op.FAIL: // FAIL e parts.push(stack.push('peg$FAILED')); parts.push('if (peg$silentFails === 0) { peg$fail(' + c(bc[ip + 1]) + '); }'); ip += 2; break; case op.LOAD_SAVED_POS: // LOAD_SAVED_POS p parts.push('peg$savedPos = ' + stack.index(bc[ip + 1]) + ';'); ip += 2; break; case op.UPDATE_SAVED_POS: // UPDATE_SAVED_POS parts.push('peg$savedPos = peg$currPos;'); ip++; break; case op.CALL: // CALL f, n, pc, p1, p2, ..., pN compileCall(); break; case op.RULE: // RULE r parts.push(stack.push("peg$parse" + ast.rules[bc[ip + 1]].name + "()")); ip += 2; break; case op.SILENT_FAILS_ON: // SILENT_FAILS_ON parts.push('peg$silentFails++;'); ip++; break; case op.SILENT_FAILS_OFF: // SILENT_FAILS_OFF parts.push('peg$silentFails--;'); ip++; break; default: throw new Error("Invalid opcode: " + bc[ip] + "."); } } return parts.join('\n'); } code = compile(rule.bytecode); parts.push('function peg$parse' + rule.name + '() {'); if (options.trace) { parts.push(' var startPos = peg$currPos;'); } for (let i = 0; i <= stack.maxSp; i++) { stackVars[i] = s(i); } parts.push(' var ' + stackVars.join(', ') + ';'); parts.push(indent2(generateRuleHeader( '"' + js.stringEscape(rule.name) + '"', asts.indexOfRule(ast, rule.name) ))); parts.push(indent2(code)); parts.push(indent2(generateRuleFooter( '"' + js.stringEscape(rule.name) + '"', s(0) ))); parts.push('}'); return parts.join('\n'); } function generateToplevel() { let parts = []; parts.push([ 'function peg$subclass(child, parent) {', ' function ctor() { this.constructor = child; }', ' ctor.prototype = parent.prototype;', ' child.prototype = new ctor();', '}', '', 'function peg$SyntaxError(message, expected, found, location) {', ' this.message = message;', ' this.expected = expected;', ' this.found = found;', ' this.location = location;', ' this.name = "SyntaxError";', '', ' if (typeof Error.captureStackTrace === "function") {', ' Error.captureStackTrace(this, peg$SyntaxError);', ' }', '}', '', 'peg$subclass(peg$SyntaxError, Error);', '', 'peg$SyntaxError.buildMessage = function(expected, found) {', ' var DESCRIBE_EXPECTATION_FNS = {', ' literal: function(expectation) {', ' return "\\\"" + literalEscape(expectation.text) + "\\\"";', ' },', '', ' "class": function(expectation) {', ' var escapedParts = expectation.parts.map(function(part) {', ' return Array.isArray(part)', ' ? classEscape(part[0]) + "-" + classEscape(part[1])', ' : classEscape(part);', ' });', '', ' return "[" + (expectation.inverted ? "^" : "") + escapedParts + "]";', ' },', '', ' any: function(expectation) {', ' return "any character";', ' },', '', ' end: function(expectation) {', ' return "end of input";', ' },', '', ' other: function(expectation) {', ' return expectation.description;', ' }', ' };', '', ' function hex(ch) {', ' return ch.charCodeAt(0).toString(16).toUpperCase();', ' }', '', ' function literalEscape(s) {', ' return s', ' .replace(/\\\\/g, \'\\\\\\\\\')', // backslash ' .replace(/"/g, \'\\\\"\')', // closing double quote ' .replace(/\\0/g, \'\\\\0\')', // null ' .replace(/\\t/g, \'\\\\t\')', // horizontal tab ' .replace(/\\n/g, \'\\\\n\')', // line feed ' .replace(/\\r/g, \'\\\\r\')', // carriage return ' .replace(/[\\x00-\\x0F]/g, function(ch) { return \'\\\\x0\' + hex(ch); })', ' .replace(/[\\x10-\\x1F\\x7F-\\x9F]/g, function(ch) { return \'\\\\x\' + hex(ch); });', ' }', '', ' function classEscape(s) {', ' return s', ' .replace(/\\\\/g, \'\\\\\\\\\')', // backslash ' .replace(/\\]/g, \'\\\\]\')', // closing bracket ' .replace(/\\^/g, \'\\\\^\')', // caret ' .replace(/-/g, \'\\\\-\')', // dash ' .replace(/\\0/g, \'\\\\0\')', // null ' .replace(/\\t/g, \'\\\\t\')', // horizontal tab ' .replace(/\\n/g, \'\\\\n\')', // line feed ' .replace(/\\r/g, \'\\\\r\')', // carriage return ' .replace(/[\\x00-\\x0F]/g, function(ch) { return \'\\\\x0\' + hex(ch); })', ' .replace(/[\\x10-\\x1F\\x7F-\\x9F]/g, function(ch) { return \'\\\\x\' + hex(ch); });', ' }', '', ' function describeExpectation(expectation) {', ' return DESCRIBE_EXPECTATION_FNS[expectation.type](expectation);', ' }', '', ' function describeExpected(expected) {', ' var descriptions = expected.map(describeExpectation);', ' var i, j;', '', ' descriptions.sort();', '', ' if (descriptions.length > 0) {', ' for (i = 1, j = 1; i < descriptions.length; i++) {', ' if (descriptions[i - 1] !== descriptions[i]) {', ' descriptions[j] = descriptions[i];', ' j++;', ' }', ' }', ' descriptions.length = j;', ' }', '', ' switch (descriptions.length) {', ' case 1:', ' return descriptions[0];', '', ' case 2:', ' return descriptions[0] + " or " + descriptions[1];', '', ' default:', ' return descriptions.slice(0, -1).join(", ")', ' + ", or "', ' + descriptions[descriptions.length - 1];', ' }', ' }', '', ' function describeFound(found) {', ' return found ? "\\"" + literalEscape(found) + "\\"" : "end of input";', ' }', '', ' return "Expected " + describeExpected(expected) + " but " + describeFound(found) + " found.";', '};', '' ].join('\n')); 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([ 'function peg$parse(input, options) {', ' options = options !== undefined ? options : {};', '', ' var peg$FAILED = {};', '' ].join('\n')); if (options.optimize === "size") { let startRuleIndices = '{ ' + options.allowedStartRules.map( r => r + ': ' + asts.indexOfRule(ast, r) ).join(', ') + ' }'; let startRuleIndex = asts.indexOfRule(ast, options.allowedStartRules[0]); parts.push([ ' var peg$startRuleIndices = ' + startRuleIndices + ';', ' var peg$startRuleIndex = ' + startRuleIndex + ';' ].join('\n')); } else { let startRuleFunctions = '{ ' + options.allowedStartRules.map( r => r + ': peg$parse' + r ).join(', ') + ' }'; let startRuleFunction = 'peg$parse' + options.allowedStartRules[0]; parts.push([ ' var peg$startRuleFunctions = ' + startRuleFunctions + ';', ' var peg$startRuleFunction = ' + startRuleFunction + ';' ].join('\n')); } parts.push(''); parts.push(indent2(generateTables())); parts.push([ '', ' var peg$currPos = 0;', ' var peg$savedPos = 0;', ' var peg$posDetailsCache = [{ line: 1, column: 1 }];', ' var peg$maxFailPos = 0;', ' var peg$maxFailExpected = [];', ' var peg$silentFails = 0;', // 0 = report failures, > 0 = silence failures '' ].join('\n')); if (options.cache) { parts.push([ ' var peg$resultsCache = {};', '' ].join('\n')); } if (options.trace) { if (options.optimize === "size") { let ruleNames = '[' + ast.rules.map( r => '"' + js.stringEscape(r.name) + '"' ).join(', ') + ']'; parts.push([ ' var peg$ruleNames = ' + ruleNames + ';', '' ].join('\n')); } parts.push([ ' var peg$tracer = "tracer" in options ? options.tracer : new peg$DefaultTracer();', '' ].join('\n')); } parts.push([ ' var 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')); } 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 !== undefined ? location : peg$computeLocation(peg$savedPos, peg$currPos)', '', ' throw peg$buildStructuredError(', ' [peg$otherExpectation(description)],', ' input.substring(peg$savedPos, peg$currPos),', ' location', ' );', ' }', '', ' function error(message, location) {', ' location = location !== undefined ? location : peg$computeLocation(peg$savedPos, peg$currPos)', '', ' throw peg$buildSimpleError(message, location);', ' }', '', ' function peg$literalExpectation(text, ignoreCase) {', ' return { type: "literal", text: text, ignoreCase: ignoreCase };', ' }', '', ' function peg$classExpectation(parts, inverted, ignoreCase) {', ' return { type: "class", parts: parts, inverted: inverted, ignoreCase: ignoreCase };', ' }', '', ' function peg$anyExpectation() {', ' return { type: "any" };', ' }', '', ' function peg$endExpectation() {', ' return { type: "end" };', ' }', '', ' function peg$otherExpectation(description) {', ' return { type: "other", description: description };', ' }', '', ' function peg$computePosDetails(pos) {', ' var details = peg$posDetailsCache[pos];', ' var 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);', ' var 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$buildSimpleError(message, location) {', ' return new peg$SyntaxError(message, null, null, location);', ' }', '', ' function peg$buildStructuredError(expected, found, location) {', ' return new peg$SyntaxError(', ' peg$SyntaxError.buildMessage(expected, found),', ' expected,', ' found,', ' location', ' );', ' }', '' ].join('\n')); if (options.optimize === "size") { parts.push(indent2(generateInterpreter())); parts.push(''); } else { ast.rules.forEach(rule => { parts.push(indent2(generateRuleFunction(rule))); 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();'); } 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(peg$endExpectation());', ' }', '', ' throw peg$buildStructuredError(', ' peg$maxFailExpected,', ' peg$maxFailPos < input.length ? input.charAt(peg$maxFailPos) : null,', ' peg$maxFailPos < input.length', ' ? peg$computeLocation(peg$maxFailPos, peg$maxFailPos + 1)', ' : peg$computeLocation(peg$maxFailPos, peg$maxFailPos)', ' );', ' }', '}' ].join('\n')); return parts.join('\n'); } function generateWrapper(toplevelCode) { function generateGeneratedByComment() { return [ '// Generated by PEG.js 0.10.0.', '//', '// http://pegjs.org/' ].join('\n'); } function generateParserObject() { return options.trace ? [ '{', ' SyntaxError: peg$SyntaxError,', ' DefaultTracer: peg$DefaultTracer,', ' parse: peg$parse', '}' ].join('\n') : [ '{', ' SyntaxError: peg$SyntaxError,', ' parse: peg$parse', '}' ].join('\n'); } let generators = { bare: function() { return [ generateGeneratedByComment(), '(function() {', ' "use strict";', '', indent2(toplevelCode), '', indent2('return ' + generateParserObject() + ';'), '})()' ].join('\n'); }, commonjs: function() { let parts = []; let dependencyVars = Object.keys(options.dependencies); parts.push([ generateGeneratedByComment(), '', '"use strict";', '' ].join('\n')); if (dependencyVars.length > 0) { dependencyVars.forEach(variable => { parts.push('var ' + variable + ' = require("' + js.stringEscape(options.dependencies[variable]) + '");' ); }); parts.push(''); } parts.push([ toplevelCode, '', 'module.exports = ' + generateParserObject() + ';', '' ].join('\n')); return parts.join('\n'); }, amd: function() { let dependencyVars = Object.keys(options.dependencies); let dependencyIds = dependencyIds.map(v => options.dependencies[v]); let dependencies = '[' + dependencyIds.map( id => '"' + js.stringEscape(id) + '"' ).join(', ') + ']'; let params = dependencyVars.join(', '); return [ generateGeneratedByComment(), 'define(' + dependencies + ', function(' + params + ') {', ' "use strict";', '', indent2(toplevelCode), '', indent2('return ' + generateParserObject() + ';'), '});', '' ].join('\n'); }, globals: function() { return [ generateGeneratedByComment(), '(function(root) {', ' "use strict";', '', indent2(toplevelCode), '', indent2('root.' + options.exportVar + ' = ' + generateParserObject() + ';'), '})(this);', '' ].join('\n'); }, umd: function() { let parts = []; let dependencyVars = Object.keys(options.dependencies); let dependencyIds = dependencyIds.map(v => options.dependencies[v]); let dependencies = '[' + dependencyIds.map( id => '"' + js.stringEscape(id) + '"' ).join(', ') + ']'; let requires = dependencyIds.map( id => 'require("' + js.stringEscape(id) + '")' ).join(', '); let params = dependencyVars.join(', '); parts.push([ generateGeneratedByComment(), '(function(root, factory) {', ' if (typeof define === "function" && define.amd) {', ' define(' + dependencies + ', factory);', ' } else if (typeof module === "object" && module.exports) {', ' module.exports = factory(' + requires + ');' ].join('\n')); if (options.exportVar !== null) { parts.push([ ' } else {', ' root.' + options.exportVar + ' = factory();' ].join('\n')); } parts.push([ ' }', '})(this, function(' + params + ') {', ' "use strict";', '', indent2(toplevelCode), '', indent2('return ' + generateParserObject() + ';'), '});', '' ].join('\n')); return parts.join('\n'); } }; return generators[options.format](); } ast.code = generateWrapper(generateToplevel()); } module.exports = generateJS;