Skip to content

Commit 40840fa

Browse files
committed
Implement soft-mode for generated parsers with access to partial results on syntax errors
ref peggyjs#501
1 parent 2aad880 commit 40840fa

File tree

5 files changed

+48
-9
lines changed

5 files changed

+48
-9
lines changed

Diff for: CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ Unreleased
99
### Breaking changes
1010

1111
### New features
12+
- [#501](https://github.com/peggyjs/peggy/issues/501) Implement soft-mode for generated parsers with access to partial results on syntax errors.
1213

1314
### Bug fixes
1415

Diff for: lib/compiler/passes/generate-js.js

+10-3
Original file line numberDiff line numberDiff line change
@@ -1393,9 +1393,8 @@ function generateJS(ast, options) {
13931393
" peg$maxFailPos",
13941394
" });",
13951395
" }",
1396-
" if (peg$result !== peg$FAILED && peg$currPos === input.length) {",
1397-
" return peg$result;",
1398-
" } else {",
1396+
" var success = (peg$result !== peg$FAILED && peg$currPos === input.length);",
1397+
" function fail() {",
13991398
" if (peg$result !== peg$FAILED && peg$currPos < input.length) {",
14001399
" peg$fail(peg$endExpectation());",
14011400
" }",
@@ -1408,6 +1407,14 @@ function generateJS(ast, options) {
14081407
" : peg$computeLocation(peg$maxFailPos, peg$maxFailPos)",
14091408
" );",
14101409
" }",
1410+
" if (options.soft) {",
1411+
" return {result: peg$result, success, fail};",
1412+
" }",
1413+
" if (success) {",
1414+
" return peg$result;",
1415+
" } else {",
1416+
" fail();",
1417+
" }",
14111418
"}"
14121419
);
14131420

Diff for: test/api/generated-parser-api.spec.js

+17
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,29 @@ describe("generated parser API", () => {
1414
expect(parser.parse("a")).to.equal("a");
1515
});
1616

17+
it("parses input in soft-mode", () => {
18+
const parser = peg.generate("start = 'a'");
19+
20+
const result = parser.parse("a", {soft: true});
21+
expect(result.result).to.equal("a");
22+
expect(result.success).to.equal(true);
23+
});
24+
1725
it("throws an exception on syntax error", () => {
1826
const parser = peg.generate("start = 'a'");
1927

2028
expect(() => { parser.parse("b"); }).to.throw();
2129
});
2230

31+
it("gives partial result on syntax error in soft-mode", () => {
32+
const parser = peg.generate("start = 'a'+");
33+
34+
const result = parser.parse("aab", {soft: true});
35+
expect(result.result).to.deep.equal(["a", "a"]);
36+
expect(result.success).to.equal(false);
37+
expect(result.fail).to.throw('Expected "a" or end of input but "b" found.');
38+
});
39+
2340
// Regression: https://github.com/peggyjs/peggy/pull/197
2441
it("correctly describe character class in syntax error", () => {
2542
const parser = peg.generate("start = [123-5]");

Diff for: test/cli/fixtures/imports_peggy.js

+10-3
Original file line numberDiff line numberDiff line change
@@ -501,9 +501,8 @@ function peg$parse(input, options) {
501501
peg$maxFailPos
502502
});
503503
}
504-
if (peg$result !== peg$FAILED && peg$currPos === input.length) {
505-
return peg$result;
506-
} else {
504+
var success = (peg$result !== peg$FAILED && peg$currPos === input.length);
505+
function fail() {
507506
if (peg$result !== peg$FAILED && peg$currPos < input.length) {
508507
peg$fail(peg$endExpectation());
509508
}
@@ -516,6 +515,14 @@ function peg$parse(input, options) {
516515
: peg$computeLocation(peg$maxFailPos, peg$maxFailPos)
517516
);
518517
}
518+
if (options.soft) {
519+
return {result: peg$result, success, fail};
520+
}
521+
if (success) {
522+
return peg$result;
523+
} else {
524+
fail();
525+
}
519526
}
520527

521528
module.exports = {

Diff for: test/cli/fixtures/lib.js

+10-3
Original file line numberDiff line numberDiff line change
@@ -471,9 +471,8 @@ function peg$parse(input, options) {
471471
peg$maxFailPos
472472
});
473473
}
474-
if (peg$result !== peg$FAILED && peg$currPos === input.length) {
475-
return peg$result;
476-
} else {
474+
var success = (peg$result !== peg$FAILED && peg$currPos === input.length);
475+
function fail() {
477476
if (peg$result !== peg$FAILED && peg$currPos < input.length) {
478477
peg$fail(peg$endExpectation());
479478
}
@@ -486,6 +485,14 @@ function peg$parse(input, options) {
486485
: peg$computeLocation(peg$maxFailPos, peg$maxFailPos)
487486
);
488487
}
488+
if (options.soft) {
489+
return {result: peg$result, success, fail};
490+
}
491+
if (success) {
492+
return peg$result;
493+
} else {
494+
fail();
495+
}
489496
}
490497

491498
module.exports = {

0 commit comments

Comments
 (0)