@@ -157,6 +157,38 @@ function _readExpNumber(context: JsonParserContext,
157
157
}
158
158
159
159
160
+ /**
161
+ * Read the hexa part of a 0xBADCAFE hexadecimal number.
162
+ * @private
163
+ */
164
+ function _readHexaNumber ( context : JsonParserContext ,
165
+ isNegative : boolean ,
166
+ start : Position ,
167
+ comments : ( JsonAstComment | JsonAstMultilineComment ) [ ] ) : JsonAstNumber {
168
+ // Read an hexadecimal number, until it's not hexadecimal.
169
+ let hexa = '' ;
170
+ const valid = '0123456789abcdefABCDEF' ;
171
+
172
+ for ( let ch = _peek ( context ) ; ch && valid . includes ( ch ) ; ch = _peek ( context ) ) {
173
+ // Add it to the hexa string.
174
+ hexa += ch ;
175
+ // Move the position of the context to the next character.
176
+ _next ( context ) ;
177
+ }
178
+
179
+ const value = Number . parseInt ( hexa , 16 ) ;
180
+
181
+ // We're done reading this number.
182
+ return {
183
+ kind : 'number' ,
184
+ start,
185
+ end : context . position ,
186
+ text : context . original . substring ( start . offset , context . position . offset ) ,
187
+ value : isNegative ? - value : value ,
188
+ comments,
189
+ } ;
190
+ }
191
+
160
192
/**
161
193
* Read a number from the context.
162
194
* @private
@@ -175,6 +207,21 @@ function _readNumber(context: JsonParserContext, comments = _readBlanks(context)
175
207
if ( str != '' ) {
176
208
throw new InvalidJsonCharacterException ( context ) ;
177
209
}
210
+ } else if ( char == 'I'
211
+ && ( str == '-' || str == '' || str == '+' )
212
+ && ( context . mode & JsonParseMode . NumberConstantsAllowed ) != 0 ) {
213
+ // Infinity?
214
+ // _token(context, 'I'); Already read.
215
+ _token ( context , 'n' ) ;
216
+ _token ( context , 'f' ) ;
217
+ _token ( context , 'i' ) ;
218
+ _token ( context , 'n' ) ;
219
+ _token ( context , 'i' ) ;
220
+ _token ( context , 't' ) ;
221
+ _token ( context , 'y' ) ;
222
+
223
+ str += 'Infinity' ;
224
+ break ;
178
225
} else if ( char == '0' ) {
179
226
if ( str == '0' || str == '-0' ) {
180
227
throw new InvalidJsonCharacterException ( context ) ;
@@ -184,29 +231,40 @@ function _readNumber(context: JsonParserContext, comments = _readBlanks(context)
184
231
if ( str == '0' || str == '-0' ) {
185
232
throw new InvalidJsonCharacterException ( context ) ;
186
233
}
234
+ } else if ( char == '+' && str == '' ) {
235
+ // Pass over.
187
236
} else if ( char == '.' ) {
188
237
if ( dotted ) {
189
238
throw new InvalidJsonCharacterException ( context ) ;
190
239
}
191
240
dotted = true ;
192
241
} else if ( char == 'e' || char == 'E' ) {
193
242
return _readExpNumber ( context , start , str + char , comments ) ;
243
+ } else if ( char == 'x' && ( str == '0' || str == '-0' )
244
+ && ( context . mode & JsonParseMode . HexadecimalNumberAllowed ) != 0 ) {
245
+ return _readHexaNumber ( context , str == '-0' , start , comments ) ;
194
246
} else {
195
- // We're done reading this number .
247
+ // We read one too many characters, so rollback the last character .
196
248
context . position = context . previous ;
197
-
198
- return {
199
- kind : 'number' ,
200
- start,
201
- end : context . position ,
202
- text : context . original . substring ( start . offset , context . position . offset ) ,
203
- value : Number . parseFloat ( str ) ,
204
- comments,
205
- } ;
249
+ break ;
206
250
}
207
251
208
252
str += char ;
209
253
}
254
+
255
+ // We're done reading this number.
256
+ if ( str . endsWith ( '.' ) && ( context . mode & JsonParseMode . HexadecimalNumberAllowed ) == 0 ) {
257
+ throw new InvalidJsonCharacterException ( context ) ;
258
+ }
259
+
260
+ return {
261
+ kind : 'number' ,
262
+ start,
263
+ end : context . position ,
264
+ text : context . original . substring ( start . offset , context . position . offset ) ,
265
+ value : Number . parseFloat ( str ) ,
266
+ comments,
267
+ } ;
210
268
}
211
269
212
270
@@ -224,8 +282,6 @@ function _readString(context: JsonParserContext, comments = _readBlanks(context)
224
282
if ( delim == '\'' ) {
225
283
throw new InvalidJsonCharacterException ( context ) ;
226
284
}
227
- } else if ( delim != '\'' && delim != '"' ) {
228
- throw new InvalidJsonCharacterException ( context ) ;
229
285
}
230
286
231
287
let str = '' ;
@@ -265,6 +321,15 @@ function _readString(context: JsonParserContext, comments = _readBlanks(context)
265
321
266
322
case undefined :
267
323
throw new UnexpectedEndOfInputException ( context ) ;
324
+
325
+ case '\n' :
326
+ // Only valid when multiline strings are allowed.
327
+ if ( ( context . mode & JsonParseMode . MultiLineStringAllowed ) == 0 ) {
328
+ throw new InvalidJsonCharacterException ( context ) ;
329
+ }
330
+ str += char ;
331
+ break ;
332
+
268
333
default :
269
334
throw new InvalidJsonCharacterException ( context ) ;
270
335
}
@@ -356,6 +421,31 @@ function _readNull(context: JsonParserContext,
356
421
}
357
422
358
423
424
+ /**
425
+ * Read the constant `NaN` from the context.
426
+ * @private
427
+ */
428
+ function _readNaN ( context : JsonParserContext ,
429
+ comments = _readBlanks ( context ) ) : JsonAstNumber {
430
+ const start = context . position ;
431
+
432
+ _token ( context , 'N' ) ;
433
+ _token ( context , 'a' ) ;
434
+ _token ( context , 'N' ) ;
435
+
436
+ const end = context . position ;
437
+
438
+ return {
439
+ kind : 'number' ,
440
+ start,
441
+ end,
442
+ text : context . original . substring ( start . offset , end . offset ) ,
443
+ value : NaN ,
444
+ comments : comments ,
445
+ } ;
446
+ }
447
+
448
+
359
449
/**
360
450
* Read an array of JSON values from the context.
361
451
* @private
@@ -638,11 +728,33 @@ function _readValue(context: JsonParserContext, comments = _readBlanks(context))
638
728
result = _readNumber ( context , comments ) ;
639
729
break ;
640
730
731
+ case '.' :
732
+ case '+' :
733
+ if ( ( context . mode & JsonParseMode . LaxNumberParsingAllowed ) == 0 ) {
734
+ throw new InvalidJsonCharacterException ( context ) ;
735
+ }
736
+ result = _readNumber ( context , comments ) ;
737
+ break ;
738
+
641
739
case '\'' :
642
740
case '"' :
643
741
result = _readString ( context , comments ) ;
644
742
break ;
645
743
744
+ case 'I' :
745
+ if ( ( context . mode & JsonParseMode . NumberConstantsAllowed ) == 0 ) {
746
+ throw new InvalidJsonCharacterException ( context ) ;
747
+ }
748
+ result = _readNumber ( context , comments ) ;
749
+ break ;
750
+
751
+ case 'N' :
752
+ if ( ( context . mode & JsonParseMode . NumberConstantsAllowed ) == 0 ) {
753
+ throw new InvalidJsonCharacterException ( context ) ;
754
+ }
755
+ result = _readNaN ( context , comments ) ;
756
+ break ;
757
+
646
758
case 't' :
647
759
result = _readTrue ( context , comments ) ;
648
760
break ;
@@ -681,10 +793,19 @@ export enum JsonParseMode {
681
793
SingleQuotesAllowed = 1 << 1 , // Allow single quoted strings.
682
794
IdentifierKeyNamesAllowed = 1 << 2 , // Allow identifiers as objectp properties.
683
795
TrailingCommasAllowed = 1 << 3 ,
796
+ HexadecimalNumberAllowed = 1 << 4 ,
797
+ MultiLineStringAllowed = 1 << 5 ,
798
+ LaxNumberParsingAllowed = 1 << 6 , // Allow `.` or `+` as the first character of a number.
799
+ NumberConstantsAllowed = 1 << 7 , // Allow -Infinity, Infinity and NaN.
684
800
685
801
Default = Strict ,
686
802
Loose = CommentsAllowed | SingleQuotesAllowed |
687
- IdentifierKeyNamesAllowed | TrailingCommasAllowed ,
803
+ IdentifierKeyNamesAllowed | TrailingCommasAllowed |
804
+ HexadecimalNumberAllowed | MultiLineStringAllowed |
805
+ LaxNumberParsingAllowed | NumberConstantsAllowed ,
806
+
807
+ Json = Strict ,
808
+ Json5 = Loose ,
688
809
}
689
810
690
811
0 commit comments