UNPKG

21.9 kBJavaScriptView Raw
1"use strict";
2// deno-lint-ignore-file no-unused-vars no-case-declarations
3Object.defineProperty(exports, "__esModule", { value: true });
4exports.JSONParser = void 0;
5// Modernized/de-nodified version of creationix/jsonparse
6// Copyright (c) 2012 Tim Caswell
7// Licensed under the MIT (licenses/MIT.md) license.
8// TODO: TypeScript conversion?
9// TODO: Integrate with other modules for better performance
10// Named constants with unique integer values
11const C = {};
12// Tokens
13const LEFT_BRACE = C.LEFT_BRACE = 0x1;
14const RIGHT_BRACE = C.RIGHT_BRACE = 0x2;
15const LEFT_BRACKET = C.LEFT_BRACKET = 0x3;
16const RIGHT_BRACKET = C.RIGHT_BRACKET = 0x4;
17const COLON = C.COLON = 0x5;
18const COMMA = C.COMMA = 0x6;
19const TRUE = C.TRUE = 0x7;
20const FALSE = C.FALSE = 0x8;
21const NULL = C.NULL = 0x9;
22const STRING = C.STRING = 0xa;
23const NUMBER = C.NUMBER = 0xb;
24// Tokenizer States
25const START = C.START = 0x11;
26const STOP = C.STOP = 0x12;
27const TRUE1 = C.TRUE1 = 0x21;
28const TRUE2 = C.TRUE2 = 0x22;
29const TRUE3 = C.TRUE3 = 0x23;
30const FALSE1 = C.FALSE1 = 0x31;
31const FALSE2 = C.FALSE2 = 0x32;
32const FALSE3 = C.FALSE3 = 0x33;
33const FALSE4 = C.FALSE4 = 0x34;
34const NULL1 = C.NULL1 = 0x41;
35const NULL2 = C.NULL2 = 0x42;
36const NULL3 = C.NULL3 = 0x43;
37const NUMBER1 = C.NUMBER1 = 0x51;
38const NUMBER3 = C.NUMBER3 = 0x53;
39const STRING1 = C.STRING1 = 0x61;
40const STRING2 = C.STRING2 = 0x62;
41const STRING3 = C.STRING3 = 0x63;
42const STRING4 = C.STRING4 = 0x64;
43const STRING5 = C.STRING5 = 0x65;
44const STRING6 = C.STRING6 = 0x66;
45// Parser States
46const VALUE = C.VALUE = 0x71;
47const KEY = C.KEY = 0x72;
48// Parser Modes
49const OBJECT = C.OBJECT = 0x81;
50const ARRAY = C.ARRAY = 0x82;
51// Character constants
52const BACK_SLASH = "\\".charCodeAt(0);
53const FORWARD_SLASH = "\/".charCodeAt(0);
54const BACKSPACE = "\b".charCodeAt(0);
55const FORM_FEED = "\f".charCodeAt(0);
56const NEWLINE = "\n".charCodeAt(0);
57const CARRIAGE_RETURN = "\r".charCodeAt(0);
58const TAB = "\t".charCodeAt(0);
59const STRING_BUFFER_SIZE = 64 * 1024;
60function alloc(size) {
61 return new Uint8Array(size);
62}
63class Parser {
64 constructor() {
65 this.tState = START;
66 this.value = undefined;
67 this.string = undefined; // string data
68 this.stringBuffer = alloc(STRING_BUFFER_SIZE);
69 this.stringBufferOffset = 0;
70 this.unicode = undefined; // unicode escapes
71 this.highSurrogate = undefined;
72 this.key = undefined;
73 this.mode = undefined;
74 this.stack = [];
75 this.state = VALUE;
76 this.bytes_remaining = 0; // number of bytes remaining in multi byte utf8 char to read after split boundary
77 this.bytes_in_sequence = 0; // bytes in multi byte utf8 char to read
78 this.temp_buffs = { "2": alloc(2), "3": alloc(3), "4": alloc(4) }; // for rebuilding chars split before boundary is reached
79 this.encoder = new TextEncoder();
80 this.decoder = new TextDecoder();
81 // Stream offset
82 this.offset = -1;
83 }
84 // Slow code to string converter (only used when throwing syntax errors)
85 static toknam(code) {
86 const keys = Object.keys(C);
87 for (let i = 0, l = keys.length; i < l; i++) {
88 const key = keys[i];
89 if (C[key] === code) {
90 return key;
91 }
92 }
93 return code && ("0x" + code.toString(16));
94 }
95 encode(string) { return this.encoder.encode(string); }
96 decode(buffer) { return this.decoder.decode(buffer); }
97 onError(err) { throw err; }
98 charError(buffer, i) {
99 this.tState = STOP;
100 this.onError(new Error("Unexpected " + JSON.stringify(String.fromCharCode(buffer[i])) + " at position " + i + " in state " + Parser.toknam(this.tState)));
101 }
102 appendStringChar(char) {
103 if (this.stringBufferOffset >= STRING_BUFFER_SIZE) {
104 this.string += this.decode(this.stringBuffer);
105 this.stringBufferOffset = 0;
106 }
107 this.stringBuffer[this.stringBufferOffset++] = char;
108 }
109 appendStringBuf(buf, start, end) {
110 let size = buf.length;
111 if (typeof start === 'number') {
112 if (typeof end === 'number') {
113 if (end < 0) {
114 // adding a negative end decreeses the size
115 size = buf.length - start + end;
116 }
117 else {
118 size = end - start;
119 }
120 }
121 else {
122 size = buf.length - start;
123 }
124 }
125 if (size < 0) {
126 size = 0;
127 }
128 if (this.stringBufferOffset + size > STRING_BUFFER_SIZE) {
129 this.string += this.decode(this.stringBuffer.subarray(0, this.stringBufferOffset));
130 this.stringBufferOffset = 0;
131 }
132 this.stringBuffer.set(buf.subarray(start, end), this.stringBufferOffset);
133 this.stringBufferOffset += size;
134 }
135 write(buffer) {
136 if (typeof buffer === "string")
137 buffer = this.encode(buffer);
138 let n;
139 for (let i = 0, l = buffer.length; i < l; i++) {
140 if (this.tState === START) {
141 n = buffer[i];
142 this.offset++;
143 if (n === 0x7b) {
144 this.onToken(LEFT_BRACE, "{"); // {
145 }
146 else if (n === 0x7d) {
147 this.onToken(RIGHT_BRACE, "}"); // }
148 }
149 else if (n === 0x5b) {
150 this.onToken(LEFT_BRACKET, "["); // [
151 }
152 else if (n === 0x5d) {
153 this.onToken(RIGHT_BRACKET, "]"); // ]
154 }
155 else if (n === 0x3a) {
156 this.onToken(COLON, ":"); // :
157 }
158 else if (n === 0x2c) {
159 this.onToken(COMMA, ","); // ,
160 }
161 else if (n === 0x74) {
162 this.tState = TRUE1; // t
163 }
164 else if (n === 0x66) {
165 this.tState = FALSE1; // f
166 }
167 else if (n === 0x6e) {
168 this.tState = NULL1; // n
169 }
170 else if (n === 0x22) { // "
171 this.string = "";
172 this.stringBufferOffset = 0;
173 this.tState = STRING1;
174 }
175 else if (n === 0x2d) {
176 this.string = "-";
177 this.tState = NUMBER1; // -
178 }
179 else {
180 if (n >= 0x30 && n < 0x40) { // 1-9
181 this.string = String.fromCharCode(n);
182 this.tState = NUMBER3;
183 }
184 else if (n === 0x20 || n === 0x09 || n === 0x0a || n === 0x0d) {
185 // whitespace
186 }
187 else {
188 return this.charError(buffer, i);
189 }
190 }
191 }
192 else if (this.tState === STRING1) { // After open quote
193 n = buffer[i]; // get current byte from buffer
194 // check for carry over of a multi byte char split between data chunks
195 // & fill temp buffer it with start of this data chunk up to the boundary limit set in the last iteration
196 if (this.bytes_remaining > 0) {
197 let j;
198 for (j = 0; j < this.bytes_remaining; j++) {
199 this.temp_buffs[this.bytes_in_sequence][this.bytes_in_sequence - this.bytes_remaining + j] = buffer[j];
200 }
201 this.appendStringBuf(this.temp_buffs[this.bytes_in_sequence]);
202 this.bytes_in_sequence = this.bytes_remaining = 0;
203 i = i + j - 1;
204 }
205 else if (this.bytes_remaining === 0 && n >= 128) { // else if no remainder bytes carried over, parse multi byte (>=128) chars one at a time
206 if (n <= 193 || n > 244) {
207 return this.onError(new Error("Invalid UTF-8 character at position " + i + " in state " + Parser.toknam(this.tState)));
208 }
209 if ((n >= 194) && (n <= 223))
210 this.bytes_in_sequence = 2;
211 if ((n >= 224) && (n <= 239))
212 this.bytes_in_sequence = 3;
213 if ((n >= 240) && (n <= 244))
214 this.bytes_in_sequence = 4;
215 if ((this.bytes_in_sequence + i) > buffer.length) { // if bytes needed to complete char fall outside buffer length, we have a boundary split
216 for (let k = 0; k <= (buffer.length - 1 - i); k++) {
217 this.temp_buffs[this.bytes_in_sequence][k] = buffer[i + k]; // fill temp buffer of correct size with bytes available in this chunk
218 }
219 this.bytes_remaining = (i + this.bytes_in_sequence) - buffer.length;
220 i = buffer.length - 1;
221 }
222 else {
223 this.appendStringBuf(buffer, i, i + this.bytes_in_sequence);
224 i = i + this.bytes_in_sequence - 1;
225 }
226 }
227 else if (n === 0x22) {
228 this.tState = START;
229 this.string += this.decode(this.stringBuffer.subarray(0, this.stringBufferOffset));
230 this.stringBufferOffset = 0;
231 this.onToken(STRING, this.string);
232 this.offset += this.encode(this.string).length + 1;
233 this.string = undefined;
234 }
235 else if (n === 0x5c) {
236 this.tState = STRING2;
237 }
238 else if (n >= 0x20) {
239 this.appendStringChar(n);
240 }
241 else {
242 return this.charError(buffer, i);
243 }
244 }
245 else if (this.tState === STRING2) { // After backslash
246 n = buffer[i];
247 if (n === 0x22) {
248 this.appendStringChar(n);
249 this.tState = STRING1;
250 }
251 else if (n === 0x5c) {
252 this.appendStringChar(BACK_SLASH);
253 this.tState = STRING1;
254 }
255 else if (n === 0x2f) {
256 this.appendStringChar(FORWARD_SLASH);
257 this.tState = STRING1;
258 }
259 else if (n === 0x62) {
260 this.appendStringChar(BACKSPACE);
261 this.tState = STRING1;
262 }
263 else if (n === 0x66) {
264 this.appendStringChar(FORM_FEED);
265 this.tState = STRING1;
266 }
267 else if (n === 0x6e) {
268 this.appendStringChar(NEWLINE);
269 this.tState = STRING1;
270 }
271 else if (n === 0x72) {
272 this.appendStringChar(CARRIAGE_RETURN);
273 this.tState = STRING1;
274 }
275 else if (n === 0x74) {
276 this.appendStringChar(TAB);
277 this.tState = STRING1;
278 }
279 else if (n === 0x75) {
280 this.unicode = "";
281 this.tState = STRING3;
282 }
283 else {
284 return this.charError(buffer, i);
285 }
286 }
287 else if (this.tState === STRING3 || this.tState === STRING4 || this.tState === STRING5 || this.tState === STRING6) { // unicode hex codes
288 n = buffer[i];
289 // 0-9 A-F a-f
290 if ((n >= 0x30 && n < 0x40) || (n > 0x40 && n <= 0x46) || (n > 0x60 && n <= 0x66)) {
291 this.unicode += String.fromCharCode(n);
292 if (this.tState++ === STRING6) {
293 const intVal = parseInt(this.unicode, 16);
294 this.unicode = undefined;
295 if (this.highSurrogate !== undefined && intVal >= 0xDC00 && intVal < (0xDFFF + 1)) { //<56320,57343> - lowSurrogate
296 this.appendStringBuf(this.encode(String.fromCharCode(this.highSurrogate, intVal)));
297 this.highSurrogate = undefined;
298 }
299 else if (this.highSurrogate === undefined && intVal >= 0xD800 && intVal < (0xDBFF + 1)) { //<55296,56319> - highSurrogate
300 this.highSurrogate = intVal;
301 }
302 else {
303 if (this.highSurrogate !== undefined) {
304 this.appendStringBuf(this.encode(String.fromCharCode(this.highSurrogate)));
305 this.highSurrogate = undefined;
306 }
307 this.appendStringBuf(this.encode(String.fromCharCode(intVal)));
308 }
309 this.tState = STRING1;
310 }
311 }
312 else {
313 return this.charError(buffer, i);
314 }
315 }
316 else if (this.tState === NUMBER1 || this.tState === NUMBER3) {
317 n = buffer[i];
318 switch (n) {
319 case 0x30: // 0
320 case 0x31: // 1
321 case 0x32: // 2
322 case 0x33: // 3
323 case 0x34: // 4
324 case 0x35: // 5
325 case 0x36: // 6
326 case 0x37: // 7
327 case 0x38: // 8
328 case 0x39: // 9
329 case 0x2e: // .
330 case 0x65: // e
331 case 0x45: // E
332 case 0x2b: // +
333 case 0x2d: // -
334 this.string += String.fromCharCode(n);
335 this.tState = NUMBER3;
336 break;
337 default:
338 this.tState = START;
339 const error = this.numberReviver(this.string);
340 if (error) {
341 return error;
342 }
343 this.offset += this.string.length - 1;
344 this.string = undefined;
345 i--;
346 break;
347 }
348 }
349 else if (this.tState === TRUE1) { // r
350 if (buffer[i] === 0x72) {
351 this.tState = TRUE2;
352 }
353 else {
354 return this.charError(buffer, i);
355 }
356 }
357 else if (this.tState === TRUE2) { // u
358 if (buffer[i] === 0x75) {
359 this.tState = TRUE3;
360 }
361 else {
362 return this.charError(buffer, i);
363 }
364 }
365 else if (this.tState === TRUE3) { // e
366 if (buffer[i] === 0x65) {
367 this.tState = START;
368 this.onToken(TRUE, true);
369 this.offset += 3;
370 }
371 else {
372 return this.charError(buffer, i);
373 }
374 }
375 else if (this.tState === FALSE1) { // a
376 if (buffer[i] === 0x61) {
377 this.tState = FALSE2;
378 }
379 else {
380 return this.charError(buffer, i);
381 }
382 }
383 else if (this.tState === FALSE2) { // l
384 if (buffer[i] === 0x6c) {
385 this.tState = FALSE3;
386 }
387 else {
388 return this.charError(buffer, i);
389 }
390 }
391 else if (this.tState === FALSE3) { // s
392 if (buffer[i] === 0x73) {
393 this.tState = FALSE4;
394 }
395 else {
396 return this.charError(buffer, i);
397 }
398 }
399 else if (this.tState === FALSE4) { // e
400 if (buffer[i] === 0x65) {
401 this.tState = START;
402 this.onToken(FALSE, false);
403 this.offset += 4;
404 }
405 else {
406 return this.charError(buffer, i);
407 }
408 }
409 else if (this.tState === NULL1) { // u
410 if (buffer[i] === 0x75) {
411 this.tState = NULL2;
412 }
413 else {
414 return this.charError(buffer, i);
415 }
416 }
417 else if (this.tState === NULL2) { // l
418 if (buffer[i] === 0x6c) {
419 this.tState = NULL3;
420 }
421 else {
422 return this.charError(buffer, i);
423 }
424 }
425 else if (this.tState === NULL3) { // l
426 if (buffer[i] === 0x6c) {
427 this.tState = START;
428 this.onToken(NULL, null);
429 this.offset += 3;
430 }
431 else {
432 return this.charError(buffer, i);
433 }
434 }
435 }
436 }
437 parseError(token, value) {
438 this.tState = STOP;
439 this.onError(new Error("Unexpected " + Parser.toknam(token) + (value ? ("(" + JSON.stringify(value) + ")") : "") + " in state " + Parser.toknam(this.state)));
440 }
441 push() {
442 this.stack.push({ value: this.value, key: this.key, mode: this.mode });
443 }
444 pop() {
445 const value = this.value;
446 const parent = this.stack.pop();
447 this.value = parent.value;
448 this.key = parent.key;
449 this.mode = parent.mode;
450 this.emit(value);
451 if (!this.mode) {
452 this.state = VALUE;
453 }
454 }
455 emit(value) {
456 if (this.mode) {
457 this.state = COMMA;
458 }
459 this.onValue(value);
460 }
461 onValue(value) {
462 // Override me
463 }
464 onToken(token, value) {
465 if (this.state === VALUE) {
466 if (token === STRING || token === NUMBER || token === TRUE || token === FALSE || token === NULL) {
467 if (this.value) {
468 this.value[this.key] = value;
469 }
470 this.emit(value);
471 }
472 else if (token === LEFT_BRACE) {
473 this.push();
474 if (this.value) {
475 this.value = this.value[this.key] = {};
476 }
477 else {
478 this.value = {};
479 }
480 this.key = undefined;
481 this.state = KEY;
482 this.mode = OBJECT;
483 }
484 else if (token === LEFT_BRACKET) {
485 this.push();
486 if (this.value) {
487 this.value = this.value[this.key] = [];
488 }
489 else {
490 this.value = [];
491 }
492 this.key = 0;
493 this.mode = ARRAY;
494 this.state = VALUE;
495 }
496 else if (token === RIGHT_BRACE) {
497 if (this.mode === OBJECT) {
498 this.pop();
499 }
500 else {
501 return this.parseError(token, value);
502 }
503 }
504 else if (token === RIGHT_BRACKET) {
505 if (this.mode === ARRAY) {
506 this.pop();
507 }
508 else {
509 return this.parseError(token, value);
510 }
511 }
512 else {
513 return this.parseError(token, value);
514 }
515 }
516 else if (this.state === KEY) {
517 if (token === STRING) {
518 this.key = value;
519 this.state = COLON;
520 }
521 else if (token === RIGHT_BRACE) {
522 this.pop();
523 }
524 else {
525 return this.parseError(token, value);
526 }
527 }
528 else if (this.state === COLON) {
529 if (token === COLON) {
530 this.state = VALUE;
531 }
532 else {
533 return this.parseError(token, value);
534 }
535 }
536 else if (this.state === COMMA) {
537 if (token === COMMA) {
538 if (this.mode === ARRAY) {
539 this.key++;
540 this.state = VALUE;
541 }
542 else if (this.mode === OBJECT) {
543 this.state = KEY;
544 }
545 }
546 else if (token === RIGHT_BRACKET && this.mode === ARRAY || token === RIGHT_BRACE && this.mode === OBJECT) {
547 this.pop();
548 }
549 else {
550 return this.parseError(token, value);
551 }
552 }
553 else {
554 return this.parseError(token, value);
555 }
556 }
557 // Override to implement your own number reviver.
558 // Any value returned is treated as error and will interrupt parsing.
559 numberReviver(text) {
560 const result = Number(text);
561 if (isNaN(result)) {
562 return this.charError(buffer, i);
563 }
564 if ((text.match(/[0-9]+/) == text) && (result.toString() != text)) {
565 // Long string of digits which is an ID string and not valid and/or safe JavaScript integer Number
566 this.onToken(STRING, text);
567 }
568 else {
569 this.onToken(NUMBER, result);
570 }
571 }
572}
573exports.JSONParser = Parser;
574Parser.C = C;
575exports.default = Parser;
576//# sourceMappingURL=json-parser.js.map
\No newline at end of file