Extract the |matchesEmpty| visitor from the |reportLeftRecursion| pass

Beside the recursion detector, the visitor will also be used by infinite
loop detector.

Note the newly created |asts.matchesEmpty| function re-creates the
visitor each time it is called, which makes it slower than necessary.
This could have been worked around in various ways but I chose to defer
that optimization because real-world performance impact is small.
redux
David Majda 9 years ago
parent 03a391e874
commit 95ce20ed92

@ -10,8 +10,8 @@ MODULES = utils/arrays \
utils/classes \ utils/classes \
grammar-error \ grammar-error \
parser \ parser \
compiler/asts \
compiler/visitor \ compiler/visitor \
compiler/asts \
compiler/opcodes \ compiler/opcodes \
compiler/javascript \ compiler/javascript \
compiler/passes/generate-bytecode \ compiler/passes/generate-bytecode \

@ -1,4 +1,5 @@
var arrays = require("../utils/arrays"); var arrays = require("../utils/arrays"),
visitor = require("./visitor");
/* AST utilities. */ /* AST utilities. */
var asts = { var asts = {
@ -8,6 +9,52 @@ var asts = {
indexOfRule: function(ast, name) { indexOfRule: function(ast, name) {
return arrays.indexOf(ast.rules, function(r) { return r.name === name; }); return arrays.indexOf(ast.rules, function(r) { return r.name === name; });
},
matchesEmpty: function(ast, node) {
function matchesTrue() { return true; }
function matchesFalse() { return false; }
function matchesExpression(node) {
return matches(node.expression);
}
var matches = visitor.build({
rule: matchesExpression,
choice: function(node) {
return arrays.some(node.alternatives, matches);
},
action: matchesExpression,
sequence: function(node) {
return arrays.every(node.elements, matches);
},
labeled: matchesExpression,
text: matchesExpression,
simple_and: matchesTrue,
simple_not: matchesTrue,
optional: matchesTrue,
zero_or_more: matchesTrue,
one_or_more: matchesExpression,
semantic_and: matchesTrue,
semantic_not: matchesTrue,
rule_ref: function(node) {
return matches(asts.findRule(ast, node.name));
},
literal: function(node) {
return node.value === "";
},
"class": matchesFalse,
any: matchesFalse
});
return matches(node);
} }
}; };

@ -16,48 +16,6 @@ var arrays = require("../../utils/arrays"),
* it can lead to left recursion. * it can lead to left recursion.
*/ */
function reportLeftRecursion(ast) { function reportLeftRecursion(ast) {
function matchesEmptyTrue() { return true; }
function matchesEmptyFalse() { return false; }
function matchesEmptyExpression(node) {
return matchesEmpty(node.expression);
}
var matchesEmpty = visitor.build({
rule: matchesEmptyExpression,
choice: function(node) {
return arrays.some(node.alternatives, matchesEmpty);
},
action: matchesEmptyExpression,
sequence: function(node) {
return arrays.every(node.elements, matchesEmpty);
},
labeled: matchesEmptyExpression,
text: matchesEmptyExpression,
simple_and: matchesEmptyTrue,
simple_not: matchesEmptyTrue,
optional: matchesEmptyTrue,
zero_or_more: matchesEmptyTrue,
one_or_more: matchesEmptyExpression,
semantic_and: matchesEmptyTrue,
semantic_not: matchesEmptyTrue,
rule_ref: function(node) {
return matchesEmpty(asts.findRule(ast, node.name));
},
literal: function(node) {
return node.value === "";
},
"class": matchesEmptyFalse,
any: matchesEmptyFalse
});
var check = visitor.build({ var check = visitor.build({
rule: function(node, visitedRules) { rule: function(node, visitedRules) {
check(node.expression, visitedRules.concat(node.name)); check(node.expression, visitedRules.concat(node.name));
@ -69,7 +27,7 @@ function reportLeftRecursion(ast) {
check(element, visitedRules); check(element, visitedRules);
} }
return matchesEmpty(element); return asts.matchesEmpty(ast, element);
}); });
}, },

Loading…
Cancel
Save