Skip to content

Commit c265d37

Browse files
committed
Merge pull request #24 from gchauvet/master
Error handling improvement
2 parents 455b08e + 833e2ce commit c265d37

4 files changed

Lines changed: 72 additions & 44 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
node_modules

jsonparse.js

Lines changed: 55 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ var STRING = C.STRING = 0xa;
1515
var NUMBER = C.NUMBER = 0xb;
1616
// Tokenizer States
1717
var START = C.START = 0x11;
18+
var STOP = C.STOP = 0x12;
1819
var TRUE1 = C.TRUE1 = 0x21;
1920
var TRUE2 = C.TRUE2 = 0x22;
2021
var TRUE3 = C.TRUE3 = 0x23;
@@ -46,16 +47,6 @@ var KEY = C.KEY = 0x72;
4647
var OBJECT = C.OBJECT = 0x81;
4748
var ARRAY = C.ARRAY = 0x82;
4849

49-
// Slow code to string converter (only used when throwing syntax errors)
50-
function toknam(code) {
51-
var keys = Object.keys(C);
52-
for (var i = 0, l = keys.length; i < l; i++) {
53-
var key = keys[i];
54-
if (C[key] === code) { return key; }
55-
}
56-
return code && ("0x" + code.toString(16));
57-
}
58-
5950

6051
function Parser() {
6152
this.tState = START;
@@ -83,15 +74,25 @@ function Parser() {
8374
// Stream offset
8475
this.offset = -1;
8576
}
77+
78+
// Slow code to string converter (only used when throwing syntax errors)
79+
Parser.toknam = function (code) {
80+
var keys = Object.keys(C);
81+
for (var i = 0, l = keys.length; i < l; i++) {
82+
var key = keys[i];
83+
if (C[key] === code) { return key; }
84+
}
85+
return code && ("0x" + code.toString(16));
86+
}
87+
8688
var proto = Parser.prototype;
89+
proto.onError = function (err) { throw err; };
8790
proto.charError = function (buffer, i) {
88-
this.onError(new Error("Unexpected " + JSON.stringify(String.fromCharCode(buffer[i])) + " at position " + i + " in state " + toknam(this.tState)));
91+
this.tState = STOP;
92+
this.onError(new Error("Unexpected " + JSON.stringify(String.fromCharCode(buffer[i])) + " at position " + i + " in state " + Parser.toknam(this.tState)));
8993
};
90-
proto.onError = function (err) { throw err; };
9194
proto.write = function (buffer) {
9295
if (typeof buffer === "string") buffer = new Buffer(buffer);
93-
//process.stdout.write("Input: ");
94-
//console.dir(buffer.toString());
9596
var n;
9697
for (var i = 0, l = buffer.length; i < l; i++) {
9798
if (this.tState === START){
@@ -114,7 +115,9 @@ proto.write = function (buffer) {
114115
this.magnatude = n - 0x30; this.tState = NUMBER3;
115116
} else if (n === 0x20 || n === 0x09 || n === 0x0a || n === 0x0d) {
116117
// whitespace
117-
} else { this.charError(buffer, i); }
118+
} else {
119+
return this.charError(buffer, i);
120+
}
118121
}
119122
}else if (this.tState === STRING1){ // After open quote
120123
n = buffer[i]; // get current byte from buffer
@@ -129,8 +132,7 @@ proto.write = function (buffer) {
129132
i = i + j - 1;
130133
} else if (this.bytes_remaining === 0 && n >= 128) { // else if no remainder bytes carried over, parse multi byte (>=128) chars one at a time
131134
if (n <= 193) {
132-
this.onError(new Error("Invalid UTF-8 character at position " + i + " in state " + toknam(this.tState)));
133-
return
135+
return this.onError(new Error("Invalid UTF-8 character at position " + i + " in state " + Parser.toknam(this.tState)));
134136
}
135137
if ((n >= 194) && (n <= 223)) this.bytes_in_sequence = 2;
136138
if ((n >= 224) && (n <= 239)) this.bytes_in_sequence = 3;
@@ -148,7 +150,9 @@ proto.write = function (buffer) {
148150
} else if (n === 0x22) { this.tState = START; this.onToken(STRING, this.string); this.offset += Buffer.byteLength(this.string, 'utf8') + 1; this.string = undefined; }
149151
else if (n === 0x5c) { this.tState = STRING2; }
150152
else if (n >= 0x20) { this.string += String.fromCharCode(n); }
151-
else { this.charError(buffer, i); }
153+
else {
154+
return this.charError(buffer, i);
155+
}
152156
}else if (this.tState === STRING2){ // After backslash
153157
n = buffer[i];
154158
if(n === 0x22){ this.string += "\""; this.tState = STRING1;
@@ -161,7 +165,7 @@ proto.write = function (buffer) {
161165
}else if(n === 0x74){ this.string += "\t"; this.tState = STRING1;
162166
}else if(n === 0x75){ this.unicode = ""; this.tState = STRING3;
163167
}else{
164-
this.charError(buffer, i);
168+
return this.charError(buffer, i);
165169
}
166170
}else if (this.tState === STRING3 || this.tState === STRING4 || this.tState === STRING5 || this.tState === STRING6){ // unicode hex codes
167171
n = buffer[i];
@@ -174,14 +178,16 @@ proto.write = function (buffer) {
174178
this.tState = STRING1;
175179
}
176180
} else {
177-
this.charError(buffer, i);
181+
return this.charError(buffer, i);
178182
}
179183
}else if (this.tState === NUMBER1){ // after minus
180184
n = buffer[i];
181185
this.numberLength++;
182186
if (n === 0x30) { this.magnatude = 0; this.tState = NUMBER2; }
183187
else if (n > 0x30 && n < 0x40) { this.magnatude = n - 0x30; this.tState = NUMBER3; }
184-
else { this.charError(buffer, i); }
188+
else {
189+
return this.charError(buffer, i);
190+
}
185191
}else if (this.tState === NUMBER2){ // * After initial zero
186192
n = buffer[i];
187193
this.numberLength++;
@@ -227,7 +233,9 @@ proto.write = function (buffer) {
227233
this.magnatude += this.position * (n - 0x30);
228234
this.position /= 10;
229235
this.tState = NUMBER5;
230-
} else { this.charError(buffer, i); }
236+
} else {
237+
return this.charError(buffer, i);
238+
}
231239
}else if (this.tState === NUMBER5){ // * After digit (after period)
232240
n = buffer[i];
233241
this.numberLength++;
@@ -260,15 +268,19 @@ proto.write = function (buffer) {
260268
this.exponent = this.exponent * 10 + (n - 0x30);
261269
this.tState = NUMBER8;
262270
}
263-
else { this.charError(buffer, i); }
271+
else {
272+
return this.charError(buffer, i);
273+
}
264274
}else if (this.tState === NUMBER7){ // After +/-
265275
n = buffer[i];
266276
this.numberLength++;
267277
if (n >= 0x30 && n < 0x40) { // 0-9
268278
this.exponent = this.exponent * 10 + (n - 0x30);
269279
this.tState = NUMBER8;
270280
}
271-
else { this.charError(buffer, i); }
281+
else {
282+
return this.charError(buffer, i);
283+
}
272284
}else if (this.tState === NUMBER8){ // * After digit (after +/-)
273285
n = buffer[i];
274286
this.numberLength++;
@@ -295,34 +307,34 @@ proto.write = function (buffer) {
295307
}
296308
}else if (this.tState === TRUE1){ // r
297309
if (buffer[i] === 0x72) { this.tState = TRUE2; }
298-
else { this.charError(buffer, i); }
310+
else { return this.charError(buffer, i); }
299311
}else if (this.tState === TRUE2){ // u
300312
if (buffer[i] === 0x75) { this.tState = TRUE3; }
301-
else { this.charError(buffer, i); }
313+
else { return this.charError(buffer, i); }
302314
}else if (this.tState === TRUE3){ // e
303315
if (buffer[i] === 0x65) { this.tState = START; this.onToken(TRUE, true); this.offset+= 3; }
304-
else { this.charError(buffer, i); }
316+
else { return this.charError(buffer, i); }
305317
}else if (this.tState === FALSE1){ // a
306318
if (buffer[i] === 0x61) { this.tState = FALSE2; }
307-
else { this.charError(buffer, i); }
319+
else { return this.charError(buffer, i); }
308320
}else if (this.tState === FALSE2){ // l
309321
if (buffer[i] === 0x6c) { this.tState = FALSE3; }
310-
else { this.charError(buffer, i); }
322+
else { return this.charError(buffer, i); }
311323
}else if (this.tState === FALSE3){ // s
312324
if (buffer[i] === 0x73) { this.tState = FALSE4; }
313-
else { this.charError(buffer, i); }
325+
else { return this.charError(buffer, i); }
314326
}else if (this.tState === FALSE4){ // e
315327
if (buffer[i] === 0x65) { this.tState = START; this.onToken(FALSE, false); this.offset+= 4; }
316-
else { this.charError(buffer, i); }
328+
else { return this.charError(buffer, i); }
317329
}else if (this.tState === NULL1){ // u
318330
if (buffer[i] === 0x75) { this.tState = NULL2; }
319-
else { this.charError(buffer, i); }
331+
else { return this.charError(buffer, i); }
320332
}else if (this.tState === NULL2){ // l
321333
if (buffer[i] === 0x6c) { this.tState = NULL3; }
322-
else { this.charError(buffer, i); }
334+
else { return this.charError(buffer, i); }
323335
}else if (this.tState === NULL3){ // l
324336
if (buffer[i] === 0x6c) { this.tState = START; this.onToken(NULL, null); this.offset += 3; }
325-
else { this.charError(buffer, i); }
337+
else { return this.charError(buffer, i); }
326338
}
327339
}
328340
};
@@ -331,7 +343,8 @@ proto.onToken = function (token, value) {
331343
};
332344

333345
proto.parseError = function (token, value) {
334-
this.onError(new Error("Unexpected " + toknam(token) + (value ? ("(" + JSON.stringify(value) + ")") : "") + " in state " + toknam(this.state)));
346+
this.tState = STOP;
347+
this.onError(new Error("Unexpected " + Parser.toknam(token) + (value ? ("(" + JSON.stringify(value) + ")") : "") + " in state " + Parser.toknam(this.state)));
335348
};
336349
proto.push = function () {
337350
this.stack.push({value: this.value, key: this.key, mode: this.mode});
@@ -353,7 +366,6 @@ proto.onValue = function (value) {
353366
// Override me
354367
};
355368
proto.onToken = function (token, value) {
356-
//console.log("OnToken: state=%s token=%s %s", toknam(this.state), toknam(token), value?JSON.stringify(value):"");
357369
if(this.state === VALUE){
358370
if(token === STRING || token === NUMBER || token === TRUE || token === FALSE || token === NULL){
359371
if (this.value) {
@@ -384,16 +396,16 @@ proto.onToken = function (token, value) {
384396
if (this.mode === OBJECT) {
385397
this.pop();
386398
} else {
387-
this.parseError(token, value);
399+
return this.parseError(token, value);
388400
}
389401
}else if(token === RIGHT_BRACKET){
390402
if (this.mode === ARRAY) {
391403
this.pop();
392404
} else {
393-
this.parseError(token, value);
405+
return this.parseError(token, value);
394406
}
395407
}else{
396-
this.parseError(token, value);
408+
return this.parseError(token, value);
397409
}
398410
}else if(this.state === KEY){
399411
if (token === STRING) {
@@ -402,11 +414,11 @@ proto.onToken = function (token, value) {
402414
} else if (token === RIGHT_BRACE) {
403415
this.pop();
404416
} else {
405-
this.parseError(token, value);
417+
return this.parseError(token, value);
406418
}
407419
}else if(this.state === COLON){
408420
if (token === COLON) { this.state = VALUE; }
409-
else { this.parseError(token, value); }
421+
else { return this.parseError(token, value); }
410422
}else if(this.state === COMMA){
411423
if (token === COMMA) {
412424
if (this.mode === ARRAY) { this.key++; this.state = VALUE; }
@@ -415,10 +427,10 @@ proto.onToken = function (token, value) {
415427
} else if (token === RIGHT_BRACKET && this.mode === ARRAY || token === RIGHT_BRACE && this.mode === OBJECT) {
416428
this.pop();
417429
} else {
418-
this.parseError(token, value);
430+
return this.parseError(token, value);
419431
}
420432
}else{
421-
this.parseError(token, value);
433+
return this.parseError(token, value);
422434
}
423435
};
424436

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "jsonparse",
33
"description": "This is a pure-js JSON streaming parser for node.js",
44
"tags": ["json", "stream"],
5-
"version": "1.0.0",
5+
"version": "1.1.0",
66
"author": "Tim Caswell <tim@creationix.com>",
77
"repository": {
88
"type": "git",

test/unvalid.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
var test = require('tape');
2+
var Parser = require('../');
3+
4+
test('unvalid', function (t) {
5+
var count = 0;
6+
7+
var p = new Parser();
8+
p.onError = function (value) {
9+
count++;
10+
t.equal(1, count);
11+
t.end();
12+
};
13+
14+
p.write('{"test": eer[');
15+
});

0 commit comments

Comments
 (0)