]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/lib/json.qc
Merge branch 'martin-t/units' into 'master'
[xonotic/xonotic-data.pk3dir.git] / qcsrc / lib / json.qc
1 #include "test.qh"
2
3 STRING_ITERATOR(_json, string_null, 0);
4 // Store interleaved keys/values in a string buffer
5 int _json_buffer;
6 // Last read string
7 string _json_temp;
8
9 /** parse a json object */
10 bool _json_parse_object();
11     bool _json_parse_members();
12         bool _json_parse_pair();
13 bool _json_parse_array();
14 bool _json_parse_value();
15     bool _json_parse_true();
16     bool _json_parse_false();
17     bool _json_parse_null();
18 bool _json_parse_string(bool add);
19 bool _json_parse_number();
20     bool _json_parse_float();
21     bool _json_parse_int();
22
23 #define JSON_BEGIN() int __i = STRING_ITERATOR_SAVE(_json)
24 #define JSON_FAIL(reason) goto fail
25 #define JSON_END() \
26    return true; \
27 :fail \
28    STRING_ITERATOR_LOAD(_json, __i); \
29    return false;
30 // Current namespace
31 string _json_ns;
32 // Current keys
33 int _json_keys;
34
35 ERASEABLE
36 bool _json_parse_object() {
37     JSON_BEGIN();
38     if (STRING_ITERATOR_GET(_json) != '{') JSON_FAIL("expected '{'");
39     WITH(int, _json_keys, bufstr_add(_json_buffer, "", 0), _json_parse_members());
40     if (STRING_ITERATOR_GET(_json) != '}') JSON_FAIL("expected '}'");
41     JSON_END();
42 }
43
44     ERASEABLE
45     bool _json_parse_members() {
46         JSON_BEGIN();
47         for (;;) {
48             if (!_json_parse_pair()) JSON_FAIL("expected pair");
49             if (STRING_ITERATOR_PEEK(_json) == ',') {
50                 STRING_ITERATOR_NEXT(_json);
51                 continue;
52             }
53             break;
54         }
55         JSON_END();
56     }
57
58         ERASEABLE
59         bool _json_parse_pair() {
60             JSON_BEGIN();
61             if (!_json_parse_string(false)) JSON_FAIL("expected string");
62             string key = _json_temp;
63             bufstr_set(_json_buffer, _json_keys, cons(bufstr_get(_json_buffer, _json_keys), key));
64             key = _json_ns ? strcat(_json_ns, ".", key) : key;
65             bufstr_add(_json_buffer, key, 0);
66             if (STRING_ITERATOR_GET(_json) != ':') JSON_FAIL("expected ':'");
67             bool ret = false; WITH(string, _json_ns, key, ret = _json_parse_value());
68             if (!ret) JSON_FAIL("expected value");
69             JSON_END();
70         }
71
72 ERASEABLE
73 bool _json_parse_array() {
74     JSON_BEGIN();
75     if (STRING_ITERATOR_GET(_json) != '[') JSON_FAIL("expected '['");
76     int len = bufstr_add(_json_buffer, "0", 0);
77     if (len) bufstr_set(_json_buffer, len - 1, strcat(bufstr_get(_json_buffer, len - 1), ".length"));
78     bool required = false;
79     for (int n = 0; ; n++) {
80         string key = ftos(n);
81         key = _json_ns ? strcat(_json_ns, ".", key) : key;
82         int it = bufstr_add(_json_buffer, key, 0);
83         bool ret = false; WITH(string, _json_ns, key, ret = _json_parse_value());
84         if (!ret) {
85             bufstr_free(_json_buffer, it);
86             if (required) JSON_FAIL("expected value"); else break;
87         }
88         bufstr_set(_json_buffer, len, ftos(n + 1));
89         if (STRING_ITERATOR_PEEK(_json) == ',') {
90             STRING_ITERATOR_NEXT(_json);
91             required = true;
92             continue;
93         }
94         break;
95     }
96     if (STRING_ITERATOR_GET(_json) != ']') JSON_FAIL("expected ']'");
97     JSON_END();
98 }
99
100 ERASEABLE
101 bool _json_parse_value() {
102     JSON_BEGIN();
103     if (!(_json_parse_string(true)
104         || _json_parse_number()
105         || _json_parse_object()
106         || _json_parse_array()
107         || _json_parse_true()
108         || _json_parse_false()
109         || _json_parse_null())) JSON_FAIL("expected value");
110     JSON_END();
111 }
112
113     ERASEABLE
114     bool _json_parse_true() {
115         JSON_BEGIN();
116         if (!(STRING_ITERATOR_GET(_json) == 't'
117             && STRING_ITERATOR_GET(_json) == 'r'
118             && STRING_ITERATOR_GET(_json) == 'u'
119             && STRING_ITERATOR_GET(_json) == 'e'))
120             JSON_FAIL("expected 'true'");
121         bufstr_add(_json_buffer, "1", 0);
122         JSON_END();
123     }
124
125     ERASEABLE
126     bool _json_parse_false() {
127         JSON_BEGIN();
128         if (!(STRING_ITERATOR_GET(_json) == 'f'
129             && STRING_ITERATOR_GET(_json) == 'a'
130             && STRING_ITERATOR_GET(_json) == 'l'
131             && STRING_ITERATOR_GET(_json) == 's'
132             && STRING_ITERATOR_GET(_json) == 'e'))
133             JSON_FAIL("expected 'false'");
134         bufstr_add(_json_buffer, "0", 0);
135         JSON_END();
136     }
137
138     ERASEABLE
139     bool _json_parse_null() {
140         JSON_BEGIN();
141         if (!(STRING_ITERATOR_GET(_json) == 'n'
142             && STRING_ITERATOR_GET(_json) == 'u'
143             && STRING_ITERATOR_GET(_json) == 'l'
144             && STRING_ITERATOR_GET(_json) == 'l'))
145             JSON_FAIL("expected 'null'");
146         bufstr_add(_json_buffer, "", 0);
147         JSON_END();
148     }
149
150 ERASEABLE
151 bool _json_parse_string(bool add) {
152     JSON_BEGIN();
153     if (STRING_ITERATOR_GET(_json) != '"') JSON_FAIL("expected opening '\"'");
154     string s = "";
155     for (int c; (c = STRING_ITERATOR_GET(_json)); ) {
156         if (c == '"') {
157             STRING_ITERATOR_UNGET(_json);
158             break;
159         } else if (c == '\\') {
160             string esc;
161             switch (STRING_ITERATOR_GET(_json)) {
162                 default:
163                     JSON_FAIL("expected ( '\"' | '\\' | 'n' | 't' )");
164                 case '"': esc = "\""; break;
165                 case '\\': esc = "\\"; break;
166                 case 'n': esc = "\n"; break;
167                 case 't': esc = "\t"; break;
168                 case 'u': esc = "\\u"; break; // TODO
169                 case '/': esc = "/"; break;
170             }
171             s = strcat(s, esc);
172         } else {
173             s = strcat(s, chr2str(c));
174         }
175     }
176     if (STRING_ITERATOR_GET(_json) != '"') JSON_FAIL("expected closing '\"'");
177     if (add) bufstr_add(_json_buffer, s, 0);
178     _json_temp = s;
179     JSON_END();
180 }
181
182 ERASEABLE
183 bool _json_parse_number() {
184     JSON_BEGIN();
185     if (!(_json_parse_float() || _json_parse_int())) JSON_FAIL("expected number");
186     JSON_END();
187 }
188
189     ERASEABLE
190     bool _json_parse_float() {
191         JSON_BEGIN();
192         string s = "";
193         bool needdot = true;
194         for (int c; (c = STRING_ITERATOR_GET(_json)); ) {
195             if (!(c >= '0' && c <= '9')) {
196                 if (c == '.' && needdot) {
197                     // fine
198                     needdot = false;
199                 } else {
200                     STRING_ITERATOR_UNGET(_json);
201                     break;
202                 }
203             }
204             s = strcat(s, chr2str(c));
205         }
206         if (s == "") JSON_FAIL("expected float");
207         bufstr_add(_json_buffer, s, 0);
208         JSON_END();
209     }
210
211     ERASEABLE
212     bool _json_parse_int() {
213         JSON_BEGIN();
214         string s = "";
215         for (int c; (c = STRING_ITERATOR_GET(_json)); ) {
216             if (!(c >= '0' && c <= '9')) {
217                 STRING_ITERATOR_UNGET(_json);
218                 break;
219             }
220             if (s == "" && c == '0') JSON_FAIL("expected [1-9]");
221             s = strcat(s, chr2str(c));
222         }
223         if (s == "") JSON_FAIL("expected int");
224         if (ftos(stof(s)) != s) JSON_FAIL("expected int");
225         bufstr_add(_json_buffer, s, 0);
226         JSON_END();
227     }
228
229 ERASEABLE
230 int json_parse(string in, bool() func) {
231     string trimmed = "";
232     LABEL(trim) {
233         int o = strstrofs(in, "\"", 0);
234         if (o >= 0) {
235             string part = substring(in, 0, o + 1); in = substring(in, o + 1, -1);
236             part = strreplace(" ", "", part);
237             part = strreplace("\n", "", part);
238             trimmed = strcat(trimmed, part);
239             goto trim_str;
240         } else {
241             string part = in;
242             part = strreplace(" ", "", part);
243             part = strreplace("\n", "", part);
244             trimmed = strcat(trimmed, part);
245             goto done;
246         }
247     }
248     LABEL(trim_str) {
249         int o = strstrofs(in, "\"", 0);
250         int esc = strstrofs(in, "\\\"", 0);
251         if (o < esc || esc < 0) {
252             // simple string
253             string part = substring(in, 0, o + 1); in = substring(in, o + 1, -1);
254             trimmed = strcat(trimmed, part);
255             goto trim;
256         } else {
257             // has escape
258             string part = substring(in, 0, esc + 2); in = substring(in, esc + 2, -1);
259             trimmed = strcat(trimmed, part);
260             goto trim_str;
261         }
262     }
263     LABEL(done);
264
265     STRING_ITERATOR_SET(_json, trimmed, 0);
266     _json_buffer = buf_create();
267     bool ret = func();
268     if (!ret) {
269         buf_del(_json_buffer);
270         _json_buffer = -1;
271     }
272     return _json_buffer;
273 }
274
275 ERASEABLE
276 string json_get(int buf, string key)
277 {
278     for (int i = 1, n = buf_getsize(buf); i < n; i += 2) {
279         if (bufstr_get(buf, i) == key) return bufstr_get(buf, i + 1);
280     }
281     return string_null;
282 }
283
284 ERASEABLE
285 void json_del(int buf)
286 {
287     buf_del(buf);
288 }
289
290 ERASEABLE
291 void json_dump(int buf)
292 {
293     for (int i = 0, n = buf_getsize(buf); i < n; ++i) {
294         print(bufstr_get(buf, i), "\n");
295     }
296 }
297
298 #undef JSON_BEGIN
299 #undef JSON_FAIL
300 #undef JSON_END
301
302 TEST(json, Parse)
303 {
304     string s = "{\n\
305     \"m_string\": \"\\\"string\\\"\",\n\
306     \"m_int\": 123,\n\
307     \"m_bool\": true,\n\
308     \"m_null\": null,\n\
309     \"m_obj\": { },\n\
310     \"m_arr\": [ ]\n}"; // "
311     print(s, "\n");
312     int buf = json_parse(s, _json_parse_object);
313     EXPECT_NE(-1, buf);
314     json_dump(buf);
315     SUCCEED();
316 }