Skip to content

Commit

Permalink
Error handling: Structured expectations
Browse files Browse the repository at this point in the history
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.)
  • Loading branch information
dmajda committed Dec 1, 2013
1 parent 5312e12 commit 435bb8f
Show file tree
Hide file tree
Showing 5 changed files with 250 additions and 164 deletions.
22 changes: 18 additions & 4 deletions lib/compiler/passes/generate-bytecode.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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],
Expand All @@ -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],
Expand Down
29 changes: 24 additions & 5 deletions lib/compiler/passes/generate-javascript.js
Original file line number Diff line number Diff line change
Expand Up @@ -703,21 +703,27 @@ 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:',
' expectedDesc = "end of input";',
' 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";',
Expand Down Expand Up @@ -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);',
Expand Down
130 changes: 72 additions & 58 deletions lib/parser.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 435bb8f

Please sign in to comment.