Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
json.qc
Go to the documentation of this file.
1#include "json.qh"
2
3#include "test.qh"
4
6
8string _json_temp;
9string _json_ns;
11
12#define JSON_BEGIN() int __i = STRING_ITERATOR_SAVE(_json)
13#define JSON_FAIL(reason) goto fail
14#define JSON_END() \
15 return true; \
16LABEL(fail) \
17 STRING_ITERATOR_LOAD(_json, __i); \
18 return false;
19
22{
23 JSON_BEGIN();
24 if (STRING_ITERATOR_GET(_json) != '{')
25 JSON_FAIL("expected '{'");
26 WITH(int, _json_keys, bufstr_add(_json_buffer, "", 0), _json_parse_members());
27 if (STRING_ITERATOR_GET(_json) != '}')
28 JSON_FAIL("expected '}'");
29 JSON_END();
30}
31
34 {
35 JSON_BEGIN();
36 for (;;)
37 {
38 if (!_json_parse_pair())
39 JSON_FAIL("expected pair");
40 if (STRING_ITERATOR_PEEK(_json) == ',')
41 {
43 continue;
44 }
45 break;
46 }
47 JSON_END();
48 }
49
52 {
53 JSON_BEGIN();
54 if (!_json_parse_string(false))
55 JSON_FAIL("expected string");
56 string key = _json_temp;
57 bufstr_set(_json_buffer, _json_keys, cons(bufstr_get(_json_buffer, _json_keys), key));
58 if (_json_ns)
59 key = strcat(_json_ns, ".", key);
60 bufstr_add(_json_buffer, key, 0);
61 if (STRING_ITERATOR_GET(_json) != ':')
62 JSON_FAIL("expected ':'");
63 bool ret = false;
64 WITH(string, _json_ns, key, ret = _json_parse_value());
65 if (!ret)
66 JSON_FAIL("expected value");
67 JSON_END();
68 }
69
72{
73 JSON_BEGIN();
74 if (STRING_ITERATOR_GET(_json) != '[')
75 JSON_FAIL("expected '['");
76 int len = bufstr_add(_json_buffer, "0", 0);
77 if (len)
78 bufstr_set(_json_buffer, len - 1, strcat(bufstr_get(_json_buffer, len - 1), ".length"));
79 bool required = false;
80 for (int n = 0; ; ++n)
81 {
82 string key = ftos(n);
83 if (_json_ns)
84 key = strcat(_json_ns, ".", key);
85 int it = bufstr_add(_json_buffer, key, 0);
86 bool ret = false;
87 WITH(string, _json_ns, key, ret = _json_parse_value());
88 if (!ret)
89 {
90 bufstr_free(_json_buffer, it);
91 if (required)
92 JSON_FAIL("expected value");
93 else
94 break;
95 }
96 bufstr_set(_json_buffer, len, ftos(n + 1));
97 if (STRING_ITERATOR_PEEK(_json) == ',')
98 {
100 required = true;
101 continue;
102 }
103 break;
104 }
105 if (STRING_ITERATOR_GET(_json) != ']')
106 JSON_FAIL("expected ']'");
107 JSON_END();
108}
109
112{
113 JSON_BEGIN();
114 if (!(_json_parse_string(true)
120 || _json_parse_null()))
121 JSON_FAIL("expected value");
122 JSON_END();
123}
124
127 {
128 JSON_BEGIN();
129 if (!(STRING_ITERATOR_GET(_json) == 't'
130 && STRING_ITERATOR_GET(_json) == 'r'
131 && STRING_ITERATOR_GET(_json) == 'u'
132 && STRING_ITERATOR_GET(_json) == 'e'))
133 JSON_FAIL("expected 'true'");
134 bufstr_add(_json_buffer, "1", 0);
135 JSON_END();
136 }
137
140 {
141 JSON_BEGIN();
142 if (!(STRING_ITERATOR_GET(_json) == 'f'
143 && STRING_ITERATOR_GET(_json) == 'a'
144 && STRING_ITERATOR_GET(_json) == 'l'
145 && STRING_ITERATOR_GET(_json) == 's'
146 && STRING_ITERATOR_GET(_json) == 'e'))
147 JSON_FAIL("expected 'false'");
148 bufstr_add(_json_buffer, "0", 0);
149 JSON_END();
150 }
151
154 {
155 JSON_BEGIN();
156 if (!(STRING_ITERATOR_GET(_json) == 'n'
157 && STRING_ITERATOR_GET(_json) == 'u'
158 && STRING_ITERATOR_GET(_json) == 'l'
159 && STRING_ITERATOR_GET(_json) == 'l'))
160 JSON_FAIL("expected 'null'");
161 bufstr_add(_json_buffer, "", 0);
162 JSON_END();
163 }
164
166bool _json_parse_string(bool add)
167{
168 JSON_BEGIN();
169 if (STRING_ITERATOR_GET(_json) != '"')
170 JSON_FAIL("expected opening '\"'");
171 string s = "";
172 for (int c; (c = STRING_ITERATOR_GET(_json)); )
173 {
174 if (c == '"')
175 {
177 break;
178 }
179 else if (c == '\\')
180 {
181 string esc;
182 switch (STRING_ITERATOR_GET(_json))
183 {
184 default:
185 JSON_FAIL("expected ( '\"' | '\\' | 'n' | 't' )");
186 case '"': esc = "\""; break;
187 case '\\': esc = "\\"; break;
188 case 'n': esc = "\n"; break;
189 case 't': esc = "\t"; break;
190 case 'u': esc = "\\u"; break; // TODO
191 case '/': esc = "/"; break;
192 }
193 s = strcat(s, esc);
194 }
195 else
196 s = strcat(s, chr2str(c));
197 }
198 if (STRING_ITERATOR_GET(_json) != '"')
199 JSON_FAIL("expected closing '\"'");
200 if (add)
201 bufstr_add(_json_buffer, s, 0);
202 _json_temp = s;
203 JSON_END();
204}
205
208{
209 JSON_BEGIN();
211 JSON_FAIL("expected number");
212 JSON_END();
213}
214
217 {
218 JSON_BEGIN();
219 string s = "";
220 bool needdot = true;
221 for (int c; (c = STRING_ITERATOR_GET(_json)); )
222 {
223 if (c < '0' || c > '9')
224 {
225 if (c == '.' && needdot) // fine
226 needdot = false;
227 else
228 {
230 break;
231 }
232 }
233 s = strcat(s, chr2str(c));
234 }
235 if (s == "")
236 JSON_FAIL("expected float");
237 bufstr_add(_json_buffer, s, 0);
238 JSON_END();
239 }
240
243 {
244 JSON_BEGIN();
245 string s = "";
246 for (int c; (c = STRING_ITERATOR_GET(_json)); )
247 {
248 if (c < '0' || c > '9')
249 {
251 break;
252 }
253 if (s == "" && c == '0')
254 JSON_FAIL("expected [1-9]");
255 s = strcat(s, chr2str(c));
256 }
257 if (s == "")
258 JSON_FAIL("expected int");
259 if (ftos(stof(s)) != s)
260 JSON_FAIL("expected int");
261 bufstr_add(_json_buffer, s, 0);
262 JSON_END();
263 }
264
266int json_parse(string in, bool() func)
267{
268 string trimmed = "";
269 LABEL(trim)
270 {
271 int o = strstrofs(in, "\"", 0);
272 if (o >= 0)
273 {
274 string part = substring(in, 0, o + 1);
275 in = substring(in, o + 1, -1);
276 part = strreplace(" ", "", part);
277 part = strreplace("\n", "", part);
278 trimmed = strcat(trimmed, part);
279 goto trim_str;
280 }
281 else
282 {
283 string part = in;
284 part = strreplace(" ", "", part);
285 part = strreplace("\n", "", part);
286 trimmed = strcat(trimmed, part);
287 goto done;
288 }
289 }
290 LABEL(trim_str)
291 {
292 int o = strstrofs(in, "\"", 0);
293 int esc = strstrofs(in, "\\\"", 0);
294 if (o < esc || esc < 0) // simple string
295 {
296 string part = substring(in, 0, o + 1);
297 in = substring(in, o + 1, -1);
298 trimmed = strcat(trimmed, part);
299 goto trim;
300 }
301 else // has escape
302 {
303 string part = substring(in, 0, esc + 2);
304 in = substring(in, esc + 2, -1);
305 trimmed = strcat(trimmed, part);
306 goto trim_str;
307 }
308 }
309 LABEL(done);
310
311 STRING_ITERATOR_SET(_json, trimmed, 0);
313 bool ret = func();
314 if (!ret)
315 {
316 buf_del(_json_buffer);
317 _json_buffer = -1;
318 }
319 return _json_buffer;
320}
321
323string json_get(int buf, string key)
324{
325 for (int i = 1, n = buf_getsize(buf); i < n; i += 2)
326 if (bufstr_get(buf, i) == key)
327 return bufstr_get(buf, i + 1);
328 return string_null;
329}
330
332void json_del(int buf)
333{
334 buf_del(buf);
335}
336
338void json_dump(int buf)
339{
340 for (int i = 0, n = buf_getsize(buf); i < n; ++i)
341 print(bufstr_get(buf, i), "\n");
342}
343
344#undef JSON_BEGIN
345#undef JSON_FAIL
346#undef JSON_END
347
348TEST(json, Parse)
349{
350 string s = "{\n"
351 " \"m_string\": \"\\\"string\\\"\",\n"
352 " \"m_int\": 123,\n"
353 " \"m_bool\": true,\n"
354 " \"m_null\": null,\n"
355 " \"m_obj\": { },\n"
356 " \"m_arr\": [ ]\n"
357 "}";
358 print(s, "\n");
359 int buf = json_parse(s, _json_parse_object);
360 EXPECT_NE(-1, buf);
361 json_dump(buf);
362 SUCCEED();
363}
#define LABEL(id)
Definition compiler.qh:34
#define strstrofs
#define buf_create
#define chr2str
#define STRING_ITERATOR(this, s, i)
Definition iter.qh:45
#define STRING_ITERATOR_NEXT(this)
Definition iter.qh:56
#define STRING_ITERATOR_UNGET(this)
Definition iter.qh:57
#define STRING_ITERATOR_SET(this, s, i)
Definition iter.qh:49
#define STRING_ITERATOR_GET(this)
Definition iter.qh:54
#define STRING_ITERATOR_PEEK(this)
Definition iter.qh:55
ERASEABLE bool _json_parse_float()
Definition json.qc:216
ERASEABLE void json_del(int buf)
Definition json.qc:332
ERASEABLE int json_parse(string in, bool() func)
Definition json.qc:266
ERASEABLE bool _json_parse_number()
Definition json.qc:207
#define JSON_FAIL(reason)
Definition json.qc:13
#define JSON_BEGIN()
Definition json.qc:12
ERASEABLE bool _json_parse_true()
Definition json.qc:126
ERASEABLE bool _json_parse_array()
Definition json.qc:71
ERASEABLE string json_get(int buf, string key)
Definition json.qc:323
ERASEABLE bool _json_parse_pair()
Definition json.qc:51
ERASEABLE bool _json_parse_false()
Definition json.qc:139
int _json_buffer
Store interleaved keys/values in a string buffer.
Definition json.qc:7
ERASEABLE bool _json_parse_null()
Definition json.qc:153
string _json_temp
Last read string.
Definition json.qc:8
#define JSON_END()
Definition json.qc:14
ERASEABLE bool _json_parse_object()
Parse a json object.
Definition json.qc:21
int _json_keys
Current keys.
Definition json.qc:10
string _json_ns
Current namespace.
Definition json.qc:9
ERASEABLE bool _json_parse_string(bool add)
Definition json.qc:166
ERASEABLE void json_dump(int buf)
Definition json.qc:338
ERASEABLE bool _json_parse_int()
Definition json.qc:242
ERASEABLE bool _json_parse_value()
Definition json.qc:111
ERASEABLE bool _json_parse_members()
Definition json.qc:33
#define ERASEABLE
Definition _all.inc:37
float stof(string val,...)
string substring(string s, float start, float length)
string ftos(float f)
void print(string text,...)
#define WITH(type, name, value, block)
Definition misc.qh:37
string string_null
Definition nil.qh:9
strcat(_("^F4Countdown stopped!"), "\n^BG", _("Teams are too unbalanced."))
ERASEABLE string cons(string a, string b)
Definition string.qh:277
#define TEST(suite, test)
Use UpperCamelCase for suite and test only.
Definition test.qh:6
#define EXPECT_NE(val1, val2)
Definition test.qh:53
#define SUCCEED()
Must be present at the end of a test.
Definition test.qh:17