347c28cd0c5dafbd75a86633feeee0429f8d18fb
[dosdemo] / src / ts_text.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <ctype.h>
5 #include "treestor.h"
6 #include "dynarr.h"
7
8 struct parser {
9         FILE *fp;
10         int nline;
11         char *token;
12 };
13
14 enum { TOK_SYM, TOK_ID, TOK_NUM, TOK_STR };
15
16 static struct ts_node *read_node(struct parser *pstate);
17 static int read_array(struct parser *pstate, struct ts_value *tsv, char endsym);
18 static int next_token(struct parser *pstate);
19
20 static void print_attr(struct ts_attr *attr, FILE *fp, int level);
21 static void print_value(struct ts_value *value, FILE *fp);
22 static int tree_level(struct ts_node *n);
23 static const char *indent(int x);
24 static const char *toktypestr(int type);
25
26 #define EXPECT(type) \
27         do { \
28                 if(next_token(pst) != (type)) { \
29                         fprintf(stderr, "expected %s token\n", toktypestr(type)); \
30                         goto err; \
31                 } \
32         } while(0)
33
34 #define EXPECT_SYM(c) \
35         do { \
36                 if(next_token(pst) != TOK_SYM || pst->token[0] != (c)) { \
37                         fprintf(stderr, "expected symbol: %c\n", c); \
38                         goto err; \
39                 } \
40         } while(0)
41
42
43 struct ts_node *ts_text_load(FILE *fp)
44 {
45         char *root_name;
46         struct parser pstate, *pst = &pstate;
47         struct ts_node *node = 0;
48
49         pstate.fp = fp;
50         pstate.nline = 0;
51         if(!(pstate.token = dynarr_alloc(0, 1))) {
52                 perror("failed to allocate token string");
53                 return 0;
54         }
55
56         EXPECT(TOK_ID);
57         if(!(root_name = strdup(pst->token))) {
58                 perror("failed to allocate root node name");
59                 dynarr_free(pst->token);
60                 return 0;
61         }
62         EXPECT_SYM('{');
63         if(!(node = read_node(pst))) {
64                 dynarr_free(pst->token);
65                 return 0;
66         }
67         node->name = root_name;
68
69 err:
70         dynarr_free(pst->token);
71         return node;
72 }
73
74 static int read_value(struct parser *pst, int toktype, struct ts_value *val)
75 {
76         switch(toktype) {
77         case TOK_NUM:
78                 ts_set_valuef(val, atof(pst->token));
79                 break;
80
81         case TOK_SYM:
82                 if(pst->token[0] == '[' || pst->token[0] == '{') {
83                         char endsym = pst->token[0] + 2; /* end symbol is dist 2 from either '[' or '{' */
84                         if(read_array(pst, val, endsym) == -1) {
85                                 return -1;
86                         }
87                 } else {
88                         fprintf(stderr, "read_node: unexpected rhs symbol: %c\n", pst->token[0]);
89                 }
90                 break;
91
92         case TOK_ID:
93         case TOK_STR:
94         default:
95                 ts_set_value_str(val, pst->token);
96         }
97
98         return 0;
99 }
100
101 static struct ts_node *read_node(struct parser *pst)
102 {
103         int type;
104         struct ts_node *node;
105
106         if(!(node = ts_alloc_node())) {
107                 perror("failed to allocate treestore node");
108                 return 0;
109         }
110
111         while((type = next_token(pst)) == TOK_ID) {
112                 char *id;
113
114                 if(!(id = strdup(pst->token))) {
115                         goto err;
116                 }
117
118                 EXPECT(TOK_SYM);
119
120                 if(pst->token[0] == '=') {
121                         /* attribute */
122                         struct ts_attr *attr;
123                         int type;
124
125                         if(!(attr = ts_alloc_attr())) {
126                                 goto err;
127                         }
128
129                         if((type = next_token(pst)) == -1) {
130                                 ts_free_attr(attr);
131                                 fprintf(stderr, "read_node: unexpected EOF\n");
132                                 goto err;
133                         }
134
135                         if(read_value(pst, type, &attr->val) == -1) {
136                                 ts_free_attr(attr);
137                                 fprintf(stderr, "failed to read value\n");
138                                 goto err;
139                         }
140                         attr->name = id;
141                         ts_add_attr(node, attr);
142
143                 } else if(pst->token[0] == '{') {
144                         /* child */
145                         struct ts_node *child;
146
147                         if(!(child = read_node(pst))) {
148                                 ts_free_node(node);
149                                 return 0;
150                         }
151
152                         child->name = id;
153                         ts_add_child(node, child);
154
155                 } else {
156                         fprintf(stderr, "unexpected token: %s\n", pst->token);
157                         goto err;
158                 }
159         }
160
161         if(type != TOK_SYM || pst->token[0] != '}') {
162                 fprintf(stderr, "expected closing brace\n");
163                 goto err;
164         }
165         return node;
166
167 err:
168         fprintf(stderr, "treestore read_node failed\n");
169         ts_free_node(node);
170         return 0;
171 }
172
173 static int read_array(struct parser *pst, struct ts_value *tsv, char endsym)
174 {
175         int type;
176         struct ts_value values[32];
177         int i, nval = 0;
178         int res;
179
180         while((type = next_token(pst)) != -1) {
181                 ts_init_value(values + nval);
182                 if(read_value(pst, type, values + nval) == -1) {
183                         return -1;
184                 }
185                 if(nval < 31) {
186                         ++nval;
187                 } else {
188                         ts_destroy_value(values + nval);
189                 }
190
191                 type = next_token(pst);
192                 if(!(type == TOK_SYM && (pst->token[0] == ',' || pst->token[0] == endsym))) {
193                         fprintf(stderr, "read_array: expected comma or end symbol ('%c')\n", endsym);
194                         return -1;
195                 }
196                 if(pst->token[0] == endsym) {
197                         break;  /* we're done */
198                 }
199         }
200
201         if(!nval) {
202                 return -1;
203         }
204
205         res = ts_set_value_arr(tsv, nval, values);
206
207         for(i=0; i<nval; i++) {
208                 ts_destroy_value(values + i);
209         }
210         return res;
211 }
212
213 static int next_token(struct parser *pst)
214 {
215         int c;
216
217         DYNARR_CLEAR(pst->token);
218
219         // skip whitespace
220         while((c = fgetc(pst->fp)) != -1) {
221                 if(c == '#') { // skip to end of line
222                         while((c = fgetc(pst->fp)) != -1 && c != '\n');
223                         if(c == -1) return -1;
224                 }
225                 if(!isspace(c)) break;
226                 if(c == '\n') ++pst->nline;
227         }
228         if(c == -1) return -1;
229
230         DYNARR_STRPUSH(pst->token, c);
231
232         if(isdigit(c) || c == '-' || c == '+') {
233                 // token is a number
234                 int found_dot = 0;
235                 while((c = fgetc(pst->fp)) != -1 &&
236                                 (isdigit(c) || (c == '.' && !found_dot))) {
237                         DYNARR_STRPUSH(pst->token, c);
238                         if(c == '.') found_dot = 1;
239                 }
240                 if(c != -1) ungetc(c, pst->fp);
241                 return TOK_NUM;
242         }
243         if(isalpha(c)) {
244                 // token is an identifier
245                 while((c = fgetc(pst->fp)) != -1 && (isalnum(c) || c == '_')) {
246                         DYNARR_STRPUSH(pst->token, c);
247                 }
248                 if(c != -1) ungetc(c, pst->fp);
249                 return TOK_ID;
250         }
251         if(c == '"') {
252                 // token is a string constant
253                 // remove the opening quote
254                 DYNARR_STRPOP(pst->token);
255                 while((c = fgetc(pst->fp)) != -1 && c != '"') {
256                         DYNARR_STRPUSH(pst->token, c);
257                         if(c == '\n') ++pst->nline;
258                 }
259                 if(c != '"') {
260                         return -1;
261                 }
262                 return TOK_STR;
263         }
264         return TOK_SYM;
265 }
266
267 int ts_text_save(struct ts_node *tree, FILE *fp)
268 {
269         struct ts_node *c;
270         struct ts_attr *attr;
271         int lvl = tree_level(tree);
272
273         fprintf(fp, "%s%s {\n", indent(lvl), tree->name);
274
275         attr = tree->attr_list;
276         while(attr) {
277                 print_attr(attr, fp, lvl);
278                 attr = attr->next;
279         }
280
281         c = tree->child_list;
282         while(c) {
283                 ts_text_save(c, fp);
284                 c = c->next;
285         }
286
287         fprintf(fp, "%s}\n", indent(lvl));
288         return 0;
289 }
290
291 static void print_attr(struct ts_attr *attr, FILE *fp, int level)
292 {
293         fprintf(fp, "%s%s = ", indent(level + 1), attr->name);
294         print_value(&attr->val, fp);
295         fputc('\n', fp);
296 }
297
298 static void print_value(struct ts_value *value, FILE *fp)
299 {
300         int i;
301
302         switch(value->type) {
303         case TS_NUMBER:
304                 fprintf(fp, "%g", value->fnum);
305                 break;
306
307         case TS_VECTOR:
308                 fputc('[', fp);
309                 for(i=0; i<value->vec_size; i++) {
310                         if(i == 0) {
311                                 fprintf(fp, "%g", value->vec[i]);
312                         } else {
313                                 fprintf(fp, ", %g", value->vec[i]);
314                         }
315                 }
316                 fputc(']', fp);
317                 break;
318
319         case TS_ARRAY:
320                 fputc('[', fp);
321                 for(i=0; i<value->array_size; i++) {
322                         if(i > 0) {
323                                 fprintf(fp, ", ");
324                         }
325                         print_value(value->array + i, fp);
326                 }
327                 fputc(']', fp);
328                 break;
329
330         default:
331                 fprintf(fp, "\"%s\"", value->str);
332         }
333 }
334
335 static int tree_level(struct ts_node *n)
336 {
337         if(!n->parent) return 0;
338         return tree_level(n->parent) + 1;
339 }
340
341 static const char *indent(int x)
342 {
343         static const char buf[] = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
344         const char *end = buf + sizeof buf - 1;
345         return x > sizeof buf - 1 ? buf : end - x;
346 }
347
348 static const char *toktypestr(int type)
349 {
350         switch(type) {
351         case TOK_ID:
352                 return "identifier";
353         case TOK_NUM:
354                 return "number";
355         case TOK_STR:
356                 return "string";
357         case TOK_SYM:
358                 return "symbol";
359         }
360         return "unknown";
361 }