AST refactoring 4/6: Rewrite compilation to not extend the AST nodes

redux
David Majda 14 years ago
parent 5885a34dde
commit 41abb7ad92

@ -47,8 +47,8 @@ PEG.ArrayUtils = {
}, },
/* /*
* The code needs to be in sync with a code template in * The code needs to be in sync with the code template in the compilation
* PEG.Grammar.Action.prototype.compile. * function for "action" nodes.
*/ */
contains: function(array, value) { contains: function(array, value) {
/* /*
@ -90,8 +90,8 @@ PEG.StringUtils = {
* Surrounds the string with quotes and escapes characters inside so that the * Surrounds the string with quotes and escapes characters inside so that the
* result is a valid JavaScript string. * result is a valid JavaScript string.
* *
* The code needs to be in sync with a code template in * The code needs to be in sync with th code template in the compilation
* PEG.Grammar.Action.prototype.compile. * function for "action" nodes.
*/ */
quote: function(s) { quote: function(s) {
/* /*
@ -447,234 +447,11 @@ PEG.Compiler = {
} }
], ],
/* _compileFunctions: {
* Generates a parser from a specified grammar AST and start rule. Throws rule: function(node) {
* |PEG.Grammar.GrammarError| if the AST contains a semantic error. Note that
* not all errors are detected during the generation and some may protrude to
* the generated parser and cause its malfunction.
*/
compileParser: function(ast, startRule) {
/*
* This ensures that the same grammar and start rule always generate exactly
* the same parser.
*/
this._resetUniqueIdentifierCounters();
for (var i = 0; i < this._checks.length; i++) {
this._checks[i](ast, startRule);
}
var parseFunctionDefinitions = [];
for (var rule in ast) {
parseFunctionDefinitions.push(ast[rule].compile());
}
var source = this.formatCode(
"(function(){",
" /* Generated by PEG.js (http://pegjs.majda.cz/). */",
" ",
" var result = {",
" _startRule: ${startRule|string},",
" ",
/* This needs to be in sync with PEG.StringUtils.quote. */
" _quoteString: function(s) {",
" /*",
" * 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.",
" */",
" return '\"' + s",
" .replace(/\\\\/g, '\\\\\\\\') // backslash",
" .replace(/\"/g, '\\\\\"') // closing quote character",
" .replace(/\\r/g, '\\\\r') // carriage return",
" .replace(/\\u2028/g, '\\\\u2028') // line separator",
" .replace(/\\u2029/g, '\\\\u2029') // paragraph separator",
" .replace(/\\n/g, '\\\\n') // line feed",
" + '\"';",
" },",
" ",
/* This needs to be in sync with PEG.ArrayUtils.contains. */
" _arrayContains: function(array, value) {",
" /*",
" * Stupid IE does not have Array.prototype.indexOf, otherwise this function",
" * would be a one-liner.",
" */",
" var length = array.length;",
" for (var i = 0; i < length; i++) {",
" if (array[i] === value) {",
" return true;",
" }",
" }",
" return false;",
" },",
" ",
" _matchFailed: function(failure) {",
" if (this._pos > this._rightmostMatchFailuresPos) {",
" this._rightmostMatchFailuresPos = this._pos;",
" this._rightmostMatchFailuresExpected = [];",
" }",
" ",
" if (!this._arrayContains(this._rightmostMatchFailuresExpected, failure)) {",
" this._rightmostMatchFailuresExpected.push(failure);",
" }",
" },",
" ",
" ${parseFunctionDefinitions}",
" ",
" /*",
" * Parses the input with a generated parser. If the parsing is successfull,",
" * returns a value explicitly or implicitly specified by the grammar from",
" * which the parser was generated (see |PEG.buildParser|). If the parsing is",
" * unsuccessful, throws |PEG.grammarParser.SyntaxError| describing the error.",
" */",
" parse: function(input) {",
" var that = this;",
" ",
" function initialize() {",
" that._input = input;",
" that._pos = 0;",
" that._rightmostMatchFailuresPos = 0;",
" that._rightmostMatchFailuresExpected = [];",
" that._cache = {};",
" }",
" ",
" function buildErrorMessage() {",
" function buildExpected(failuresExpected) {",
" switch (failuresExpected.length) {",
" case 0:",
" return 'end of input';",
" case 1:",
" return failuresExpected[0];",
" default:",
" failuresExpected.sort();",
" return failuresExpected.slice(0, failuresExpected.length - 1).join(', ')",
" + ' or '",
" + failuresExpected[failuresExpected.length - 1];",
" }",
" }",
" ",
" var expected = buildExpected(that._rightmostMatchFailuresExpected);",
" var pos = Math.max(that._pos, that._rightmostMatchFailuresPos);",
" var actual = pos < that._input.length",
" ? that._quoteString(that._input.charAt(pos))",
" : 'end of input';",
" ",
" return 'Expected ' + expected + ' but ' + actual + ' found.';",
" }",
" ",
" function computeErrorPosition() {",
" /*",
" * The first idea was to use |String.split| to break the input up to the",
" * error position along newlines and derive the line and column from",
" * there. However IE's |split| implementation is so broken that it was",
" * enough to prevent it.",
" */",
" ",
" var input = that._input;",
" var pos = that._rightmostMatchFailuresPos;",
" var line = 1;",
" var column = 1;",
" var seenCR = false;",
" ",
" for (var i = 0; i < pos; i++) {",
" var ch = input.charAt(i);",
" if (ch === '\\n') {",
" if (!seenCR) { line++; }",
" column = 1;",
" seenCR = false;",
" } else if (ch === '\\r' | ch === '\\u2028' || ch === '\\u2029') {",
" line++;",
" column = 1;",
" seenCR = true;",
" } else {",
" column++;",
" seenCR = false;",
" }",
" }",
" ",
" return { line: line, column: column };",
" }",
" ",
" initialize();",
" ",
" var initialContext = {",
" reportMatchFailures: true",
" };",
" ",
" var result = this['_parse_' + this._startRule](initialContext);",
" ",
" /*",
" * The parser is now in one of the following three states:",
" *",
" * 1. The parser successfully parsed the whole input.",
" *",
" * - |result !== null|",
" * - |that._pos === input.length|",
" * - |that._rightmostMatchFailuresExpected.length| may or may not contain",
" * something",
" *",
" * 2. The parser successfully parsed only a part of the input.",
" *",
" * - |result !== null|",
" * - |that._pos < input.length|",
" * - |that._rightmostMatchFailuresExpected.length| may or may not contain",
" * something",
" *",
" * 3. The parser did not successfully parse any part of the input.",
" *",
" * - |result === null|",
" * - |that._pos === 0|",
" * - |that._rightmostMatchFailuresExpected.length| contains at least one failure",
" *",
" * All code following this comment (including called functions) must",
" * handle these states.",
" */",
" if (result === null || this._pos !== input.length) {",
" var errorPosition = computeErrorPosition();",
" throw new this.SyntaxError(",
" buildErrorMessage(),",
" errorPosition.line,",
" errorPosition.column",
" );",
" }",
" ",
" return result;",
" },",
" ",
" /* Returns the parser source code. */",
" toSource: function() { return this._source; }",
" };",
" ",
" /* Thrown when a parser encounters a syntax error. */",
" ",
" result.SyntaxError = function(message, line, column) {",
" this.name = 'SyntaxError';",
" this.message = message;",
" this.line = line;",
" this.column = column;",
" };",
" ",
" result.SyntaxError.prototype = Error.prototype;",
" ",
" return result;",
"})()",
{
parseFunctionDefinitions: parseFunctionDefinitions.join("\n\n"),
startRule: startRule
}
);
var result = eval(source);
result._source = source;
return result;
}
};
PEG.Grammar.Rule.prototype.compile = function() {
var resultVar = PEG.Compiler.generateUniqueIdentifier("result"); var resultVar = PEG.Compiler.generateUniqueIdentifier("result");
if (this.displayName !== null) { if (node.displayName !== null) {
var setReportMatchFailuresCode = PEG.Compiler.formatCode( var setReportMatchFailuresCode = PEG.Compiler.formatCode(
"var savedReportMatchFailures = context.reportMatchFailures;", "var savedReportMatchFailures = context.reportMatchFailures;",
"context.reportMatchFailures = false;" "context.reportMatchFailures = false;"
@ -687,7 +464,7 @@ PEG.Grammar.Rule.prototype.compile = function() {
" this._matchFailed(${displayName|string});", " this._matchFailed(${displayName|string});",
"}", "}",
{ {
displayName: this.displayName, displayName: node.displayName,
resultVar: resultVar resultVar: resultVar
} }
); );
@ -720,26 +497,26 @@ PEG.Grammar.Rule.prototype.compile = function() {
" return ${resultVar};", " return ${resultVar};",
"},", "},",
{ {
name: this.name, name: node.name,
setReportMatchFailuresCode: setReportMatchFailuresCode, setReportMatchFailuresCode: setReportMatchFailuresCode,
restoreReportMatchFailuresCode: restoreReportMatchFailuresCode, restoreReportMatchFailuresCode: restoreReportMatchFailuresCode,
reportMatchFailureCode: reportMatchFailureCode, reportMatchFailureCode: reportMatchFailureCode,
code: this.expression.compile(resultVar), code: PEG.Compiler.compileNode(node.expression, resultVar),
resultVar: resultVar resultVar: resultVar
} }
); );
}; },
/* /*
* The contract for all code fragments generated by the following |compile| * The contract for all code fragments generated by the following functions
* methods is as follows: * is as follows:
* *
* * The code fragment should try to match a part of the input starting with the * * The code fragment should try to match a part of the input starting with
* position indicated in |this._pos|. That position may point past the end of * the position indicated in |this._pos|. That position may point past the
* the input. * end of the input.
* *
* * If the code fragment matches the input, it advances |this._pos| after the * * If the code fragment matches the input, it advances |this._pos| after
* matched part of the input and sets variable with a name stored in * the matched part of the input and sets variable with a name stored in
* |resultVar| to appropriate value, which is always non-null. * |resultVar| to appropriate value, which is always non-null.
* *
* * If the code fragment does not match the input, it does not change * * If the code fragment does not match the input, it does not change
@ -747,13 +524,13 @@ PEG.Grammar.Rule.prototype.compile = function() {
* |null|. * |null|.
*/ */
PEG.Grammar.Choice.prototype.compile = function(resultVar) { choice: function(node, resultVar) {
var code = PEG.Compiler.formatCode( var code = PEG.Compiler.formatCode(
"var ${resultVar} = null;", "var ${resultVar} = null;",
{ resultVar: resultVar } { resultVar: resultVar }
); );
for (var i = this.alternatives.length - 1; i >= 0; i--) { for (var i = node.alternatives.length - 1; i >= 0; i--) {
var alternativeResultVar = PEG.Compiler.generateUniqueIdentifier("result"); var alternativeResultVar = PEG.Compiler.generateUniqueIdentifier("result");
code = PEG.Compiler.formatCode( code = PEG.Compiler.formatCode(
"${alternativeCode}", "${alternativeCode}",
@ -763,7 +540,7 @@ PEG.Grammar.Choice.prototype.compile = function(resultVar) {
" ${code};", " ${code};",
"}", "}",
{ {
alternativeCode: this.alternatives[i].compile(alternativeResultVar), alternativeCode: PEG.Compiler.compileNode(node.alternatives[i], alternativeResultVar),
alternativeResultVar: alternativeResultVar, alternativeResultVar: alternativeResultVar,
code: code, code: code,
resultVar: resultVar resultVar: resultVar
@ -772,12 +549,12 @@ PEG.Grammar.Choice.prototype.compile = function(resultVar) {
} }
return code; return code;
}; },
PEG.Grammar.Sequence.prototype.compile = function(resultVar) { sequence: function(node, resultVar) {
var savedPosVar = PEG.Compiler.generateUniqueIdentifier("savedPos"); var savedPosVar = PEG.Compiler.generateUniqueIdentifier("savedPos");
var elementResultVars = PEG.ArrayUtils.map(this.elements, function() { var elementResultVars = PEG.ArrayUtils.map(node.elements, function() {
return PEG.Compiler.generateUniqueIdentifier("result") return PEG.Compiler.generateUniqueIdentifier("result")
}); });
@ -789,7 +566,7 @@ PEG.Grammar.Sequence.prototype.compile = function(resultVar) {
} }
); );
for (var i = this.elements.length - 1; i >= 0; i--) { for (var i = node.elements.length - 1; i >= 0; i--) {
code = PEG.Compiler.formatCode( code = PEG.Compiler.formatCode(
"${elementCode}", "${elementCode}",
"if (${elementResultVar} !== null) {", "if (${elementResultVar} !== null) {",
@ -799,7 +576,7 @@ PEG.Grammar.Sequence.prototype.compile = function(resultVar) {
" this._pos = ${savedPosVar};", " this._pos = ${savedPosVar};",
"}", "}",
{ {
elementCode: this.elements[i].compile(elementResultVars[i]), elementCode: PEG.Compiler.compileNode(node.elements[i], elementResultVars[i]),
elementResultVar: elementResultVars[i], elementResultVar: elementResultVars[i],
code: code, code: code,
savedPosVar: savedPosVar, savedPosVar: savedPosVar,
@ -816,9 +593,9 @@ PEG.Grammar.Sequence.prototype.compile = function(resultVar) {
savedPosVar: savedPosVar savedPosVar: savedPosVar
} }
); );
}; },
PEG.Grammar.AndPredicate.prototype.compile = function(resultVar) { and_predicate: function(node, resultVar) {
var savedPosVar = PEG.Compiler.generateUniqueIdentifier("savedPos"); var savedPosVar = PEG.Compiler.generateUniqueIdentifier("savedPos");
var savedReportMatchFailuresVar = PEG.Compiler.generateUniqueIdentifier("savedReportMatchFailuresVar"); var savedReportMatchFailuresVar = PEG.Compiler.generateUniqueIdentifier("savedReportMatchFailuresVar");
var expressionResultVar = PEG.Compiler.generateUniqueIdentifier("result"); var expressionResultVar = PEG.Compiler.generateUniqueIdentifier("result");
@ -836,16 +613,16 @@ PEG.Grammar.AndPredicate.prototype.compile = function(resultVar) {
" var ${resultVar} = null;", " var ${resultVar} = null;",
"}", "}",
{ {
expressionCode: this.expression.compile(expressionResultVar), expressionCode: PEG.Compiler.compileNode(node.expression, expressionResultVar),
expressionResultVar: expressionResultVar, expressionResultVar: expressionResultVar,
savedPosVar: savedPosVar, savedPosVar: savedPosVar,
savedReportMatchFailuresVar: savedReportMatchFailuresVar, savedReportMatchFailuresVar: savedReportMatchFailuresVar,
resultVar: resultVar resultVar: resultVar
} }
); );
}; },
PEG.Grammar.NotPredicate.prototype.compile = function(resultVar) { not_predicate: function(node, resultVar) {
var savedPosVar = PEG.Compiler.generateUniqueIdentifier("savedPos"); var savedPosVar = PEG.Compiler.generateUniqueIdentifier("savedPos");
var savedReportMatchFailuresVar = PEG.Compiler.generateUniqueIdentifier("savedReportMatchFailuresVar"); var savedReportMatchFailuresVar = PEG.Compiler.generateUniqueIdentifier("savedReportMatchFailuresVar");
var expressionResultVar = PEG.Compiler.generateUniqueIdentifier("result"); var expressionResultVar = PEG.Compiler.generateUniqueIdentifier("result");
@ -863,30 +640,30 @@ PEG.Grammar.NotPredicate.prototype.compile = function(resultVar) {
" this._pos = ${savedPosVar};", " this._pos = ${savedPosVar};",
"}", "}",
{ {
expressionCode: this.expression.compile(expressionResultVar), expressionCode: PEG.Compiler.compileNode(node.expression, expressionResultVar),
expressionResultVar: expressionResultVar, expressionResultVar: expressionResultVar,
savedPosVar: savedPosVar, savedPosVar: savedPosVar,
savedReportMatchFailuresVar: savedReportMatchFailuresVar, savedReportMatchFailuresVar: savedReportMatchFailuresVar,
resultVar: resultVar resultVar: resultVar
} }
); );
}; },
PEG.Grammar.Optional.prototype.compile = function(resultVar) { optional: function(node, resultVar) {
var expressionResultVar = PEG.Compiler.generateUniqueIdentifier("result"); var expressionResultVar = PEG.Compiler.generateUniqueIdentifier("result");
return PEG.Compiler.formatCode( return PEG.Compiler.formatCode(
"${expressionCode}", "${expressionCode}",
"var ${resultVar} = ${expressionResultVar} !== null ? ${expressionResultVar} : '';", "var ${resultVar} = ${expressionResultVar} !== null ? ${expressionResultVar} : '';",
{ {
expressionCode: this.expression.compile(expressionResultVar), expressionCode: PEG.Compiler.compileNode(node.expression, expressionResultVar),
expressionResultVar: expressionResultVar, expressionResultVar: expressionResultVar,
resultVar: resultVar resultVar: resultVar
} }
); );
}; },
PEG.Grammar.ZeroOrMore.prototype.compile = function(resultVar) { zero_or_more: function(node, resultVar) {
var expressionResultVar = PEG.Compiler.generateUniqueIdentifier("result"); var expressionResultVar = PEG.Compiler.generateUniqueIdentifier("result");
return PEG.Compiler.formatCode( return PEG.Compiler.formatCode(
@ -897,14 +674,14 @@ PEG.Grammar.ZeroOrMore.prototype.compile = function(resultVar) {
" ${expressionCode}", " ${expressionCode}",
"}", "}",
{ {
expressionCode: this.expression.compile(expressionResultVar), expressionCode: PEG.Compiler.compileNode(node.expression, expressionResultVar),
expressionResultVar: expressionResultVar, expressionResultVar: expressionResultVar,
resultVar: resultVar resultVar: resultVar
} }
); );
}; },
PEG.Grammar.OneOrMore.prototype.compile = function(resultVar) { one_or_more: function(node, resultVar) {
var expressionResultVar = PEG.Compiler.generateUniqueIdentifier("result"); var expressionResultVar = PEG.Compiler.generateUniqueIdentifier("result");
return PEG.Compiler.formatCode( return PEG.Compiler.formatCode(
@ -919,17 +696,17 @@ PEG.Grammar.OneOrMore.prototype.compile = function(resultVar) {
" var ${resultVar} = null;", " var ${resultVar} = null;",
"}", "}",
{ {
expressionCode: this.expression.compile(expressionResultVar), expressionCode: PEG.Compiler.compileNode(node.expression, expressionResultVar),
expressionResultVar: expressionResultVar, expressionResultVar: expressionResultVar,
resultVar: resultVar resultVar: resultVar
} }
); );
}; },
PEG.Grammar.Action.prototype.compile = function(resultVar) { action: function(node, resultVar) {
/* /*
* In case of sequences, we splat their elements into function arguments one * In case of sequences, we splat their elements into function arguments
* by one. Example: * one by one. Example:
* *
* start: "a" "b" "c" { alert(arguments.length) } // => 3 * start: "a" "b" "c" { alert(arguments.length) } // => 3
* *
@ -938,9 +715,9 @@ PEG.Grammar.Action.prototype.compile = function(resultVar) {
var expressionResultVar = PEG.Compiler.generateUniqueIdentifier("result"); var expressionResultVar = PEG.Compiler.generateUniqueIdentifier("result");
if (this.expression.type === "sequence") { if (node.expression instanceof PEG.Grammar.Sequence) {
var params = PEG.ArrayUtils.map( var params = PEG.ArrayUtils.map(
PEG.ArrayUtils.range(1, this.expression.elements.length + 1), PEG.ArrayUtils.range(1, node.expression.elements.length + 1),
function(n) { return "$" + n; } function(n) { return "$" + n; }
).join(", "); ).join(", ");
@ -948,7 +725,7 @@ PEG.Grammar.Action.prototype.compile = function(resultVar) {
"(function(${params}) { ${action} }).apply(null, ${expressionResultVar})", "(function(${params}) { ${action} }).apply(null, ${expressionResultVar})",
{ {
params: params, params: params,
action: this.action, action: node.action,
expressionResultVar: expressionResultVar expressionResultVar: expressionResultVar
} }
); );
@ -956,7 +733,7 @@ PEG.Grammar.Action.prototype.compile = function(resultVar) {
var invocationCode = PEG.Compiler.formatCode( var invocationCode = PEG.Compiler.formatCode(
"(function($1) { ${action} })(${expressionResultVar})", "(function($1) { ${action} })(${expressionResultVar})",
{ {
action: this.action, action: node.action,
expressionResultVar: expressionResultVar expressionResultVar: expressionResultVar
} }
); );
@ -968,25 +745,25 @@ PEG.Grammar.Action.prototype.compile = function(resultVar) {
" ? ${invocationCode}", " ? ${invocationCode}",
" : null;", " : null;",
{ {
expressionCode: this.expression.compile(expressionResultVar), expressionCode: PEG.Compiler.compileNode(node.expression, expressionResultVar),
expressionResultVar: expressionResultVar, expressionResultVar: expressionResultVar,
invocationCode: invocationCode, invocationCode: invocationCode,
resultVar: resultVar resultVar: resultVar
} }
); );
}; },
PEG.Grammar.RuleRef.prototype.compile = function(resultVar) { rule_ref: function(node, resultVar) {
return PEG.Compiler.formatCode( return PEG.Compiler.formatCode(
"var ${resultVar} = this.${ruleMethod}(context);", "var ${resultVar} = this.${ruleMethod}(context);",
{ {
ruleMethod: "_parse_" + this.name, ruleMethod: "_parse_" + node.name,
resultVar: resultVar resultVar: resultVar
} }
); );
}; },
PEG.Grammar.Literal.prototype.compile = function(resultVar) { literal: function(node, resultVar) {
return PEG.Compiler.formatCode( return PEG.Compiler.formatCode(
"if (this._input.substr(this._pos, ${length}) === ${value|string}) {", "if (this._input.substr(this._pos, ${length}) === ${value|string}) {",
" var ${resultVar} = ${value|string};", " var ${resultVar} = ${value|string};",
@ -998,14 +775,14 @@ PEG.Grammar.Literal.prototype.compile = function(resultVar) {
" }", " }",
"}", "}",
{ {
value: this.value, value: node.value,
length: this.value.length, length: node.value.length,
resultVar: resultVar resultVar: resultVar
} }
); );
}; },
PEG.Grammar.Any.prototype.compile = function(resultVar) { any: function(node, resultVar) {
return PEG.Compiler.formatCode( return PEG.Compiler.formatCode(
"if (this._input.length > this._pos) {", "if (this._input.length > this._pos) {",
" var ${resultVar} = this._input.charAt(this._pos);", " var ${resultVar} = this._input.charAt(this._pos);",
@ -1018,20 +795,19 @@ PEG.Grammar.Any.prototype.compile = function(resultVar) {
"}", "}",
{ resultVar: resultVar } { resultVar: resultVar }
); );
}; },
PEG.Grammar.Class.prototype.compile = function(resultVar) { "class": function(node, resultVar) {
/* /*
* Stupid IE considers regexps /[]/ and /[^]/ syntactically invalid, so we * Stupid IE considers regexps /[]/ and /[^]/ syntactically invalid, so we
* translate them into euqivalents it can handle. * translate them into euqivalents it can handle.
*/ */
if (this.characters === "") { if (node.characters === "") {
var regexp = "/^(?!)/"; var regexp = "/^(?!)/";
} else if (this.characters === "^") { } else if (node.characters === "^") {
var regexp = "/^[\\S\\s]/"; var regexp = "/^[\\S\\s]/";
} else { } else {
var regexp = "/^[" + this.characters + "]/"; var regexp = "/^[" + node.characters + "]/";
} }
return PEG.Compiler.formatCode( return PEG.Compiler.formatCode(
@ -1045,11 +821,245 @@ PEG.Grammar.Class.prototype.compile = function(resultVar) {
" }", " }",
"}", "}",
{ {
characters: this.characters, characters: node.characters,
regexp: regexp, regexp: regexp,
resultVar: resultVar resultVar: resultVar
} }
); );
}
},
/*
* Compiles an AST node and returns the generated code. The |resultVar|
* parameter contains a name of variable in which the match result will be
* stored in the generated code.
*/
compileNode: function(node, resultVar) {
return this._compileFunctions[node.type](node, resultVar);
},
/*
* Generates a parser from a specified grammar AST and start rule. Throws
* |PEG.Grammar.GrammarError| if the AST contains a semantic error. Note that
* not all errors are detected during the generation and some may protrude to
* the generated parser and cause its malfunction.
*/
compileParser: function(ast, startRule) {
/*
* This ensures that the same grammar and start rule always generate exactly
* the same parser.
*/
this._resetUniqueIdentifierCounters();
for (var i = 0; i < this._checks.length; i++) {
this._checks[i](ast, startRule);
}
var parseFunctionDefinitions = [];
for (var rule in ast) {
parseFunctionDefinitions.push(this.compileNode(ast[rule]));
}
var source = this.formatCode(
"(function(){",
" /* Generated by PEG.js (http://pegjs.majda.cz/). */",
" ",
" var result = {",
" _startRule: ${startRule|string},",
" ",
/* This needs to be in sync with PEG.StringUtils.quote. */
" _quoteString: function(s) {",
" /*",
" * 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.",
" */",
" return '\"' + s",
" .replace(/\\\\/g, '\\\\\\\\') // backslash",
" .replace(/\"/g, '\\\\\"') // closing quote character",
" .replace(/\\r/g, '\\\\r') // carriage return",
" .replace(/\\u2028/g, '\\\\u2028') // line separator",
" .replace(/\\u2029/g, '\\\\u2029') // paragraph separator",
" .replace(/\\n/g, '\\\\n') // line feed",
" + '\"';",
" },",
" ",
/* This needs to be in sync with PEG.ArrayUtils.contains. */
" _arrayContains: function(array, value) {",
" /*",
" * Stupid IE does not have Array.prototype.indexOf, otherwise this function",
" * would be a one-liner.",
" */",
" var length = array.length;",
" for (var i = 0; i < length; i++) {",
" if (array[i] === value) {",
" return true;",
" }",
" }",
" return false;",
" },",
" ",
" _matchFailed: function(failure) {",
" if (this._pos > this._rightmostMatchFailuresPos) {",
" this._rightmostMatchFailuresPos = this._pos;",
" this._rightmostMatchFailuresExpected = [];",
" }",
" ",
" if (!this._arrayContains(this._rightmostMatchFailuresExpected, failure)) {",
" this._rightmostMatchFailuresExpected.push(failure);",
" }",
" },",
" ",
" ${parseFunctionDefinitions}",
" ",
" /*",
" * Parses the input with a generated parser. If the parsing is successfull,",
" * returns a value explicitly or implicitly specified by the grammar from",
" * which the parser was generated (see |PEG.buildParser|). If the parsing is",
" * unsuccessful, throws |PEG.grammarParser.SyntaxError| describing the error.",
" */",
" parse: function(input) {",
" var that = this;",
" ",
" function initialize() {",
" that._input = input;",
" that._pos = 0;",
" that._rightmostMatchFailuresPos = 0;",
" that._rightmostMatchFailuresExpected = [];",
" that._cache = {};",
" }",
" ",
" function buildErrorMessage() {",
" function buildExpected(failuresExpected) {",
" switch (failuresExpected.length) {",
" case 0:",
" return 'end of input';",
" case 1:",
" return failuresExpected[0];",
" default:",
" failuresExpected.sort();",
" return failuresExpected.slice(0, failuresExpected.length - 1).join(', ')",
" + ' or '",
" + failuresExpected[failuresExpected.length - 1];",
" }",
" }",
" ",
" var expected = buildExpected(that._rightmostMatchFailuresExpected);",
" var pos = Math.max(that._pos, that._rightmostMatchFailuresPos);",
" var actual = pos < that._input.length",
" ? that._quoteString(that._input.charAt(pos))",
" : 'end of input';",
" ",
" return 'Expected ' + expected + ' but ' + actual + ' found.';",
" }",
" ",
" function computeErrorPosition() {",
" /*",
" * The first idea was to use |String.split| to break the input up to the",
" * error position along newlines and derive the line and column from",
" * there. However IE's |split| implementation is so broken that it was",
" * enough to prevent it.",
" */",
" ",
" var input = that._input;",
" var pos = that._rightmostMatchFailuresPos;",
" var line = 1;",
" var column = 1;",
" var seenCR = false;",
" ",
" for (var i = 0; i < pos; i++) {",
" var ch = input.charAt(i);",
" if (ch === '\\n') {",
" if (!seenCR) { line++; }",
" column = 1;",
" seenCR = false;",
" } else if (ch === '\\r' | ch === '\\u2028' || ch === '\\u2029') {",
" line++;",
" column = 1;",
" seenCR = true;",
" } else {",
" column++;",
" seenCR = false;",
" }",
" }",
" ",
" return { line: line, column: column };",
" }",
" ",
" initialize();",
" ",
" var initialContext = {",
" reportMatchFailures: true",
" };",
" ",
" var result = this['_parse_' + this._startRule](initialContext);",
" ",
" /*",
" * The parser is now in one of the following three states:",
" *",
" * 1. The parser successfully parsed the whole input.",
" *",
" * - |result !== null|",
" * - |that._pos === input.length|",
" * - |that._rightmostMatchFailuresExpected.length| may or may not contain",
" * something",
" *",
" * 2. The parser successfully parsed only a part of the input.",
" *",
" * - |result !== null|",
" * - |that._pos < input.length|",
" * - |that._rightmostMatchFailuresExpected.length| may or may not contain",
" * something",
" *",
" * 3. The parser did not successfully parse any part of the input.",
" *",
" * - |result === null|",
" * - |that._pos === 0|",
" * - |that._rightmostMatchFailuresExpected.length| contains at least one failure",
" *",
" * All code following this comment (including called functions) must",
" * handle these states.",
" */",
" if (result === null || this._pos !== input.length) {",
" var errorPosition = computeErrorPosition();",
" throw new this.SyntaxError(",
" buildErrorMessage(),",
" errorPosition.line,",
" errorPosition.column",
" );",
" }",
" ",
" return result;",
" },",
" ",
" /* Returns the parser source code. */",
" toSource: function() { return this._source; }",
" };",
" ",
" /* Thrown when a parser encounters a syntax error. */",
" ",
" result.SyntaxError = function(message, line, column) {",
" this.name = 'SyntaxError';",
" this.message = message;",
" this.line = line;",
" this.column = column;",
" };",
" ",
" result.SyntaxError.prototype = Error.prototype;",
" ",
" return result;",
"})()",
{
parseFunctionDefinitions: parseFunctionDefinitions.join("\n\n"),
startRule: startRule
}
);
var result = eval(source);
result._source = source;
return result;
}
}; };
})(); })();

Loading…
Cancel
Save