Xonotic
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
7 string _json_temp;
8 
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
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 
44  ERASEABLE
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
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) == ',') {
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
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
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
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
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
184  JSON_BEGIN();
185  if (!(_json_parse_float() || _json_parse_int())) JSON_FAIL("expected number");
186  JSON_END();
187 }
188 
189  ERASEABLE
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
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);
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 }
#define JSON_BEGIN()
Definition: json.qc:23
string string_null
Definition: nil.qh:9
bool _json_parse_value()
Definition: json.qc:101
bool _json_parse_array()
Definition: json.qc:73
ERASEABLE int json_parse(string in, bool() func)
Definition: json.qc:230
bool _json_parse_number()
Definition: json.qc:183
bool _json_parse_members()
Definition: json.qc:45
TEST(json, Parse)
Definition: json.qc:302
#define EXPECT_NE(val1, val2)
Definition: test.qh:52
string _json_temp
Definition: json.qc:7
#define ERASEABLE
Definition: _all.inc:35
bool _json_parse_false()
Definition: json.qc:126
string _json_ns
Definition: json.qc:31
#define SUCCEED()
Must be present at the end of a test.
Definition: test.qh:15
#define buf_create
Definition: dpextensions.qh:63
ERASEABLE string json_get(int buf, string key)
Definition: json.qc:276
#define STRING_ITERATOR_UNGET(this)
Definition: iter.qh:58
#define STRING_ITERATOR_NEXT(this)
Definition: iter.qh:57
STRING_ITERATOR(_json, string_null, 0)
spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 f1 s1 strcat(_("Level %s: "), "^BG%s\3\, _("^BGPress ^F2%s^BG to enter the game"))
ERASEABLE void json_dump(int buf)
Definition: json.qc:291
#define JSON_END()
Definition: json.qc:25
#define strstrofs
Definition: dpextensions.qh:42
bool _json_parse_true()
Definition: json.qc:114
int _json_keys
Definition: json.qc:33
bool _json_parse_string(bool add)
Definition: json.qc:151
#define STRING_ITERATOR_GET(this)
Definition: iter.qh:55
bool _json_parse_int()
Definition: json.qc:212
int _json_buffer
Definition: json.qc:5
bool _json_parse_object()
parse a json object
Definition: json.qc:36
#define JSON_FAIL(reason)
Definition: json.qc:24
#define LABEL(id)
Definition: compiler.qh:36
#define WITH(type, name, value, block)
Definition: misc.qh:40
#define STRING_ITERATOR_SET(this, s, i)
Definition: iter.qh:49
ERASEABLE string cons(string a, string b)
Definition: string.qh:257
bool _json_parse_pair()
Definition: json.qc:59
#define STRING_ITERATOR_PEEK(this)
Definition: iter.qh:56
#define chr2str
Definition: dpextensions.qh:48
ERASEABLE void json_del(int buf)
Definition: json.qc:285
bool _json_parse_float()
Definition: json.qc:190
bool _json_parse_null()
Definition: json.qc:139