2 #include <stdio.h> // for debugging
7 #include "yacjs_dict.h"
11 enum yacjs_node_type type;
17 struct yacjs_node *entries;
21 struct yacjs_dict *dict;
41 static enum yacjs_error last_error = YACJS_ERROR_NONE;
43 static void destroy_helper(struct yacjs_node *node);
44 static void skip_whitespace(const char ** const ptr);
45 static const char *next_token(const char ** const ptr, int *length,
46 enum token_type *type);
47 static const char *peek_token(const char ** const ptr, int *length,
48 enum token_type *type);
49 static struct yacjs_node *parse_any(const char **string);
50 static struct yacjs_node *parse_dict_contents(const char **string);
51 static struct yacjs_node *parse_array_contents(const char **string);
53 enum yacjs_error yacjs_last_error() {
54 enum yacjs_error err = last_error;
55 last_error = YACJS_ERROR_NONE;
59 struct yacjs_node *yacjs_parse(const char *string) {
60 struct yacjs_node *ret = parse_any(&string);
61 // ensure there's nothing afterwards.
62 skip_whitespace(&string);
65 last_error = YACJS_ERROR_PARSE;
71 void yacjs_destroy(struct yacjs_node *node) {
77 static void destroy_helper(struct yacjs_node *node) {
78 if(node->type == YACJS_NODE_ARRAY) {
79 for(int i = 0; i < node->data.array.entries_count; i ++) {
80 yacjs_destroy(node->data.array.entries + i);
82 free(node->data.array.entries);
84 else if(node->type == YACJS_NODE_DICT) {
85 yacjs_dict_destroy(node->data.dict,
86 (yacjs_dict_visitor)destroy_helper);
90 enum yacjs_node_type yacjs_node_type(struct yacjs_node *node) {
94 const char *yacjs_node_str(struct yacjs_node *node) {
95 if(node->type != YACJS_NODE_STRING) {
96 last_error = YACJS_ERROR_TYPE;
99 return node->data.string;
102 int64_t yacjs_node_num(struct yacjs_node *node) {
103 if(node->type != YACJS_NODE_NUMBER) {
104 last_error = YACJS_ERROR_TYPE;
107 return node->data.number;
110 double yacjs_node_float(struct yacjs_node *node) {
111 if(node->type != YACJS_NODE_FLOAT) {
112 last_error = YACJS_ERROR_TYPE;
115 return node->data.fp;
118 int yacjs_node_array_size(struct yacjs_node *node) {
119 if(node->type != YACJS_NODE_ARRAY) {
120 last_error = YACJS_ERROR_TYPE;
123 return node->data.array.entries_count;
126 struct yacjs_node *yacjs_node_array_elem(struct yacjs_node *node, int index) {
127 if(node->type != YACJS_NODE_ARRAY) {
128 last_error = YACJS_ERROR_TYPE;
131 if(index >= node->data.array.entries_count || index < 0) {
132 last_error = YACJS_ERROR_BOUNDS;
136 return node->data.array.entries + index;
139 struct yacjs_node *yacjs_node_dict_get(struct yacjs_node *node,
142 if(node->type != YACJS_NODE_DICT) {
143 last_error = YACJS_ERROR_TYPE;
147 return yacjs_dict_get(node->data.dict, key);
150 static void skip_whitespace(const char ** const ptr) {
151 while((**ptr == ' ' || **ptr == '\t' || **ptr == '\n') && **ptr != 0) {
152 *ptr = yacjs_u8s_next(*ptr);
156 static const char *next_token(const char ** const ptr, int *length,
157 enum token_type *type) {
161 skip_whitespace(ptr);
168 // TODO: parse string PROPERLY
169 const char *start = ++(*ptr);
171 while(**ptr != '"' && **ptr != 0) {
172 (*ptr) ++, (*length) ++;
174 if(**ptr == 0) return NULL;
175 // skip closing quotes
178 *type = TOKEN_STRING;
181 else if(isdigit(**ptr)) {
182 // TODO: support floating-point etc.
183 const char *start = (*ptr);
186 while((isdigit(**ptr) || **ptr == '.' || **ptr == 'e') && **ptr != 0) {
187 if(**ptr == '.' || **ptr == 'e') is_fp = true;
188 (*ptr) ++, (*length) ++;
190 if(*ptr == 0) return NULL;
191 if(is_fp) *type = TOKEN_FLOAT;
192 else *type = TOKEN_NUMBER;
196 else if(**ptr == '{') {
197 *type = TOKEN_OPENDICT;
200 else if(**ptr == '}') {
201 *type = TOKEN_CLOSEDICT;
204 else if(**ptr == '[') {
205 *type = TOKEN_OPENARRAY;
208 else if(**ptr == ']') {
209 *type = TOKEN_CLOSEARRAY;
212 else if(**ptr == ',') {
216 else if(**ptr == ':') {
221 printf("Don't know what to do with a '%c'\n", **ptr);
228 static const char *peek_token(const char ** const ptr, int *length,
229 enum token_type *type) {
231 const char *s = *ptr;
233 return next_token(&s, length, type);
236 static struct yacjs_node *parse_any(const char **string) {
237 enum token_type type;
241 s = next_token(string, &len, &type);
242 if(type == TOKEN_OPENDICT) return parse_dict_contents(string);
243 else if(type == TOKEN_OPENARRAY) return parse_array_contents(string);
244 else if(type == TOKEN_STRING) {
245 struct yacjs_node *build = malloc(sizeof(*build));
246 build->type = YACJS_NODE_STRING;
247 build->data.string = yacjs_u8s_strndup(s, len);
250 else if(type == TOKEN_NUMBER) {
251 struct yacjs_node *build = malloc(sizeof(*build));
252 build->type = YACJS_NODE_NUMBER;
253 build->data.number = strtoll(s, NULL, 0);
256 else if(type == TOKEN_FLOAT) {
257 struct yacjs_node *build = malloc(sizeof(*build));
258 build->type = YACJS_NODE_FLOAT;
260 build->data.fp = strtod(s, &e);
263 // error parsing number, not everything was used
269 printf("Unknown token type %i\n", type);
274 static struct yacjs_node *parse_dict_contents(const char **string) {
275 enum token_type type;
279 struct yacjs_node *result = malloc(sizeof(*result));
280 result->type = YACJS_NODE_DICT;
281 result->data.dict = yacjs_dict_make();
283 while((s = next_token(string, &len, &type))) {
284 // closing dictionary token
285 if(type == TOKEN_CLOSEDICT) break;
286 // we expect a string here if it's not a closing dictionary
287 else if(type != TOKEN_STRING) {
291 char *ds = yacjs_u8s_strndup(s, len);
293 // expect a colon after the name
294 next_token(string, &len, &type);
295 if(type != TOKEN_COLON) {
299 struct yacjs_node *value = parse_any(string);
302 // TODO: leaked memory
306 yacjs_dict_set(result->data.dict, ds, value);
309 peek_token(string, &len, &type);
310 if(type == TOKEN_COMMA) {
311 // NOTE: this means things like {"a":1,} are accepted
313 next_token(string, &len, &type);
317 // TODO: leaked memory
324 static struct yacjs_node *parse_array_contents(const char **string) {
325 enum token_type type;
329 struct yacjs_node *result = malloc(sizeof(*result));
330 result->type = YACJS_NODE_ARRAY;
331 result->data.array.entries = NULL;
332 result->data.array.entries_size = 0;
333 result->data.array.entries_count = 0;
335 while((s = peek_token(string, &len, &type))) {
336 // closing dictionary token
337 if(type == TOKEN_CLOSEARRAY) {
339 next_token(string, &len, &type);
342 struct yacjs_node *next = parse_any(string);
344 // TODO: leaked memory
348 if(result->data.array.entries_size
349 == result->data.array.entries_count) {
351 void *nmem = realloc(result->data.array.entries,
352 sizeof(struct yacjs_node)
353 * (result->data.array.entries_size * 2 + 1));
355 last_error = YACJS_ERROR_MEMORY;
359 result->data.array.entries_size *= 2;
360 result->data.array.entries_size ++;
361 result->data.array.entries = nmem;
363 result->data.array.entries[result->data.array.entries_count++] = *next;
366 peek_token(string, &len, &type);
367 if(type == TOKEN_COMMA) {
368 // NOTE: this means things like [1,2,] are accepted
370 next_token(string, &len, &type);
374 // TODO: leaked memory