1#include "jsi.h"
2#include "jslex.h"
3#include "jsvalue.h"
4#include "jsbuiltin.h"
5
6#include "utf.h"
7
8static void jsonnext(js_State *J)
9{
10 J->lookahead = jsY_lexjson(J);
11}
12
13static 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
22static 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
29static 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
95static 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
143static 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
159static 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
170static 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
200static 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
207static int fmtvalue(js_State *J, js_Buffer **sb, const char *key, const char *gap, int level);
208
209static 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
244static 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
267static 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
323static 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
368void 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