Skip to content

Commit

Permalink
Implement soft-mode for generated parsers with access to partial resu…
Browse files Browse the repository at this point in the history
…lts on syntax errors

ref peggyjs#501
  • Loading branch information
frostburn committed Mar 5, 2024
1 parent 2aad880 commit 40840fa
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 9 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Unreleased
### Breaking changes

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

### Bug fixes

Expand Down
13 changes: 10 additions & 3 deletions lib/compiler/passes/generate-js.js
Original file line number Diff line number Diff line change
Expand Up @@ -1393,9 +1393,8 @@ function generateJS(ast, options) {
" peg$maxFailPos",
" });",
" }",
" if (peg$result !== peg$FAILED && peg$currPos === input.length) {",
" return peg$result;",
" } else {",
" var success = (peg$result !== peg$FAILED && peg$currPos === input.length);",
" function fail() {",
" if (peg$result !== peg$FAILED && peg$currPos < input.length) {",
" peg$fail(peg$endExpectation());",
" }",
Expand All @@ -1408,6 +1407,14 @@ function generateJS(ast, options) {
" : peg$computeLocation(peg$maxFailPos, peg$maxFailPos)",
" );",
" }",
" if (options.soft) {",
" return {result: peg$result, success, fail};",
" }",
" if (success) {",
" return peg$result;",
" } else {",
" fail();",
" }",
"}"
);

Expand Down
17 changes: 17 additions & 0 deletions test/api/generated-parser-api.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,29 @@ describe("generated parser API", () => {
expect(parser.parse("a")).to.equal("a");
});

it("parses input in soft-mode", () => {
const parser = peg.generate("start = 'a'");

const result = parser.parse("a", {soft: true});
expect(result.result).to.equal("a");
expect(result.success).to.equal(true);
});

it("throws an exception on syntax error", () => {
const parser = peg.generate("start = 'a'");

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

it("gives partial result on syntax error in soft-mode", () => {
const parser = peg.generate("start = 'a'+");

const result = parser.parse("aab", {soft: true});
expect(result.result).to.deep.equal(["a", "a"]);
expect(result.success).to.equal(false);
expect(result.fail).to.throw('Expected "a" or end of input but "b" found.');
});

// Regression: https://github.com/peggyjs/peggy/pull/197
it("correctly describe character class in syntax error", () => {
const parser = peg.generate("start = [123-5]");
Expand Down
13 changes: 10 additions & 3 deletions test/cli/fixtures/imports_peggy.js
Original file line number Diff line number Diff line change
Expand Up @@ -501,9 +501,8 @@ function peg$parse(input, options) {
peg$maxFailPos
});
}
if (peg$result !== peg$FAILED && peg$currPos === input.length) {
return peg$result;
} else {
var success = (peg$result !== peg$FAILED && peg$currPos === input.length);
function fail() {
if (peg$result !== peg$FAILED && peg$currPos < input.length) {
peg$fail(peg$endExpectation());
}
Expand All @@ -516,6 +515,14 @@ function peg$parse(input, options) {
: peg$computeLocation(peg$maxFailPos, peg$maxFailPos)
);
}
if (options.soft) {
return {result: peg$result, success, fail};
}
if (success) {
return peg$result;
} else {
fail();
}
}

module.exports = {
Expand Down
13 changes: 10 additions & 3 deletions test/cli/fixtures/lib.js
Original file line number Diff line number Diff line change
Expand Up @@ -471,9 +471,8 @@ function peg$parse(input, options) {
peg$maxFailPos
});
}
if (peg$result !== peg$FAILED && peg$currPos === input.length) {
return peg$result;
} else {
var success = (peg$result !== peg$FAILED && peg$currPos === input.length);
function fail() {
if (peg$result !== peg$FAILED && peg$currPos < input.length) {
peg$fail(peg$endExpectation());
}
Expand All @@ -486,6 +485,14 @@ function peg$parse(input, options) {
: peg$computeLocation(peg$maxFailPos, peg$maxFailPos)
);
}
if (options.soft) {
return {result: peg$result, success, fail};
}
if (success) {
return peg$result;
} else {
fail();
}
}

module.exports = {
Expand Down

0 comments on commit 40840fa

Please sign in to comment.