From a4ccdcb24d718ab72bde5eb7233a1d74566a846c Mon Sep 17 00:00:00 2001 From: Olivier Matz Date: Thu, 5 Jul 2018 20:49:50 +0200 Subject: [PATCH] use config for cmd node --- lib/ecoli_node_cmd.c | 280 ++++++++++++++++++++++++++++--------------- lib/ecoli_node_cmd.h | 5 - 2 files changed, 185 insertions(+), 100 deletions(-) diff --git a/lib/ecoli_node_cmd.c b/lib/ecoli_node_cmd.c index 99a7732..070bb02 100644 --- a/lib/ecoli_node_cmd.c +++ b/lib/ecoli_node_cmd.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -35,25 +36,29 @@ EC_LOG_TYPE_REGISTER(node_cmd); struct ec_node_cmd { struct ec_node gen; char *cmd_str; /* the command string. */ - struct ec_node *cmd; /* the command node. */ - struct ec_node *lex; /* the lexer node. */ - struct ec_node *expr; /* the expression parser. */ - struct ec_node **table; /* table of node referenced in command. */ + struct ec_node *cmd; /* the command node. */ + struct ec_node *parser; /* the expression parser. */ + struct ec_node *expr; /* the expression parser without lexer. */ + struct ec_node **table; /* table of node referenced in command. */ unsigned int len; /* len of the table. */ }; +/* passed as user context to expression parser */ +struct ec_node_cmd_ctx { + struct ec_node **table; + unsigned int len; +}; + static int ec_node_cmd_eval_var(void **result, void *userctx, const struct ec_parse *var) { const struct ec_strvec *vec; - struct ec_node_cmd *node = userctx; + struct ec_node_cmd_ctx *ctx = userctx; struct ec_node *eval = NULL; const char *str, *id; unsigned int i; - (void)userctx; - /* get parsed string vector, it should contain only one str */ vec = ec_parse_strvec(var); if (ec_strvec_len(vec) != 1) { @@ -62,14 +67,14 @@ ec_node_cmd_eval_var(void **result, void *userctx, } str = ec_strvec_val(vec, 0); - for (i = 0; i < node->len; i++) { - id = ec_node_id(node->table[i]); + for (i = 0; i < ctx->len; i++) { + id = ec_node_id(ctx->table[i]); if (id == NULL) continue; if (strcmp(str, id)) continue; /* if id matches, use a node provided by the user... */ - eval = ec_node_clone(node->table[i]); + eval = ec_node_clone(ctx->table[i]); if (eval == NULL) return -1; break; @@ -258,7 +263,7 @@ ec_node_cmd_eval_free(void *result, void *userctx) ec_free(result); } -static const struct ec_node_expr_eval_ops test_ops = { +static const struct ec_node_expr_eval_ops expr_ops = { .eval_var = ec_node_cmd_eval_var, .eval_pre_op = ec_node_cmd_eval_pre_op, .eval_post_op = ec_node_cmd_eval_post_op, @@ -267,20 +272,12 @@ static const struct ec_node_expr_eval_ops test_ops = { .eval_free = ec_node_cmd_eval_free, }; -static int ec_node_cmd_build(struct ec_node_cmd *node) +static struct ec_node * +ec_node_cmd_build_expr(void) { - struct ec_node *expr = NULL, *lex = NULL, *cmd = NULL; - struct ec_parse *p = NULL; - void *result; + struct ec_node *expr = NULL; int ret; - ec_node_free(node->expr); - node->expr = NULL; - ec_node_free(node->lex); - node->lex = NULL; - ec_node_free(node->cmd); - node->cmd = NULL; - /* build the expression parser */ expr = ec_node("expr", "expr"); if (expr == NULL) @@ -313,6 +310,19 @@ static int ec_node_cmd_build(struct ec_node_cmd *node) if (ret < 0) goto fail; + return expr; + +fail: + ec_node_free(expr); + return NULL; +} + +static struct ec_node * +ec_node_cmd_build_parser(struct ec_node *expr) +{ + struct ec_node *lex = NULL; + int ret; + /* prepend a lexer to the expression node */ lex = ec_node_re_lex(EC_NO_ID, ec_node_clone(expr)); if (lex == NULL) @@ -334,8 +344,25 @@ static int ec_node_cmd_build(struct ec_node_cmd *node) if (ret < 0) goto fail; + return lex; + +fail: + ec_node_free(lex); + + return NULL; +} + +static struct ec_node * +ec_node_cmd_build(struct ec_node_cmd *node, const char *cmd_str, + struct ec_node **table, size_t len) +{ + struct ec_node_cmd_ctx ctx = { table, len }; + struct ec_parse *p = NULL; + void *result; + int ret; + /* parse the command expression */ - p = ec_node_parse(lex, node->cmd_str); + p = ec_node_parse(node->parser, cmd_str); if (p == NULL) goto fail; @@ -348,27 +375,18 @@ static int ec_node_cmd_build(struct ec_node_cmd *node) goto fail; } - ret = ec_node_expr_eval(&result, expr, ec_parse_get_first_child(p), - &test_ops, node); + ret = ec_node_expr_eval(&result, node->expr, + ec_parse_get_first_child(p), + &expr_ops, &ctx); if (ret < 0) goto fail; ec_parse_free(p); - p = NULL; - - node->expr = expr; - node->lex = lex; - node->cmd = result; - - return 0; + return result; fail: ec_parse_free(p); - ec_node_free(expr); - ec_node_free(lex); - ec_node_free(cmd); - - return -1; + return NULL; } static int @@ -393,120 +411,189 @@ ec_node_cmd_complete(const struct ec_node *gen_node, static void ec_node_cmd_free_priv(struct ec_node *gen_node) { struct ec_node_cmd *node = (struct ec_node_cmd *)gen_node; - unsigned int i; ec_free(node->cmd_str); ec_node_free(node->cmd); ec_node_free(node->expr); - ec_node_free(node->lex); - for (i = 0; i < node->len; i++) - ec_node_free(node->table[i]); + ec_node_free(node->parser); ec_free(node->table); } - -static struct ec_node_type ec_node_cmd_type = { - .name = "cmd", - .parse = ec_node_cmd_parse, - .complete = ec_node_cmd_complete, - .size = sizeof(struct ec_node_cmd), - .free_priv = ec_node_cmd_free_priv, +static const struct ec_config_schema ec_node_cmd_subschema[] = { + { + .desc = "A child node whose id is referenced in the expression.", + .type = EC_CONFIG_TYPE_NODE, + }, }; -EC_NODE_TYPE_REGISTER(ec_node_cmd_type); +static const struct ec_config_schema ec_node_cmd_schema[] = { + { + .key = "expr", + .desc = "The expression to match. Supported operators " + "are or '|', list ',', many '+', many-or-zero '*', " + "option '[]', group '()'. An identifier (alphanumeric) can " + "reference a node whose node_id matches. Else it is " + "interpreted as ec_node_str() matching this string. " + "Example: command [option] (subset1, subset2) x|y", + .type = EC_CONFIG_TYPE_STRING, + }, + { + .key = "children", + .desc = "The list of children nodes.", + .type = EC_CONFIG_TYPE_LIST, + .subschema = ec_node_cmd_subschema, + .subschema_len = EC_COUNT_OF(ec_node_cmd_subschema), + }, +}; -int ec_node_cmd_add_child(struct ec_node *gen_node, struct ec_node *child) +static int ec_node_cmd_set_config(struct ec_node *gen_node, + const struct ec_config *config) { struct ec_node_cmd *node = (struct ec_node_cmd *)gen_node; - struct ec_node **table; - int ret; - - assert(node != NULL); + const struct ec_config *expr = NULL, *children = NULL, *child; + struct ec_node *cmd = NULL; + struct ec_node **table = NULL; + char *cmd_str = NULL; + size_t n; + + /* retrieve config locally */ + expr = ec_config_dict_get(config, "expr"); + if (expr == NULL) { + errno = EINVAL; + goto fail; + } - if (child == NULL) { + children = ec_config_dict_get(config, "children"); + if (children == NULL) { errno = EINVAL; goto fail; } - if (ec_node_check_type(gen_node, &ec_node_cmd_type) < 0) + cmd_str = ec_strdup(expr->string); + if (cmd_str == NULL) goto fail; - if (node->cmd == NULL) { - ret = ec_node_cmd_build(node); - if (ret < 0) - return ret; - } + n = 0; + TAILQ_FOREACH(child, &children->list, next) + n++; - table = ec_realloc(node->table, (node->len + 1) * sizeof(*node->table)); + table = ec_malloc(n * sizeof(*table)); if (table == NULL) goto fail; - node->table = table; + n = 0; + TAILQ_FOREACH(child, &children->list, next) { + table[n] = child->node; + n++; + } - if (ec_node_add_child(gen_node, child) < 0) + /* parse expression to build the cmd child node */ + cmd = ec_node_cmd_build(node, cmd_str, table, n); + if (cmd == NULL) goto fail; - table[node->len] = child; - node->len++; + gen_node->n_children = 0; /* XXX */ + TAILQ_FOREACH(child, &children->list, next) { + /* XXX if it fails... too late */ + if (ec_node_add_child(gen_node, child->node) < 0) + goto fail; + } + + ec_node_free(node->cmd); + node->cmd = cmd; + ec_free(node->cmd_str); + node->cmd_str = cmd_str; + ec_free(node->table); + node->table = table; + node->len = n; return 0; fail: - ec_node_free(child); + ec_free(table); + ec_free(cmd_str); + ec_node_free(cmd); return -1; } +static struct ec_node_type ec_node_cmd_type = { + .name = "cmd", + .schema = ec_node_cmd_schema, + .schema_len = EC_COUNT_OF(ec_node_cmd_schema), + .set_config = ec_node_cmd_set_config, + .parse = ec_node_cmd_parse, + .complete = ec_node_cmd_complete, + .size = sizeof(struct ec_node_cmd), + .free_priv = ec_node_cmd_free_priv, +}; + +EC_NODE_TYPE_REGISTER(ec_node_cmd_type); + struct ec_node *__ec_node_cmd(const char *id, const char *cmd, ...) { + struct ec_config *config = NULL, *children = NULL; struct ec_node *gen_node = NULL; struct ec_node_cmd *node = NULL; struct ec_node *child; va_list ap; - int fail = 0; + + va_start(ap, cmd); + child = va_arg(ap, struct ec_node *); gen_node = __ec_node(&ec_node_cmd_type, id); if (gen_node == NULL) - fail = 1; + goto fail_free_children; + node = (struct ec_node_cmd *)gen_node; - if (fail == 0) { - node = (struct ec_node_cmd *)gen_node; - node->cmd_str = ec_strdup(cmd); - if (node->cmd_str == NULL) - fail = 1; - } + node->expr = ec_node_cmd_build_expr(); + if (node->expr == NULL) + goto fail_free_children; - va_start(ap, cmd); + node->parser = ec_node_cmd_build_parser(node->expr); + if (node->parser == NULL) + goto fail_free_children; - for (child = va_arg(ap, struct ec_node *); - child != EC_NODE_ENDLIST; - child = va_arg(ap, struct ec_node *)) { + config = ec_config_dict(); + if (config == NULL) + goto fail_free_children; - /* on error, don't quit the loop to avoid leaks */ - if (fail == 1 || child == NULL || - ec_node_cmd_add_child(&node->gen, child) < 0) { - fail = 1; - ec_node_free(child); - } - } + if (ec_config_dict_set(config, "expr", ec_config_string(cmd)) < 0) + goto fail_free_children; - va_end(ap); + children = ec_config_list(); + if (children == NULL) + goto fail_free_children; + + for (; child != EC_NODE_ENDLIST; child = va_arg(ap, struct ec_node *)) { + if (child == NULL) + goto fail_free_children; + + if (ec_config_list_add(children, ec_config_node(child)) < 0) + goto fail_free_children; + } - if (fail == 1) + if (ec_config_dict_set(config, "children", children) < 0) { + children = NULL; /* freed */ goto fail; + } + children = NULL; - if (ec_node_cmd_build(node) < 0) + if (ec_node_set_config(gen_node, config) < 0) goto fail; + va_end(ap); + return gen_node; +fail_free_children: + for (; child != EC_NODE_ENDLIST; child = va_arg(ap, struct ec_node *)) + ec_node_free(child); fail: - ec_node_free(gen_node); /* will also free children */ - return NULL; -} + ec_node_free(gen_node); /* will also free added children */ + ec_config_free(config); + va_end(ap); -struct ec_node *ec_node_cmd(const char *id, const char *cmd_str) -{ - return __ec_node_cmd(id, cmd_str, EC_NODE_ENDLIST); + return NULL; } /* LCOV_EXCL_START */ @@ -520,6 +607,9 @@ static int ec_node_cmd_testcase(void) ec_node_int("x", 0, 10, 10), ec_node_int("y", 20, 30, 10) ); + ec_node_free(node); + return 0; + if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; diff --git a/lib/ecoli_node_cmd.h b/lib/ecoli_node_cmd.h index 2eab193..99afc01 100644 --- a/lib/ecoli_node_cmd.h +++ b/lib/ecoli_node_cmd.h @@ -11,9 +11,4 @@ struct ec_node *__ec_node_cmd(const char *id, const char *cmd_str, ...); -struct ec_node *ec_node_cmd(const char *id, const char *cmd_str); - -/* child is consumed */ -int ec_node_cmd_add_child(struct ec_node *node, struct ec_node *child); - #endif -- 2.20.1