X-Git-Url: http://de.git.xonotic.org/?a=blobdiff_plain;f=qcsrc%2Flib%2Fjson.qc;h=555c4b10cc75a6877eb3450bedcde8a29aab87cb;hb=3cd3ce4699814aa2cf2d13c3bd3f5a994fb2c40f;hp=0f2f44c4ec3df06d6bd2a61f378f445e10b0eea0;hpb=e48ff3f92d4cbc542e865518c7b0843fc5ccd54e;p=xonotic%2Fxonotic-data.pk3dir.git diff --git a/qcsrc/lib/json.qc b/qcsrc/lib/json.qc index 0f2f44c4e..555c4b10c 100644 --- a/qcsrc/lib/json.qc +++ b/qcsrc/lib/json.qc @@ -1,19 +1,23 @@ #include "test.qh" STRING_ITERATOR(_json, string_null, 0); +// Store interleaved keys/values in a string buffer +int _json_buffer; +// Last read string +string _json_temp; /** parse a json object */ bool _json_parse_object(); bool _json_parse_members(); bool _json_parse_pair(); bool _json_parse_array(); - bool _json_parse_elements(); bool _json_parse_value(); bool _json_parse_true(); bool _json_parse_false(); bool _json_parse_null(); -bool _json_parse_string(); +bool _json_parse_string(bool add); bool _json_parse_number(); + bool _json_parse_float(); bool _json_parse_int(); #define JSON_BEGIN() int __i = STRING_ITERATOR_SAVE(_json) @@ -23,58 +27,75 @@ bool _json_parse_number(); :fail \ STRING_ITERATOR_LOAD(_json, __i); \ return false; +// Current namespace +string _json_ns; +// Current keys +int _json_keys; bool _json_parse_object() { JSON_BEGIN(); if (STRING_ITERATOR_GET(_json) != '{') JSON_FAIL("expected '{'"); - LOG_INFO(">> object\n"); - _json_parse_members(); + WITH(int, _json_keys, bufstr_add(_json_buffer, "", 0), _json_parse_members()); if (STRING_ITERATOR_GET(_json) != '}') JSON_FAIL("expected '}'"); - LOG_INFO("<< object\n"); JSON_END(); } bool _json_parse_members() { JSON_BEGIN(); - if (!_json_parse_pair()) JSON_FAIL("expected pair"); - if (STRING_ITERATOR_PEEK(_json) == ',') { - STRING_ITERATOR_NEXT(_json); - if (!_json_parse_members()) JSON_FAIL("expected pair"); + for (;;) { + if (!_json_parse_pair()) JSON_FAIL("expected pair"); + if (STRING_ITERATOR_PEEK(_json) == ',') { + STRING_ITERATOR_NEXT(_json); + continue; + } + break; } JSON_END(); } bool _json_parse_pair() { JSON_BEGIN(); - if (!_json_parse_string()) JSON_FAIL("expected string"); + if (!_json_parse_string(false)) JSON_FAIL("expected string"); + string key = _json_temp; + bufstr_set(_json_buffer, _json_keys, cons(bufstr_get(_json_buffer, _json_keys), key)); + key = _json_ns ? strcat(_json_ns, ".", key) : key; + bufstr_add(_json_buffer, key, 0); if (STRING_ITERATOR_GET(_json) != ':') JSON_FAIL("expected ':'"); - if (!_json_parse_value()) JSON_FAIL("expected value"); + bool ret = false; WITH(string, _json_ns, key, ret = _json_parse_value()); + if (!ret) JSON_FAIL("expected value"); JSON_END(); } bool _json_parse_array() { JSON_BEGIN(); if (STRING_ITERATOR_GET(_json) != '[') JSON_FAIL("expected '['"); - LOG_INFO(">> array\n"); - _json_parse_elements(); - if (STRING_ITERATOR_GET(_json) != ']') JSON_FAIL("expected ']'"); - LOG_INFO("<< array\n"); - JSON_END(); -} - - bool _json_parse_elements() { - JSON_BEGIN(); - if (!_json_parse_value()) JSON_FAIL("expected value"); + int len = bufstr_add(_json_buffer, "0", 0); + if (len) bufstr_set(_json_buffer, len - 1, strcat(bufstr_get(_json_buffer, len - 1), ".length")); + bool required = false; + for (int n = 0; ; n++) { + string key = ftos(n); + key = _json_ns ? strcat(_json_ns, ".", key) : key; + int it = bufstr_add(_json_buffer, key, 0); + bool ret = false; WITH(string, _json_ns, key, ret = _json_parse_value()); + if (!ret) { + bufstr_set(_json_buffer, it, string_null); + if (required) JSON_FAIL("expected value"); else break; + } + bufstr_set(_json_buffer, len, ftos(n + 1)); if (STRING_ITERATOR_PEEK(_json) == ',') { STRING_ITERATOR_NEXT(_json); - if (!_json_parse_elements()) JSON_FAIL("expected value"); + required = true; + continue; } - JSON_END(); + break; } + if (STRING_ITERATOR_GET(_json) != ']') JSON_FAIL("expected ']'"); + JSON_END(); +} bool _json_parse_value() { JSON_BEGIN(); - if (!(_json_parse_string() + if (!(_json_parse_string(true) || _json_parse_number() || _json_parse_object() || _json_parse_array() @@ -91,7 +112,7 @@ bool _json_parse_value() { && STRING_ITERATOR_GET(_json) == 'u' && STRING_ITERATOR_GET(_json) == 'e')) JSON_FAIL("expected 'true'"); - LOG_INFO(">> bool (true)\n"); + bufstr_add(_json_buffer, "1", 0); JSON_END(); } @@ -103,7 +124,7 @@ bool _json_parse_value() { && STRING_ITERATOR_GET(_json) == 's' && STRING_ITERATOR_GET(_json) == 'e')) JSON_FAIL("expected 'false'"); - LOG_INFO(">> bool (false)\n"); + bufstr_add(_json_buffer, "0", 0); JSON_END(); } @@ -114,11 +135,11 @@ bool _json_parse_value() { && STRING_ITERATOR_GET(_json) == 'l' && STRING_ITERATOR_GET(_json) == 'l')) JSON_FAIL("expected 'null'"); - LOG_INFO(">> null\n"); + bufstr_add(_json_buffer, "", 0); JSON_END(); } -bool _json_parse_string() { +bool _json_parse_string(bool add) { JSON_BEGIN(); if (STRING_ITERATOR_GET(_json) != '"') JSON_FAIL("expected opening '\"'"); string s = ""; @@ -135,6 +156,7 @@ bool _json_parse_string() { case '\\': esc = "\\"; break; case 'n': esc = "\n"; break; case 't': esc = "\t"; break; + case 'u': esc = "\\u"; break; // TODO } s = strcat(s, esc); } else { @@ -142,16 +164,38 @@ bool _json_parse_string() { } } if (STRING_ITERATOR_GET(_json) != '"') JSON_FAIL("expected closing '\"'"); - LOG_INFOF(">> string ('%s')\n", s); + if (add) bufstr_add(_json_buffer, s, 0); + _json_temp = s; JSON_END(); } bool _json_parse_number() { JSON_BEGIN(); - if (!_json_parse_int()) JSON_FAIL("expected int"); + if (!(_json_parse_float() || _json_parse_int())) JSON_FAIL("expected number"); JSON_END(); } + bool _json_parse_float() { + JSON_BEGIN(); + string s = ""; + bool needdot = true; + for (int c; (c = STRING_ITERATOR_GET(_json)); ) { + if (!(c >= '0' && c <= '9')) { + if (c == '.' && needdot) { + // fine + needdot = false; + } else { + STRING_ITERATOR_UNGET(_json); + break; + } + } + s = strcat(s, chr2str(c)); + } + if (s == "") JSON_FAIL("expected float"); + bufstr_add(_json_buffer, s, 0); + JSON_END(); + } + bool _json_parse_int() { JSON_BEGIN(); string s = ""; @@ -164,15 +208,67 @@ bool _json_parse_number() { s = strcat(s, chr2str(c)); } if (s == "") JSON_FAIL("expected int"); - int i = stof(s); - LOG_INFOF(">> int (%d)\n", i); + if (ftos(stof(s)) != s) JSON_FAIL("expected int"); + bufstr_add(_json_buffer, s, 0); JSON_END(); } -bool json_parse(string in) { - // TODO: remove insignificant whitespace - STRING_ITERATOR_SET(_json, in, 0); - return _json_parse_object(); +int json_parse(string in, bool() func) { + string trimmed = ""; + LABEL(trim) { + int o = strstrofs(in, "\"", 0); + if (o >= 0) { + string part = substring(in, 0, o + 1); in = substring(in, o + 1, -1); + part = strreplace(" ", "", part); + part = strreplace("\n", "", part); + trimmed = strcat(trimmed, part); + goto trim_str; + } else { + string part = in; + part = strreplace(" ", "", part); + part = strreplace("\n", "", part); + trimmed = strcat(trimmed, part); + goto done; + } + } + LABEL(trim_str) { + int o = strstrofs(in, "\"", 0); + int esc = strstrofs(in, "\\\"", 0); + if (o < esc || esc < 0) { + // simple string + string part = substring(in, 0, o + 1); in = substring(in, o + 1, -1); + trimmed = strcat(trimmed, part); + goto trim; + } else { + // has escape + string part = substring(in, 0, esc + 2); in = substring(in, esc + 2, -1); + trimmed = strcat(trimmed, part); + goto trim_str; + } + } + LABEL(done); + + STRING_ITERATOR_SET(_json, trimmed, 0); + _json_buffer = buf_create(); + bool ret = func(); + if (!ret) { + buf_del(_json_buffer); + _json_buffer = -1; + } + return _json_buffer; +} + +string json_get(int buf, string key) +{ + for (int i = 1, n = buf_getsize(buf); i < n; i += 2) { + if (bufstr_get(buf, i) == key) return bufstr_get(buf, i + 1); + } + return string_null; +} + +void json_del(int buf) +{ + buf_del(buf); } #undef JSON_BEGIN @@ -181,6 +277,18 @@ bool json_parse(string in) { TEST(json, Parse) { - EXPECT_EQ(true, json_parse("{\"string\":\"string\",\"int\":123,\"bool\":true,\"null\":null,\"obj\":{\"arr\":[1,2,3]}}")); - SUCCEED(); + string s = "{\n\ + \"m_string\": \"\\\"string\\\"\",\n\ + \"m_int\": 123,\n\ + \"m_bool\": true,\n\ + \"m_null\": null,\n\ + \"m_obj\": { },\n\ + \"m_arr\": [ ]\n}"; // " + print(s, "\n"); + int buf = json_parse(s, _json_parse_object); + EXPECT_NE(-1, buf); + for (int i = 0, n = buf_getsize(buf); i < n; ++i) { + print(bufstr_get(buf, i), "\n"); + } + SUCCEED(); }