Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
7639 views
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_pushliteral(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 JSON_parse(js_State *J)
96
{
97
const char *source = js_tostring(J, 1);
98
jsY_initlex(J, "JSON", source);
99
jsonnext(J);
100
jsonvalue(J);
101
// TODO: reviver Walk()
102
}
103
104
static void fmtnum(js_State *J, js_Buffer **sb, double n)
105
{
106
if (isnan(n)) js_puts(J, sb, "null");
107
else if (isinf(n)) js_puts(J, sb, "null");
108
else if (n == 0) js_puts(J, sb, "0");
109
else {
110
char buf[40];
111
sprintf(buf, "%.17g", n);
112
js_puts(J, sb, buf);
113
}
114
}
115
116
static void fmtstr(js_State *J, js_Buffer **sb, const char *s)
117
{
118
static const char *HEX = "0123456789ABCDEF";
119
Rune c;
120
js_putc(J, sb, '"');
121
while (*s) {
122
s += chartorune(&c, s);
123
switch (c) {
124
case '"': js_puts(J, sb, "\\\""); break;
125
case '\\': js_puts(J, sb, "\\\\"); break;
126
case '\b': js_puts(J, sb, "\\b"); break;
127
case '\f': js_puts(J, sb, "\\f"); break;
128
case '\n': js_puts(J, sb, "\\n"); break;
129
case '\r': js_puts(J, sb, "\\r"); break;
130
case '\t': js_puts(J, sb, "\\t"); break;
131
default:
132
if (c < ' ') {
133
js_puts(J, sb, "\\u");
134
js_putc(J, sb, HEX[(c>>12)&15]);
135
js_putc(J, sb, HEX[(c>>8)&15]);
136
js_putc(J, sb, HEX[(c>>4)&15]);
137
js_putc(J, sb, HEX[c&15]);
138
} else {
139
js_putc(J, sb, c); break;
140
}
141
}
142
}
143
js_putc(J, sb, '"');
144
}
145
146
static void fmtindent(js_State *J, js_Buffer **sb, const char *gap, int level)
147
{
148
js_putc(J, sb, '\n');
149
while (level--)
150
js_puts(J, sb, gap);
151
}
152
153
static int fmtvalue(js_State *J, js_Buffer **sb, const char *key, const char *gap, int level);
154
155
static void fmtobject(js_State *J, js_Buffer **sb, js_Object *obj, const char *gap, int level)
156
{
157
js_Property *ref;
158
int save;
159
int n = 0;
160
161
js_putc(J, sb, '{');
162
for (ref = obj->head; ref; ref = ref->next) {
163
if (ref->atts & JS_DONTENUM)
164
continue;
165
save = (*sb)->n;
166
if (n) js_putc(J, sb, ',');
167
if (gap) fmtindent(J, sb, gap, level + 1);
168
fmtstr(J, sb, ref->name);
169
js_putc(J, sb, ':');
170
if (gap)
171
js_putc(J, sb, ' ');
172
js_pushvalue(J, ref->value);
173
if (!fmtvalue(J, sb, ref->name, gap, level + 1))
174
(*sb)->n = save;
175
else
176
++n;
177
js_pop(J, 1);
178
}
179
if (gap && n) fmtindent(J, sb, gap, level);
180
js_putc(J, sb, '}');
181
}
182
183
static void fmtarray(js_State *J, js_Buffer **sb, const char *gap, int level)
184
{
185
unsigned int n, k;
186
char buf[32];
187
188
n = js_getlength(J, -1);
189
190
js_putc(J, sb, '[');
191
for (k = 0; k < n; ++k) {
192
if (k) js_putc(J, sb, ',');
193
if (gap) fmtindent(J, sb, gap, level + 1);
194
js_itoa(buf, k);
195
js_getproperty(J, -1, buf);
196
if (!fmtvalue(J, sb, js_intern(J, buf), gap, level + 1))
197
js_puts(J, sb, "null");
198
js_pop(J, 1);
199
}
200
if (gap && n) fmtindent(J, sb, gap, level);
201
js_putc(J, sb, ']');
202
}
203
204
static int fmtvalue(js_State *J, js_Buffer **sb, const char *key, const char *gap, int level)
205
{
206
if (js_try(J)) {
207
js_free(J, *sb);
208
js_throw(J);
209
}
210
if (js_isobject(J, -1)) {
211
if (js_hasproperty(J, -1, "toJSON")) {
212
if (js_iscallable(J, -1)) {
213
js_copy(J, -2);
214
js_pushliteral(J, key);
215
js_call(J, 1);
216
js_rot2pop1(J);
217
} else {
218
js_pop(J, 1);
219
}
220
}
221
}
222
js_endtry(J);
223
224
// TODO: replacer()
225
226
if (js_isobject(J, -1) && !js_iscallable(J, -1)) {
227
js_Object *obj = js_toobject(J, -1);
228
switch (obj->type) {
229
case JS_CNUMBER: fmtnum(J, sb, obj->u.number); break;
230
case JS_CSTRING: fmtstr(J, sb, obj->u.s.string); break;
231
case JS_CBOOLEAN: js_puts(J, sb, obj->u.boolean ? "true" : "false"); break;
232
case JS_CARRAY: fmtarray(J, sb, gap, level); break;
233
default: fmtobject(J, sb, obj, gap, level); break;
234
}
235
}
236
else if (js_isboolean(J, -1))
237
js_puts(J, sb, js_toboolean(J, -1) ? "true" : "false");
238
else if (js_isnumber(J, -1))
239
fmtnum(J, sb, js_tonumber(J, -1));
240
else if (js_isstring(J, -1))
241
fmtstr(J, sb, js_tostring(J, -1));
242
else if (js_isnull(J, -1))
243
js_puts(J, sb, "null");
244
else
245
return 0;
246
247
return 1;
248
}
249
250
static void JSON_stringify(js_State *J)
251
{
252
js_Buffer *sb = NULL;
253
char buf[12];
254
const char *s, *gap;
255
int n;
256
257
gap = NULL;
258
259
if (js_isnumber(J, 3)) {
260
n = js_tointeger(J, 3);
261
if (n < 0) n = 0;
262
if (n > 10) n = 10;
263
memset(buf, ' ', n);
264
buf[n] = 0;
265
if (n > 0) gap = buf;
266
} else if (js_isstring(J, 3)) {
267
s = js_tostring(J, 3);
268
n = strlen(s);
269
if (n > 10) n = 10;
270
memcpy(buf, s, n);
271
buf[n] = 0;
272
if (n > 0) gap = buf;
273
}
274
275
// TODO: replacer
276
277
if (js_isdefined(J, 1)) {
278
js_copy(J, 1);
279
if (fmtvalue(J, &sb, "", gap, 0)) {
280
js_putc(J, &sb, 0);
281
if (js_try(J)) {
282
js_free(J, sb);
283
js_throw(J);
284
}
285
js_pushstring(J, sb ? sb->s : "");
286
js_endtry(J);
287
js_free(J, sb);
288
}
289
} else {
290
js_pushundefined(J);
291
}
292
}
293
294
void jsB_initjson(js_State *J)
295
{
296
js_pushobject(J, jsV_newobject(J, JS_CJSON, J->Object_prototype));
297
{
298
jsB_propf(J, "parse", JSON_parse, 2);
299
jsB_propf(J, "stringify", JSON_stringify, 3);
300
}
301
js_defglobal(J, "JSON", JS_DONTENUM);
302
}
303
304