options/config
[demo_prior] / libs / treestore / src / text.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <ctype.h>
5 #include <assert.h>
6 #include "treestore.h"
7 #include "dynarr.h"
8
9 struct parser {
10         struct ts_io *io;
11         int nline;
12         char *token;
13         int nextc;
14 };
15
16 enum { TOK_SYM, TOK_ID, TOK_NUM, TOK_STR };
17
18 static struct ts_node *read_node(struct parser *pstate);
19 static int read_array(struct parser *pstate, struct ts_value *tsv, char endsym);
20 static int next_token(struct parser *pstate);
21
22 static int print_attr(struct ts_attr *attr, struct ts_io *io, int level);
23 static char *value_to_str(struct ts_value *value);
24 static int tree_level(struct ts_node *n);
25 static const char *indent(int x);
26 static const char *toktypestr(int type);
27
28 #define EXPECT(type) \
29         do { \
30                 if(next_token(pst) != (type)) { \
31                         fprintf(stderr, "expected %s token\n", toktypestr(type)); \
32                         goto err; \
33                 } \
34         } while(0)
35
36 #define EXPECT_SYM(c) \
37         do { \
38                 if(next_token(pst) != TOK_SYM || pst->token[0] != (c)) { \
39                         fprintf(stderr, "expected symbol: %c\n", c); \
40                         goto err; \
41                 } \
42         } while(0)
43
44
45 struct ts_node *ts_text_load(struct ts_io *io)
46 {
47         char *root_name;
48         struct parser pstate, *pst = &pstate;
49         struct ts_node *node = 0;
50
51         pstate.io = io;
52         pstate.nline = 0;
53         pstate.nextc = -1;
54         if(!(pstate.token = ts_dynarr_alloc(0, 1))) {
55                 perror("failed to allocate token string");
56                 return 0;
57         }
58
59         EXPECT(TOK_ID);
60         if(!(root_name = strdup(pst->token))) {
61                 perror("failed to allocate root node name");
62                 ts_dynarr_free(pst->token);
63                 return 0;
64         }
65         EXPECT_SYM('{');
66         if(!(node = read_node(pst))) {
67                 ts_dynarr_free(pst->token);
68                 return 0;
69         }
70         node->name = root_name;
71
72 err:
73         ts_dynarr_free(pst->token);
74         return node;
75 }
76
77 static int read_value(struct parser *pst, int toktype, struct ts_value *val)
78 {
79         switch(toktype) {
80         case TOK_NUM:
81                 ts_set_valuef(val, atof(pst->token));
82                 break;
83
84         case TOK_SYM:
85                 if(pst->token[0] == '[' || pst->token[0] == '{') {
86                         char endsym = pst->token[0] + 2; /* end symbol is dist 2 from either '[' or '{' */
87                         if(read_array(pst, val, endsym) == -1) {
88                                 return -1;
89                         }
90                 } else {
91                         fprintf(stderr, "read_node: unexpected rhs symbol: %c\n", pst->token[0]);
92                 }
93                 break;
94
95         case TOK_ID:
96         case TOK_STR:
97         default:
98                 ts_set_value_str(val, pst->token);
99         }
100
101         return 0;
102 }
103
104 static struct ts_node *read_node(struct parser *pst)
105 {
106         int type;
107         struct ts_node *node;
108
109         if(!(node = ts_alloc_node())) {
110                 perror("failed to allocate treestore node");
111                 return 0;
112         }
113
114         while((type = next_token(pst)) == TOK_ID) {
115                 char *id;
116
117                 if(!(id = strdup(pst->token))) {
118                         goto err;
119                 }
120
121                 EXPECT(TOK_SYM);
122
123                 if(pst->token[0] == '=') {
124                         /* attribute */
125                         struct ts_attr *attr;
126                         int type;
127
128                         if(!(attr = ts_alloc_attr())) {
129                                 goto err;
130                         }
131
132                         if((type = next_token(pst)) == -1) {
133                                 ts_free_attr(attr);
134                                 fprintf(stderr, "read_node: unexpected EOF\n");
135                                 goto err;
136                         }
137
138                         if(read_value(pst, type, &attr->val) == -1) {
139                                 ts_free_attr(attr);
140                                 fprintf(stderr, "failed to read value\n");
141                                 goto err;
142                         }
143                         attr->name = id;
144                         ts_add_attr(node, attr);
145
146                 } else if(pst->token[0] == '{') {
147                         /* child */
148                         struct ts_node *child;
149
150                         if(!(child = read_node(pst))) {
151                                 ts_free_node(node);
152                                 return 0;
153                         }
154
155                         child->name = id;
156                         ts_add_child(node, child);
157
158                 } else {
159                         fprintf(stderr, "unexpected token: %s\n", pst->token);
160                         goto err;
161                 }
162         }
163
164         if(type != TOK_SYM || pst->token[0] != '}') {
165                 fprintf(stderr, "expected closing brace\n");
166                 goto err;
167         }
168         return node;
169
170 err:
171         fprintf(stderr, "treestore read_node failed\n");
172         ts_free_node(node);
173         return 0;
174 }
175
176 static int read_array(struct parser *pst, struct ts_value *tsv, char endsym)
177 {
178         int type;
179         struct ts_value values[32];
180         int i, nval = 0;
181         int res;
182
183         while((type = next_token(pst)) != -1) {
184                 ts_init_value(values + nval);
185                 if(read_value(pst, type, values + nval) == -1) {
186                         return -1;
187                 }
188                 if(nval < 31) {
189                         ++nval;
190                 } else {
191                         ts_destroy_value(values + nval);
192                 }
193
194                 type = next_token(pst);
195                 if(!(type == TOK_SYM && (pst->token[0] == ',' || pst->token[0] == endsym))) {
196                         fprintf(stderr, "read_array: expected comma or end symbol ('%c')\n", endsym);
197                         return -1;
198                 }
199                 if(pst->token[0] == endsym) {
200                         break;  /* we're done */
201                 }
202         }
203
204         if(!nval) {
205                 return -1;
206         }
207
208         res = ts_set_value_arr(tsv, nval, values);
209
210         for(i=0; i<nval; i++) {
211                 ts_destroy_value(values + i);
212         }
213         return res;
214 }
215
216 static int nextchar(struct parser *pst)
217 {
218         char c;
219
220         if(pst->nextc >= 0) {
221                 c = pst->nextc;
222                 pst->nextc = -1;
223         } else {
224                 if(pst->io->read(&c, 1, pst->io->data) < 1) {
225                         return -1;
226                 }
227         }
228         return c;
229 }
230
231 static void ungetchar(char c, struct parser *pst)
232 {
233         assert(pst->nextc == -1);
234         pst->nextc = c;
235 }
236
237 static int next_token(struct parser *pst)
238 {
239         int c;
240
241         DYNARR_CLEAR(pst->token);
242
243         /* skip whitespace */
244         while((c = nextchar(pst)) != -1) {
245                 if(c == '#') { /* skip to end of line */
246                         while((c = nextchar(pst)) != -1 && c != '\n');
247                         if(c == -1) return -1;
248                 }
249                 if(!isspace(c)) break;
250                 if(c == '\n') ++pst->nline;
251         }
252         if(c == -1) return -1;
253
254         DYNARR_STRPUSH(pst->token, c);
255
256         if(isdigit(c) || c == '-' || c == '+') {
257                 /* token is a number */
258                 int found_dot = 0;
259                 while((c = nextchar(pst)) != -1 &&
260                                 (isdigit(c) || (c == '.' && !found_dot))) {
261                         DYNARR_STRPUSH(pst->token, c);
262                         if(c == '.') found_dot = 1;
263                 }
264                 if(c != -1) ungetchar(c, pst);
265                 return TOK_NUM;
266         }
267         if(isalpha(c)) {
268                 /* token is an identifier */
269                 while((c = nextchar(pst)) != -1 && (isalnum(c) || c == '_')) {
270                         DYNARR_STRPUSH(pst->token, c);
271                 }
272                 if(c != -1) ungetchar(c, pst);
273                 return TOK_ID;
274         }
275         if(c == '"') {
276                 /* token is a string constant, remove the opening quote */
277                 DYNARR_STRPOP(pst->token);
278                 while((c = nextchar(pst)) != -1 && c != '"') {
279                         DYNARR_STRPUSH(pst->token, c);
280                         if(c == '\n') ++pst->nline;
281                 }
282                 if(c != '"') {
283                         return -1;
284                 }
285                 return TOK_STR;
286         }
287         return TOK_SYM;
288 }
289
290 int ts_text_save(struct ts_node *tree, struct ts_io *io)
291 {
292         char *buf;
293         struct ts_node *c;
294         struct ts_attr *attr;
295         int lvl = tree_level(tree);
296         int sz, inline_attr, res = -1;
297
298         if(!(buf = malloc(lvl + strlen(tree->name) + 4))) {
299                 perror("ts_text_save failed to allocate buffer");
300                 goto end;
301         }
302
303         if(tree->child_list || (tree->attr_list && tree->attr_list->next)) {
304                 inline_attr = 0;
305         } else {
306                 inline_attr = 1;
307         }
308
309         sz = sprintf(buf, "%s%s {", indent(lvl), tree->name);
310         if(!inline_attr) {
311                 strcat(buf, "\n");
312                 sz++;
313         }
314         if(io->write(buf, sz, io->data) < sz) {
315                 goto end;
316         }
317
318         attr = tree->attr_list;
319         while(attr) {
320                 if(print_attr(attr, io, inline_attr ? -1 : lvl) == -1) {
321                         goto end;
322                 }
323                 attr = attr->next;
324         }
325
326         c = tree->child_list;
327         while(c) {
328                 if(ts_text_save(c, io) == -1) {
329                         goto end;
330                 }
331                 c = c->next;
332         }
333
334         if(inline_attr) {
335                 sz = sprintf(buf, "}\n");
336         } else {
337                 sz = sprintf(buf, "%s}\n", indent(lvl));
338         }
339         if(io->write(buf, sz, io->data) < sz) {
340                 goto end;
341         }
342         res = 0;
343 end:
344         free(buf);
345         return res;
346 }
347
348 static int print_attr(struct ts_attr *attr, struct ts_io *io, int level)
349 {
350         char *buf, *val;
351         int sz;
352
353         if(!(val = value_to_str(&attr->val))) {
354                 return -1;
355         }
356
357         sz = (level >= 0 ? level : 0) + strlen(attr->name) + ts_dynarr_size(val) + 5;
358         if(!(buf = malloc(sz))) {
359                 perror("print_attr: failed to allocate name buffer");
360                 ts_dynarr_free(val);
361         }
362
363         if(level >= 0) {
364                 sz = sprintf(buf, "%s%s = %s\n", indent(level + 1), attr->name, val);
365         } else {
366                 sz = sprintf(buf, " %s = %s ", attr->name, val);
367         }
368         if(io->write(buf, sz, io->data) < sz) {
369                 ts_dynarr_free(val);
370                 free(buf);
371                 return -1;
372         }
373         ts_dynarr_free(val);
374         free(buf);
375         return 0;
376 }
377
378 static char *append_dynstr(char *dest, char *s)
379 {
380         while(*s) {
381                 DYNARR_STRPUSH(dest, *s++);
382         }
383         return dest;
384 }
385
386 static char *value_to_str(struct ts_value *value)
387 {
388         int i;
389         char buf[128];
390         char *str, *valstr;
391
392         if(!(str = ts_dynarr_alloc(0, 1))) {
393                 return 0;
394         }
395
396         switch(value->type) {
397         case TS_NUMBER:
398                 sprintf(buf, "%g", value->fnum);
399                 str = append_dynstr(str, buf);
400                 break;
401
402         case TS_VECTOR:
403                 DYNARR_STRPUSH(str, '[');
404                 for(i=0; i<value->vec_size; i++) {
405                         if(i == 0) {
406                                 sprintf(buf, "%g", value->vec[i]);
407                         } else {
408                                 sprintf(buf, ", %g", value->vec[i]);
409                         }
410                         str = append_dynstr(str, buf);
411                 }
412                 DYNARR_STRPUSH(str, ']');
413                 break;
414
415         case TS_ARRAY:
416                 DYNARR_STRPUSH(str, '[');
417                 for(i=0; i<value->array_size; i++) {
418                         if(i > 0) {
419                                 str = append_dynstr(str, ", ");
420                         }
421                         if(!(valstr = value_to_str(value->array + i))) {
422                                 ts_dynarr_free(str);
423                                 return 0;
424                         }
425                         str = append_dynstr(str, valstr);
426                         ts_dynarr_free(valstr);
427                 }
428                 DYNARR_STRPUSH(str, ']');
429                 break;
430
431         default:
432                 sprintf(buf, "\"%s\"", value->str);
433                 str = append_dynstr(str, buf);
434         }
435
436         return str;
437 }
438
439 static int tree_level(struct ts_node *n)
440 {
441         if(!n->parent) return 0;
442         return tree_level(n->parent) + 1;
443 }
444
445 static const char *indent(int x)
446 {
447         static const char buf[] = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
448         const char *end = buf + sizeof buf - 1;
449         return x > sizeof buf - 1 ? buf : end - x;
450 }
451
452 static const char *toktypestr(int type)
453 {
454         switch(type) {
455         case TOK_ID:
456                 return "identifier";
457         case TOK_NUM:
458                 return "number";
459         case TOK_STR:
460                 return "string";
461         case TOK_SYM:
462                 return "symbol";
463         }
464         return "unknown";
465 }