var utils = require("../../utils"), op = require("../opcodes"); /* Generates parser JavaScript code. */ module.exports = function(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 indent10(code) { return code.replace(/^(.+)$/gm, ' $1'); } function generateTables() { if (options.optimize === "size") { return [ 'peg$consts = [', indent2(ast.consts.join(',\n')), '],', '', 'peg$bytecode = [', indent2(utils.map( ast.rules, function(rule) { return 'peg$decode(' + utils.quote(utils.map( rule.bytecode, function(b) { return String.fromCharCode(b + 32); } ).join('')) + ')'; } ).join(',\n')), '],' ].join('\n'); } else { return utils.map( ast.consts, function(c, i) { return 'peg$c' + i + ' = ' + c + ','; } ).join('\n'); } } function generateCacheHeader(ruleIndexCode) { return [ 'var key = peg$currPos * ' + ast.rules.length + ' + ' + ruleIndexCode + ',', ' cached = peg$cache[key];', '', 'if (cached) {', ' peg$currPos = cached.nextPos;', ' return cached.result;', '}', '' ].join('\n'); } function generateCacheFooter(resultCode) { return [ '', 'peg$cache[key] = { nextPos: peg$currPos, result: ' + resultCode + ' };' ].join('\n'); } function generateInterpreter() { var parts = []; function generateCondition(cond, argsLength) { var baseLength = argsLength + 3, thenLengthCode = 'bc[ip + ' + (baseLength - 2) + ']', 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) { var baseLength = 2, 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() { var baseLength = 4, paramsLengthCode = 'bc[ip + ' + (baseLength - 1) + ']'; return [ 'params = bc.slice(ip + ' + baseLength + ', ip + ' + baseLength + ' + ' + paramsLengthCode + ');', 'for (i = 0; i < ' + paramsLengthCode + '; i++) {', ' params[i] = stack[stack.length - 1 - params[i]];', '}', '', '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) {', ' var bc = new Array(s.length), i;', '', ' for (i = 0; i < s.length; i++) {', ' bc[i] = s.charCodeAt(i) - 32;', ' }', '', ' return bc;', '}', '', 'function peg$parseRule(index) {', ' var bc = peg$bytecode[index],', ' ip = 0,', ' ips = [],', ' end = bc.length,', ' ends = [],', ' stack = [],', ' params, i;', '' ].join('\n')); if (options.cache) { parts.push(indent2(generateCacheHeader('index'))); } parts.push([ ' function protect(object) {', ' return Object.prototype.toString.apply(object) === "[object Array]" ? [] : object;', ' }', '', /* * 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 /* * Hack: One of the constants can be an empty array. It needs to be cloned * because it can be modified later on the stack by |APPEND|. */ ' stack.push(protect(peg$consts[bc[ip + 1]]));', ' ip += 2;', ' 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.NIP_CURR_POS + ':', // NIP_CURR_POS ' peg$currPos = stack.splice(-2, 1)[0];', ' 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]));', ' ip += 2;', ' break;', '', ' case ' + op.TEXT + ':', // TEXT ' stack.pop();', ' stack.push(input.substring(stack[stack.length - 1], 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] === null', 0 )), '', ' case ' + op.IF_NOT_ERROR + ':', // IF_NOT_ERROR t, f indent10( generateCondition('stack[stack.length - 1] !== null', 0 )), '', ' case ' + op.WHILE_NOT_ERROR + ':', // WHILE_NOT_ERROR b indent10(generateLoop('stack[stack.length - 1] !== null')), '', ' 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(null);', ' if (peg$silentFails === 0) {', ' peg$fail(peg$consts[bc[ip + 1]]);', ' }', ' ip += 2;', ' break;', '', ' case ' + op.REPORT_SAVED_POS + ':', // REPORT_SAVED_POS p ' peg$reportedPos = stack[stack.length - 1 - bc[ip + 1]];', ' ip += 2;', ' break;', '', ' case ' + op.REPORT_CURR_POS + ':', // REPORT_CURR_POS ' peg$reportedPos = 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')); if (options.cache) { parts.push(indent2(generateCacheFooter('stack[0]'))); } parts.push([ '', ' return stack[0];', '}' ].join('\n')); return parts.join('\n'); } function generateRuleFunction(rule) { var parts = [], 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 var stack = { sp: -1, maxSp: -1, push: function(exprCode) { var code = s(++this.sp) + ' = ' + exprCode + ';'; if (this.sp > this.maxSp) { this.maxSp = this.sp; } return code; }, pop: function() { var n, values; if (arguments.length === 0) { return s(this.sp--); } else { n = arguments[0]; values = utils.map(utils.range(this.sp - n + 1, this.sp + 1), s); this.sp -= n; return values; } }, top: function() { return s(this.sp); }, index: function(i) { return s(this.sp - i); } }; function compile(bc) { var ip = 0, end = bc.length, parts = [], value; function compileCondition(cond, argCount) { var baseLength = argCount + 3, thenLength = bc[ip + baseLength - 2], elseLength = bc[ip + baseLength - 1], baseSp = stack.sp, thenCode, elseCode; ip += baseLength; thenCode = compile(bc.slice(ip, ip + thenLength)); ip += thenLength; if (elseLength > 0) { stack.sp = baseSp; elseCode = compile(bc.slice(ip, ip + elseLength)); ip += elseLength; } parts.push('if (' + cond + ') {'); parts.push(indent2(thenCode)); if (elseLength > 0) { parts.push('} else {'); parts.push(indent2(elseCode)); } parts.push('}'); } function compileLoop(cond) { var baseLength = 2, bodyLength = bc[ip + baseLength - 1], bodyCode; ip += baseLength; bodyCode = compile(bc.slice(ip, ip + bodyLength)); ip += bodyLength; parts.push('while (' + cond + ') {'); parts.push(indent2(bodyCode)); parts.push('}'); } function compileCall(cond) { var baseLength = 4, paramsLength = bc[ip + baseLength - 1]; var value = c(bc[ip + 1]) + '(' + utils.map( bc.slice(ip + baseLength, ip + baseLength + paramsLength), stackIndex ) + ')'; stack.pop(bc[ip + 2]); parts.push(stack.push(value)); ip += baseLength + paramsLength; } /* * Extracted into a function just to silence JSHint complaining about * creating functions in a loop. */ function stackIndex(p) { return stack.index(p); } while (ip < end) { switch (bc[ip]) { case op.PUSH: // PUSH c /* * Hack: One of the constants can be an empty array. It needs to be * handled specially because it can be modified later on the stack * by |APPEND|. */ parts.push( stack.push(ast.consts[bc[ip + 1]] === "[]" ? "[]" : 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.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.NIP_CURR_POS: // NIP_CURR_POS value = stack.pop(); parts.push('peg$currPos = ' + 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 stack.pop(); parts.push( stack.push('input.substring(' + stack.top() + ', 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() + ' === null', 0); break; case op.IF_NOT_ERROR: // IF_NOT_ERROR t, f compileCondition(stack.top() + ' !== null', 0); break; case op.WHILE_NOT_ERROR: // WHILE_NOT_ERROR b compileLoop(stack.top() + ' !== null', 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, ' + 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('null')); parts.push('if (peg$silentFails === 0) { peg$fail(' + c(bc[ip + 1]) + '); }'); ip += 2; break; case op.REPORT_SAVED_POS: // REPORT_SAVED_POS p parts.push('peg$reportedPos = ' + stack.index(bc[ip + 1]) + ';'); ip += 2; break; case op.REPORT_CURR_POS: // REPORT_CURR_POS parts.push('peg$reportedPos = 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 + '() {', ' var ' + utils.map(utils.range(0, stack.maxSp + 1), s).join(', ') + ';', '' ].join('\n')); if (options.cache) { parts.push(indent2( generateCacheHeader(utils.indexOfRuleByName(ast, rule.name)) )); } parts.push(indent2(code)); if (options.cache) { parts.push(indent2(generateCacheFooter('s0'))); } parts.push([ '', ' return s0;', '}' ].join('\n')); return parts.join('\n'); } var parts = [], startRuleIndices, startRuleIndex, startRuleFunctions, startRuleFunction; parts.push([ '(function() {', ' /*', ' * Generated by PEG.js 0.7.0.', ' *', ' * http://pegjs.majda.cz/', ' */', '', ' function peg$subclass(child, parent) {', ' function ctor() { this.constructor = child; }', ' ctor.prototype = parent.prototype;', ' child.prototype = new ctor();', ' }', '', ' function SyntaxError(expected, found, offset, line, column) {', ' function buildMessage(expected, found) {', ' function stringEscape(s) {', ' function hex(ch) { return ch.charCodeAt(0).toString(16).toUpperCase(); }', '', /* * ECMA-262, 5th ed., 7.8.4: All characters may appear literally in a string * literal except for the closing quote character, backslash, carriage * return, line separator, paragraph separator, and line feed. Any character * may appear in the form of an escape sequence. * * For portability, we also escape escape all control and non-ASCII * characters. Note that "\0" and "\v" escape sequences are not used because * JSHint does not like the first and IE the second. */ ' return s', ' .replace(/\\\\/g, \'\\\\\\\\\')', // backslash ' .replace(/"/g, \'\\\\"\')', // closing double quote ' .replace(/\\x08/g, \'\\\\b\')', // backspace ' .replace(/\\t/g, \'\\\\t\')', // horizontal tab ' .replace(/\\n/g, \'\\\\n\')', // line feed ' .replace(/\\f/g, \'\\\\f\')', // form feed ' .replace(/\\r/g, \'\\\\r\')', // carriage return ' .replace(/[\\x00-\\x07\\x0B\\x0E\\x0F]/g, function(ch) { return \'\\\\x0\' + hex(ch); })', ' .replace(/[\\x10-\\x1F\\x80-\\xFF]/g, function(ch) { return \'\\\\x\' + hex(ch); })', ' .replace(/[\\u0180-\\u0FFF]/g, function(ch) { return \'\\\\u0\' + hex(ch); })', ' .replace(/[\\u1080-\\uFFFF]/g, function(ch) { return \'\\\\u\' + hex(ch); });', ' }', '', ' var expectedDesc, foundDesc;', '', ' switch (expected.length) {', ' case 0:', ' expectedDesc = "end of input";', ' break;', '', ' case 1:', ' expectedDesc = expected[0];', ' break;', '', ' default:', ' expectedDesc = expected.slice(0, -1).join(", ")', ' + " or "', ' + expected[expected.length - 1];', ' }', '', ' foundDesc = found ? "\\"" + stringEscape(found) + "\\"" : "end of input";', '', ' return "Expected " + expectedDesc + " but " + foundDesc + " found.";', ' }', '', ' this.expected = expected;', ' this.found = found;', ' this.offset = offset;', ' this.line = line;', ' this.column = column;', '', ' this.name = "SyntaxError";', ' this.message = buildMessage(expected, found);', ' }', '', ' peg$subclass(SyntaxError, Error);', '', ' function parse(input) {', ' var options = arguments.length > 1 ? arguments[1] : {},', '' ].join('\n')); if (options.optimize === "size") { startRuleIndices = '{ ' + utils.map( options.allowedStartRules, function(r) { return r + ': ' + utils.indexOfRuleByName(ast, r); } ).join(', ') + ' }'; startRuleIndex = utils.indexOfRuleByName(ast, options.allowedStartRules[0]); parts.push([ ' peg$startRuleIndices = ' + startRuleIndices + ',', ' peg$startRuleIndex = ' + startRuleIndex + ',' ].join('\n')); } else { startRuleFunctions = '{ ' + utils.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')); } parts.push(''); parts.push(indent8(generateTables())); parts.push([ '', ' peg$currPos = 0,', ' peg$reportedPos = 0,', ' peg$cachedPos = 0,', ' peg$cachedPosDetails = { line: 1, column: 1, seenCR: false },', ' peg$maxFailPos = 0,', ' peg$maxFailExpected = [],', ' peg$silentFails = 0,', // 0 = report failures, > 0 = silence failures '' ].join('\n')); if (options.cache) { parts.push(' peg$cache = {},'); } 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')); } parts.push([ '', ' function text() {', ' return input.substring(peg$reportedPos, peg$currPos);', ' }', '', ' function offset() {', ' return peg$reportedPos;', ' }', '', ' function line() {', ' return peg$computePosDetails(peg$reportedPos).line;', ' }', '', ' function column() {', ' return peg$computePosDetails(peg$reportedPos).column;', ' }', '', ' function peg$computePosDetails(pos) {', ' function advance(details, pos) {', ' var p, ch;', '', ' for (p = 0; p < pos; p++) {', ' ch = input.charAt(p);', ' if (ch === "\\n") {', ' if (!details.seenCR) { details.line++; }', ' details.column = 1;', ' details.seenCR = false;', ' } else if (ch === "\\r" || ch === "\\u2028" || ch === "\\u2029") {', ' details.line++;', ' details.column = 1;', ' details.seenCR = true;', ' } else {', ' details.column++;', ' details.seenCR = false;', ' }', ' }', ' }', '', ' if (peg$cachedPos !== pos) {', ' if (peg$cachedPos > pos) {', ' peg$cachedPos = 0;', ' peg$cachedPosDetails = { line: 1, column: 1, seenCR: false };', ' }', ' peg$cachedPos = pos;', ' advance(peg$cachedPosDetails, peg$cachedPos);', ' }', '', ' return peg$cachedPosDetails;', ' }', '', ' 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$cleanupExpected(expected) {', ' var i;', '', ' expected.sort();', '', ' for (i = 1; i < expected.length; i++) {', ' if (expected[i - 1] === expected[i]) {', ' expected.splice(i, 1);', ' }', ' }', ' }', '' ].join('\n')); if (options.optimize === "size") { parts.push(indent4(generateInterpreter())); parts.push(''); } else { utils.each(ast.rules, function(rule) { parts.push(indent4(generateRuleFunction(rule))); parts.push(''); }); } if (ast.initializer) { parts.push(indent4(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 !== null && peg$currPos === input.length) {', ' return peg$result;', ' } else {', ' peg$cleanupExpected(peg$maxFailExpected);', ' peg$reportedPos = Math.max(peg$currPos, peg$maxFailPos);', '', ' throw new SyntaxError(', ' peg$maxFailExpected,', ' peg$reportedPos < input.length ? input.charAt(peg$reportedPos) : null,', ' peg$reportedPos,', ' peg$computePosDetails(peg$reportedPos).line,', ' peg$computePosDetails(peg$reportedPos).column', ' );', ' }', ' }', '', ' return {', ' SyntaxError: SyntaxError,', ' parse : parse', ' };', '})()' ].join('\n')); ast.code = parts.join('\n'); };