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 */
45 char *cond_str; /* the condition string. */
46 struct ec_pnode *parsed_cond; /* the parsed condition. */
47 struct ec_node *child; /* the child node. */
50 enum cond_result_type {
59 * - find by attrs? get_attr ?
64 enum cond_result_type type;
66 struct ec_htable *htable;
73 typedef struct cond_result *(cond_func_t)(
74 const struct ec_pnode *pstate,
75 struct cond_result **in, size_t in_len);
78 cond_result_free(struct cond_result *res)
85 ec_htable_free(res->htable);
99 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_str", "a_identifier")) < 0)
141 if (ec_node_or_add(expr,
142 ec_node_any("id_value_int", "a_int")) < 0)
145 /* prepend a lexer to the expression node */
146 lex = ec_node_re_lex(EC_NO_ID, ec_node_clone(expr));
153 ret = ec_node_re_lex_add(lex, "[_a-zA-Z][._a-zA-Z0-9]*", 1,
157 ret = ec_node_re_lex_add(lex, "[0-9]+", 1, "a_int");
160 ret = ec_node_re_lex_add(lex, "\\(", 1, "a_open");
163 ret = ec_node_re_lex_add(lex, "\\)", 1, "a_close");
166 ret = ec_node_re_lex_add(lex, ",", 1, NULL);
169 ret = ec_node_re_lex_add(lex, "[ ]", 0, NULL);
182 static struct ec_pnode *
183 ec_node_cond_build(const char *cond_str)
185 struct ec_pnode *p = NULL;
187 /* parse the condition expression */
188 p = ec_parse(ec_node_cond_parser, cond_str);
192 if (!ec_pnode_matches(p)) {
204 static struct cond_result *
205 eval_root(const struct ec_pnode *pstate, struct cond_result **in, size_t in_len)
207 struct cond_result *out = NULL;
208 const struct ec_pnode *root = NULL;
213 EC_LOG(LOG_ERR, "root() does not take any argument\n");
218 out = ec_malloc(sizeof(*out));
223 out->htable = ec_htable();
224 if (out->htable == NULL)
227 root = EC_PNODE_GET_ROOT(pstate);
228 if (ec_htable_set(out->htable, &root, sizeof(root), NULL, NULL) < 0)
231 cond_result_table_free(in, in_len);
235 cond_result_free(out);
236 cond_result_table_free(in, in_len);
240 static struct cond_result *
241 eval_current(const struct ec_pnode *pstate, struct cond_result **in,
244 struct cond_result *out = NULL;
249 EC_LOG(LOG_ERR, "current() does not take any argument\n");
254 out = ec_malloc(sizeof(*out));
259 out->htable = ec_htable();
260 if (out->htable == NULL)
263 if (ec_htable_set(out->htable, &pstate, sizeof(pstate), NULL, NULL) < 0)
266 cond_result_table_free(in, in_len);
270 cond_result_free(out);
271 cond_result_table_free(in, in_len);
276 boolean_value(const struct cond_result *res)
280 return (ec_htable_len(res->htable) > 0);
284 return (res->int64 != 0);
286 return (res->str[0] != 0);
292 static struct cond_result *
293 eval_bool(const struct ec_pnode *pstate, struct cond_result **in, size_t in_len)
295 struct cond_result *out = NULL;
300 EC_LOG(LOG_ERR, "bool() takes one argument.\n");
305 out = ec_malloc(sizeof(*out));
310 out->boolean = boolean_value(in[0]);
312 cond_result_table_free(in, in_len);
316 cond_result_free(out);
317 cond_result_table_free(in, in_len);
321 static struct cond_result *
322 eval_or(const struct ec_pnode *pstate, struct cond_result **in, size_t in_len)
324 struct cond_result *out = NULL;
330 EC_LOG(LOG_ERR, "or() takes at least two arguments\n");
335 /* return the first true element, or the last one */
336 for (i = 0; i < in_len; i++) {
337 if (boolean_value(in[i]))
346 cond_result_table_free(in, in_len);
350 cond_result_free(out);
351 cond_result_table_free(in, in_len);
355 static struct cond_result *
356 eval_and(const struct ec_pnode *pstate, struct cond_result **in, size_t in_len)
358 struct cond_result *out = NULL;
364 EC_LOG(LOG_ERR, "or() takes at least two arguments\n");
369 /* return the first false element, or the last one */
370 for (i = 0; i < in_len; i++) {
371 if (!boolean_value(in[i]))
380 cond_result_table_free(in, in_len);
384 cond_result_free(out);
385 cond_result_table_free(in, in_len);
389 static struct cond_result *
390 eval_first_child(const struct ec_pnode *pstate, struct cond_result **in,
393 struct cond_result *out = NULL;
394 struct ec_htable_elt_ref *iter;
395 const struct ec_pnode * const *pparse;
396 struct ec_pnode *parse;
400 if (in_len != 1 || in[0]->type != NODESET) {
401 EC_LOG(LOG_ERR, "first_child() takes one argument of type nodeset.\n");
406 out = ec_malloc(sizeof(*out));
411 out->htable = ec_htable();
412 if (out->htable == NULL)
415 for (iter = ec_htable_iter(in[0]->htable);
416 iter != NULL; iter = ec_htable_iter_next(iter)) {
417 pparse = ec_htable_iter_get_key(iter);
418 parse = ec_pnode_get_first_child(*pparse);
421 if (ec_htable_set(out->htable, &parse, sizeof(parse), NULL,
426 cond_result_table_free(in, in_len);
430 cond_result_free(out);
431 cond_result_table_free(in, in_len);
435 static struct cond_result *
436 eval_find(const struct ec_pnode *pstate, struct cond_result **in,
439 struct cond_result *out = NULL;
440 struct ec_htable_elt_ref *iter;
441 struct ec_pnode * const *pparse;
442 struct ec_pnode *parse;
447 if (in_len != 2 || in[0]->type != NODESET || in[1]->type != STR) {
448 EC_LOG(LOG_ERR, "find() takes two arguments (nodeset, str).\n");
453 out = ec_malloc(sizeof(*out));
458 out->htable = ec_htable();
459 if (out->htable == NULL)
463 for (iter = ec_htable_iter(in[0]->htable);
464 iter != NULL; iter = ec_htable_iter_next(iter)) {
465 pparse = ec_htable_iter_get_key(iter);
466 parse = ec_pnode_find(*pparse, id);
467 while (parse != NULL) {
468 if (ec_htable_set(out->htable, &parse,
472 parse = ec_pnode_find_next(*pparse, parse, id, 1);
476 cond_result_table_free(in, in_len);
480 cond_result_free(out);
481 cond_result_table_free(in, in_len);
485 static struct cond_result *
486 eval_cmp(const struct ec_pnode *pstate, struct cond_result **in,
489 struct cond_result *out = NULL;
490 struct ec_htable_elt_ref *iter;
491 bool eq = false, gt = false;
495 if (in_len != 3 || in[0]->type != STR || in[1]->type != in[2]->type) {
496 EC_LOG(LOG_ERR, "cmp() takes 3 arguments (str, <type>, <type>).\n");
501 if (strcmp(in[0]->str, "eq") && strcmp(in[0]->str, "ne") &&
502 strcmp(in[0]->str, "gt") && strcmp(in[0]->str, "lt") &&
503 strcmp(in[0]->str, "ge") && strcmp(in[0]->str, "le")) {
504 EC_LOG(LOG_ERR, "invalid comparison operator in cmp().\n");
509 if (strcmp(in[0]->str, "eq") && strcmp(in[0]->str, "ne") &&
510 in[1]->type != INT) {
511 EC_LOG(LOG_ERR, "cmp(gt|lt|ge|le, ...) is only allowed with integers.\n");
516 if (in[1]->type == INT) {
517 eq = in[1]->int64 == in[2]->int64;
518 gt = in[1]->int64 > in[2]->int64;
519 } else if (in[1]->type == NODESET &&
520 ec_htable_len(in[1]->htable) !=
521 ec_htable_len(in[2]->htable)) {
523 } else if (in[1]->type == NODESET) {
525 for (iter = ec_htable_iter(in[1]->htable);
526 iter != NULL; iter = ec_htable_iter_next(iter)) {
529 ec_htable_iter_get_key(iter),
530 sizeof(struct ec_pnode *)) == NULL) {
535 } else if (in[1]->type == STR) {
536 eq = !strcmp(in[1]->str, in[2]->str);;
537 } else if (in[1]->type == BOOLEAN) {
538 eq = in[1]->boolean == in[2]->boolean;
541 out = ec_malloc(sizeof(*out));
546 if (!strcmp(in[0]->str, "eq"))
548 else if (!strcmp(in[0]->str, "ne"))
550 else if (!strcmp(in[0]->str, "lt"))
551 out->boolean = !gt && !eq;
552 else if (!strcmp(in[0]->str, "gt"))
553 out->boolean = gt && !eq;
554 else if (!strcmp(in[0]->str, "le"))
555 out->boolean = !gt || eq;
556 else if (!strcmp(in[0]->str, "ge"))
557 out->boolean = gt || eq;
559 cond_result_table_free(in, in_len);
563 cond_result_free(out);
564 cond_result_table_free(in, in_len);
568 static struct cond_result *
569 eval_count(const struct ec_pnode *pstate, struct cond_result **in, size_t in_len)
571 struct cond_result *out = NULL;
575 if (in_len != 1 || in[0]->type != NODESET) {
576 EC_LOG(LOG_ERR, "count() takes one argument of type nodeset.\n");
581 out = ec_malloc(sizeof(*out));
586 out->int64 = ec_htable_len(in[0]->htable);
588 cond_result_table_free(in, in_len);
592 cond_result_free(out);
593 cond_result_table_free(in, in_len);
597 static struct cond_result *
598 eval_func(const char *name, const struct ec_pnode *pstate,
599 struct cond_result **in, size_t in_len)
603 f = ec_dict_get(ec_node_cond_functions, name);
605 EC_LOG(LOG_ERR, "No such function <%s>\n",
608 cond_result_table_free(in, in_len);
612 return f(pstate, in, in_len);
616 static struct cond_result *
617 eval_condition(const struct ec_pnode *cond, const struct ec_pnode *pstate)
619 const struct ec_pnode *iter;
620 struct cond_result *res = NULL;
621 struct cond_result **args = NULL;
622 const struct ec_pnode *func = NULL, *func_name = NULL, *arg_list = NULL;
623 const struct ec_pnode *value = NULL;
628 func = ec_pnode_find((void *)cond, "id_function");
630 EC_PNODE_FOREACH_CHILD(iter, func) {
631 id = ec_node_id(ec_pnode_get_node(iter));
632 if (!strcmp(id, "id_function_name"))
634 if (!strcmp(id, "id_arg_list"))
638 iter = ec_pnode_find((void *)arg_list, "id_arg");
639 while (iter != NULL) {
640 args = ec_realloc(args, (n_arg + 1) * sizeof(*args));
641 args[n_arg] = eval_condition(iter, pstate);
642 if (args[n_arg] == NULL)
645 iter = ec_pnode_find_next((void *)arg_list,
646 (void *)iter, "id_arg", 0);
649 res = eval_func(ec_strvec_val(ec_pnode_get_strvec(func_name), 0),
650 pstate, args, n_arg);
655 value = ec_pnode_find((void *)cond, "id_value_str");
657 res = ec_malloc(sizeof(*res));
661 res->str = ec_strdup(ec_strvec_val(ec_pnode_get_strvec(value), 0));
662 if (res->str == NULL)
667 value = ec_pnode_find((void *)cond, "id_value_int");
669 res = ec_malloc(sizeof(*res));
673 if (ec_str_parse_llint(ec_strvec_val(ec_pnode_get_strvec(value), 0),
674 0, LLONG_MIN, LLONG_MAX,
681 cond_result_free(res);
682 cond_result_table_free(args, n_arg);
687 validate_condition(const struct ec_pnode *cond, const struct ec_pnode *pstate)
689 struct cond_result *res;
692 res = eval_condition(cond, pstate);
696 ret = boolean_value(res);
697 cond_result_free(res);
703 ec_node_cond_parse(const struct ec_node *node, struct ec_pnode *pstate,
704 const struct ec_strvec *strvec)
706 struct ec_node_cond *priv = ec_node_priv(node);
707 struct ec_pnode *child;
710 ret = ec_parse_child(priv->child, pstate, strvec);
714 valid = validate_condition(priv->parsed_cond, pstate);
719 child = ec_pnode_get_last_child(pstate);
720 ec_pnode_unlink_child(child);
721 ec_pnode_free(child);
722 return EC_PARSE_NOMATCH;
729 ec_node_cond_complete(const struct ec_node *node,
730 struct ec_comp *comp,
731 const struct ec_strvec *strvec)
733 struct ec_node_cond *priv = ec_node_priv(node);
735 // XXX eval condition
736 // XXX before or after completing ? configurable ?
738 return ec_complete_child(priv->child, comp, strvec);
741 static void ec_node_cond_free_priv(struct ec_node *node)
743 struct ec_node_cond *priv = ec_node_priv(node);
745 ec_free(priv->cond_str);
746 priv->cond_str = NULL;
747 ec_pnode_free(priv->parsed_cond);
748 priv->parsed_cond = NULL;
749 ec_node_free(priv->child);
752 static const struct ec_config_schema ec_node_cond_schema[] = {
756 .type = EC_CONFIG_TYPE_STRING,
760 .desc = "The child node.",
761 .type = EC_CONFIG_TYPE_NODE,
764 .type = EC_CONFIG_TYPE_NONE,
768 static int ec_node_cond_set_config(struct ec_node *node,
769 const struct ec_config *config)
771 struct ec_node_cond *priv = ec_node_priv(node);
772 const struct ec_config *cond = NULL;
773 struct ec_pnode *parsed_cond = NULL;
774 const struct ec_config *child;
775 char *cond_str = NULL;
777 cond = ec_config_dict_get(config, "expr");
783 cond_str = ec_strdup(cond->string);
784 if (cond_str == NULL)
787 child = ec_config_dict_get(config, "child");
791 /* parse expression to build the cmd child node */
792 parsed_cond = ec_node_cond_build(cond_str);
793 if (parsed_cond == NULL)
796 /* ok, store the config */
797 ec_pnode_free(priv->parsed_cond);
798 priv->parsed_cond = parsed_cond;
799 ec_free(priv->cond_str);
800 priv->cond_str = cond_str;
801 ec_node_free(priv->child);
802 priv->child = ec_node_clone(child->node);
807 ec_pnode_free(parsed_cond);
813 ec_node_cond_get_children_count(const struct ec_node *node)
815 struct ec_node_cond *priv = ec_node_priv(node);
817 if (priv->child == NULL)
823 ec_node_cond_get_child(const struct ec_node *node, size_t i,
824 struct ec_node **child, unsigned int *refs)
826 struct ec_node_cond *priv = ec_node_priv(node);
831 *child = priv->child;
836 static struct ec_node_type ec_node_cond_type = {
838 .schema = ec_node_cond_schema,
839 .set_config = ec_node_cond_set_config,
840 .parse = ec_node_cond_parse,
841 .complete = ec_node_cond_complete,
842 .size = sizeof(struct ec_node_cond),
843 .free_priv = ec_node_cond_free_priv,
844 .get_children_count = ec_node_cond_get_children_count,
845 .get_child = ec_node_cond_get_child,
848 EC_NODE_TYPE_REGISTER(ec_node_cond_type);
850 struct ec_node *ec_node_cond(const char *id, const char *cmd,
851 struct ec_node *child)
853 struct ec_config *config = NULL;
854 struct ec_node *node = NULL;
860 node = ec_node_from_type(&ec_node_cond_type, id);
864 config = ec_config_dict();
868 if (ec_config_dict_set(config, "expr", ec_config_string(cmd)) < 0)
871 if (ec_config_dict_set(config, "child", ec_config_node(child)) < 0) {
872 child = NULL; /* freed */
877 ret = ec_node_set_config(node, config);
878 config = NULL; /* freed */
887 ec_config_free(config);
892 static void ec_node_cond_exit_func(void)
894 ec_node_free(ec_node_cond_parser);
895 ec_node_cond_parser = NULL;
896 ec_dict_free(ec_node_cond_functions);
897 ec_node_cond_functions = NULL;
900 static int add_func(const char *name, cond_func_t *f)
902 return ec_dict_set(ec_node_cond_functions, name, f, NULL);
905 static int ec_node_cond_init_func(void)
907 ec_node_cond_parser = ec_node_cond_build_parser();
908 if (ec_node_cond_parser == NULL)
911 ec_node_cond_functions = ec_dict();
912 if (ec_node_cond_functions == NULL)
915 if (add_func("root", eval_root) < 0)
917 if (add_func("current", eval_current) < 0)
919 if (add_func("bool", eval_bool) < 0)
921 if (add_func("or", eval_or) < 0)
923 if (add_func("and", eval_and) < 0)
925 if (add_func("first_child", eval_first_child) < 0)
927 if (add_func("find", eval_find) < 0)
929 if (add_func("cmp", eval_cmp) < 0)
931 if (add_func("count", eval_count) < 0)
937 EC_LOG(EC_LOG_ERR, "Failed to initialize condition parser\n");
938 ec_node_cond_exit_func();
942 static struct ec_init ec_node_cond_init = {
943 .init = ec_node_cond_init_func,
944 .exit = ec_node_cond_exit_func,
948 EC_INIT_REGISTER(ec_node_cond_init);
950 /* LCOV_EXCL_START */
951 static int ec_node_cond_testcase(void)
953 struct ec_node *node;
957 node = EC_NODE_SEQ(EC_NO_ID,
958 EC_NODE_SUBSET(EC_NO_ID,
959 ec_node_str("id_node1", "node1"),
960 ec_node_str("id_node2", "node2"),
961 ec_node_str("id_node3", "node3"),
962 ec_node_str("id_node4", "node4")),
963 ec_node_cond(EC_NO_ID,
964 "or(find(root(), id_node1), "
965 " and(find(root(), id_node2),"
966 " find(root(), id_node3)))",
967 ec_node_str(EC_NO_ID, "ok")));
969 EC_LOG(EC_LOG_ERR, "cannot create node\n");
972 testres |= EC_TEST_CHECK_PARSE(node, 2, "node1", "ok");
973 testres |= EC_TEST_CHECK_PARSE(node, 3, "node2", "node3", "ok");
974 testres |= EC_TEST_CHECK_PARSE(node, 4, "node1", "node2", "node3", "ok");
975 testres |= EC_TEST_CHECK_PARSE(node, 3, "node2", "node1", "ok");
976 testres |= EC_TEST_CHECK_PARSE(node, -1, "node2", "node4", "ok");
977 testres |= EC_TEST_CHECK_PARSE(node, -1, "node2", "ok");
978 testres |= EC_TEST_CHECK_PARSE(node, -1, "node3", "ok");
979 testres |= EC_TEST_CHECK_PARSE(node, -1, "node4", "ok");
982 node = ec_node_cond(EC_NO_ID,
983 "cmp(le, count(find(root(), id_node)), 3)",
984 ec_node_many(EC_NO_ID,
985 ec_node_str("id_node", "foo"), 0, 0));
987 EC_LOG(EC_LOG_ERR, "cannot create node\n");
990 testres |= EC_TEST_CHECK_PARSE(node, 0);
991 testres |= EC_TEST_CHECK_PARSE(node, 1, "foo");
992 testres |= EC_TEST_CHECK_PARSE(node, 2, "foo", "foo");
993 testres |= EC_TEST_CHECK_PARSE(node, 3, "foo", "foo", "foo");
994 testres |= EC_TEST_CHECK_PARSE(node, -1, "foo", "foo", "foo", "foo");
997 // XXX test completion
1001 /* LCOV_EXCL_STOP */
1003 static struct ec_test ec_node_cond_test = {
1004 .name = "node_cond",
1005 .test = ec_node_cond_testcase,
1008 EC_TEST_REGISTER(ec_node_cond_test);