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_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>
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_keyval *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 if (ec_node_type(node)->parse == NULL)
49 child = ec_parse(node);
53 ec_parse_link_child(state, child);
57 ret = ec_node_type(node)->parse(node, child, strvec);
58 if (ret < 0 || ret == EC_PARSE_NOMATCH)
61 match_strvec = ec_strvec_ndup(strvec, 0, ret);
62 if (match_strvec == NULL) {
67 child->strvec = match_strvec;
73 ec_parse_unlink_child(state, child);
79 int ec_node_parse_child(const struct ec_node *node, struct ec_parse *state,
80 const struct ec_strvec *strvec)
82 assert(state != NULL);
83 return __ec_node_parse_child(node, state, false, strvec);
86 struct ec_parse *ec_node_parse_strvec(const struct ec_node *node,
87 const struct ec_strvec *strvec)
89 struct ec_parse *parse = ec_parse(node);
95 ret = __ec_node_parse_child(node, parse, true, strvec);
104 struct ec_parse *ec_node_parse(const struct ec_node *node, const char *str)
106 struct ec_strvec *strvec = NULL;
107 struct ec_parse *parse = NULL;
110 strvec = ec_strvec();
114 if (ec_strvec_add(strvec, str) < 0)
117 parse = ec_node_parse_strvec(node, strvec);
121 ec_strvec_free(strvec);
125 ec_strvec_free(strvec);
126 ec_parse_free(parse);
130 struct ec_parse *ec_parse(const struct ec_node *node)
132 struct ec_parse *parse = NULL;
134 parse = ec_calloc(1, sizeof(*parse));
138 TAILQ_INIT(&parse->children);
141 parse->attrs = ec_keyval();
142 if (parse->attrs == NULL)
149 ec_keyval_free(parse->attrs);
155 static struct ec_parse *
156 __ec_parse_dup(const struct ec_parse *root, const struct ec_parse *ref,
157 struct ec_parse **new_ref)
159 struct ec_parse *dup = NULL;
160 struct ec_parse *child, *dup_child;
161 struct ec_keyval *attrs = NULL;
166 dup = ec_parse(root->node);
173 attrs = ec_keyval_dup(root->attrs);
176 ec_keyval_free(dup->attrs);
179 if (root->strvec != NULL) {
180 dup->strvec = ec_strvec_dup(root->strvec);
181 if (dup->strvec == NULL)
185 TAILQ_FOREACH(child, &root->children, next) {
186 dup_child = __ec_parse_dup(child, ref, new_ref);
187 if (dup_child == NULL)
189 ec_parse_link_child(dup, dup_child);
199 struct ec_parse *ec_parse_dup(const struct ec_parse *parse)
201 const struct ec_parse *root;
202 struct ec_parse *dup_root, *dup = NULL;
204 root = ec_parse_get_root(parse);
205 dup_root = __ec_parse_dup(root, parse, &dup);
206 if (dup_root == NULL)
213 void ec_parse_free_children(struct ec_parse *parse)
215 struct ec_parse *child;
220 while (!TAILQ_EMPTY(&parse->children)) {
221 child = TAILQ_FIRST(&parse->children);
222 TAILQ_REMOVE(&parse->children, child, next);
223 child->parent = NULL;
224 ec_parse_free(child);
228 void ec_parse_free(struct ec_parse *parse)
233 ec_assert_print(parse->parent == NULL,
234 "parent not NULL in ec_parse_free()");
236 ec_parse_free_children(parse);
237 ec_strvec_free(parse->strvec);
238 ec_keyval_free(parse->attrs);
242 static void __ec_parse_dump(FILE *out,
243 const struct ec_parse *parse, size_t indent)
245 struct ec_parse *child;
246 const struct ec_strvec *vec;
247 const char *id, *typename = "none";
249 /* node can be null when parsing is incomplete */
250 if (parse->node != NULL) {
251 id = parse->node->id;
252 typename = ec_node_type(parse->node)->name;
255 fprintf(out, "%*s" "type=%s id=%s vec=",
256 (int)indent * 4, "", typename, id);
257 vec = ec_parse_strvec(parse);
258 ec_strvec_dump(out, vec);
260 TAILQ_FOREACH(child, &parse->children, next)
261 __ec_parse_dump(out, child, indent + 1);
264 void ec_parse_dump(FILE *out, const struct ec_parse *parse)
266 fprintf(out, "------------------- parse dump:\n");
269 fprintf(out, "parse is NULL\n");
273 /* only exist if it does not match (strvec == NULL) and if it
274 * does not have children: an incomplete parse, like those
275 * generated by complete() don't match but have children that
277 if (!ec_parse_matches(parse) && TAILQ_EMPTY(&parse->children)) {
278 fprintf(out, "no match\n");
282 __ec_parse_dump(out, parse, 0);
285 void ec_parse_link_child(struct ec_parse *parse,
286 struct ec_parse *child)
288 TAILQ_INSERT_TAIL(&parse->children, child, next);
289 child->parent = parse;
292 void ec_parse_unlink_child(struct ec_parse *parse,
293 struct ec_parse *child)
295 TAILQ_REMOVE(&parse->children, child, next);
296 child->parent = NULL;
300 ec_parse_get_first_child(const struct ec_parse *parse)
302 return TAILQ_FIRST(&parse->children);
306 ec_parse_get_last_child(const struct ec_parse *parse)
308 return TAILQ_LAST(&parse->children, ec_parse_list);
311 struct ec_parse *ec_parse_get_next(const struct ec_parse *parse)
313 return TAILQ_NEXT(parse, next);
316 bool ec_parse_has_child(const struct ec_parse *parse)
318 return !TAILQ_EMPTY(&parse->children);
321 const struct ec_node *ec_parse_get_node(const struct ec_parse *parse)
326 void ec_parse_del_last_child(struct ec_parse *parse)
328 struct ec_parse *child;
330 child = ec_parse_get_last_child(parse);
331 ec_parse_unlink_child(parse, child);
332 ec_parse_free(child);
335 struct ec_parse *__ec_parse_get_root(struct ec_parse *parse)
340 while (parse->parent != NULL)
341 parse = parse->parent;
346 struct ec_parse *ec_parse_get_parent(const struct ec_parse *parse)
351 return parse->parent;
354 struct ec_parse *ec_parse_iter_next(struct ec_parse *parse)
356 struct ec_parse *child, *parent, *next;
358 child = TAILQ_FIRST(&parse->children);
361 parent = parse->parent;
362 while (parent != NULL) {
363 next = TAILQ_NEXT(parse, next);
367 parent = parse->parent;
372 struct ec_parse *ec_parse_find_first(struct ec_parse *parse,
375 struct ec_parse *iter;
380 for (iter = parse; iter != NULL; iter = ec_parse_iter_next(iter)) {
381 if (iter->node != NULL &&
382 iter->node->id != NULL &&
383 !strcmp(iter->node->id, id))
391 ec_parse_get_attrs(struct ec_parse *parse)
399 const struct ec_strvec *ec_parse_strvec(const struct ec_parse *parse)
401 if (parse == NULL || parse->strvec == NULL)
404 return parse->strvec;
407 /* number of strings in the parse vector */
408 size_t ec_parse_len(const struct ec_parse *parse)
410 if (parse == NULL || parse->strvec == NULL)
413 return ec_strvec_len(parse->strvec);
416 size_t ec_parse_matches(const struct ec_parse *parse)
421 if (parse->strvec == NULL)
427 /* LCOV_EXCL_START */
428 static int ec_parse_testcase(void)
430 struct ec_node *node = NULL;
431 struct ec_parse *p = NULL, *p2 = NULL;
432 const struct ec_parse *pc;
439 node = ec_node_sh_lex(EC_NO_ID,
440 EC_NODE_SEQ(EC_NO_ID,
441 ec_node_str("id_x", "x"),
442 ec_node_str("id_y", "y")));
446 p = ec_node_parse(node, "xcdscds");
447 testres |= EC_TEST_CHECK(
448 p != NULL && !ec_parse_matches(p),
449 "parse should not match\n");
451 f = open_memstream(&buf, &buflen);
458 testres |= EC_TEST_CHECK(
459 strstr(buf, "no match"), "bad dump\n");
464 p = ec_node_parse(node, "x y");
465 testres |= EC_TEST_CHECK(
466 p != NULL && ec_parse_matches(p),
467 "parse should match\n");
468 testres |= EC_TEST_CHECK(
469 ec_parse_len(p) == 1, "bad parse len\n");
471 ret = ec_keyval_set(ec_parse_get_attrs(p), "key", "val", NULL);
472 testres |= EC_TEST_CHECK(ret == 0,
473 "cannot set parse attribute\n");
475 p2 = ec_parse_dup(p);
476 testres |= EC_TEST_CHECK(
477 p2 != NULL && ec_parse_matches(p2),
478 "parse should match\n");
482 pc = ec_parse_find_first(p, "id_x");
483 testres |= EC_TEST_CHECK(pc != NULL, "cannot find id_x");
484 testres |= EC_TEST_CHECK(pc != NULL &&
485 ec_parse_get_parent(pc) != NULL &&
486 ec_parse_get_parent(ec_parse_get_parent(pc)) == p,
489 pc = ec_parse_find_first(p, "id_y");
490 testres |= EC_TEST_CHECK(pc != NULL, "cannot find id_y");
491 pc = ec_parse_find_first(p, "id_dezdezdez");
492 testres |= EC_TEST_CHECK(pc == NULL, "should not find bad id");
495 f = open_memstream(&buf, &buflen);
502 testres |= EC_TEST_CHECK(
503 strstr(buf, "type=sh_lex id=no-id") &&
504 strstr(buf, "type=seq id=no-id") &&
505 strstr(buf, "type=str id=id_x") &&
506 strstr(buf, "type=str id=id_x"),
527 static struct ec_test ec_parse_test = {
529 .test = ec_parse_testcase,
532 EC_TEST_REGISTER(ec_parse_test);