diff --git a/lib/compiler/passes/remove-proxy-rules.js b/lib/compiler/passes/remove-proxy-rules.js index 49cbcc4..8ab922e 100644 --- a/lib/compiler/passes/remove-proxy-rules.js +++ b/lib/compiler/passes/remove-proxy-rules.js @@ -10,48 +10,15 @@ function removeProxyRules(ast, options) { } function replaceRuleRefs(ast, from, to) { - function nop() {} - - function replaceInExpression(node, from, to) { - replace(node.expression, from, to); - } - - function replaceInSubnodes(propertyName) { - return function(node, from, to) { - arrays.each(node[propertyName], function(n) { replace(n, from, to); }); - }; - } - var replace = visitor.build({ - grammar: replaceInSubnodes("rules"), - rule: replaceInExpression, - named: replaceInExpression, - choice: replaceInSubnodes("alternatives"), - sequence: replaceInSubnodes("elements"), - labeled: replaceInExpression, - text: replaceInExpression, - simple_and: replaceInExpression, - simple_not: replaceInExpression, - semantic_and: nop, - semantic_not: nop, - optional: replaceInExpression, - zero_or_more: replaceInExpression, - one_or_more: replaceInExpression, - action: replaceInExpression, - - rule_ref: - function(node, from, to) { - if (node.name === from) { - node.name = to; - } - }, - - literal: nop, - "class": nop, - any: nop + rule_ref: function(node) { + if (node.name === from) { + node.name = to; + } + } }); - replace(ast, from, to); + replace(ast); } var indices = []; diff --git a/lib/compiler/passes/report-left-recursion.js b/lib/compiler/passes/report-left-recursion.js index 7652065..56d98f8 100644 --- a/lib/compiler/passes/report-left-recursion.js +++ b/lib/compiler/passes/report-left-recursion.js @@ -5,60 +5,25 @@ var arrays = require("../../utils/arrays"), /* Checks that no left recursion is present. */ function reportLeftRecursion(ast) { - function nop() {} - - function checkExpression(node, appliedRules) { - check(node.expression, appliedRules); - } - - function checkSubnodes(propertyName) { - return function(node, appliedRules) { - arrays.each(node[propertyName], function(n) { check(n, appliedRules); }); - }; - } - var check = visitor.build({ - grammar: checkSubnodes("rules"), - - rule: - function(node, appliedRules) { - check(node.expression, appliedRules.concat(node.name)); - }, - - named: checkExpression, - choice: checkSubnodes("alternatives"), - action: checkExpression, - - sequence: - function(node, appliedRules) { - if (node.elements.length > 0) { - check(node.elements[0], appliedRules); - } - }, - - labeled: checkExpression, - text: checkExpression, - simple_and: checkExpression, - simple_not: checkExpression, - semantic_and: nop, - semantic_not: nop, - optional: checkExpression, - zero_or_more: checkExpression, - one_or_more: checkExpression, - - rule_ref: - function(node, appliedRules) { - if (arrays.contains(appliedRules, node.name)) { - throw new GrammarError( - "Left recursion detected for rule \"" + node.name + "\"." - ); - } - check(asts.findRule(ast, node.name), appliedRules); - }, - - literal: nop, - "class": nop, - any: nop + rule: function(node, appliedRules) { + check(node.expression, appliedRules.concat(node.name)); + }, + + sequence: function(node, appliedRules) { + if (node.elements.length > 0) { + check(node.elements[0], appliedRules); + } + }, + + rule_ref: function(node, appliedRules) { + if (arrays.contains(appliedRules, node.name)) { + throw new GrammarError( + "Left recursion detected for rule \"" + node.name + "\"." + ); + } + check(asts.findRule(ast, node.name), appliedRules); + } }); check(ast, []); diff --git a/lib/compiler/passes/report-missing-rules.js b/lib/compiler/passes/report-missing-rules.js index 4cc4559..417e414 100644 --- a/lib/compiler/passes/report-missing-rules.js +++ b/lib/compiler/passes/report-missing-rules.js @@ -1,47 +1,17 @@ -var arrays = require("../../utils/arrays"), - GrammarError = require("../../grammar-error"), +var GrammarError = require("../../grammar-error"), asts = require("../asts"), visitor = require("../visitor"); /* Checks that all referenced rules exist. */ function reportMissingRules(ast) { - function nop() {} - - function checkExpression(node) { check(node.expression); } - - function checkSubnodes(propertyName) { - return function(node) { arrays.each(node[propertyName], check); }; - } - var check = visitor.build({ - grammar: checkSubnodes("rules"), - rule: checkExpression, - named: checkExpression, - choice: checkSubnodes("alternatives"), - action: checkExpression, - sequence: checkSubnodes("elements"), - labeled: checkExpression, - text: checkExpression, - simple_and: checkExpression, - simple_not: checkExpression, - semantic_and: nop, - semantic_not: nop, - optional: checkExpression, - zero_or_more: checkExpression, - one_or_more: checkExpression, - - rule_ref: - function(node) { - if (!asts.findRule(ast, node.name)) { - throw new GrammarError( - "Referenced rule \"" + node.name + "\" does not exist." - ); - } - }, - - literal: nop, - "class": nop, - any: nop + rule_ref: function(node) { + if (!asts.findRule(ast, node.name)) { + throw new GrammarError( + "Referenced rule \"" + node.name + "\" does not exist." + ); + } + } }); check(ast); diff --git a/lib/compiler/visitor.js b/lib/compiler/visitor.js index fe65e6c..607788a 100644 --- a/lib/compiler/visitor.js +++ b/lib/compiler/visitor.js @@ -1,9 +1,68 @@ +var objects = require("../utils/objects"), + arrays = require("../utils/arrays"); + /* Simple AST node visitor builder. */ var visitor = { build: function(functions) { - return function(node) { + function visit(node) { return functions[node.type].apply(null, arguments); - }; + } + + function visitNop() { } + + function visitExpression(node) { + var extraArgs = Array.prototype.slice.call(arguments, 1); + + visit.apply(null, [node.expression].concat(extraArgs)); + } + + function visitChildren(property) { + return function(node) { + var extraArgs = Array.prototype.slice.call(arguments, 1); + + arrays.each(node[property], function(child) { + visit.apply(null, [child].concat(extraArgs)); + }); + }; + } + + var DEFAULT_FUNCTIONS = { + grammar: function(node) { + var extraArgs = Array.prototype.slice.call(arguments, 1); + + if (node.initializer) { + visit.apply(null, [node.initializer].concat(extraArgs)); + } + + arrays.each(node.rules, function(rule) { + visit.apply(null, [rule].concat(extraArgs)); + }); + }, + + initializer: visitNop, + rule: visitExpression, + named: visitExpression, + choice: visitChildren("alternatives"), + action: visitExpression, + sequence: visitChildren("elements"), + labeled: visitExpression, + text: visitExpression, + simple_and: visitExpression, + simple_not: visitExpression, + semantic_and: visitNop, + semantic_not: visitNop, + optional: visitExpression, + zero_or_more: visitExpression, + one_or_more: visitExpression, + rule_ref: visitNop, + literal: visitNop, + "class": visitNop, + any: visitNop + }; + + objects.defaults(functions, DEFAULT_FUNCTIONS); + + return visit; } };