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_string.h>
20 #include <ecoli_node.h>
21 #include <ecoli_config.h>
22 #include <ecoli_dict.h>
23 #include <ecoli_htable.h>
24 #include <ecoli_parse.h>
25 #include <ecoli_complete.h>
26 #include <ecoli_node_str.h>
27 #include <ecoli_node_re.h>
28 #include <ecoli_node_many.h>
29 #include <ecoli_node_subset.h>
30 #include <ecoli_node_seq.h>
31 #include <ecoli_node_or.h>
32 #include <ecoli_node_option.h>
33 #include <ecoli_node_any.h>
34 #include <ecoli_node_many.h>
35 #include <ecoli_node_bypass.h>
36 #include <ecoli_node_re_lex.h>
37 #include <ecoli_node_cond.h>
39 EC_LOG_TYPE_REGISTER(node_cond);
41 static struct ec_node *ec_node_cond_parser; /* the expression parser. */
42 static struct ec_dict *ec_node_cond_functions; /* functions dictionary */
46 char *cond_str; /* the condition string. */
47 struct ec_parse *parsed_cond; /* the parsed condition. */
48 struct ec_node *child; /* the child node. */
51 enum cond_result_type {
60 * - find by attrs? get_attr ?
65 enum cond_result_type type;
67 struct ec_htable *htable;
74 typedef struct cond_result *(cond_func_t)(
75 const struct ec_parse *state,
76 struct cond_result **in, size_t in_len);
78 void cond_result_free(struct cond_result *res)
85 ec_htable_free(res->htable);
98 void cond_result_table_free(struct cond_result **table, size_t len)
102 for (i = 0; i < len; i++) {
103 cond_result_free(table[i]);
109 static struct ec_node *
110 ec_node_cond_build_parser(void)
112 struct ec_node *lex = NULL;
113 struct ec_node *expr = NULL;
116 expr = ec_node("or", "id_arg");
120 if (ec_node_or_add(expr,
121 EC_NODE_SEQ("id_function",
122 ec_node_any("id_function_name", "a_identifier"),
123 ec_node_any(EC_NO_ID, "a_open"),
124 ec_node_option("id_arg_list",
125 EC_NODE_SEQ(EC_NO_ID,
127 ec_node_many(EC_NO_ID,
128 EC_NODE_SEQ(EC_NO_ID,
129 ec_node_str(EC_NO_ID,
131 ec_node_clone(expr)),
133 ec_node_any(EC_NO_ID, "a_close"))) < 0)
136 if (ec_node_or_add(expr,
137 ec_node_any("id_value_str", "a_identifier")) < 0)
140 if (ec_node_or_add(expr,
141 ec_node_any("id_value_int", "a_int")) < 0)
144 /* prepend a lexer to the expression node */
145 lex = ec_node_re_lex(EC_NO_ID, ec_node_clone(expr));
152 ret = ec_node_re_lex_add(lex, "[_a-zA-Z][._a-zA-Z0-9]*", 1,
156 ret = ec_node_re_lex_add(lex, "[0-9]+", 1, "a_int");
159 ret = ec_node_re_lex_add(lex, "\\(", 1, "a_open");
162 ret = ec_node_re_lex_add(lex, "\\)", 1, "a_close");
165 ret = ec_node_re_lex_add(lex, ",", 1, NULL);
168 ret = ec_node_re_lex_add(lex, "[ ]", 0, NULL);
181 static struct ec_parse *
182 ec_node_cond_build(const char *cond_str)
184 struct ec_parse *p = NULL;
186 /* parse the condition expression */
187 p = ec_node_parse(ec_node_cond_parser, cond_str);
191 if (!ec_parse_matches(p)) {
195 if (!ec_parse_has_child(p)) {
207 static struct cond_result *
208 eval_root(const struct ec_parse *state, struct cond_result **in, size_t in_len)
210 struct cond_result *out = NULL;
211 const struct ec_parse *root = NULL;
216 EC_LOG(LOG_ERR, "root() does not take any argument\n");
221 out = ec_malloc(sizeof(*out));
226 out->htable = ec_htable();
227 if (out->htable == NULL)
230 root = ec_parse_get_root(state);
231 if (ec_htable_set(out->htable, &root, sizeof(root), NULL, NULL) < 0)
234 cond_result_table_free(in, in_len);
238 cond_result_free(out);
239 cond_result_table_free(in, in_len);
243 static struct cond_result *
244 eval_current(const struct ec_parse *state, struct cond_result **in,
247 struct cond_result *out = NULL;
252 EC_LOG(LOG_ERR, "current() does not take any argument\n");
257 out = ec_malloc(sizeof(*out));
262 out->htable = ec_htable();
263 if (out->htable == NULL)
266 if (ec_htable_set(out->htable, &state, sizeof(state), NULL, NULL) < 0)
269 cond_result_table_free(in, in_len);
273 cond_result_free(out);
274 cond_result_table_free(in, in_len);
279 boolean_value(const struct cond_result *res)
283 return (ec_htable_len(res->htable) > 0);
287 return (res->int64 != 0);
289 return (res->str[0] != 0);
295 static struct cond_result *
296 eval_bool(const struct ec_parse *state, struct cond_result **in, size_t in_len)
298 struct cond_result *out = NULL;
303 EC_LOG(LOG_ERR, "bool() takes one argument.\n");
308 out = ec_malloc(sizeof(*out));
313 out->boolean = boolean_value(in[0]);
315 cond_result_table_free(in, in_len);
319 cond_result_free(out);
320 cond_result_table_free(in, in_len);
324 static struct cond_result *
325 eval_or(const struct ec_parse *state, struct cond_result **in, size_t in_len)
327 struct cond_result *out = NULL;
333 EC_LOG(LOG_ERR, "or() takes at least two arguments\n");
338 /* return the first true element, or the last one */
339 for (i = 0; i < in_len; i++) {
340 if (boolean_value(in[i]))
349 cond_result_table_free(in, in_len);
353 cond_result_free(out);
354 cond_result_table_free(in, in_len);
358 static struct cond_result *
359 eval_and(const struct ec_parse *state, struct cond_result **in, size_t in_len)
361 struct cond_result *out = NULL;
367 EC_LOG(LOG_ERR, "or() takes at least two arguments\n");
372 /* return the first false element, or the last one */
373 for (i = 0; i < in_len; i++) {
374 if (!boolean_value(in[i]))
383 cond_result_table_free(in, in_len);
387 cond_result_free(out);
388 cond_result_table_free(in, in_len);
392 static struct cond_result *
393 eval_first_child(const struct ec_parse *state, struct cond_result **in,
396 struct cond_result *out = NULL;
397 struct ec_htable_elt_ref *iter;
398 const struct ec_parse * const *pparse;
399 struct ec_parse *parse;
403 if (in_len != 1 || in[0]->type != NODESET) {
404 EC_LOG(LOG_ERR, "first_child() takes one argument of type nodeset.\n");
409 out = ec_malloc(sizeof(*out));
414 out->htable = ec_htable();
415 if (out->htable == NULL)
418 for (iter = ec_htable_iter(in[0]->htable);
419 iter != NULL; iter = ec_htable_iter_next(iter)) {
420 pparse = ec_htable_iter_get_key(iter);
421 parse = ec_parse_get_first_child(*pparse);
424 if (ec_htable_set(out->htable, &parse, sizeof(parse), NULL,
429 cond_result_table_free(in, in_len);
433 cond_result_free(out);
434 cond_result_table_free(in, in_len);
438 static struct cond_result *
439 eval_find(const struct ec_parse *state, struct cond_result **in,
442 struct cond_result *out = NULL;
443 struct ec_htable_elt_ref *iter;
444 struct ec_parse * const *pparse;
445 struct ec_parse *parse;
450 if (in_len != 2 || in[0]->type != NODESET || in[1]->type != STR) {
451 EC_LOG(LOG_ERR, "find() takes two arguments (nodeset, str).\n");
456 out = ec_malloc(sizeof(*out));
461 out->htable = ec_htable();
462 if (out->htable == NULL)
466 for (iter = ec_htable_iter(in[0]->htable);
467 iter != NULL; iter = ec_htable_iter_next(iter)) {
468 pparse = ec_htable_iter_get_key(iter);
469 parse = ec_parse_find(*pparse, id);
470 while (parse != NULL) {
471 if (ec_htable_set(out->htable, &parse,
475 parse = ec_parse_find_next(*pparse, parse, id, 1);
479 cond_result_table_free(in, in_len);
483 cond_result_free(out);
484 cond_result_table_free(in, in_len);
488 static struct cond_result *
489 eval_cmp(const struct ec_parse *state, struct cond_result **in,
492 struct cond_result *out = NULL;
493 struct ec_htable_elt_ref *iter;
494 bool eq = false, gt = false;
498 if (in_len != 3 || in[0]->type != STR || in[1]->type != in[2]->type) {
499 EC_LOG(LOG_ERR, "cmp() takes 3 arguments (str, <type>, <type>).\n");
504 if (strcmp(in[0]->str, "eq") && strcmp(in[0]->str, "ne") &&
505 strcmp(in[0]->str, "gt") && strcmp(in[0]->str, "lt") &&
506 strcmp(in[0]->str, "ge") && strcmp(in[0]->str, "le")) {
507 EC_LOG(LOG_ERR, "invalid comparison operator in cmp().\n");
512 if (strcmp(in[0]->str, "eq") && strcmp(in[0]->str, "ne") &&
513 in[1]->type != INT) {
514 EC_LOG(LOG_ERR, "cmp(gt|lt|ge|le, ...) is only allowed with integers.\n");
519 if (in[1]->type == INT) {
520 eq = in[1]->int64 == in[2]->int64;
521 gt = in[1]->int64 > in[2]->int64;
522 } else if (in[1]->type == NODESET &&
523 ec_htable_len(in[1]->htable) !=
524 ec_htable_len(in[2]->htable)) {
526 } else if (in[1]->type == NODESET) {
528 for (iter = ec_htable_iter(in[1]->htable);
529 iter != NULL; iter = ec_htable_iter_next(iter)) {
532 ec_htable_iter_get_key(iter),
533 sizeof(struct ec_parse *)) == NULL) {
538 } else if (in[1]->type == STR) {
539 eq = !strcmp(in[1]->str, in[2]->str);;
540 } else if (in[1]->type == BOOLEAN) {
541 eq = in[1]->boolean == in[2]->boolean;
544 out = ec_malloc(sizeof(*out));
549 if (!strcmp(in[0]->str, "eq"))
551 else if (!strcmp(in[0]->str, "ne"))
553 else if (!strcmp(in[0]->str, "lt"))
554 out->boolean = !gt && !eq;
555 else if (!strcmp(in[0]->str, "gt"))
556 out->boolean = gt && !eq;
557 else if (!strcmp(in[0]->str, "le"))
558 out->boolean = !gt || eq;
559 else if (!strcmp(in[0]->str, "ge"))
560 out->boolean = gt || eq;
562 cond_result_table_free(in, in_len);
566 cond_result_free(out);
567 cond_result_table_free(in, in_len);
571 static struct cond_result *
572 eval_count(const struct ec_parse *state, struct cond_result **in, size_t in_len)
574 struct cond_result *out = NULL;
578 if (in_len != 1 || in[0]->type != NODESET) {
579 EC_LOG(LOG_ERR, "count() takes one argument of type nodeset.\n");
584 out = ec_malloc(sizeof(*out));
589 out->int64 = ec_htable_len(in[0]->htable);
591 cond_result_table_free(in, in_len);
595 cond_result_free(out);
596 cond_result_table_free(in, in_len);
600 static struct cond_result *
601 eval_func(const char *name, const struct ec_parse *state,
602 struct cond_result **in, size_t in_len)
606 f = ec_dict_get(ec_node_cond_functions, name);
608 EC_LOG(LOG_ERR, "No such function <%s>\n",
611 cond_result_table_free(in, in_len);
615 return f(state, in, in_len);
619 static struct cond_result *
620 eval_condition(const struct ec_parse *cond, const struct ec_parse *state)
622 const struct ec_parse *iter;
623 struct cond_result *res = NULL;
624 struct cond_result **args = NULL;
625 const struct ec_parse *func = NULL, *func_name = NULL, *arg_list = NULL;
626 const struct ec_parse *value = NULL;
631 func = ec_parse_find((void *)cond, "id_function");
633 EC_PARSE_FOREACH_CHILD(iter, func) {
634 id = ec_node_id(ec_parse_get_node(iter));
635 if (!strcmp(id, "id_function_name"))
637 if (!strcmp(id, "id_arg_list"))
641 iter = ec_parse_find((void *)arg_list, "id_arg");
642 while (iter != NULL) {
643 args = ec_realloc(args, (n_arg + 1) * sizeof(*args));
644 args[n_arg] = eval_condition(iter, state);
645 if (args[n_arg] == NULL)
648 iter = ec_parse_find_next((void *)arg_list,
649 (void *)iter, "id_arg", 0);
652 res = eval_func(ec_strvec_val(ec_parse_strvec(func_name), 0),
658 value = ec_parse_find((void *)cond, "id_value_str");
660 res = ec_malloc(sizeof(*res));
664 res->str = ec_strdup(ec_strvec_val(ec_parse_strvec(value), 0));
665 if (res->str == NULL)
670 value = ec_parse_find((void *)cond, "id_value_int");
672 res = ec_malloc(sizeof(*res));
676 if (ec_str_parse_llint(ec_strvec_val(ec_parse_strvec(value), 0),
677 0, LLONG_MIN, LLONG_MAX,
684 cond_result_free(res);
685 cond_result_table_free(args, n_arg);
690 validate_condition(const struct ec_parse *cond, const struct ec_parse *state)
692 struct cond_result *res;
695 res = eval_condition(cond, state);
699 ret = boolean_value(res);
700 cond_result_free(res);
706 ec_node_cond_parse(const struct ec_node *gen_node, struct ec_parse *state,
707 const struct ec_strvec *strvec)
709 struct ec_node_cond *node = (struct ec_node_cond *)gen_node;
710 struct ec_parse *child;
713 ret = ec_node_parse_child(node->child, state, strvec);
717 valid = validate_condition(node->parsed_cond, state);
722 child = ec_parse_get_last_child(state);
723 ec_parse_unlink_child(state, child);
724 ec_parse_free(child);
725 return EC_PARSE_NOMATCH;
732 ec_node_cond_complete(const struct ec_node *gen_node,
733 struct ec_comp *comp,
734 const struct ec_strvec *strvec)
736 struct ec_node_cond *node = (struct ec_node_cond *)gen_node;
738 // XXX eval condition
739 // XXX before or after completing ? configurable ?
741 return ec_node_complete_child(node->child, comp, strvec);
744 static void ec_node_cond_free_priv(struct ec_node *gen_node)
746 struct ec_node_cond *node = (struct ec_node_cond *)gen_node;
748 ec_free(node->cond_str);
749 node->cond_str = NULL;
750 ec_parse_free(node->parsed_cond);
751 node->parsed_cond = NULL;
752 ec_node_free(node->child);
755 static const struct ec_config_schema ec_node_cond_schema[] = {
759 .type = EC_CONFIG_TYPE_STRING,
763 .desc = "The child node.",
764 .type = EC_CONFIG_TYPE_NODE,
767 .type = EC_CONFIG_TYPE_NONE,
771 static int ec_node_cond_set_config(struct ec_node *gen_node,
772 const struct ec_config *config)
774 struct ec_node_cond *node = (struct ec_node_cond *)gen_node;
775 const struct ec_config *cond = NULL;
776 struct ec_parse *parsed_cond = NULL;
777 const struct ec_config *child;
778 char *cond_str = NULL;
780 cond = ec_config_dict_get(config, "expr");
786 cond_str = ec_strdup(cond->string);
787 if (cond_str == NULL)
790 child = ec_config_dict_get(config, "child");
794 /* parse expression to build the cmd child node */
795 parsed_cond = ec_node_cond_build(cond_str);
796 if (parsed_cond == NULL)
799 /* ok, store the config */
800 ec_parse_free(node->parsed_cond);
801 node->parsed_cond = parsed_cond;
802 ec_free(node->cond_str);
803 node->cond_str = cond_str;
804 ec_node_free(node->child);
805 node->child = ec_node_clone(child->node);
810 ec_parse_free(parsed_cond);
816 ec_node_cond_get_children_count(const struct ec_node *gen_node)
818 struct ec_node_cond *node = (struct ec_node_cond *)gen_node;
820 if (node->child == NULL)
826 ec_node_cond_get_child(const struct ec_node *gen_node, size_t i,
827 struct ec_node **child, unsigned int *refs)
829 struct ec_node_cond *node = (struct ec_node_cond *)gen_node;
834 *child = node->child;
839 static struct ec_node_type ec_node_cond_type = {
841 .schema = ec_node_cond_schema,
842 .set_config = ec_node_cond_set_config,
843 .parse = ec_node_cond_parse,
844 .complete = ec_node_cond_complete,
845 .size = sizeof(struct ec_node_cond),
846 .free_priv = ec_node_cond_free_priv,
847 .get_children_count = ec_node_cond_get_children_count,
848 .get_child = ec_node_cond_get_child,
851 EC_NODE_TYPE_REGISTER(ec_node_cond_type);
853 struct ec_node *ec_node_cond(const char *id, const char *cmd,
854 struct ec_node *child)
856 struct ec_config *config = NULL;
857 struct ec_node *gen_node = NULL;
863 gen_node = ec_node_from_type(&ec_node_cond_type, id);
864 if (gen_node == NULL)
867 config = ec_config_dict();
871 if (ec_config_dict_set(config, "expr", ec_config_string(cmd)) < 0)
874 if (ec_config_dict_set(config, "child", ec_config_node(child)) < 0) {
875 child = NULL; /* freed */
880 ret = ec_node_set_config(gen_node, config);
881 config = NULL; /* freed */
888 ec_node_free(gen_node);
890 ec_config_free(config);
895 static void ec_node_cond_exit_func(void)
897 ec_node_free(ec_node_cond_parser);
898 ec_node_cond_parser = NULL;
899 ec_dict_free(ec_node_cond_functions);
900 ec_node_cond_functions = NULL;
903 static int add_func(const char *name, cond_func_t *f)
905 return ec_dict_set(ec_node_cond_functions, name, f, NULL);
908 static int ec_node_cond_init_func(void)
910 ec_node_cond_parser = ec_node_cond_build_parser();
911 if (ec_node_cond_parser == NULL)
914 ec_node_cond_functions = ec_dict();
915 if (ec_node_cond_functions == NULL)
918 if (add_func("root", eval_root) < 0)
920 if (add_func("current", eval_current) < 0)
922 if (add_func("bool", eval_bool) < 0)
924 if (add_func("or", eval_or) < 0)
926 if (add_func("and", eval_and) < 0)
928 if (add_func("first_child", eval_first_child) < 0)
930 if (add_func("find", eval_find) < 0)
932 if (add_func("cmp", eval_cmp) < 0)
934 if (add_func("count", eval_count) < 0)
940 EC_LOG(EC_LOG_ERR, "Failed to initialize condition parser\n");
941 ec_node_cond_exit_func();
945 static struct ec_init ec_node_cond_init = {
946 .init = ec_node_cond_init_func,
947 .exit = ec_node_cond_exit_func,
951 EC_INIT_REGISTER(ec_node_cond_init);
953 /* LCOV_EXCL_START */
954 static int ec_node_cond_testcase(void)
956 struct ec_node *node;
960 node = EC_NODE_SEQ(EC_NO_ID,
961 EC_NODE_SUBSET(EC_NO_ID,
962 ec_node_str("id_node1", "node1"),
963 ec_node_str("id_node2", "node2"),
964 ec_node_str("id_node3", "node3"),
965 ec_node_str("id_node4", "node4")),
966 ec_node_cond(EC_NO_ID,
967 "or(find(root(), id_node1), "
968 " and(find(root(), id_node2),"
969 " find(root(), id_node3)))",
970 ec_node_str(EC_NO_ID, "ok")));
972 EC_LOG(EC_LOG_ERR, "cannot create node\n");
975 testres |= EC_TEST_CHECK_PARSE(node, 2, "node1", "ok");
976 testres |= EC_TEST_CHECK_PARSE(node, 3, "node2", "node3", "ok");
977 testres |= EC_TEST_CHECK_PARSE(node, 4, "node1", "node2", "node3", "ok");
978 testres |= EC_TEST_CHECK_PARSE(node, 3, "node2", "node1", "ok");
979 testres |= EC_TEST_CHECK_PARSE(node, -1, "node2", "node4", "ok");
980 testres |= EC_TEST_CHECK_PARSE(node, -1, "node2", "ok");
981 testres |= EC_TEST_CHECK_PARSE(node, -1, "node3", "ok");
982 testres |= EC_TEST_CHECK_PARSE(node, -1, "node4", "ok");
985 node = ec_node_cond(EC_NO_ID,
986 "cmp(le, count(find(root(), id_node)), 3)",
987 ec_node_many(EC_NO_ID,
988 ec_node_str("id_node", "foo"), 0, 0));
990 EC_LOG(EC_LOG_ERR, "cannot create node\n");
993 testres |= EC_TEST_CHECK_PARSE(node, 0);
994 testres |= EC_TEST_CHECK_PARSE(node, 1, "foo");
995 testres |= EC_TEST_CHECK_PARSE(node, 2, "foo", "foo");
996 testres |= EC_TEST_CHECK_PARSE(node, 3, "foo", "foo", "foo");
997 testres |= EC_TEST_CHECK_PARSE(node, -1, "foo", "foo", "foo", "foo");
1000 // XXX test completion
1004 /* LCOV_EXCL_STOP */
1006 static struct ec_test ec_node_cond_test = {
1007 .name = "node_cond",
1008 .test = ec_node_cond_testcase,
1011 EC_TEST_REGISTER(ec_node_cond_test);