diff --git a/src/checks.js b/src/checks.js
deleted file mode 100644
index 40adb1b..0000000
--- a/src/checks.js
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Checks made on the grammar AST before compilation. Each check is a function
- * that is passed the AST and does not return anything. If the check passes, the
- * function does not do anything special, otherwise it throws
- * |PEG.GrammarError|. The order in which the checks are run is specified in
- * |PEG.compiler.compile| and should be the same as the order of definitions
- * here.
- */
-PEG.compiler.checks = {
- /* Checks that all referenced rules exist. */
- reportMissingRules: function(ast) {
- function nop() {}
-
- function checkExpression(node) { check(node.expression); }
-
- function checkSubnodes(propertyName) {
- return function(node) { each(node[propertyName], check); };
- }
-
- var check = buildNodeVisitor({
- grammar:
- function(node) {
- for (var name in node.rules) {
- check(node.rules[name]);
- }
- },
-
- rule: checkExpression,
- choice: checkSubnodes("alternatives"),
- sequence: checkSubnodes("elements"),
- labeled: checkExpression,
- simple_and: checkExpression,
- simple_not: checkExpression,
- semantic_and: nop,
- semantic_not: nop,
- optional: checkExpression,
- zero_or_more: checkExpression,
- one_or_more: checkExpression,
- action: checkExpression,
-
- rule_ref:
- function(node) {
- if (ast.rules[node.name] === undefined) {
- throw new PEG.GrammarError(
- "Referenced rule \"" + node.name + "\" does not exist."
- );
- }
- },
-
- literal: nop,
- any: nop,
- "class": nop
- });
-
- check(ast);
- },
-
- /* Checks that no left recursion is present. */
- reportLeftRecursion: function(ast) {
- function nop() {}
-
- function checkExpression(node, appliedRules) {
- check(node.expression, appliedRules);
- }
-
- var check = buildNodeVisitor({
- grammar:
- function(node, appliedRules) {
- for (var name in node.rules) {
- check(node.rules[name], appliedRules);
- }
- },
-
- rule:
- function(node, appliedRules) {
- check(node.expression, appliedRules.concat(node.name));
- },
-
- choice:
- function(node, appliedRules) {
- each(node.alternatives, function(alternative) {
- check(alternative, appliedRules);
- });
- },
-
- sequence:
- function(node, appliedRules) {
- if (node.elements.length > 0) {
- check(node.elements[0], appliedRules);
- }
- },
-
- labeled: checkExpression,
- simple_and: checkExpression,
- simple_not: checkExpression,
- semantic_and: nop,
- semantic_not: nop,
- optional: checkExpression,
- zero_or_more: checkExpression,
- one_or_more: checkExpression,
- action: checkExpression,
-
- rule_ref:
- function(node, appliedRules) {
- if (contains(appliedRules, node.name)) {
- throw new PEG.GrammarError(
- "Left recursion detected for rule \"" + node.name + "\"."
- );
- }
- check(ast.rules[node.name], appliedRules);
- },
-
- literal: nop,
- any: nop,
- "class": nop
- });
-
- check(ast, []);
- }
-};
diff --git a/src/compiler.js b/src/compiler.js
index 1601876..c913e06 100644
--- a/src/compiler.js
+++ b/src/compiler.js
@@ -6,23 +6,14 @@ PEG.compiler = {
* cause its malfunction.
*/
compile: function(ast) {
- var CHECK_NAMES = [
- "reportMissingRules",
- "reportLeftRecursion"
- ];
-
var PASS_NAMES = [
+ "reportMissingRules",
+ "reportLeftRecursion",
"removeProxyRules",
"computeStackDepths"
];
- var i;
-
- for (i = 0; i < CHECK_NAMES.length; i++) {
- this.checks[CHECK_NAMES[i]](ast);
- }
-
- for (i = 0; i < PASS_NAMES.length; i++) {
+ for (var i = 0; i < PASS_NAMES.length; i++) {
this.passes[PASS_NAMES[i]](ast);
}
@@ -34,6 +25,5 @@ PEG.compiler = {
}
};
-// @include "checks.js"
// @include "passes.js"
// @include "emitter.js"
diff --git a/src/passes.js b/src/passes.js
index 427aa03..6c87152 100644
--- a/src/passes.js
+++ b/src/passes.js
@@ -1,11 +1,122 @@
/*
- * Optimalization passes made on the grammar AST before compilation. Each pass
- * is a function that is passed the AST and does not return anything. The AST
- * can be modified in-place by the pass. The order in which the passes are run
- * is specified in |PEG.compiler.compile| and should be the same as the order of
- * definitions here.
+ * Compiler passes.
+ *
+ * Each pass is a function that is passed the AST. It can perform checks on it
+ * or modify it as needed. If the pass encounters a semantic error, it throws
+ * |PEG.GrammarError|.
*/
PEG.compiler.passes = {
+ /* Checks that all referenced rules exist. */
+ reportMissingRules: function(ast) {
+ function nop() {}
+
+ function checkExpression(node) { check(node.expression); }
+
+ function checkSubnodes(propertyName) {
+ return function(node) { each(node[propertyName], check); };
+ }
+
+ var check = buildNodeVisitor({
+ grammar:
+ function(node) {
+ for (var name in node.rules) {
+ check(node.rules[name]);
+ }
+ },
+
+ rule: checkExpression,
+ choice: checkSubnodes("alternatives"),
+ sequence: checkSubnodes("elements"),
+ labeled: checkExpression,
+ simple_and: checkExpression,
+ simple_not: checkExpression,
+ semantic_and: nop,
+ semantic_not: nop,
+ optional: checkExpression,
+ zero_or_more: checkExpression,
+ one_or_more: checkExpression,
+ action: checkExpression,
+
+ rule_ref:
+ function(node) {
+ if (ast.rules[node.name] === undefined) {
+ throw new PEG.GrammarError(
+ "Referenced rule \"" + node.name + "\" does not exist."
+ );
+ }
+ },
+
+ literal: nop,
+ any: nop,
+ "class": nop
+ });
+
+ check(ast);
+ },
+
+ /* Checks that no left recursion is present. */
+ reportLeftRecursion: function(ast) {
+ function nop() {}
+
+ function checkExpression(node, appliedRules) {
+ check(node.expression, appliedRules);
+ }
+
+ var check = buildNodeVisitor({
+ grammar:
+ function(node, appliedRules) {
+ for (var name in node.rules) {
+ check(node.rules[name], appliedRules);
+ }
+ },
+
+ rule:
+ function(node, appliedRules) {
+ check(node.expression, appliedRules.concat(node.name));
+ },
+
+ choice:
+ function(node, appliedRules) {
+ each(node.alternatives, function(alternative) {
+ check(alternative, appliedRules);
+ });
+ },
+
+ sequence:
+ function(node, appliedRules) {
+ if (node.elements.length > 0) {
+ check(node.elements[0], appliedRules);
+ }
+ },
+
+ labeled: checkExpression,
+ simple_and: checkExpression,
+ simple_not: checkExpression,
+ semantic_and: nop,
+ semantic_not: nop,
+ optional: checkExpression,
+ zero_or_more: checkExpression,
+ one_or_more: checkExpression,
+ action: checkExpression,
+
+ rule_ref:
+ function(node, appliedRules) {
+ if (contains(appliedRules, node.name)) {
+ throw new PEG.GrammarError(
+ "Left recursion detected for rule \"" + node.name + "\"."
+ );
+ }
+ check(ast.rules[node.name], appliedRules);
+ },
+
+ literal: nop,
+ any: nop,
+ "class": nop
+ });
+
+ check(ast, []);
+ },
+
/*
* Removes proxy rules -- that is, rules that only delegate to other rule.
*/
diff --git a/test/checks-test.js b/test/checks-test.js
deleted file mode 100644
index d69a63d..0000000
--- a/test/checks-test.js
+++ /dev/null
@@ -1,72 +0,0 @@
-(function() {
-
-module("PEG.compiler.checks");
-
-test("reports missing referenced rules", function() {
- function testGrammar(grammar) {
- raises(
- function() {
- var ast = PEG.parser.parse(grammar);
- PEG.compiler.checks.reportMissingRules(ast);
- },
- function(e) {
- return e instanceof PEG.GrammarError
- && e.message === "Referenced rule \"missing\" does not exist.";
- }
- );
- }
-
- var grammars = [
- 'start = missing',
- 'start = missing / "a" / "b"',
- 'start = "a" / "b" / missing',
- 'start = missing "a" "b"',
- 'start = "a" "b" missing',
- 'start = label:missing',
- 'start = &missing',
- 'start = !missing',
- 'start = missing?',
- 'start = missing*',
- 'start = missing+',
- 'start = missing { }'
- ];
-
- for (var i = 0; i < grammars.length; i++) { testGrammar(grammars[i]); }
-});
-
-test("reports left recursion", function() {
- function testGrammar(grammar) {
- raises(
- function() {
- var ast = PEG.parser.parse(grammar);
- PEG.compiler.checks.reportLeftRecursion(ast);
- },
- function(e) {
- return e instanceof PEG.GrammarError
- && e.message === "Left recursion detected for rule \"start\".";
- }
- );
- }
-
- var grammars = [
- /* Direct */
- 'start = start',
- 'start = start / "a" / "b"',
- 'start = "a" / "b" / start',
- 'start = start "a" "b"',
- 'start = label:start',
- 'start = &start',
- 'start = !start',
- 'start = start?',
- 'start = start*',
- 'start = start+',
- 'start = start { }',
-
- /* Indirect */
- 'start = stop; stop = start'
- ];
-
- for (var i = 0; i < grammars.length; i++) { testGrammar(grammars[i]); }
-});
-
-})();
diff --git a/test/index.html b/test/index.html
index 89c8bf8..12422ff 100644
--- a/test/index.html
+++ b/test/index.html
@@ -8,7 +8,6 @@
-
diff --git a/test/passes-test.js b/test/passes-test.js
index 5ec4f60..0f27def 100644
--- a/test/passes-test.js
+++ b/test/passes-test.js
@@ -2,6 +2,73 @@
module("PEG.compiler.passes");
+test("reports missing referenced rules", function() {
+ function testGrammar(grammar) {
+ raises(
+ function() {
+ var ast = PEG.parser.parse(grammar);
+ PEG.compiler.passes.reportMissingRules(ast);
+ },
+ function(e) {
+ return e instanceof PEG.GrammarError
+ && e.message === "Referenced rule \"missing\" does not exist.";
+ }
+ );
+ }
+
+ var grammars = [
+ 'start = missing',
+ 'start = missing / "a" / "b"',
+ 'start = "a" / "b" / missing',
+ 'start = missing "a" "b"',
+ 'start = "a" "b" missing',
+ 'start = label:missing',
+ 'start = &missing',
+ 'start = !missing',
+ 'start = missing?',
+ 'start = missing*',
+ 'start = missing+',
+ 'start = missing { }'
+ ];
+
+ for (var i = 0; i < grammars.length; i++) { testGrammar(grammars[i]); }
+});
+
+test("reports left recursion", function() {
+ function testGrammar(grammar) {
+ raises(
+ function() {
+ var ast = PEG.parser.parse(grammar);
+ PEG.compiler.passes.reportLeftRecursion(ast);
+ },
+ function(e) {
+ return e instanceof PEG.GrammarError
+ && e.message === "Left recursion detected for rule \"start\".";
+ }
+ );
+ }
+
+ var grammars = [
+ /* Direct */
+ 'start = start',
+ 'start = start / "a" / "b"',
+ 'start = "a" / "b" / start',
+ 'start = start "a" "b"',
+ 'start = label:start',
+ 'start = &start',
+ 'start = !start',
+ 'start = start?',
+ 'start = start*',
+ 'start = start+',
+ 'start = start { }',
+
+ /* Indirect */
+ 'start = stop; stop = start'
+ ];
+
+ for (var i = 0; i < grammars.length; i++) { testGrammar(grammars[i]); }
+});
+
test("removes proxy rules", function() {
function simpleGrammar(rules, startRule) {
return {
diff --git a/test/run b/test/run
index 53f788c..551aa3c 100755
--- a/test/run
+++ b/test/run
@@ -77,7 +77,6 @@ QUnit.done = function(details) {
[
"helpers.js",
"parser-test.js",
- "checks-test.js",
"passes-test.js",
"compiler-test.js",
].forEach(function(file) {