|
|
|
@ -47,8 +47,8 @@ PEG.ArrayUtils = {
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The code needs to be in sync with a code template in
|
|
|
|
|
* PEG.Grammar.Action.prototype.compile.
|
|
|
|
|
* The code needs to be in sync with the code template in the compilation
|
|
|
|
|
* function for "action" nodes.
|
|
|
|
|
*/
|
|
|
|
|
contains: function(array, value) {
|
|
|
|
|
/*
|
|
|
|
@ -90,8 +90,8 @@ PEG.StringUtils = {
|
|
|
|
|
* Surrounds the string with quotes and escapes characters inside so that the
|
|
|
|
|
* result is a valid JavaScript string.
|
|
|
|
|
*
|
|
|
|
|
* The code needs to be in sync with a code template in
|
|
|
|
|
* PEG.Grammar.Action.prototype.compile.
|
|
|
|
|
* The code needs to be in sync with th code template in the compilation
|
|
|
|
|
* function for "action" nodes.
|
|
|
|
|
*/
|
|
|
|
|
quote: function(s) {
|
|
|
|
|
/*
|
|
|
|
@ -447,6 +447,397 @@ PEG.Compiler = {
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
|
|
|
|
|
_compileFunctions: {
|
|
|
|
|
rule: function(node) {
|
|
|
|
|
var resultVar = PEG.Compiler.generateUniqueIdentifier("result");
|
|
|
|
|
|
|
|
|
|
if (node.displayName !== null) {
|
|
|
|
|
var setReportMatchFailuresCode = PEG.Compiler.formatCode(
|
|
|
|
|
"var savedReportMatchFailures = context.reportMatchFailures;",
|
|
|
|
|
"context.reportMatchFailures = false;"
|
|
|
|
|
);
|
|
|
|
|
var restoreReportMatchFailuresCode = PEG.Compiler.formatCode(
|
|
|
|
|
"context.reportMatchFailures = savedReportMatchFailures;"
|
|
|
|
|
);
|
|
|
|
|
var reportMatchFailureCode = PEG.Compiler.formatCode(
|
|
|
|
|
"if (context.reportMatchFailures && ${resultVar} === null) {",
|
|
|
|
|
" this._matchFailed(${displayName|string});",
|
|
|
|
|
"}",
|
|
|
|
|
{
|
|
|
|
|
displayName: node.displayName,
|
|
|
|
|
resultVar: resultVar
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
var setReportMatchFailuresCode = "";
|
|
|
|
|
var restoreReportMatchFailuresCode = "";
|
|
|
|
|
var reportMatchFailureCode = "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return PEG.Compiler.formatCode(
|
|
|
|
|
"_parse_${name}: function(context) {",
|
|
|
|
|
" var cacheKey = ${name|string} + '@' + this._pos;",
|
|
|
|
|
" var cachedResult = this._cache[cacheKey];",
|
|
|
|
|
" if (cachedResult !== undefined) {",
|
|
|
|
|
" this._pos = cachedResult.nextPos;",
|
|
|
|
|
" return cachedResult.result;",
|
|
|
|
|
" }",
|
|
|
|
|
" ",
|
|
|
|
|
" var pos = this._pos;",
|
|
|
|
|
" ",
|
|
|
|
|
" ${setReportMatchFailuresCode}",
|
|
|
|
|
" ${code}",
|
|
|
|
|
" ${restoreReportMatchFailuresCode}",
|
|
|
|
|
" ${reportMatchFailureCode}",
|
|
|
|
|
" ",
|
|
|
|
|
" this._cache[cacheKey] = {",
|
|
|
|
|
" nextPos: this._pos,",
|
|
|
|
|
" result: ${resultVar}",
|
|
|
|
|
" };",
|
|
|
|
|
" return ${resultVar};",
|
|
|
|
|
"},",
|
|
|
|
|
{
|
|
|
|
|
name: node.name,
|
|
|
|
|
setReportMatchFailuresCode: setReportMatchFailuresCode,
|
|
|
|
|
restoreReportMatchFailuresCode: restoreReportMatchFailuresCode,
|
|
|
|
|
reportMatchFailureCode: reportMatchFailureCode,
|
|
|
|
|
code: PEG.Compiler.compileNode(node.expression, resultVar),
|
|
|
|
|
resultVar: resultVar
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The contract for all code fragments generated by the following functions
|
|
|
|
|
* is as follows:
|
|
|
|
|
*
|
|
|
|
|
* * The code fragment should try to match a part of the input starting with
|
|
|
|
|
* the position indicated in |this._pos|. That position may point past the
|
|
|
|
|
* end of the input.
|
|
|
|
|
*
|
|
|
|
|
* * If the code fragment matches the input, it advances |this._pos| after
|
|
|
|
|
* the matched part of the input and sets variable with a name stored in
|
|
|
|
|
* |resultVar| to appropriate value, which is always non-null.
|
|
|
|
|
*
|
|
|
|
|
* * If the code fragment does not match the input, it does not change
|
|
|
|
|
* |this._pos| and it sets a variable with a name stored in |resultVar| to
|
|
|
|
|
* |null|.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
choice: function(node, resultVar) {
|
|
|
|
|
var code = PEG.Compiler.formatCode(
|
|
|
|
|
"var ${resultVar} = null;",
|
|
|
|
|
{ resultVar: resultVar }
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
for (var i = node.alternatives.length - 1; i >= 0; i--) {
|
|
|
|
|
var alternativeResultVar = PEG.Compiler.generateUniqueIdentifier("result");
|
|
|
|
|
code = PEG.Compiler.formatCode(
|
|
|
|
|
"${alternativeCode}",
|
|
|
|
|
"if (${alternativeResultVar} !== null) {",
|
|
|
|
|
" var ${resultVar} = ${alternativeResultVar};",
|
|
|
|
|
"} else {",
|
|
|
|
|
" ${code};",
|
|
|
|
|
"}",
|
|
|
|
|
{
|
|
|
|
|
alternativeCode: PEG.Compiler.compileNode(node.alternatives[i], alternativeResultVar),
|
|
|
|
|
alternativeResultVar: alternativeResultVar,
|
|
|
|
|
code: code,
|
|
|
|
|
resultVar: resultVar
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return code;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
sequence: function(node, resultVar) {
|
|
|
|
|
var savedPosVar = PEG.Compiler.generateUniqueIdentifier("savedPos");
|
|
|
|
|
|
|
|
|
|
var elementResultVars = PEG.ArrayUtils.map(node.elements, function() {
|
|
|
|
|
return PEG.Compiler.generateUniqueIdentifier("result")
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
var code = PEG.Compiler.formatCode(
|
|
|
|
|
"var ${resultVar} = ${elementResultVarArray};",
|
|
|
|
|
{
|
|
|
|
|
resultVar: resultVar,
|
|
|
|
|
elementResultVarArray: "[" + elementResultVars.join(", ") + "]"
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
for (var i = node.elements.length - 1; i >= 0; i--) {
|
|
|
|
|
code = PEG.Compiler.formatCode(
|
|
|
|
|
"${elementCode}",
|
|
|
|
|
"if (${elementResultVar} !== null) {",
|
|
|
|
|
" ${code}",
|
|
|
|
|
"} else {",
|
|
|
|
|
" var ${resultVar} = null;",
|
|
|
|
|
" this._pos = ${savedPosVar};",
|
|
|
|
|
"}",
|
|
|
|
|
{
|
|
|
|
|
elementCode: PEG.Compiler.compileNode(node.elements[i], elementResultVars[i]),
|
|
|
|
|
elementResultVar: elementResultVars[i],
|
|
|
|
|
code: code,
|
|
|
|
|
savedPosVar: savedPosVar,
|
|
|
|
|
resultVar: resultVar
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return PEG.Compiler.formatCode(
|
|
|
|
|
"var ${savedPosVar} = this._pos;",
|
|
|
|
|
"${code}",
|
|
|
|
|
{
|
|
|
|
|
code: code,
|
|
|
|
|
savedPosVar: savedPosVar
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
and_predicate: function(node, resultVar) {
|
|
|
|
|
var savedPosVar = PEG.Compiler.generateUniqueIdentifier("savedPos");
|
|
|
|
|
var savedReportMatchFailuresVar = PEG.Compiler.generateUniqueIdentifier("savedReportMatchFailuresVar");
|
|
|
|
|
var expressionResultVar = PEG.Compiler.generateUniqueIdentifier("result");
|
|
|
|
|
|
|
|
|
|
return PEG.Compiler.formatCode(
|
|
|
|
|
"var ${savedPosVar} = this._pos;",
|
|
|
|
|
"var ${savedReportMatchFailuresVar} = context.reportMatchFailures;",
|
|
|
|
|
"context.reportMatchFailures = false;",
|
|
|
|
|
"${expressionCode}",
|
|
|
|
|
"context.reportMatchFailures = ${savedReportMatchFailuresVar};",
|
|
|
|
|
"if (${expressionResultVar} !== null) {",
|
|
|
|
|
" var ${resultVar} = '';",
|
|
|
|
|
" this._pos = ${savedPosVar};",
|
|
|
|
|
"} else {",
|
|
|
|
|
" var ${resultVar} = null;",
|
|
|
|
|
"}",
|
|
|
|
|
{
|
|
|
|
|
expressionCode: PEG.Compiler.compileNode(node.expression, expressionResultVar),
|
|
|
|
|
expressionResultVar: expressionResultVar,
|
|
|
|
|
savedPosVar: savedPosVar,
|
|
|
|
|
savedReportMatchFailuresVar: savedReportMatchFailuresVar,
|
|
|
|
|
resultVar: resultVar
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
not_predicate: function(node, resultVar) {
|
|
|
|
|
var savedPosVar = PEG.Compiler.generateUniqueIdentifier("savedPos");
|
|
|
|
|
var savedReportMatchFailuresVar = PEG.Compiler.generateUniqueIdentifier("savedReportMatchFailuresVar");
|
|
|
|
|
var expressionResultVar = PEG.Compiler.generateUniqueIdentifier("result");
|
|
|
|
|
|
|
|
|
|
return PEG.Compiler.formatCode(
|
|
|
|
|
"var ${savedPosVar} = this._pos;",
|
|
|
|
|
"var ${savedReportMatchFailuresVar} = context.reportMatchFailures;",
|
|
|
|
|
"context.reportMatchFailures = false;",
|
|
|
|
|
"${expressionCode}",
|
|
|
|
|
"context.reportMatchFailures = ${savedReportMatchFailuresVar};",
|
|
|
|
|
"if (${expressionResultVar} === null) {",
|
|
|
|
|
" var ${resultVar} = '';",
|
|
|
|
|
"} else {",
|
|
|
|
|
" var ${resultVar} = null;",
|
|
|
|
|
" this._pos = ${savedPosVar};",
|
|
|
|
|
"}",
|
|
|
|
|
{
|
|
|
|
|
expressionCode: PEG.Compiler.compileNode(node.expression, expressionResultVar),
|
|
|
|
|
expressionResultVar: expressionResultVar,
|
|
|
|
|
savedPosVar: savedPosVar,
|
|
|
|
|
savedReportMatchFailuresVar: savedReportMatchFailuresVar,
|
|
|
|
|
resultVar: resultVar
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
optional: function(node, resultVar) {
|
|
|
|
|
var expressionResultVar = PEG.Compiler.generateUniqueIdentifier("result");
|
|
|
|
|
|
|
|
|
|
return PEG.Compiler.formatCode(
|
|
|
|
|
"${expressionCode}",
|
|
|
|
|
"var ${resultVar} = ${expressionResultVar} !== null ? ${expressionResultVar} : '';",
|
|
|
|
|
{
|
|
|
|
|
expressionCode: PEG.Compiler.compileNode(node.expression, expressionResultVar),
|
|
|
|
|
expressionResultVar: expressionResultVar,
|
|
|
|
|
resultVar: resultVar
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
zero_or_more: function(node, resultVar) {
|
|
|
|
|
var expressionResultVar = PEG.Compiler.generateUniqueIdentifier("result");
|
|
|
|
|
|
|
|
|
|
return PEG.Compiler.formatCode(
|
|
|
|
|
"var ${resultVar} = [];",
|
|
|
|
|
"${expressionCode}",
|
|
|
|
|
"while (${expressionResultVar} !== null) {",
|
|
|
|
|
" ${resultVar}.push(${expressionResultVar});",
|
|
|
|
|
" ${expressionCode}",
|
|
|
|
|
"}",
|
|
|
|
|
{
|
|
|
|
|
expressionCode: PEG.Compiler.compileNode(node.expression, expressionResultVar),
|
|
|
|
|
expressionResultVar: expressionResultVar,
|
|
|
|
|
resultVar: resultVar
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
one_or_more: function(node, resultVar) {
|
|
|
|
|
var expressionResultVar = PEG.Compiler.generateUniqueIdentifier("result");
|
|
|
|
|
|
|
|
|
|
return PEG.Compiler.formatCode(
|
|
|
|
|
"${expressionCode}",
|
|
|
|
|
"if (${expressionResultVar} !== null) {",
|
|
|
|
|
" var ${resultVar} = [];",
|
|
|
|
|
" while (${expressionResultVar} !== null) {",
|
|
|
|
|
" ${resultVar}.push(${expressionResultVar});",
|
|
|
|
|
" ${expressionCode}",
|
|
|
|
|
" }",
|
|
|
|
|
"} else {",
|
|
|
|
|
" var ${resultVar} = null;",
|
|
|
|
|
"}",
|
|
|
|
|
{
|
|
|
|
|
expressionCode: PEG.Compiler.compileNode(node.expression, expressionResultVar),
|
|
|
|
|
expressionResultVar: expressionResultVar,
|
|
|
|
|
resultVar: resultVar
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
action: function(node, resultVar) {
|
|
|
|
|
/*
|
|
|
|
|
* In case of sequences, we splat their elements into function arguments
|
|
|
|
|
* one by one. Example:
|
|
|
|
|
*
|
|
|
|
|
* start: "a" "b" "c" { alert(arguments.length) } // => 3
|
|
|
|
|
*
|
|
|
|
|
* This behavior is reflected in this function.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
var expressionResultVar = PEG.Compiler.generateUniqueIdentifier("result");
|
|
|
|
|
|
|
|
|
|
if (node.expression instanceof PEG.Grammar.Sequence) {
|
|
|
|
|
var params = PEG.ArrayUtils.map(
|
|
|
|
|
PEG.ArrayUtils.range(1, node.expression.elements.length + 1),
|
|
|
|
|
function(n) { return "$" + n; }
|
|
|
|
|
).join(", ");
|
|
|
|
|
|
|
|
|
|
var invocationCode = PEG.Compiler.formatCode(
|
|
|
|
|
"(function(${params}) { ${action} }).apply(null, ${expressionResultVar})",
|
|
|
|
|
{
|
|
|
|
|
params: params,
|
|
|
|
|
action: node.action,
|
|
|
|
|
expressionResultVar: expressionResultVar
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
var invocationCode = PEG.Compiler.formatCode(
|
|
|
|
|
"(function($1) { ${action} })(${expressionResultVar})",
|
|
|
|
|
{
|
|
|
|
|
action: node.action,
|
|
|
|
|
expressionResultVar: expressionResultVar
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return PEG.Compiler.formatCode(
|
|
|
|
|
"${expressionCode}",
|
|
|
|
|
"var ${resultVar} = ${expressionResultVar} !== null",
|
|
|
|
|
" ? ${invocationCode}",
|
|
|
|
|
" : null;",
|
|
|
|
|
{
|
|
|
|
|
expressionCode: PEG.Compiler.compileNode(node.expression, expressionResultVar),
|
|
|
|
|
expressionResultVar: expressionResultVar,
|
|
|
|
|
invocationCode: invocationCode,
|
|
|
|
|
resultVar: resultVar
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
rule_ref: function(node, resultVar) {
|
|
|
|
|
return PEG.Compiler.formatCode(
|
|
|
|
|
"var ${resultVar} = this.${ruleMethod}(context);",
|
|
|
|
|
{
|
|
|
|
|
ruleMethod: "_parse_" + node.name,
|
|
|
|
|
resultVar: resultVar
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
literal: function(node, resultVar) {
|
|
|
|
|
return PEG.Compiler.formatCode(
|
|
|
|
|
"if (this._input.substr(this._pos, ${length}) === ${value|string}) {",
|
|
|
|
|
" var ${resultVar} = ${value|string};",
|
|
|
|
|
" this._pos += ${length};",
|
|
|
|
|
"} else {",
|
|
|
|
|
" var ${resultVar} = null;",
|
|
|
|
|
" if (context.reportMatchFailures) {",
|
|
|
|
|
" this._matchFailed(this._quoteString(${value|string}));",
|
|
|
|
|
" }",
|
|
|
|
|
"}",
|
|
|
|
|
{
|
|
|
|
|
value: node.value,
|
|
|
|
|
length: node.value.length,
|
|
|
|
|
resultVar: resultVar
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
any: function(node, resultVar) {
|
|
|
|
|
return PEG.Compiler.formatCode(
|
|
|
|
|
"if (this._input.length > this._pos) {",
|
|
|
|
|
" var ${resultVar} = this._input.charAt(this._pos);",
|
|
|
|
|
" this._pos++;",
|
|
|
|
|
"} else {",
|
|
|
|
|
" var ${resultVar} = null;",
|
|
|
|
|
" if (context.reportMatchFailures) {",
|
|
|
|
|
" this._matchFailed('any character');",
|
|
|
|
|
" }",
|
|
|
|
|
"}",
|
|
|
|
|
{ resultVar: resultVar }
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
"class": function(node, resultVar) {
|
|
|
|
|
/*
|
|
|
|
|
* Stupid IE considers regexps /[]/ and /[^]/ syntactically invalid, so we
|
|
|
|
|
* translate them into euqivalents it can handle.
|
|
|
|
|
*/
|
|
|
|
|
if (node.characters === "") {
|
|
|
|
|
var regexp = "/^(?!)/";
|
|
|
|
|
} else if (node.characters === "^") {
|
|
|
|
|
var regexp = "/^[\\S\\s]/";
|
|
|
|
|
} else {
|
|
|
|
|
var regexp = "/^[" + node.characters + "]/";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return PEG.Compiler.formatCode(
|
|
|
|
|
"if (this._input.substr(this._pos).match(${regexp}) !== null) {",
|
|
|
|
|
" var ${resultVar} = this._input.charAt(this._pos);",
|
|
|
|
|
" this._pos++;",
|
|
|
|
|
"} else {",
|
|
|
|
|
" var ${resultVar} = null;",
|
|
|
|
|
" if (context.reportMatchFailures) {",
|
|
|
|
|
" this._matchFailed('[' + ${characters|string} + ']');",
|
|
|
|
|
" }",
|
|
|
|
|
"}",
|
|
|
|
|
{
|
|
|
|
|
characters: node.characters,
|
|
|
|
|
regexp: regexp,
|
|
|
|
|
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
|
|
|
|
@ -466,7 +857,7 @@ PEG.Compiler = {
|
|
|
|
|
|
|
|
|
|
var parseFunctionDefinitions = [];
|
|
|
|
|
for (var rule in ast) {
|
|
|
|
|
parseFunctionDefinitions.push(ast[rule].compile());
|
|
|
|
|
parseFunctionDefinitions.push(this.compileNode(ast[rule]));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var source = this.formatCode(
|
|
|
|
@ -671,385 +1062,4 @@ PEG.Compiler = {
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
PEG.Grammar.Rule.prototype.compile = function() {
|
|
|
|
|
var resultVar = PEG.Compiler.generateUniqueIdentifier("result");
|
|
|
|
|
|
|
|
|
|
if (this.displayName !== null) {
|
|
|
|
|
var setReportMatchFailuresCode = PEG.Compiler.formatCode(
|
|
|
|
|
"var savedReportMatchFailures = context.reportMatchFailures;",
|
|
|
|
|
"context.reportMatchFailures = false;"
|
|
|
|
|
);
|
|
|
|
|
var restoreReportMatchFailuresCode = PEG.Compiler.formatCode(
|
|
|
|
|
"context.reportMatchFailures = savedReportMatchFailures;"
|
|
|
|
|
);
|
|
|
|
|
var reportMatchFailureCode = PEG.Compiler.formatCode(
|
|
|
|
|
"if (context.reportMatchFailures && ${resultVar} === null) {",
|
|
|
|
|
" this._matchFailed(${displayName|string});",
|
|
|
|
|
"}",
|
|
|
|
|
{
|
|
|
|
|
displayName: this.displayName,
|
|
|
|
|
resultVar: resultVar
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
var setReportMatchFailuresCode = "";
|
|
|
|
|
var restoreReportMatchFailuresCode = "";
|
|
|
|
|
var reportMatchFailureCode = "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return PEG.Compiler.formatCode(
|
|
|
|
|
"_parse_${name}: function(context) {",
|
|
|
|
|
" var cacheKey = ${name|string} + '@' + this._pos;",
|
|
|
|
|
" var cachedResult = this._cache[cacheKey];",
|
|
|
|
|
" if (cachedResult !== undefined) {",
|
|
|
|
|
" this._pos = cachedResult.nextPos;",
|
|
|
|
|
" return cachedResult.result;",
|
|
|
|
|
" }",
|
|
|
|
|
" ",
|
|
|
|
|
" var pos = this._pos;",
|
|
|
|
|
" ",
|
|
|
|
|
" ${setReportMatchFailuresCode}",
|
|
|
|
|
" ${code}",
|
|
|
|
|
" ${restoreReportMatchFailuresCode}",
|
|
|
|
|
" ${reportMatchFailureCode}",
|
|
|
|
|
" ",
|
|
|
|
|
" this._cache[cacheKey] = {",
|
|
|
|
|
" nextPos: this._pos,",
|
|
|
|
|
" result: ${resultVar}",
|
|
|
|
|
" };",
|
|
|
|
|
" return ${resultVar};",
|
|
|
|
|
"},",
|
|
|
|
|
{
|
|
|
|
|
name: this.name,
|
|
|
|
|
setReportMatchFailuresCode: setReportMatchFailuresCode,
|
|
|
|
|
restoreReportMatchFailuresCode: restoreReportMatchFailuresCode,
|
|
|
|
|
reportMatchFailureCode: reportMatchFailureCode,
|
|
|
|
|
code: this.expression.compile(resultVar),
|
|
|
|
|
resultVar: resultVar
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The contract for all code fragments generated by the following |compile|
|
|
|
|
|
* methods is as follows:
|
|
|
|
|
*
|
|
|
|
|
* * The code fragment should try to match a part of the input starting with the
|
|
|
|
|
* position indicated in |this._pos|. That position may point past the end of
|
|
|
|
|
* the input.
|
|
|
|
|
*
|
|
|
|
|
* * If the code fragment matches the input, it advances |this._pos| after the
|
|
|
|
|
* matched part of the input and sets variable with a name stored in
|
|
|
|
|
* |resultVar| to appropriate value, which is always non-null.
|
|
|
|
|
*
|
|
|
|
|
* * If the code fragment does not match the input, it does not change
|
|
|
|
|
* |this._pos| and it sets a variable with a name stored in |resultVar| to
|
|
|
|
|
* |null|.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
PEG.Grammar.Choice.prototype.compile = function(resultVar) {
|
|
|
|
|
var code = PEG.Compiler.formatCode(
|
|
|
|
|
"var ${resultVar} = null;",
|
|
|
|
|
{ resultVar: resultVar }
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
for (var i = this.alternatives.length - 1; i >= 0; i--) {
|
|
|
|
|
var alternativeResultVar = PEG.Compiler.generateUniqueIdentifier("result");
|
|
|
|
|
code = PEG.Compiler.formatCode(
|
|
|
|
|
"${alternativeCode}",
|
|
|
|
|
"if (${alternativeResultVar} !== null) {",
|
|
|
|
|
" var ${resultVar} = ${alternativeResultVar};",
|
|
|
|
|
"} else {",
|
|
|
|
|
" ${code};",
|
|
|
|
|
"}",
|
|
|
|
|
{
|
|
|
|
|
alternativeCode: this.alternatives[i].compile(alternativeResultVar),
|
|
|
|
|
alternativeResultVar: alternativeResultVar,
|
|
|
|
|
code: code,
|
|
|
|
|
resultVar: resultVar
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return code;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
PEG.Grammar.Sequence.prototype.compile = function(resultVar) {
|
|
|
|
|
var savedPosVar = PEG.Compiler.generateUniqueIdentifier("savedPos");
|
|
|
|
|
|
|
|
|
|
var elementResultVars = PEG.ArrayUtils.map(this.elements, function() {
|
|
|
|
|
return PEG.Compiler.generateUniqueIdentifier("result")
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
var code = PEG.Compiler.formatCode(
|
|
|
|
|
"var ${resultVar} = ${elementResultVarArray};",
|
|
|
|
|
{
|
|
|
|
|
resultVar: resultVar,
|
|
|
|
|
elementResultVarArray: "[" + elementResultVars.join(", ") + "]"
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
for (var i = this.elements.length - 1; i >= 0; i--) {
|
|
|
|
|
code = PEG.Compiler.formatCode(
|
|
|
|
|
"${elementCode}",
|
|
|
|
|
"if (${elementResultVar} !== null) {",
|
|
|
|
|
" ${code}",
|
|
|
|
|
"} else {",
|
|
|
|
|
" var ${resultVar} = null;",
|
|
|
|
|
" this._pos = ${savedPosVar};",
|
|
|
|
|
"}",
|
|
|
|
|
{
|
|
|
|
|
elementCode: this.elements[i].compile(elementResultVars[i]),
|
|
|
|
|
elementResultVar: elementResultVars[i],
|
|
|
|
|
code: code,
|
|
|
|
|
savedPosVar: savedPosVar,
|
|
|
|
|
resultVar: resultVar
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return PEG.Compiler.formatCode(
|
|
|
|
|
"var ${savedPosVar} = this._pos;",
|
|
|
|
|
"${code}",
|
|
|
|
|
{
|
|
|
|
|
code: code,
|
|
|
|
|
savedPosVar: savedPosVar
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
PEG.Grammar.AndPredicate.prototype.compile = function(resultVar) {
|
|
|
|
|
var savedPosVar = PEG.Compiler.generateUniqueIdentifier("savedPos");
|
|
|
|
|
var savedReportMatchFailuresVar = PEG.Compiler.generateUniqueIdentifier("savedReportMatchFailuresVar");
|
|
|
|
|
var expressionResultVar = PEG.Compiler.generateUniqueIdentifier("result");
|
|
|
|
|
|
|
|
|
|
return PEG.Compiler.formatCode(
|
|
|
|
|
"var ${savedPosVar} = this._pos;",
|
|
|
|
|
"var ${savedReportMatchFailuresVar} = context.reportMatchFailures;",
|
|
|
|
|
"context.reportMatchFailures = false;",
|
|
|
|
|
"${expressionCode}",
|
|
|
|
|
"context.reportMatchFailures = ${savedReportMatchFailuresVar};",
|
|
|
|
|
"if (${expressionResultVar} !== null) {",
|
|
|
|
|
" var ${resultVar} = '';",
|
|
|
|
|
" this._pos = ${savedPosVar};",
|
|
|
|
|
"} else {",
|
|
|
|
|
" var ${resultVar} = null;",
|
|
|
|
|
"}",
|
|
|
|
|
{
|
|
|
|
|
expressionCode: this.expression.compile(expressionResultVar),
|
|
|
|
|
expressionResultVar: expressionResultVar,
|
|
|
|
|
savedPosVar: savedPosVar,
|
|
|
|
|
savedReportMatchFailuresVar: savedReportMatchFailuresVar,
|
|
|
|
|
resultVar: resultVar
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
PEG.Grammar.NotPredicate.prototype.compile = function(resultVar) {
|
|
|
|
|
var savedPosVar = PEG.Compiler.generateUniqueIdentifier("savedPos");
|
|
|
|
|
var savedReportMatchFailuresVar = PEG.Compiler.generateUniqueIdentifier("savedReportMatchFailuresVar");
|
|
|
|
|
var expressionResultVar = PEG.Compiler.generateUniqueIdentifier("result");
|
|
|
|
|
|
|
|
|
|
return PEG.Compiler.formatCode(
|
|
|
|
|
"var ${savedPosVar} = this._pos;",
|
|
|
|
|
"var ${savedReportMatchFailuresVar} = context.reportMatchFailures;",
|
|
|
|
|
"context.reportMatchFailures = false;",
|
|
|
|
|
"${expressionCode}",
|
|
|
|
|
"context.reportMatchFailures = ${savedReportMatchFailuresVar};",
|
|
|
|
|
"if (${expressionResultVar} === null) {",
|
|
|
|
|
" var ${resultVar} = '';",
|
|
|
|
|
"} else {",
|
|
|
|
|
" var ${resultVar} = null;",
|
|
|
|
|
" this._pos = ${savedPosVar};",
|
|
|
|
|
"}",
|
|
|
|
|
{
|
|
|
|
|
expressionCode: this.expression.compile(expressionResultVar),
|
|
|
|
|
expressionResultVar: expressionResultVar,
|
|
|
|
|
savedPosVar: savedPosVar,
|
|
|
|
|
savedReportMatchFailuresVar: savedReportMatchFailuresVar,
|
|
|
|
|
resultVar: resultVar
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
PEG.Grammar.Optional.prototype.compile = function(resultVar) {
|
|
|
|
|
var expressionResultVar = PEG.Compiler.generateUniqueIdentifier("result");
|
|
|
|
|
|
|
|
|
|
return PEG.Compiler.formatCode(
|
|
|
|
|
"${expressionCode}",
|
|
|
|
|
"var ${resultVar} = ${expressionResultVar} !== null ? ${expressionResultVar} : '';",
|
|
|
|
|
{
|
|
|
|
|
expressionCode: this.expression.compile(expressionResultVar),
|
|
|
|
|
expressionResultVar: expressionResultVar,
|
|
|
|
|
resultVar: resultVar
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
PEG.Grammar.ZeroOrMore.prototype.compile = function(resultVar) {
|
|
|
|
|
var expressionResultVar = PEG.Compiler.generateUniqueIdentifier("result");
|
|
|
|
|
|
|
|
|
|
return PEG.Compiler.formatCode(
|
|
|
|
|
"var ${resultVar} = [];",
|
|
|
|
|
"${expressionCode}",
|
|
|
|
|
"while (${expressionResultVar} !== null) {",
|
|
|
|
|
" ${resultVar}.push(${expressionResultVar});",
|
|
|
|
|
" ${expressionCode}",
|
|
|
|
|
"}",
|
|
|
|
|
{
|
|
|
|
|
expressionCode: this.expression.compile(expressionResultVar),
|
|
|
|
|
expressionResultVar: expressionResultVar,
|
|
|
|
|
resultVar: resultVar
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
PEG.Grammar.OneOrMore.prototype.compile = function(resultVar) {
|
|
|
|
|
var expressionResultVar = PEG.Compiler.generateUniqueIdentifier("result");
|
|
|
|
|
|
|
|
|
|
return PEG.Compiler.formatCode(
|
|
|
|
|
"${expressionCode}",
|
|
|
|
|
"if (${expressionResultVar} !== null) {",
|
|
|
|
|
" var ${resultVar} = [];",
|
|
|
|
|
" while (${expressionResultVar} !== null) {",
|
|
|
|
|
" ${resultVar}.push(${expressionResultVar});",
|
|
|
|
|
" ${expressionCode}",
|
|
|
|
|
" }",
|
|
|
|
|
"} else {",
|
|
|
|
|
" var ${resultVar} = null;",
|
|
|
|
|
"}",
|
|
|
|
|
{
|
|
|
|
|
expressionCode: this.expression.compile(expressionResultVar),
|
|
|
|
|
expressionResultVar: expressionResultVar,
|
|
|
|
|
resultVar: resultVar
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
PEG.Grammar.Action.prototype.compile = function(resultVar) {
|
|
|
|
|
/*
|
|
|
|
|
* In case of sequences, we splat their elements into function arguments one
|
|
|
|
|
* by one. Example:
|
|
|
|
|
*
|
|
|
|
|
* start: "a" "b" "c" { alert(arguments.length) } // => 3
|
|
|
|
|
*
|
|
|
|
|
* This behavior is reflected in this function.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
var expressionResultVar = PEG.Compiler.generateUniqueIdentifier("result");
|
|
|
|
|
|
|
|
|
|
if (this.expression.type === "sequence") {
|
|
|
|
|
var params = PEG.ArrayUtils.map(
|
|
|
|
|
PEG.ArrayUtils.range(1, this.expression.elements.length + 1),
|
|
|
|
|
function(n) { return "$" + n; }
|
|
|
|
|
).join(", ");
|
|
|
|
|
|
|
|
|
|
var invocationCode = PEG.Compiler.formatCode(
|
|
|
|
|
"(function(${params}) { ${action} }).apply(null, ${expressionResultVar})",
|
|
|
|
|
{
|
|
|
|
|
params: params,
|
|
|
|
|
action: this.action,
|
|
|
|
|
expressionResultVar: expressionResultVar
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
var invocationCode = PEG.Compiler.formatCode(
|
|
|
|
|
"(function($1) { ${action} })(${expressionResultVar})",
|
|
|
|
|
{
|
|
|
|
|
action: this.action,
|
|
|
|
|
expressionResultVar: expressionResultVar
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return PEG.Compiler.formatCode(
|
|
|
|
|
"${expressionCode}",
|
|
|
|
|
"var ${resultVar} = ${expressionResultVar} !== null",
|
|
|
|
|
" ? ${invocationCode}",
|
|
|
|
|
" : null;",
|
|
|
|
|
{
|
|
|
|
|
expressionCode: this.expression.compile(expressionResultVar),
|
|
|
|
|
expressionResultVar: expressionResultVar,
|
|
|
|
|
invocationCode: invocationCode,
|
|
|
|
|
resultVar: resultVar
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
PEG.Grammar.RuleRef.prototype.compile = function(resultVar) {
|
|
|
|
|
return PEG.Compiler.formatCode(
|
|
|
|
|
"var ${resultVar} = this.${ruleMethod}(context);",
|
|
|
|
|
{
|
|
|
|
|
ruleMethod: "_parse_" + this.name,
|
|
|
|
|
resultVar: resultVar
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
PEG.Grammar.Literal.prototype.compile = function(resultVar) {
|
|
|
|
|
return PEG.Compiler.formatCode(
|
|
|
|
|
"if (this._input.substr(this._pos, ${length}) === ${value|string}) {",
|
|
|
|
|
" var ${resultVar} = ${value|string};",
|
|
|
|
|
" this._pos += ${length};",
|
|
|
|
|
"} else {",
|
|
|
|
|
" var ${resultVar} = null;",
|
|
|
|
|
" if (context.reportMatchFailures) {",
|
|
|
|
|
" this._matchFailed(this._quoteString(${value|string}));",
|
|
|
|
|
" }",
|
|
|
|
|
"}",
|
|
|
|
|
{
|
|
|
|
|
value: this.value,
|
|
|
|
|
length: this.value.length,
|
|
|
|
|
resultVar: resultVar
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
PEG.Grammar.Any.prototype.compile = function(resultVar) {
|
|
|
|
|
return PEG.Compiler.formatCode(
|
|
|
|
|
"if (this._input.length > this._pos) {",
|
|
|
|
|
" var ${resultVar} = this._input.charAt(this._pos);",
|
|
|
|
|
" this._pos++;",
|
|
|
|
|
"} else {",
|
|
|
|
|
" var ${resultVar} = null;",
|
|
|
|
|
" if (context.reportMatchFailures) {",
|
|
|
|
|
" this._matchFailed('any character');",
|
|
|
|
|
" }",
|
|
|
|
|
"}",
|
|
|
|
|
{ resultVar: resultVar }
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PEG.Grammar.Class.prototype.compile = function(resultVar) {
|
|
|
|
|
/*
|
|
|
|
|
* Stupid IE considers regexps /[]/ and /[^]/ syntactically invalid, so we
|
|
|
|
|
* translate them into euqivalents it can handle.
|
|
|
|
|
*/
|
|
|
|
|
if (this.characters === "") {
|
|
|
|
|
var regexp = "/^(?!)/";
|
|
|
|
|
} else if (this.characters === "^") {
|
|
|
|
|
var regexp = "/^[\\S\\s]/";
|
|
|
|
|
} else {
|
|
|
|
|
var regexp = "/^[" + this.characters + "]/";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return PEG.Compiler.formatCode(
|
|
|
|
|
"if (this._input.substr(this._pos).match(${regexp}) !== null) {",
|
|
|
|
|
" var ${resultVar} = this._input.charAt(this._pos);",
|
|
|
|
|
" this._pos++;",
|
|
|
|
|
"} else {",
|
|
|
|
|
" var ${resultVar} = null;",
|
|
|
|
|
" if (context.reportMatchFailures) {",
|
|
|
|
|
" this._matchFailed('[' + ${characters|string} + ']');",
|
|
|
|
|
" }",
|
|
|
|
|
"}",
|
|
|
|
|
{
|
|
|
|
|
characters: this.characters,
|
|
|
|
|
regexp: regexp,
|
|
|
|
|
resultVar: resultVar
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
})();
|
|
|
|
|