From 3a65316416cb30585c4c0a99213d828a5b586a96 Mon Sep 17 00:00:00 2001 From: David Majda Date: Fri, 19 Mar 2010 11:15:53 +0100 Subject: [PATCH] PEG.buildParser reports missing referenced rules. --- lib/compiler.js | 43 +++++++++++++++++++++++++++++++++++++++++++ test/compiler-test.js | 18 ++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/lib/compiler.js b/lib/compiler.js index 2931516..e1be6ee 100644 --- a/lib/compiler.js +++ b/lib/compiler.js @@ -38,6 +38,9 @@ PEG.buildParser = function(grammar, startRule) { throw new PEG.Grammar.GrammarError("Grammar must be object or string."); } + for (var key in ast) { + ast[key].checkReferencedRulesExist(ast); + } if (ast[startRule] === undefined) { throw new PEG.Grammar.GrammarError("Missing \"" + startRule + "\" rule."); } @@ -97,6 +100,46 @@ PEG.Grammar.Action = function(expression, action) { this._action = action; }; +/* ===== Referenced Rule Existence Checks ===== */ + +PEG.Grammar.Rule.prototype.checkReferencedRulesExist = function(grammar) { + this._expression.checkReferencedRulesExist(grammar); +}; + +PEG.Grammar.Literal.prototype.checkReferencedRulesExist = function(grammar) {}; + +PEG.Grammar.Any.prototype.checkReferencedRulesExist = function(grammar) {}; + +PEG.Grammar.Sequence.prototype.checkReferencedRulesExist = function(grammar) { + PEG.ArrayUtils.each(this._elements, function(element) { + element.checkReferencedRulesExist(grammar); + }); +}; + +PEG.Grammar.Choice.prototype.checkReferencedRulesExist = function(grammar) { + PEG.ArrayUtils.each(this._alternatives, function(alternative) { + alternative.checkReferencedRulesExist(grammar); + }); +}; + +PEG.Grammar.ZeroOrMore.prototype.checkReferencedRulesExist = function(grammar) { + this._element.checkReferencedRulesExist(grammar); +}; + +PEG.Grammar.NotPredicate.prototype.checkReferencedRulesExist = function(grammar) { + this._expression.checkReferencedRulesExist(grammar); +}; + +PEG.Grammar.RuleRef.prototype.checkReferencedRulesExist = function(grammar) { + if (grammar[this._name] === undefined) { + throw new PEG.Grammar.GrammarError("Referenced rule \"" + this._name + "\" does not exist."); + } +}; + +PEG.Grammar.Action.prototype.checkReferencedRulesExist = function(grammar) { + this._expression.checkReferencedRulesExist(grammar); +}; + /* ===== PEG.Compiler ===== */ PEG.Compiler = { diff --git a/test/compiler-test.js b/test/compiler-test.js index cdf94c4..3f92041 100644 --- a/test/compiler-test.js +++ b/test/compiler-test.js @@ -113,6 +113,24 @@ test("buildParser reports missing start rule", function() { throws(function() { PEG.buildParser({}); }, PEG.Grammar.GrammarError); }); +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*', + 'start: !missing', + 'start: &missing', + 'start: missing { }' + ]; + + 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");