quick-json5 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/LICENSE.md +7 -0
  2. package/README.md +66 -0
  3. package/dist/exports/Exports.d.ts +6 -0
  4. package/dist/exports/Exports.d.ts.map +1 -0
  5. package/dist/exports/Exports.js +6 -0
  6. package/dist/exports/Exports.js.map +1 -0
  7. package/dist/json/JsonEncoder.d.ts +3 -0
  8. package/dist/json/JsonEncoder.d.ts.map +1 -0
  9. package/dist/json/JsonEncoder.js +147 -0
  10. package/dist/json/JsonEncoder.js.map +1 -0
  11. package/dist/json/JsonParser.d.ts +3 -0
  12. package/dist/json/JsonParser.d.ts.map +1 -0
  13. package/dist/json/JsonParser.js +378 -0
  14. package/dist/json/JsonParser.js.map +1 -0
  15. package/dist/json5/Json5Encoder.d.ts +3 -0
  16. package/dist/json5/Json5Encoder.d.ts.map +1 -0
  17. package/dist/json5/Json5Encoder.js +155 -0
  18. package/dist/json5/Json5Encoder.js.map +1 -0
  19. package/dist/json5/Json5Parser.d.ts +3 -0
  20. package/dist/json5/Json5Parser.d.ts.map +1 -0
  21. package/dist/json5/Json5Parser.js +781 -0
  22. package/dist/json5/Json5Parser.js.map +1 -0
  23. package/dist/tests/Json5EncoderTest.d.ts +2 -0
  24. package/dist/tests/Json5EncoderTest.d.ts.map +1 -0
  25. package/dist/tests/Json5EncoderTest.js +44 -0
  26. package/dist/tests/Json5EncoderTest.js.map +1 -0
  27. package/dist/tests/Json5ParserTests.d.ts +3 -0
  28. package/dist/tests/Json5ParserTests.d.ts.map +1 -0
  29. package/dist/tests/Json5ParserTests.js +211 -0
  30. package/dist/tests/Json5ParserTests.js.map +1 -0
  31. package/dist/tests/JsonEncoderTests.d.ts +2 -0
  32. package/dist/tests/JsonEncoderTests.d.ts.map +1 -0
  33. package/dist/tests/JsonEncoderTests.js +44 -0
  34. package/dist/tests/JsonEncoderTests.js.map +1 -0
  35. package/dist/tests/JsonParserTests.d.ts +3 -0
  36. package/dist/tests/JsonParserTests.d.ts.map +1 -0
  37. package/dist/tests/JsonParserTests.js +140 -0
  38. package/dist/tests/JsonParserTests.js.map +1 -0
  39. package/dist/tests/Test.d.ts +2 -0
  40. package/dist/tests/Test.d.ts.map +1 -0
  41. package/dist/tests/Test.js +9 -0
  42. package/dist/tests/Test.js.map +1 -0
  43. package/dist/types/Types.d.ts +8 -0
  44. package/dist/types/Types.d.ts.map +1 -0
  45. package/dist/types/Types.js +2 -0
  46. package/dist/types/Types.js.map +1 -0
  47. package/dist/unused/Unused.d.ts +2 -0
  48. package/dist/unused/Unused.d.ts.map +1 -0
  49. package/dist/unused/Unused.js +47 -0
  50. package/dist/unused/Unused.js.map +1 -0
  51. package/dist/utilities/JsonParseError.d.ts +7 -0
  52. package/dist/utilities/JsonParseError.d.ts.map +1 -0
  53. package/dist/utilities/JsonParseError.js +10 -0
  54. package/dist/utilities/JsonParseError.js.map +1 -0
  55. package/dist/utilities/ObjectUtilities.d.ts +2 -0
  56. package/dist/utilities/ObjectUtilities.d.ts.map +1 -0
  57. package/dist/utilities/ObjectUtilities.js +142 -0
  58. package/dist/utilities/ObjectUtilities.js.map +1 -0
  59. package/dist/utilities/TypedArray.d.ts +4 -0
  60. package/dist/utilities/TypedArray.d.ts.map +1 -0
  61. package/dist/utilities/TypedArray.js +2 -0
  62. package/dist/utilities/TypedArray.js.map +1 -0
  63. package/dist/utilities/Utilities.d.ts +17 -0
  64. package/dist/utilities/Utilities.d.ts.map +1 -0
  65. package/dist/utilities/Utilities.js +97 -0
  66. package/dist/utilities/Utilities.js.map +1 -0
  67. package/package.json +38 -0
  68. package/src/exports/Exports.ts +7 -0
  69. package/src/json/JsonEncoder.ts +180 -0
  70. package/src/json/JsonParser.ts +491 -0
  71. package/src/json5/Json5Encoder.ts +189 -0
  72. package/src/json5/Json5Parser.ts +959 -0
  73. package/src/tests/Json5EncoderTest.ts +51 -0
  74. package/src/tests/Json5ParserTests.ts +237 -0
  75. package/src/tests/JsonEncoderTests.ts +51 -0
  76. package/src/tests/JsonParserTests.ts +159 -0
  77. package/src/tests/Test.ts +13 -0
  78. package/src/types/Types.ts +9 -0
  79. package/src/unused/Unused.ts +51 -0
  80. package/src/utilities/JsonParseError.ts +7 -0
  81. package/src/utilities/ObjectUtilities.ts +184 -0
  82. package/src/utilities/TypedArray.ts +39 -0
  83. package/src/utilities/Utilities.ts +130 -0
  84. package/tsconfig.json +55 -0
package/LICENSE.md ADDED
@@ -0,0 +1,7 @@
1
+ Copyright (c) 2026 Rotem Dan
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,66 @@
1
+ # Quick JSON5
2
+
3
+ Fast JSON5 parser and serializer.
4
+
5
+ * Written in pure TypeScript (JavaScript)
6
+ * Parses up to 10x faster than reference JSON5 implementation
7
+ * Tested for 100% compliance with the official [JSON5 specification](https://spec.json5.org/)
8
+ * Fully supports `replacer` and `space` properties in `stringify`, and `reviver` in `parse` (including the new `context` callback argument)
9
+ * Support several extensions to the JSON5 standard (optional)
10
+ * Improved validation and error reporting. Errors embed machine-readable positional metadata
11
+ * No external dependencies
12
+
13
+ ## Support for extended numeric literals, beyond JSON5 (optional)
14
+
15
+ Parser also supports additional numeric literal syntax added in later versions of ECMAScript:
16
+
17
+ * Octal literals (`0o1234567`), and binary literals (`0b1001011`). Introduced in ES2015
18
+ * `BigInt` numeric literals (`123412341234n`). Introduced in ES2020
19
+ * Underscore separators in numeric literals (`123_456_789`, `123.456_789`, `0b0110_1101`). Introduced in ES2021
20
+
21
+ Enabling these extensions requires the `enableExtensions` option to be set to `true` in the `options` argument for `parse` and `stringify`.
22
+
23
+ ## Extra: Plain JSON parser and serializer
24
+
25
+ As an extra, also includes an optimized pure JavaScript JSON parser and serializer, with performance approaching to the native `JSON.parse` and `JSON.stringify`.
26
+
27
+ The alternative parser can be useful when detailed metadata is required when handling errors. For example when implementing a syntax checker that needs to highlight the location of errors in the JSON file.
28
+
29
+ You can also use the source code as a starting point for implementing various custom JSON extensions, like adding support for comments (AKA "JSON with comments").
30
+
31
+ ## Usage examples
32
+
33
+ Installation:
34
+ ```
35
+ npm install quick-json5
36
+ ```
37
+
38
+ JSON5 serializer and parser:
39
+ ```ts
40
+ import * as JSON5 from 'quick-json5'
41
+
42
+ const json5Str = JSON5.stringify({ 'hello': 123 })
43
+ const parsedObject = JSON5.parse(json5Str)
44
+ ```
45
+
46
+ JSON5 serializer and parser with extensions enabled:
47
+ ```ts
48
+ import * as JSON5 from 'quick-json5'
49
+
50
+ const options = { enableExtensions: true }
51
+
52
+ const json5Str = JSON5.stringify({ 'hello': 12356123456123456n }, undefined, 4, options)
53
+ const parsedObject = JSON5.parse(json5Str, undefined, options)
54
+ ```
55
+
56
+ Plain JSON serializer and parser:
57
+ ```ts
58
+ import * as JSON5 from 'quick-json5'
59
+
60
+ const jsonStr = JSON5.stringifyJSON({ 'hello': 123 })
61
+ const parsedObject = JSON5.parseJSON(jsonStr)
62
+ ```
63
+
64
+ ## License
65
+
66
+ MIT
@@ -0,0 +1,6 @@
1
+ export * from '../json5/Json5Encoder.js';
2
+ export * from '../json5/Json5Parser.js';
3
+ export * from '../json/JsonEncoder.js';
4
+ export * from '../json/JsonParser.js';
5
+ export * from '../types/Types.js';
6
+ //# sourceMappingURL=Exports.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Exports.d.ts","sourceRoot":"","sources":["../../src/exports/Exports.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAA;AACxC,cAAc,yBAAyB,CAAA;AAEvC,cAAc,wBAAwB,CAAA;AACtC,cAAc,uBAAuB,CAAA;AAErC,cAAc,mBAAmB,CAAA"}
@@ -0,0 +1,6 @@
1
+ export * from '../json5/Json5Encoder.js';
2
+ export * from '../json5/Json5Parser.js';
3
+ export * from '../json/JsonEncoder.js';
4
+ export * from '../json/JsonParser.js';
5
+ export * from '../types/Types.js';
6
+ //# sourceMappingURL=Exports.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Exports.js","sourceRoot":"","sources":["../../src/exports/Exports.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAA;AACxC,cAAc,yBAAyB,CAAA;AAEvC,cAAc,wBAAwB,CAAA;AACtC,cAAc,uBAAuB,CAAA;AAErC,cAAc,mBAAmB,CAAA"}
@@ -0,0 +1,3 @@
1
+ import { JsonReplacerType } from '../types/Types.js';
2
+ export declare function stringifyJSON(obj: any, replacer?: JsonReplacerType, space?: number): any;
3
+ //# sourceMappingURL=JsonEncoder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"JsonEncoder.d.ts","sourceRoot":"","sources":["../../src/json/JsonEncoder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwB,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AAG1E,wBAAgB,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,CAAC,EAAE,gBAAgB,EAAE,KAAK,SAAI,OAsI7E"}
@@ -0,0 +1,147 @@
1
+ import { charCodeToHex } from '../utilities/Utilities.js';
2
+ export function stringifyJSON(obj, replacer, space = 0) {
3
+ let replacerFunc;
4
+ if (replacer != null) {
5
+ if (Array.isArray(replacer)) {
6
+ const replacerSet = new Set();
7
+ for (const key of replacer) {
8
+ replacerSet.add(String(key));
9
+ }
10
+ replacerFunc = (key, value) => {
11
+ if (replacerSet.has(key)) {
12
+ return value;
13
+ }
14
+ };
15
+ }
16
+ else if (typeof replacer === 'function') {
17
+ replacerFunc = replacer;
18
+ }
19
+ else {
20
+ throw new TypeError('Invalid replacer argument.');
21
+ }
22
+ }
23
+ const baseIndentString = ' '.repeat(space);
24
+ let currentIndentLevel = 0;
25
+ function getIndentString() {
26
+ if (space === 0) {
27
+ return '';
28
+ }
29
+ const spaces = baseIndentString.repeat(currentIndentLevel);
30
+ return `\n${spaces}`;
31
+ }
32
+ function encode(obj) {
33
+ if (obj === undefined) {
34
+ return '';
35
+ }
36
+ const typeofObj = typeof obj;
37
+ if (obj === null || typeofObj === 'boolean') {
38
+ return String(obj);
39
+ }
40
+ if (typeofObj === 'number') {
41
+ if (isNaN(obj) || obj === Infinity || obj === -Infinity) {
42
+ throw new Error(`NaN and Infinity can't be encoded in JSON`);
43
+ }
44
+ return String(obj);
45
+ }
46
+ if (typeofObj === 'string') {
47
+ return `"${escapeStringIfNeeded(obj)}"`;
48
+ }
49
+ if (typeof obj !== 'object') {
50
+ throw new Error(`Type '${typeof obj}' cannot be encoded in JSON`);
51
+ }
52
+ if (typeof obj.toJSON === 'function') {
53
+ return obj.toJSON();
54
+ }
55
+ if (Array.isArray(obj)) {
56
+ let str = '[';
57
+ let isFirstElement = true;
58
+ for (let i = 0; i < obj.length; i++) {
59
+ const element = obj[i];
60
+ if (element === undefined) {
61
+ continue;
62
+ }
63
+ currentIndentLevel += 1;
64
+ const saparatingComma = isFirstElement === true ? '' : ',';
65
+ const indentString = getIndentString();
66
+ const encodedElement = encode(element);
67
+ str += `${saparatingComma}${indentString}${encodedElement}`;
68
+ currentIndentLevel -= 1;
69
+ isFirstElement = false;
70
+ }
71
+ str += `${getIndentString()}]`;
72
+ return str;
73
+ }
74
+ else {
75
+ let str = '{';
76
+ let isFirstEntry = true;
77
+ for (const key in obj) {
78
+ let value = obj[key];
79
+ if (replacerFunc !== undefined) {
80
+ value = replacerFunc(key, value);
81
+ }
82
+ if (value === undefined) {
83
+ continue;
84
+ }
85
+ currentIndentLevel += 1;
86
+ const saparatingComma = isFirstEntry === true ? '' : ',';
87
+ const indentString = getIndentString();
88
+ const escapedKey = escapeStringIfNeeded(key);
89
+ const spaceAfterColons = space > 0 ? ' ' : '';
90
+ const encodedValue = encode(value);
91
+ str += `${saparatingComma}${indentString}"${escapedKey}":${spaceAfterColons}${encodedValue}`;
92
+ currentIndentLevel -= 1;
93
+ isFirstEntry = false;
94
+ }
95
+ str += `${getIndentString()}}`;
96
+ return str;
97
+ }
98
+ }
99
+ return encode(obj);
100
+ }
101
+ function escapeStringIfNeeded(str) {
102
+ if (!/["\\\x00-\x1F]/.test(str)) {
103
+ return str;
104
+ }
105
+ else {
106
+ return escapeString(str);
107
+ }
108
+ }
109
+ function escapeString(str) {
110
+ let escapedStr = '';
111
+ for (let i = 0; i < str.length; i++) {
112
+ const charCode = str.charCodeAt(i);
113
+ if (charCode >= 32) {
114
+ if (charCode === 34) { // '"'
115
+ escapedStr += '\\"';
116
+ }
117
+ else if (charCode === 92) { // '\\'
118
+ escapedStr += '\\\\';
119
+ }
120
+ else {
121
+ escapedStr += String.fromCharCode(charCode);
122
+ }
123
+ }
124
+ else {
125
+ if (charCode === 10) { // '\n'
126
+ escapedStr += '\\n';
127
+ }
128
+ else if (charCode === 13) { // '\r'
129
+ escapedStr += '\\r';
130
+ }
131
+ else if (charCode === 9) { // '\t'
132
+ escapedStr += '\\t';
133
+ }
134
+ else if (charCode === 12) { // '\f'
135
+ escapedStr += '\\f';
136
+ }
137
+ else if (charCode === 8) { // '\b'
138
+ escapedStr += '\\b';
139
+ }
140
+ else {
141
+ escapedStr += `\\u${charCodeToHex(charCode)}`;
142
+ }
143
+ }
144
+ }
145
+ return escapedStr;
146
+ }
147
+ //# sourceMappingURL=JsonEncoder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"JsonEncoder.js","sourceRoot":"","sources":["../../src/json/JsonEncoder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { JsonReviverFunction } from '../types/Types.js';
2
+ export declare function parseJSON(jsonString: string, reviver?: JsonReviverFunction): any;
3
+ //# sourceMappingURL=JsonParser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"JsonParser.d.ts","sourceRoot":"","sources":["../../src/json/JsonParser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AAIvD,wBAAgB,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,mBAAmB,OAse1E"}
@@ -0,0 +1,378 @@
1
+ import { JsonParserError } from '../utilities/JsonParseError.js';
2
+ import { getPositionInfo, hexCharCodeToNumber, positivePowersOf10 } from '../utilities/Utilities.js';
3
+ export function parseJSON(jsonString, reviver) {
4
+ if (typeof jsonString !== 'string') {
5
+ throw new TypeError(`Given JSON string argument is not a string.`);
6
+ }
7
+ if (reviver !== undefined && typeof reviver !== 'function') {
8
+ throw new TypeError(`Reviver can only be a function.`);
9
+ }
10
+ let readPosition = 0;
11
+ function parse(initialCharCode) {
12
+ ////////////////////////////////////////////////////////////////////////////////////////////
13
+ // Parse string
14
+ ////////////////////////////////////////////////////////////////////////////////////////////
15
+ if (initialCharCode === 34) { // '"' for string start
16
+ readPosition += 1;
17
+ // Try to quickly parse the string for the common case where there are no escape patterns
18
+ {
19
+ const match = jsonString.substring(readPosition).match(/^[^\\\x00-\x1F]*?"/);
20
+ if (match !== null) {
21
+ const matchString = match[0];
22
+ const str = matchString.substring(0, matchString.length - 1);
23
+ readPosition += matchString.length;
24
+ return str;
25
+ }
26
+ }
27
+ // Fallback to slower method if an escape pattern is found or string is unterminated.
28
+ let decodedString = '';
29
+ while (true) {
30
+ const charCode = readCharCodeAndAdvance();
31
+ if (charCode === 34) { // '"'
32
+ break;
33
+ }
34
+ else if (charCode === 92) { // '\\'
35
+ const escapedCharCharcode = readCharCodeAndAdvance();
36
+ if (escapedCharCharcode <= 92) {
37
+ if (escapedCharCharcode === 34 || escapedCharCharcode === 92 || escapedCharCharcode === 47) { // '"', '\' and '/'
38
+ decodedString += String.fromCharCode(escapedCharCharcode);
39
+ }
40
+ else {
41
+ const positionInfo = getInfoForPosition(readPosition - 2);
42
+ throw new JsonParserError(`Invalid escaped character '${String.fromCharCode(escapedCharCharcode)}' in escape sequence starting at ${positionInfo.positionString}.`, positionInfo);
43
+ }
44
+ }
45
+ else if (escapedCharCharcode === 110) { // 'n'
46
+ decodedString += '\n';
47
+ }
48
+ else if (escapedCharCharcode === 114) { // 'r'
49
+ decodedString += '\r';
50
+ }
51
+ else if (escapedCharCharcode === 116) { // 't'
52
+ decodedString += '\t';
53
+ }
54
+ else if (escapedCharCharcode === 117) { // 'u'
55
+ if (readPosition + 4 > jsonString.length) {
56
+ const positionInfo = getInfoForCurrentReadPosition();
57
+ throw new JsonParserError(`Unterminated hexadecimal sequence. Expected 4 hexadecimal characters at ${positionInfo.positionString}.`, positionInfo);
58
+ }
59
+ const codePoint = hexCharCodeToNumber(jsonString.charCodeAt(readPosition + 0)) << 12 |
60
+ hexCharCodeToNumber(jsonString.charCodeAt(readPosition + 1)) << 8 |
61
+ hexCharCodeToNumber(jsonString.charCodeAt(readPosition + 2)) << 4 |
62
+ hexCharCodeToNumber(jsonString.charCodeAt(readPosition + 3));
63
+ decodedString += String.fromCharCode(codePoint);
64
+ readPosition += 4;
65
+ }
66
+ else if (escapedCharCharcode === 102) { // 'f'
67
+ decodedString += '\f';
68
+ }
69
+ else if (escapedCharCharcode === 98) { // 'b'
70
+ decodedString += '\b';
71
+ }
72
+ else {
73
+ const positionInfo = getInfoForPosition(readPosition - 2);
74
+ throw new JsonParserError(`Invalid escaped character '${String.fromCharCode(escapedCharCharcode)}' in escape sequence at ${positionInfo.positionString}.`, positionInfo);
75
+ }
76
+ }
77
+ else if (charCode === undefined) {
78
+ const positionInfo = getInfoForPosition(jsonString.length - 1);
79
+ throw new JsonParserError(`Unterminated string literal.`, positionInfo);
80
+ }
81
+ else if (charCode < 32) {
82
+ const positionInfo = getInfoForPosition(readPosition - 1);
83
+ throw new JsonParserError(`Invalid unescaped control character (${charCode}) at ${positionInfo.positionString}.`, positionInfo);
84
+ }
85
+ else {
86
+ decodedString += String.fromCharCode(charCode);
87
+ }
88
+ }
89
+ return decodedString;
90
+ }
91
+ ////////////////////////////////////////////////////////////////////////////////////////////
92
+ // Parse number
93
+ ////////////////////////////////////////////////////////////////////////////////////////////
94
+ if (initialCharCode < 91) { // '0'-'9' or '-' for number start
95
+ let numberStringStartPosition = readPosition;
96
+ let charCode = initialCharCode;
97
+ let isNegative;
98
+ if (charCode === 45) { // '-'
99
+ isNegative = true;
100
+ charCode = advanceAndReadCharCode();
101
+ }
102
+ else {
103
+ isNegative = false;
104
+ }
105
+ let concatenatedInteger = 0;
106
+ // Parse integer part
107
+ {
108
+ const digitsStartPosition = readPosition;
109
+ while (charCode >= 48 && charCode <= 57) { // '0'-'9'
110
+ const digitValue = charCode - 48;
111
+ concatenatedInteger = (concatenatedInteger * 10) + digitValue;
112
+ charCode = advanceAndReadCharCode();
113
+ }
114
+ const integerPartDigitCount = readPosition - digitsStartPosition;
115
+ if (integerPartDigitCount === 0) {
116
+ const positionInfo = getInfoForCurrentReadPosition();
117
+ throw new JsonParserError(`Invalid character '${String.fromCharCode(charCode)}' at ${positionInfo.positionString}.`, positionInfo);
118
+ }
119
+ if (integerPartDigitCount > 1 && jsonString[digitsStartPosition] === '0') {
120
+ const positionInfo = getInfoForPosition(digitsStartPosition);
121
+ throw new JsonParserError(`Invalid leading zero found in number literal at ${positionInfo.positionString}.`, positionInfo);
122
+ }
123
+ }
124
+ let fractionalDigitCount = 0;
125
+ // Parse fractional part
126
+ if (charCode === 46) { // '.'
127
+ charCode = advanceAndReadCharCode();
128
+ const digitsStartPosition = readPosition;
129
+ while (charCode >= 48 && charCode <= 57) { // '0'-'9'
130
+ const digitValue = charCode - 48;
131
+ concatenatedInteger = (concatenatedInteger * 10) + digitValue;
132
+ charCode = advanceAndReadCharCode();
133
+ }
134
+ fractionalDigitCount = readPosition - digitsStartPosition;
135
+ if (fractionalDigitCount === 0) {
136
+ const positionInfo = getInfoForCurrentReadPosition();
137
+ throw new JsonParserError(`Invalid character '${String.fromCharCode(charCode)}' at ${positionInfo.positionString}. Exepcted at least one decimal digit following '.'.`, positionInfo);
138
+ }
139
+ }
140
+ let exponent = 0;
141
+ // Parse exponent part
142
+ if (charCode === 101 || charCode === 69) { // 'e' or 'E'
143
+ charCode = advanceAndReadCharCode();
144
+ let isNegativeExponent = false;
145
+ if (charCode === 43) { // '+'
146
+ charCode = advanceAndReadCharCode();
147
+ }
148
+ else if (charCode === 45) { // '-'
149
+ isNegativeExponent = true;
150
+ charCode = advanceAndReadCharCode();
151
+ }
152
+ let exponentDigitsStartPosition = readPosition;
153
+ while (charCode >= 48 && charCode <= 57) { // '0'-'9'
154
+ const digitValue = charCode - 48;
155
+ exponent = (exponent * 10) + digitValue;
156
+ charCode = advanceAndReadCharCode();
157
+ }
158
+ if (readPosition === exponentDigitsStartPosition) {
159
+ const positionInfo = getInfoForCurrentReadPosition();
160
+ throw new JsonParserError(`Invalid character '${String.fromCharCode(charCode)}' at ${positionInfo.positionString}. Exepcted at least one exponent digit.`, positionInfo);
161
+ }
162
+ if (isNegativeExponent) {
163
+ exponent = -exponent;
164
+ }
165
+ }
166
+ const adjustedExponent = exponent - fractionalDigitCount;
167
+ // Use fast method for cases where the number can be parsed efficiently,
168
+ // or fall back to slower method if not possible.
169
+ if (concatenatedInteger < 2 ** 53 && adjustedExponent >= -22 && adjustedExponent <= 22) {
170
+ let parsedNumber = concatenatedInteger;
171
+ if (adjustedExponent > 0) {
172
+ parsedNumber *= positivePowersOf10[adjustedExponent];
173
+ }
174
+ else if (adjustedExponent < 0) {
175
+ parsedNumber /= positivePowersOf10[-adjustedExponent];
176
+ }
177
+ if (isNegative) {
178
+ return -parsedNumber;
179
+ }
180
+ else {
181
+ return parsedNumber;
182
+ }
183
+ }
184
+ else {
185
+ return Number(jsonString.substring(numberStringStartPosition, readPosition));
186
+ }
187
+ }
188
+ ////////////////////////////////////////////////////////////////////////////////////////////
189
+ // Parse 'true' literal
190
+ ////////////////////////////////////////////////////////////////////////////////////////////
191
+ if (initialCharCode === 116) { // 't' for true
192
+ if (jsonString.charCodeAt(readPosition + 1) !== 114 || // 'r'
193
+ jsonString.charCodeAt(readPosition + 2) !== 117 || // 'u'
194
+ jsonString.charCodeAt(readPosition + 3) !== 101) { // 'e'
195
+ const positionInfo = getInfoForCurrentReadPosition();
196
+ throw new JsonParserError(`Expected 'true' at ${positionInfo.positionString}.`, positionInfo);
197
+ }
198
+ readPosition += 4;
199
+ return true;
200
+ }
201
+ ////////////////////////////////////////////////////////////////////////////////////////////
202
+ // Parse 'false' literal
203
+ ////////////////////////////////////////////////////////////////////////////////////////////
204
+ if (initialCharCode === 102) { // 'f' for false
205
+ if (jsonString.charCodeAt(readPosition + 1) !== 97 || // 'a'
206
+ jsonString.charCodeAt(readPosition + 2) !== 108 || // 'l'
207
+ jsonString.charCodeAt(readPosition + 3) !== 115 || // 's'
208
+ jsonString.charCodeAt(readPosition + 4) !== 101) { // 'e'
209
+ const positionInfo = getInfoForCurrentReadPosition();
210
+ throw new JsonParserError(`Expected 'false' at ${positionInfo.positionString}.`, positionInfo);
211
+ }
212
+ readPosition += 5;
213
+ return false;
214
+ }
215
+ ////////////////////////////////////////////////////////////////////////////////////////////
216
+ // Parse 'null' literal
217
+ ////////////////////////////////////////////////////////////////////////////////////////////
218
+ if (initialCharCode === 110) { // 'n' for null
219
+ if (jsonString.charCodeAt(readPosition + 1) !== 117 || // 'u'
220
+ jsonString.charCodeAt(readPosition + 2) !== 108 || // 'l'
221
+ jsonString.charCodeAt(readPosition + 3) !== 108) { // 'l'
222
+ const positionInfo = getInfoForCurrentReadPosition();
223
+ throw new JsonParserError(`Expected 'null' at ${positionInfo.positionString}.`, positionInfo);
224
+ }
225
+ readPosition += 4;
226
+ return null;
227
+ }
228
+ ////////////////////////////////////////////////////////////////////////////////////////////
229
+ // Parse array
230
+ ////////////////////////////////////////////////////////////////////////////////////////////
231
+ if (initialCharCode === 91) { // '[' for array start
232
+ readPosition += 1;
233
+ let charCode = skipToNextReadableCharCode();
234
+ if (charCode === 93) { // ']' for array end
235
+ readPosition += 1;
236
+ return [];
237
+ }
238
+ const arr = [];
239
+ while (true) {
240
+ const elementStartPosition = readPosition;
241
+ let element = parse(charCode);
242
+ if (reviver !== undefined) {
243
+ element = applyReviver('', element, elementStartPosition);
244
+ }
245
+ arr.push(element);
246
+ charCode = skipToNextReadableCharCode();
247
+ if (charCode === 44) { // ',' for comma
248
+ readPosition += 1;
249
+ charCode = skipToNextReadableCharCode();
250
+ continue;
251
+ }
252
+ if (charCode === 93) { // ']' for array end
253
+ readPosition += 1;
254
+ break;
255
+ }
256
+ {
257
+ const positionInfo = getInfoForCurrentReadPosition();
258
+ throw new JsonParserError(`Invalid character '${String.fromCharCode(charCode)}' in array expression at ${positionInfo.positionString}. Expected ',' or ']'.`, positionInfo);
259
+ }
260
+ }
261
+ return arr;
262
+ }
263
+ ////////////////////////////////////////////////////////////////////////////////////////////
264
+ // Parse object
265
+ ////////////////////////////////////////////////////////////////////////////////////////////
266
+ if (initialCharCode === 123) { // '{' for object start
267
+ readPosition += 1;
268
+ let charCode = skipToNextReadableCharCode();
269
+ if (charCode === 125) { // '}' for object end
270
+ readPosition += 1;
271
+ return {};
272
+ }
273
+ const obj = {};
274
+ while (true) {
275
+ if (charCode !== 34) {
276
+ const positionInfo = getInfoForCurrentReadPosition();
277
+ throw new JsonParserError(`Invalid character '${String.fromCharCode(charCode)}' at ${positionInfo.positionString}. Expected '"'.`, positionInfo);
278
+ }
279
+ const key = parse(charCode);
280
+ charCode = skipToNextReadableCharCode();
281
+ if (charCode !== 58) { // ':'
282
+ const positionInfo = getInfoForCurrentReadPosition();
283
+ throw new JsonParserError(`Invalid character '${String.fromCharCode(charCode)}' at ${positionInfo.positionString}. Expected ':'.`, positionInfo);
284
+ }
285
+ readPosition += 1;
286
+ charCode = skipToNextReadableCharCode();
287
+ const valueStartPosition = readPosition;
288
+ let value = parse(charCode);
289
+ if (reviver !== undefined) {
290
+ value = applyReviver(key, value, valueStartPosition, obj);
291
+ }
292
+ obj[key] = value;
293
+ const commaOrClosingBraceCharCode = skipToNextReadableCharCode();
294
+ if (commaOrClosingBraceCharCode === 44) { // ','
295
+ readPosition += 1;
296
+ charCode = skipToNextReadableCharCode();
297
+ continue;
298
+ }
299
+ if (commaOrClosingBraceCharCode === 125) { // '}'
300
+ readPosition += 1;
301
+ break;
302
+ }
303
+ {
304
+ const positionInfo = getInfoForCurrentReadPosition();
305
+ throw new JsonParserError(`Invalid character '${String.fromCharCode(commaOrClosingBraceCharCode)}' in object expression at ${positionInfo.positionString}. Expected ',' or '}'.`, positionInfo);
306
+ }
307
+ }
308
+ return obj;
309
+ }
310
+ {
311
+ const positionInfo = getInfoForCurrentReadPosition();
312
+ throw new JsonParserError(`Invalid character '${String.fromCharCode(initialCharCode)}' at ${positionInfo.positionString}.`, positionInfo);
313
+ }
314
+ }
315
+ function advanceAndReadCharCode() {
316
+ return jsonString.charCodeAt(++readPosition);
317
+ }
318
+ function readCharCodeAndAdvance() {
319
+ return jsonString.charCodeAt(readPosition++);
320
+ }
321
+ function skipWhitespace() {
322
+ while (readPosition < jsonString.length) {
323
+ const charCode = jsonString.charCodeAt(readPosition);
324
+ if (charCode > 32 || (charCode !== 32 && charCode !== 10 && charCode !== 13 && charCode !== 9)) {
325
+ return charCode;
326
+ }
327
+ readPosition += 1;
328
+ }
329
+ return undefined;
330
+ }
331
+ function skipToNextReadableCharCode() {
332
+ const nextCharCode = skipWhitespace();
333
+ if (nextCharCode === undefined) {
334
+ const positionInfo = getInfoForPosition(jsonString.length - 1);
335
+ throw new JsonParserError(`Unexpected termination of JSON input.`, positionInfo);
336
+ }
337
+ return nextCharCode;
338
+ }
339
+ function getInfoForCurrentReadPosition() {
340
+ return getInfoForPosition(readPosition);
341
+ }
342
+ function getInfoForPosition(position) {
343
+ return getPositionInfo(jsonString, position);
344
+ }
345
+ function applyReviver(key, value, valueStartPosition, thisArg) {
346
+ if (reviver === undefined) {
347
+ return;
348
+ }
349
+ let context;
350
+ if (typeof value !== 'object' || value === null) {
351
+ context = jsonString.substring(valueStartPosition, readPosition);
352
+ }
353
+ let result;
354
+ if (thisArg) {
355
+ result = reviver.call(thisArg, key, value, context);
356
+ }
357
+ else {
358
+ result = reviver(key, value, context);
359
+ }
360
+ return result;
361
+ }
362
+ // Parse the given string
363
+ {
364
+ const initialCharCode = skipToNextReadableCharCode();
365
+ const documentStartPosition = readPosition;
366
+ let result = parse(initialCharCode);
367
+ if (reviver !== undefined) {
368
+ result = applyReviver('', result, documentStartPosition);
369
+ }
370
+ const finalSkipResult = skipWhitespace();
371
+ if (finalSkipResult !== undefined) {
372
+ const positionInfo = getInfoForCurrentReadPosition();
373
+ throw new JsonParserError(`Unexpected trailing character(s) starting at ${positionInfo.positionString}.`, positionInfo);
374
+ }
375
+ return result;
376
+ }
377
+ }
378
+ //# sourceMappingURL=JsonParser.js.map