Skip to content

Commit

Permalink
Fix #379. Move reportInfiniteRecursion to prepare phase, ensure that …
Browse files Browse the repository at this point in the history
…it doesn't keep going when it finds an error.
  • Loading branch information
hildjj committed Feb 8, 2024
1 parent 433f5bd commit 2a6623f
Show file tree
Hide file tree
Showing 7 changed files with 52 additions and 7 deletions.
2 changes: 1 addition & 1 deletion docs/js/benchmark-bundle.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/js/test-bundle.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/vendor/peggy/peggy.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion lib/compiler/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,12 @@ const compiler = {
passes: {
prepare: [
addImportedRules,
reportInfiniteRecursion,
],
check: [
reportUndefinedRules,
reportDuplicateRules,
reportDuplicateLabels,
reportInfiniteRecursion,
reportInfiniteRepetition,
reportIncorrectPlucking,
reportDuplicateImports,
Expand Down
17 changes: 16 additions & 1 deletion lib/compiler/passes/report-infinite-recursion.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,31 @@ function reportInfiniteRecursion(ast, options, session) {

const check = visitor.build({
rule(node) {
if (session.errors > 0) {
return;
}
visitedRules.push(node.name);
check(node.expression);
visitedRules.pop();
},

sequence(node) {
if (session.errors > 0) {
return;
}
node.elements.every(element => {
check(element);

if (session.errors > 0) {
return false;
}
return !asts.alwaysConsumesOnSuccess(ast, element);
});
},

repeated(node) {
if (session.errors > 0) {
return;
}
check(node.expression);

// If an expression does not consume input then recursion
Expand All @@ -47,6 +58,10 @@ function reportInfiniteRecursion(ast, options, session) {
},

rule_ref(node) {
if (session.errors > 0) {
return;
}

backtraceRefs.push(node);

const rule = asts.findRule(ast, node.name);
Expand Down
22 changes: 20 additions & 2 deletions test/behavior/generated-parser-behavior.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3032,13 +3032,32 @@ Error: Expected "import", "{", code block, comment, end of line, identifier, or
}
});

it("detects infinite loops before other errors", () => {
try {
peg.generate(`
start = start
dup = label:"foo" label:"bar"
`);
} catch (e) {
expect(e).with.property("stage", "prepare");
expect(e).with.property("problems").to.be.an("array");
// Get second elements of errors (error messages)
const messages = e.problems.filter(p => p[0] === "error").map(p => p[1]);

// Check that only infloop errors are present
expect(messages).to.deep.equal([
"Possible infinite loop when parsing (left recursion: start -> start)",
]);
}
});

it("reports multiple errors in each compilation stage", () => {
try {
peg.generate(`
start = leftRecursion
leftRecursion = duplicatedLabel:duplicatedRule duplicatedLabel:missingRule
duplicatedRule = missingRule
duplicatedRule = start
duplicatedRule = "nothing"
`);
} catch (e) {
expect(e).with.property("stage", "check");
Expand All @@ -3058,7 +3077,6 @@ Error: Expected "import", "{", code block, comment, end of line, identifier, or
"Rule \"missingRule\" is not defined",
"Rule \"duplicatedRule\" is already defined",
"Label \"duplicatedLabel\" is already defined",
"Possible infinite loop when parsing (left recursion: duplicatedRule -> start -> leftRecursion -> duplicatedRule)",
]);
}
});
Expand Down
12 changes: 12 additions & 0 deletions test/unit/compiler/passes/report-infinite-recursion.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -157,5 +157,17 @@ describe("compiler pass |reportInfiniteRecursion|", () => {
expect(pass).to.reportError("start = ''|2..3, start|");
expect(pass).to.reportError("start = ''| 42 , start|");
});

it("does not inifinite loop", () => {
// From https://github.com/peggyjs/peggy/issues/379
expect(pass).to.reportError(`
start = expr*
expr
= expr "++"
`, {
message: "Possible infinite loop when parsing (left recursion: start -> expr -> expr)",
});
});
});
});

0 comments on commit 2a6623f

Please sign in to comment.