From a56d3ac94fbf19cabef4b7d3c279a0c45cc6962f Mon Sep 17 00:00:00 2001 From: David Majda Date: Sun, 8 Dec 2013 14:48:30 +0100 Subject: [PATCH] Fix error messages in certain cases with trailing input In case the generated parser parsed successfully part of input and left some input unparsed (trailing input), the error message produced was sometimes wrong. The code worked correctly only if there were no match failures in the successfully parsed part (highly unlikely). This commit fixes things by explicitly triggering a match failure with the following expectation at the end of the successfully parsed part of the input: peg$fail({ type: "end", description: "end of input" }); This change also made it possible to simplify the |buildMessage| function, which can now ignore the case of no expectations. Fixes #119. --- lib/compiler/passes/generate-javascript.js | 38 ++++++++-------------- lib/parser.js | 38 ++++++++-------------- spec/generated-parser.spec.js | 4 ++- 3 files changed, 31 insertions(+), 49 deletions(-) diff --git a/lib/compiler/passes/generate-javascript.js b/lib/compiler/passes/generate-javascript.js index 6de510c..405c852 100644 --- a/lib/compiler/passes/generate-javascript.js +++ b/lib/compiler/passes/generate-javascript.js @@ -902,28 +902,18 @@ module.exports = function(ast, options) { ' .replace(/[\\u1080-\\uFFFF]/g, function(ch) { return \'\\\\u\' + hex(ch); });', ' }', '', - ' var expectedDescs, expectedDesc, foundDesc, i;', + ' var expectedDescs = new Array(expected.length),', + ' expectedDesc, foundDesc, i;', '', - ' switch (expected.length) {', - ' case 0:', - ' expectedDesc = "end of input";', - ' break;', - '', - ' case 1:', - ' expectedDesc = expected[0].description;', - ' break;', - '', - ' default:', - ' expectedDescs = new Array(expected.length);', - '', - ' for (i = 0; i < expected.length; i++) {', - ' expectedDescs[i] = expected[i].description;', - ' }', + ' for (i = 0; i < expected.length; i++) {', + ' expectedDescs[i] = expected[i].description;', + ' }', '', - ' expectedDesc = expectedDescs.slice(0, -1).join(", ")', + ' expectedDesc = expected.length > 1', + ' ? expectedDescs.slice(0, -1).join(", ")', ' + " or "', - ' + expectedDescs[expected.length - 1];', - ' }', + ' + expectedDescs[expected.length - 1]', + ' : expectedDescs[0];', '', ' foundDesc = found ? "\\"" + stringEscape(found) + "\\"" : "end of input";', '', @@ -975,11 +965,11 @@ module.exports = function(ast, options) { ' if (peg$result !== peg$FAILED && peg$currPos === input.length) {', ' return peg$result;', ' } else {', - ' throw peg$buildException(', - ' null,', - ' peg$maxFailExpected,', - ' Math.max(peg$currPos, peg$maxFailPos)', - ' );', + ' if (peg$result !== peg$FAILED && peg$currPos < input.length) {', + ' peg$fail({ type: "end", description: "end of input" });', + ' }', + '', + ' throw peg$buildException(null, peg$maxFailExpected, peg$maxFailPos);', ' }', ' }', '', diff --git a/lib/parser.js b/lib/parser.js index 154af65..43c5b79 100644 --- a/lib/parser.js +++ b/lib/parser.js @@ -465,28 +465,18 @@ module.exports = (function() { .replace(/[\u1080-\uFFFF]/g, function(ch) { return '\\u' + hex(ch); }); } - var expectedDescs, expectedDesc, foundDesc, i; + var expectedDescs = new Array(expected.length), + expectedDesc, foundDesc, i; - switch (expected.length) { - case 0: - expectedDesc = "end of input"; - break; - - case 1: - expectedDesc = expected[0].description; - break; - - default: - expectedDescs = new Array(expected.length); - - for (i = 0; i < expected.length; i++) { - expectedDescs[i] = expected[i].description; - } + for (i = 0; i < expected.length; i++) { + expectedDescs[i] = expected[i].description; + } - expectedDesc = expectedDescs.slice(0, -1).join(", ") + expectedDesc = expected.length > 1 + ? expectedDescs.slice(0, -1).join(", ") + " or " - + expectedDescs[expected.length - 1]; - } + + expectedDescs[expected.length - 1] + : expectedDescs[0]; foundDesc = found ? "\"" + stringEscape(found) + "\"" : "end of input"; @@ -2792,11 +2782,11 @@ module.exports = (function() { if (peg$result !== peg$FAILED && peg$currPos === input.length) { return peg$result; } else { - throw peg$buildException( - null, - peg$maxFailExpected, - Math.max(peg$currPos, peg$maxFailPos) - ); + if (peg$result !== peg$FAILED && peg$currPos < input.length) { + peg$fail({ type: "end", description: "end of input" }); + } + + throw peg$buildException(null, peg$maxFailExpected, peg$maxFailPos); } } diff --git a/spec/generated-parser.spec.js b/spec/generated-parser.spec.js index 98818f9..61a3a4c 100644 --- a/spec/generated-parser.spec.js +++ b/spec/generated-parser.spec.js @@ -843,7 +843,9 @@ describe("generated parser", function() { it("reports expectations correctly with no alternative", function() { var parser = PEG.buildParser('start = ', options); - expect(parser).toFailToParse("a", { expected: [] }); + expect(parser).toFailToParse("a", { + expected: [{ type: "end", description: "end of input" }] + }); }); it("reports expectations correctly with one alternative", function() {