diff options
author | default <nobody@localhost> | 2022-09-19 20:41:11 +0200 |
---|---|---|
committer | default <nobody@localhost> | 2022-09-19 20:41:11 +0200 |
commit | 67288a763b8bceadbb128d2cf5bdc431ba8a686f (patch) | |
tree | 0a0bc1922016bbd3d3cd2ec3c80a033970280a9f /xs_json.h | |
parent | ef03035ef0af5a68ba7e03ae834a43db5dd3a8e3 (diff) |
Imported xs.
Diffstat (limited to 'xs_json.h')
-rw-r--r-- | xs_json.h | 501 |
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 @@ +/* copyright (c) 2022 grunfink - MIT license */ + +#ifndef _XS_JSON_H + +#define _XS_JSON_H + +d_char *xs_json_dumps_pp(char *data, int indent); +#define xs_json_dumps(data) xs_json_dumps_pp(data, 0) +d_char *xs_json_loads(const char *json); + + +#ifdef XS_IMPLEMENTATION + +/** IMPLEMENTATION **/ + +/** JSON dumps **/ + +d_char *_xs_json_dumps_str(d_char *s, char *data) +/* dumps a string in JSON format */ +{ + unsigned char c; + s = xs_str_cat(s, "\""); + + while ((c = *data)) { + if (c == '\n') + s = xs_str_cat(s, "\\n"); + else + if (c == '\r') + s = xs_str_cat(s, "\\r"); + else + if (c == '\t') + s = xs_str_cat(s, "\\t"); + else + if (c == '\\') + s = xs_str_cat(s, "\\\\"); + else + if (c == '"') + s = xs_str_cat(s, "\\\""); + else + if (c < 32) { + char tmp[10]; + + sprintf(tmp, "\\u%04x", (unsigned int) c); + s = xs_str_cat(s, tmp); + } + else + s = xs_append_m(s, data, 1); + + data++; + } + + s = xs_str_cat(s, "\""); + + return s; +} + + +d_char *_xs_json_indent(d_char *s, int level, int indent) +/* adds indentation */ +{ + if (indent) { + int n; + + s = xs_str_cat(s, "\n"); + + for (n = 0; n < level * indent; n++) + s = xs_str_cat(s, " "); + } + + return s; +} + + +d_char *_xs_json_dumps(d_char *s, char *data, int level, int indent) +/* dumps partial data as JSON */ +{ + char *k, *v; + int c = 0; + + switch (xs_type(data)) { + case XSTYPE_NULL: + s = xs_str_cat(s, "null"); + break; + + case XSTYPE_TRUE: + s = xs_str_cat(s, "true"); + break; + + case XSTYPE_FALSE: + s = xs_str_cat(s, "false"); + break; + + case XSTYPE_NUMBER: + { + char tmp[32]; + snprintf(tmp, sizeof(tmp), "%g", xs_get_number(data)); + s = xs_str_cat(s, tmp); + } + break; + + case XSTYPE_SOL: + s = xs_str_cat(s, "["); + + while (xs_list_iter(&data, &v)) { + if (c != 0) + s = xs_str_cat(s, ","); + + s = _xs_json_indent(s, level + 1, indent); + s = _xs_json_dumps(s, v, level + 1, indent); + + c++; + } + + s = _xs_json_indent(s, level, indent); + s = xs_str_cat(s, "]"); + + break; + + case XSTYPE_SOD: + s = xs_str_cat(s, "{"); + + while (xs_dict_iter(&data, &k, &v)) { + if (c != 0) + s = xs_str_cat(s, ","); + + s = _xs_json_indent(s, level + 1, indent); + + s = _xs_json_dumps_str(s, k); + s = xs_str_cat(s, ":"); + + if (indent) + s = xs_str_cat(s, " "); + + s = _xs_json_dumps(s, v, level + 1, indent); + + c++; + } + + s = _xs_json_indent(s, level, indent); + s = xs_str_cat(s, "}"); + break; + + case XSTYPE_STRING: + s = _xs_json_dumps_str(s, data); + break; + + default: + break; + } + + return s; +} + + +d_char *xs_json_dumps_pp(char *data, int indent) +/* dumps a piece of data as JSON */ +{ + xstype t = xs_type(data); + d_char *s = NULL; + + if (t == XSTYPE_SOL || t == XSTYPE_SOD) { + s = xs_str_new(NULL); + s = _xs_json_dumps(s, data, 0, indent); + } + + return s; +} + + +/** JSON loads **/ + +/* this code comes mostly from the Minimum Profit Text Editor (MPDM) */ + +typedef enum { + JS_ERROR = -1, + JS_INCOMPLETE, + JS_OCURLY, + JS_OBRACK, + JS_CCURLY, + JS_CBRACK, + JS_COMMA, + JS_COLON, + JS_VALUE, + JS_STRING, + JS_INTEGER, + JS_REAL, + JS_TRUE, + JS_FALSE, + JS_NULL, + JS_ARRAY, + JS_OBJECT +} js_type; + + +d_char *_xs_json_loads_lexer(const char **json, js_type *t) +{ + char c; + const char *s = *json; + d_char *v = NULL; + + /* skip blanks */ + while (*s == L' ' || *s == L'\t' || *s == L'\n' || *s == L'\r') + s++; + + c = *s++; + + if (c == '{') + *t = JS_OCURLY; + else + if (c == '}') + *t = JS_CCURLY; + else + if (c == '[') + *t = JS_OBRACK; + else + if (c == ']') + *t = JS_CBRACK; + else + if (c == ',') + *t = JS_COMMA; + else + if (c == ':') + *t = JS_COLON; + else + if (c == '"') { + *t = JS_STRING; + + v = xs_str_new(NULL); + + while ((c = *s) != '"' && c != '\0') { + char tmp[5]; + int i; + + if (c == '\\') { + s++; + c = *s; + switch (c) { + case 'n': c = '\n'; break; + case 'r': c = '\r'; break; + case 't': c = '\t'; break; + case 'u': /* Unicode codepoint as an hex char */ + s++; + tmp[0] = (char)*s; s++; + tmp[1] = (char)*s; s++; + tmp[2] = (char)*s; s++; + tmp[3] = (char)*s; + tmp[4] = '\0'; + + sscanf(tmp, "%04x", &i); + + v = xs_utf8_enc(v, i); + c = '\0'; + + break; + } + } + + if (c) + v = xs_append_m(v, &c, 1); + + s++; + } + + if (c != '\0') + s++; + } + else + if (c == '-' || (c >= '0' && c <= '9') || c == '.') { + xs *vn = NULL; + + *t = JS_INTEGER; + + vn = xs_str_new(NULL); + vn = xs_append_m(vn, &c, 1); + + while (((c = *s) >= '0' && c <= '9') || c == '.') { + if (c == '.') + *t = JS_REAL; + + vn = xs_append_m(vn, &c, 1); + s++; + } + + /* convert to XSTYPE_NUMBER */ + v = xs_number_new(atof(vn)); + } + else + if (c == 't' && strncmp(s, "rue", 3) == 0) { + s += 3; + *t = JS_TRUE; + + v = xs_val_new(XSTYPE_TRUE); + } + else + if (c == 'f' && strncmp(s, "alse", 4) == 0) { + s += 4; + *t = JS_FALSE; + + v = xs_val_new(XSTYPE_FALSE); + } + else + if (c == 'n' && strncmp(s, "ull", 3) == 0) { + s += 3; + *t = JS_NULL; + + v = xs_val_new(XSTYPE_NULL); + } + else + *t = JS_ERROR; + + *json = s; + + return v; +} + + +d_char *_xs_json_loads_array(const char **json, js_type *t); +d_char *_xs_json_loads_object(const char **json, js_type *t); + +d_char *_xs_json_loads_value(const char **json, js_type *t, d_char *v) +/* parses a JSON value */ +{ + if (*t == JS_OBRACK) + v = _xs_json_loads_array(json, t); + else + if (*t == JS_OCURLY) + v = _xs_json_loads_object(json, t); + + if (*t >= JS_VALUE) + *t = JS_VALUE; + else + *t = JS_ERROR; + + return v; +} + + +d_char *_xs_json_loads_array(const char **json, js_type *t) +/* parses a JSON array */ +{ + const char *s = *json; + xs *v; + d_char *l; + js_type tt; + + l = xs_list_new(); + + *t = JS_INCOMPLETE; + + v = _xs_json_loads_lexer(&s, &tt); + + if (tt == JS_CBRACK) + *t = JS_ARRAY; + else { + v = _xs_json_loads_value(&s, &tt, v); + + if (tt == JS_VALUE) { + l = xs_list_append(l, v); + + while (*t == JS_INCOMPLETE) { + _xs_json_loads_lexer(&s, &tt); + + if (tt == JS_CBRACK) + *t = JS_ARRAY; + else + if (tt == JS_COMMA) { + xs *v2; + + v2 = _xs_json_loads_lexer(&s, &tt); + v2 = _xs_json_loads_value(&s, &tt, v2); + + if (tt == JS_VALUE) + l = xs_list_append(l, v2); + else + *t = JS_ERROR; + } + else + *t = JS_ERROR; + } + } + else + *t = JS_ERROR; + } + + if (*t == JS_ERROR) { + free(l); + l = NULL; + } + + *json = s; + + return l; +} + + +d_char *_xs_json_loads_object(const char **json, js_type *t) +/* parses a JSON object */ +{ + const char *s = *json; + xs *k1; + d_char *d; + js_type tt; + + d = xs_dict_new(); + + *t = JS_INCOMPLETE; + + k1 = _xs_json_loads_lexer(&s, &tt); + + if (tt == JS_CCURLY) + *t = JS_OBJECT; + else + if (tt == JS_STRING) { + _xs_json_loads_lexer(&s, &tt); + + if (tt == JS_COLON) { + xs *v1; + + v1 = _xs_json_loads_lexer(&s, &tt); + v1 = _xs_json_loads_value(&s, &tt, v1); + + if (tt == JS_VALUE) { + d = xs_dict_append(d, k1, v1); + + while (*t == JS_INCOMPLETE) { + _xs_json_loads_lexer(&s, &tt); + + if (tt == JS_CCURLY) + *t = JS_OBJECT; + else + if (tt == JS_COMMA) { + xs *k; + + k = _xs_json_loads_lexer(&s, &tt); + + if (tt == JS_STRING) { + _xs_json_loads_lexer(&s, &tt); + + if (tt == JS_COLON) { + xs *v; + + v = _xs_json_loads_lexer(&s, &tt); + v = _xs_json_loads_value(&s, &tt, v); + + if (tt == JS_VALUE) + d = xs_dict_append(d, k, v); + else + *t = JS_ERROR; + } + else + *t = JS_ERROR; + } + else + *t = JS_ERROR; + } + else + *t = JS_ERROR; + } + } + else + *t = JS_ERROR; + } + else + *t = JS_ERROR; + } + else + *t = JS_ERROR; + + if (*t == JS_ERROR) { + free(d); + d = NULL; + } + + *json = s; + + return d; +} + + +d_char *xs_json_loads(const char *json) +/* loads a string in JSON format and converts to a multiple data */ +{ + d_char *v = NULL; + js_type t; + + _xs_json_loads_lexer(&json, &t); + + if (t == JS_OBRACK) + v = _xs_json_loads_array(&json, &t); + else + if (t == JS_OCURLY) + v = _xs_json_loads_object(&json, &t); + else + t = JS_ERROR; + + return v; +} + +#endif /* XS_IMPLEMENTATION */ + +#endif /* _XS_JSON_H */ |