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)) {
196 if (!ec_pnode_has_child(p)) {
208 static struct cond_result *
209 eval_root(const struct ec_pnode *pstate, struct cond_result **in, size_t in_len)
211 struct cond_result *out = NULL;
212 const struct ec_pnode *root = NULL;
217 EC_LOG(LOG_ERR, "root() does not take any argument\n");
222 out = ec_malloc(sizeof(*out));
227 out->htable = ec_htable();
228 if (out->htable == NULL)
231 root = ec_pnode_get_root(pstate);
232 if (ec_htable_set(out->htable, &root, sizeof(root), NULL, NULL) < 0)
235 cond_result_table_free(in, in_len);
239 cond_result_free(out);
240 cond_result_table_free(in, in_len);
244 static struct cond_result *
245 eval_current(const struct ec_pnode *pstate, struct cond_result **in,
248 struct cond_result *out = NULL;
253 EC_LOG(LOG_ERR, "current() does not take any argument\n");
258 out = ec_malloc(sizeof(*out));
263 out->htable = ec_htable();
264 if (out->htable == NULL)
267 if (ec_htable_set(out->htable, &pstate, sizeof(pstate), NULL, NULL) < 0)
270 cond_result_table_free(in, in_len);
274 cond_result_free(out);
275 cond_result_table_free(in, in_len);
280 boolean_value(const struct cond_result *res)
284 return (ec_htable_len(res->htable) > 0);
288 return (res->int64 != 0);
290 return (res->str[0] != 0);
296 static struct cond_result *
297 eval_bool(const struct ec_pnode *pstate, struct cond_result **in, size_t in_len)
299 struct cond_result *out = NULL;
304 EC_LOG(LOG_ERR, "bool() takes one argument.\n");
309 out = ec_malloc(sizeof(*out));
314 out->boolean = boolean_value(in[0]);
316 cond_result_table_free(in, in_len);
320 cond_result_free(out);
321 cond_result_table_free(in, in_len);
325 static struct cond_result *
326 eval_or(const struct ec_pnode *pstate, struct cond_result **in, size_t in_len)
328 struct cond_result *out = NULL;
334 EC_LOG(LOG_ERR, "or() takes at least two arguments\n");
339 /* return the first true element, or the last one */
340 for (i = 0; i < in_len; i++) {
341 if (boolean_value(in[i]))
350 cond_result_table_free(in, in_len);
354 cond_result_free(out);
355 cond_result_table_free(in, in_len);
359 static struct cond_result *
360 eval_and(const struct ec_pnode *pstate, struct cond_result **in, size_t in_len)
362 struct cond_result *out = NULL;
368 EC_LOG(LOG_ERR, "or() takes at least two arguments\n");
373 /* return the first false element, or the last one */
374 for (i = 0; i < in_len; i++) {
375 if (!boolean_value(in[i]))
384 cond_result_table_free(in, in_len);
388 cond_result_free(out);
389 cond_result_table_free(in, in_len);
393 static struct cond_result *
394 eval_first_child(const struct ec_pnode *pstate, struct cond_result **in,
397 struct cond_result *out = NULL;
398 struct ec_htable_elt_ref *iter;
399 const struct ec_pnode * const *pparse;
400 struct ec_pnode *parse;
404 if (in_len != 1 || in[0]->type != NODESET) {
405 EC_LOG(LOG_ERR, "first_child() takes one argument of type nodeset.\n");
410 out = ec_malloc(sizeof(*out));
415 out->htable = ec_htable();
416 if (out->htable == NULL)
419 for (iter = ec_htable_iter(in[0]->htable);
420 iter != NULL; iter = ec_htable_iter_next(iter)) {
421 pparse = ec_htable_iter_get_key(iter);
422 parse = ec_pnode_get_first_child(*pparse);
425 if (ec_htable_set(out->htable, &parse, sizeof(parse), NULL,
430 cond_result_table_free(in, in_len);
434 cond_result_free(out);
435 cond_result_table_free(in, in_len);
439 static struct cond_result *
440 eval_find(const struct ec_pnode *pstate, struct cond_result **in,
443 struct cond_result *out = NULL;
444 struct ec_htable_elt_ref *iter;
445 struct ec_pnode * const *pparse;
446 struct ec_pnode *parse;
451 if (in_len != 2 || in[0]->type != NODESET || in[1]->type != STR) {
452 EC_LOG(LOG_ERR, "find() takes two arguments (nodeset, str).\n");
457 out = ec_malloc(sizeof(*out));
462 out->htable = ec_htable();
463 if (out->htable == NULL)
467 for (iter = ec_htable_iter(in[0]->htable);
468 iter != NULL; iter = ec_htable_iter_next(iter)) {
469 pparse = ec_htable_iter_get_key(iter);
470 parse = ec_pnode_find(*pparse, id);
471 while (parse != NULL) {
472 if (ec_htable_set(out->htable, &parse,
476 parse = ec_pnode_find_next(*pparse, parse, id, 1);
480 cond_result_table_free(in, in_len);
484 cond_result_free(out);
485 cond_result_table_free(in, in_len);
489 static struct cond_result *
490 eval_cmp(const struct ec_pnode *pstate, struct cond_result **in,
493 struct cond_result *out = NULL;
494 struct ec_htable_elt_ref *iter;
495 bool eq = false, gt = false;
499 if (in_len != 3 || in[0]->type != STR || in[1]->type != in[2]->type) {
500 EC_LOG(LOG_ERR, "cmp() takes 3 arguments (str, <type>, <type>).\n");
505 if (strcmp(in[0]->str, "eq") && strcmp(in[0]->str, "ne") &&
506 strcmp(in[0]->str, "gt") && strcmp(in[0]->str, "lt") &&
507 strcmp(in[0]->str, "ge") && strcmp(in[0]->str, "le")) {
508 EC_LOG(LOG_ERR, "invalid comparison operator in cmp().\n");
513 if (strcmp(in[0]->str, "eq") && strcmp(in[0]->str, "ne") &&
514 in[1]->type != INT) {
515 EC_LOG(LOG_ERR, "cmp(gt|lt|ge|le, ...) is only allowed with integers.\n");
520 if (in[1]->type == INT) {
521 eq = in[1]->int64 == in[2]->int64;
522 gt = in[1]->int64 > in[2]->int64;
523 } else if (in[1]->type == NODESET &&
524 ec_htable_len(in[1]->htable) !=
525 ec_htable_len(in[2]->htable)) {
527 } else if (in[1]->type == NODESET) {
529 for (iter = ec_htable_iter(in[1]->htable);
530 iter != NULL; iter = ec_htable_iter_next(iter)) {
533 ec_htable_iter_get_key(iter),
534 sizeof(struct ec_pnode *)) == NULL) {
539 } else if (in[1]->type == STR) {
540 eq = !strcmp(in[1]->str, in[2]->str);;
541 } else if (in[1]->type == BOOLEAN) {
542 eq = in[1]->boolean == in[2]->boolean;
545 out = ec_malloc(sizeof(*out));
550 if (!strcmp(in[0]->str, "eq"))
552 else if (!strcmp(in[0]->str, "ne"))
554 else if (!strcmp(in[0]->str, "lt"))
555 out->boolean = !gt && !eq;
556 else if (!strcmp(in[0]->str, "gt"))
557 out->boolean = gt && !eq;
558 else if (!strcmp(in[0]->str, "le"))
559 out->boolean = !gt || eq;
560 else if (!strcmp(in[0]->str, "ge"))
561 out->boolean = gt || eq;
563 cond_result_table_free(in, in_len);
567 cond_result_free(out);
568 cond_result_table_free(in, in_len);
572 static struct cond_result *
573 eval_count(const struct ec_pnode *pstate, struct cond_result **in, size_t in_len)
575 struct cond_result *out = NULL;
579 if (in_len != 1 || in[0]->type != NODESET) {
580 EC_LOG(LOG_ERR, "count() takes one argument of type nodeset.\n");
585 out = ec_malloc(sizeof(*out));
590 out->int64 = ec_htable_len(in[0]->htable);
592 cond_result_table_free(in, in_len);
596 cond_result_free(out);
597 cond_result_table_free(in, in_len);
601 static struct cond_result *
602 eval_func(const char *name, const struct ec_pnode *pstate,
603 struct cond_result **in, size_t in_len)
607 f = ec_dict_get(ec_node_cond_functions, name);
609 EC_LOG(LOG_ERR, "No such function <%s>\n",
612 cond_result_table_free(in, in_len);
616 return f(pstate, in, in_len);
620 static struct cond_result *
621 eval_condition(const struct ec_pnode *cond, const struct ec_pnode *pstate)
623 const struct ec_pnode *iter;
624 struct cond_result *res = NULL;
625 struct cond_result **args = NULL;
626 const struct ec_pnode *func = NULL, *func_name = NULL, *arg_list = NULL;
627 const struct ec_pnode *value = NULL;
632 func = ec_pnode_find((void *)cond, "id_function");
634 EC_PNODE_FOREACH_CHILD(iter, func) {
635 id = ec_node_id(ec_pnode_get_node(iter));
636 if (!strcmp(id, "id_function_name"))
638 if (!strcmp(id, "id_arg_list"))
642 iter = ec_pnode_find((void *)arg_list, "id_arg");
643 while (iter != NULL) {
644 args = ec_realloc(args, (n_arg + 1) * sizeof(*args));
645 args[n_arg] = eval_condition(iter, pstate);
646 if (args[n_arg] == NULL)
649 iter = ec_pnode_find_next((void *)arg_list,
650 (void *)iter, "id_arg", 0);
653 res = eval_func(ec_strvec_val(ec_pnode_strvec(func_name), 0),
654 pstate, args, n_arg);
659 value = ec_pnode_find((void *)cond, "id_value_str");
661 res = ec_malloc(sizeof(*res));
665 res->str = ec_strdup(ec_strvec_val(ec_pnode_strvec(value), 0));
666 if (res->str == NULL)
671 value = ec_pnode_find((void *)cond, "id_value_int");
673 res = ec_malloc(sizeof(*res));
677 if (ec_str_parse_llint(ec_strvec_val(ec_pnode_strvec(value), 0),
678 0, LLONG_MIN, LLONG_MAX,
685 cond_result_free(res);
686 cond_result_table_free(args, n_arg);
691 validate_condition(const struct ec_pnode *cond, const struct ec_pnode *pstate)
693 struct cond_result *res;
696 res = eval_condition(cond, pstate);
700 ret = boolean_value(res);
701 cond_result_free(res);
707 ec_node_cond_parse(const struct ec_node *node, struct ec_pnode *pstate,
708 const struct ec_strvec *strvec)
710 struct ec_node_cond *priv = ec_node_priv(node);
711 struct ec_pnode *child;
714 ret = ec_parse_child(priv->child, pstate, strvec);
718 valid = validate_condition(priv->parsed_cond, pstate);
723 child = ec_pnode_get_last_child(pstate);
724 ec_pnode_unlink_child(pstate, child);
725 ec_pnode_free(child);
726 return EC_PARSE_NOMATCH;
733 ec_node_cond_complete(const struct ec_node *node,
734 struct ec_comp *comp,
735 const struct ec_strvec *strvec)
737 struct ec_node_cond *priv = ec_node_priv(node);
739 // XXX eval condition
740 // XXX before or after completing ? configurable ?
742 return ec_complete_child(priv->child, comp, strvec);
745 static void ec_node_cond_free_priv(struct ec_node *node)
747 struct ec_node_cond *priv = ec_node_priv(node);
749 ec_free(priv->cond_str);
750 priv->cond_str = NULL;
751 ec_pnode_free(priv->parsed_cond);
752 priv->parsed_cond = NULL;
753 ec_node_free(priv->child);
756 static const struct ec_config_schema ec_node_cond_schema[] = {
760 .type = EC_CONFIG_TYPE_STRING,
764 .desc = "The child node.",
765 .type = EC_CONFIG_TYPE_NODE,
768 .type = EC_CONFIG_TYPE_NONE,
772 static int ec_node_cond_set_config(struct ec_node *node,
773 const struct ec_config *config)
775 struct ec_node_cond *priv = ec_node_priv(node);
776 const struct ec_config *cond = NULL;
777 struct ec_pnode *parsed_cond = NULL;
778 const struct ec_config *child;
779 char *cond_str = NULL;
781 cond = ec_config_dict_get(config, "expr");
787 cond_str = ec_strdup(cond->string);
788 if (cond_str == NULL)
791 child = ec_config_dict_get(config, "child");
795 /* parse expression to build the cmd child node */
796 parsed_cond = ec_node_cond_build(cond_str);
797 if (parsed_cond == NULL)
800 /* ok, store the config */
801 ec_pnode_free(priv->parsed_cond);
802 priv->parsed_cond = parsed_cond;
803 ec_free(priv->cond_str);
804 priv->cond_str = cond_str;
805 ec_node_free(priv->child);
806 priv->child = ec_node_clone(child->node);
811 ec_pnode_free(parsed_cond);
817 ec_node_cond_get_children_count(const struct ec_node *node)
819 struct ec_node_cond *priv = ec_node_priv(node);
821 if (priv->child == NULL)
827 ec_node_cond_get_child(const struct ec_node *node, size_t i,
828 struct ec_node **child, unsigned int *refs)
830 struct ec_node_cond *priv = ec_node_priv(node);
835 *child = priv->child;
840 static struct ec_node_type ec_node_cond_type = {
842 .schema = ec_node_cond_schema,
843 .set_config = ec_node_cond_set_config,
844 .parse = ec_node_cond_parse,
845 .complete = ec_node_cond_complete,
846 .size = sizeof(struct ec_node_cond),
847 .free_priv = ec_node_cond_free_priv,
848 .get_children_count = ec_node_cond_get_children_count,
849 .get_child = ec_node_cond_get_child,
852 EC_NODE_TYPE_REGISTER(ec_node_cond_type);
854 struct ec_node *ec_node_cond(const char *id, const char *cmd,
855 struct ec_node *child)
857 struct ec_config *config = NULL;
858 struct ec_node *node = NULL;
864 node = ec_node_from_type(&ec_node_cond_type, id);
868 config = ec_config_dict();
872 if (ec_config_dict_set(config, "expr", ec_config_string(cmd)) < 0)
875 if (ec_config_dict_set(config, "child", ec_config_node(child)) < 0) {
876 child = NULL; /* freed */
881 ret = ec_node_set_config(node, config);
882 config = NULL; /* freed */
891 ec_config_free(config);
896 static void ec_node_cond_exit_func(void)
898 ec_node_free(ec_node_cond_parser);
899 ec_node_cond_parser = NULL;
900 ec_dict_free(ec_node_cond_functions);
901 ec_node_cond_functions = NULL;
904 static int add_func(const char *name, cond_func_t *f)
906 return ec_dict_set(ec_node_cond_functions, name, f, NULL);
909 static int ec_node_cond_init_func(void)
911 ec_node_cond_parser = ec_node_cond_build_parser();
912 if (ec_node_cond_parser == NULL)
915 ec_node_cond_functions = ec_dict();
916 if (ec_node_cond_functions == NULL)
919 if (add_func("root", eval_root) < 0)
921 if (add_func("current", eval_current) < 0)
923 if (add_func("bool", eval_bool) < 0)
925 if (add_func("or", eval_or) < 0)
927 if (add_func("and", eval_and) < 0)
929 if (add_func("first_child", eval_first_child) < 0)
931 if (add_func("find", eval_find) < 0)
933 if (add_func("cmp", eval_cmp) < 0)
935 if (add_func("count", eval_count) < 0)
941 EC_LOG(EC_LOG_ERR, "Failed to initialize condition parser\n");
942 ec_node_cond_exit_func();
946 static struct ec_init ec_node_cond_init = {
947 .init = ec_node_cond_init_func,
948 .exit = ec_node_cond_exit_func,
952 EC_INIT_REGISTER(ec_node_cond_init);
954 /* LCOV_EXCL_START */
955 static int ec_node_cond_testcase(void)
957 struct ec_node *node;
961 node = EC_NODE_SEQ(EC_NO_ID,
962 EC_NODE_SUBSET(EC_NO_ID,
963 ec_node_str("id_node1", "node1"),
964 ec_node_str("id_node2", "node2"),
965 ec_node_str("id_node3", "node3"),
966 ec_node_str("id_node4", "node4")),
967 ec_node_cond(EC_NO_ID,
968 "or(find(root(), id_node1), "
969 " and(find(root(), id_node2),"
970 " find(root(), id_node3)))",
971 ec_node_str(EC_NO_ID, "ok")));
973 EC_LOG(EC_LOG_ERR, "cannot create node\n");
976 testres |= EC_TEST_CHECK_PARSE(node, 2, "node1", "ok");
977 testres |= EC_TEST_CHECK_PARSE(node, 3, "node2", "node3", "ok");
978 testres |= EC_TEST_CHECK_PARSE(node, 4, "node1", "node2", "node3", "ok");
979 testres |= EC_TEST_CHECK_PARSE(node, 3, "node2", "node1", "ok");
980 testres |= EC_TEST_CHECK_PARSE(node, -1, "node2", "node4", "ok");
981 testres |= EC_TEST_CHECK_PARSE(node, -1, "node2", "ok");
982 testres |= EC_TEST_CHECK_PARSE(node, -1, "node3", "ok");
983 testres |= EC_TEST_CHECK_PARSE(node, -1, "node4", "ok");
986 node = ec_node_cond(EC_NO_ID,
987 "cmp(le, count(find(root(), id_node)), 3)",
988 ec_node_many(EC_NO_ID,
989 ec_node_str("id_node", "foo"), 0, 0));
991 EC_LOG(EC_LOG_ERR, "cannot create node\n");
994 testres |= EC_TEST_CHECK_PARSE(node, 0);
995 testres |= EC_TEST_CHECK_PARSE(node, 1, "foo");
996 testres |= EC_TEST_CHECK_PARSE(node, 2, "foo", "foo");
997 testres |= EC_TEST_CHECK_PARSE(node, 3, "foo", "foo", "foo");
998 testres |= EC_TEST_CHECK_PARSE(node, -1, "foo", "foo", "foo", "foo");
1001 // XXX test completion
1005 /* LCOV_EXCL_STOP */
1007 static struct ec_test ec_node_cond_test = {
1008 .name = "node_cond",
1009 .test = ec_node_cond_testcase,
1012 EC_TEST_REGISTER(ec_node_cond_test);