diff --git a/README.md b/README.md index adf2d95..fb6dd3d 100644 --- a/README.md +++ b/README.md @@ -403,13 +403,16 @@ using the `return` statement. This value is considered match result of the preceding expression. To indicate an error, the code inside the action can invoke the `expected` -function, which makes the parser throw an exception. The function takes one -parameter — a description of what was expected at the current position. This -description will be used as part of a message of the thrown exception. +function, which makes the parser throw an exception. The function takes two +parameters — a description of what was expected at the current position and +optional location information (the default is what `location` would return — see +below). The description will be used as part of a message of the thrown +exception. The code inside an action can also invoke the `error` function, which also makes -the parser throw an exception. The function takes one parameter — an error -message. This message will be used by the thrown exception. +the parser throw an exception. The function takes two parameters — an error +message and optional location information (the default is what `location` would +return — see below). The message will be used by the thrown exception. The code inside the action can access all variables and functions defined in the initializer at the beginning of the grammar. Curly braces in the action code diff --git a/lib/compiler/passes/generate-js.js b/lib/compiler/passes/generate-js.js index 799231e..594a7c9 100644 --- a/lib/compiler/passes/generate-js.js +++ b/lib/compiler/passes/generate-js.js @@ -967,20 +967,20 @@ function generateJS(ast, options) { ' return peg$computeLocation(peg$savedPos, peg$currPos);', ' }', '', - ' function expected(description) {', + ' function expected(description, location) {', + ' location = location !== void 0 ? location : peg$computeLocation(peg$savedPos, peg$currPos)', + '', ' throw peg$buildException(', ' null,', ' [{ type: "other", description: description }],', - ' peg$computeLocation(peg$savedPos, peg$currPos)', + ' location', ' );', ' }', '', - ' function error(message) {', - ' throw peg$buildException(', - ' message,', - ' null,', - ' peg$computeLocation(peg$savedPos, peg$currPos)', - ' );', + ' function error(message, location) {', + ' location = location !== void 0 ? location : peg$computeLocation(peg$savedPos, peg$currPos)', + '', + ' throw peg$buildException(message, null, location);', ' }', '', ' function peg$computePosDetails(pos) {', diff --git a/lib/parser.js b/lib/parser.js index dd38f53..353a80d 100644 --- a/lib/parser.js +++ b/lib/parser.js @@ -416,20 +416,20 @@ module.exports = (function() { return peg$computeLocation(peg$savedPos, peg$currPos); } - function expected(description) { + function expected(description, location) { + location = location !== void 0 ? location : peg$computeLocation(peg$savedPos, peg$currPos) + throw peg$buildException( null, [{ type: "other", description: description }], - peg$computeLocation(peg$savedPos, peg$currPos) + location ); } - function error(message) { - throw peg$buildException( - message, - null, - peg$computeLocation(peg$savedPos, peg$currPos) - ); + function error(message, location) { + location = location !== void 0 ? location : peg$computeLocation(peg$savedPos, peg$currPos) + + throw peg$buildException(message, null, location); } function peg$computePosDetails(pos) { diff --git a/spec/behavior/generated-parser-behavior.spec.js b/spec/behavior/generated-parser-behavior.spec.js index d45b1ed..e596d44 100644 --- a/spec/behavior/generated-parser-behavior.spec.js +++ b/spec/behavior/generated-parser-behavior.spec.js @@ -1211,35 +1211,79 @@ describe("generated parser behavior", function() { }); }); - it("|expected| terminates parsing and throws an exception", function() { - var parser = PEG.buildParser( - 'start = "a" { expected("a"); }', - options - ); + describe("|expected|", function() { + it("terminates parsing and throws an exception", function() { + var parser = PEG.buildParser( + 'start = "a" { expected("a"); }', + options + ); - expect(parser).toFailToParse("a", { - message: 'Expected a.', - expected: [{ type: "other", description: "a" }], - location: { - start: { offset: 0, line: 1, column: 1 }, - end: { offset: 1, line: 1, column: 2 } - } + expect(parser).toFailToParse("a", { + message: 'Expected a.', + expected: [{ type: "other", description: "a" }], + location: { + start: { offset: 0, line: 1, column: 1 }, + end: { offset: 1, line: 1, column: 2 } + } + }); + }); + + it("allows to set custom location info", function() { + var parser = PEG.buildParser([ + 'start = "a" {', + ' expected("a", {', + ' start: { offset: 1, line: 1, column: 2 },', + ' end: { offset: 2, line: 1, column: 3 }', + ' });', + '}' + ].join("\n"), options); + + expect(parser).toFailToParse("a", { + message: 'Expected a.', + expected: [{ type: "other", description: "a" }], + location: { + start: { offset: 1, line: 1, column: 2 }, + end: { offset: 2, line: 1, column: 3 } + } + }); }); }); - it("|error| terminates parsing and throws an exception", function() { - var parser = PEG.buildParser( - 'start = "a" { error("a"); }', - options - ); + describe("|error|", function() { + it("terminates parsing and throws an exception", function() { + var parser = PEG.buildParser( + 'start = "a" { error("a"); }', + options + ); - expect(parser).toFailToParse("a", { - message: "a", - expected: null, - location: { - start: { offset: 0, line: 1, column: 1 }, - end: { offset: 1, line: 1, column: 2 } - } + expect(parser).toFailToParse("a", { + message: "a", + expected: null, + location: { + start: { offset: 0, line: 1, column: 1 }, + end: { offset: 1, line: 1, column: 2 } + } + }); + }); + + it("allows to set custom location info", function() { + var parser = PEG.buildParser([ + 'start = "a" {', + ' error("a", {', + ' start: { offset: 1, line: 1, column: 2 },', + ' end: { offset: 2, line: 1, column: 3 }', + ' });', + '}' + ].join("\n"), options); + + expect(parser).toFailToParse("a", { + message: "a", + expected: null, + location: { + start: { offset: 1, line: 1, column: 2 }, + end: { offset: 2, line: 1, column: 3 } + } + }); }); }); });