1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright 2016, Olivier MATZ <zer0@droids-corp.org>
14 #include <ecoli_malloc.h>
15 #include <ecoli_log.h>
16 #include <ecoli_test.h>
17 #include <ecoli_strvec.h>
18 #include <ecoli_node.h>
19 #include <ecoli_parsed.h>
20 #include <ecoli_completed.h>
21 #include <ecoli_node_expr.h>
22 #include <ecoli_node_str.h>
23 #include <ecoli_node_or.h>
24 #include <ecoli_node_subset.h>
25 #include <ecoli_node_int.h>
26 #include <ecoli_node_many.h>
27 #include <ecoli_node_seq.h>
28 #include <ecoli_node_option.h>
29 #include <ecoli_node_re.h>
30 #include <ecoli_node_re_lex.h>
31 #include <ecoli_node_cmd.h>
33 EC_LOG_TYPE_REGISTER(node_cmd);
37 char *cmd_str; /* the command string. */
38 struct ec_node *cmd; /* the command node. */
39 struct ec_node *lex; /* the lexer node. */
40 struct ec_node *expr; /* the expression parser. */
41 struct ec_node **table; /* table of node referenced in command. */
42 unsigned int len; /* len of the table. */
46 ec_node_cmd_eval_var(void **result, void *userctx,
47 const struct ec_parsed *var)
49 const struct ec_strvec *vec;
50 struct ec_node_cmd *node = userctx;
51 struct ec_node *eval = NULL;
57 /* get parsed string vector, it should contain only one str */
58 vec = ec_parsed_strvec(var);
59 if (ec_strvec_len(vec) != 1)
61 str = ec_strvec_val(vec, 0);
63 for (i = 0; i < node->len; i++) {
64 id = ec_node_id(node->table[i]);
69 /* if id matches, use a node provided by the user... */
70 eval = ec_node_clone(node->table[i]);
76 /* ...or create a string node */
78 eval = ec_node_str(EC_NO_ID, str);
89 ec_node_cmd_eval_pre_op(void **result, void *userctx, void *operand,
90 const struct ec_parsed *operator)
101 ec_node_cmd_eval_post_op(void **result, void *userctx, void *operand,
102 const struct ec_parsed *operator)
104 const struct ec_strvec *vec;
105 struct ec_node *in = operand;;
106 struct ec_node *out = NULL;;
110 /* get parsed string vector, it should contain only one str */
111 vec = ec_parsed_strvec(operator);
112 if (ec_strvec_len(vec) != 1)
115 if (!strcmp(ec_strvec_val(vec, 0), "*")) {
116 out = ec_node_many(EC_NO_ID,
117 ec_node_clone(in), 0, 0);
130 ec_node_cmd_eval_bin_op(void **result, void *userctx, void *operand1,
131 const struct ec_parsed *operator, void *operand2)
134 const struct ec_strvec *vec;
135 struct ec_node *out = NULL;
136 struct ec_node *in1 = operand1;
137 struct ec_node *in2 = operand2;
141 /* get parsed string vector, it should contain only one str */
142 vec = ec_parsed_strvec(operator);
143 if (ec_strvec_len(vec) > 1)
146 if (ec_strvec_len(vec) == 0) {
147 if (!strcmp(in1->type->name, "seq")) {
148 if (ec_node_seq_add(in1, ec_node_clone(in2)) < 0)
153 out = EC_NODE_SEQ(EC_NO_ID, ec_node_clone(in1),
161 } else if (!strcmp(ec_strvec_val(vec, 0), "|")) {
162 if (!strcmp(in2->type->name, "or")) {
163 if (ec_node_or_add(in2, ec_node_clone(in1)) < 0)
167 } else if (!strcmp(in1->type->name, "or")) {
168 if (ec_node_or_add(in1, ec_node_clone(in2)) < 0)
173 out = EC_NODE_OR(EC_NO_ID, ec_node_clone(in1),
181 } else if (!strcmp(ec_strvec_val(vec, 0), ",")) {
182 if (!strcmp(in2->type->name, "subset")) {
183 if (ec_node_subset_add(in2, ec_node_clone(in1)) < 0)
187 } else if (!strcmp(in1->type->name, "subset")) {
188 if (ec_node_subset_add(in1, ec_node_clone(in2)) < 0)
193 out = EC_NODE_SUBSET(EC_NO_ID, ec_node_clone(in1),
209 ec_node_cmd_eval_parenthesis(void **result, void *userctx,
210 const struct ec_parsed *open_paren,
211 const struct ec_parsed *close_paren,
214 const struct ec_strvec *vec;
215 struct ec_node *in = value;;
216 struct ec_node *out = NULL;;
221 /* get parsed string vector, it should contain only one str */
222 vec = ec_parsed_strvec(open_paren);
223 if (ec_strvec_len(vec) != 1)
226 if (!strcmp(ec_strvec_val(vec, 0), "[")) {
227 out = ec_node_option(EC_NO_ID, ec_node_clone(in));
231 } else if (!strcmp(ec_strvec_val(vec, 0), "(")) {
243 ec_node_cmd_eval_free(void *result, void *userctx)
249 static const struct ec_node_expr_eval_ops test_ops = {
250 .eval_var = ec_node_cmd_eval_var,
251 .eval_pre_op = ec_node_cmd_eval_pre_op,
252 .eval_post_op = ec_node_cmd_eval_post_op,
253 .eval_bin_op = ec_node_cmd_eval_bin_op,
254 .eval_parenthesis = ec_node_cmd_eval_parenthesis,
255 .eval_free = ec_node_cmd_eval_free,
258 static int ec_node_cmd_build(struct ec_node_cmd *node)
260 struct ec_node *expr = NULL, *lex = NULL, *cmd = NULL;
261 struct ec_parsed *p = NULL;
265 ec_node_free(node->expr);
267 ec_node_free(node->lex);
269 ec_node_free(node->cmd);
272 /* build the expression parser */
274 expr = ec_node("expr", "expr");
277 ret = ec_node_expr_set_val_node(expr, ec_node_re(EC_NO_ID, "[a-zA-Z0-9]+"));
280 ret = ec_node_expr_add_bin_op(expr, ec_node_str(EC_NO_ID, ","));
283 ret = ec_node_expr_add_bin_op(expr, ec_node_str(EC_NO_ID, "|"));
286 ret = ec_node_expr_add_bin_op(expr, ec_node("empty", EC_NO_ID));
289 ret = ec_node_expr_add_post_op(expr, ec_node_str(EC_NO_ID, "+"));
292 ret = ec_node_expr_add_post_op(expr, ec_node_str(EC_NO_ID, "*"));
295 ret = ec_node_expr_add_parenthesis(expr, ec_node_str(EC_NO_ID, "["),
296 ec_node_str(EC_NO_ID, "]"));
299 ec_node_expr_add_parenthesis(expr, ec_node_str(EC_NO_ID, "("),
300 ec_node_str(EC_NO_ID, ")"));
304 /* prepend a lexer to the expression node */
306 lex = ec_node_re_lex(EC_NO_ID, ec_node_clone(expr));
310 ret = ec_node_re_lex_add(lex, "[a-zA-Z0-9]+", 1);
313 ret = ec_node_re_lex_add(lex, "[*|,()]", 1);
316 ret = ec_node_re_lex_add(lex, "\\[", 1);
319 ret = ec_node_re_lex_add(lex, "\\]", 1);
322 ret = ec_node_re_lex_add(lex, "[ ]+", 0);
326 /* parse the command expression */
328 p = ec_node_parse(lex, node->cmd_str);
333 if (!ec_parsed_matches(p))
335 if (!ec_parsed_has_child(p))
338 ret = ec_node_expr_eval(&result, expr, ec_parsed_get_first_child(p),
361 ec_node_cmd_parse(const struct ec_node *gen_node, struct ec_parsed *state,
362 const struct ec_strvec *strvec)
364 struct ec_node_cmd *node = (struct ec_node_cmd *)gen_node;
366 if (node->cmd == NULL)
368 return ec_node_parse_child(node->cmd, state, strvec);
372 ec_node_cmd_complete(const struct ec_node *gen_node,
373 struct ec_completed *completed,
374 const struct ec_strvec *strvec)
376 struct ec_node_cmd *node = (struct ec_node_cmd *)gen_node;
378 if (node->cmd == NULL)
380 return ec_node_complete_child(node->cmd, completed, strvec);
383 static void ec_node_cmd_free_priv(struct ec_node *gen_node)
385 struct ec_node_cmd *node = (struct ec_node_cmd *)gen_node;
388 ec_free(node->cmd_str);
389 ec_node_free(node->cmd);
390 ec_node_free(node->expr);
391 ec_node_free(node->lex);
392 for (i = 0; i < node->len; i++)
393 ec_node_free(node->table[i]);
394 ec_free(node->table);
398 static struct ec_node_type ec_node_cmd_type = {
400 .parse = ec_node_cmd_parse,
401 .complete = ec_node_cmd_complete,
402 .size = sizeof(struct ec_node_cmd),
403 .free_priv = ec_node_cmd_free_priv,
406 EC_NODE_TYPE_REGISTER(ec_node_cmd_type);
408 int ec_node_cmd_add_child(struct ec_node *gen_node, struct ec_node *child)
410 struct ec_node_cmd *node = (struct ec_node_cmd *)gen_node;
411 struct ec_node **table;
414 assert(node != NULL);
421 if (ec_node_check_type(gen_node, &ec_node_cmd_type) < 0)
424 if (node->cmd == NULL) {
425 ret = ec_node_cmd_build(node);
430 table = ec_realloc(node->table, (node->len + 1) * sizeof(*node->table));
436 if (ec_node_add_child(gen_node, child) < 0)
439 table[node->len] = child;
449 struct ec_node *__ec_node_cmd(const char *id, const char *cmd, ...)
451 struct ec_node *gen_node = NULL;
452 struct ec_node_cmd *node = NULL;
453 struct ec_node *child;
457 gen_node = __ec_node(&ec_node_cmd_type, id);
458 if (gen_node == NULL)
462 node = (struct ec_node_cmd *)gen_node;
463 node->cmd_str = ec_strdup(cmd);
464 if (node->cmd_str == NULL)
470 for (child = va_arg(ap, struct ec_node *);
471 child != EC_NODE_ENDLIST;
472 child = va_arg(ap, struct ec_node *)) {
474 /* on error, don't quit the loop to avoid leaks */
475 if (fail == 1 || child == NULL ||
476 ec_node_cmd_add_child(&node->gen, child) < 0) {
487 if (ec_node_cmd_build(node) < 0)
493 ec_node_free(gen_node); /* will also free children */
497 struct ec_node *ec_node_cmd(const char *id, const char *cmd_str)
499 return __ec_node_cmd(id, cmd_str, EC_NODE_ENDLIST);
502 /* LCOV_EXCL_START */
503 static int ec_node_cmd_testcase(void)
505 struct ec_node *node;
508 node = EC_NODE_CMD(EC_NO_ID,
509 "command [option] (subset1, subset2, subset3, subset4) x|y z*",
510 ec_node_int("x", 0, 10, 10),
511 ec_node_int("y", 20, 30, 10)
514 EC_LOG(EC_LOG_ERR, "cannot create node\n");
517 testres |= EC_TEST_CHECK_PARSE(node, 2, "command", "1");
518 testres |= EC_TEST_CHECK_PARSE(node, 3, "command", "subset1", "1");
519 testres |= EC_TEST_CHECK_PARSE(node, 4, "command", "subset3", "subset2",
521 testres |= EC_TEST_CHECK_PARSE(node, 5, "command", "subset2", "subset3",
523 testres |= EC_TEST_CHECK_PARSE(node, 6, "command", "subset3", "subset1",
524 "subset4", "subset2", "4");
525 testres |= EC_TEST_CHECK_PARSE(node, 2, "command", "23");
526 testres |= EC_TEST_CHECK_PARSE(node, 3, "command", "option", "23");
527 testres |= EC_TEST_CHECK_PARSE(node, 5, "command", "option", "23",
529 testres |= EC_TEST_CHECK_PARSE(node, -1, "command", "15");
530 testres |= EC_TEST_CHECK_PARSE(node, -1, "foo");
533 node = EC_NODE_CMD(EC_NO_ID, "good morning [count] bob|bobby|michael",
534 ec_node_int("count", 0, 10, 10));
536 EC_LOG(EC_LOG_ERR, "cannot create node\n");
539 testres |= EC_TEST_CHECK_PARSE(node, 4, "good", "morning", "1", "bob");
541 testres |= EC_TEST_CHECK_COMPLETE(node,
543 "good", EC_NODE_ENDLIST);
544 testres |= EC_TEST_CHECK_COMPLETE(node,
545 "g", EC_NODE_ENDLIST,
546 "good", EC_NODE_ENDLIST);
547 testres |= EC_TEST_CHECK_COMPLETE(node,
548 "good", "morning", "", EC_NODE_ENDLIST,
549 "bob", "bobby", "michael", EC_NODE_ENDLIST);
553 node = EC_NODE_CMD(EC_NO_ID, "[foo [bar]]");
555 EC_LOG(EC_LOG_ERR, "cannot create node\n");
558 testres |= EC_TEST_CHECK_PARSE(node, 0);
559 testres |= EC_TEST_CHECK_PARSE(node, 1, "foo");
560 testres |= EC_TEST_CHECK_PARSE(node, 2, "foo", "bar");
561 testres |= EC_TEST_CHECK_PARSE(node, 0, "x");
568 static struct ec_test ec_node_cmd_test = {
570 .test = ec_node_cmd_testcase,
573 EC_TEST_REGISTER(ec_node_cmd_test);