1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright 2016, Olivier MATZ <zer0@droids-corp.org>
12 #include <ecoli_assert.h>
13 #include <ecoli_malloc.h>
14 #include <ecoli_strvec.h>
15 #include <ecoli_dict.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>
24 EC_LOG_TYPE_REGISTER(parse);
26 TAILQ_HEAD(ec_parse_list, 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_dict *attrs;
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)
41 struct ec_strvec *match_strvec;
42 struct ec_parse *child = NULL;
45 // XXX limit max number of recursions to avoid segfault
47 if (ec_node_type(node)->parse == NULL) {
53 child = ec_parse(node);
57 ec_parse_link_child(state, child);
61 ret = ec_node_type(node)->parse(node, child, strvec);
65 if (ret == EC_PARSE_NOMATCH) {
67 ec_parse_unlink_child(state, child);
73 match_strvec = ec_strvec_ndup(strvec, 0, ret);
74 if (match_strvec == NULL)
77 child->strvec = match_strvec;
83 ec_parse_unlink_child(state, child);
89 int ec_node_parse_child(const struct ec_node *node, struct ec_parse *state,
90 const struct ec_strvec *strvec)
92 assert(state != NULL);
93 return __ec_node_parse_child(node, state, false, strvec);
96 // XXX what is returned if no match ??
97 struct ec_parse *ec_node_parse_strvec(const struct ec_node *node,
98 const struct ec_strvec *strvec)
100 struct ec_parse *parse = ec_parse(node);
106 ret = __ec_node_parse_child(node, parse, true, strvec);
108 ec_parse_free(parse);
115 struct ec_parse *ec_node_parse(const struct ec_node *node, const char *str)
117 struct ec_strvec *strvec = NULL;
118 struct ec_parse *parse = NULL;
121 strvec = ec_strvec();
125 if (ec_strvec_add(strvec, str) < 0)
128 parse = ec_node_parse_strvec(node, strvec);
132 ec_strvec_free(strvec);
136 ec_strvec_free(strvec);
137 ec_parse_free(parse);
141 struct ec_parse *ec_parse(const struct ec_node *node)
143 struct ec_parse *parse = NULL;
145 parse = ec_calloc(1, sizeof(*parse));
149 TAILQ_INIT(&parse->children);
152 parse->attrs = ec_dict();
153 if (parse->attrs == NULL)
160 ec_dict_free(parse->attrs);
166 static struct ec_parse *
167 __ec_parse_dup(const struct ec_parse *root, const struct ec_parse *ref,
168 struct ec_parse **new_ref)
170 struct ec_parse *dup = NULL;
171 struct ec_parse *child, *dup_child;
172 struct ec_dict *attrs = NULL;
177 dup = ec_parse(root->node);
184 attrs = ec_dict_dup(root->attrs);
187 ec_dict_free(dup->attrs);
190 if (root->strvec != NULL) {
191 dup->strvec = ec_strvec_dup(root->strvec);
192 if (dup->strvec == NULL)
196 TAILQ_FOREACH(child, &root->children, next) {
197 dup_child = __ec_parse_dup(child, ref, new_ref);
198 if (dup_child == NULL)
200 ec_parse_link_child(dup, dup_child);
210 struct ec_parse *ec_parse_dup(const struct ec_parse *parse)
212 const struct ec_parse *root;
213 struct ec_parse *dup_root, *dup = NULL;
215 root = ec_parse_get_root(parse);
216 dup_root = __ec_parse_dup(root, parse, &dup);
217 if (dup_root == NULL)
224 void ec_parse_free_children(struct ec_parse *parse)
226 struct ec_parse *child;
231 while (!TAILQ_EMPTY(&parse->children)) {
232 child = TAILQ_FIRST(&parse->children);
233 TAILQ_REMOVE(&parse->children, child, next);
234 child->parent = NULL;
235 ec_parse_free(child);
239 void ec_parse_free(struct ec_parse *parse)
244 ec_assert_print(parse->parent == NULL,
245 "parent not NULL in ec_parse_free()");
247 ec_parse_free_children(parse);
248 ec_strvec_free(parse->strvec);
249 ec_dict_free(parse->attrs);
253 static void __ec_parse_dump(FILE *out,
254 const struct ec_parse *parse, size_t indent)
256 struct ec_parse *child;
257 const struct ec_strvec *vec;
258 const char *id = "none", *typename = "none";
260 /* node can be null when parsing is incomplete */
261 if (parse->node != NULL) {
262 id = parse->node->id;
263 typename = ec_node_type(parse->node)->name;
266 fprintf(out, "%*s" "type=%s id=%s vec=",
267 (int)indent * 4, "", typename, id);
268 vec = ec_parse_strvec(parse);
269 ec_strvec_dump(out, vec);
271 TAILQ_FOREACH(child, &parse->children, next)
272 __ec_parse_dump(out, child, indent + 1);
275 void ec_parse_dump(FILE *out, const struct ec_parse *parse)
277 fprintf(out, "------------------- parse dump:\n");
280 fprintf(out, "parse is NULL\n");
284 /* only exist if it does not match (strvec == NULL) and if it
285 * does not have children: an incomplete parse, like those
286 * generated by complete() don't match but have children that
288 if (!ec_parse_matches(parse) && TAILQ_EMPTY(&parse->children)) {
289 fprintf(out, "no match\n");
293 __ec_parse_dump(out, parse, 0);
296 void ec_parse_link_child(struct ec_parse *parse,
297 struct ec_parse *child)
299 TAILQ_INSERT_TAIL(&parse->children, child, next);
300 child->parent = parse;
303 void ec_parse_unlink_child(struct ec_parse *parse,
304 struct ec_parse *child)
306 TAILQ_REMOVE(&parse->children, child, next);
307 child->parent = NULL;
311 ec_parse_get_first_child(const struct ec_parse *parse)
313 return TAILQ_FIRST(&parse->children);
317 ec_parse_get_last_child(const struct ec_parse *parse)
319 return TAILQ_LAST(&parse->children, ec_parse_list);
322 struct ec_parse *ec_parse_next(const struct ec_parse *parse)
324 return TAILQ_NEXT(parse, next);
327 bool ec_parse_has_child(const struct ec_parse *parse)
329 return !TAILQ_EMPTY(&parse->children);
332 const struct ec_node *ec_parse_get_node(const struct ec_parse *parse)
340 void ec_parse_del_last_child(struct ec_parse *parse)
342 struct ec_parse *child;
344 child = ec_parse_get_last_child(parse);
345 ec_parse_unlink_child(parse, child);
346 ec_parse_free(child);
349 struct ec_parse *__ec_parse_get_root(struct ec_parse *parse)
354 while (parse->parent != NULL)
355 parse = parse->parent;
360 struct ec_parse *ec_parse_get_parent(const struct ec_parse *parse)
365 return parse->parent;
368 struct ec_parse *__ec_parse_iter_next(const struct ec_parse *root,
369 struct ec_parse *parse, bool iter_children)
371 struct ec_parse *child, *parent, *next;
374 child = TAILQ_FIRST(&parse->children);
378 parent = parse->parent;
379 while (parent != NULL && parse != root) {
380 next = TAILQ_NEXT(parse, next);
384 parent = parse->parent;
390 ec_parse_find_next(struct ec_parse *root, struct ec_parse *start,
391 const char *id, bool iter_children)
393 struct ec_parse *iter;
400 start = EC_PARSE_ITER_NEXT(root, start, iter_children);
402 for (iter = start; iter != NULL;
403 iter = EC_PARSE_ITER_NEXT(root, iter, 1)) {
404 if (iter->node != NULL &&
405 iter->node->id != NULL &&
406 !strcmp(iter->node->id, id))
413 struct ec_parse *ec_parse_find(struct ec_parse *parse,
416 return ec_parse_find_next(parse, NULL, id, 1);
420 ec_parse_get_attrs(struct ec_parse *parse)
428 const struct ec_strvec *ec_parse_strvec(const struct ec_parse *parse)
430 if (parse == NULL || parse->strvec == NULL)
433 return parse->strvec;
436 /* number of strings in the parse vector */
437 size_t ec_parse_len(const struct ec_parse *parse)
439 if (parse == NULL || parse->strvec == NULL)
442 return ec_strvec_len(parse->strvec);
445 size_t ec_parse_matches(const struct ec_parse *parse)
450 if (parse->strvec == NULL)
456 /* LCOV_EXCL_START */
457 static int ec_parse_testcase(void)
459 struct ec_node *node = NULL;
460 struct ec_parse *p = NULL, *p2 = NULL;
461 const struct ec_parse *pc;
468 node = ec_node_sh_lex(EC_NO_ID,
469 EC_NODE_SEQ(EC_NO_ID,
470 ec_node_str("id_x", "x"),
471 ec_node_str("id_y", "y")));
475 p = ec_node_parse(node, "xcdscds");
476 testres |= EC_TEST_CHECK(
477 p != NULL && !ec_parse_matches(p),
478 "parse should not match\n");
480 f = open_memstream(&buf, &buflen);
487 testres |= EC_TEST_CHECK(
488 strstr(buf, "no match"), "bad dump\n");
493 p = ec_node_parse(node, "x y");
494 testres |= EC_TEST_CHECK(
495 p != NULL && ec_parse_matches(p),
496 "parse should match\n");
497 testres |= EC_TEST_CHECK(
498 ec_parse_len(p) == 1, "bad parse len\n");
500 ret = ec_dict_set(ec_parse_get_attrs(p), "key", "val", NULL);
501 testres |= EC_TEST_CHECK(ret == 0,
502 "cannot set parse attribute\n");
504 p2 = ec_parse_dup(p);
505 testres |= EC_TEST_CHECK(
506 p2 != NULL && ec_parse_matches(p2),
507 "parse should match\n");
511 pc = ec_parse_find(p, "id_x");
512 testres |= EC_TEST_CHECK(pc != NULL, "cannot find id_x");
513 testres |= EC_TEST_CHECK(pc != NULL &&
514 ec_parse_get_parent(pc) != NULL &&
515 ec_parse_get_parent(ec_parse_get_parent(pc)) == p,
518 pc = ec_parse_find(p, "id_y");
519 testres |= EC_TEST_CHECK(pc != NULL, "cannot find id_y");
520 pc = ec_parse_find(p, "id_dezdezdez");
521 testres |= EC_TEST_CHECK(pc == NULL, "should not find bad id");
524 f = open_memstream(&buf, &buflen);
531 testres |= EC_TEST_CHECK(
532 strstr(buf, "type=sh_lex id=no-id") &&
533 strstr(buf, "type=seq id=no-id") &&
534 strstr(buf, "type=str id=id_x") &&
535 strstr(buf, "type=str id=id_x"),
556 static struct ec_test ec_parse_test = {
558 .test = ec_parse_testcase,
561 EC_TEST_REGISTER(ec_parse_test);