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_pnode_list, ec_pnode);
29 TAILQ_ENTRY(ec_pnode) next;
30 struct ec_pnode_list children;
31 struct ec_pnode *parent;
32 const struct ec_node *node;
33 struct ec_strvec *strvec;
34 struct ec_dict *attrs;
37 static int __ec_parse_child(const struct ec_node *node,
38 struct ec_pnode *pstate,
39 bool is_root, const struct ec_strvec *strvec)
41 struct ec_strvec *match_strvec;
42 struct ec_pnode *child = NULL;
45 // XXX limit max number of recursions to avoid segfault
47 if (ec_node_type(node)->parse == NULL) {
53 child = ec_pnode(node);
57 ec_pnode_link_child(pstate, child);
61 ret = ec_node_type(node)->parse(node, child, strvec);
65 if (ret == EC_PARSE_NOMATCH) {
67 ec_pnode_unlink_child(child);
73 match_strvec = ec_strvec_ndup(strvec, 0, ret);
74 if (match_strvec == NULL)
77 child->strvec = match_strvec;
83 ec_pnode_unlink_child(child);
89 int ec_parse_child(const struct ec_node *node, struct ec_pnode *pstate,
90 const struct ec_strvec *strvec)
92 assert(pstate != NULL);
93 return __ec_parse_child(node, pstate, false, strvec);
96 struct ec_pnode *ec_parse_strvec(const struct ec_node *node,
97 const struct ec_strvec *strvec)
99 struct ec_pnode *pnode = ec_pnode(node);
105 ret = __ec_parse_child(node, pnode, true, strvec);
107 ec_pnode_free(pnode);
114 struct ec_pnode *ec_parse(const struct ec_node *node, const char *str)
116 struct ec_strvec *strvec = NULL;
117 struct ec_pnode *pnode = NULL;
120 strvec = ec_strvec();
124 if (ec_strvec_add(strvec, str) < 0)
127 pnode = ec_parse_strvec(node, strvec);
131 ec_strvec_free(strvec);
135 ec_strvec_free(strvec);
136 ec_pnode_free(pnode);
140 struct ec_pnode *ec_pnode(const struct ec_node *node)
142 struct ec_pnode *pnode = NULL;
144 pnode = ec_calloc(1, sizeof(*pnode));
148 TAILQ_INIT(&pnode->children);
151 pnode->attrs = ec_dict();
152 if (pnode->attrs == NULL)
159 ec_dict_free(pnode->attrs);
165 static struct ec_pnode *
166 __ec_pnode_dup(const struct ec_pnode *root, const struct ec_pnode *ref,
167 struct ec_pnode **new_ref)
169 struct ec_pnode *dup = NULL;
170 struct ec_pnode *child, *dup_child;
171 struct ec_dict *attrs = NULL;
176 dup = ec_pnode(root->node);
183 attrs = ec_dict_dup(root->attrs);
186 ec_dict_free(dup->attrs);
189 if (root->strvec != NULL) {
190 dup->strvec = ec_strvec_dup(root->strvec);
191 if (dup->strvec == NULL)
195 TAILQ_FOREACH(child, &root->children, next) {
196 dup_child = __ec_pnode_dup(child, ref, new_ref);
197 if (dup_child == NULL)
199 ec_pnode_link_child(dup, dup_child);
209 struct ec_pnode *ec_pnode_dup(const struct ec_pnode *pnode)
211 const struct ec_pnode *root;
212 struct ec_pnode *dup_root, *dup = NULL;
214 root = EC_PNODE_GET_ROOT(pnode);
215 dup_root = __ec_pnode_dup(root, pnode, &dup);
216 if (dup_root == NULL)
223 void ec_pnode_free_children(struct ec_pnode *pnode)
225 struct ec_pnode *child;
230 while (!TAILQ_EMPTY(&pnode->children)) {
231 child = TAILQ_FIRST(&pnode->children);
232 TAILQ_REMOVE(&pnode->children, child, next);
233 child->parent = NULL;
234 ec_pnode_free(child);
238 void ec_pnode_free(struct ec_pnode *pnode)
243 ec_assert_print(pnode->parent == NULL,
244 "parent not NULL in ec_pnode_free()");
246 ec_pnode_free_children(pnode);
247 ec_strvec_free(pnode->strvec);
248 ec_dict_free(pnode->attrs);
252 static void __ec_pnode_dump(FILE *out,
253 const struct ec_pnode *pnode, size_t indent)
255 struct ec_pnode *child;
256 const struct ec_strvec *vec;
257 const char *id = "none", *typename = "none";
259 /* node can be null when parsing is incomplete */
260 if (pnode->node != NULL) {
261 id = ec_node_id(pnode->node);
262 typename = ec_node_type(pnode->node)->name;
265 fprintf(out, "%*s" "type=%s id=%s vec=",
266 (int)indent * 4, "", typename, id);
267 vec = ec_pnode_get_strvec(pnode);
268 ec_strvec_dump(out, vec);
270 TAILQ_FOREACH(child, &pnode->children, next)
271 __ec_pnode_dump(out, child, indent + 1);
274 // XXX dump in other formats? yaml? json?
275 void ec_pnode_dump(FILE *out, const struct ec_pnode *pnode)
277 fprintf(out, "------------------- parse dump:\n");
280 fprintf(out, "pnode is NULL\n");
284 /* Do not dump if it does not match (strvec == NULL) and if it
285 * does not have children. Note that an incomplete parsing tree,
286 * like those generated by complete(), don't match but have
287 * children that may match, and we want to dump them. */
288 if (!ec_pnode_matches(pnode) && TAILQ_EMPTY(&pnode->children)) {
289 fprintf(out, "no match\n");
293 __ec_pnode_dump(out, pnode, 0);
296 void ec_pnode_link_child(struct ec_pnode *pnode,
297 struct ec_pnode *child)
299 TAILQ_INSERT_TAIL(&pnode->children, child, next);
300 child->parent = pnode;
303 void ec_pnode_unlink_child(struct ec_pnode *child)
305 struct ec_pnode *parent = child->parent;
307 if (parent != NULL) {
308 TAILQ_REMOVE(&parent->children, child, next);
309 child->parent = NULL;
314 ec_pnode_get_first_child(const struct ec_pnode *pnode)
316 return TAILQ_FIRST(&pnode->children);
320 ec_pnode_get_last_child(const struct ec_pnode *pnode)
322 return TAILQ_LAST(&pnode->children, ec_pnode_list);
325 struct ec_pnode *ec_pnode_next(const struct ec_pnode *pnode)
327 return TAILQ_NEXT(pnode, next);
330 const struct ec_node *ec_pnode_get_node(const struct ec_pnode *pnode)
338 void ec_pnode_del_last_child(struct ec_pnode *pnode)
340 struct ec_pnode *child;
342 child = ec_pnode_get_last_child(pnode);
344 ec_pnode_unlink_child(child);
345 ec_pnode_free(child);
349 struct ec_pnode *ec_pnode_get_root(struct ec_pnode *pnode)
354 while (pnode->parent != NULL)
355 pnode = pnode->parent;
360 struct ec_pnode *ec_pnode_get_parent(const struct ec_pnode *pnode)
365 return pnode->parent;
368 struct ec_pnode *__ec_pnode_iter_next(const struct ec_pnode *root,
369 struct ec_pnode *pnode, bool iter_children)
371 struct ec_pnode *child, *parent, *next;
374 child = TAILQ_FIRST(&pnode->children);
378 parent = pnode->parent;
379 while (parent != NULL && pnode != root) {
380 next = TAILQ_NEXT(pnode, next);
384 parent = pnode->parent;
390 ec_pnode_find_next(struct ec_pnode *root, struct ec_pnode *prev,
391 const char *id, bool iter_children)
393 struct ec_pnode *iter;
400 prev = EC_PNODE_ITER_NEXT(root, prev, iter_children);
402 for (iter = prev; iter != NULL;
403 iter = EC_PNODE_ITER_NEXT(root, iter, 1)) {
404 if (iter->node != NULL &&
405 !strcmp(ec_node_id(iter->node), id))
412 struct ec_pnode *ec_pnode_find(struct ec_pnode *root, const char *id)
414 return ec_pnode_find_next(root, NULL, id, 1);
418 ec_pnode_get_attrs(struct ec_pnode *pnode)
426 const struct ec_strvec *ec_pnode_get_strvec(const struct ec_pnode *pnode)
431 return pnode->strvec;
434 /* number of strings in the parsed string vector */
435 size_t ec_pnode_len(const struct ec_pnode *pnode)
437 if (pnode == NULL || pnode->strvec == NULL)
440 return ec_strvec_len(pnode->strvec);
443 size_t ec_pnode_matches(const struct ec_pnode *pnode)
448 if (pnode->strvec == NULL)
454 /* LCOV_EXCL_START */
455 static int ec_pnode_testcase(void)
457 struct ec_node *node = NULL;
458 struct ec_pnode *p = NULL, *p2 = NULL;
459 const struct ec_pnode *pc;
466 node = ec_node_sh_lex(EC_NO_ID,
467 EC_NODE_SEQ(EC_NO_ID,
468 ec_node_str("id_x", "x"),
469 ec_node_str("id_y", "y")));
473 p = ec_parse(node, "xcdscds");
474 testres |= EC_TEST_CHECK(
475 p != NULL && !ec_pnode_matches(p),
476 "parse should not match\n");
478 f = open_memstream(&buf, &buflen);
485 testres |= EC_TEST_CHECK(
486 strstr(buf, "no match"), "bad dump\n");
491 p = ec_parse(node, "x y");
492 testres |= EC_TEST_CHECK(
493 p != NULL && ec_pnode_matches(p),
494 "parse should match\n");
495 testres |= EC_TEST_CHECK(
496 ec_pnode_len(p) == 1, "bad parse len\n");
498 ret = ec_dict_set(ec_pnode_get_attrs(p), "key", "val", NULL);
499 testres |= EC_TEST_CHECK(ret == 0,
500 "cannot set parse attribute\n");
502 p2 = ec_pnode_dup(p);
503 testres |= EC_TEST_CHECK(
504 p2 != NULL && ec_pnode_matches(p2),
505 "parse should match\n");
509 pc = ec_pnode_find(p, "id_x");
510 testres |= EC_TEST_CHECK(pc != NULL, "cannot find id_x");
511 testres |= EC_TEST_CHECK(pc != NULL &&
512 ec_pnode_get_parent(pc) != NULL &&
513 ec_pnode_get_parent(ec_pnode_get_parent(pc)) == p,
516 pc = ec_pnode_find(p, "id_y");
517 testres |= EC_TEST_CHECK(pc != NULL, "cannot find id_y");
518 pc = ec_pnode_find(p, "id_dezdezdez");
519 testres |= EC_TEST_CHECK(pc == NULL, "should not find bad id");
522 f = open_memstream(&buf, &buflen);
529 testres |= EC_TEST_CHECK(
530 strstr(buf, "type=sh_lex id=no-id") &&
531 strstr(buf, "type=seq id=no-id") &&
532 strstr(buf, "type=str id=id_x") &&
533 strstr(buf, "type=str id=id_x"),
554 static struct ec_test ec_pnode_test = {
556 .test = ec_pnode_testcase,
559 EC_TEST_REGISTER(ec_pnode_test);