Error handling: Implement the |error| function
The |error| function allows users to report custom match failures inside actions. If the |error| function is called, and the reported match failure turns out to be the cause of a parse error, the error message reported by the parser will be exactly the one specified in the |error| call. Implements part of #198. Speed impact ------------ Before: 999.83 kB/s After: 1000.84 kB/s Difference: 0.10% Size impact ----------- Before: 1017212 b After: 1019968 b Difference: 0.27% (Measured by /tools/impact with Node.js v0.6.18 on x86_64 GNU/Linux.)
This commit is contained in:
parent
dd74ea4144
commit
5460a881af
|
@ -381,6 +381,10 @@ To indicate a match failure, the code inside the action can invoke the
|
|||
at the current position. This description will be used as part of a message of
|
||||
the exception thrown if the match failure leads to an parse error.
|
||||
|
||||
The code inside an action can also invoke the `error` function. It takes one
|
||||
parameter — an error message. This message will be used by the exception thrown
|
||||
if the match failure leads to an parse error.
|
||||
|
||||
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
|
||||
must be balanced.
|
||||
|
|
|
@ -745,6 +745,7 @@ module.exports = function(ast, options) {
|
|||
' peg$cachedPosDetails = { line: 1, column: 1, seenCR: false },',
|
||||
' peg$maxFailPos = 0,',
|
||||
' peg$maxFailExpected = [],',
|
||||
' peg$maxFailMessage = null,',
|
||||
' peg$silentFails = 0,', // 0 = report failures, > 0 = silence failures
|
||||
' peg$userFail = false,',
|
||||
''
|
||||
|
@ -809,6 +810,13 @@ module.exports = function(ast, options) {
|
|||
' peg$userFail = true;',
|
||||
' }',
|
||||
'',
|
||||
' function error(message) {',
|
||||
' if (peg$silentFails === 0) {',
|
||||
' peg$error(message, peg$reportedPos);',
|
||||
' }',
|
||||
' peg$userFail = true;',
|
||||
' }',
|
||||
'',
|
||||
' function peg$computePosDetails(pos) {',
|
||||
' function advance(details, startPos, endPos) {',
|
||||
' var p, ch;',
|
||||
|
@ -848,11 +856,24 @@ module.exports = function(ast, options) {
|
|||
' if (pos > peg$maxFailPos) {',
|
||||
' peg$maxFailPos = pos;',
|
||||
' peg$maxFailExpected = [];',
|
||||
' peg$maxFailMessage = null;',
|
||||
' }',
|
||||
'',
|
||||
' peg$maxFailExpected.push(expected);',
|
||||
' }',
|
||||
'',
|
||||
' function peg$error(message, pos) {',
|
||||
' if (pos < peg$maxFailPos) { return; }',
|
||||
'',
|
||||
' if (pos > peg$maxFailPos) {',
|
||||
' peg$maxFailPos = pos;',
|
||||
' peg$maxFailExpected = [];',
|
||||
' peg$maxFailMessage = null;',
|
||||
' }',
|
||||
'',
|
||||
' peg$maxFailMessage = message;',
|
||||
' }',
|
||||
'',
|
||||
' function peg$buildException() {',
|
||||
' function cleanupExpected(expected) {',
|
||||
' var i = 1;',
|
||||
|
@ -934,13 +955,19 @@ module.exports = function(ast, options) {
|
|||
'',
|
||||
' var pos = Math.max(peg$currPos, peg$maxFailPos),',
|
||||
' posDetails = peg$computePosDetails(pos),',
|
||||
' found = pos < input.length ? input.charAt(pos) : null;',
|
||||
' expected = peg$maxFailMessage === null ? peg$maxFailExpected : null,',
|
||||
' found = pos < input.length ? input.charAt(pos) : null,',
|
||||
' message = peg$maxFailMessage !== null',
|
||||
' ? peg$maxFailMessage',
|
||||
' : buildMessage(expected, found);',
|
||||
'',
|
||||
' cleanupExpected(peg$maxFailExpected);',
|
||||
' if (expected !== null) {',
|
||||
' cleanupExpected(expected);',
|
||||
' }',
|
||||
'',
|
||||
' return new SyntaxError(',
|
||||
' buildMessage(peg$maxFailExpected, found),',
|
||||
' peg$maxFailExpected,',
|
||||
' message,',
|
||||
' expected,',
|
||||
' found,',
|
||||
' pos,',
|
||||
' posDetails.line,',
|
||||
|
|
|
@ -340,6 +340,7 @@ module.exports = (function() {
|
|||
peg$cachedPosDetails = { line: 1, column: 1, seenCR: false },
|
||||
peg$maxFailPos = 0,
|
||||
peg$maxFailExpected = [],
|
||||
peg$maxFailMessage = null,
|
||||
peg$silentFails = 0,
|
||||
peg$userFail = false,
|
||||
|
||||
|
@ -379,6 +380,13 @@ module.exports = (function() {
|
|||
peg$userFail = true;
|
||||
}
|
||||
|
||||
function error(message) {
|
||||
if (peg$silentFails === 0) {
|
||||
peg$error(message, peg$reportedPos);
|
||||
}
|
||||
peg$userFail = true;
|
||||
}
|
||||
|
||||
function peg$computePosDetails(pos) {
|
||||
function advance(details, startPos, endPos) {
|
||||
var p, ch;
|
||||
|
@ -418,11 +426,24 @@ module.exports = (function() {
|
|||
if (pos > peg$maxFailPos) {
|
||||
peg$maxFailPos = pos;
|
||||
peg$maxFailExpected = [];
|
||||
peg$maxFailMessage = null;
|
||||
}
|
||||
|
||||
peg$maxFailExpected.push(expected);
|
||||
}
|
||||
|
||||
function peg$error(message, pos) {
|
||||
if (pos < peg$maxFailPos) { return; }
|
||||
|
||||
if (pos > peg$maxFailPos) {
|
||||
peg$maxFailPos = pos;
|
||||
peg$maxFailExpected = [];
|
||||
peg$maxFailMessage = null;
|
||||
}
|
||||
|
||||
peg$maxFailMessage = message;
|
||||
}
|
||||
|
||||
function peg$buildException() {
|
||||
function cleanupExpected(expected) {
|
||||
var i = 1;
|
||||
|
@ -494,13 +515,19 @@ module.exports = (function() {
|
|||
|
||||
var pos = Math.max(peg$currPos, peg$maxFailPos),
|
||||
posDetails = peg$computePosDetails(pos),
|
||||
found = pos < input.length ? input.charAt(pos) : null;
|
||||
expected = peg$maxFailMessage === null ? peg$maxFailExpected : null,
|
||||
found = pos < input.length ? input.charAt(pos) : null,
|
||||
message = peg$maxFailMessage !== null
|
||||
? peg$maxFailMessage
|
||||
: buildMessage(expected, found);
|
||||
|
||||
cleanupExpected(peg$maxFailExpected);
|
||||
if (expected !== null) {
|
||||
cleanupExpected(expected);
|
||||
}
|
||||
|
||||
return new SyntaxError(
|
||||
buildMessage(peg$maxFailExpected, found),
|
||||
peg$maxFailExpected,
|
||||
message,
|
||||
expected,
|
||||
found,
|
||||
pos,
|
||||
posDetails.line,
|
||||
|
|
|
@ -397,6 +397,60 @@ describe("generated parser", function() {
|
|||
});
|
||||
});
|
||||
|
||||
describe("|error| function", function() {
|
||||
it("generates a custom match failure", function() {
|
||||
var parser = PEG.buildParser(
|
||||
'start = "a" { error("a"); }',
|
||||
options
|
||||
);
|
||||
|
||||
expect(parser).toFailToParse("a", {
|
||||
offset: 0,
|
||||
line: 1,
|
||||
column: 1,
|
||||
expected: null,
|
||||
found: "a",
|
||||
message: "a"
|
||||
});
|
||||
});
|
||||
|
||||
it("generated failures overrides failures generated before", function() {
|
||||
var parser = PEG.buildParser(
|
||||
'start = "a" / ("b" { error("b"); })',
|
||||
options
|
||||
);
|
||||
|
||||
expect(parser).toFailToParse("b", {
|
||||
message: "b",
|
||||
expected: null
|
||||
});
|
||||
});
|
||||
|
||||
it("generated failures override failures generated after", function() {
|
||||
var parser = PEG.buildParser(
|
||||
'start = ("a" { error("a"); }) / "b"',
|
||||
options
|
||||
);
|
||||
|
||||
expect(parser).toFailToParse("a", {
|
||||
message: "a",
|
||||
expected: null
|
||||
});
|
||||
});
|
||||
|
||||
it("the last invocation wins", function() {
|
||||
var parser = PEG.buildParser(
|
||||
'start = "a" { error("a1"); error("a2"); }',
|
||||
options
|
||||
);
|
||||
|
||||
expect(parser).toFailToParse("a", {
|
||||
message: "a2",
|
||||
expected: null
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("can use functions defined in the initializer", function() {
|
||||
var parser = PEG.buildParser([
|
||||
'{ function f() { return 42; } }',
|
||||
|
|
Loading…
Reference in a new issue