Reorder AST stuff more consistently and sensibly
There is no functional change in this commit.
This commit is contained in:
parent
f28c52fde2
commit
85930cbcfe
391
lib/compiler.js
391
lib/compiler.js
|
@ -200,11 +200,9 @@ PEG.Grammar.Rule.prototype = {
|
||||||
getName: function() { return this._name; }
|
getName: function() { return this._name; }
|
||||||
};
|
};
|
||||||
|
|
||||||
PEG.Grammar.Literal = function(value) { this._value = value; };
|
PEG.Grammar.Choice = function(alternatives) {
|
||||||
|
this._alternatives = alternatives;
|
||||||
PEG.Grammar.Class = function(characters) { this._characters = characters; };
|
};
|
||||||
|
|
||||||
PEG.Grammar.Any = function() {};
|
|
||||||
|
|
||||||
PEG.Grammar.Sequence = function(elements) { this._elements = elements; };
|
PEG.Grammar.Sequence = function(elements) { this._elements = elements; };
|
||||||
|
|
||||||
|
@ -212,16 +210,6 @@ PEG.Grammar.Sequence.prototype = {
|
||||||
getElements: function() { return this._elements; }
|
getElements: function() { return this._elements; }
|
||||||
};
|
};
|
||||||
|
|
||||||
PEG.Grammar.Choice = function(alternatives) {
|
|
||||||
this._alternatives = alternatives;
|
|
||||||
};
|
|
||||||
|
|
||||||
PEG.Grammar.Optional = function(expression) { this._expression = expression; };
|
|
||||||
|
|
||||||
PEG.Grammar.ZeroOrMore = function(expression) { this._expression = expression; };
|
|
||||||
|
|
||||||
PEG.Grammar.OneOrMore = function(expression) { this._expression = expression; };
|
|
||||||
|
|
||||||
PEG.Grammar.AndPredicate = function(expression) {
|
PEG.Grammar.AndPredicate = function(expression) {
|
||||||
this._expression = expression;
|
this._expression = expression;
|
||||||
};
|
};
|
||||||
|
@ -230,13 +218,25 @@ PEG.Grammar.NotPredicate = function(expression) {
|
||||||
this._expression = expression;
|
this._expression = expression;
|
||||||
};
|
};
|
||||||
|
|
||||||
PEG.Grammar.RuleRef = function(name) { this._name = name; };
|
PEG.Grammar.Optional = function(expression) { this._expression = expression; };
|
||||||
|
|
||||||
|
PEG.Grammar.ZeroOrMore = function(expression) { this._expression = expression; };
|
||||||
|
|
||||||
|
PEG.Grammar.OneOrMore = function(expression) { this._expression = expression; };
|
||||||
|
|
||||||
PEG.Grammar.Action = function(expression, action) {
|
PEG.Grammar.Action = function(expression, action) {
|
||||||
this._expression = expression;
|
this._expression = expression;
|
||||||
this._action = action;
|
this._action = action;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
PEG.Grammar.RuleRef = function(name) { this._name = name; };
|
||||||
|
|
||||||
|
PEG.Grammar.Literal = function(value) { this._value = value; };
|
||||||
|
|
||||||
|
PEG.Grammar.Any = function() {};
|
||||||
|
|
||||||
|
PEG.Grammar.Class = function(characters) { this._characters = characters; };
|
||||||
|
|
||||||
/* ===== Referenced Rule Existence Checks ===== */
|
/* ===== Referenced Rule Existence Checks ===== */
|
||||||
|
|
||||||
PEG.Grammar.extendNodes("checkReferencedRulesExist", {
|
PEG.Grammar.extendNodes("checkReferencedRulesExist", {
|
||||||
|
@ -245,9 +245,12 @@ PEG.Grammar.extendNodes("checkReferencedRulesExist", {
|
||||||
this._expression.checkReferencedRulesExist(grammar);
|
this._expression.checkReferencedRulesExist(grammar);
|
||||||
},
|
},
|
||||||
|
|
||||||
Literal: nop,
|
Choice:
|
||||||
Class: nop,
|
function(grammar) {
|
||||||
Any: nop,
|
PEG.ArrayUtils.each(this._alternatives, function(alternative) {
|
||||||
|
alternative.checkReferencedRulesExist(grammar);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
Sequence:
|
Sequence:
|
||||||
function(grammar) {
|
function(grammar) {
|
||||||
|
@ -256,12 +259,11 @@ PEG.Grammar.extendNodes("checkReferencedRulesExist", {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
Choice:
|
AndPredicate:
|
||||||
function(grammar) {
|
function(grammar) { this._expression.checkReferencedRulesExist(grammar); },
|
||||||
PEG.ArrayUtils.each(this._alternatives, function(alternative) {
|
|
||||||
alternative.checkReferencedRulesExist(grammar);
|
NotPredicate:
|
||||||
});
|
function(grammar) { this._expression.checkReferencedRulesExist(grammar); },
|
||||||
},
|
|
||||||
|
|
||||||
Optional:
|
Optional:
|
||||||
function(grammar) { this._expression.checkReferencedRulesExist(grammar); },
|
function(grammar) { this._expression.checkReferencedRulesExist(grammar); },
|
||||||
|
@ -272,10 +274,7 @@ PEG.Grammar.extendNodes("checkReferencedRulesExist", {
|
||||||
OneOrMore:
|
OneOrMore:
|
||||||
function(grammar) { this._expression.checkReferencedRulesExist(grammar); },
|
function(grammar) { this._expression.checkReferencedRulesExist(grammar); },
|
||||||
|
|
||||||
AndPredicate:
|
Action:
|
||||||
function(grammar) { this._expression.checkReferencedRulesExist(grammar); },
|
|
||||||
|
|
||||||
NotPredicate:
|
|
||||||
function(grammar) { this._expression.checkReferencedRulesExist(grammar); },
|
function(grammar) { this._expression.checkReferencedRulesExist(grammar); },
|
||||||
|
|
||||||
RuleRef:
|
RuleRef:
|
||||||
|
@ -287,8 +286,9 @@ PEG.Grammar.extendNodes("checkReferencedRulesExist", {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
Action:
|
Literal: nop,
|
||||||
function(grammar) { this._expression.checkReferencedRulesExist(grammar); }
|
Any: nop,
|
||||||
|
Class: nop
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@ -300,9 +300,12 @@ PEG.Grammar.extendNodes("checkNoLeftRecursion", {
|
||||||
this._expression.checkNoLeftRecursion(grammar, appliedRules.concat(this._name));
|
this._expression.checkNoLeftRecursion(grammar, appliedRules.concat(this._name));
|
||||||
},
|
},
|
||||||
|
|
||||||
Literal: nop,
|
Choice:
|
||||||
Class: nop,
|
function(grammar, appliedRules) {
|
||||||
Any: nop,
|
PEG.ArrayUtils.each(this._alternatives, function(alternative) {
|
||||||
|
alternative.checkNoLeftRecursion(grammar, appliedRules);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
Sequence:
|
Sequence:
|
||||||
function(grammar, appliedRules) {
|
function(grammar, appliedRules) {
|
||||||
|
@ -311,11 +314,14 @@ PEG.Grammar.extendNodes("checkNoLeftRecursion", {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
Choice:
|
AndPredicate:
|
||||||
function(grammar, appliedRules) {
|
function(grammar, appliedRules) {
|
||||||
PEG.ArrayUtils.each(this._alternatives, function(alternative) {
|
this._expression.checkNoLeftRecursion(grammar, appliedRules);
|
||||||
alternative.checkNoLeftRecursion(grammar, appliedRules);
|
},
|
||||||
});
|
|
||||||
|
NotPredicate:
|
||||||
|
function(grammar, appliedRules) {
|
||||||
|
this._expression.checkNoLeftRecursion(grammar, appliedRules);
|
||||||
},
|
},
|
||||||
|
|
||||||
Optional:
|
Optional:
|
||||||
|
@ -333,12 +339,7 @@ PEG.Grammar.extendNodes("checkNoLeftRecursion", {
|
||||||
this._expression.checkNoLeftRecursion(grammar, appliedRules);
|
this._expression.checkNoLeftRecursion(grammar, appliedRules);
|
||||||
},
|
},
|
||||||
|
|
||||||
AndPredicate:
|
Action:
|
||||||
function(grammar, appliedRules) {
|
|
||||||
this._expression.checkNoLeftRecursion(grammar, appliedRules);
|
|
||||||
},
|
|
||||||
|
|
||||||
NotPredicate:
|
|
||||||
function(grammar, appliedRules) {
|
function(grammar, appliedRules) {
|
||||||
this._expression.checkNoLeftRecursion(grammar, appliedRules);
|
this._expression.checkNoLeftRecursion(grammar, appliedRules);
|
||||||
},
|
},
|
||||||
|
@ -351,10 +352,9 @@ PEG.Grammar.extendNodes("checkNoLeftRecursion", {
|
||||||
grammar[this._name].checkNoLeftRecursion(grammar, appliedRules);
|
grammar[this._name].checkNoLeftRecursion(grammar, appliedRules);
|
||||||
},
|
},
|
||||||
|
|
||||||
Action:
|
Literal: nop,
|
||||||
function(grammar, appliedRules) {
|
Any: nop,
|
||||||
this._expression.checkNoLeftRecursion(grammar, appliedRules);
|
Class: nop
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/* ===== PEG.Compiler ===== */
|
/* ===== PEG.Compiler ===== */
|
||||||
|
@ -754,69 +754,31 @@ PEG.Grammar.Rule.prototype.compile = function() {
|
||||||
* |null|.
|
* |null|.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
PEG.Grammar.Literal.prototype.compile = function(resultVar) {
|
PEG.Grammar.Choice.prototype.compile = function(resultVar) {
|
||||||
return PEG.Compiler.formatCode(
|
var code = PEG.Compiler.formatCode(
|
||||||
"if (this._input.substr(this._pos, ${length}) === ${value|string}) {",
|
"var ${resultVar} = null;",
|
||||||
" 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.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
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
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 }
|
{ 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) {
|
PEG.Grammar.Sequence.prototype.compile = function(resultVar) {
|
||||||
|
@ -863,31 +825,58 @@ PEG.Grammar.Sequence.prototype.compile = function(resultVar) {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
PEG.Grammar.Choice.prototype.compile = function(resultVar) {
|
PEG.Grammar.AndPredicate.prototype.compile = function(resultVar) {
|
||||||
var code = PEG.Compiler.formatCode(
|
var savedPosVar = PEG.Compiler.generateUniqueIdentifier("savedPos");
|
||||||
"var ${resultVar} = null;",
|
var savedReportMatchFailuresVar = PEG.Compiler.generateUniqueIdentifier("savedReportMatchFailuresVar");
|
||||||
{ resultVar: resultVar }
|
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
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
for (var i = this._alternatives.length - 1; i >= 0; i--) {
|
PEG.Grammar.NotPredicate.prototype.compile = function(resultVar) {
|
||||||
var alternativeResultVar = PEG.Compiler.generateUniqueIdentifier("result");
|
var savedPosVar = PEG.Compiler.generateUniqueIdentifier("savedPos");
|
||||||
code = PEG.Compiler.formatCode(
|
var savedReportMatchFailuresVar = PEG.Compiler.generateUniqueIdentifier("savedReportMatchFailuresVar");
|
||||||
"${alternativeCode}",
|
var expressionResultVar = PEG.Compiler.generateUniqueIdentifier("result");
|
||||||
"if (${alternativeResultVar} !== null) {",
|
|
||||||
" var ${resultVar} = ${alternativeResultVar};",
|
|
||||||
"} else {",
|
|
||||||
" ${code};",
|
|
||||||
"}",
|
|
||||||
{
|
|
||||||
alternativeCode: this._alternatives[i].compile(alternativeResultVar),
|
|
||||||
alternativeResultVar: alternativeResultVar,
|
|
||||||
code: code,
|
|
||||||
resultVar: resultVar
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return code;
|
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) {
|
PEG.Grammar.Optional.prototype.compile = function(resultVar) {
|
||||||
|
@ -944,70 +933,6 @@ PEG.Grammar.OneOrMore.prototype.compile = function(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.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.RuleRef.prototype.compile = function(resultVar) {
|
|
||||||
return PEG.Compiler.formatCode(
|
|
||||||
"var ${resultVar} = this.${ruleMethod}(context);",
|
|
||||||
{
|
|
||||||
ruleMethod: "_parse_" + this._name,
|
|
||||||
resultVar: resultVar
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
PEG.Grammar.Action.prototype.compile = function(resultVar) {
|
PEG.Grammar.Action.prototype.compile = function(resultVar) {
|
||||||
/*
|
/*
|
||||||
* In case of sequences, we splat their elements into function arguments one
|
* In case of sequences, we splat their elements into function arguments one
|
||||||
|
@ -1058,4 +983,80 @@ PEG.Grammar.Action.prototype.compile = function(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
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -202,15 +202,15 @@ test("buildParser reports missing start rule", function() {
|
||||||
test("buildParser reports missing referenced rules", function() {
|
test("buildParser reports missing referenced rules", function() {
|
||||||
var grammars = [
|
var grammars = [
|
||||||
'start: missing',
|
'start: missing',
|
||||||
'start: missing "a" "b"',
|
|
||||||
'start: "a" "b" missing',
|
|
||||||
'start: missing / "a" / "b"',
|
'start: missing / "a" / "b"',
|
||||||
'start: "a" / "b" / missing',
|
'start: "a" / "b" / missing',
|
||||||
|
'start: missing "a" "b"',
|
||||||
|
'start: "a" "b" missing',
|
||||||
|
'start: &missing',
|
||||||
|
'start: !missing',
|
||||||
'start: missing?',
|
'start: missing?',
|
||||||
'start: missing*',
|
'start: missing*',
|
||||||
'start: missing+',
|
'start: missing+',
|
||||||
'start: &missing',
|
|
||||||
'start: !missing',
|
|
||||||
'start: missing { }'
|
'start: missing { }'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -227,14 +227,14 @@ test("buildParser reports left recursion", function() {
|
||||||
var grammars = [
|
var grammars = [
|
||||||
/* Direct */
|
/* Direct */
|
||||||
'start: start',
|
'start: start',
|
||||||
'start: start "a" "b"',
|
|
||||||
'start: start / "a" / "b"',
|
'start: start / "a" / "b"',
|
||||||
'start: "a" / "b" / start',
|
'start: "a" / "b" / start',
|
||||||
|
'start: start "a" "b"',
|
||||||
|
'start: &start',
|
||||||
|
'start: !start',
|
||||||
'start: start?',
|
'start: start?',
|
||||||
'start: start*',
|
'start: start*',
|
||||||
'start: start+',
|
'start: start+',
|
||||||
'start: &start',
|
|
||||||
'start: !start',
|
|
||||||
'start: start { }',
|
'start: start { }',
|
||||||
|
|
||||||
/* Indirect */
|
/* Indirect */
|
||||||
|
@ -259,6 +259,120 @@ test("buildParser allows custom start rule", function() {
|
||||||
|
|
||||||
module("Generated Parser");
|
module("Generated Parser");
|
||||||
|
|
||||||
|
test("choices", function() {
|
||||||
|
var parser = PEG.buildParser('start: "a" / "b" / "c"');
|
||||||
|
parses(parser, "a", "a");
|
||||||
|
parses(parser, "b", "b");
|
||||||
|
parses(parser, "c", "c");
|
||||||
|
doesNotParse(parser, "");
|
||||||
|
doesNotParse(parser, "ab");
|
||||||
|
doesNotParse(parser, "d");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("sequences", function() {
|
||||||
|
var emptySequenceParser = PEG.buildParser('start: ');
|
||||||
|
parses(emptySequenceParser, "", []);
|
||||||
|
doesNotParse(emptySequenceParser, "abc");
|
||||||
|
|
||||||
|
var nonEmptySequenceParser = PEG.buildParser('start: "a" "b" "c"');
|
||||||
|
parses(nonEmptySequenceParser, "abc", ["a", "b", "c"]);
|
||||||
|
doesNotParse(nonEmptySequenceParser, "");
|
||||||
|
doesNotParse(nonEmptySequenceParser, "ab");
|
||||||
|
doesNotParse(nonEmptySequenceParser, "abcd");
|
||||||
|
doesNotParse(nonEmptySequenceParser, "efg");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test that the parsing position returns after unsuccessful parsing of a
|
||||||
|
* sequence.
|
||||||
|
*/
|
||||||
|
var posTestParser = PEG.buildParser('start: ("a" "b") / "a"');
|
||||||
|
parses(posTestParser, "a", "a");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("and predicate", function() {
|
||||||
|
var parser = PEG.buildParser('start: "a" &"b" "b"');
|
||||||
|
parses(parser, "ab", ["a", "", "b"]);
|
||||||
|
doesNotParse(parser, "ac");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test that the parsing position returns after successful parsing of a
|
||||||
|
* predicate is not needed, it is implicit in the tests above.
|
||||||
|
*/
|
||||||
|
});
|
||||||
|
|
||||||
|
test("not predicate", function() {
|
||||||
|
var parser = PEG.buildParser('start: "a" !"b"');
|
||||||
|
parses(parser, "a", ["a", ""]);
|
||||||
|
doesNotParse(parser, "ab");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test that the parsing position returns after successful parsing of a
|
||||||
|
* predicate.
|
||||||
|
*/
|
||||||
|
var posTestParser = PEG.buildParser('start: "a" !"b" "c"');
|
||||||
|
parses(posTestParser, "ac", ["a", "", "c"]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("optional expressions", function() {
|
||||||
|
var parser = PEG.buildParser('start: "a"?');
|
||||||
|
parses(parser, "", "");
|
||||||
|
parses(parser, "a", "a");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("zero or more expressions", function() {
|
||||||
|
var parser = PEG.buildParser('start: "a"*');
|
||||||
|
parses(parser, "", []);
|
||||||
|
parses(parser, "a", ["a"]);
|
||||||
|
parses(parser, "aaa", ["a", "a", "a"]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("one or more expressions", function() {
|
||||||
|
var parser = PEG.buildParser('start: "a"+');
|
||||||
|
doesNotParse(parser, "");
|
||||||
|
parses(parser, "a", ["a"]);
|
||||||
|
parses(parser, "aaa", ["a", "a", "a"]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("actions", function() {
|
||||||
|
var singleMatchParser = PEG.buildParser(
|
||||||
|
'start: "a" { return Array.prototype.slice.call(arguments).join("").toUpperCase(); }'
|
||||||
|
);
|
||||||
|
parses(singleMatchParser, "a", "A");
|
||||||
|
|
||||||
|
var multiMatchParser = PEG.buildParser(
|
||||||
|
'start: "a" "b" "c" { return Array.prototype.slice.call(arguments).join("").toUpperCase(); }'
|
||||||
|
);
|
||||||
|
parses(multiMatchParser, "abc", "ABC");
|
||||||
|
|
||||||
|
var innerMatchParser = PEG.buildParser(
|
||||||
|
'start: "a" ("b" "c" "d" { return Array.prototype.slice.call(arguments).join("").toUpperCase(); }) "e"'
|
||||||
|
);
|
||||||
|
parses(innerMatchParser, "abcde", ["a", "BCD", "e"]);
|
||||||
|
|
||||||
|
/* Test that the action is not called when its expression does not match. */
|
||||||
|
var notAMatchParser = PEG.buildParser(
|
||||||
|
'start: "a" { ok(false, "action got called when it should not be"); }'
|
||||||
|
);
|
||||||
|
doesNotParse(notAMatchParser, "b");
|
||||||
|
|
||||||
|
var variablesParser = PEG.buildParser([
|
||||||
|
'start: "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" {',
|
||||||
|
' return [$1, $2, $3, $4, $5, $6, $7, $8, $9, $10].join("").toUpperCase();',
|
||||||
|
' }'
|
||||||
|
].join("\n"));
|
||||||
|
parses(variablesParser, "abcdefghij", "ABCDEFGHIJ");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("rule references", function() {
|
||||||
|
var parser = PEG.buildParser([
|
||||||
|
'start: static / dynamic',
|
||||||
|
'static: "C" / "C++" / "Java" / "C#"',
|
||||||
|
'dynamic: "Ruby" / "Python" / "JavaScript"'
|
||||||
|
].join("\n"));
|
||||||
|
parses(parser, "Java", "Java");
|
||||||
|
parses(parser, "Python", "Python");
|
||||||
|
});
|
||||||
|
|
||||||
test("literals", function() {
|
test("literals", function() {
|
||||||
var parser = PEG.buildParser('start: "abcd"');
|
var parser = PEG.buildParser('start: "abcd"');
|
||||||
parses(parser, "abcd", "abcd");
|
parses(parser, "abcd", "abcd");
|
||||||
|
@ -324,120 +438,6 @@ test("classes", function() {
|
||||||
parses(posTestParser, "ab", ["a", "b"]);
|
parses(posTestParser, "ab", ["a", "b"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("sequences", function() {
|
|
||||||
var emptySequenceParser = PEG.buildParser('start: ');
|
|
||||||
parses(emptySequenceParser, "", []);
|
|
||||||
doesNotParse(emptySequenceParser, "abc");
|
|
||||||
|
|
||||||
var nonEmptySequenceParser = PEG.buildParser('start: "a" "b" "c"');
|
|
||||||
parses(nonEmptySequenceParser, "abc", ["a", "b", "c"]);
|
|
||||||
doesNotParse(nonEmptySequenceParser, "");
|
|
||||||
doesNotParse(nonEmptySequenceParser, "ab");
|
|
||||||
doesNotParse(nonEmptySequenceParser, "abcd");
|
|
||||||
doesNotParse(nonEmptySequenceParser, "efg");
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Test that the parsing position returns after unsuccessful parsing of a
|
|
||||||
* sequence.
|
|
||||||
*/
|
|
||||||
var posTestParser = PEG.buildParser('start: ("a" "b") / "a"');
|
|
||||||
parses(posTestParser, "a", "a");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("choices", function() {
|
|
||||||
var parser = PEG.buildParser('start: "a" / "b" / "c"');
|
|
||||||
parses(parser, "a", "a");
|
|
||||||
parses(parser, "b", "b");
|
|
||||||
parses(parser, "c", "c");
|
|
||||||
doesNotParse(parser, "");
|
|
||||||
doesNotParse(parser, "ab");
|
|
||||||
doesNotParse(parser, "d");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("optional expressions", function() {
|
|
||||||
var parser = PEG.buildParser('start: "a"?');
|
|
||||||
parses(parser, "", "");
|
|
||||||
parses(parser, "a", "a");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("zero or more expressions", function() {
|
|
||||||
var parser = PEG.buildParser('start: "a"*');
|
|
||||||
parses(parser, "", []);
|
|
||||||
parses(parser, "a", ["a"]);
|
|
||||||
parses(parser, "aaa", ["a", "a", "a"]);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("one or more expressions", function() {
|
|
||||||
var parser = PEG.buildParser('start: "a"+');
|
|
||||||
doesNotParse(parser, "");
|
|
||||||
parses(parser, "a", ["a"]);
|
|
||||||
parses(parser, "aaa", ["a", "a", "a"]);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("and predicate", function() {
|
|
||||||
var parser = PEG.buildParser('start: "a" &"b" "b"');
|
|
||||||
parses(parser, "ab", ["a", "", "b"]);
|
|
||||||
doesNotParse(parser, "ac");
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Test that the parsing position returns after successful parsing of a
|
|
||||||
* predicate is not needed, it is implicit in the tests above.
|
|
||||||
*/
|
|
||||||
});
|
|
||||||
|
|
||||||
test("not predicate", function() {
|
|
||||||
var parser = PEG.buildParser('start: "a" !"b"');
|
|
||||||
parses(parser, "a", ["a", ""]);
|
|
||||||
doesNotParse(parser, "ab");
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Test that the parsing position returns after successful parsing of a
|
|
||||||
* predicate.
|
|
||||||
*/
|
|
||||||
var posTestParser = PEG.buildParser('start: "a" !"b" "c"');
|
|
||||||
parses(posTestParser, "ac", ["a", "", "c"]);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("rule references", function() {
|
|
||||||
var parser = PEG.buildParser([
|
|
||||||
'start: static / dynamic',
|
|
||||||
'static: "C" / "C++" / "Java" / "C#"',
|
|
||||||
'dynamic: "Ruby" / "Python" / "JavaScript"'
|
|
||||||
].join("\n"));
|
|
||||||
parses(parser, "Java", "Java");
|
|
||||||
parses(parser, "Python", "Python");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("actions", function() {
|
|
||||||
var singleMatchParser = PEG.buildParser(
|
|
||||||
'start: "a" { return Array.prototype.slice.call(arguments).join("").toUpperCase(); }'
|
|
||||||
);
|
|
||||||
parses(singleMatchParser, "a", "A");
|
|
||||||
|
|
||||||
var multiMatchParser = PEG.buildParser(
|
|
||||||
'start: "a" "b" "c" { return Array.prototype.slice.call(arguments).join("").toUpperCase(); }'
|
|
||||||
);
|
|
||||||
parses(multiMatchParser, "abc", "ABC");
|
|
||||||
|
|
||||||
var innerMatchParser = PEG.buildParser(
|
|
||||||
'start: "a" ("b" "c" "d" { return Array.prototype.slice.call(arguments).join("").toUpperCase(); }) "e"'
|
|
||||||
);
|
|
||||||
parses(innerMatchParser, "abcde", ["a", "BCD", "e"]);
|
|
||||||
|
|
||||||
/* Test that the action is not called when its expression does not match. */
|
|
||||||
var notAMatchParser = PEG.buildParser(
|
|
||||||
'start: "a" { ok(false, "action got called when it should not be"); }'
|
|
||||||
);
|
|
||||||
doesNotParse(notAMatchParser, "b");
|
|
||||||
|
|
||||||
var variablesParser = PEG.buildParser([
|
|
||||||
'start: "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" {',
|
|
||||||
' return [$1, $2, $3, $4, $5, $6, $7, $8, $9, $10].join("").toUpperCase();',
|
|
||||||
' }'
|
|
||||||
].join("\n"));
|
|
||||||
parses(variablesParser, "abcdefghij", "ABCDEFGHIJ");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("cache", function() {
|
test("cache", function() {
|
||||||
/*
|
/*
|
||||||
* Should trigger a codepath where the cache is used (for the "a" rule).
|
* Should trigger a codepath where the cache is used (for the "a" rule).
|
||||||
|
|
Loading…
Reference in a new issue