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