api documentation for ec_parse
[protos/libecoli.git] / src / ecoli_node.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_malloc.h>
13 #include <ecoli_string.h>
14 #include <ecoli_strvec.h>
15 #include <ecoli_dict.h>
16 #include <ecoli_log.h>
17 #include <ecoli_config.h>
18 #include <ecoli_test.h>
19 #include <ecoli_node.h>
20
21 #include <ecoli_node_str.h>
22 #include <ecoli_node_seq.h>
23 #include <ecoli_node_or.h>
24 #include <ecoli_node_int.h>
25
26 EC_LOG_TYPE_REGISTER(node);
27
28 /* These states are used to mark the grammar graph when freeing, to
29  * detect loop. */
30 enum ec_node_free_state {
31         EC_NODE_FREE_STATE_NONE,
32         EC_NODE_FREE_STATE_TRAVERSED,
33         EC_NODE_FREE_STATE_FREEABLE,
34         EC_NODE_FREE_STATE_NOT_FREEABLE,
35         EC_NODE_FREE_STATE_FREEING,
36 };
37
38 /**
39  * The grammar node structure.
40  */
41 struct ec_node {
42         const struct ec_node_type *type; /**< The node type. */
43         struct ec_config *config;        /**< Node configuration. */
44         char *id;                  /**< Node identifier (EC_NO_ID if none). */
45         struct ec_dict *attrs;           /**< Attributes of the node. */
46         unsigned int refcnt;             /**< Reference counter. */
47         struct {
48                 enum ec_node_free_state state; /**< State of loop detection. */
49                 unsigned int refcnt;    /**< Number of reachable references
50                                          *   starting from node beeing freed. */
51         } free; /**< Freeing state: used for loop detection */
52 };
53
54 static struct ec_node_type_list node_type_list =
55         TAILQ_HEAD_INITIALIZER(node_type_list);
56
57 const struct ec_node_type *
58 ec_node_type_lookup(const char *name)
59 {
60         struct ec_node_type *type;
61
62         TAILQ_FOREACH(type, &node_type_list, next) {
63                 if (!strcmp(name, type->name))
64                         return type;
65         }
66
67         errno = ENOENT;
68         return NULL;
69 }
70
71 int ec_node_type_register(struct ec_node_type *type, bool override)
72 {
73         if (!override && ec_node_type_lookup(type->name) != NULL) {
74                 errno = EEXIST;
75                 return -1;
76         }
77
78         TAILQ_INSERT_HEAD(&node_type_list, type, next);
79
80         return 0;
81 }
82
83 void ec_node_type_dump(FILE *out)
84 {
85         struct ec_node_type *type;
86
87         TAILQ_FOREACH(type, &node_type_list, next)
88                 fprintf(out, "%s\n", type->name);
89 }
90
91 struct ec_node *ec_node_from_type(const struct ec_node_type *type, const char *id)
92 {
93         struct ec_node *node = NULL;
94
95         EC_LOG(EC_LOG_DEBUG, "create node type=%s id=%s\n",
96                 type->name, id);
97         if (id == NULL) {
98                 errno = EINVAL;
99                 goto fail;
100         }
101
102         node = ec_calloc(1, sizeof(*node) + type->size);
103         if (node == NULL)
104                 goto fail;
105
106         node->type = type;
107         node->refcnt = 1;
108
109         // XXX check that id matches [_a-zA-Z][:-_0-9a-zA-Z]*
110         node->id = ec_strdup(id);
111         if (node->id == NULL)
112                 goto fail;
113
114         node->attrs = ec_dict();
115         if (node->attrs == NULL)
116                 goto fail;
117
118         if (type->init_priv != NULL) {
119                 if (type->init_priv(node) < 0)
120                         goto fail;
121         }
122
123         return node;
124
125  fail:
126         if (node != NULL) {
127                 ec_dict_free(node->attrs);
128                 ec_free(node->id);
129         }
130         ec_free(node);
131
132         return NULL;
133 }
134
135 const struct ec_config_schema *
136 ec_node_type_schema(const struct ec_node_type *type)
137 {
138         return type->schema;
139 }
140
141 const char *
142 ec_node_type_name(const struct ec_node_type *type)
143 {
144         return type->name;
145 }
146
147 struct ec_node *ec_node(const char *typename, const char *id)
148 {
149         const struct ec_node_type *type;
150
151         type = ec_node_type_lookup(typename);
152         if (type == NULL) {
153                 EC_LOG(EC_LOG_ERR, "type=%s does not exist\n",
154                         typename);
155                 return NULL;
156         }
157
158         return ec_node_from_type(type, id);
159 }
160
161 static void count_references(struct ec_node *node, unsigned int refs)
162 {
163         struct ec_node *child;
164         size_t i, n;
165         int ret;
166
167         if (node->free.state == EC_NODE_FREE_STATE_TRAVERSED) {
168                 node->free.refcnt += refs;
169                 return;
170         }
171         node->free.refcnt = refs;
172         node->free.state = EC_NODE_FREE_STATE_TRAVERSED;
173         n = ec_node_get_children_count(node);
174         for (i = 0; i < n; i++) {
175                 ret = ec_node_get_child(node, i, &child, &refs);
176                 assert(ret == 0);
177                 count_references(child, refs);
178         }
179 }
180
181 static void mark_freeable(struct ec_node *node, enum ec_node_free_state mark)
182 {
183         struct ec_node *child;
184         unsigned int refs;
185         size_t i, n;
186         int ret;
187
188         if (mark == node->free.state)
189                 return;
190
191         if (node->refcnt > node->free.refcnt)
192                 mark = EC_NODE_FREE_STATE_NOT_FREEABLE;
193         assert(node->refcnt >= node->free.refcnt);
194         node->free.state = mark;
195
196         n = ec_node_get_children_count(node);
197         for (i = 0; i < n; i++) {
198                 ret = ec_node_get_child(node, i, &child, &refs);
199                 assert(ret == 0);
200                 mark_freeable(child, mark);
201         }
202 }
203
204 static void reset_mark(struct ec_node *node)
205 {
206         struct ec_node *child;
207         unsigned int refs;
208         size_t i, n;
209         int ret;
210
211         if (node->free.state == EC_NODE_FREE_STATE_NONE)
212                 return;
213
214         node->free.state = EC_NODE_FREE_STATE_NONE;
215         node->free.refcnt = 0;
216
217         n = ec_node_get_children_count(node);
218         for (i = 0; i < n; i++) {
219                 ret = ec_node_get_child(node, i, &child, &refs);
220                 assert(ret == 0);
221                 reset_mark(child);
222         }
223 }
224
225 /* free a node, taking care of loops in the node graph */
226 void ec_node_free(struct ec_node *node)
227 {
228         size_t n;
229
230         if (node == NULL)
231                 return;
232
233         assert(node->refcnt > 0);
234
235         if (node->free.state == EC_NODE_FREE_STATE_NONE &&
236                         node->refcnt != 1) {
237
238                 /* Traverse the node tree starting from this node, and for each
239                  * node, count the number of reachable references. Then, all
240                  * nodes whose reachable references == total reference are
241                  * marked as freeable, and other are marked as unfreeable. Any
242                  * node reachable from an unfreeable node is also marked as
243                  * unfreeable. */
244                 if (node->free.state == EC_NODE_FREE_STATE_NONE) {
245                         count_references(node, 1);
246                         mark_freeable(node, EC_NODE_FREE_STATE_FREEABLE);
247                 }
248         }
249
250         if (node->free.state == EC_NODE_FREE_STATE_NOT_FREEABLE) {
251                 node->refcnt--;
252                 reset_mark(node);
253                 return;
254         }
255
256         if (node->free.state != EC_NODE_FREE_STATE_FREEING) {
257                 node->free.state = EC_NODE_FREE_STATE_FREEING;
258
259                 /* children will be freed by config_free() and free_priv() */
260                 ec_config_free(node->config);
261                 node->config = NULL;
262                 n = ec_node_get_children_count(node);
263                 assert(n == 0 || node->type->free_priv != NULL);
264                 if (node->type->free_priv != NULL)
265                         node->type->free_priv(node);
266                 ec_free(node->id);
267                 ec_dict_free(node->attrs);
268         }
269
270         node->refcnt--;
271         if (node->refcnt != 0)
272                 return;
273
274         node->free.state = EC_NODE_FREE_STATE_NONE;
275         node->free.refcnt = 0;
276
277         ec_free(node);
278 }
279
280 struct ec_node *ec_node_clone(struct ec_node *node)
281 {
282         if (node != NULL)
283                 node->refcnt++;
284         return node;
285 }
286
287 size_t ec_node_get_children_count(const struct ec_node *node)
288 {
289         if (node->type->get_children_count == NULL)
290                 return 0;
291         return node->type->get_children_count(node);
292 }
293
294 int
295 ec_node_get_child(const struct ec_node *node, size_t i,
296         struct ec_node **child, unsigned int *refs)
297 {
298         *child = NULL;
299         *refs = 0;
300         if (node->type->get_child == NULL)
301                 return -1;
302         return node->type->get_child(node, i, child, refs);
303 }
304
305 int
306 ec_node_set_config(struct ec_node *node, struct ec_config *config)
307 {
308         if (node->type->schema == NULL) {
309                 errno = EINVAL;
310                 goto fail;
311         }
312         if (ec_config_validate(config, node->type->schema) < 0)
313                 goto fail;
314         if (node->type->set_config != NULL) {
315                 if (node->type->set_config(node, config) < 0)
316                         goto fail;
317         }
318
319         ec_config_free(node->config);
320         node->config = config;
321
322         return 0;
323
324 fail:
325         ec_config_free(config);
326         return -1;
327 }
328
329 const struct ec_config *ec_node_get_config(struct ec_node *node)
330 {
331         return node->config;
332 }
333
334 struct ec_node *ec_node_find(struct ec_node *node, const char *id)
335 {
336         struct ec_node *child, *retnode;
337         const char *node_id = ec_node_id(node);
338         unsigned int refs;
339         size_t i, n;
340         int ret;
341
342         if (id != NULL && node_id != NULL && !strcmp(node_id, id))
343                 return node;
344
345         n = ec_node_get_children_count(node);
346         for (i = 0; i < n; i++) {
347                 ret = ec_node_get_child(node, i, &child, &refs);
348                 assert(ret == 0);
349                 retnode = ec_node_find(child, id);
350                 if (retnode != NULL)
351                         return retnode;
352         }
353
354         return NULL;
355 }
356
357 const struct ec_node_type *ec_node_type(const struct ec_node *node)
358 {
359         return node->type;
360 }
361
362 struct ec_dict *ec_node_attrs(const struct ec_node *node)
363 {
364         return node->attrs;
365 }
366
367 const char *ec_node_id(const struct ec_node *node)
368 {
369         return node->id;
370 }
371
372 static void __ec_node_dump(FILE *out,
373         const struct ec_node *node, size_t indent, struct ec_dict *dict)
374 {
375         const char *id, *typename;
376         struct ec_node *child;
377         unsigned int refs;
378         char buf[32];
379         size_t i, n;
380         int ret;
381
382         id = ec_node_id(node);
383         typename = node->type->name;
384
385         snprintf(buf, sizeof(buf), "%p", node);
386         if (ec_dict_has_key(dict, buf)) {
387                 fprintf(out, "%*s" "type=%s id=%s %p... (loop)\n",
388                         (int)indent * 4, "", typename, id, node);
389                 return;
390         }
391
392         ec_dict_set(dict, buf, NULL, NULL);
393         fprintf(out, "%*s" "type=%s id=%s %p refs=%u free_state=%d free_refs=%d\n",
394                 (int)indent * 4, "", typename, id, node, node->refcnt,
395                 node->free.state, node->free.refcnt);
396
397         n = ec_node_get_children_count(node);
398         for (i = 0; i < n; i++) {
399                 ret = ec_node_get_child(node, i, &child, &refs);
400                 assert(ret == 0);
401                 __ec_node_dump(out, child, indent + 1, dict);
402         }
403 }
404
405 /* XXX this is too much debug-oriented, we should have a parameter or 2 funcs */
406 void ec_node_dump(FILE *out, const struct ec_node *node)
407 {
408         struct ec_dict *dict = NULL;
409
410         fprintf(out, "------------------- node dump:\n");
411
412         if (node == NULL) {
413                 fprintf(out, "node is NULL\n");
414                 return;
415         }
416
417         dict = ec_dict();
418         if (dict == NULL)
419                 goto fail;
420
421         __ec_node_dump(out, node, 0, dict);
422
423         ec_dict_free(dict);
424         return;
425
426 fail:
427         ec_dict_free(dict);
428         EC_LOG(EC_LOG_ERR, "failed to dump node\n");
429 }
430
431 char *ec_node_desc(const struct ec_node *node)
432 {
433         char *desc = NULL;
434
435         if (node->type->desc != NULL)
436                 return node->type->desc(node);
437
438         if (ec_asprintf(&desc, "<%s>", node->type->name) < 0)
439                 return NULL;
440
441         return desc;
442 }
443
444 int ec_node_check_type(const struct ec_node *node,
445                 const struct ec_node_type *type)
446 {
447         if (strcmp(node->type->name, type->name)) {
448                 errno = EINVAL;
449                 return -1;
450         }
451
452         return 0;
453 }
454
455 const char *ec_node_get_type_name(const struct ec_node *node)
456 {
457         return node->type->name;
458 }
459
460 void *ec_node_priv(const struct ec_node *node)
461 {
462         if (node == NULL)
463                 return NULL;
464         return (void *)(node + 1);
465 }
466
467 /* LCOV_EXCL_START */
468 static int ec_node_testcase(void)
469 {
470         struct ec_node *node = NULL, *expr = NULL;
471         struct ec_node *expr2 = NULL, *val = NULL, *op = NULL, *seq = NULL;
472         const struct ec_node_type *type;
473         struct ec_node *child;
474         unsigned int refs;
475         FILE *f = NULL;
476         char *buf = NULL;
477         char *desc = NULL;
478         size_t buflen = 0;
479         int testres = 0;
480         int ret;
481
482         node = EC_NODE_SEQ(EC_NO_ID,
483                         ec_node_str("id_x", "x"),
484                         ec_node_str("id_y", "y"));
485         if (node == NULL)
486                 goto fail;
487
488         ec_node_clone(node);
489         ec_node_free(node);
490
491         f = open_memstream(&buf, &buflen);
492         if (f == NULL)
493                 goto fail;
494         ec_node_dump(f, node);
495         ec_node_type_dump(f);
496         ec_node_dump(f, NULL);
497         fclose(f);
498         f = NULL;
499
500         testres |= EC_TEST_CHECK(
501                 strstr(buf, "type=seq id=no-id"), "bad dump\n");
502         testres |= EC_TEST_CHECK(
503                 strstr(buf, "type=str id=id_x") &&
504                 strstr(strstr(buf, "type=str id=id_x") + 1,
505                         "type=str id=id_y"),
506                 "bad dump\n");
507         free(buf);
508         buf = NULL;
509
510         desc = ec_node_desc(node);
511         testres |= EC_TEST_CHECK(
512                 !strcmp(ec_node_type(node)->name, "seq") &&
513                 !strcmp(ec_node_id(node), EC_NO_ID) &&
514                 !strcmp(desc, "<seq>"),
515                 "bad child 0");
516         ec_free(desc);
517         desc = NULL;
518
519         testres |= EC_TEST_CHECK(
520                 ec_node_get_children_count(node) == 2,
521                 "bad children count\n");
522         ret = ec_node_get_child(node, 0, &child, &refs);
523         testres |= EC_TEST_CHECK(ret == 0 &&
524                 child != NULL &&
525                 !strcmp(ec_node_type(child)->name, "str") &&
526                 !strcmp(ec_node_id(child), "id_x"),
527                 "bad child 0");
528         ret = ec_node_get_child(node, 1, &child, &refs);
529         testres |= EC_TEST_CHECK(ret == 0 &&
530                 child != NULL &&
531                 !strcmp(ec_node_type(child)->name, "str") &&
532                 !strcmp(ec_node_id(child), "id_y"),
533                 "bad child 1");
534         ret = ec_node_get_child(node, 2, &child, &refs);
535         testres |= EC_TEST_CHECK(ret != 0,
536                 "ret should be != 0");
537         testres |= EC_TEST_CHECK(child == NULL,
538                 "child 2 should be NULL");
539
540         child = ec_node_find(node, "id_x");
541         desc = ec_node_desc(child);
542         testres |= EC_TEST_CHECK(child != NULL &&
543                 !strcmp(ec_node_type(child)->name, "str") &&
544                 !strcmp(ec_node_id(child), "id_x") &&
545                 !strcmp(desc, "x"),
546                 "bad child id_x");
547         ec_free(desc);
548         desc = NULL;
549         child = ec_node_find(node, "id_dezdex");
550         testres |= EC_TEST_CHECK(child == NULL,
551                 "child with wrong id should be NULL");
552
553         ret = ec_dict_set(ec_node_attrs(node), "key", "val", NULL);
554         testres |= EC_TEST_CHECK(ret == 0,
555                 "cannot set node attribute\n");
556
557         type = ec_node_type_lookup("seq");
558         testres |= EC_TEST_CHECK(type != NULL &&
559                 ec_node_check_type(node, type) == 0,
560                 "cannot get seq node type");
561         type = ec_node_type_lookup("str");
562         testres |= EC_TEST_CHECK(type != NULL &&
563                 ec_node_check_type(node, type) < 0,
564                 "node type should not be str");
565
566         ec_node_free(node);
567         node = NULL;
568
569         node = ec_node("deznuindez", EC_NO_ID);
570         testres |= EC_TEST_CHECK(node == NULL,
571                         "should not be able to create node\n");
572
573         /* test loop */
574         expr = ec_node("or", EC_NO_ID);
575         val = ec_node_int(EC_NO_ID, 0, 10, 0);
576         op = ec_node_str(EC_NO_ID, "!");
577         seq = EC_NODE_SEQ(EC_NO_ID,
578                         op,
579                         ec_node_clone(expr));
580         op = NULL;
581         if (expr == NULL || val == NULL || seq == NULL)
582                 goto fail;
583         if (ec_node_or_add(expr, ec_node_clone(seq)) < 0)
584                 goto fail;
585         ec_node_free(seq);
586         seq = NULL;
587         if (ec_node_or_add(expr, ec_node_clone(val)) < 0)
588                 goto fail;
589         ec_node_free(val);
590         val = NULL;
591
592         testres |= EC_TEST_CHECK_PARSE(expr, 1, "1");
593         testres |= EC_TEST_CHECK_PARSE(expr, 3, "!", "!", "1");
594         testres |= EC_TEST_CHECK_PARSE(expr, -1, "!", "!", "!");
595
596         ec_node_free(expr);
597         expr = NULL;
598
599         /* same loop test, but keep some refs (released later) */
600         expr = ec_node("or", EC_NO_ID);
601         ec_node_clone(expr);
602         expr2 = expr;
603         val = ec_node_int(EC_NO_ID, 0, 10, 0);
604         op = ec_node_str(EC_NO_ID, "!");
605         seq = EC_NODE_SEQ(EC_NO_ID,
606                         op,
607                         ec_node_clone(expr));
608         op = NULL;
609         if (expr == NULL || val == NULL || seq == NULL)
610                 goto fail;
611         if (ec_node_or_add(expr, ec_node_clone(seq)) < 0)
612                 goto fail;
613         ec_node_free(seq);
614         seq = NULL;
615         if (ec_node_or_add(expr, ec_node_clone(val)) < 0)
616                 goto fail;
617
618         testres |= EC_TEST_CHECK_PARSE(expr, 1, "1");
619         testres |= EC_TEST_CHECK_PARSE(expr, 3, "!", "!", "1");
620         testres |= EC_TEST_CHECK_PARSE(expr, -1, "!", "!", "!");
621
622         ec_node_free(expr2);
623         expr2 = NULL;
624         ec_node_free(val);
625         val = NULL;
626         ec_node_free(expr);
627         expr = NULL;
628
629         return testres;
630
631 fail:
632         ec_node_free(expr);
633         ec_node_free(expr2);
634         ec_node_free(val);
635         ec_node_free(seq);
636         ec_node_free(node);
637         if (f != NULL)
638                 fclose(f);
639         free(buf);
640
641         assert(errno != 0);
642         return -1;
643 }
644 /* LCOV_EXCL_STOP */
645
646 static struct ec_test ec_node_test = {
647         .name = "node",
648         .test = ec_node_testcase,
649 };
650
651 EC_TEST_REGISTER(ec_node_test);