Report duplicate labels as errors

Resolves #270.
redux
David Majda 8 years ago
parent 2a889535bd
commit eb67cbedb4

@ -19,17 +19,18 @@ var compiler = {
*/
passes: {
check: {
reportMissingRules: require("./passes/report-missing-rules"),
reportDuplicateRules: require("./passes/report-duplicate-rules"),
reportLeftRecursion: require("./passes/report-left-recursion"),
reportInfiniteLoops: require("./passes/report-infinite-loops")
reportMissingRules: require("./passes/report-missing-rules"),
reportDuplicateRules: require("./passes/report-duplicate-rules"),
reportDuplicateLabels: require("./passes/report-duplicate-labels"),
reportLeftRecursion: require("./passes/report-left-recursion"),
reportInfiniteLoops: require("./passes/report-infinite-loops")
},
transform: {
removeProxyRules: require("./passes/remove-proxy-rules")
removeProxyRules: require("./passes/remove-proxy-rules")
},
generate: {
generateBytecode: require("./passes/generate-bytecode"),
generateJS: require("./passes/generate-js")
generateBytecode: require("./passes/generate-bytecode"),
generateJS: require("./passes/generate-js")
}
},

@ -0,0 +1,54 @@
"use strict";
var GrammarError = require("../../grammar-error"),
arrays = require("../../utils/arrays"),
objects = require("../../utils/objects"),
visitor = require("../visitor");
/* Checks that each label is defined only once within each scope. */
function reportDuplicateLabels(ast) {
function checkExpressionWithClonedEnv(node, env) {
check(node.expression, objects.clone(env));
}
var check = visitor.build({
rule: function(node) {
check(node.expression, { });
},
choice: function(node, env) {
arrays.each(node.alternatives, function(alternative) {
check(alternative, objects.clone(env));
});
},
action: checkExpressionWithClonedEnv,
labeled: function(node, env) {
if (env.hasOwnProperty(node.label)) {
throw new GrammarError(
"Label \"" + node.label + "\" is already defined "
+ "at line " + env[node.label].start.line + ", "
+ "column " + env[node.label].start.column + ".",
node.location
);
}
check(node.expression, env);
env[node.label] = node.location;
},
text: checkExpressionWithClonedEnv,
simple_and: checkExpressionWithClonedEnv,
simple_not: checkExpressionWithClonedEnv,
optional: checkExpressionWithClonedEnv,
zero_or_more: checkExpressionWithClonedEnv,
one_or_more: checkExpressionWithClonedEnv,
group: checkExpressionWithClonedEnv
});
check(ast);
}
module.exports = reportDuplicateLabels;

@ -28,6 +28,7 @@
"lib/compiler/passes/generate-js.js",
"lib/compiler/passes/remove-proxy-rules.js",
"lib/compiler/passes/report-duplicate-rules.js",
"lib/compiler/passes/report-duplicate-labels.js",
"lib/compiler/passes/report-infinite-loops.js",
"lib/compiler/passes/report-left-recursion.js",
"lib/compiler/passes/report-missing-rules.js",

@ -11,6 +11,7 @@
<script src="unit/compiler/passes/helpers.js"></script>
<script src="unit/compiler/passes/report-missing-rules.spec.js"></script>
<script src="unit/compiler/passes/report-duplicate-rules.spec.js"></script>
<script src="unit/compiler/passes/report-duplicate-labels.spec.js"></script>
<script src="unit/compiler/passes/report-left-recursion.spec.js"></script>
<script src="unit/compiler/passes/report-infinite-loops.spec.js"></script>
<script src="unit/compiler/passes/remove-proxy-rules.spec.js"></script>

@ -0,0 +1,59 @@
/* global peg */
"use strict";
describe("compiler pass |reportDuplicateLabels|", function() {
var pass = peg.compiler.passes.check.reportDuplicateLabels;
describe("in a sequence", function() {
it("reports labels duplicate with labels of preceding elements", function() {
expect(pass).toReportError('start = a:"a" a:"a"', {
message: 'Label "a" is already defined at line 1, column 9.',
location: {
start: { offset: 14, line: 1, column: 15 },
end: { offset: 19, line: 1, column: 20 }
}
});
});
it("doesn't report labels duplicate with labels in subexpressions", function() {
expect(pass).not.toReportError('start = ("a" / a:"a" / "a") a:"a"');
expect(pass).not.toReportError('start = (a:"a" { }) a:"a"');
expect(pass).not.toReportError('start = ("a" a:"a" "a") a:"a"');
expect(pass).not.toReportError('start = b:(a:"a") a:"a"');
expect(pass).not.toReportError('start = $(a:"a") a:"a"');
expect(pass).not.toReportError('start = &(a:"a") a:"a"');
expect(pass).not.toReportError('start = !(a:"a") a:"a"');
expect(pass).not.toReportError('start = (a:"a")? a:"a"');
expect(pass).not.toReportError('start = (a:"a")* a:"a"');
expect(pass).not.toReportError('start = (a:"a")+ a:"a"');
expect(pass).not.toReportError('start = (a:"a") a:"a"');
});
});
describe("in a choice", function() {
it("doesn't report labels duplicate with labels of preceding alternatives", function() {
expect(pass).not.toReportError('start = a:"a" / a:"a"');
});
});
describe("in outer sequence", function() {
it("reports labels duplicate with labels of preceding elements", function() {
expect(pass).toReportError('start = a:"a" (a:"a")', {
message: 'Label "a" is already defined at line 1, column 9.',
location: {
start: { offset: 15, line: 1, column: 16 },
end: { offset: 20, line: 1, column: 21 }
}
});
});
it("doesn't report labels duplicate with the label of the current element", function() {
expect(pass).not.toReportError('start = a:(a:"a")');
});
it("doesn't report labels duplicate with labels of following elements", function() {
expect(pass).not.toReportError('start = (a:"a") a:"a"');
});
});
});
Loading…
Cancel
Save