Reorder AST stuff more consistently and sensibly
There is no functional change in this commit.
This commit is contained in:
parent
f28c52fde2
commit
85930cbcfe
379
lib/compiler.js
379
lib/compiler.js
|
@ -200,11 +200,9 @@ PEG.Grammar.Rule.prototype = {
|
|||
getName: function() { return this._name; }
|
||||
};
|
||||
|
||||
PEG.Grammar.Literal = function(value) { this._value = value; };
|
||||
|
||||
PEG.Grammar.Class = function(characters) { this._characters = characters; };
|
||||
|
||||
PEG.Grammar.Any = function() {};
|
||||
PEG.Grammar.Choice = function(alternatives) {
|
||||
this._alternatives = alternatives;
|
||||
};
|
||||
|
||||
PEG.Grammar.Sequence = function(elements) { this._elements = elements; };
|
||||
|
||||
|
@ -212,16 +210,6 @@ PEG.Grammar.Sequence.prototype = {
|
|||
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) {
|
||||
this._expression = expression;
|
||||
};
|
||||
|
@ -230,13 +218,25 @@ PEG.Grammar.NotPredicate = function(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) {
|
||||
this._expression = expression;
|
||||
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 ===== */
|
||||
|
||||
PEG.Grammar.extendNodes("checkReferencedRulesExist", {
|
||||
|
@ -245,9 +245,12 @@ PEG.Grammar.extendNodes("checkReferencedRulesExist", {
|
|||
this._expression.checkReferencedRulesExist(grammar);
|
||||
},
|
||||
|
||||
Literal: nop,
|
||||
Class: nop,
|
||||
Any: nop,
|
||||
Choice:
|
||||
function(grammar) {
|
||||
PEG.ArrayUtils.each(this._alternatives, function(alternative) {
|
||||
alternative.checkReferencedRulesExist(grammar);
|
||||
});
|
||||
},
|
||||
|
||||
Sequence:
|
||||
function(grammar) {
|
||||
|
@ -256,12 +259,11 @@ PEG.Grammar.extendNodes("checkReferencedRulesExist", {
|
|||
});
|
||||
},
|
||||
|
||||
Choice:
|
||||
function(grammar) {
|
||||
PEG.ArrayUtils.each(this._alternatives, function(alternative) {
|
||||
alternative.checkReferencedRulesExist(grammar);
|
||||
});
|
||||
},
|
||||
AndPredicate:
|
||||
function(grammar) { this._expression.checkReferencedRulesExist(grammar); },
|
||||
|
||||
NotPredicate:
|
||||
function(grammar) { this._expression.checkReferencedRulesExist(grammar); },
|
||||
|
||||
Optional:
|
||||
function(grammar) { this._expression.checkReferencedRulesExist(grammar); },
|
||||
|
@ -272,10 +274,7 @@ PEG.Grammar.extendNodes("checkReferencedRulesExist", {
|
|||
OneOrMore:
|
||||
function(grammar) { this._expression.checkReferencedRulesExist(grammar); },
|
||||
|
||||
AndPredicate:
|
||||
function(grammar) { this._expression.checkReferencedRulesExist(grammar); },
|
||||
|
||||
NotPredicate:
|
||||
Action:
|
||||
function(grammar) { this._expression.checkReferencedRulesExist(grammar); },
|
||||
|
||||
RuleRef:
|
||||
|
@ -287,8 +286,9 @@ PEG.Grammar.extendNodes("checkReferencedRulesExist", {
|
|||
}
|
||||
},
|
||||
|
||||
Action:
|
||||
function(grammar) { this._expression.checkReferencedRulesExist(grammar); }
|
||||
Literal: nop,
|
||||
Any: nop,
|
||||
Class: nop
|
||||
});
|
||||
|
||||
|
||||
|
@ -300,9 +300,12 @@ PEG.Grammar.extendNodes("checkNoLeftRecursion", {
|
|||
this._expression.checkNoLeftRecursion(grammar, appliedRules.concat(this._name));
|
||||
},
|
||||
|
||||
Literal: nop,
|
||||
Class: nop,
|
||||
Any: nop,
|
||||
Choice:
|
||||
function(grammar, appliedRules) {
|
||||
PEG.ArrayUtils.each(this._alternatives, function(alternative) {
|
||||
alternative.checkNoLeftRecursion(grammar, appliedRules);
|
||||
});
|
||||
},
|
||||
|
||||
Sequence:
|
||||
function(grammar, appliedRules) {
|
||||
|
@ -311,11 +314,14 @@ PEG.Grammar.extendNodes("checkNoLeftRecursion", {
|
|||
}
|
||||
},
|
||||
|
||||
Choice:
|
||||
AndPredicate:
|
||||
function(grammar, appliedRules) {
|
||||
PEG.ArrayUtils.each(this._alternatives, function(alternative) {
|
||||
alternative.checkNoLeftRecursion(grammar, appliedRules);
|
||||
});
|
||||
this._expression.checkNoLeftRecursion(grammar, appliedRules);
|
||||
},
|
||||
|
||||
NotPredicate:
|
||||
function(grammar, appliedRules) {
|
||||
this._expression.checkNoLeftRecursion(grammar, appliedRules);
|
||||
},
|
||||
|
||||
Optional:
|
||||
|
@ -333,12 +339,7 @@ PEG.Grammar.extendNodes("checkNoLeftRecursion", {
|
|||
this._expression.checkNoLeftRecursion(grammar, appliedRules);
|
||||
},
|
||||
|
||||
AndPredicate:
|
||||
function(grammar, appliedRules) {
|
||||
this._expression.checkNoLeftRecursion(grammar, appliedRules);
|
||||
},
|
||||
|
||||
NotPredicate:
|
||||
Action:
|
||||
function(grammar, appliedRules) {
|
||||
this._expression.checkNoLeftRecursion(grammar, appliedRules);
|
||||
},
|
||||
|
@ -351,10 +352,9 @@ PEG.Grammar.extendNodes("checkNoLeftRecursion", {
|
|||
grammar[this._name].checkNoLeftRecursion(grammar, appliedRules);
|
||||
},
|
||||
|
||||
Action:
|
||||
function(grammar, appliedRules) {
|
||||
this._expression.checkNoLeftRecursion(grammar, appliedRules);
|
||||
}
|
||||
Literal: nop,
|
||||
Any: nop,
|
||||
Class: nop
|
||||
});
|
||||
|
||||
/* ===== PEG.Compiler ===== */
|
||||
|
@ -754,69 +754,31 @@ PEG.Grammar.Rule.prototype.compile = function() {
|
|||
* |null|.
|
||||
*/
|
||||
|
||||
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 {",
|
||||
PEG.Grammar.Choice.prototype.compile = function(resultVar) {
|
||||
var code = PEG.Compiler.formatCode(
|
||||
"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 }
|
||||
);
|
||||
|
||||
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) {
|
||||
|
@ -863,31 +825,58 @@ PEG.Grammar.Sequence.prototype.compile = function(resultVar) {
|
|||
);
|
||||
};
|
||||
|
||||
PEG.Grammar.Choice.prototype.compile = function(resultVar) {
|
||||
var code = PEG.Compiler.formatCode(
|
||||
"var ${resultVar} = null;",
|
||||
{ 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");
|
||||
|
||||
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};",
|
||||
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 {",
|
||||
" ${code};",
|
||||
" var ${resultVar} = null;",
|
||||
"}",
|
||||
{
|
||||
alternativeCode: this._alternatives[i].compile(alternativeResultVar),
|
||||
alternativeResultVar: alternativeResultVar,
|
||||
code: code,
|
||||
expressionCode: this._expression.compile(expressionResultVar),
|
||||
expressionResultVar: expressionResultVar,
|
||||
savedPosVar: savedPosVar,
|
||||
savedReportMatchFailuresVar: savedReportMatchFailuresVar,
|
||||
resultVar: resultVar
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return code;
|
||||
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) {
|
||||
|
@ -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) {
|
||||
/*
|
||||
* 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() {
|
||||
var grammars = [
|
||||
'start: missing',
|
||||
'start: missing "a" "b"',
|
||||
'start: "a" "b" missing',
|
||||
'start: missing / "a" / "b"',
|
||||
'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 { }'
|
||||
];
|
||||
|
||||
|
@ -227,14 +227,14 @@ test("buildParser reports left recursion", function() {
|
|||
var grammars = [
|
||||
/* Direct */
|
||||
'start: start',
|
||||
'start: start "a" "b"',
|
||||
'start: start / "a" / "b"',
|
||||
'start: "a" / "b" / start',
|
||||
'start: start "a" "b"',
|
||||
'start: &start',
|
||||
'start: !start',
|
||||
'start: start?',
|
||||
'start: start*',
|
||||
'start: start+',
|
||||
'start: &start',
|
||||
'start: !start',
|
||||
'start: start { }',
|
||||
|
||||
/* Indirect */
|
||||
|
@ -259,6 +259,120 @@ test("buildParser allows custom start rule", function() {
|
|||
|
||||
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() {
|
||||
var parser = PEG.buildParser('start: "abcd"');
|
||||
parses(parser, "abcd", "abcd");
|
||||
|
@ -324,120 +438,6 @@ test("classes", function() {
|
|||
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() {
|
||||
/*
|
||||
* Should trigger a codepath where the cache is used (for the "a" rule).
|
||||
|
|
Loading…
Reference in a new issue