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.
redux
David Majda 10 years ago
parent 4a3b9cbb8d
commit 898a7b5a2d

@ -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 = [];

@ -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, []);

@ -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);

@ -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;
}
};

Loading…
Cancel
Save