1 #include <stdio.h> // for debugging
5 #include "yacjs_dict.h"
9 enum yacjs_node_type type;
15 struct yacjs_node *entries;
19 struct yacjs_dict *dict;
37 static enum yacjs_error last_error = YACJS_ERROR_NONE;
39 static void destroy_helper(struct yacjs_node *node);
40 static void skip_whitespace(const char ** const ptr);
41 static const char *next_token(const char ** const ptr, int *length,
42 enum token_type *type);
43 static const char *peek_token(const char ** const ptr, int *length,
44 enum token_type *type);
45 static struct yacjs_node *parse_any(const char **string);
46 static struct yacjs_node *parse_dict_contents(const char **string);
47 static struct yacjs_node *parse_array_contents(const char **string);
49 enum yacjs_error yacjs_last_error() {
50 enum yacjs_error err = last_error;
51 last_error = YACJS_ERROR_NONE;
55 struct yacjs_node *yacjs_parse(const char *string) {
56 struct yacjs_node *ret = parse_any(&string);
57 // ensure there's nothing afterwards.
58 skip_whitespace(&string);
61 last_error = YACJS_ERROR_PARSE;
67 void yacjs_destroy(struct yacjs_node *node) {
73 static void destroy_helper(struct yacjs_node *node) {
74 if(node->type == YACJS_NODE_ARRAY) {
75 for(int i = 0; i < node->data.array.entries_count; i ++) {
76 yacjs_destroy(node->data.array.entries + i);
78 free(node->data.array.entries);
80 else if(node->type == YACJS_NODE_DICT) {
81 yacjs_dict_destroy(node->data.dict,
82 (yacjs_dict_visitor)destroy_helper);
86 enum yacjs_node_type yacjs_node_type(struct yacjs_node *node) {
90 const char *yacjs_node_str(struct yacjs_node *node) {
91 if(node->type != YACJS_NODE_STRING) {
92 last_error = YACJS_ERROR_TYPE;
95 return node->data.string;
98 int64_t yacjs_node_num(struct yacjs_node *node) {
99 if(node->type != YACJS_NODE_NUMBER) {
100 last_error = YACJS_ERROR_TYPE;
103 return node->data.number;
106 double yacjs_node_float(struct yacjs_node *node) {
107 if(node->type != YACJS_NODE_FLOAT) {
108 last_error = YACJS_ERROR_TYPE;
111 return node->data.fp;
114 int yacjs_node_array_size(struct yacjs_node *node) {
115 if(node->type != YACJS_NODE_ARRAY) {
116 last_error = YACJS_ERROR_TYPE;
119 return node->data.array.entries_count;
122 struct yacjs_node *yacjs_node_array_elem(struct yacjs_node *node, int index) {
123 if(node->type != YACJS_NODE_ARRAY) {
124 last_error = YACJS_ERROR_TYPE;
127 if(index >= node->data.array.entries_count || index < 0) {
128 last_error = YACJS_ERROR_BOUNDS;
132 return node->data.array.entries + index;
135 struct yacjs_node *yacjs_node_dict_get(struct yacjs_node *node,
138 if(node->type != YACJS_NODE_DICT) {
139 last_error = YACJS_ERROR_TYPE;
143 return yacjs_dict_get(node->data.dict, key);
146 static void skip_whitespace(const char ** const ptr) {
147 while((**ptr == ' ' || **ptr == '\t') && **ptr != 0) {
148 *ptr = yacjs_u8s_next(*ptr);
152 static const char *next_token(const char ** const ptr, int *length,
153 enum token_type *type) {
157 skip_whitespace(ptr);
164 // TODO: parse string PROPERLY
165 const char *start = ++(*ptr);
167 while(**ptr != '"' && **ptr != 0) {
168 (*ptr) ++, (*length) ++;
170 if(**ptr == 0) return NULL;
171 // skip closing quotes
174 *type = TOKEN_STRING;
177 else if(**ptr == '{') {
178 *type = TOKEN_OPENDICT;
181 else if(**ptr == '}') {
182 *type = TOKEN_CLOSEDICT;
185 else if(**ptr == '[') {
186 *type = TOKEN_OPENARRAY;
189 else if(**ptr == ']') {
190 *type = TOKEN_CLOSEARRAY;
193 else if(**ptr == ',') {
197 else if(**ptr == ':') {
207 static const char *peek_token(const char ** const ptr, int *length,
208 enum token_type *type) {
210 const char *s = *ptr;
212 return next_token(&s, length, type);
215 static struct yacjs_node *parse_any(const char **string) {
216 enum token_type type;
220 s = next_token(string, &len, &type);
221 if(type == TOKEN_OPENDICT) return parse_dict_contents(string);
222 else if(type == TOKEN_OPENARRAY) return parse_array_contents(string);
223 else if(type == TOKEN_STRING) {
224 struct yacjs_node *build = malloc(sizeof(*build));
225 build->type = YACJS_NODE_STRING;
226 build->data.string = yacjs_u8s_strndup(s, len);
229 else if(type == TOKEN_NUMBER) {
230 struct yacjs_node *build = malloc(sizeof(*build));
231 build->type = YACJS_NODE_NUMBER;
232 build->data.number = strtoll(s, NULL, 0);
236 printf("Unknown token type %i\n", type);
241 static struct yacjs_node *parse_dict_contents(const char **string) {
242 enum token_type type;
246 struct yacjs_node *result = malloc(sizeof(*result));
247 result->type = YACJS_NODE_DICT;
248 result->data.dict = yacjs_dict_make();
250 while((s = next_token(string, &len, &type))) {
251 // closing dictionary token
252 if(type == TOKEN_CLOSEDICT) break;
253 // we expect a string here if it's not a closing dictionary
254 else if(type != TOKEN_STRING) {
258 char *ds = yacjs_u8s_strndup(s, len);
260 // expect a colon after the name
261 next_token(string, &len, &type);
262 if(type != TOKEN_COLON) {
266 struct yacjs_node *value = parse_any(string);
269 // TODO: leaked memory
273 yacjs_dict_set(result->data.dict, ds, value);
276 peek_token(string, &len, &type);
277 if(type == TOKEN_COMMA) {
278 // NOTE: this means things like {"a":1,} are accepted
280 next_token(string, &len, &type);
284 // TODO: leaked memory
291 static struct yacjs_node *parse_array_contents(const char **string) {
292 enum token_type type;
296 struct yacjs_node *result = malloc(sizeof(*result));
297 result->type = YACJS_NODE_ARRAY;
298 result->data.array.entries = NULL;
299 result->data.array.entries_size = 0;
300 result->data.array.entries_count = 0;
302 while((s = peek_token(string, &len, &type))) {
303 // closing dictionary token
304 if(type == TOKEN_CLOSEARRAY) {
306 next_token(string, &len, &type);
309 struct yacjs_node *next = parse_any(string);
311 // TODO: leaked memory
315 if(result->data.array.entries_size
316 == result->data.array.entries_count) {
318 void *nmem = realloc(result->data.array.entries,
319 result->data.array.entries_size * 2 + 1);
321 last_error = YACJS_ERROR_MEMORY;
325 result->data.array.entries_size *= 2;
326 result->data.array.entries_size ++;
327 result->data.array.entries = nmem;
329 result->data.array.entries[result->data.array.entries_count++] = *next;
332 peek_token(string, &len, &type);
333 if(type == TOKEN_COMMA) {
334 // NOTE: this means things like [1,2,] are accepted
336 next_token(string, &len, &type);
340 // TODO: leaked memory