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 "test.qh"
2
4// Store interleaved keys/values in a string buffer
6// Last read string
7string _json_temp;
8
12 bool _json_parse_pair();
15 bool _json_parse_true();
16 bool _json_parse_false();
17 bool _json_parse_null();
18bool _json_parse_string(bool add);
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
31string _json_ns;
32// Current keys
34
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
46 JSON_BEGIN();
47 for (;;) {
48 if (!_json_parse_pair()) JSON_FAIL("expected pair");
49 if (STRING_ITERATOR_PEEK(_json) == ',') {
51 continue;
52 }
53 break;
54 }
55 JSON_END();
56 }
57
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
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) == ',') {
91 required = true;
92 continue;
93 }
94 break;
95 }
96 if (STRING_ITERATOR_GET(_json) != ']') JSON_FAIL("expected ']'");
97 JSON_END();
98}
99
102 JSON_BEGIN();
103 if (!(_json_parse_string(true)
109 || _json_parse_null())) JSON_FAIL("expected value");
110 JSON_END();
111}
112
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
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
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
151bool _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 == '"') {
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
184 JSON_BEGIN();
185 if (!(_json_parse_float() || _json_parse_int())) JSON_FAIL("expected number");
186 JSON_END();
187}
188
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 {
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
213 JSON_BEGIN();
214 string s = "";
215 for (int c; (c = STRING_ITERATOR_GET(_json)); ) {
216 if (!(c >= '0' && c <= '9')) {
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
230int 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);
267 bool ret = func();
268 if (!ret) {
269 buf_del(_json_buffer);
270 _json_buffer = -1;
271 }
272 return _json_buffer;
273}
274
276string 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
285void json_del(int buf)
286{
287 buf_del(buf);
288}
289
291void 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
302TEST(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}
#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:57
#define STRING_ITERATOR_UNGET(this)
Definition iter.qh:58
#define STRING_ITERATOR_SET(this, s, i)
Definition iter.qh:49
#define STRING_ITERATOR_GET(this)
Definition iter.qh:55
#define STRING_ITERATOR_PEEK(this)
Definition iter.qh:56
bool _json_parse_float()
Definition json.qc:190
ERASEABLE void json_del(int buf)
Definition json.qc:285
ERASEABLE int json_parse(string in, bool() func)
Definition json.qc:230
bool _json_parse_number()
Definition json.qc:183
#define JSON_FAIL(reason)
Definition json.qc:24
#define JSON_BEGIN()
Definition json.qc:23
bool _json_parse_true()
Definition json.qc:114
bool _json_parse_array()
Definition json.qc:73
ERASEABLE string json_get(int buf, string key)
Definition json.qc:276
bool _json_parse_pair()
Definition json.qc:59
bool _json_parse_false()
Definition json.qc:126
int _json_buffer
Definition json.qc:5
bool _json_parse_null()
Definition json.qc:139
string _json_temp
Definition json.qc:7
#define JSON_END()
Definition json.qc:25
bool _json_parse_object()
parse a json object
Definition json.qc:36
int _json_keys
Definition json.qc:33
string _json_ns
Definition json.qc:31
bool _json_parse_string(bool add)
Definition json.qc:151
ERASEABLE void json_dump(int buf)
Definition json.qc:291
bool _json_parse_int()
Definition json.qc:212
bool _json_parse_value()
Definition json.qc:101
bool _json_parse_members()
Definition json.qc:45
#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:276
#define TEST(suite, test)
Use UpperCamelCase for suite and test only.
Definition test.qh:6
#define EXPECT_NE(val1, val2)
Definition test.qh:52
#define SUCCEED()
Must be present at the end of a test.
Definition test.qh:15