libtreestore

view src/text.c @ 5:f3ade599cfbb

ts_free*/ts_destroy* functions shouldn't bork when passed a null pointer
author John Tsiombikas <nuclear@member.fsf.org>
date Sun, 13 Nov 2016 20:40:07 +0200
parents 48d75df3ef04
children
line source
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <ctype.h>
5 #include "treestore.h"
7 #define MAX_TOKEN_SIZE 512
8 struct parser {
9 FILE *fp;
10 int nline;
11 char token[MAX_TOKEN_SIZE];
12 };
14 enum { TOK_SYM, TOK_ID, TOK_NUM, TOK_STR };
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);
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);
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)
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)
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;
49 pstate.fp = fp;
50 pstate.nline = 0;
52 EXPECT(TOK_ID);
53 if(!(root_name = strdup(pst->token))) {
54 perror("failed to allocate root node name");
55 return 0;
56 }
57 EXPECT_SYM('{');
58 if(!(node = read_node(pst))) {
59 return 0;
60 }
61 node->name = root_name;
63 err:
64 return node;
65 }
67 static struct ts_node *read_node(struct parser *pst)
68 {
69 int type;
70 struct ts_node *node;
72 if(!(node = ts_alloc_node())) {
73 perror("failed to allocate treestore node");
74 return 0;
75 }
77 while((type = next_token(pst)) == TOK_ID) {
78 char *id;
80 if(!(id = strdup(pst->token))) {
81 goto err;
82 }
84 EXPECT(TOK_SYM);
86 if(pst->token[0] == '=') {
87 /* attribute */
88 struct ts_attr *attr;
89 int type;
91 if(!(attr = ts_alloc_attr())) {
92 goto err;
93 }
95 if((type = next_token(pst)) == -1) {
96 ts_free_attr(attr);
97 fprintf(stderr, "read_node: unexpected EOF\n");
98 goto err;
99 }
101 switch(type) {
102 case TOK_NUM:
103 ts_set_valuef(&attr->val, atof(pst->token));
104 break;
106 case TOK_SYM:
107 if(pst->token[0] == '[' || pst->token[0] == '{') {
108 char endsym = pst->token[0] + 2; /* end symbol is dist 2 from either '[' or '{' */
109 if(read_array(pst, &attr->val, endsym) == -1) {
110 goto err;
111 }
112 } else {
113 fprintf(stderr, "read_node: unexpected rhs symbol: %c\n", pst->token[0]);
114 }
115 break;
117 case TOK_ID:
118 case TOK_STR:
119 default:
120 ts_set_value(&attr->val, pst->token);
121 }
123 attr->name = id;
124 ts_add_attr(node, attr);
126 } else if(pst->token[0] == '{') {
127 /* child */
128 struct ts_node *child;
130 if(!(child = read_node(pst))) {
131 ts_free_node(node);
132 return 0;
133 }
135 child->name = id;
136 ts_add_child(node, child);
138 } else {
139 fprintf(stderr, "unexpected token: %s\n", pst->token);
140 goto err;
141 }
142 }
144 if(type != TOK_SYM || pst->token[0] != '}') {
145 fprintf(stderr, "expected closing brace\n");
146 goto err;
147 }
148 return node;
150 err:
151 fprintf(stderr, "treestore read_node failed\n");
152 ts_free_node(node);
153 return 0;
154 }
156 static int read_array(struct parser *pst, struct ts_value *tsv, char endsym)
157 {
158 // TODO implement
159 return -1;
160 }
162 static int next_token(struct parser *pst)
163 {
164 int c;
165 char *ptr, *bend = pst->token + MAX_TOKEN_SIZE - 1; /* leave space for the terminator */
167 // skip whitespace
168 while((c = fgetc(pst->fp)) != -1) {
169 if(c == '#') { // skip to end of line
170 while((c = fgetc(pst->fp)) != -1 && c != '\n');
171 if(c == -1) return -1;
172 }
173 if(!isspace(c)) break;
174 if(c == '\n') ++pst->nline;
175 }
176 if(c == -1) return -1;
178 pst->token[0] = c;
179 pst->token[1] = 0;
181 if(isdigit(c)) {
182 // token is a number
183 ptr = pst->token + 1;
184 while((c = fgetc(pst->fp)) != -1 && isdigit(c)) {
185 if(ptr < bend) *ptr++ = c;
186 }
187 if(c != -1) ungetc(c, pst->fp);
188 *ptr = 0;
189 return TOK_NUM;
190 }
191 if(isalpha(c)) {
192 // token is an identifier
193 ptr = pst->token + 1;
194 while((c = fgetc(pst->fp)) != -1 && isalnum(c)) {
195 if(ptr < bend) *ptr++ = c;
196 }
197 if(c != -1) ungetc(c, pst->fp);
198 *ptr = 0;
199 return TOK_ID;
200 }
201 if(c == '"') {
202 // token is a string constant
203 ptr = pst->token;
204 while((c = fgetc(pst->fp)) != -1 && c != '"' && c != '\r' && c != '\n') {
205 if(ptr < bend) *ptr++ = c;
206 }
207 if(c == '\n') ++pst->nline;
208 if(c != '"') {
209 return -1;
210 }
211 *ptr = 0;
212 return TOK_STR;
213 }
214 return TOK_SYM;
215 }
217 int ts_text_save(struct ts_node *tree, FILE *fp)
218 {
219 struct ts_node *c;
220 struct ts_attr *attr;
221 int lvl = tree_level(tree);
223 fprintf(fp, "%s%s {\n", indent(lvl), tree->name);
225 attr = tree->attr_list;
226 while(attr) {
227 print_attr(attr, fp, lvl);
228 attr = attr->next;
229 }
231 c = tree->child_list;
232 while(c) {
233 ts_text_save(c, fp);
234 c = c->next;
235 }
237 fprintf(fp, "%s}\n", indent(lvl));
238 }
240 static void print_attr(struct ts_attr *attr, FILE *fp, int level)
241 {
242 fprintf(fp, "%s%s = ", indent(level + 1), attr->name);
243 print_value(&attr->val, fp);
244 fputc('\n', fp);
245 }
247 static void print_value(struct ts_value *value, FILE *fp)
248 {
249 int i;
251 switch(value->type) {
252 case TS_NUMBER:
253 fprintf(fp, "%g", value->fnum);
254 break;
256 case TS_VECTOR:
257 fputc('[', fp);
258 for(i=0; i<value->vec_size; i++) {
259 if(i == 0) {
260 fprintf(fp, "%g", value->vec[i]);
261 } else {
262 fprintf(fp, ", %g", value->vec[i]);
263 }
264 }
265 fputc(']', fp);
266 break;
268 case TS_ARRAY:
269 fputc('[', fp);
270 for(i=0; i<value->array_size; i++) {
271 if(i > 0) {
272 fprintf(fp, ", ");
273 }
274 print_value(value->array + i, fp);
275 }
276 fputc(']', fp);
277 break;
279 default:
280 fprintf(fp, "\"%s\"", value->str);
281 }
282 }
284 static int tree_level(struct ts_node *n)
285 {
286 if(!n->parent) return 0;
287 return tree_level(n->parent) + 1;
288 }
290 static const char *indent(int x)
291 {
292 static const char buf[] = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
293 const char *end = buf + sizeof buf - 1;
294 return x > sizeof buf - 1 ? buf : end - x;
295 }
297 static const char *toktypestr(int type)
298 {
299 switch(type) {
300 case TOK_ID:
301 return "identifier";
302 case TOK_NUM:
303 return "number";
304 case TOK_STR:
305 return "string";
306 case TOK_SYM:
307 return "symbol";
308 }
309 return "unknown";
310 }