From 5885a34ddeb12e2613452651a0d942a7eaf57ddf Mon Sep 17 00:00:00 2001 From: David Majda Date: Fri, 21 May 2010 17:10:19 +0200 Subject: [PATCH] AST refactoring 3/6: Rewrite checks to not extend the AST nodes --- lib/compiler.js | 241 ++++++++++++++++++------------------------------ 1 file changed, 92 insertions(+), 149 deletions(-) diff --git a/lib/compiler.js b/lib/compiler.js index 44c794d..c28ffff 100644 --- a/lib/compiler.js +++ b/lib/compiler.js @@ -2,8 +2,6 @@ (function() { -function nop() {} - /* ===== PEG ===== */ /* no var */ PEG = {}; @@ -142,31 +140,7 @@ PEG.RegExpUtils = { /* Namespace with grammar AST nodes. */ -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 = {}; /* ===== PEG.Grammar.GrammarError ===== */ @@ -248,126 +222,6 @@ PEG.Grammar.Class = function(characters) { this.characters = characters; }; -/* ===== Referenced Rule Existence Checks ===== */ - -PEG.Grammar.extendNodes("checkReferencedRulesExist", { - Rule: - function(grammar) { - this.expression.checkReferencedRulesExist(grammar); - }, - - Choice: - function(grammar) { - PEG.ArrayUtils.each(this.alternatives, function(alternative) { - alternative.checkReferencedRulesExist(grammar); - }); - }, - - Sequence: - function(grammar) { - PEG.ArrayUtils.each(this.elements, function(element) { - element.checkReferencedRulesExist(grammar); - }); - }, - - AndPredicate: - function(grammar) { this.expression.checkReferencedRulesExist(grammar); }, - - NotPredicate: - function(grammar) { this.expression.checkReferencedRulesExist(grammar); }, - - Optional: - function(grammar) { this.expression.checkReferencedRulesExist(grammar); }, - - ZeroOrMore: - function(grammar) { this.expression.checkReferencedRulesExist(grammar); }, - - OneOrMore: - function(grammar) { this.expression.checkReferencedRulesExist(grammar); }, - - Action: - function(grammar) { this.expression.checkReferencedRulesExist(grammar); }, - - RuleRef: - function(grammar) { - if (grammar[this.name] === undefined) { - throw new PEG.Grammar.GrammarError( - "Referenced rule \"" + this.name + "\" does not exist." - ); - } - }, - - Literal: nop, - Any: nop, - Class: nop -}); - - -/* ===== Left Recursion Checks ===== */ - -PEG.Grammar.extendNodes("checkNoLeftRecursion", { - Rule: - function(grammar, appliedRules) { - this.expression.checkNoLeftRecursion(grammar, appliedRules.concat(this.name)); - }, - - Choice: - function(grammar, appliedRules) { - PEG.ArrayUtils.each(this.alternatives, function(alternative) { - alternative.checkNoLeftRecursion(grammar, appliedRules); - }); - }, - - Sequence: - function(grammar, appliedRules) { - if (this.elements.length > 0) { - this.elements[0].checkNoLeftRecursion(grammar, appliedRules); - } - }, - - AndPredicate: - function(grammar, appliedRules) { - this.expression.checkNoLeftRecursion(grammar, appliedRules); - }, - - NotPredicate: - function(grammar, appliedRules) { - this.expression.checkNoLeftRecursion(grammar, appliedRules); - }, - - Optional: - function(grammar, appliedRules) { - this.expression.checkNoLeftRecursion(grammar, appliedRules); - }, - - ZeroOrMore: - function(grammar, appliedRules) { - this.expression.checkNoLeftRecursion(grammar, appliedRules); - }, - - OneOrMore: - function(grammar, appliedRules) { - this.expression.checkNoLeftRecursion(grammar, appliedRules); - }, - - Action: - function(grammar, appliedRules) { - this.expression.checkNoLeftRecursion(grammar, appliedRules); - }, - - RuleRef: - function(grammar, appliedRules) { - if (PEG.ArrayUtils.contains(appliedRules, this.name)) { - throw new PEG.Grammar.GrammarError("Left recursion detected for rule \"" + this.name + "\"."); - } - grammar[this.name].checkNoLeftRecursion(grammar, appliedRules); - }, - - Literal: nop, - Any: nop, - Class: nop -}); - /* ===== PEG.Compiler ===== */ PEG.Compiler = { @@ -482,8 +336,45 @@ PEG.Compiler = { _checks: [ /* Checks that all referenced rules exist. */ function(ast, startRule) { + function nop() {} + + function checkExpression(node) { check(node.expression); } + + function checkSubnodes(propertyName) { + return function(node) { + PEG.ArrayUtils.each(node[propertyName], check); + }; + } + + var checkFunctions = { + rule: checkExpression, + choice: checkSubnodes("alternatives"), + sequence: checkSubnodes("elements"), + and_predicate: checkExpression, + not_predicate: checkExpression, + optional: checkExpression, + zero_or_more: checkExpression, + one_or_more: checkExpression, + action: checkExpression, + + rule_ref: + function(node) { + if (ast[node.name] === undefined) { + throw new PEG.Grammar.GrammarError( + "Referenced rule \"" + node.name + "\" does not exist." + ); + } + }, + + literal: nop, + any: nop, + "class": nop + }; + + function check(node) { checkFunctions[node.type](node); } + for (var rule in ast) { - ast[rule].checkReferencedRulesExist(ast); + check(ast[rule]); } }, @@ -498,8 +389,60 @@ PEG.Compiler = { /* Checks that no left recursion is present. */ function(ast, startRule) { + function nop() {} + + function checkExpression(node, appliedRules) { + check(node.expression, appliedRules); + } + + var checkFunctions = { + rule: + function(node, appliedRules) { + check(node.expression, appliedRules.concat(node.name)); + }, + + choice: + function(node, appliedRules) { + PEG.ArrayUtils.each(node.alternatives, function(alternative) { + check(alternative, appliedRules); + }); + }, + + sequence: + function(node, appliedRules) { + if (node.elements.length > 0) { + check(node.elements[0], appliedRules); + } + }, + + and_predicate: checkExpression, + not_predicate: checkExpression, + optional: checkExpression, + zero_or_more: checkExpression, + one_or_more: checkExpression, + action: checkExpression, + + rule_ref: + function(node, appliedRules) { + if (PEG.ArrayUtils.contains(appliedRules, node.name)) { + throw new PEG.Grammar.GrammarError( + "Left recursion detected for rule \"" + node.name + "\"." + ); + } + check(ast[node.name], appliedRules); + }, + + literal: nop, + any: nop, + "class": nop + }; + + function check(node, appliedRules) { + checkFunctions[node.type](node, appliedRules); + } + for (var rule in ast) { - ast[rule].checkNoLeftRecursion(ast, []); + check(ast[rule], []); } } ],