From dd74ea4144b75631ec99c7c0716dbb0c838f71e2 Mon Sep 17 00:00:00 2001 From: David Majda Date: Sat, 30 Nov 2013 17:11:15 +0100 Subject: [PATCH] Error handling: Build error message out of |SyntaxError|'s constructor It will be possible to create errors with user-supplied messages soon. The |SyntaxError| class needs to be ready for that. Implements part of #198. --- lib/compiler/passes/generate-javascript.js | 117 +++++++++++---------- lib/parser.js | 97 ++++++++--------- 2 files changed, 108 insertions(+), 106 deletions(-) diff --git a/lib/compiler/passes/generate-javascript.js b/lib/compiler/passes/generate-javascript.js index 1e177d3..2bdd8bd 100644 --- a/lib/compiler/passes/generate-javascript.js +++ b/lib/compiler/passes/generate-javascript.js @@ -685,63 +685,8 @@ module.exports = function(ast, options) { ' child.prototype = new ctor();', ' }', '', - ' function SyntaxError(expected, found, offset, line, column) {', - ' function buildMessage(expected, found) {', - ' function stringEscape(s) {', - ' function hex(ch) { return ch.charCodeAt(0).toString(16).toUpperCase(); }', - '', - /* - * ECMA-262, 5th ed., 7.8.4: All characters may appear literally in a string - * literal except for the closing quote character, backslash, carriage - * return, line separator, paragraph separator, and line feed. Any character - * may appear in the form of an escape sequence. - * - * For portability, we also escape all control and non-ASCII characters. - * Note that "\0" and "\v" escape sequences are not used because JSHint does - * not like the first and IE the second. - */ - ' return s', - ' .replace(/\\\\/g, \'\\\\\\\\\')', // backslash - ' .replace(/"/g, \'\\\\"\')', // closing double quote - ' .replace(/\\x08/g, \'\\\\b\')', // backspace - ' .replace(/\\t/g, \'\\\\t\')', // horizontal tab - ' .replace(/\\n/g, \'\\\\n\')', // line feed - ' .replace(/\\f/g, \'\\\\f\')', // form feed - ' .replace(/\\r/g, \'\\\\r\')', // carriage return - ' .replace(/[\\x00-\\x07\\x0B\\x0E\\x0F]/g, function(ch) { return \'\\\\x0\' + hex(ch); })', - ' .replace(/[\\x10-\\x1F\\x80-\\xFF]/g, function(ch) { return \'\\\\x\' + hex(ch); })', - ' .replace(/[\\u0180-\\u0FFF]/g, function(ch) { return \'\\\\u0\' + hex(ch); })', - ' .replace(/[\\u1080-\\uFFFF]/g, function(ch) { return \'\\\\u\' + hex(ch); });', - ' }', - '', - ' var expectedDescs, 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;', - ' }', - '', - ' expectedDesc = expectedDescs.slice(0, -1).join(", ")', - ' + " or "', - ' + expectedDescs[expected.length - 1];', - ' }', - '', - ' foundDesc = found ? "\\"" + stringEscape(found) + "\\"" : "end of input";', - '', - ' return "Expected " + expectedDesc + " but " + foundDesc + " found.";', - ' }', - '', + ' function SyntaxError(message, expected, found, offset, line, column) {', + ' this.message = message;', ' this.expected = expected;', ' this.found = found;', ' this.offset = offset;', @@ -749,7 +694,6 @@ module.exports = function(ast, options) { ' this.column = column;', '', ' this.name = "SyntaxError";', - ' this.message = buildMessage(expected, found);', ' }', '', ' peg$subclass(SyntaxError, Error);', @@ -932,6 +876,62 @@ module.exports = function(ast, options) { ' }', ' }', '', + ' function buildMessage(expected, found) {', + ' function stringEscape(s) {', + ' function hex(ch) { return ch.charCodeAt(0).toString(16).toUpperCase(); }', + '', + /* + * ECMA-262, 5th ed., 7.8.4: All characters may appear literally in a string + * literal except for the closing quote character, backslash, carriage + * return, line separator, paragraph separator, and line feed. Any character + * may appear in the form of an escape sequence. + * + * For portability, we also escape all control and non-ASCII characters. + * Note that "\0" and "\v" escape sequences are not used because JSHint does + * not like the first and IE the second. + */ + ' return s', + ' .replace(/\\\\/g, \'\\\\\\\\\')', // backslash + ' .replace(/"/g, \'\\\\"\')', // closing double quote + ' .replace(/\\x08/g, \'\\\\b\')', // backspace + ' .replace(/\\t/g, \'\\\\t\')', // horizontal tab + ' .replace(/\\n/g, \'\\\\n\')', // line feed + ' .replace(/\\f/g, \'\\\\f\')', // form feed + ' .replace(/\\r/g, \'\\\\r\')', // carriage return + ' .replace(/[\\x00-\\x07\\x0B\\x0E\\x0F]/g, function(ch) { return \'\\\\x0\' + hex(ch); })', + ' .replace(/[\\x10-\\x1F\\x80-\\xFF]/g, function(ch) { return \'\\\\x\' + hex(ch); })', + ' .replace(/[\\u0180-\\u0FFF]/g, function(ch) { return \'\\\\u0\' + hex(ch); })', + ' .replace(/[\\u1080-\\uFFFF]/g, function(ch) { return \'\\\\u\' + hex(ch); });', + ' }', + '', + ' var expectedDescs, 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;', + ' }', + '', + ' expectedDesc = expectedDescs.slice(0, -1).join(", ")', + ' + " or "', + ' + expectedDescs[expected.length - 1];', + ' }', + '', + ' foundDesc = found ? "\\"" + stringEscape(found) + "\\"" : "end of input";', + '', + ' return "Expected " + expectedDesc + " but " + foundDesc + " found.";', + ' }', + '', ' var pos = Math.max(peg$currPos, peg$maxFailPos),', ' posDetails = peg$computePosDetails(pos),', ' found = pos < input.length ? input.charAt(pos) : null;', @@ -939,6 +939,7 @@ module.exports = function(ast, options) { ' cleanupExpected(peg$maxFailExpected);', '', ' return new SyntaxError(', + ' buildMessage(peg$maxFailExpected, found),', ' peg$maxFailExpected,', ' found,', ' pos,', diff --git a/lib/parser.js b/lib/parser.js index ce98ba0..330631c 100644 --- a/lib/parser.js +++ b/lib/parser.js @@ -11,53 +11,8 @@ module.exports = (function() { child.prototype = new ctor(); } - function SyntaxError(expected, found, offset, line, column) { - function buildMessage(expected, found) { - function stringEscape(s) { - function hex(ch) { return ch.charCodeAt(0).toString(16).toUpperCase(); } - - return s - .replace(/\\/g, '\\\\') - .replace(/"/g, '\\"') - .replace(/\x08/g, '\\b') - .replace(/\t/g, '\\t') - .replace(/\n/g, '\\n') - .replace(/\f/g, '\\f') - .replace(/\r/g, '\\r') - .replace(/[\x00-\x07\x0B\x0E\x0F]/g, function(ch) { return '\\x0' + hex(ch); }) - .replace(/[\x10-\x1F\x80-\xFF]/g, function(ch) { return '\\x' + hex(ch); }) - .replace(/[\u0180-\u0FFF]/g, function(ch) { return '\\u0' + hex(ch); }) - .replace(/[\u1080-\uFFFF]/g, function(ch) { return '\\u' + hex(ch); }); - } - - var expectedDescs, 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; - } - - expectedDesc = expectedDescs.slice(0, -1).join(", ") - + " or " - + expectedDescs[expected.length - 1]; - } - - foundDesc = found ? "\"" + stringEscape(found) + "\"" : "end of input"; - - return "Expected " + expectedDesc + " but " + foundDesc + " found."; - } - + function SyntaxError(message, expected, found, offset, line, column) { + this.message = message; this.expected = expected; this.found = found; this.offset = offset; @@ -65,7 +20,6 @@ module.exports = (function() { this.column = column; this.name = "SyntaxError"; - this.message = buildMessage(expected, found); } peg$subclass(SyntaxError, Error); @@ -492,6 +446,52 @@ module.exports = (function() { } } + function buildMessage(expected, found) { + function stringEscape(s) { + function hex(ch) { return ch.charCodeAt(0).toString(16).toUpperCase(); } + + return s + .replace(/\\/g, '\\\\') + .replace(/"/g, '\\"') + .replace(/\x08/g, '\\b') + .replace(/\t/g, '\\t') + .replace(/\n/g, '\\n') + .replace(/\f/g, '\\f') + .replace(/\r/g, '\\r') + .replace(/[\x00-\x07\x0B\x0E\x0F]/g, function(ch) { return '\\x0' + hex(ch); }) + .replace(/[\x10-\x1F\x80-\xFF]/g, function(ch) { return '\\x' + hex(ch); }) + .replace(/[\u0180-\u0FFF]/g, function(ch) { return '\\u0' + hex(ch); }) + .replace(/[\u1080-\uFFFF]/g, function(ch) { return '\\u' + hex(ch); }); + } + + var expectedDescs, 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; + } + + expectedDesc = expectedDescs.slice(0, -1).join(", ") + + " or " + + expectedDescs[expected.length - 1]; + } + + foundDesc = found ? "\"" + stringEscape(found) + "\"" : "end of input"; + + return "Expected " + expectedDesc + " but " + foundDesc + " found."; + } + var pos = Math.max(peg$currPos, peg$maxFailPos), posDetails = peg$computePosDetails(pos), found = pos < input.length ? input.charAt(pos) : null; @@ -499,6 +499,7 @@ module.exports = (function() { cleanupExpected(peg$maxFailExpected); return new SyntaxError( + buildMessage(peg$maxFailExpected, found), peg$maxFailExpected, found, pos,