Fixed newline characters throughout the code
[com/gs-lite.git] / src / lib / gscpaux / json.cpp
1 // Distributed under the MIT license. Copyright (c) 2010, Ivan Vashchaev
2
3 #include <string.h>
4 #include "json.h"
5
6 // true if character represent a digit
7 #define IS_DIGIT(c) (c >= '0' && c <= '9')
8
9 // convert string to integer
10 static char *atoi(char *first, char *last, int *out)
11 {
12         int sign = 1;
13         if (first != last)
14         {
15                 if (*first == '-')
16                 {
17                         sign = -1;
18                         ++first;
19                 }
20                 else if (*first == '+')
21                 {
22                         ++first;
23                 }
24         }
25
26         int result = 0;
27         for (; first != last && IS_DIGIT(*first); ++first)
28         {
29                 result = 10 * result + (*first - '0');
30         }
31         *out = result * sign;
32
33         return first;
34 }
35
36 // convert hexadecimal string to unsigned integer
37 static char *hatoui(char *first, char *last, unsigned int *out)
38 {
39         unsigned int result = 0;
40         for (; first != last; ++first)
41         {
42                 int digit;
43                 if (IS_DIGIT(*first))
44                 {
45                         digit = *first - '0';
46                 }
47                 else if (*first >= 'a' && *first <= 'f')
48                 {
49                         digit = *first - 'a' + 10;
50                 }
51                 else if (*first >= 'A' && *first <= 'F')
52                 {
53                         digit = *first - 'A' + 10;
54                 }
55                 else
56                 {
57                         break;
58                 }
59                 result = 16 * result + (unsigned int)digit;
60         }
61         *out = result;
62
63         return first;
64 }
65
66 // convert string to floating point
67 static char *atof(char *first, char *last, float *out)
68 {
69         // sign
70         float sign = 1;
71         if (first != last)
72         {
73                 if (*first == '-')
74                 {
75                         sign = -1;
76                         ++first;
77                 }
78                 else if (*first == '+')
79                 {
80                         ++first;
81                 }
82         }
83
84         // integer part
85         float result = 0;
86         for (; first != last && IS_DIGIT(*first); ++first)
87         {
88                 result = 10 * result + (float)(*first - '0');
89         }
90
91         // fraction part
92         if (first != last && *first == '.')
93         {
94                 ++first;
95
96                 float inv_base = 0.1f;
97                 for (; first != last && IS_DIGIT(*first); ++first)
98                 {
99                         result += (float)(*first - '0') * inv_base;
100                         inv_base *= 0.1f;
101                 }
102         }
103
104         // result w\o exponent
105         result *= sign;
106
107         // exponent
108         bool exponent_negative = false;
109         int exponent = 0;
110         if (first != last && (*first == 'e' || *first == 'E'))
111         {
112                 ++first;
113
114                 if (*first == '-')
115                 {
116                         exponent_negative = true;
117                         ++first;
118                 }
119                 else if (*first == '+')
120                 {
121                         ++first;
122                 }
123
124                 for (; first != last && IS_DIGIT(*first); ++first)
125                 {
126                         exponent = 10 * exponent + (*first - '0');
127                 }
128         }
129
130         if (exponent)
131         {
132                 float power_of_ten = 10;
133                 for (; exponent > 1; exponent--)
134                 {
135                         power_of_ten *= 10;
136                 }
137
138                 if (exponent_negative)
139                 {
140                         result /= power_of_ten;
141                 }
142                 else
143                 {
144                         result *= power_of_ten;
145                 }
146         }
147
148         *out = result;
149
150         return first;
151 }
152
153 static inline json_value *json_alloc(block_allocator *allocator)
154 {
155         json_value *value = (json_value *)allocator->malloc(sizeof(json_value));
156         memset(value, 0, sizeof(json_value));
157         return value;
158 }
159
160 static inline void json_append(json_value *lhs, json_value *rhs)
161 {
162         rhs->parent = lhs;
163         if (lhs->last_child)
164         {
165                 lhs->last_child = lhs->last_child->next_sibling = rhs;
166         }
167         else
168         {
169                 lhs->first_child = lhs->last_child = rhs;
170         }
171 }
172
173 #define ERROR(it, desc)\
174         *error_pos = it;\
175         *error_desc = desc;\
176         *error_line = 1 - escaped_newlines;\
177         for (char *c = it; c != source; --c)\
178                 if (*c == '\n') ++*error_line;\
179         return 0
180
181 #define CHECK_TOP() if (!top) {ERROR(it, "Unexpected character");}
182
183 json_value *json_parse(char *source, char **error_pos, const char **error_desc, int *error_line, block_allocator *allocator)
184 {
185         json_value *root = 0;
186         json_value *top = 0;
187
188         char *name = 0;
189         char *it = source;
190
191         int escaped_newlines = 0;
192
193         while (*it)
194         {
195                 // skip white space
196                 while (*it == '\x20' || *it == '\x9' || *it == '\xD' || *it == '\xA')
197                 {
198                         ++it;
199                 }
200
201                 switch (*it)
202                 {
203                 case '\0':
204                         break;
205                 case '{':
206                 case '[':
207                         {
208                                 // create new value
209                                 json_value *object = json_alloc(allocator);
210
211                                 // name
212                                 object->name = name;
213                                 name = 0;
214
215                                 // type
216                                 object->type = (*it == '{') ? JSON_OBJECT : JSON_ARRAY;
217
218                                 // skip open character
219                                 ++it;
220
221                                 // set top and root
222                                 if (top)
223                                 {
224                                         json_append(top, object);
225                                 }
226                                 else if (!root)
227                                 {
228                                         root = object;
229                                 }
230                                 else
231                                 {
232                                         ERROR(it, "Second root. Only one root allowed");
233                                 }
234                                 top = object;
235                         }
236                         break;
237
238                 case '}':
239                 case ']':
240                         {
241                                 if (!top || top->type != ((*it == '}') ? JSON_OBJECT : JSON_ARRAY))
242                                 {
243                                         ERROR(it, "Mismatch closing brace/bracket");
244                                 }
245
246                                 // skip close character
247                                 ++it;
248
249                                 // set top
250                                 top = top->parent;
251                         }
252                         break;
253
254                 case ':':
255                         if (!top || top->type != JSON_OBJECT)
256                         {
257                                 ERROR(it, "Unexpected character");
258                         }
259                         ++it;
260                         break;
261
262                 case ',':
263                         CHECK_TOP();
264                         ++it;
265                         break;
266
267                 case '"':
268                         {
269                                 CHECK_TOP();
270
271                                 // skip '"' character
272                                 ++it;
273
274                                 char *first = it;
275                                 char *last = it;
276                                 while (*it)
277                                 {
278                                         if ((unsigned char)*it < '\x20')
279                                         {
280                                                 ERROR(first, "Control characters not allowed in strings");
281                                         }
282                                         else if (*it == '\\')
283                                         {
284                                                 switch (it[1])
285                                                 {
286                                                 case '"':
287                                                         *last = '"';
288                                                         break;
289                                                 case '\\':
290                                                         *last = '\\';
291                                                         break;
292                                                 case '/':
293                                                         *last = '/';
294                                                         break;
295                                                 case 'b':
296                                                         *last = '\b';
297                                                         break;
298                                                 case 'f':
299                                                         *last = '\f';
300                                                         break;
301                                                 case 'n':
302                                                         *last = '\n';
303                                                         ++escaped_newlines;
304                                                         break;
305                                                 case 'r':
306                                                         *last = '\r';
307                                                         break;
308                                                 case 't':
309                                                         *last = '\t';
310                                                         break;
311                                                 case 'u':
312                                                         {
313                                                                 unsigned int codepoint;
314                                                                 if (hatoui(it + 2, it + 6, &codepoint) != it + 6)
315                                                                 {
316                                                                         ERROR(it, "Bad unicode codepoint");
317                                                                 }
318
319                                                                 if (codepoint <= 0x7F)
320                                                                 {
321                                                                         *last = (char)codepoint;
322                                                                 }
323                                                                 else if (codepoint <= 0x7FF)
324                                                                 {
325                                                                         *last++ = (char)(0xC0 | (codepoint >> 6));
326                                                                         *last = (char)(0x80 | (codepoint & 0x3F));
327                                                                 }
328                                                                 else if (codepoint <= 0xFFFF)
329                                                                 {
330                                                                         *last++ = (char)(0xE0 | (codepoint >> 12));
331                                                                         *last++ = (char)(0x80 | ((codepoint >> 6) & 0x3F));
332                                                                         *last = (char)(0x80 | (codepoint & 0x3F));
333                                                                 }
334                                                         }
335                                                         it += 4;
336                                                         break;
337                                                 default:
338                                                         ERROR(first, "Unrecognized escape sequence");
339                                                 }
340
341                                                 ++last;
342                                                 it += 2;
343                                         }
344                                         else if (*it == '"')
345                                         {
346                                                 *last = 0;
347                                                 ++it;
348                                                 break;
349                                         }
350                                         else
351                                         {
352                                                 *last++ = *it++;
353                                         }
354                                 }
355
356                                 if (!name && top->type == JSON_OBJECT)
357                                 {
358                                         // field name in object
359                                         name = first;
360                                 }
361                                 else
362                                 {
363                                         // new string value
364                                         json_value *object = json_alloc(allocator);
365
366                                         object->name = name;
367                                         name = 0;
368
369                                         object->type = JSON_STRING;
370                                         object->string_value = first;
371
372                                         json_append(top, object);
373                                 }
374                         }
375                         break;
376
377                 case 'n':
378                 case 't':
379                 case 'f':
380                         {
381                                 CHECK_TOP();
382
383                                 // new null/bool value
384                                 json_value *object = json_alloc(allocator);
385
386                                 object->name = name;
387                                 name = 0;
388
389                                 // null
390                                 if (it[0] == 'n' && it[1] == 'u' && it[2] == 'l' && it[3] == 'l')
391                                 {
392                                         object->type = JSON_NULL;
393                                         it += 4;
394                                 }
395                                 // true
396                                 else if (it[0] == 't' && it[1] == 'r' && it[2] == 'u' && it[3] == 'e')
397                                 {
398                                         object->type = JSON_BOOL;
399                                         object->int_value = 1;
400                                         it += 4;
401                                 }
402                                 // false
403                                 else if (it[0] == 'f' && it[1] == 'a' && it[2] == 'l' && it[3] == 's' && it[4] == 'e')
404                                 {
405                                         object->type = JSON_BOOL;
406                                         object->int_value = 0;
407                                         it += 5;
408                                 }
409                                 else
410                                 {
411                                         ERROR(it, "Unknown identifier");
412                                 }
413
414                                 json_append(top, object);
415                         }
416                         break;
417
418                 case '-':
419                 case '0':
420                 case '1':
421                 case '2':
422                 case '3':
423                 case '4':
424                 case '5':
425                 case '6':
426                 case '7':
427                 case '8':
428                 case '9':
429                         {
430                                 CHECK_TOP();
431
432                                 // new number value
433                                 json_value *object = json_alloc(allocator);
434
435                                 object->name = name;
436                                 name = 0;
437
438                                 object->type = JSON_INT;
439
440                                 char *first = it;
441                                 while (*it != '\x20' && *it != '\x9' && *it != '\xD' && *it != '\xA' && *it != ',' && *it != ']' && *it != '}')
442                                 {
443                                         if (*it == '.' || *it == 'e' || *it == 'E')
444                                         {
445                                                 object->type = JSON_FLOAT;
446                                         }
447                                         ++it;
448                                 }
449
450                                 if (object->type == JSON_INT && atoi(first, it, &object->int_value) != it)
451                                 {
452                                         ERROR(first, "Bad integer number");
453                                 }
454
455                                 if (object->type == JSON_FLOAT && atof(first, it, &object->float_value) != it)
456                                 {
457                                         ERROR(first, "Bad float number");
458                                 }
459
460                                 json_append(top, object);
461                         }
462                         break;
463
464                 default:
465                         ERROR(it, "Unexpected character");
466                 }
467         }
468
469         if (top)
470         {
471                 ERROR(it, "Not all objects/arrays have been properly closed");
472         }
473
474         return root;
475 }