2011-02-13 12:22:56 +01:00
|
|
|
(function() {
|
2010-08-21 14:36:44 +02:00
|
|
|
|
|
|
|
module("PEG.compiler.passes");
|
|
|
|
|
2011-10-03 14:01:25 +02:00
|
|
|
test("reports missing referenced rules", function() {
|
|
|
|
function testGrammar(grammar) {
|
|
|
|
raises(
|
|
|
|
function() {
|
|
|
|
var ast = PEG.parser.parse(grammar);
|
|
|
|
PEG.compiler.passes.reportMissingRules(ast);
|
|
|
|
},
|
|
|
|
function(e) {
|
|
|
|
return e instanceof PEG.GrammarError
|
|
|
|
&& e.message === "Referenced rule \"missing\" does not exist.";
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
var grammars = [
|
|
|
|
'start = missing',
|
|
|
|
'start = missing / "a" / "b"',
|
|
|
|
'start = "a" / "b" / missing',
|
|
|
|
'start = missing "a" "b"',
|
|
|
|
'start = "a" "b" missing',
|
|
|
|
'start = label:missing',
|
|
|
|
'start = &missing',
|
|
|
|
'start = !missing',
|
|
|
|
'start = missing?',
|
|
|
|
'start = missing*',
|
|
|
|
'start = missing+',
|
|
|
|
'start = missing { }'
|
|
|
|
];
|
|
|
|
|
|
|
|
for (var i = 0; i < grammars.length; i++) { testGrammar(grammars[i]); }
|
|
|
|
});
|
|
|
|
|
|
|
|
test("reports left recursion", function() {
|
|
|
|
function testGrammar(grammar) {
|
|
|
|
raises(
|
|
|
|
function() {
|
|
|
|
var ast = PEG.parser.parse(grammar);
|
|
|
|
PEG.compiler.passes.reportLeftRecursion(ast);
|
|
|
|
},
|
|
|
|
function(e) {
|
|
|
|
return e instanceof PEG.GrammarError
|
|
|
|
&& e.message === "Left recursion detected for rule \"start\".";
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
var grammars = [
|
|
|
|
/* Direct */
|
|
|
|
'start = start',
|
|
|
|
'start = start / "a" / "b"',
|
|
|
|
'start = "a" / "b" / start',
|
|
|
|
'start = start "a" "b"',
|
|
|
|
'start = label:start',
|
|
|
|
'start = &start',
|
|
|
|
'start = !start',
|
|
|
|
'start = start?',
|
|
|
|
'start = start*',
|
|
|
|
'start = start+',
|
|
|
|
'start = start { }',
|
|
|
|
|
|
|
|
/* Indirect */
|
|
|
|
'start = stop; stop = start'
|
|
|
|
];
|
|
|
|
|
|
|
|
for (var i = 0; i < grammars.length; i++) { testGrammar(grammars[i]); }
|
|
|
|
});
|
|
|
|
|
2010-08-21 14:36:44 +02:00
|
|
|
test("removes proxy rules", function() {
|
|
|
|
function simpleGrammar(rules, startRule) {
|
|
|
|
return {
|
|
|
|
type: "grammar",
|
|
|
|
initializer: null,
|
|
|
|
rules: rules,
|
|
|
|
startRule: startRule
|
2011-09-14 12:48:11 +02:00
|
|
|
};
|
2010-08-21 14:36:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
var proxiedRule = {
|
|
|
|
type: "rule",
|
|
|
|
name: "proxied",
|
|
|
|
displayName: null,
|
2011-09-30 11:17:47 +02:00
|
|
|
expression: { type: "literal", value: "a", ignoreCase: false }
|
2010-08-21 14:36:44 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
var proxiedRuleRef = {
|
|
|
|
type: "rule_ref",
|
|
|
|
name: "proxied"
|
2011-09-14 12:48:11 +02:00
|
|
|
};
|
2010-08-21 14:36:44 +02:00
|
|
|
|
|
|
|
function simpleGrammarWithStartAndProxied(startRuleExpression) {
|
|
|
|
return simpleGrammar(
|
|
|
|
{
|
|
|
|
start: {
|
|
|
|
type: "rule",
|
|
|
|
name: "start",
|
|
|
|
displayName: null,
|
|
|
|
expression: startRuleExpression
|
|
|
|
},
|
|
|
|
proxied: proxiedRule
|
|
|
|
},
|
|
|
|
"start"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
var cases = [
|
|
|
|
{
|
|
|
|
grammar: 'start = proxy; proxy = proxied; proxied = "a"',
|
|
|
|
ast: simpleGrammar({ proxied: proxiedRule }, "proxied")
|
|
|
|
},
|
|
|
|
{
|
|
|
|
grammar: 'start = proxy / "a" / "b"; proxy = proxied; proxied = "a"',
|
|
|
|
ast: simpleGrammarWithStartAndProxied({
|
|
|
|
type: "choice",
|
|
|
|
alternatives: [
|
|
|
|
proxiedRuleRef,
|
2011-09-30 11:17:47 +02:00
|
|
|
{ type: "literal", value: "a", ignoreCase: false },
|
|
|
|
{ type: "literal", value: "b", ignoreCase: false }
|
2010-08-21 14:36:44 +02:00
|
|
|
]
|
|
|
|
})
|
|
|
|
},
|
|
|
|
{
|
|
|
|
grammar: 'start = "a" / "b" / proxy; proxy = proxied; proxied = "a"',
|
|
|
|
ast: simpleGrammarWithStartAndProxied({
|
|
|
|
type: "choice",
|
|
|
|
alternatives: [
|
2011-09-30 11:17:47 +02:00
|
|
|
{ type: "literal", value: "a", ignoreCase: false },
|
|
|
|
{ type: "literal", value: "b", ignoreCase: false },
|
2010-08-21 14:36:44 +02:00
|
|
|
proxiedRuleRef
|
|
|
|
]
|
|
|
|
})
|
|
|
|
},
|
|
|
|
{
|
|
|
|
grammar: 'start = proxy "a" "b"; proxy = proxied; proxied = "a"',
|
|
|
|
ast: simpleGrammarWithStartAndProxied({
|
|
|
|
type: "sequence",
|
|
|
|
elements: [
|
|
|
|
proxiedRuleRef,
|
2011-09-30 11:17:47 +02:00
|
|
|
{ type: "literal", value: "a", ignoreCase: false },
|
|
|
|
{ type: "literal", value: "b", ignoreCase: false }
|
2010-08-21 14:36:44 +02:00
|
|
|
]
|
|
|
|
})
|
|
|
|
},
|
|
|
|
{
|
|
|
|
grammar: 'start = "a" "b" proxy; proxy = proxied; proxied = "a"',
|
|
|
|
ast: simpleGrammarWithStartAndProxied({
|
|
|
|
type: "sequence",
|
|
|
|
elements: [
|
2011-09-30 11:17:47 +02:00
|
|
|
{ type: "literal", value: "a", ignoreCase: false },
|
|
|
|
{ type: "literal", value: "b", ignoreCase: false },
|
2010-08-21 14:36:44 +02:00
|
|
|
proxiedRuleRef
|
|
|
|
]
|
|
|
|
})
|
|
|
|
},
|
|
|
|
{
|
|
|
|
grammar: 'start = label:proxy; proxy = proxied; proxied = "a"',
|
|
|
|
ast: simpleGrammarWithStartAndProxied({
|
|
|
|
type: "labeled",
|
|
|
|
label: "label",
|
|
|
|
expression: proxiedRuleRef
|
|
|
|
})
|
|
|
|
},
|
|
|
|
{
|
|
|
|
grammar: 'start = &proxy; proxy = proxied; proxied = "a"',
|
|
|
|
ast: simpleGrammarWithStartAndProxied({
|
|
|
|
type: "simple_and",
|
|
|
|
expression: proxiedRuleRef
|
|
|
|
})
|
|
|
|
},
|
|
|
|
{
|
|
|
|
grammar: 'start = !proxy; proxy = proxied; proxied = "a"',
|
|
|
|
ast: simpleGrammarWithStartAndProxied({
|
|
|
|
type: "simple_not",
|
|
|
|
expression: proxiedRuleRef
|
|
|
|
})
|
|
|
|
},
|
|
|
|
{
|
|
|
|
grammar: 'start = proxy?; proxy = proxied; proxied = "a"',
|
|
|
|
ast: simpleGrammarWithStartAndProxied({
|
|
|
|
type: "optional",
|
|
|
|
expression: proxiedRuleRef
|
|
|
|
})
|
|
|
|
},
|
|
|
|
{
|
|
|
|
grammar: 'start = proxy*; proxy = proxied; proxied = "a"',
|
|
|
|
ast: simpleGrammarWithStartAndProxied({
|
|
|
|
type: "zero_or_more",
|
|
|
|
expression: proxiedRuleRef
|
|
|
|
})
|
|
|
|
},
|
|
|
|
{
|
|
|
|
grammar: 'start = proxy+; proxy = proxied; proxied = "a"',
|
|
|
|
ast: simpleGrammarWithStartAndProxied({
|
|
|
|
type: "one_or_more",
|
|
|
|
expression: proxiedRuleRef
|
|
|
|
})
|
|
|
|
},
|
|
|
|
{
|
|
|
|
grammar: 'start = proxy { }; proxy = proxied; proxied = "a"',
|
|
|
|
ast: simpleGrammarWithStartAndProxied({
|
|
|
|
type: "action",
|
|
|
|
code: " ",
|
|
|
|
expression: proxiedRuleRef
|
|
|
|
})
|
|
|
|
}
|
|
|
|
];
|
|
|
|
|
|
|
|
for (var i = 0; i < cases.length; i++) {
|
|
|
|
var ast = PEG.parser.parse(cases[i].grammar);
|
2011-10-03 13:50:22 +02:00
|
|
|
PEG.compiler.passes.removeProxyRules(ast);
|
|
|
|
|
|
|
|
deepEqual(ast, cases[i].ast);
|
2010-08-21 14:36:44 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2011-08-22 19:48:43 +02:00
|
|
|
test("computes stack depths", function() {
|
|
|
|
var cases = [
|
|
|
|
/* Choice */
|
|
|
|
{
|
|
|
|
grammar: 'start = "a" / "b" / "c"',
|
|
|
|
resultStackDepth: 1,
|
2011-09-30 17:57:48 +02:00
|
|
|
posStackDepth: 0
|
2011-08-22 19:48:43 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
grammar: 'start = "a" / "b"* / "c"',
|
|
|
|
resultStackDepth: 2,
|
2011-09-30 17:57:48 +02:00
|
|
|
posStackDepth: 0
|
2011-08-22 19:48:43 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
grammar: 'start = "a" / &"b" / "c"',
|
|
|
|
resultStackDepth: 1,
|
2011-09-30 17:57:48 +02:00
|
|
|
posStackDepth: 1
|
2011-08-22 19:48:43 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
/* Sequence */
|
2011-09-30 19:47:05 +02:00
|
|
|
{
|
|
|
|
grammar: 'start = ',
|
|
|
|
resultStackDepth: 1,
|
|
|
|
posStackDepth: 1
|
|
|
|
},
|
2011-08-22 19:48:43 +02:00
|
|
|
{
|
|
|
|
grammar: 'start = "a" "b" "c"',
|
2011-09-30 17:19:14 +02:00
|
|
|
resultStackDepth: 3,
|
2011-09-30 17:57:48 +02:00
|
|
|
posStackDepth: 1
|
2011-08-22 19:48:43 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
grammar: 'start = "a" "b" "c"*',
|
2011-09-30 17:19:14 +02:00
|
|
|
resultStackDepth: 4,
|
2011-09-30 17:57:48 +02:00
|
|
|
posStackDepth: 1
|
2011-08-22 19:48:43 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
grammar: 'start = "a" "b"* "c"',
|
2011-09-30 17:19:14 +02:00
|
|
|
resultStackDepth: 3,
|
2011-09-30 17:57:48 +02:00
|
|
|
posStackDepth: 1
|
2011-08-22 19:48:43 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
grammar: 'start = "a" ("b"*)* "c"',
|
2011-09-30 17:19:14 +02:00
|
|
|
resultStackDepth: 4,
|
2011-09-30 17:57:48 +02:00
|
|
|
posStackDepth: 1
|
2011-08-22 19:48:43 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
grammar: 'start = "a"* "b" "c"',
|
2011-09-30 17:19:14 +02:00
|
|
|
resultStackDepth: 3,
|
2011-09-30 17:57:48 +02:00
|
|
|
posStackDepth: 1
|
2011-08-22 19:48:43 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
grammar: 'start = ("a"*)* "b" "c"',
|
2011-09-30 17:19:14 +02:00
|
|
|
resultStackDepth: 3,
|
2011-09-30 17:57:48 +02:00
|
|
|
posStackDepth: 1
|
2011-08-22 19:48:43 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
grammar: 'start = (("a"*)*)* "b" "c"',
|
2011-09-30 17:19:14 +02:00
|
|
|
resultStackDepth: 4,
|
2011-09-30 17:57:48 +02:00
|
|
|
posStackDepth: 1
|
2011-08-22 19:48:43 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
grammar: 'start = "a" &"b" "c"',
|
2011-09-30 17:19:14 +02:00
|
|
|
resultStackDepth: 3,
|
2011-09-30 17:57:48 +02:00
|
|
|
posStackDepth: 2
|
2011-08-22 19:48:43 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
/* Others */
|
2011-09-30 17:57:48 +02:00
|
|
|
{ grammar: 'start = label:"a"', resultStackDepth: 1, posStackDepth: 0 },
|
|
|
|
{ grammar: 'start = &"a"', resultStackDepth: 1, posStackDepth: 1 },
|
|
|
|
{ grammar: 'start = !"a"', resultStackDepth: 1, posStackDepth: 1 },
|
|
|
|
{ grammar: 'start = &{ code }', resultStackDepth: 1, posStackDepth: 0 },
|
|
|
|
{ grammar: 'start = !{ code }', resultStackDepth: 1, posStackDepth: 0 },
|
|
|
|
{ grammar: 'start = "a"?', resultStackDepth: 1, posStackDepth: 0 },
|
|
|
|
{ grammar: 'start = "a"*', resultStackDepth: 2, posStackDepth: 0 },
|
|
|
|
{ grammar: 'start = "a"+', resultStackDepth: 2, posStackDepth: 0 },
|
|
|
|
{ grammar: 'start = "a" { code }', resultStackDepth: 1, posStackDepth: 1 },
|
|
|
|
{ grammar: 'start = a', resultStackDepth: 1, posStackDepth: 0 },
|
|
|
|
{ grammar: 'start = "a"', resultStackDepth: 1, posStackDepth: 0 },
|
|
|
|
{ grammar: 'start = .', resultStackDepth: 1, posStackDepth: 0 },
|
|
|
|
{ grammar: 'start = [a-z]', resultStackDepth: 1, posStackDepth: 0 }
|
2011-08-22 19:48:43 +02:00
|
|
|
];
|
|
|
|
|
|
|
|
for (var i = 0; i < cases.length; i++) {
|
|
|
|
var ast = PEG.parser.parse(cases[i].grammar);
|
2011-10-03 13:34:25 +02:00
|
|
|
PEG.compiler.passes.computeStackDepths(ast);
|
2011-08-22 19:48:43 +02:00
|
|
|
|
|
|
|
deepEqual(ast.rules["start"].resultStackDepth, cases[i].resultStackDepth);
|
|
|
|
deepEqual(ast.rules["start"].posStackDepth, cases[i].posStackDepth);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2011-02-13 12:22:56 +01:00
|
|
|
})();
|