e33d09e5dbc4eb48a1a502b3755585f8fc63fe4e
[yacjs.git] / src / yacjs.c
1 #include <stdbool.h>
2 #include <stdio.h> // for debugging
3 #include <stdlib.h>
4 #include <string.h>
5 #include <ctype.h>
6
7 #include "yacjs.h"
8 #include "yacjs_dict.h"
9 #include "yacjs_u8s.h"
10
11 struct YACJS_NAME(node) {
12     enum YACJS_NAME(node_type) type;
13     union {
14         const char *string;
15         int64_t number;
16         double fp;
17         struct {
18             struct YACJS_NAME(node) *entries;
19             int entries_count;
20             int entries_size;
21         } array;
22         struct YACJS_NAME(dict) *dict;
23         bool boolean;
24     } data;
25 };
26
27 enum token_type {
28     TOKEN_STRING,
29     TOKEN_NUMBER,
30     TOKEN_FLOAT,
31     TOKEN_FPNUMBER,
32     TOKEN_TRUE,
33     TOKEN_FALSE,
34     TOKEN_NULL,
35     TOKEN_OPENDICT,
36     TOKEN_CLOSEDICT,
37     TOKEN_OPENARRAY,
38     TOKEN_CLOSEARRAY,
39     TOKEN_COMMA,
40     TOKEN_COLON,
41     TOKEN_NONE,
42     TOKEN_ERROR,
43     TOKEN_TYPES
44 };
45
46 static enum YACJS_NAME(error) last_error = YACJS_NAME_CAP(ERROR_NONE);
47
48 static void destroy_helper(struct YACJS_NAME(node) *node);
49 static void skip_whitespace(const char ** const ptr);
50 static const char *next_token(const char ** const ptr, int *length,
51     enum token_type *type);
52 static const char *peek_token(const char ** const ptr, int *length,
53     enum token_type *type);
54 static struct YACJS_NAME(node) *parse_any(const char **string);
55 static struct YACJS_NAME(node) *parse_dict_contents(const char **string);
56 static struct YACJS_NAME(node) *parse_array_contents(const char **string);
57
58 enum YACJS_NAME(error) YACJS_NAME(last_error)() {
59     enum YACJS_NAME(error) err = last_error;
60     last_error = YACJS_NAME_CAP(ERROR_NONE);
61     return err;
62 }
63
64 struct YACJS_NAME(node) *YACJS_NAME(parse)(const char *string) {
65     struct YACJS_NAME(node) *ret = parse_any(&string);
66     // ensure there's nothing afterwards.
67     skip_whitespace(&string);
68     if(string[0] != 0) {
69         YACJS_NAME(destroy)(ret);
70         last_error = YACJS_NAME_CAP(ERROR_PARSE);
71         return NULL;
72     }
73     return ret;
74 }
75
76 void YACJS_NAME(destroy)(struct YACJS_NAME(node) *node) {
77     if(!node) return;
78     destroy_helper(node);
79     free(node);
80 }
81
82 static void destroy_helper(struct YACJS_NAME(node) *node) {
83     if(node->type == YACJS_NAME_CAP(NODE_ARRAY)) {
84         for(int i = 0; i < node->data.array.entries_count; i ++) {
85             YACJS_NAME(destroy)(node->data.array.entries + i);
86         }
87         free(node->data.array.entries);
88     }
89     else if(node->type == YACJS_NAME_CAP(NODE_DICT)) {
90         YACJS_NAME(dict_destroy)(node->data.dict,
91             (YACJS_NAME(dict_visitor))destroy_helper);
92     }
93 }
94
95 enum YACJS_NAME(node_type) YACJS_NAME(node_type)(
96     struct YACJS_NAME(node) *node) {
97
98     return node->type;
99 }
100
101 bool YACJS_NAME(node_bool)(struct YACJS_NAME(node) *node) {
102     if(node->type != YACJS_NAME_CAP(NODE_BOOLEAN)) {
103         last_error = YACJS_NAME_CAP(ERROR_TYPE);
104         return NULL;
105     }
106     return node->data.boolean;
107 }
108
109 const char *YACJS_NAME(node_str)(struct YACJS_NAME(node) *node) {
110     if(node->type != YACJS_NAME_CAP(NODE_STRING)) {
111         last_error = YACJS_NAME_CAP(ERROR_TYPE);
112         return NULL;
113     }
114     return node->data.string;
115 }
116
117 int64_t YACJS_NAME(node_num)(struct YACJS_NAME(node) *node) {
118     if(node->type != YACJS_NAME_CAP(NODE_NUMBER)) {
119         last_error = YACJS_NAME_CAP(ERROR_TYPE);
120         return -1;
121     }
122     return node->data.number;
123 }
124
125 double YACJS_NAME(node_float)(struct YACJS_NAME(node) *node) {
126     if(node->type != YACJS_NAME_CAP(NODE_FLOAT)) {
127         last_error = YACJS_NAME_CAP(ERROR_TYPE);
128         return 1/0.0; // NaN
129     }
130     return node->data.fp;
131 }
132
133 int YACJS_NAME(node_array_size)(struct YACJS_NAME(node) *node) {
134     if(node->type != YACJS_NAME_CAP(NODE_ARRAY)) {
135         last_error = YACJS_NAME_CAP(ERROR_TYPE);
136         return -1;
137     }
138     return node->data.array.entries_count;
139 }
140
141 struct YACJS_NAME(node) *YACJS_NAME(node_array_elem)(
142     struct YACJS_NAME(node) *node, int index) {
143
144     if(node->type != YACJS_NAME_CAP(NODE_ARRAY)) {
145         last_error = YACJS_NAME_CAP(ERROR_TYPE);
146         return NULL;
147     }
148     if(index >= node->data.array.entries_count || index < 0) {
149         last_error = YACJS_NAME_CAP(ERROR_BOUNDS);
150         return NULL;
151         
152     }
153     return node->data.array.entries + index;
154 }
155
156 struct YACJS_NAME(node) *YACJS_NAME(node_dict_get)(
157     struct YACJS_NAME(node) *node, const char *key) {
158
159     if(node->type != YACJS_NAME_CAP(NODE_DICT)) {
160         last_error = YACJS_NAME_CAP(ERROR_TYPE);
161         return NULL;
162     }
163
164     return YACJS_NAME(dict_get)(node->data.dict, key);
165 }
166
167 static void skip_whitespace(const char ** const ptr) {
168     while((**ptr == ' ' || **ptr == '\t' || **ptr == '\n') && **ptr != 0) {
169         *ptr = U8S_NAME(next)(*ptr);
170     }
171 }
172
173 static const char *next_token(const char ** const ptr, int *length,
174     enum token_type *type) {
175
176     *length = 0;
177     // skip whitespace
178     skip_whitespace(ptr);
179     if(*ptr == 0) {
180         *type = TOKEN_NONE;
181         return NULL;
182     }
183
184     if(**ptr == '"') {
185         // TODO: parse string PROPERLY
186         const char *start = ++(*ptr);
187         *length = 0;
188         while(**ptr != '"' && **ptr != 0) {
189             (*ptr) ++, (*length) ++;
190         }
191         if(**ptr == 0) return NULL;
192         // skip closing quotes
193         else (*ptr)++;
194
195         *type = TOKEN_STRING;
196         return start;
197     }
198     else if(isdigit(**ptr) || **ptr == '-') {
199         const char *start = (*ptr);
200         *length = 0;
201         bool is_fp = false;
202         while((isdigit(**ptr) || **ptr == '.' || **ptr == 'e' || **ptr == '-')
203             && **ptr != 0) {
204
205             if(**ptr == '.' || **ptr == 'e') is_fp = true;
206             (*ptr) ++, (*length) ++;
207         }
208         if(*ptr == 0) return NULL;
209         if(is_fp) *type = TOKEN_FLOAT;
210         else *type = TOKEN_NUMBER;
211
212         return start;
213     }
214     else if(**ptr == '{') {
215         *type = TOKEN_OPENDICT;
216         return (*ptr)++;
217     }
218     else if(**ptr == '}') {
219         *type = TOKEN_CLOSEDICT;
220         return (*ptr)++;
221     }
222     else if(**ptr == '[') {
223         *type = TOKEN_OPENARRAY;
224         return (*ptr)++;
225     }
226     else if(**ptr == ']') {
227         *type = TOKEN_CLOSEARRAY;
228         return (*ptr)++;
229     }
230     else if(**ptr == ',') {
231         *type = TOKEN_COMMA;
232         return (*ptr)++;
233     }
234     else if(**ptr == ':') {
235         *type = TOKEN_COLON;
236         return (*ptr)++;
237     }
238     else if(!strncmp(*ptr, "false", 5)) {
239         *type = TOKEN_FALSE;
240         return (*ptr) += 5;
241     }
242     else if(!strncmp(*ptr, "true", 4)) {
243         *type = TOKEN_TRUE;
244         return (*ptr) += 4;
245     }
246     else if(!strncmp(*ptr, "null", 4)) {
247         *type = TOKEN_NULL;
248         return (*ptr) += 4;
249     }
250
251     printf("Don't know what to do with a '%c'\n", **ptr);
252     printf("context: \n\n<\n%s\n>\n\n", *ptr);
253
254     *type = TOKEN_ERROR;
255
256     return NULL;
257 }
258
259 static const char *peek_token(const char ** const ptr, int *length,
260     enum token_type *type) {
261
262     const char *s = *ptr;
263
264     return next_token(&s, length, type);
265 }
266
267 static struct YACJS_NAME(node) *parse_any(const char **string) {
268     enum token_type type;
269     const char *s;
270     int len;
271
272     s = next_token(string, &len, &type);
273     if(type == TOKEN_OPENDICT) return parse_dict_contents(string);
274     else if(type == TOKEN_OPENARRAY) return parse_array_contents(string);
275     else if(type == TOKEN_STRING) {
276         struct YACJS_NAME(node) *build = malloc(sizeof(*build));
277         build->type = YACJS_NAME_CAP(NODE_STRING);
278         build->data.string = U8S_NAME(strndup)(s, len);
279         return build;
280     }
281     else if(type == TOKEN_NUMBER) {
282         struct YACJS_NAME(node) *build = malloc(sizeof(*build));
283         build->type = YACJS_NAME_CAP(NODE_NUMBER);
284         build->data.number = strtoll(s, NULL, 0);
285         return build;
286     }
287     else if(type == TOKEN_FLOAT) {
288         struct YACJS_NAME(node) *build = malloc(sizeof(*build));
289         build->type = YACJS_NAME_CAP(NODE_FLOAT);
290         char *e;
291         build->data.fp = strtod(s, &e);
292         if(e != *string) {
293             free(build);
294             // error parsing number, not everything was used
295             return NULL;
296         }
297         return build;
298     }
299     else if(type == TOKEN_FALSE || type == TOKEN_TRUE) {
300         struct YACJS_NAME(node) *build = malloc(sizeof(*build));
301         build->type = YACJS_NAME_CAP(NODE_BOOLEAN);
302         build->data.boolean = type == TOKEN_TRUE;
303         return build;
304     }
305     else if(type == TOKEN_NULL) {
306         struct YACJS_NAME(node) *build = malloc(sizeof(*build));
307         build->type = YACJS_NAME_CAP(NODE_NULL);
308         return build;
309     }
310
311     printf("Unknown token type %i\n", type);
312
313     return NULL;
314 }
315
316 static struct YACJS_NAME(node) *parse_dict_contents(const char **string) {
317     enum token_type type;
318     const char *s;
319     int len;
320
321     struct YACJS_NAME(node) *result = malloc(sizeof(*result));
322     result->type = YACJS_NAME_CAP(NODE_DICT);
323     result->data.dict = YACJS_NAME(dict_make)();
324
325     while((s = next_token(string, &len, &type))) {
326         // closing dictionary token
327         if(type == TOKEN_CLOSEDICT) break;
328         // we expect a string here if it's not a closing dictionary
329         else if(type != TOKEN_STRING) {
330             return NULL;
331         }
332
333         char *ds = U8S_NAME(strndup)(s, len);
334
335         // expect a colon after the name
336         next_token(string, &len, &type);
337         if(type != TOKEN_COLON) {
338             return NULL;
339         }
340
341         struct YACJS_NAME(node) *value = parse_any(string);
342
343         if(value == NULL) {
344             // TODO: leaked memory
345             return NULL;
346         }
347
348         YACJS_NAME(dict_set)(result->data.dict, ds, value);
349         free(ds);
350
351         peek_token(string, &len, &type);
352         if(type == TOKEN_COMMA) {
353             // NOTE: this means things like {"a":1,} are accepted
354             // eat the comma
355             next_token(string, &len, &type);
356         }
357     }
358     if(!s) {
359         // TODO: leaked memory
360         return NULL;
361     }
362
363     return result;
364 }
365
366 static struct YACJS_NAME(node) *parse_array_contents(const char **string) {
367     enum token_type type;
368     const char *s;
369     int len;
370
371     struct YACJS_NAME(node) *result = malloc(sizeof(*result));
372     result->type = YACJS_NAME_CAP(NODE_ARRAY);
373     result->data.array.entries = NULL;
374     result->data.array.entries_size = 0;
375     result->data.array.entries_count = 0;
376
377     while((s = peek_token(string, &len, &type))) {
378         // closing dictionary token
379         if(type == TOKEN_CLOSEARRAY) {
380             // eat first
381             next_token(string, &len, &type);
382             break;
383         }
384         struct YACJS_NAME(node) *next = parse_any(string);
385         if(!next) {
386             // TODO: leaked memory
387             return NULL;
388         }
389
390         if(result->data.array.entries_size
391             == result->data.array.entries_count) {
392
393             void *nmem = realloc(result->data.array.entries,
394                 sizeof(struct YACJS_NAME(node))
395                     * (result->data.array.entries_size * 2 + 1));
396             if(nmem == NULL) {
397                 last_error = YACJS_NAME_CAP(ERROR_MEMORY);
398                 return NULL;
399             }
400
401             result->data.array.entries_size *= 2;
402             result->data.array.entries_size ++;
403             result->data.array.entries = nmem;
404         }
405         result->data.array.entries[result->data.array.entries_count++] = *next;
406         free(next);
407
408         peek_token(string, &len, &type);
409         if(type == TOKEN_COMMA) {
410             // NOTE: this means things like [1,2,] are accepted
411             // eat the comma
412             next_token(string, &len, &type);
413         }
414     }
415     if(!s) {
416         // TODO: leaked memory
417         return NULL;
418     }
419
420     return result;
421 }