1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright 2019, Olivier MATZ <zer0@droids-corp.org>
14 #include <ecoli_init.h>
15 #include <ecoli_malloc.h>
16 #include <ecoli_log.h>
17 #include <ecoli_test.h>
18 #include <ecoli_strvec.h>
19 #include <ecoli_node.h>
20 #include <ecoli_config.h>
21 #include <ecoli_dict.h>
22 #include <ecoli_htable.h>
23 #include <ecoli_parse.h>
24 #include <ecoli_complete.h>
25 #include <ecoli_node_str.h>
26 #include <ecoli_node_re.h>
27 #include <ecoli_node_many.h>
28 #include <ecoli_node_subset.h>
29 #include <ecoli_node_seq.h>
30 #include <ecoli_node_or.h>
31 #include <ecoli_node_option.h>
32 #include <ecoli_node_any.h>
33 #include <ecoli_node_many.h>
34 #include <ecoli_node_bypass.h>
35 #include <ecoli_node_re_lex.h>
36 #include <ecoli_node_cond.h>
38 EC_LOG_TYPE_REGISTER(node_cond);
40 static struct ec_node *ec_node_cond_parser; /* the expression parser. */
41 static struct ec_dict *ec_node_cond_functions; /* functions dictionary */
45 char *cond_str; /* the condition string. */
46 struct ec_parse *parsed_cond; /* the parsed condition. */
47 struct ec_node *child; /* the child node. */
50 enum cond_result_type {
58 compare(eq|ne|gt|lt|ge|le, x, y)
62 find by attrs? get_attr ?
66 enum cond_result_type type;
68 struct ec_htable *htable;
75 typedef struct cond_result *(cond_func_t)(
76 const struct ec_parse *state,
77 struct cond_result **in, size_t in_len);
79 void cond_result_free(struct cond_result *res)
86 ec_htable_free(res->htable);
99 void cond_result_table_free(struct cond_result **table, size_t len)
103 for (i = 0; i < len; i++) {
104 cond_result_free(table[i]);
110 static struct ec_node *
111 ec_node_cond_build_parser(void)
113 struct ec_node *lex = NULL;
114 struct ec_node *expr = NULL;
117 expr = ec_node("or", "id_arg");
121 if (ec_node_or_add(expr,
122 EC_NODE_SEQ("id_function",
123 ec_node_any("id_function_name", "a_identifier"),
124 ec_node_any(EC_NO_ID, "a_open"),
125 ec_node_option("id_arg_list",
126 EC_NODE_SEQ(EC_NO_ID,
128 ec_node_many(EC_NO_ID,
129 EC_NODE_SEQ(EC_NO_ID,
130 ec_node_str(EC_NO_ID,
132 ec_node_clone(expr)),
134 ec_node_any(EC_NO_ID, "a_close"))) < 0)
137 if (ec_node_or_add(expr,
138 ec_node_any("id_value", "a_identifier")) < 0)
141 /* prepend a lexer to the expression node */
142 lex = ec_node_re_lex(EC_NO_ID, ec_node_clone(expr));
149 ret = ec_node_re_lex_add(lex, "[_a-zA-Z][._a-zA-Z0-9]*", 1,
153 ret = ec_node_re_lex_add(lex, "\\(", 1, "a_open");
156 ret = ec_node_re_lex_add(lex, "\\)", 1, "a_close");
159 ret = ec_node_re_lex_add(lex, ",", 1, NULL);
162 ret = ec_node_re_lex_add(lex, "[ ]", 0, NULL);
175 static struct ec_parse *
176 ec_node_cond_build(const char *cond_str)
178 struct ec_parse *p = NULL;
180 /* parse the condition expression */
181 p = ec_node_parse(ec_node_cond_parser, cond_str);
185 if (!ec_parse_matches(p)) {
189 if (!ec_parse_has_child(p)) {
201 static struct cond_result *
202 eval_root(const struct ec_parse *state, struct cond_result **in, size_t in_len)
204 struct cond_result *out = NULL;
205 const struct ec_parse *root = NULL;
210 EC_LOG(LOG_ERR, "root() does not take any argument\n");
215 out = ec_malloc(sizeof(*out));
220 out->htable = ec_htable();
221 if (out->htable == NULL)
224 root = ec_parse_get_root(state);
225 if (ec_htable_set(out->htable, &root, sizeof(root), NULL, NULL) < 0)
228 cond_result_table_free(in, in_len);
232 cond_result_free(out);
233 cond_result_table_free(in, in_len);
237 static struct cond_result *
238 eval_current(const struct ec_parse *state, struct cond_result **in,
241 struct cond_result *out = NULL;
246 EC_LOG(LOG_ERR, "current() does not take any argument\n");
251 out = ec_malloc(sizeof(*out));
256 out->htable = ec_htable();
257 if (out->htable == NULL)
260 if (ec_htable_set(out->htable, &state, sizeof(state), NULL, NULL) < 0)
263 cond_result_table_free(in, in_len);
267 cond_result_free(out);
268 cond_result_table_free(in, in_len);
273 boolean_value(const struct cond_result *res)
277 return (ec_htable_len(res->htable) > 0);
281 return (res->int64 != 0);
283 return (res->str[0] != 0);
289 static struct cond_result *
290 eval_bool(const struct ec_parse *state, struct cond_result **in, size_t in_len)
292 struct cond_result *out = NULL;
297 EC_LOG(LOG_ERR, "bool() takes one argument.\n");
302 out = ec_malloc(sizeof(*out));
307 out->boolean = boolean_value(in[0]);
309 cond_result_table_free(in, in_len);
313 cond_result_free(out);
314 cond_result_table_free(in, in_len);
318 static struct cond_result *
319 eval_or(const struct ec_parse *state, struct cond_result **in, size_t in_len)
321 struct cond_result *out = NULL;
327 EC_LOG(LOG_ERR, "or() takes at least two arguments\n");
332 /* return the first true element, or the last one */
333 for (i = 0; i < in_len; i++) {
334 if (boolean_value(in[i]))
343 cond_result_table_free(in, in_len);
347 cond_result_free(out);
348 cond_result_table_free(in, in_len);
352 static struct cond_result *
353 eval_and(const struct ec_parse *state, struct cond_result **in, size_t in_len)
355 struct cond_result *out = NULL;
361 EC_LOG(LOG_ERR, "or() takes at least two arguments\n");
366 /* return the first false element, or the last one */
367 for (i = 0; i < in_len; i++) {
368 if (!boolean_value(in[i]))
377 cond_result_table_free(in, in_len);
381 cond_result_free(out);
382 cond_result_table_free(in, in_len);
386 static struct cond_result *
387 eval_first_child(const struct ec_parse *state, struct cond_result **in,
390 struct cond_result *out = NULL;
391 struct ec_htable_elt_ref *iter;
392 const struct ec_parse * const *pparse;
393 struct ec_parse *parse;
397 if (in_len != 1 || in[0]->type != NODESET) {
398 EC_LOG(LOG_ERR, "first_child() takes one argument of type nodeset.\n");
403 out = ec_malloc(sizeof(*out));
408 out->htable = ec_htable();
409 if (out->htable == NULL)
412 for (iter = ec_htable_iter(in[0]->htable);
413 iter != NULL; iter = ec_htable_iter_next(iter)) {
414 pparse = ec_htable_iter_get_key(iter);
415 parse = ec_parse_get_first_child(*pparse);
418 if (ec_htable_set(out->htable, &parse, sizeof(parse), NULL,
423 cond_result_table_free(in, in_len);
427 cond_result_free(out);
428 cond_result_table_free(in, in_len);
432 static struct cond_result *
433 eval_find(const struct ec_parse *state, struct cond_result **in,
436 struct cond_result *out = NULL;
437 struct ec_htable_elt_ref *iter;
438 struct ec_parse * const *pparse;
439 struct ec_parse *parse;
444 if (in_len != 2 || in[0]->type != NODESET || in[1]->type != STR) {
445 EC_LOG(LOG_ERR, "find() takes two arguments (nodeset, str).\n");
450 out = ec_malloc(sizeof(*out));
455 out->htable = ec_htable();
456 if (out->htable == NULL)
460 for (iter = ec_htable_iter(in[0]->htable);
461 iter != NULL; iter = ec_htable_iter_next(iter)) {
462 pparse = ec_htable_iter_get_key(iter);
463 parse = ec_parse_find(*pparse, id);
464 while (parse != NULL) {
465 if (ec_htable_set(out->htable, &parse,
469 parse = ec_parse_find_next(*pparse, parse, id, 1);
473 cond_result_table_free(in, in_len);
477 cond_result_free(out);
478 cond_result_table_free(in, in_len);
482 static struct cond_result *
483 eval_func(const char *name, const struct ec_parse *state,
484 struct cond_result **in, size_t in_len)
488 f = ec_dict_get(ec_node_cond_functions, name);
490 EC_LOG(LOG_ERR, "No such function <%s>\n",
493 cond_result_table_free(in, in_len);
497 return f(state, in, in_len);
501 static struct cond_result *
502 eval_condition(const struct ec_parse *cond, const struct ec_parse *state)
504 const struct ec_parse *iter;
505 struct cond_result *res = NULL;
506 struct cond_result **args = NULL;
507 const struct ec_parse *func = NULL, *func_name = NULL, *arg_list = NULL;
508 const struct ec_parse *value = NULL;
513 func = ec_parse_find((void *)cond, "id_function");
514 value = ec_parse_find((void *)cond, "id_value");
516 EC_PARSE_FOREACH_CHILD(iter, func) {
517 id = ec_node_id(ec_parse_get_node(iter));
518 if (!strcmp(id, "id_function_name"))
520 if (!strcmp(id, "id_arg_list"))
524 iter = ec_parse_find((void *)arg_list, "id_arg");
525 while (iter != NULL) {
526 args = ec_realloc(args, (n_arg + 1) * sizeof(*args));
527 args[n_arg] = eval_condition(iter, state);
528 if (args[n_arg] == NULL)
531 iter = ec_parse_find_next((void *)arg_list,
532 (void *)iter, "id_arg", 0);
535 res = eval_func(ec_strvec_val(ec_parse_strvec(func_name), 0),
537 printf("%s(%p[%zd]) -> %p\n", ec_strvec_val(ec_parse_strvec(func_name), 0),
540 } else if (value != NULL) {
541 printf("%s\n", ec_strvec_val(ec_parse_strvec(value), 0));
542 res = ec_malloc(sizeof(*res));
546 res->str = ec_strdup(ec_strvec_val(ec_parse_strvec(value), 0));
547 if (res->str == NULL)
556 cond_result_free(res);
557 cond_result_table_free(args, n_arg);
562 validate_condition(const struct ec_parse *cond, const struct ec_parse *state)
564 struct cond_result *res;
567 res = eval_condition(cond, state);
571 ret = boolean_value(res);
572 cond_result_free(res);
578 ec_node_cond_parse(const struct ec_node *gen_node, struct ec_parse *state,
579 const struct ec_strvec *strvec)
581 struct ec_node_cond *node = (struct ec_node_cond *)gen_node;
584 ret = validate_condition(node->parsed_cond, state);
589 return EC_PARSE_NOMATCH;
591 return ec_node_parse_child(node->child, state, strvec);
595 ec_node_cond_complete(const struct ec_node *gen_node,
596 struct ec_comp *comp,
597 const struct ec_strvec *strvec)
599 struct ec_node_cond *node = (struct ec_node_cond *)gen_node;
601 // XXX eval condition
602 // XXX before or after completing ? configurable ?
604 return ec_node_complete_child(node->child, comp, strvec);
607 static void ec_node_cond_free_priv(struct ec_node *gen_node)
609 struct ec_node_cond *node = (struct ec_node_cond *)gen_node;
611 ec_free(node->cond_str);
612 node->cond_str = NULL;
613 ec_parse_free(node->parsed_cond);
614 node->parsed_cond = NULL;
615 ec_node_free(node->child);
618 static const struct ec_config_schema ec_node_cond_schema[] = {
622 .type = EC_CONFIG_TYPE_STRING,
626 .desc = "The child node.",
627 .type = EC_CONFIG_TYPE_NODE,
630 .type = EC_CONFIG_TYPE_NONE,
634 static int ec_node_cond_set_config(struct ec_node *gen_node,
635 const struct ec_config *config)
637 struct ec_node_cond *node = (struct ec_node_cond *)gen_node;
638 const struct ec_config *cond = NULL;
639 struct ec_parse *parsed_cond = NULL;
640 const struct ec_config *child;
641 char *cond_str = NULL;
643 cond = ec_config_dict_get(config, "expr");
649 cond_str = ec_strdup(cond->string);
650 if (cond_str == NULL)
653 child = ec_config_dict_get(config, "child");
657 /* parse expression to build the cmd child node */
658 parsed_cond = ec_node_cond_build(cond_str);
659 if (parsed_cond == NULL)
662 /* ok, store the config */
663 ec_parse_free(node->parsed_cond);
664 node->parsed_cond = parsed_cond;
665 ec_free(node->cond_str);
666 node->cond_str = cond_str;
667 ec_node_free(node->child);
668 node->child = ec_node_clone(child->node);
673 ec_parse_free(parsed_cond);
679 ec_node_cond_get_children_count(const struct ec_node *gen_node)
681 struct ec_node_cond *node = (struct ec_node_cond *)gen_node;
683 if (node->child == NULL)
689 ec_node_cond_get_child(const struct ec_node *gen_node, size_t i,
690 struct ec_node **child, unsigned int *refs)
692 struct ec_node_cond *node = (struct ec_node_cond *)gen_node;
697 *child = node->child;
702 static struct ec_node_type ec_node_cond_type = {
704 .schema = ec_node_cond_schema,
705 .set_config = ec_node_cond_set_config,
706 .parse = ec_node_cond_parse,
707 .complete = ec_node_cond_complete,
708 .size = sizeof(struct ec_node_cond),
709 .free_priv = ec_node_cond_free_priv,
710 .get_children_count = ec_node_cond_get_children_count,
711 .get_child = ec_node_cond_get_child,
714 EC_NODE_TYPE_REGISTER(ec_node_cond_type);
716 struct ec_node *ec_node_cond(const char *id, const char *cmd,
717 struct ec_node *child)
719 struct ec_config *config = NULL;
720 struct ec_node *gen_node = NULL;
726 gen_node = ec_node_from_type(&ec_node_cond_type, id);
727 if (gen_node == NULL)
730 config = ec_config_dict();
734 if (ec_config_dict_set(config, "expr", ec_config_string(cmd)) < 0)
737 if (ec_config_dict_set(config, "child", ec_config_node(child)) < 0) {
738 child = NULL; /* freed */
743 ret = ec_node_set_config(gen_node, config);
744 config = NULL; /* freed */
751 ec_node_free(gen_node);
753 ec_config_free(config);
758 static void ec_node_cond_exit_func(void)
760 ec_node_free(ec_node_cond_parser);
761 ec_node_cond_parser = NULL;
762 ec_dict_free(ec_node_cond_functions);
763 ec_node_cond_functions = NULL;
766 static int add_func(const char *name, cond_func_t *f)
768 return ec_dict_set(ec_node_cond_functions, name, f, NULL);
771 static int ec_node_cond_init_func(void)
773 ec_node_cond_parser = ec_node_cond_build_parser();
774 if (ec_node_cond_parser == NULL)
777 ec_node_cond_functions = ec_dict();
778 if (ec_node_cond_functions == NULL)
781 if (add_func("root", eval_root) < 0)
783 if (add_func("current", eval_current) < 0)
785 if (add_func("bool", eval_bool) < 0)
787 if (add_func("or", eval_or) < 0)
789 if (add_func("and", eval_and) < 0)
791 if (add_func("first_child", eval_first_child) < 0)
793 if (add_func("find", eval_find) < 0)
799 EC_LOG(EC_LOG_ERR, "Failed to initialize condition parser\n");
800 ec_node_cond_exit_func();
804 static struct ec_init ec_node_cond_init = {
805 .init = ec_node_cond_init_func,
806 .exit = ec_node_cond_exit_func,
810 EC_INIT_REGISTER(ec_node_cond_init);
812 /* LCOV_EXCL_START */
813 static int ec_node_cond_testcase(void)
815 struct ec_node *node;
818 node = EC_NODE_SEQ(EC_NO_ID,
819 EC_NODE_SUBSET(EC_NO_ID,
820 ec_node_str("id_node1", "node1"),
821 ec_node_str("id_node2", "node2"),
822 ec_node_str("id_node3", "node3"),
823 ec_node_str("id_node4", "node4")),
824 ec_node_cond(EC_NO_ID,
825 "or(find(root(), id_node1), "
826 " and(find(root(), id_node2),"
827 " find(root(), id_node3)))",
828 ec_node_str(EC_NO_ID, "ok")));
830 EC_LOG(EC_LOG_ERR, "cannot create node\n");
833 testres |= EC_TEST_CHECK_PARSE(node, 2, "node1", "ok");
834 testres |= EC_TEST_CHECK_PARSE(node, 3, "node2", "node3", "ok");
835 testres |= EC_TEST_CHECK_PARSE(node, 4, "node1", "node2", "node3", "ok");
836 testres |= EC_TEST_CHECK_PARSE(node, 3, "node2", "node1", "ok");
837 testres |= EC_TEST_CHECK_PARSE(node, -1, "node2", "node4", "ok");
838 testres |= EC_TEST_CHECK_PARSE(node, -1, "node2", "ok");
839 testres |= EC_TEST_CHECK_PARSE(node, -1, "node3", "ok");
840 testres |= EC_TEST_CHECK_PARSE(node, -1, "node4", "ok");
843 // XXX test completion
849 static struct ec_test ec_node_cond_test = {
851 .test = ec_node_cond_testcase,
854 EC_TEST_REGISTER(ec_node_cond_test);