diff --git a/lib/runtime.js b/lib/runtime.js index 770117a..e910088 100644 --- a/lib/runtime.js +++ b/lib/runtime.js @@ -87,7 +87,7 @@ PEG.Parser.prototype = { function buildExpectedFromMatchFailures(failures) { switch (failures.length) { case 0: - return "nothing"; + return "end of input"; case 1: return failures[0].toString(); default: @@ -100,19 +100,13 @@ PEG.Parser.prototype = { } } - if (that._pos === 0) { - var expected = buildExpectedFromMatchFailures( - that._rightmostMatchFailures - ); - var actual = that._rightmostMatchFailuresPos < that._input.length - ? PEG.StringUtils.quote( - that._input.charAt(that._rightmostMatchFailuresPos) - ) - : "end of input"; - } else { - var expected = "end of input"; - var actual = PEG.StringUtils.quote(that._input.charAt(that._pos)); - } + var expected = buildExpectedFromMatchFailures( + that._rightmostMatchFailures + ); + var pos = Math.max(that._pos, that._rightmostMatchFailuresPos); + var actual = pos < that._input.length + ? PEG.StringUtils.quote(that._input.charAt(pos)) + : "end of input"; return "Expected " + expected + " but " + actual + " found."; } @@ -157,6 +151,33 @@ PEG.Parser.prototype = { }; var result = this["_parse_" + this._startRule](initialContext); + + /* + * The parser is now in one of the following three states: + * + * 1. The parser successfully parsed the whole input. + * + * - |result !== null| + * - |that._pos === input.length| + * - |that._rightmostMatchFailures.length| may or may not contain + * something + * + * 2. The parser successfully parsed only a part of the input. + * + * - |result !== null| + * - |that._pos < input.length| + * - |that._rightmostMatchFailures.length| may or may not contain + * something + * + * 3. The parser did not successfully parse any part of the input. + * + * - |result === null| + * - |that._pos === 0| + * - |that._rightmostMatchFailures.length| contains at least one failure + * + * All code following this comment (including called functions) must + * handle these states. + */ if (result === null || this._pos !== input.length) { var errorPosition = computeErrorPosition(); throw new PEG.Parser.SyntaxError( diff --git a/test/compiler-test.js b/test/compiler-test.js index 1c24ae1..32213cd 100644 --- a/test/compiler-test.js +++ b/test/compiler-test.js @@ -365,18 +365,25 @@ test("error messages", function() { 'Expected digits but end of input found.' ); - var choiceParser = PEG.buildParser('start: "a" / "b" / "c"'); + var choiceParser1 = PEG.buildParser('start: "a" / "b" / "c"'); doesNotParseWithMessage( - choiceParser, + choiceParser1, "def", 'Expected "a", "b" or "c" but "d" found.' ); + var choiceParser2 = PEG.buildParser('start: "a" "b" "c" / "a"'); + doesNotParseWithMessage( + choiceParser2, + "abd", + 'Expected "c" but "d" found.' + ); + var emptyParser = PEG.buildParser('start: '); doesNotParseWithMessage( emptyParser, "something", - 'Expected nothing but "s" found.' + 'Expected end of input but "s" found.' ); });