From 435bb8f2dfdf21d8bfca5d1bbfeb9a08cc65a068 Mon Sep 17 00:00:00 2001 From: David Majda Date: Wed, 28 Aug 2013 19:33:21 +0200 Subject: [PATCH] Error handling: Structured expectations Before this commit, the |expected| property of an exception object thrown when a generated parser encountered an error contained expectations as strings. These strings were in a human-readable format suitable for displaying in the UI but not suitable for machine processing. For example, expected string literals included quotes and a string "any character" was used when any character was expected. This commit makes expectations structured objects. This makes the machine processing easier, while still allowing to generate a human-readable representation if needed. Implements part of #198. Speed impact ------------ Before: 1180.41 kB/s After: 1165.31 kB/s Difference: -1.28% Size impact ----------- Before: 863523 b After: 950817 b Difference: 10.10% (Measured by /tools/impact with Node.js v0.6.18 on x86_64 GNU/Linux.) --- lib/compiler/passes/generate-bytecode.js | 22 ++- lib/compiler/passes/generate-javascript.js | 29 ++- lib/parser.js | 130 +++++++------ .../compiler/passes/generate-bytecode.spec.js | 174 ++++++++++-------- spec/generated-parser.spec.js | 59 ++++-- 5 files changed, 250 insertions(+), 164 deletions(-) diff --git a/lib/compiler/passes/generate-bytecode.js b/lib/compiler/passes/generate-bytecode.js index 3a88b29..fc9b7fc 100644 --- a/lib/compiler/passes/generate-bytecode.js +++ b/lib/compiler/passes/generate-bytecode.js @@ -286,7 +286,9 @@ module.exports = function(ast, options) { }, named: function(node, context) { - var nameIndex = addConst(utils.quote(node.name)); + var nameIndex = addConst( + '{ type: "other", description: ' + utils.quote(node.name) + ' }' + ); /* * The code generated below is slightly suboptimal because |FAIL| pushes @@ -528,7 +530,13 @@ module.exports = function(ast, options) { ? utils.quote(node.value.toLowerCase()) : utils.quote(node.value) ); - expectedIndex = addConst(utils.quote(utils.quote(node.value))); + expectedIndex = addConst([ + '{', + 'type: "literal",', + 'value: ' + utils.quote(node.value) + ',', + 'description: ' + utils.quote(utils.quote(node.value)), + '}' + ].join(' ')); /* * For case-sensitive strings the value must match the beginning of the @@ -574,7 +582,13 @@ module.exports = function(ast, options) { } regexpIndex = addConst(regexp); - expectedIndex = addConst(utils.quote(node.rawText)); + expectedIndex = addConst([ + '{', + 'type: "class",', + 'value: ' + utils.quote(node.rawText) + ',', + 'description: ' + utils.quote(node.rawText), + '}' + ].join(' ')); return buildCondition( [op.MATCH_REGEXP, regexpIndex], @@ -584,7 +598,7 @@ module.exports = function(ast, options) { }, any: function(node) { - var expectedIndex = addConst(utils.quote("any character")); + var expectedIndex = addConst('{ type: "any", description: "any character" }'); return buildCondition( [op.MATCH_ANY], diff --git a/lib/compiler/passes/generate-javascript.js b/lib/compiler/passes/generate-javascript.js index 5aa667a..d463f11 100644 --- a/lib/compiler/passes/generate-javascript.js +++ b/lib/compiler/passes/generate-javascript.js @@ -703,7 +703,7 @@ module.exports = function(ast, options) { ' .replace(/[\\u1080-\\uFFFF]/g, function(ch) { return \'\\\\u\' + hex(ch); });', ' }', '', - ' var expectedDesc, foundDesc;', + ' var expectedDescs, expectedDesc, foundDesc, i;', '', ' switch (expected.length) {', ' case 0:', @@ -711,13 +711,19 @@ module.exports = function(ast, options) { ' break;', '', ' case 1:', - ' expectedDesc = expected[0];', + ' expectedDesc = expected[0].description;', ' break;', '', ' default:', - ' expectedDesc = expected.slice(0, -1).join(", ")', + ' expectedDescs = new Array(expected.length);', + '', + ' for (i = 0; i < expected.length; i++) {', + ' expectedDescs[i] = expected[i].description;', + ' }', + '', + ' expectedDesc = expectedDescs.slice(0, -1).join(", ")', ' + " or "', - ' + expected[expected.length - 1];', + ' + expectedDescs[expected.length - 1];', ' }', '', ' foundDesc = found ? "\\"" + stringEscape(found) + "\\"" : "end of input";', @@ -882,8 +888,21 @@ module.exports = function(ast, options) { ' function peg$cleanupExpected(expected) {', ' var i = 0;', '', - ' expected.sort();', + ' expected.sort(function(a, b) {', + ' if (a.description < b.description) {', + ' return -1;', + ' } else if (a.description > b.description) {', + ' return 1;', + ' } else {', + ' return 0;', + ' }', + ' });', '', + /* + * This works because the bytecode generator guarantees that every + * expectation object exists only once, so it's enough to use |===| instead + * of deeper structural comparison. + */ ' while (i < expected.length) {', ' if (expected[i - 1] === expected[i]) {', ' expected.splice(i, 1);', diff --git a/lib/parser.js b/lib/parser.js index 2816560..ab6b1cf 100644 --- a/lib/parser.js +++ b/lib/parser.js @@ -30,7 +30,7 @@ module.exports = (function() { .replace(/[\u1080-\uFFFF]/g, function(ch) { return '\\u' + hex(ch); }); } - var expectedDesc, foundDesc; + var expectedDescs, expectedDesc, foundDesc, i; switch (expected.length) { case 0: @@ -38,13 +38,19 @@ module.exports = (function() { break; case 1: - expectedDesc = expected[0]; + expectedDesc = expected[0].description; break; default: - expectedDesc = expected.slice(0, -1).join(", ") + expectedDescs = new Array(expected.length); + + for (i = 0; i < expected.length; i++) { + expectedDescs[i] = expected[i].description; + } + + expectedDesc = expectedDescs.slice(0, -1).join(", ") + " or " - + expected[expected.length - 1]; + + expectedDescs[expected.length - 1]; } foundDesc = found ? "\"" + stringEscape(found) + "\"" : "end of input"; @@ -197,60 +203,60 @@ module.exports = (function() { }, peg$c19 = function() { return { type: "any" }; }, peg$c20 = function(expression) { return expression; }, - peg$c21 = "action", + peg$c21 = { type: "other", description: "action" }, peg$c22 = function(braced) { return braced.substr(1, braced.length - 2); }, peg$c23 = "{", - peg$c24 = "\"{\"", + peg$c24 = { type: "literal", value: "{", description: "\"{\"" }, peg$c25 = "}", - peg$c26 = "\"}\"", + peg$c26 = { type: "literal", value: "}", description: "\"}\"" }, peg$c27 = /^[^{}]/, - peg$c28 = "[^{}]", + peg$c28 = { type: "class", value: "[^{}]", description: "[^{}]" }, peg$c29 = "=", - peg$c30 = "\"=\"", + peg$c30 = { type: "literal", value: "=", description: "\"=\"" }, peg$c31 = function() { return "="; }, peg$c32 = ":", - peg$c33 = "\":\"", + peg$c33 = { type: "literal", value: ":", description: "\":\"" }, peg$c34 = function() { return ":"; }, peg$c35 = ";", - peg$c36 = "\";\"", + peg$c36 = { type: "literal", value: ";", description: "\";\"" }, peg$c37 = function() { return ";"; }, peg$c38 = "/", - peg$c39 = "\"/\"", + peg$c39 = { type: "literal", value: "/", description: "\"/\"" }, peg$c40 = function() { return "/"; }, peg$c41 = "&", - peg$c42 = "\"&\"", + peg$c42 = { type: "literal", value: "&", description: "\"&\"" }, peg$c43 = function() { return "&"; }, peg$c44 = "!", - peg$c45 = "\"!\"", + peg$c45 = { type: "literal", value: "!", description: "\"!\"" }, peg$c46 = function() { return "!"; }, peg$c47 = "$", - peg$c48 = "\"$\"", + peg$c48 = { type: "literal", value: "$", description: "\"$\"" }, peg$c49 = function() { return "$"; }, peg$c50 = "?", - peg$c51 = "\"?\"", + peg$c51 = { type: "literal", value: "?", description: "\"?\"" }, peg$c52 = function() { return "?"; }, peg$c53 = "*", - peg$c54 = "\"*\"", + peg$c54 = { type: "literal", value: "*", description: "\"*\"" }, peg$c55 = function() { return "*"; }, peg$c56 = "+", - peg$c57 = "\"+\"", + peg$c57 = { type: "literal", value: "+", description: "\"+\"" }, peg$c58 = function() { return "+"; }, peg$c59 = "(", - peg$c60 = "\"(\"", + peg$c60 = { type: "literal", value: "(", description: "\"(\"" }, peg$c61 = function() { return "("; }, peg$c62 = ")", - peg$c63 = "\")\"", + peg$c63 = { type: "literal", value: ")", description: "\")\"" }, peg$c64 = function() { return ")"; }, peg$c65 = ".", - peg$c66 = "\".\"", + peg$c66 = { type: "literal", value: ".", description: "\".\"" }, peg$c67 = function() { return "."; }, - peg$c68 = "identifier", + peg$c68 = { type: "other", description: "identifier" }, peg$c69 = "_", - peg$c70 = "\"_\"", + peg$c70 = { type: "literal", value: "_", description: "\"_\"" }, peg$c71 = function(chars) { return chars; }, - peg$c72 = "literal", + peg$c72 = { type: "other", description: "literal" }, peg$c73 = "i", - peg$c74 = "\"i\"", + peg$c74 = { type: "literal", value: "i", description: "\"i\"" }, peg$c75 = function(value, flags) { return { type: "literal", @@ -258,24 +264,24 @@ module.exports = (function() { ignoreCase: flags === "i" }; }, - peg$c76 = "string", + peg$c76 = { type: "other", description: "string" }, peg$c77 = function(string) { return string; }, peg$c78 = "\"", - peg$c79 = "\"\\\"\"", + peg$c79 = { type: "literal", value: "\"", description: "\"\\\"\"" }, peg$c80 = function(chars) { return chars.join(""); }, peg$c81 = "\\", - peg$c82 = "\"\\\\\"", - peg$c83 = "any character", + peg$c82 = { type: "literal", value: "\\", description: "\"\\\\\"" }, + peg$c83 = { type: "any", description: "any character" }, peg$c84 = function(char_) { return char_; }, peg$c85 = "'", - peg$c86 = "\"'\"", - peg$c87 = "character class", + peg$c86 = { type: "literal", value: "'", description: "\"'\"" }, + peg$c87 = { type: "other", description: "character class" }, peg$c88 = "[", - peg$c89 = "\"[\"", + peg$c89 = { type: "literal", value: "[", description: "\"[\"" }, peg$c90 = "^", - peg$c91 = "\"^\"", + peg$c91 = { type: "literal", value: "^", description: "\"^\"" }, peg$c92 = "]", - peg$c93 = "\"]\"", + peg$c93 = { type: "literal", value: "]", description: "\"]\"" }, peg$c94 = function(inverted, parts, flags) { var partsConverted = utils.map(parts, function(part) { return part.data; }); var rawText = "[" @@ -294,7 +300,7 @@ module.exports = (function() { }; }, peg$c95 = "-", - peg$c96 = "\"-\"", + peg$c96 = { type: "literal", value: "-", description: "\"-\"" }, peg$c97 = function(begin, end) { if (begin.data.charCodeAt(0) > end.data.charCodeAt(0)) { throw new this.SyntaxError( @@ -316,9 +322,9 @@ module.exports = (function() { }; }, peg$c99 = "x", - peg$c100 = "\"x\"", + peg$c100 = { type: "literal", value: "x", description: "\"x\"" }, peg$c101 = "u", - peg$c102 = "\"u\"", + peg$c102 = { type: "literal", value: "u", description: "\"u\"" }, peg$c103 = function(char_) { return char_ .replace("b", "\b") @@ -329,47 +335,47 @@ module.exports = (function() { .replace("v", "\x0B"); // IE does not recognize "\v". }, peg$c104 = "\\0", - peg$c105 = "\"\\\\0\"", + peg$c105 = { type: "literal", value: "\\0", description: "\"\\\\0\"" }, peg$c106 = function() { return "\x00"; }, peg$c107 = "\\x", - peg$c108 = "\"\\\\x\"", + peg$c108 = { type: "literal", value: "\\x", description: "\"\\\\x\"" }, peg$c109 = function(digits) { return String.fromCharCode(parseInt(digits, 16)); }, peg$c110 = "\\u", - peg$c111 = "\"\\\\u\"", + peg$c111 = { type: "literal", value: "\\u", description: "\"\\\\u\"" }, peg$c112 = function(eol) { return eol; }, peg$c113 = /^[0-9]/, - peg$c114 = "[0-9]", + peg$c114 = { type: "class", value: "[0-9]", description: "[0-9]" }, peg$c115 = /^[0-9a-fA-F]/, - peg$c116 = "[0-9a-fA-F]", + peg$c116 = { type: "class", value: "[0-9a-fA-F]", description: "[0-9a-fA-F]" }, peg$c117 = /^[a-z]/, - peg$c118 = "[a-z]", + peg$c118 = { type: "class", value: "[a-z]", description: "[a-z]" }, peg$c119 = /^[A-Z]/, - peg$c120 = "[A-Z]", - peg$c121 = "comment", + peg$c120 = { type: "class", value: "[A-Z]", description: "[A-Z]" }, + peg$c121 = { type: "other", description: "comment" }, peg$c122 = "//", - peg$c123 = "\"//\"", + peg$c123 = { type: "literal", value: "//", description: "\"//\"" }, peg$c124 = "/*", - peg$c125 = "\"/*\"", + peg$c125 = { type: "literal", value: "/*", description: "\"/*\"" }, peg$c126 = "*/", - peg$c127 = "\"*/\"", - peg$c128 = "end of line", + peg$c127 = { type: "literal", value: "*/", description: "\"*/\"" }, + peg$c128 = { type: "other", description: "end of line" }, peg$c129 = "\n", - peg$c130 = "\"\\n\"", + peg$c130 = { type: "literal", value: "\n", description: "\"\\n\"" }, peg$c131 = "\r\n", - peg$c132 = "\"\\r\\n\"", + peg$c132 = { type: "literal", value: "\r\n", description: "\"\\r\\n\"" }, peg$c133 = "\r", - peg$c134 = "\"\\r\"", + peg$c134 = { type: "literal", value: "\r", description: "\"\\r\"" }, peg$c135 = "\u2028", - peg$c136 = "\"\\u2028\"", + peg$c136 = { type: "literal", value: "\u2028", description: "\"\\u2028\"" }, peg$c137 = "\u2029", - peg$c138 = "\"\\u2029\"", + peg$c138 = { type: "literal", value: "\u2029", description: "\"\\u2029\"" }, peg$c139 = /^[\n\r\u2028\u2029]/, - peg$c140 = "[\\n\\r\\u2028\\u2029]", - peg$c141 = "whitespace", + peg$c140 = { type: "class", value: "[\\n\\r\\u2028\\u2029]", description: "[\\n\\r\\u2028\\u2029]" }, + peg$c141 = { type: "other", description: "whitespace" }, peg$c142 = /^[ \t\x0B\f\xA0\uFEFF\u1680\u180E\u2000-\u200A\u202F\u205F\u3000]/, - peg$c143 = "[ \\t\\x0B\\f\\xA0\\uFEFF\\u1680\\u180E\\u2000-\\u200A\\u202F\\u205F\\u3000]", + peg$c143 = { type: "class", value: "[ \\t\\x0B\\f\\xA0\\uFEFF\\u1680\\u180E\\u2000-\\u200A\\u202F\\u205F\\u3000]", description: "[ \\t\\x0B\\f\\xA0\\uFEFF\\u1680\\u180E\\u2000-\\u200A\\u202F\\u205F\\u3000]" }, peg$currPos = 0, peg$reportedPos = 0, @@ -452,7 +458,15 @@ module.exports = (function() { function peg$cleanupExpected(expected) { var i = 0; - expected.sort(); + expected.sort(function(a, b) { + if (a.description < b.description) { + return -1; + } else if (a.description > b.description) { + return 1; + } else { + return 0; + } + }); while (i < expected.length) { if (expected[i - 1] === expected[i]) { diff --git a/spec/compiler/passes/generate-bytecode.spec.js b/spec/compiler/passes/generate-bytecode.spec.js index 1228e33..dd9d0d4 100644 --- a/spec/compiler/passes/generate-bytecode.spec.js +++ b/spec/compiler/passes/generate-bytecode.spec.js @@ -31,11 +31,11 @@ describe("compiler pass |generateBytecode|", function() { 'c = "c"' ].join("\n"), constsDetails([ '"a"', - '"\\"a\\""', + '{ type: "literal", value: "a", description: "\\"a\\"" }', '"b"', - '"\\"b\\""', + '{ type: "literal", value: "b", description: "\\"b\\"" }', '"c"', - '"\\"c\\""' + '{ type: "literal", value: "c", description: "\\"c\\"" }' ])); }); }); @@ -61,10 +61,11 @@ describe("compiler pass |generateBytecode|", function() { }); it("defines correct constants", function() { - expect(pass).toChangeAST( - grammar, - constsDetails(['"start"', '"a"', '"\\"a\\""']) - ); + expect(pass).toChangeAST(grammar, constsDetails([ + '{ type: "other", description: "start" }', + '"a"', + '{ type: "literal", value: "a", description: "\\"a\\"" }' + ])); }); }); @@ -124,10 +125,11 @@ describe("compiler pass |generateBytecode|", function() { }); it("defines correct constants", function() { - expect(pass).toChangeAST( - grammar, - constsDetails(['"a"', '"\\"a\\""', 'function(a) { code }']) - ); + expect(pass).toChangeAST(grammar, constsDetails([ + '"a"', + '{ type: "literal", value: "a", description: "\\"a\\"" }', + 'function(a) { code }' + ])); }); }); @@ -163,11 +165,11 @@ describe("compiler pass |generateBytecode|", function() { expect(pass).toChangeAST(grammar, constsDetails([ 'null', '"a"', - '"\\"a\\""', + '{ type: "literal", value: "a", description: "\\"a\\"" }', '"b"', - '"\\"b\\""', + '{ type: "literal", value: "b", description: "\\"b\\"" }', '"c"', - '"\\"c\\""', + '{ type: "literal", value: "c", description: "\\"c\\"" }', 'function(a, b, c) { code }' ])); }); @@ -216,18 +218,15 @@ describe("compiler pass |generateBytecode|", function() { }); it("defines correct constants", function() { - expect(pass).toChangeAST( - grammar, - constsDetails([ - 'null', - '"a"', - '"\\"a\\""', - '"b"', - '"\\"b\\""', - '"c"', - '"\\"c\\""' - ]) - ); + expect(pass).toChangeAST(grammar, constsDetails([ + 'null', + '"a"', + '{ type: "literal", value: "a", description: "\\"a\\"" }', + '"b"', + '{ type: "literal", value: "b", description: "\\"b\\"" }', + '"c"', + '{ type: "literal", value: "c", description: "\\"c\\"" }' + ])); }); }); }); @@ -272,10 +271,12 @@ describe("compiler pass |generateBytecode|", function() { }); it("defines correct constants", function() { - expect(pass).toChangeAST( - grammar, - constsDetails(['""', 'null', '"a"', '"\\"a\\""']) - ); + expect(pass).toChangeAST(grammar, constsDetails([ + '""', + 'null', + '"a"', + '{ type: "literal", value: "a", description: "\\"a\\"" }' + ])); }); }); @@ -299,10 +300,12 @@ describe("compiler pass |generateBytecode|", function() { }); it("defines correct constants", function() { - expect(pass).toChangeAST( - grammar, - constsDetails(['""', 'null', '"a"', '"\\"a\\""']) - ); + expect(pass).toChangeAST(grammar, constsDetails([ + '""', + 'null', + '"a"', + '{ type: "literal", value: "a", description: "\\"a\\"" }' + ])); }); }); @@ -371,11 +374,11 @@ describe("compiler pass |generateBytecode|", function() { expect(pass).toChangeAST(grammar, constsDetails([ 'null', '"a"', - '"\\"a\\""', + '{ type: "literal", value: "a", description: "\\"a\\"" }', '"b"', - '"\\"b\\""', + '{ type: "literal", value: "b", description: "\\"b\\"" }', '"c"', - '"\\"c\\""', + '{ type: "literal", value: "c", description: "\\"c\\"" }', 'function(a, b, c) { code }', '""' ])); @@ -448,11 +451,11 @@ describe("compiler pass |generateBytecode|", function() { expect(pass).toChangeAST(grammar, constsDetails([ 'null', '"a"', - '"\\"a\\""', + '{ type: "literal", value: "a", description: "\\"a\\"" }', '"b"', - '"\\"b\\""', + '{ type: "literal", value: "b", description: "\\"b\\"" }', '"c"', - '"\\"c\\""', + '{ type: "literal", value: "c", description: "\\"c\\"" }', 'function(a, b, c) { code }', '""' ])); @@ -473,10 +476,11 @@ describe("compiler pass |generateBytecode|", function() { }); it("defines correct constants", function() { - expect(pass).toChangeAST( - grammar, - constsDetails(['""', '"a"', '"\\"a\\""']) - ); + expect(pass).toChangeAST(grammar, constsDetails([ + '""', + '"a"', + '{ type: "literal", value: "a", description: "\\"a\\"" }' + ])); }); }); @@ -495,10 +499,11 @@ describe("compiler pass |generateBytecode|", function() { }); it("defines correct constants", function() { - expect(pass).toChangeAST( - grammar, - constsDetails(['[]', '"a"', '"\\"a\\""']) - ); + expect(pass).toChangeAST(grammar, constsDetails([ + '[]', + '"a"', + '{ type: "literal", value: "a", description: "\\"a\\"" }' + ])); }); }); @@ -521,10 +526,12 @@ describe("compiler pass |generateBytecode|", function() { }); it("defines correct constants", function() { - expect(pass).toChangeAST( - grammar, - constsDetails(['[]', 'null', '"a"', '"\\"a\\""']) - ); + expect(pass).toChangeAST(grammar, constsDetails([ + '[]', + 'null', + '"a"', + '{ type: "literal", value: "a", description: "\\"a\\"" }' + ])); }); }); @@ -571,7 +578,10 @@ describe("compiler pass |generateBytecode|", function() { }); it("defines correct constants", function() { - expect(pass).toChangeAST(grammar, constsDetails(['"a"', '"\\"a\\""'])); + expect(pass).toChangeAST(grammar, constsDetails([ + '"a"', + '{ type: "literal", value: "a", description: "\\"a\\"" }' + ])); }); }); @@ -587,7 +597,10 @@ describe("compiler pass |generateBytecode|", function() { }); it("defines correct constants", function() { - expect(pass).toChangeAST(grammar, constsDetails(['"a"', '"\\"A\\""'])); + expect(pass).toChangeAST(grammar, constsDetails([ + '"a"', + '{ type: "literal", value: "A", description: "\\"A\\"" }' + ])); }); }); }); @@ -603,55 +616,55 @@ describe("compiler pass |generateBytecode|", function() { describe("non-empty non-inverted case-sensitive", function() { it("defines correct constants", function() { - expect(pass).toChangeAST( - 'start = [a]', - constsDetails(['/^[a]/', '"[a]"']) - ); + expect(pass).toChangeAST('start = [a]', constsDetails([ + '/^[a]/', + '{ type: "class", value: "[a]", description: "[a]" }' + ])); }); }); describe("non-empty inverted case-sensitive", function() { it("defines correct constants", function() { - expect(pass).toChangeAST( - 'start = [^a]', - constsDetails(['/^[^a]/', '"[^a]"']) - ); + expect(pass).toChangeAST('start = [^a]', constsDetails([ + '/^[^a]/', + '{ type: "class", value: "[^a]", description: "[^a]" }' + ])); }); }); describe("non-empty non-inverted case-insensitive", function() { it("defines correct constants", function() { - expect(pass).toChangeAST( - 'start = [a]i', - constsDetails(['/^[a]/i', '"[a]i"']) - ); + expect(pass).toChangeAST('start = [a]i', constsDetails([ + '/^[a]/i', + '{ type: "class", value: "[a]i", description: "[a]i" }' + ])); }); }); describe("non-empty complex", function() { it("defines correct constants", function() { - expect(pass).toChangeAST( - 'start = [ab-def-hij-l]', - constsDetails(['/^[ab-def-hij-l]/', '"[ab-def-hij-l]"']) - ); + expect(pass).toChangeAST('start = [ab-def-hij-l]', constsDetails([ + '/^[ab-def-hij-l]/', + '{ type: "class", value: "[ab-def-hij-l]", description: "[ab-def-hij-l]" }' + ])); }); }); describe("empty non-inverted", function() { it("defines correct constants", function() { - expect(pass).toChangeAST( - 'start = []', - constsDetails(['/^(?!)/', '"[]"']) - ); + expect(pass).toChangeAST('start = []', constsDetails([ + '/^(?!)/', + '{ type: "class", value: "[]", description: "[]" }' + ])); }); }); describe("empty inverted", function() { it("defines correct constants", function() { - expect(pass).toChangeAST( - 'start = [^]', - constsDetails(['/^[\\S\\s]/', '"[^]"']) - ); + expect(pass).toChangeAST('start = [^]', constsDetails([ + '/^[\\S\\s]/', + '{ type: "class", value: "[^]", description: "[^]" }' + ])); }); }); }); @@ -668,7 +681,10 @@ describe("compiler pass |generateBytecode|", function() { }); it("defines correct constants", function() { - expect(pass).toChangeAST(grammar, constsDetails(['"any character"'])); + expect(pass).toChangeAST( + grammar, + constsDetails(['{ type: "any", description: "any character" }']) + ); }); }); }); diff --git a/spec/generated-parser.spec.js b/spec/generated-parser.spec.js index 56548a0..71eb211 100644 --- a/spec/generated-parser.spec.js +++ b/spec/generated-parser.spec.js @@ -248,7 +248,9 @@ describe("generated parser", function() { }); it("overwrites expected string on failure", function() { - expect(parser).toFailToParse("b", { expected: ["start"] }); + expect(parser).toFailToParse("b", { + expected: [{ type: "other", description: "start" }] + }); }); }); @@ -716,10 +718,12 @@ describe("generated parser", function() { expect(parser).toParse("ab", ["a", "b"]); }); - it("sets expected string correctly on failure", function() { + it("sets expectation correctly on failure", function() { var parser = PEG.buildParser('start = "a"', options); - expect(parser).toFailToParse("b", { expected: ['"a"'] }); + expect(parser).toFailToParse("b", { + expected: [{ type: "literal", value: "a", description: '"a"' }] + }); }); }); @@ -775,10 +779,12 @@ describe("generated parser", function() { expect(parser).toParse("ab", ["a", "b"]); }); - it("sets expected string correctly on failure", function() { + it("sets expectation correctly on failure", function() { var parser = PEG.buildParser('start = [a]', options); - expect(parser).toFailToParse("b", { expected: ["[a]"] }); + expect(parser).toFailToParse("b", { + expected: [{ type: "class", value: "[a]", description: "[a]" }] + }); }); }); @@ -795,10 +801,12 @@ describe("generated parser", function() { expect(parser).toParse("ab", ["a", "b"]); }); - it("sets expected string correctly on failure", function() { + it("sets expectation correctly on failure", function() { var parser = PEG.buildParser('start = .', options); - expect(parser).toFailToParse("", { expected: ['any character'] }); + expect(parser).toFailToParse("", { + expected: [{ type: "any", description: "any character" }] + }); }); }); @@ -807,32 +815,41 @@ describe("generated parser", function() { it("reports only the rightmost error", function() { var parser = PEG.buildParser('start = "a" "b" / "a" "c" "d"', options); - expect(parser).toFailToParse("ace", { offset: 2, expected: ['"d"'] }); + expect(parser).toFailToParse("ace", { + offset: 2, + expected: [{ type: "literal", value: "d", description: '"d"' }] + }); }); }); - describe("expected strings reporting", function() { - it("reports expected strings correctly with no alternative", function() { + describe("expectations reporting", function() { + it("reports expectations correctly with no alternative", function() { var parser = PEG.buildParser('start = ', options); expect(parser).toFailToParse("a", { expected: [] }); }); - it("reports expected strings correctly with one alternative", function() { + it("reports expectations correctly with one alternative", function() { var parser = PEG.buildParser('start = "a"', options); - expect(parser).toFailToParse("b", { expected: ['"a"'] }); + expect(parser).toFailToParse("b", { + expected: [{ type: "literal", value: "a", description: '"a"' }] + }); }); - it("reports expected strings correctly with multiple alternatives", function() { + it("reports expectations correctly with multiple alternatives", function() { var parser = PEG.buildParser('start = "a" / "b" / "c"', options); expect(parser).toFailToParse("d", { - expected: ['"a"', '"b"', '"c"'] + expected: [ + { type: "literal", value: "a", description: '"a"' }, + { type: "literal", value: "b", description: '"b"' }, + { type: "literal", value: "c", description: '"c"' } + ] }); }); - it("removes duplicates from expected strings", function() { + it("removes duplicates from expectations", function() { /* * There was a bug in the code that manifested only with three * duplicates. This is why the following test uses three choices @@ -842,14 +859,20 @@ describe("generated parser", function() { */ var parser = PEG.buildParser('start = "a" / "a" / "a"', options); - expect(parser).toFailToParse("b", { expected: ['"a"'] }); + expect(parser).toFailToParse("b", { + expected: [{ type: "literal", value: "a", description: '"a"' }] + }); }); - it("sorts expected strings", function() { + it("sorts expectations", function() { var parser = PEG.buildParser('start = "c" / "b" / "a"', options); expect(parser).toFailToParse("d", { - expected: ['"a"', '"b"', '"c"'] + expected: [ + { type: "literal", value: "a", description: '"a"' }, + { type: "literal", value: "b", description: '"b"' }, + { type: "literal", value: "c", description: '"c"' } + ] }); }); });