From 95ce20ed9237032bd5785d0be03a0d48dadcc67f Mon Sep 17 00:00:00 2001 From: David Majda Date: Wed, 1 Apr 2015 11:33:15 +0200 Subject: [PATCH] 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. --- Makefile | 2 +- lib/compiler/asts.js | 49 +++++++++++++++++++- lib/compiler/passes/report-left-recursion.js | 44 +----------------- 3 files changed, 50 insertions(+), 45 deletions(-) diff --git a/Makefile b/Makefile index e16de99..4f32b3d 100644 --- a/Makefile +++ b/Makefile @@ -10,8 +10,8 @@ MODULES = utils/arrays \ utils/classes \ grammar-error \ parser \ - compiler/asts \ compiler/visitor \ + compiler/asts \ compiler/opcodes \ compiler/javascript \ compiler/passes/generate-bytecode \ diff --git a/lib/compiler/asts.js b/lib/compiler/asts.js index 7c3a44f..cf51bcd 100644 --- a/lib/compiler/asts.js +++ b/lib/compiler/asts.js @@ -1,4 +1,5 @@ -var arrays = require("../utils/arrays"); +var arrays = require("../utils/arrays"), + visitor = require("./visitor"); /* AST utilities. */ var asts = { @@ -8,6 +9,52 @@ var asts = { indexOfRule: function(ast, 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); } }; diff --git a/lib/compiler/passes/report-left-recursion.js b/lib/compiler/passes/report-left-recursion.js index ead952a..774f1a3 100644 --- a/lib/compiler/passes/report-left-recursion.js +++ b/lib/compiler/passes/report-left-recursion.js @@ -16,48 +16,6 @@ var arrays = require("../../utils/arrays"), * it can lead to left recursion. */ 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({ rule: function(node, visitedRules) { check(node.expression, visitedRules.concat(node.name)); @@ -69,7 +27,7 @@ function reportLeftRecursion(ast) { check(element, visitedRules); } - return matchesEmpty(element); + return asts.matchesEmpty(ast, element); }); },