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_parse *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_parse *state,
75 struct cond_result **in, size_t in_len);
77 void cond_result_free(struct cond_result *res)
84 ec_htable_free(res->htable);
97 void cond_result_table_free(struct cond_result **table, size_t len)
101 for (i = 0; i < len; i++) {
102 cond_result_free(table[i]);
108 static struct ec_node *
109 ec_node_cond_build_parser(void)
111 struct ec_node *lex = NULL;
112 struct ec_node *expr = NULL;
115 expr = ec_node("or", "id_arg");
119 if (ec_node_or_add(expr,
120 EC_NODE_SEQ("id_function",
121 ec_node_any("id_function_name", "a_identifier"),
122 ec_node_any(EC_NO_ID, "a_open"),
123 ec_node_option("id_arg_list",
124 EC_NODE_SEQ(EC_NO_ID,
126 ec_node_many(EC_NO_ID,
127 EC_NODE_SEQ(EC_NO_ID,
128 ec_node_str(EC_NO_ID,
130 ec_node_clone(expr)),
132 ec_node_any(EC_NO_ID, "a_close"))) < 0)
135 if (ec_node_or_add(expr,
136 ec_node_any("id_value_str", "a_identifier")) < 0)
139 if (ec_node_or_add(expr,
140 ec_node_any("id_value_int", "a_int")) < 0)
143 /* prepend a lexer to the expression node */
144 lex = ec_node_re_lex(EC_NO_ID, ec_node_clone(expr));
151 ret = ec_node_re_lex_add(lex, "[_a-zA-Z][._a-zA-Z0-9]*", 1,
155 ret = ec_node_re_lex_add(lex, "[0-9]+", 1, "a_int");
158 ret = ec_node_re_lex_add(lex, "\\(", 1, "a_open");
161 ret = ec_node_re_lex_add(lex, "\\)", 1, "a_close");
164 ret = ec_node_re_lex_add(lex, ",", 1, NULL);
167 ret = ec_node_re_lex_add(lex, "[ ]", 0, NULL);
180 static struct ec_parse *
181 ec_node_cond_build(const char *cond_str)
183 struct ec_parse *p = NULL;
185 /* parse the condition expression */
186 p = ec_node_parse(ec_node_cond_parser, cond_str);
190 if (!ec_parse_matches(p)) {
194 if (!ec_parse_has_child(p)) {
206 static struct cond_result *
207 eval_root(const struct ec_parse *state, struct cond_result **in, size_t in_len)
209 struct cond_result *out = NULL;
210 const struct ec_parse *root = NULL;
215 EC_LOG(LOG_ERR, "root() does not take any argument\n");
220 out = ec_malloc(sizeof(*out));
225 out->htable = ec_htable();
226 if (out->htable == NULL)
229 root = ec_parse_get_root(state);
230 if (ec_htable_set(out->htable, &root, sizeof(root), NULL, NULL) < 0)
233 cond_result_table_free(in, in_len);
237 cond_result_free(out);
238 cond_result_table_free(in, in_len);
242 static struct cond_result *
243 eval_current(const struct ec_parse *state, struct cond_result **in,
246 struct cond_result *out = NULL;
251 EC_LOG(LOG_ERR, "current() does not take any argument\n");
256 out = ec_malloc(sizeof(*out));
261 out->htable = ec_htable();
262 if (out->htable == NULL)
265 if (ec_htable_set(out->htable, &state, sizeof(state), NULL, NULL) < 0)
268 cond_result_table_free(in, in_len);
272 cond_result_free(out);
273 cond_result_table_free(in, in_len);
278 boolean_value(const struct cond_result *res)
282 return (ec_htable_len(res->htable) > 0);
286 return (res->int64 != 0);
288 return (res->str[0] != 0);
294 static struct cond_result *
295 eval_bool(const struct ec_parse *state, struct cond_result **in, size_t in_len)
297 struct cond_result *out = NULL;
302 EC_LOG(LOG_ERR, "bool() takes one argument.\n");
307 out = ec_malloc(sizeof(*out));
312 out->boolean = boolean_value(in[0]);
314 cond_result_table_free(in, in_len);
318 cond_result_free(out);
319 cond_result_table_free(in, in_len);
323 static struct cond_result *
324 eval_or(const struct ec_parse *state, struct cond_result **in, size_t in_len)
326 struct cond_result *out = NULL;
332 EC_LOG(LOG_ERR, "or() takes at least two arguments\n");
337 /* return the first true element, or the last one */
338 for (i = 0; i < in_len; i++) {
339 if (boolean_value(in[i]))
348 cond_result_table_free(in, in_len);
352 cond_result_free(out);
353 cond_result_table_free(in, in_len);
357 static struct cond_result *
358 eval_and(const struct ec_parse *state, struct cond_result **in, size_t in_len)
360 struct cond_result *out = NULL;
366 EC_LOG(LOG_ERR, "or() takes at least two arguments\n");
371 /* return the first false element, or the last one */
372 for (i = 0; i < in_len; i++) {
373 if (!boolean_value(in[i]))
382 cond_result_table_free(in, in_len);
386 cond_result_free(out);
387 cond_result_table_free(in, in_len);
391 static struct cond_result *
392 eval_first_child(const struct ec_parse *state, struct cond_result **in,
395 struct cond_result *out = NULL;
396 struct ec_htable_elt_ref *iter;
397 const struct ec_parse * const *pparse;
398 struct ec_parse *parse;
402 if (in_len != 1 || in[0]->type != NODESET) {
403 EC_LOG(LOG_ERR, "first_child() takes one argument of type nodeset.\n");
408 out = ec_malloc(sizeof(*out));
413 out->htable = ec_htable();
414 if (out->htable == NULL)
417 for (iter = ec_htable_iter(in[0]->htable);
418 iter != NULL; iter = ec_htable_iter_next(iter)) {
419 pparse = ec_htable_iter_get_key(iter);
420 parse = ec_parse_get_first_child(*pparse);
423 if (ec_htable_set(out->htable, &parse, sizeof(parse), NULL,
428 cond_result_table_free(in, in_len);
432 cond_result_free(out);
433 cond_result_table_free(in, in_len);
437 static struct cond_result *
438 eval_find(const struct ec_parse *state, struct cond_result **in,
441 struct cond_result *out = NULL;
442 struct ec_htable_elt_ref *iter;
443 struct ec_parse * const *pparse;
444 struct ec_parse *parse;
449 if (in_len != 2 || in[0]->type != NODESET || in[1]->type != STR) {
450 EC_LOG(LOG_ERR, "find() takes two arguments (nodeset, str).\n");
455 out = ec_malloc(sizeof(*out));
460 out->htable = ec_htable();
461 if (out->htable == NULL)
465 for (iter = ec_htable_iter(in[0]->htable);
466 iter != NULL; iter = ec_htable_iter_next(iter)) {
467 pparse = ec_htable_iter_get_key(iter);
468 parse = ec_parse_find(*pparse, id);
469 while (parse != NULL) {
470 if (ec_htable_set(out->htable, &parse,
474 parse = ec_parse_find_next(*pparse, parse, id, 1);
478 cond_result_table_free(in, in_len);
482 cond_result_free(out);
483 cond_result_table_free(in, in_len);
487 static struct cond_result *
488 eval_cmp(const struct ec_parse *state, struct cond_result **in,
491 struct cond_result *out = NULL;
492 struct ec_htable_elt_ref *iter;
493 bool eq = false, gt = false;
497 if (in_len != 3 || in[0]->type != STR || in[1]->type != in[2]->type) {
498 EC_LOG(LOG_ERR, "cmp() takes 3 arguments (str, <type>, <type>).\n");
503 if (strcmp(in[0]->str, "eq") && strcmp(in[0]->str, "ne") &&
504 strcmp(in[0]->str, "gt") && strcmp(in[0]->str, "lt") &&
505 strcmp(in[0]->str, "ge") && strcmp(in[0]->str, "le")) {
506 EC_LOG(LOG_ERR, "invalid comparison operator in cmp().\n");
511 if (strcmp(in[0]->str, "eq") && strcmp(in[0]->str, "ne") &&
512 in[1]->type != INT) {
513 EC_LOG(LOG_ERR, "cmp(gt|lt|ge|le, ...) is only allowed with integers.\n");
518 if (in[1]->type == INT) {
519 eq = in[1]->int64 == in[2]->int64;
520 gt = in[1]->int64 > in[2]->int64;
521 } else if (in[1]->type == NODESET &&
522 ec_htable_len(in[1]->htable) !=
523 ec_htable_len(in[2]->htable)) {
525 } else if (in[1]->type == NODESET) {
527 for (iter = ec_htable_iter(in[1]->htable);
528 iter != NULL; iter = ec_htable_iter_next(iter)) {
531 ec_htable_iter_get_key(iter),
532 sizeof(struct ec_parse *)) == NULL) {
537 } else if (in[1]->type == STR) {
538 eq = !strcmp(in[1]->str, in[2]->str);;
539 } else if (in[1]->type == BOOLEAN) {
540 eq = in[1]->boolean == in[2]->boolean;
543 out = ec_malloc(sizeof(*out));
548 if (!strcmp(in[0]->str, "eq"))
550 else if (!strcmp(in[0]->str, "ne"))
552 else if (!strcmp(in[0]->str, "lt"))
553 out->boolean = !gt && !eq;
554 else if (!strcmp(in[0]->str, "gt"))
555 out->boolean = gt && !eq;
556 else if (!strcmp(in[0]->str, "le"))
557 out->boolean = !gt || eq;
558 else if (!strcmp(in[0]->str, "ge"))
559 out->boolean = gt || eq;
561 cond_result_table_free(in, in_len);
565 cond_result_free(out);
566 cond_result_table_free(in, in_len);
570 static struct cond_result *
571 eval_count(const struct ec_parse *state, struct cond_result **in, size_t in_len)
573 struct cond_result *out = NULL;
577 if (in_len != 1 || in[0]->type != NODESET) {
578 EC_LOG(LOG_ERR, "count() takes one argument of type nodeset.\n");
583 out = ec_malloc(sizeof(*out));
588 out->int64 = ec_htable_len(in[0]->htable);
590 cond_result_table_free(in, in_len);
594 cond_result_free(out);
595 cond_result_table_free(in, in_len);
599 static struct cond_result *
600 eval_func(const char *name, const struct ec_parse *state,
601 struct cond_result **in, size_t in_len)
605 f = ec_dict_get(ec_node_cond_functions, name);
607 EC_LOG(LOG_ERR, "No such function <%s>\n",
610 cond_result_table_free(in, in_len);
614 return f(state, in, in_len);
618 static struct cond_result *
619 eval_condition(const struct ec_parse *cond, const struct ec_parse *state)
621 const struct ec_parse *iter;
622 struct cond_result *res = NULL;
623 struct cond_result **args = NULL;
624 const struct ec_parse *func = NULL, *func_name = NULL, *arg_list = NULL;
625 const struct ec_parse *value = NULL;
630 func = ec_parse_find((void *)cond, "id_function");
632 EC_PARSE_FOREACH_CHILD(iter, func) {
633 id = ec_node_id(ec_parse_get_node(iter));
634 if (!strcmp(id, "id_function_name"))
636 if (!strcmp(id, "id_arg_list"))
640 iter = ec_parse_find((void *)arg_list, "id_arg");
641 while (iter != NULL) {
642 args = ec_realloc(args, (n_arg + 1) * sizeof(*args));
643 args[n_arg] = eval_condition(iter, state);
644 if (args[n_arg] == NULL)
647 iter = ec_parse_find_next((void *)arg_list,
648 (void *)iter, "id_arg", 0);
651 res = eval_func(ec_strvec_val(ec_parse_strvec(func_name), 0),
657 value = ec_parse_find((void *)cond, "id_value_str");
659 res = ec_malloc(sizeof(*res));
663 res->str = ec_strdup(ec_strvec_val(ec_parse_strvec(value), 0));
664 if (res->str == NULL)
669 value = ec_parse_find((void *)cond, "id_value_int");
671 res = ec_malloc(sizeof(*res));
675 if (ec_str_parse_llint(ec_strvec_val(ec_parse_strvec(value), 0),
676 0, LLONG_MIN, LLONG_MAX,
683 cond_result_free(res);
684 cond_result_table_free(args, n_arg);
689 validate_condition(const struct ec_parse *cond, const struct ec_parse *state)
691 struct cond_result *res;
694 res = eval_condition(cond, state);
698 ret = boolean_value(res);
699 cond_result_free(res);
705 ec_node_cond_parse(const struct ec_node *node, struct ec_parse *state,
706 const struct ec_strvec *strvec)
708 struct ec_node_cond *priv = ec_node_priv(node);
709 struct ec_parse *child;
712 ret = ec_node_parse_child(priv->child, state, strvec);
716 valid = validate_condition(priv->parsed_cond, state);
721 child = ec_parse_get_last_child(state);
722 ec_parse_unlink_child(state, child);
723 ec_parse_free(child);
724 return EC_PARSE_NOMATCH;
731 ec_node_cond_complete(const struct ec_node *node,
732 struct ec_comp *comp,
733 const struct ec_strvec *strvec)
735 struct ec_node_cond *priv = ec_node_priv(node);
737 // XXX eval condition
738 // XXX before or after completing ? configurable ?
740 return ec_node_complete_child(priv->child, comp, strvec);
743 static void ec_node_cond_free_priv(struct ec_node *node)
745 struct ec_node_cond *priv = ec_node_priv(node);
747 ec_free(priv->cond_str);
748 priv->cond_str = NULL;
749 ec_parse_free(priv->parsed_cond);
750 priv->parsed_cond = NULL;
751 ec_node_free(priv->child);
754 static const struct ec_config_schema ec_node_cond_schema[] = {
758 .type = EC_CONFIG_TYPE_STRING,
762 .desc = "The child node.",
763 .type = EC_CONFIG_TYPE_NODE,
766 .type = EC_CONFIG_TYPE_NONE,
770 static int ec_node_cond_set_config(struct ec_node *node,
771 const struct ec_config *config)
773 struct ec_node_cond *priv = ec_node_priv(node);
774 const struct ec_config *cond = NULL;
775 struct ec_parse *parsed_cond = NULL;
776 const struct ec_config *child;
777 char *cond_str = NULL;
779 cond = ec_config_dict_get(config, "expr");
785 cond_str = ec_strdup(cond->string);
786 if (cond_str == NULL)
789 child = ec_config_dict_get(config, "child");
793 /* parse expression to build the cmd child node */
794 parsed_cond = ec_node_cond_build(cond_str);
795 if (parsed_cond == NULL)
798 /* ok, store the config */
799 ec_parse_free(priv->parsed_cond);
800 priv->parsed_cond = parsed_cond;
801 ec_free(priv->cond_str);
802 priv->cond_str = cond_str;
803 ec_node_free(priv->child);
804 priv->child = ec_node_clone(child->node);
809 ec_parse_free(parsed_cond);
815 ec_node_cond_get_children_count(const struct ec_node *node)
817 struct ec_node_cond *priv = ec_node_priv(node);
819 if (priv->child == NULL)
825 ec_node_cond_get_child(const struct ec_node *node, size_t i,
826 struct ec_node **child, unsigned int *refs)
828 struct ec_node_cond *priv = ec_node_priv(node);
833 *child = priv->child;
838 static struct ec_node_type ec_node_cond_type = {
840 .schema = ec_node_cond_schema,
841 .set_config = ec_node_cond_set_config,
842 .parse = ec_node_cond_parse,
843 .complete = ec_node_cond_complete,
844 .size = sizeof(struct ec_node_cond),
845 .free_priv = ec_node_cond_free_priv,
846 .get_children_count = ec_node_cond_get_children_count,
847 .get_child = ec_node_cond_get_child,
850 EC_NODE_TYPE_REGISTER(ec_node_cond_type);
852 struct ec_node *ec_node_cond(const char *id, const char *cmd,
853 struct ec_node *child)
855 struct ec_config *config = NULL;
856 struct ec_node *node = NULL;
862 node = ec_node_from_type(&ec_node_cond_type, id);
866 config = ec_config_dict();
870 if (ec_config_dict_set(config, "expr", ec_config_string(cmd)) < 0)
873 if (ec_config_dict_set(config, "child", ec_config_node(child)) < 0) {
874 child = NULL; /* freed */
879 ret = ec_node_set_config(node, config);
880 config = NULL; /* freed */
889 ec_config_free(config);
894 static void ec_node_cond_exit_func(void)
896 ec_node_free(ec_node_cond_parser);
897 ec_node_cond_parser = NULL;
898 ec_dict_free(ec_node_cond_functions);
899 ec_node_cond_functions = NULL;
902 static int add_func(const char *name, cond_func_t *f)
904 return ec_dict_set(ec_node_cond_functions, name, f, NULL);
907 static int ec_node_cond_init_func(void)
909 ec_node_cond_parser = ec_node_cond_build_parser();
910 if (ec_node_cond_parser == NULL)
913 ec_node_cond_functions = ec_dict();
914 if (ec_node_cond_functions == NULL)
917 if (add_func("root", eval_root) < 0)
919 if (add_func("current", eval_current) < 0)
921 if (add_func("bool", eval_bool) < 0)
923 if (add_func("or", eval_or) < 0)
925 if (add_func("and", eval_and) < 0)
927 if (add_func("first_child", eval_first_child) < 0)
929 if (add_func("find", eval_find) < 0)
931 if (add_func("cmp", eval_cmp) < 0)
933 if (add_func("count", eval_count) < 0)
939 EC_LOG(EC_LOG_ERR, "Failed to initialize condition parser\n");
940 ec_node_cond_exit_func();
944 static struct ec_init ec_node_cond_init = {
945 .init = ec_node_cond_init_func,
946 .exit = ec_node_cond_exit_func,
950 EC_INIT_REGISTER(ec_node_cond_init);
952 /* LCOV_EXCL_START */
953 static int ec_node_cond_testcase(void)
955 struct ec_node *node;
959 node = EC_NODE_SEQ(EC_NO_ID,
960 EC_NODE_SUBSET(EC_NO_ID,
961 ec_node_str("id_node1", "node1"),
962 ec_node_str("id_node2", "node2"),
963 ec_node_str("id_node3", "node3"),
964 ec_node_str("id_node4", "node4")),
965 ec_node_cond(EC_NO_ID,
966 "or(find(root(), id_node1), "
967 " and(find(root(), id_node2),"
968 " find(root(), id_node3)))",
969 ec_node_str(EC_NO_ID, "ok")));
971 EC_LOG(EC_LOG_ERR, "cannot create node\n");
974 testres |= EC_TEST_CHECK_PARSE(node, 2, "node1", "ok");
975 testres |= EC_TEST_CHECK_PARSE(node, 3, "node2", "node3", "ok");
976 testres |= EC_TEST_CHECK_PARSE(node, 4, "node1", "node2", "node3", "ok");
977 testres |= EC_TEST_CHECK_PARSE(node, 3, "node2", "node1", "ok");
978 testres |= EC_TEST_CHECK_PARSE(node, -1, "node2", "node4", "ok");
979 testres |= EC_TEST_CHECK_PARSE(node, -1, "node2", "ok");
980 testres |= EC_TEST_CHECK_PARSE(node, -1, "node3", "ok");
981 testres |= EC_TEST_CHECK_PARSE(node, -1, "node4", "ok");
984 node = ec_node_cond(EC_NO_ID,
985 "cmp(le, count(find(root(), id_node)), 3)",
986 ec_node_many(EC_NO_ID,
987 ec_node_str("id_node", "foo"), 0, 0));
989 EC_LOG(EC_LOG_ERR, "cannot create node\n");
992 testres |= EC_TEST_CHECK_PARSE(node, 0);
993 testres |= EC_TEST_CHECK_PARSE(node, 1, "foo");
994 testres |= EC_TEST_CHECK_PARSE(node, 2, "foo", "foo");
995 testres |= EC_TEST_CHECK_PARSE(node, 3, "foo", "foo", "foo");
996 testres |= EC_TEST_CHECK_PARSE(node, -1, "foo", "foo", "foo", "foo");
999 // XXX test completion
1003 /* LCOV_EXCL_STOP */
1005 static struct ec_test ec_node_cond_test = {
1006 .name = "node_cond",
1007 .test = ec_node_cond_testcase,
1010 EC_TEST_REGISTER(ec_node_cond_test);