From 714512f23221b8c91711371ef1a6f716c2ba68ab Mon Sep 17 00:00:00 2001 From: David Majda Date: Sun, 11 Apr 2010 14:48:52 +0200 Subject: [PATCH] Implemented generic AST node extension mechanism. --- lib/compiler.js | 171 +++++++++++++++++++++++++++++------------------- 1 file changed, 103 insertions(+), 68 deletions(-) diff --git a/lib/compiler.js b/lib/compiler.js index 485510b..eb73fd0 100644 --- a/lib/compiler.js +++ b/lib/compiler.js @@ -57,7 +57,31 @@ PEG.buildParser = function(grammar, startRule) { /* Namespace with grammar AST nodes. */ -PEG.Grammar = {}; +PEG.Grammar = { + /* + * Extends specified AST node classes with a function named |name|. The + * definition of the function is different for each node class and it is + * specified in the |functions| object, which contains the functions keyed by + * unqualified AST node names. + * + * Example: + * + * PEG.Grammar.extendNodes("foo", { + * Literal: function() { return 1; }, + * Class: function() { return 2; } + * }); + * + * This is is equivalent to: + * + * PEG.Grammar.Literal.prototype.foo = function() { return 1; }; + * PEG.Grammar.Class.prototype.foo = function() { return 2; }; + */ + extendNodes: function(name, functions) { + for (var nodeName in functions) { + PEG.Grammar[nodeName].prototype[name] = functions[nodeName]; + } + } +}; /* ===== PEG.Grammar.GrammarError ===== */ @@ -109,88 +133,99 @@ PEG.Grammar.Action = function(expression, action) { /* ===== Referenced Rule Existence Checks ===== */ -PEG.Grammar.Rule.prototype.checkReferencedRulesExist = function(grammar) { - this._expression.checkReferencedRulesExist(grammar); -}; +PEG.Grammar.extendNodes("checkReferencedRulesExist", { + Rule: + function(grammar) { + this._expression.checkReferencedRulesExist(grammar); + }, -PEG.Grammar.Literal.prototype.checkReferencedRulesExist = nop; + Literal: nop, + Class: nop, + Any: nop, -PEG.Grammar.Class.prototype.checkReferencedRulesExist = nop; + Sequence: + function(grammar) { + PEG.ArrayUtils.each(this._elements, function(element) { + element.checkReferencedRulesExist(grammar); + }); + }, -PEG.Grammar.Any.prototype.checkReferencedRulesExist = nop; + Choice: + function(grammar) { + PEG.ArrayUtils.each(this._alternatives, function(alternative) { + alternative.checkReferencedRulesExist(grammar); + }); + }, -PEG.Grammar.Sequence.prototype.checkReferencedRulesExist = function(grammar) { - PEG.ArrayUtils.each(this._elements, function(element) { - element.checkReferencedRulesExist(grammar); - }); -}; + ZeroOrMore: + function(grammar) { this._element.checkReferencedRulesExist(grammar); }, -PEG.Grammar.Choice.prototype.checkReferencedRulesExist = function(grammar) { - PEG.ArrayUtils.each(this._alternatives, function(alternative) { - alternative.checkReferencedRulesExist(grammar); - }); -}; + NotPredicate: + function(grammar) { this._expression.checkReferencedRulesExist(grammar); }, -PEG.Grammar.ZeroOrMore.prototype.checkReferencedRulesExist = function(grammar) { - this._element.checkReferencedRulesExist(grammar); -}; + RuleRef: + function(grammar) { + if (grammar[this._name] === undefined) { + throw new PEG.Grammar.GrammarError( + "Referenced rule \"" + this._name + "\" does not exist." + ); + } + }, -PEG.Grammar.NotPredicate.prototype.checkReferencedRulesExist = function(grammar) { - this._expression.checkReferencedRulesExist(grammar); -}; + Action: + 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); -}; /* ===== Left Recursion Checks ===== */ -PEG.Grammar.Rule.prototype.checkNoLeftRecursion = function(grammar, appliedRules) { - this._expression.checkNoLeftRecursion(grammar, appliedRules.concat(this._name)); -}; - -PEG.Grammar.Literal.prototype.checkNoLeftRecursion = nop; - -PEG.Grammar.Class.prototype.checkNoLeftRecursion = nop; - -PEG.Grammar.Any.prototype.checkNoLeftRecursion = nop; +PEG.Grammar.extendNodes("checkNoLeftRecursion", { + Rule: + function(grammar, appliedRules) { + this._expression.checkNoLeftRecursion(grammar, appliedRules.concat(this._name)); + }, -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); - }); -}; + Literal: nop, + Class: nop, + Any: nop, -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); -}; + Sequence: + function(grammar, appliedRules) { + if (this._elements.length > 0) { + this._elements[0].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); -}; + Choice: + function(grammar, appliedRules) { + PEG.ArrayUtils.each(this._alternatives, function(alternative) { + alternative.checkNoLeftRecursion(grammar, appliedRules); + }); + }, + + ZeroOrMore: + function(grammar, appliedRules) { + this._element.checkNoLeftRecursion(grammar, appliedRules); + }, + + NotPredicate: + function(grammar, appliedRules) { + this._expression.checkNoLeftRecursion(grammar, appliedRules); + }, + + RuleRef: + 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); -}; + Action: + function(grammar, appliedRules) { + this._expression.checkNoLeftRecursion(grammar, appliedRules); + } +}); /* ===== PEG.Compiler ===== */