From 898a7b5a2d0438aecfca9d0ec548ff60fcb00747 Mon Sep 17 00:00:00 2001 From: David Majda Date: Wed, 4 Jun 2014 07:23:34 +0200 Subject: [PATCH] Simplify visitors by providing default visit functions The |visitor.build| function now supplies default visit functions for visitors it builds. These functions don't do anything beside traversing the tree and passing arguments around to child visit functions. Having the default visit functions allowed to simplify several visitors. --- lib/compiler/passes/remove-proxy-rules.js | 45 ++----------- lib/compiler/passes/report-left-recursion.js | 71 +++++--------------- lib/compiler/passes/report-missing-rules.js | 46 +++---------- lib/compiler/visitor.js | 63 ++++++++++++++++- 4 files changed, 93 insertions(+), 132 deletions(-) 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; } };