Browse Source

PEG.buildParser reports left recursion (both direct and indirect).

redux
David Majda 12 years ago
parent
commit
56ffa94cc7
  1. 44
      lib/compiler.js
  2. 21
      test/compiler-test.js

44
lib/compiler.js

@ -44,6 +44,9 @@ PEG.buildParser = function(grammar, startRule) {
if (ast[startRule] === undefined) {
throw new PEG.Grammar.GrammarError("Missing \"" + startRule + "\" rule.");
}
for (var key in ast) {
ast[key].checkNoLeftRecursion(ast, []);
}
return PEG.Compiler.compileParser(ast, startRule);
};
@ -140,6 +143,47 @@ PEG.Grammar.Action.prototype.checkReferencedRulesExist = function(grammar) {
this._expression.checkReferencedRulesExist(grammar);
};
/* ===== Left Recursion Checks ===== */
PEG.Grammar.Rule.prototype.checkNoLeftRecursion = function(grammar, appliedRules) {
this._expression.checkNoLeftRecursion(grammar, appliedRules.concat(this._name));
};
PEG.Grammar.Literal.prototype.checkNoLeftRecursion = function(grammar, appliedRules) {};
PEG.Grammar.Any.prototype.checkNoLeftRecursion = function(grammar, appliedRules) {};
PEG.Grammar.Sequence.prototype.checkNoLeftRecursion = function(grammar, appliedRules) {
if (this._elements.length > 0) {
this._elements[0].checkNoLeftRecursion(grammar, appliedRules);
}
};
PEG.Grammar.Choice.prototype.checkNoLeftRecursion = function(grammar, appliedRules) {
PEG.ArrayUtils.each(this._alternatives, function(alternative) {
alternative.checkNoLeftRecursion(grammar, appliedRules);
});
};
PEG.Grammar.ZeroOrMore.prototype.checkNoLeftRecursion = function(grammar, appliedRules) {
this._element.checkNoLeftRecursion(grammar, appliedRules);
};
PEG.Grammar.NotPredicate.prototype.checkNoLeftRecursion = function(grammar, appliedRules) {
this._expression.checkNoLeftRecursion(grammar, appliedRules);
};
PEG.Grammar.RuleRef.prototype.checkNoLeftRecursion = function(grammar, appliedRules) {
if (appliedRules.indexOf(this._name) !== -1) {
throw new PEG.Grammar.GrammarError("Left recursion detected for rule \"" + this._name + "\".");
}
grammar[this._name].checkNoLeftRecursion(grammar, appliedRules);
};
PEG.Grammar.Action.prototype.checkNoLeftRecursion = function(grammar, appliedRules) {
this._expression.checkNoLeftRecursion(grammar, appliedRules);
};
/* ===== PEG.Compiler ===== */
PEG.Compiler = {

21
test/compiler-test.js

@ -131,6 +131,27 @@ test("buildParser reports missing referenced rules", function() {
});
});
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*',
'start: !start',
'start: &start',
'start: start { }',
/* Indirect */
'start: stop\nstop: start'
];
PEG.ArrayUtils.each(grammars, function(grammar) {
throws(function() { PEG.buildParser(grammar); }, PEG.Grammar.GrammarError);
});
});
test("buildParser allows custom start rule", function() {
var parser = PEG.buildParser('s: "abcd"', "s");
parses(parser, "abcd", "abcd");

Loading…
Cancel
Save