summaryrefslogtreecommitdiff
path: root/xs_json.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--xs_json.h501
1 files changed, 501 insertions, 0 deletions
diff --git a/xs_json.h b/xs_json.h
new file mode 100644
index 0000000..4bf73d5
--- /dev/null
+++ b/xs_json.h
@@ -0,0 +1,501 @@
1/* copyright (c) 2022 grunfink - MIT license */
2
3#ifndef _XS_JSON_H
4
5#define _XS_JSON_H
6
7d_char *xs_json_dumps_pp(char *data, int indent);
8#define xs_json_dumps(data) xs_json_dumps_pp(data, 0)
9d_char *xs_json_loads(const char *json);
10
11
12#ifdef XS_IMPLEMENTATION
13
14/** IMPLEMENTATION **/
15
16/** JSON dumps **/
17
18d_char *_xs_json_dumps_str(d_char *s, char *data)
19/* dumps a string in JSON format */
20{
21 unsigned char c;
22 s = xs_str_cat(s, "\"");
23
24 while ((c = *data)) {
25 if (c == '\n')
26 s = xs_str_cat(s, "\\n");
27 else
28 if (c == '\r')
29 s = xs_str_cat(s, "\\r");
30 else
31 if (c == '\t')
32 s = xs_str_cat(s, "\\t");
33 else
34 if (c == '\\')
35 s = xs_str_cat(s, "\\\\");
36 else
37 if (c == '"')
38 s = xs_str_cat(s, "\\\"");
39 else
40 if (c < 32) {
41 char tmp[10];
42
43 sprintf(tmp, "\\u%04x", (unsigned int) c);
44 s = xs_str_cat(s, tmp);
45 }
46 else
47 s = xs_append_m(s, data, 1);
48
49 data++;
50 }
51
52 s = xs_str_cat(s, "\"");
53
54 return s;
55}
56
57
58d_char *_xs_json_indent(d_char *s, int level, int indent)
59/* adds indentation */
60{
61 if (indent) {
62 int n;
63
64 s = xs_str_cat(s, "\n");
65
66 for (n = 0; n < level * indent; n++)
67 s = xs_str_cat(s, " ");
68 }
69
70 return s;
71}
72
73
74d_char *_xs_json_dumps(d_char *s, char *data, int level, int indent)
75/* dumps partial data as JSON */
76{
77 char *k, *v;
78 int c = 0;
79
80 switch (xs_type(data)) {
81 case XSTYPE_NULL:
82 s = xs_str_cat(s, "null");
83 break;
84
85 case XSTYPE_TRUE:
86 s = xs_str_cat(s, "true");
87 break;
88
89 case XSTYPE_FALSE:
90 s = xs_str_cat(s, "false");
91 break;
92
93 case XSTYPE_NUMBER:
94 {
95 char tmp[32];
96 snprintf(tmp, sizeof(tmp), "%g", xs_get_number(data));
97 s = xs_str_cat(s, tmp);
98 }
99 break;
100
101 case XSTYPE_SOL:
102 s = xs_str_cat(s, "[");
103
104 while (xs_list_iter(&data, &v)) {
105 if (c != 0)
106 s = xs_str_cat(s, ",");
107
108 s = _xs_json_indent(s, level + 1, indent);
109 s = _xs_json_dumps(s, v, level + 1, indent);
110
111 c++;
112 }
113
114 s = _xs_json_indent(s, level, indent);
115 s = xs_str_cat(s, "]");
116
117 break;
118
119 case XSTYPE_SOD:
120 s = xs_str_cat(s, "{");
121
122 while (xs_dict_iter(&data, &k, &v)) {
123 if (c != 0)
124 s = xs_str_cat(s, ",");
125
126 s = _xs_json_indent(s, level + 1, indent);
127
128 s = _xs_json_dumps_str(s, k);
129 s = xs_str_cat(s, ":");
130
131 if (indent)
132 s = xs_str_cat(s, " ");
133
134 s = _xs_json_dumps(s, v, level + 1, indent);
135
136 c++;
137 }
138
139 s = _xs_json_indent(s, level, indent);
140 s = xs_str_cat(s, "}");
141 break;
142
143 case XSTYPE_STRING:
144 s = _xs_json_dumps_str(s, data);
145 break;
146
147 default:
148 break;
149 }
150
151 return s;
152}
153
154
155d_char *xs_json_dumps_pp(char *data, int indent)
156/* dumps a piece of data as JSON */
157{
158 xstype t = xs_type(data);
159 d_char *s = NULL;
160
161 if (t == XSTYPE_SOL || t == XSTYPE_SOD) {
162 s = xs_str_new(NULL);
163 s = _xs_json_dumps(s, data, 0, indent);
164 }
165
166 return s;
167}
168
169
170/** JSON loads **/
171
172/* this code comes mostly from the Minimum Profit Text Editor (MPDM) */
173
174typedef enum {
175 JS_ERROR = -1,
176 JS_INCOMPLETE,
177 JS_OCURLY,
178 JS_OBRACK,
179 JS_CCURLY,
180 JS_CBRACK,
181 JS_COMMA,
182 JS_COLON,
183 JS_VALUE,
184 JS_STRING,
185 JS_INTEGER,
186 JS_REAL,
187 JS_TRUE,
188 JS_FALSE,
189 JS_NULL,
190 JS_ARRAY,
191 JS_OBJECT
192} js_type;
193
194
195d_char *_xs_json_loads_lexer(const char **json, js_type *t)
196{
197 char c;
198 const char *s = *json;
199 d_char *v = NULL;
200
201 /* skip blanks */
202 while (*s == L' ' || *s == L'\t' || *s == L'\n' || *s == L'\r')
203 s++;
204
205 c = *s++;
206
207 if (c == '{')
208 *t = JS_OCURLY;
209 else
210 if (c == '}')
211 *t = JS_CCURLY;
212 else
213 if (c == '[')
214 *t = JS_OBRACK;
215 else
216 if (c == ']')
217 *t = JS_CBRACK;
218 else
219 if (c == ',')
220 *t = JS_COMMA;
221 else
222 if (c == ':')
223 *t = JS_COLON;
224 else
225 if (c == '"') {
226 *t = JS_STRING;
227
228 v = xs_str_new(NULL);
229
230 while ((c = *s) != '"' && c != '\0') {
231 char tmp[5];
232 int i;
233
234 if (c == '\\') {
235 s++;
236 c = *s;
237 switch (c) {
238 case 'n': c = '\n'; break;
239 case 'r': c = '\r'; break;
240 case 't': c = '\t'; break;
241 case 'u': /* Unicode codepoint as an hex char */
242 s++;
243 tmp[0] = (char)*s; s++;
244 tmp[1] = (char)*s; s++;
245 tmp[2] = (char)*s; s++;
246 tmp[3] = (char)*s;
247 tmp[4] = '\0';
248
249 sscanf(tmp, "%04x", &i);
250
251 v = xs_utf8_enc(v, i);
252 c = '\0';
253
254 break;
255 }
256 }
257
258 if (c)
259 v = xs_append_m(v, &c, 1);
260
261 s++;
262 }
263
264 if (c != '\0')
265 s++;
266 }
267 else
268 if (c == '-' || (c >= '0' && c <= '9') || c == '.') {
269 xs *vn = NULL;
270
271 *t = JS_INTEGER;
272
273 vn = xs_str_new(NULL);
274 vn = xs_append_m(vn, &c, 1);
275
276 while (((c = *s) >= '0' && c <= '9') || c == '.') {
277 if (c == '.')
278 *t = JS_REAL;
279
280 vn = xs_append_m(vn, &c, 1);
281 s++;
282 }
283
284 /* convert to XSTYPE_NUMBER */
285 v = xs_number_new(atof(vn));
286 }
287 else
288 if (c == 't' && strncmp(s, "rue", 3) == 0) {
289 s += 3;
290 *t = JS_TRUE;
291
292 v = xs_val_new(XSTYPE_TRUE);
293 }
294 else
295 if (c == 'f' && strncmp(s, "alse", 4) == 0) {
296 s += 4;
297 *t = JS_FALSE;
298
299 v = xs_val_new(XSTYPE_FALSE);
300 }
301 else
302 if (c == 'n' && strncmp(s, "ull", 3) == 0) {
303 s += 3;
304 *t = JS_NULL;
305
306 v = xs_val_new(XSTYPE_NULL);
307 }
308 else
309 *t = JS_ERROR;
310
311 *json = s;
312
313 return v;
314}
315
316
317d_char *_xs_json_loads_array(const char **json, js_type *t);
318d_char *_xs_json_loads_object(const char **json, js_type *t);
319
320d_char *_xs_json_loads_value(const char **json, js_type *t, d_char *v)
321/* parses a JSON value */
322{
323 if (*t == JS_OBRACK)
324 v = _xs_json_loads_array(json, t);
325 else
326 if (*t == JS_OCURLY)
327 v = _xs_json_loads_object(json, t);
328
329 if (*t >= JS_VALUE)
330 *t = JS_VALUE;
331 else
332 *t = JS_ERROR;
333
334 return v;
335}
336
337
338d_char *_xs_json_loads_array(const char **json, js_type *t)
339/* parses a JSON array */
340{
341 const char *s = *json;
342 xs *v;
343 d_char *l;
344 js_type tt;
345
346 l = xs_list_new();
347
348 *t = JS_INCOMPLETE;
349
350 v = _xs_json_loads_lexer(&s, &tt);
351
352 if (tt == JS_CBRACK)
353 *t = JS_ARRAY;
354 else {
355 v = _xs_json_loads_value(&s, &tt, v);
356
357 if (tt == JS_VALUE) {
358 l = xs_list_append(l, v);
359
360 while (*t == JS_INCOMPLETE) {
361 _xs_json_loads_lexer(&s, &tt);
362
363 if (tt == JS_CBRACK)
364 *t = JS_ARRAY;
365 else
366 if (tt == JS_COMMA) {
367 xs *v2;
368
369 v2 = _xs_json_loads_lexer(&s, &tt);
370 v2 = _xs_json_loads_value(&s, &tt, v2);
371
372 if (tt == JS_VALUE)
373 l = xs_list_append(l, v2);
374 else
375 *t = JS_ERROR;
376 }
377 else
378 *t = JS_ERROR;
379 }
380 }
381 else
382 *t = JS_ERROR;
383 }
384
385 if (*t == JS_ERROR) {
386 free(l);
387 l = NULL;
388 }
389
390 *json = s;
391
392 return l;
393}
394
395
396d_char *_xs_json_loads_object(const char **json, js_type *t)
397/* parses a JSON object */
398{
399 const char *s = *json;
400 xs *k1;
401 d_char *d;
402 js_type tt;
403
404 d = xs_dict_new();
405
406 *t = JS_INCOMPLETE;
407
408 k1 = _xs_json_loads_lexer(&s, &tt);
409
410 if (tt == JS_CCURLY)
411 *t = JS_OBJECT;
412 else
413 if (tt == JS_STRING) {
414 _xs_json_loads_lexer(&s, &tt);
415
416 if (tt == JS_COLON) {
417 xs *v1;
418
419 v1 = _xs_json_loads_lexer(&s, &tt);
420 v1 = _xs_json_loads_value(&s, &tt, v1);
421
422 if (tt == JS_VALUE) {
423 d = xs_dict_append(d, k1, v1);
424
425 while (*t == JS_INCOMPLETE) {
426 _xs_json_loads_lexer(&s, &tt);
427
428 if (tt == JS_CCURLY)
429 *t = JS_OBJECT;
430 else
431 if (tt == JS_COMMA) {
432 xs *k;
433
434 k = _xs_json_loads_lexer(&s, &tt);
435
436 if (tt == JS_STRING) {
437 _xs_json_loads_lexer(&s, &tt);
438
439 if (tt == JS_COLON) {
440 xs *v;
441
442 v = _xs_json_loads_lexer(&s, &tt);
443 v = _xs_json_loads_value(&s, &tt, v);
444
445 if (tt == JS_VALUE)
446 d = xs_dict_append(d, k, v);
447 else
448 *t = JS_ERROR;
449 }
450 else
451 *t = JS_ERROR;
452 }
453 else
454 *t = JS_ERROR;
455 }
456 else
457 *t = JS_ERROR;
458 }
459 }
460 else
461 *t = JS_ERROR;
462 }
463 else
464 *t = JS_ERROR;
465 }
466 else
467 *t = JS_ERROR;
468
469 if (*t == JS_ERROR) {
470 free(d);
471 d = NULL;
472 }
473
474 *json = s;
475
476 return d;
477}
478
479
480d_char *xs_json_loads(const char *json)
481/* loads a string in JSON format and converts to a multiple data */
482{
483 d_char *v = NULL;
484 js_type t;
485
486 _xs_json_loads_lexer(&json, &t);
487
488 if (t == JS_OBRACK)
489 v = _xs_json_loads_array(&json, &t);
490 else
491 if (t == JS_OCURLY)
492 v = _xs_json_loads_object(&json, &t);
493 else
494 t = JS_ERROR;
495
496 return v;
497}
498
499#endif /* XS_IMPLEMENTATION */
500
501#endif /* _XS_JSON_H */