reorganize sources
[protos/libecoli.git] / libecoli / ecoli_parse.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright 2016, Olivier MATZ <zer0@droids-corp.org>
3  */
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <stdint.h>
8 #include <string.h>
9 #include <assert.h>
10 #include <errno.h>
11
12 #include <ecoli_assert.h>
13 #include <ecoli_malloc.h>
14 #include <ecoli_strvec.h>
15 #include <ecoli_keyval.h>
16 #include <ecoli_log.h>
17 #include <ecoli_test.h>
18 #include <ecoli_node.h>
19 #include <ecoli_node_sh_lex.h>
20 #include <ecoli_node_str.h>
21 #include <ecoli_node_seq.h>
22 #include <ecoli_parse.h>
23
24 EC_LOG_TYPE_REGISTER(parse);
25
26 TAILQ_HEAD(ec_parse_list, ec_parse);
27
28 struct ec_parse {
29         TAILQ_ENTRY(ec_parse) next;
30         struct ec_parse_list children;
31         struct ec_parse *parent;
32         const struct ec_node *node;
33         struct ec_strvec *strvec;
34         struct ec_keyval *attrs;
35 };
36
37 static int __ec_node_parse_child(const struct ec_node *node,
38                                 struct ec_parse *state,
39                                 bool is_root, const struct ec_strvec *strvec)
40 {
41         struct ec_strvec *match_strvec;
42         struct ec_parse *child = NULL;
43         int ret;
44
45         if (ec_node_type(node)->parse == NULL) {
46                 errno = ENOTSUP;
47                 return -1;
48         }
49
50         if (!is_root) {
51                 child = ec_parse(node);
52                 if (child == NULL)
53                         return -1;
54
55                 ec_parse_link_child(state, child);
56         } else {
57                 child = state;
58         }
59         ret = ec_node_type(node)->parse(node, child, strvec);
60         if (ret < 0)
61                 goto fail;
62
63         if (ret == EC_PARSE_NOMATCH) {
64                 if (!is_root) {
65                         ec_parse_unlink_child(state, child);
66                         ec_parse_free(child);
67                 }
68                 return ret;
69         }
70
71         match_strvec = ec_strvec_ndup(strvec, 0, ret);
72         if (match_strvec == NULL)
73                 goto fail;
74
75         child->strvec = match_strvec;
76
77         return ret;
78
79 fail:
80         if (!is_root) {
81                 ec_parse_unlink_child(state, child);
82                 ec_parse_free(child);
83         }
84         return -1;
85 }
86
87 int ec_node_parse_child(const struct ec_node *node, struct ec_parse *state,
88                         const struct ec_strvec *strvec)
89 {
90         assert(state != NULL);
91         return __ec_node_parse_child(node, state, false, strvec);
92 }
93
94 struct ec_parse *ec_node_parse_strvec(const struct ec_node *node,
95                                 const struct ec_strvec *strvec)
96 {
97         struct ec_parse *parse = ec_parse(node);
98         int ret;
99
100         if (parse == NULL)
101                 return NULL;
102
103         ret = __ec_node_parse_child(node, parse, true, strvec);
104         if (ret < 0) {
105                 ec_parse_free(parse);
106                 return NULL;
107         }
108
109         return parse;
110 }
111
112 struct ec_parse *ec_node_parse(const struct ec_node *node, const char *str)
113 {
114         struct ec_strvec *strvec = NULL;
115         struct ec_parse *parse = NULL;
116
117         errno = ENOMEM;
118         strvec = ec_strvec();
119         if (strvec == NULL)
120                 goto fail;
121
122         if (ec_strvec_add(strvec, str) < 0)
123                 goto fail;
124
125         parse = ec_node_parse_strvec(node, strvec);
126         if (parse == NULL)
127                 goto fail;
128
129         ec_strvec_free(strvec);
130         return parse;
131
132  fail:
133         ec_strvec_free(strvec);
134         ec_parse_free(parse);
135         return NULL;
136 }
137
138 struct ec_parse *ec_parse(const struct ec_node *node)
139 {
140         struct ec_parse *parse = NULL;
141
142         parse = ec_calloc(1, sizeof(*parse));
143         if (parse == NULL)
144                 goto fail;
145
146         TAILQ_INIT(&parse->children);
147
148         parse->node = node;
149         parse->attrs = ec_keyval();
150         if (parse->attrs == NULL)
151                 goto fail;
152
153         return parse;
154
155  fail:
156         if (parse != NULL)
157                 ec_keyval_free(parse->attrs);
158         ec_free(parse);
159
160         return NULL;
161 }
162
163 static struct ec_parse *
164 __ec_parse_dup(const struct ec_parse *root, const struct ec_parse *ref,
165                 struct ec_parse **new_ref)
166 {
167         struct ec_parse *dup = NULL;
168         struct ec_parse *child, *dup_child;
169         struct ec_keyval *attrs = NULL;
170
171         if (root == NULL)
172                 return NULL;
173
174         dup = ec_parse(root->node);
175         if (dup == NULL)
176                 return NULL;
177
178         if (root == ref)
179                 *new_ref = dup;
180
181         attrs = ec_keyval_dup(root->attrs);
182         if (attrs == NULL)
183                 goto fail;
184         ec_keyval_free(dup->attrs);
185         dup->attrs = attrs;
186
187         if (root->strvec != NULL) {
188                 dup->strvec = ec_strvec_dup(root->strvec);
189                 if (dup->strvec == NULL)
190                         goto fail;
191         }
192
193         TAILQ_FOREACH(child, &root->children, next) {
194                 dup_child = __ec_parse_dup(child, ref, new_ref);
195                 if (dup_child == NULL)
196                         goto fail;
197                 ec_parse_link_child(dup, dup_child);
198         }
199
200         return dup;
201
202 fail:
203         ec_parse_free(dup);
204         return NULL;
205 }
206
207 struct ec_parse *ec_parse_dup(const struct ec_parse *parse)
208 {
209         const struct ec_parse *root;
210         struct ec_parse *dup_root, *dup = NULL;
211
212         root = ec_parse_get_root(parse);
213         dup_root = __ec_parse_dup(root, parse, &dup);
214         if (dup_root == NULL)
215                 return NULL;
216         assert(dup != NULL);
217
218         return dup;
219 }
220
221 void ec_parse_free_children(struct ec_parse *parse)
222 {
223         struct ec_parse *child;
224
225         if (parse == NULL)
226                 return;
227
228         while (!TAILQ_EMPTY(&parse->children)) {
229                 child = TAILQ_FIRST(&parse->children);
230                 TAILQ_REMOVE(&parse->children, child, next);
231                 child->parent = NULL;
232                 ec_parse_free(child);
233         }
234 }
235
236 void ec_parse_free(struct ec_parse *parse)
237 {
238         if (parse == NULL)
239                 return;
240
241         ec_assert_print(parse->parent == NULL,
242                         "parent not NULL in ec_parse_free()");
243
244         ec_parse_free_children(parse);
245         ec_strvec_free(parse->strvec);
246         ec_keyval_free(parse->attrs);
247         ec_free(parse);
248 }
249
250 static void __ec_parse_dump(FILE *out,
251         const struct ec_parse *parse, size_t indent)
252 {
253         struct ec_parse *child;
254         const struct ec_strvec *vec;
255         const char *id = "none", *typename = "none";
256
257         /* node can be null when parsing is incomplete */
258         if (parse->node != NULL) {
259                 id = parse->node->id;
260                 typename = ec_node_type(parse->node)->name;
261         }
262
263         fprintf(out, "%*s" "type=%s id=%s vec=",
264                 (int)indent * 4, "", typename, id);
265         vec = ec_parse_strvec(parse);
266         ec_strvec_dump(out, vec);
267
268         TAILQ_FOREACH(child, &parse->children, next)
269                 __ec_parse_dump(out, child, indent + 1);
270 }
271
272 void ec_parse_dump(FILE *out, const struct ec_parse *parse)
273 {
274         fprintf(out, "------------------- parse dump:\n");
275
276         if (parse == NULL) {
277                 fprintf(out, "parse is NULL\n");
278                 return;
279         }
280
281         /* only exist if it does not match (strvec == NULL) and if it
282          * does not have children: an incomplete parse, like those
283          * generated by complete() don't match but have children that
284          * may match. */
285         if (!ec_parse_matches(parse) && TAILQ_EMPTY(&parse->children)) {
286                 fprintf(out, "no match\n");
287                 return;
288         }
289
290         __ec_parse_dump(out, parse, 0);
291 }
292
293 void ec_parse_link_child(struct ec_parse *parse,
294         struct ec_parse *child)
295 {
296         TAILQ_INSERT_TAIL(&parse->children, child, next);
297         child->parent = parse;
298 }
299
300 void ec_parse_unlink_child(struct ec_parse *parse,
301         struct ec_parse *child)
302 {
303         TAILQ_REMOVE(&parse->children, child, next);
304         child->parent = NULL;
305 }
306
307 struct ec_parse *
308 ec_parse_get_first_child(const struct ec_parse *parse)
309 {
310         return TAILQ_FIRST(&parse->children);
311 }
312
313 struct ec_parse *
314 ec_parse_get_last_child(const struct ec_parse *parse)
315 {
316         return TAILQ_LAST(&parse->children, ec_parse_list);
317 }
318
319 struct ec_parse *ec_parse_get_next(const struct ec_parse *parse)
320 {
321         return TAILQ_NEXT(parse, next);
322 }
323
324 bool ec_parse_has_child(const struct ec_parse *parse)
325 {
326         return !TAILQ_EMPTY(&parse->children);
327 }
328
329 const struct ec_node *ec_parse_get_node(const struct ec_parse *parse)
330 {
331         return parse->node;
332 }
333
334 void ec_parse_del_last_child(struct ec_parse *parse)
335 {
336         struct ec_parse *child;
337
338         child = ec_parse_get_last_child(parse);
339         ec_parse_unlink_child(parse, child);
340         ec_parse_free(child);
341 }
342
343 struct ec_parse *__ec_parse_get_root(struct ec_parse *parse)
344 {
345         if (parse == NULL)
346                 return NULL;
347
348         while (parse->parent != NULL)
349                 parse = parse->parent;
350
351         return parse;
352 }
353
354 struct ec_parse *ec_parse_get_parent(const struct ec_parse *parse)
355 {
356         if (parse == NULL)
357                 return NULL;
358
359         return parse->parent;
360 }
361
362 struct ec_parse *ec_parse_iter_next(struct ec_parse *parse)
363 {
364         struct ec_parse *child, *parent, *next;
365
366         child = TAILQ_FIRST(&parse->children);
367         if (child != NULL)
368                 return child;
369         parent = parse->parent;
370         while (parent != NULL) {
371                 next = TAILQ_NEXT(parse, next);
372                 if (next != NULL)
373                         return next;
374                 parse = parent;
375                 parent = parse->parent;
376         }
377         return NULL;
378 }
379
380 struct ec_parse *ec_parse_find_first(struct ec_parse *parse,
381         const char *id)
382 {
383         struct ec_parse *iter;
384
385         if (parse == NULL)
386                 return NULL;
387
388         for (iter = parse; iter != NULL; iter = ec_parse_iter_next(iter)) {
389                 if (iter->node != NULL &&
390                                 iter->node->id != NULL &&
391                                 !strcmp(iter->node->id, id))
392                         return iter;
393         }
394
395         return NULL;
396 }
397
398 struct ec_keyval *
399 ec_parse_get_attrs(struct ec_parse *parse)
400 {
401         if (parse == NULL)
402                 return NULL;
403
404         return parse->attrs;
405 }
406
407 const struct ec_strvec *ec_parse_strvec(const struct ec_parse *parse)
408 {
409         if (parse == NULL || parse->strvec == NULL)
410                 return NULL;
411
412         return parse->strvec;
413 }
414
415 /* number of strings in the parse vector */
416 size_t ec_parse_len(const struct ec_parse *parse)
417 {
418         if (parse == NULL || parse->strvec == NULL)
419                 return 0;
420
421         return ec_strvec_len(parse->strvec);
422 }
423
424 size_t ec_parse_matches(const struct ec_parse *parse)
425 {
426         if (parse == NULL)
427                 return 0;
428
429         if (parse->strvec == NULL)
430                 return 0;
431
432         return 1;
433 }
434
435 /* LCOV_EXCL_START */
436 static int ec_parse_testcase(void)
437 {
438         struct ec_node *node = NULL;
439         struct ec_parse *p = NULL, *p2 = NULL;
440         const struct ec_parse *pc;
441         FILE *f = NULL;
442         char *buf = NULL;
443         size_t buflen = 0;
444         int testres = 0;
445         int ret;
446
447         node = ec_node_sh_lex(EC_NO_ID,
448                         EC_NODE_SEQ(EC_NO_ID,
449                                 ec_node_str("id_x", "x"),
450                                 ec_node_str("id_y", "y")));
451         if (node == NULL)
452                 goto fail;
453
454         p = ec_node_parse(node, "xcdscds");
455         testres |= EC_TEST_CHECK(
456                 p != NULL && !ec_parse_matches(p),
457                 "parse should not match\n");
458
459         f = open_memstream(&buf, &buflen);
460         if (f == NULL)
461                 goto fail;
462         ec_parse_dump(f, p);
463         fclose(f);
464         f = NULL;
465
466         testres |= EC_TEST_CHECK(
467                 strstr(buf, "no match"), "bad dump\n");
468         free(buf);
469         buf = NULL;
470         ec_parse_free(p);
471
472         p = ec_node_parse(node, "x y");
473         testres |= EC_TEST_CHECK(
474                 p != NULL && ec_parse_matches(p),
475                 "parse should match\n");
476         testres |= EC_TEST_CHECK(
477                 ec_parse_len(p) == 1, "bad parse len\n");
478
479         ret = ec_keyval_set(ec_parse_get_attrs(p), "key", "val", NULL);
480         testres |= EC_TEST_CHECK(ret == 0,
481                 "cannot set parse attribute\n");
482
483         p2 = ec_parse_dup(p);
484         testres |= EC_TEST_CHECK(
485                 p2 != NULL && ec_parse_matches(p2),
486                 "parse should match\n");
487         ec_parse_free(p2);
488         p2 = NULL;
489
490         pc = ec_parse_find_first(p, "id_x");
491         testres |= EC_TEST_CHECK(pc != NULL, "cannot find id_x");
492         testres |= EC_TEST_CHECK(pc != NULL &&
493                 ec_parse_get_parent(pc) != NULL &&
494                 ec_parse_get_parent(ec_parse_get_parent(pc)) == p,
495                 "invalid parent\n");
496
497         pc = ec_parse_find_first(p, "id_y");
498         testres |= EC_TEST_CHECK(pc != NULL, "cannot find id_y");
499         pc = ec_parse_find_first(p, "id_dezdezdez");
500         testres |= EC_TEST_CHECK(pc == NULL, "should not find bad id");
501
502
503         f = open_memstream(&buf, &buflen);
504         if (f == NULL)
505                 goto fail;
506         ec_parse_dump(f, p);
507         fclose(f);
508         f = NULL;
509
510         testres |= EC_TEST_CHECK(
511                 strstr(buf, "type=sh_lex id=no-id") &&
512                 strstr(buf, "type=seq id=no-id") &&
513                 strstr(buf, "type=str id=id_x") &&
514                 strstr(buf, "type=str id=id_x"),
515                 "bad dump\n");
516         free(buf);
517         buf = NULL;
518
519         ec_parse_free(p);
520         ec_node_free(node);
521         return testres;
522
523 fail:
524         ec_parse_free(p2);
525         ec_parse_free(p);
526         ec_node_free(node);
527         if (f != NULL)
528                 fclose(f);
529         free(buf);
530
531         return -1;
532 }
533 /* LCOV_EXCL_STOP */
534
535 static struct ec_test ec_parse_test = {
536         .name = "parse",
537         .test = ec_parse_testcase,
538 };
539
540 EC_TEST_REGISTER(ec_parse_test);