Skip to content

Commit 1dfaa61

Browse files
author
Fabrice Bellard
committed
improved compatibility of std.parseExtJSON() with JSON5
1 parent 8b2a124 commit 1dfaa61

File tree

3 files changed

+64
-16
lines changed

3 files changed

+64
-16
lines changed

doc/quickjs.texi

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -449,17 +449,20 @@ optional properties:
449449

450450
@item parseExtJSON(str)
451451

452-
Parse @code{str} using a superset of @code{JSON.parse}. The
453-
following extensions are accepted:
452+
Parse @code{str} using a superset of @code{JSON.parse}. The superset
453+
is very close to the JSON5 specification. The following extensions
454+
are accepted:
454455

455456
@itemize
456457
@item Single line and multiline comments
457458
@item unquoted properties (ASCII-only Javascript identifiers)
458459
@item trailing comma in array and object definitions
459460
@item single quoted strings
461+
@item @code{\v} escape and multi-line strings with trailing @code{\}
460462
@item @code{\f} and @code{\v} are accepted as space characters
461-
@item leading plus in numbers
462-
@item octal (@code{0o} prefix) and hexadecimal (@code{0x} prefix) numbers
463+
@item leading plus or decimal point in numbers
464+
@item hexadecimal (@code{0x} prefix), octal (@code{0o} prefix) and binary (@code{0b} prefix) integers
465+
@item @code{NaN} and @code{Infinity} are accepted as numbers
463466
@end itemize
464467
@end table
465468

quickjs.c

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21824,6 +21824,7 @@ static __exception int next_token(JSParseState *s)
2182421824
}
2182521825

2182621826
/* 'c' is the first character. Return JS_ATOM_NULL in case of error */
21827+
/* XXX: accept unicode identifiers as JSON5 ? */
2182721828
static JSAtom json_parse_ident(JSParseState *s, const uint8_t **pp, int c)
2182821829
{
2182921830
const uint8_t *p;
@@ -21899,11 +21900,22 @@ static int json_parse_string(JSParseState *s, const uint8_t **pp, int sep)
2189921900
c = (c << 4) | h;
2190021901
}
2190121902
break;
21903+
case '\n':
21904+
if (s->ext_json)
21905+
continue;
21906+
goto bad_escape;
21907+
case 'v':
21908+
if (s->ext_json) {
21909+
c = '\v';
21910+
break;
21911+
}
21912+
goto bad_escape;
2190221913
default:
2190321914
if (c == sep)
2190421915
break;
2190521916
if (p > s->buf_end)
2190621917
goto end_of_input;
21918+
bad_escape:
2190721919
js_parse_error_pos(s, p - 1, "Bad escaped character");
2190821920
goto fail;
2190921921
}
@@ -21943,8 +21955,23 @@ static int json_parse_number(JSParseState *s, const uint8_t **pp)
2194321955
if (*p == '+' || *p == '-')
2194421956
p++;
2194521957

21946-
if (!is_digit(*p))
21947-
return js_parse_error_pos(s, p, "Unexpected token '%c'", *p_start);
21958+
if (!is_digit(*p)) {
21959+
if (s->ext_json) {
21960+
if (strstart((const char *)p, "Infinity", (const char **)&p)) {
21961+
d = 1.0 / 0.0;
21962+
if (*p_start == '-')
21963+
d = -d;
21964+
goto done;
21965+
} else if (strstart((const char *)p, "NaN", (const char **)&p)) {
21966+
d = NAN;
21967+
goto done;
21968+
} else if (*p != '.') {
21969+
goto unexpected_token;
21970+
}
21971+
} else {
21972+
goto unexpected_token;
21973+
}
21974+
}
2194821975

2194921976
if (p[0] == '0') {
2195021977
if (s->ext_json) {
@@ -21962,8 +21989,10 @@ static int json_parse_number(JSParseState *s, const uint8_t **pp)
2196221989
}
2196321990
if (radix != 10) {
2196421991
/* prefix is present */
21965-
if (to_digit(*p) >= radix)
21992+
if (to_digit(*p) >= radix) {
21993+
unexpected_token:
2196621994
return js_parse_error_pos(s, p, "Unexpected token '%c'", *p);
21995+
}
2196721996
d = js_atod((const char *)p_start, (const char **)&p, 0,
2196821997
JS_ATOD_INT_ONLY | JS_ATOD_ACCEPT_BIN_OCT, &atod_mem);
2196921998
goto done;
@@ -22122,7 +22151,6 @@ static __exception int json_next_token(JSParseState *s)
2212222151
case 'Y': case 'Z':
2212322152
case '_':
2212422153
case '$':
22125-
/* identifier : only pure ascii characters are accepted */
2212622154
p++;
2212722155
atom = json_parse_ident(s, &p, c);
2212822156
if (atom == JS_ATOM_NULL)
@@ -22133,17 +22161,16 @@ static __exception int json_next_token(JSParseState *s)
2213322161
s->token.val = TOK_IDENT;
2213422162
break;
2213522163
case '+':
22136-
if (!s->ext_json || !is_digit(p[1]))
22164+
if (!s->ext_json)
2213722165
goto def_token;
2213822166
goto parse_number;
22139-
case '0':
22140-
if (is_digit(p[1]))
22167+
case '.':
22168+
if (s->ext_json && is_digit(p[1]))
22169+
goto parse_number;
22170+
else
2214122171
goto def_token;
22142-
goto parse_number;
2214322172
case '-':
22144-
if (!is_digit(p[1]))
22145-
goto def_token;
22146-
goto parse_number;
22173+
case '0':
2214722174
case '1': case '2': case '3': case '4':
2214822175
case '5': case '6': case '7': case '8':
2214922176
case '9':
@@ -46187,6 +46214,12 @@ static JSValue json_parse_value(JSParseState *s)
4618746214
val = JS_NewBool(ctx, s->token.u.ident.atom == JS_ATOM_true);
4618846215
} else if (s->token.u.ident.atom == JS_ATOM_null) {
4618946216
val = JS_NULL;
46217+
} else if (s->token.u.ident.atom == JS_ATOM_NaN && s->ext_json) {
46218+
/* Note: json5 identifier handling is ambiguous e.g. is
46219+
'{ NaN: 1 }' a valid JSON5 production ? */
46220+
val = JS_NewFloat64(s->ctx, NAN);
46221+
} else if (s->token.u.ident.atom == JS_ATOM_Infinity && s->ext_json) {
46222+
val = JS_NewFloat64(s->ctx, INFINITY);
4619046223
} else {
4619146224
goto def_token;
4619246225
}

tests/test_std.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,15 +129,27 @@ function test_popen()
129129
function test_ext_json()
130130
{
131131
var expected, input, obj;
132-
expected = '{"x":false,"y":true,"z2":null,"a":[1,8,160],"s":"str"}';
132+
expected = '{"x":false,"y":true,"z2":null,"a":[1,8,160],"b":"abc\\u000bd","s":"str"}';
133133
input = `{ "x":false, /*comments are allowed */
134134
"y":true, // also a comment
135135
z2:null, // unquoted property names
136136
"a":[+1,0o10,0xa0,], // plus prefix, octal, hexadecimal
137+
"b": "ab\
138+
c\\vd", // multi-line strings, '\v' escape
137139
"s":'str',} // trailing comma in objects and arrays, single quoted string
138140
`;
139141
obj = std.parseExtJSON(input);
140142
assert(JSON.stringify(obj), expected);
143+
144+
obj = std.parseExtJSON('[Infinity, +Infinity, -Infinity, NaN, +NaN, -NaN, .1, -.2]');
145+
assert(obj[0], Infinity);
146+
assert(obj[1], Infinity);
147+
assert(obj[2], -Infinity);
148+
assert(obj[3], NaN);
149+
assert(obj[4], NaN);
150+
assert(obj[5], NaN);
151+
assert(obj[6], 0.1);
152+
assert(obj[7], -0.2);
141153
}
142154

143155
function test_os()

0 commit comments

Comments
 (0)