1 | #include "jsi.h" |
2 | #include "jslex.h" |
3 | #include "jsvalue.h" |
4 | #include "jsbuiltin.h" |
5 | |
6 | #include "utf.h" |
7 | |
8 | static void jsonnext(js_State *J) |
9 | { |
10 | J->lookahead = jsY_lexjson(J); |
11 | } |
12 | |
13 | static int jsonaccept(js_State *J, int t) |
14 | { |
15 | if (J->lookahead == t) { |
16 | jsonnext(J); |
17 | return 1; |
18 | } |
19 | return 0; |
20 | } |
21 | |
22 | static void jsonexpect(js_State *J, int t) |
23 | { |
24 | if (!jsonaccept(J, t)) |
25 | js_syntaxerror(J, "JSON: unexpected token: %s (expected %s)" , |
26 | jsY_tokenstring(J->lookahead), jsY_tokenstring(t)); |
27 | } |
28 | |
29 | static void jsonvalue(js_State *J) |
30 | { |
31 | int i; |
32 | const char *name; |
33 | |
34 | switch (J->lookahead) { |
35 | case TK_STRING: |
36 | js_pushstring(J, J->text); |
37 | jsonnext(J); |
38 | break; |
39 | |
40 | case TK_NUMBER: |
41 | js_pushnumber(J, J->number); |
42 | jsonnext(J); |
43 | break; |
44 | |
45 | case '{': |
46 | js_newobject(J); |
47 | jsonnext(J); |
48 | if (jsonaccept(J, '}')) |
49 | return; |
50 | do { |
51 | if (J->lookahead != TK_STRING) |
52 | js_syntaxerror(J, "JSON: unexpected token: %s (expected string)" , jsY_tokenstring(J->lookahead)); |
53 | name = J->text; |
54 | jsonnext(J); |
55 | jsonexpect(J, ':'); |
56 | jsonvalue(J); |
57 | js_setproperty(J, -2, name); |
58 | } while (jsonaccept(J, ',')); |
59 | jsonexpect(J, '}'); |
60 | break; |
61 | |
62 | case '[': |
63 | js_newarray(J); |
64 | jsonnext(J); |
65 | i = 0; |
66 | if (jsonaccept(J, ']')) |
67 | return; |
68 | do { |
69 | jsonvalue(J); |
70 | js_setindex(J, -2, i++); |
71 | } while (jsonaccept(J, ',')); |
72 | jsonexpect(J, ']'); |
73 | break; |
74 | |
75 | case TK_TRUE: |
76 | js_pushboolean(J, 1); |
77 | jsonnext(J); |
78 | break; |
79 | |
80 | case TK_FALSE: |
81 | js_pushboolean(J, 0); |
82 | jsonnext(J); |
83 | break; |
84 | |
85 | case TK_NULL: |
86 | js_pushnull(J); |
87 | jsonnext(J); |
88 | break; |
89 | |
90 | default: |
91 | js_syntaxerror(J, "JSON: unexpected token: %s" , jsY_tokenstring(J->lookahead)); |
92 | } |
93 | } |
94 | |
95 | static void jsonrevive(js_State *J, const char *name) |
96 | { |
97 | const char *key; |
98 | char buf[32]; |
99 | |
100 | /* revive is in 2 */ |
101 | /* holder is in -1 */ |
102 | |
103 | js_getproperty(J, -1, name); /* get value from holder */ |
104 | |
105 | if (js_isobject(J, -1)) { |
106 | if (js_isarray(J, -1)) { |
107 | int i = 0; |
108 | int n = js_getlength(J, -1); |
109 | for (i = 0; i < n; ++i) { |
110 | jsonrevive(J, js_itoa(buf, i)); |
111 | if (js_isundefined(J, -1)) { |
112 | js_pop(J, 1); |
113 | js_delproperty(J, -1, buf); |
114 | } else { |
115 | js_setproperty(J, -2, buf); |
116 | } |
117 | } |
118 | } else { |
119 | js_pushiterator(J, -1, 1); |
120 | while ((key = js_nextiterator(J, -1))) { |
121 | js_rot2(J); |
122 | jsonrevive(J, key); |
123 | if (js_isundefined(J, -1)) { |
124 | js_pop(J, 1); |
125 | js_delproperty(J, -1, key); |
126 | } else { |
127 | js_setproperty(J, -2, key); |
128 | } |
129 | js_rot2(J); |
130 | } |
131 | js_pop(J, 1); |
132 | } |
133 | } |
134 | |
135 | js_copy(J, 2); /* reviver function */ |
136 | js_copy(J, -3); /* holder as this */ |
137 | js_pushstring(J, name); /* name */ |
138 | js_copy(J, -4); /* value */ |
139 | js_call(J, 2); |
140 | js_rot2pop1(J); /* pop old value, leave new value on stack */ |
141 | } |
142 | |
143 | static void JSON_parse(js_State *J) |
144 | { |
145 | const char *source = js_tostring(J, 1); |
146 | jsY_initlex(J, "JSON" , source); |
147 | jsonnext(J); |
148 | |
149 | if (js_iscallable(J, 2)) { |
150 | js_newobject(J); |
151 | jsonvalue(J); |
152 | js_defproperty(J, -2, "" , 0); |
153 | jsonrevive(J, "" ); |
154 | } else { |
155 | jsonvalue(J); |
156 | } |
157 | } |
158 | |
159 | static void fmtnum(js_State *J, js_Buffer **sb, double n) |
160 | { |
161 | if (isnan(n)) js_puts(J, sb, "null" ); |
162 | else if (isinf(n)) js_puts(J, sb, "null" ); |
163 | else if (n == 0) js_puts(J, sb, "0" ); |
164 | else { |
165 | char buf[40]; |
166 | js_puts(J, sb, jsV_numbertostring(J, buf, n)); |
167 | } |
168 | } |
169 | |
170 | static void fmtstr(js_State *J, js_Buffer **sb, const char *s) |
171 | { |
172 | static const char *HEX = "0123456789ABCDEF" ; |
173 | Rune c; |
174 | js_putc(J, sb, '"'); |
175 | while (*s) { |
176 | s += chartorune(&c, s); |
177 | switch (c) { |
178 | case '"': js_puts(J, sb, "\\\"" ); break; |
179 | case '\\': js_puts(J, sb, "\\\\" ); break; |
180 | case '\b': js_puts(J, sb, "\\b" ); break; |
181 | case '\f': js_puts(J, sb, "\\f" ); break; |
182 | case '\n': js_puts(J, sb, "\\n" ); break; |
183 | case '\r': js_puts(J, sb, "\\r" ); break; |
184 | case '\t': js_puts(J, sb, "\\t" ); break; |
185 | default: |
186 | if (c < ' ' || c > 127) { |
187 | js_puts(J, sb, "\\u" ); |
188 | js_putc(J, sb, HEX[(c>>12)&15]); |
189 | js_putc(J, sb, HEX[(c>>8)&15]); |
190 | js_putc(J, sb, HEX[(c>>4)&15]); |
191 | js_putc(J, sb, HEX[c&15]); |
192 | } else { |
193 | js_putc(J, sb, c); break; |
194 | } |
195 | } |
196 | } |
197 | js_putc(J, sb, '"'); |
198 | } |
199 | |
200 | static void fmtindent(js_State *J, js_Buffer **sb, const char *gap, int level) |
201 | { |
202 | js_putc(J, sb, '\n'); |
203 | while (level--) |
204 | js_puts(J, sb, gap); |
205 | } |
206 | |
207 | static int fmtvalue(js_State *J, js_Buffer **sb, const char *key, const char *gap, int level); |
208 | |
209 | static void fmtobject(js_State *J, js_Buffer **sb, js_Object *obj, const char *gap, int level) |
210 | { |
211 | const char *key; |
212 | int save; |
213 | int i, n; |
214 | |
215 | n = js_gettop(J) - 1; |
216 | for (i = 4; i < n; ++i) |
217 | if (js_isobject(J, i)) |
218 | if (js_toobject(J, i) == js_toobject(J, -1)) |
219 | js_typeerror(J, "cyclic object value" ); |
220 | |
221 | n = 0; |
222 | js_putc(J, sb, '{'); |
223 | js_pushiterator(J, -1, 1); |
224 | while ((key = js_nextiterator(J, -1))) { |
225 | save = (*sb)->n; |
226 | if (n) js_putc(J, sb, ','); |
227 | if (gap) fmtindent(J, sb, gap, level + 1); |
228 | fmtstr(J, sb, key); |
229 | js_putc(J, sb, ':'); |
230 | if (gap) |
231 | js_putc(J, sb, ' '); |
232 | js_rot2(J); |
233 | if (!fmtvalue(J, sb, key, gap, level + 1)) |
234 | (*sb)->n = save; |
235 | else |
236 | ++n; |
237 | js_rot2(J); |
238 | } |
239 | js_pop(J, 1); |
240 | if (gap && n) fmtindent(J, sb, gap, level); |
241 | js_putc(J, sb, '}'); |
242 | } |
243 | |
244 | static void fmtarray(js_State *J, js_Buffer **sb, const char *gap, int level) |
245 | { |
246 | int n, i; |
247 | char buf[32]; |
248 | |
249 | n = js_gettop(J) - 1; |
250 | for (i = 4; i < n; ++i) |
251 | if (js_isobject(J, i)) |
252 | if (js_toobject(J, i) == js_toobject(J, -1)) |
253 | js_typeerror(J, "cyclic object value" ); |
254 | |
255 | js_putc(J, sb, '['); |
256 | n = js_getlength(J, -1); |
257 | for (i = 0; i < n; ++i) { |
258 | if (i) js_putc(J, sb, ','); |
259 | if (gap) fmtindent(J, sb, gap, level + 1); |
260 | if (!fmtvalue(J, sb, js_itoa(buf, i), gap, level + 1)) |
261 | js_puts(J, sb, "null" ); |
262 | } |
263 | if (gap && n) fmtindent(J, sb, gap, level); |
264 | js_putc(J, sb, ']'); |
265 | } |
266 | |
267 | static int fmtvalue(js_State *J, js_Buffer **sb, const char *key, const char *gap, int level) |
268 | { |
269 | /* replacer is in 2 */ |
270 | /* holder is in -1 */ |
271 | |
272 | js_getproperty(J, -1, key); |
273 | |
274 | if (js_isobject(J, -1)) { |
275 | if (js_hasproperty(J, -1, "toJSON" )) { |
276 | if (js_iscallable(J, -1)) { |
277 | js_copy(J, -2); |
278 | js_pushstring(J, key); |
279 | js_call(J, 1); |
280 | js_rot2pop1(J); |
281 | } else { |
282 | js_pop(J, 1); |
283 | } |
284 | } |
285 | } |
286 | |
287 | if (js_iscallable(J, 2)) { |
288 | js_copy(J, 2); /* replacer function */ |
289 | js_copy(J, -3); /* holder as this */ |
290 | js_pushstring(J, key); /* name */ |
291 | js_copy(J, -4); /* old value */ |
292 | js_call(J, 2); |
293 | js_rot2pop1(J); /* pop old value, leave new value on stack */ |
294 | } |
295 | |
296 | if (js_isobject(J, -1) && !js_iscallable(J, -1)) { |
297 | js_Object *obj = js_toobject(J, -1); |
298 | switch (obj->type) { |
299 | case JS_CNUMBER: fmtnum(J, sb, obj->u.number); break; |
300 | case JS_CSTRING: fmtstr(J, sb, obj->u.s.string); break; |
301 | case JS_CBOOLEAN: js_puts(J, sb, obj->u.boolean ? "true" : "false" ); break; |
302 | case JS_CARRAY: fmtarray(J, sb, gap, level); break; |
303 | default: fmtobject(J, sb, obj, gap, level); break; |
304 | } |
305 | } |
306 | else if (js_isboolean(J, -1)) |
307 | js_puts(J, sb, js_toboolean(J, -1) ? "true" : "false" ); |
308 | else if (js_isnumber(J, -1)) |
309 | fmtnum(J, sb, js_tonumber(J, -1)); |
310 | else if (js_isstring(J, -1)) |
311 | fmtstr(J, sb, js_tostring(J, -1)); |
312 | else if (js_isnull(J, -1)) |
313 | js_puts(J, sb, "null" ); |
314 | else { |
315 | js_pop(J, 1); |
316 | return 0; |
317 | } |
318 | |
319 | js_pop(J, 1); |
320 | return 1; |
321 | } |
322 | |
323 | static void JSON_stringify(js_State *J) |
324 | { |
325 | js_Buffer *sb = NULL; |
326 | char buf[12]; |
327 | const char *s, *gap; |
328 | int n; |
329 | |
330 | gap = NULL; |
331 | |
332 | if (js_isnumber(J, 3)) { |
333 | n = js_tointeger(J, 3); |
334 | if (n < 0) n = 0; |
335 | if (n > 10) n = 10; |
336 | memset(buf, ' ', n); |
337 | buf[n] = 0; |
338 | if (n > 0) gap = buf; |
339 | } else if (js_isstring(J, 3)) { |
340 | s = js_tostring(J, 3); |
341 | n = strlen(s); |
342 | if (n > 10) n = 10; |
343 | memcpy(buf, s, n); |
344 | buf[n] = 0; |
345 | if (n > 0) gap = buf; |
346 | } |
347 | |
348 | if (js_try(J)) { |
349 | js_free(J, sb); |
350 | js_throw(J); |
351 | } |
352 | |
353 | js_newobject(J); /* wrapper */ |
354 | js_copy(J, 1); |
355 | js_defproperty(J, -2, "" , 0); |
356 | if (!fmtvalue(J, &sb, "" , gap, 0)) { |
357 | js_pushundefined(J); |
358 | } else { |
359 | js_putc(J, &sb, 0); |
360 | js_pushstring(J, sb ? sb->s : "" ); |
361 | js_rot2pop1(J); |
362 | } |
363 | |
364 | js_endtry(J); |
365 | js_free(J, sb); |
366 | } |
367 | |
368 | void jsB_initjson(js_State *J) |
369 | { |
370 | js_pushobject(J, jsV_newobject(J, JS_CJSON, J->Object_prototype)); |
371 | { |
372 | jsB_propf(J, "JSON.parse" , JSON_parse, 2); |
373 | jsB_propf(J, "JSON.stringify" , JSON_stringify, 3); |
374 | } |
375 | js_defglobal(J, "JSON" , JS_DONTENUM); |
376 | } |
377 | |