From fd55c75d4ece51751ae95d98706458615e5b4776 Mon Sep 17 00:00:00 2001 From: Olivier Matz Date: Thu, 18 May 2017 20:15:43 +0200 Subject: [PATCH] save --- lib/Makefile | 3 + lib/ecoli_keyval.c | 2 +- lib/ecoli_test.c | 23 +- lib/ecoli_test.h | 11 +- lib/ecoli_tk.c | 135 +++++++++-- lib/ecoli_tk.h | 59 ++++- lib/ecoli_tk_cmd.c | 499 +++++++++++++++++++++++++++++++++++++++ lib/ecoli_tk_cmd.h | 42 ++++ lib/ecoli_tk_empty.c | 10 +- lib/ecoli_tk_expr.c | 252 +++++++++----------- lib/ecoli_tk_expr.h | 75 ++++-- lib/ecoli_tk_expr_test.c | 230 +++++++++--------- lib/ecoli_tk_int.c | 10 +- lib/ecoli_tk_int.h | 2 + lib/ecoli_tk_many.c | 12 +- lib/ecoli_tk_option.c | 18 +- lib/ecoli_tk_or.c | 20 +- lib/ecoli_tk_or.h | 1 - lib/ecoli_tk_re.c | 180 ++++++++++++++ lib/ecoli_tk_re.h | 40 ++++ lib/ecoli_tk_re_lex.c | 24 +- lib/ecoli_tk_seq.c | 11 +- lib/ecoli_tk_seq.h | 2 +- lib/ecoli_tk_sh_lex.c | 10 +- lib/ecoli_tk_space.c | 10 +- lib/ecoli_tk_str.c | 13 +- lib/ecoli_tk_subset.c | 465 ++++++++++++++++++++++++++++++++++++ lib/ecoli_tk_subset.h | 46 ++++ lib/ecoli_tk_weakref.c | 10 +- lib/main-readline.c | 32 ++- lib/main.c | 29 ++- lib/test.sh | 2 +- lib/todo.txt | 94 +++++++- 33 files changed, 1975 insertions(+), 397 deletions(-) create mode 100644 lib/ecoli_tk_cmd.c create mode 100644 lib/ecoli_tk_cmd.h create mode 100644 lib/ecoli_tk_re.c create mode 100644 lib/ecoli_tk_re.h create mode 100644 lib/ecoli_tk_subset.c create mode 100644 lib/ecoli_tk_subset.h diff --git a/lib/Makefile b/lib/Makefile index ecb030b..d868c39 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -40,6 +40,7 @@ srcs += ecoli_malloc.c srcs += ecoli_strvec.c srcs += ecoli_test.c srcs += ecoli_tk.c +srcs += ecoli_tk_cmd.c srcs += ecoli_tk_empty.c srcs += ecoli_tk_expr.c srcs += ecoli_tk_expr_test.c @@ -47,11 +48,13 @@ srcs += ecoli_tk_int.c srcs += ecoli_tk_many.c srcs += ecoli_tk_option.c srcs += ecoli_tk_or.c +srcs += ecoli_tk_re.c srcs += ecoli_tk_re_lex.c srcs += ecoli_tk_seq.c srcs += ecoli_tk_sh_lex.c srcs += ecoli_tk_space.c srcs += ecoli_tk_str.c +srcs += ecoli_tk_subset.c srcs += ecoli_tk_weakref.c shlib-y-$(O)libecoli.so := $(srcs) diff --git a/lib/ecoli_keyval.c b/lib/ecoli_keyval.c index 7f87b01..0557bff 100644 --- a/lib/ecoli_keyval.c +++ b/lib/ecoli_keyval.c @@ -232,4 +232,4 @@ static struct ec_test ec_keyval_test = { .test = ec_keyval_testcase, }; -EC_REGISTER_TEST(ec_keyval_test); +EC_TEST_REGISTER(ec_keyval_test); diff --git a/lib/ecoli_test.c b/lib/ecoli_test.c index fc82c69..2ac659b 100644 --- a/lib/ecoli_test.c +++ b/lib/ecoli_test.c @@ -42,6 +42,7 @@ static struct ec_test_list test_list = TAILQ_HEAD_INITIALIZER(test_list); void ec_test_register(struct ec_test *test) { TAILQ_INSERT_TAIL(&test_list, test, next); + // XXX check if already exist, like for type } int ec_test_check_tk_parse(struct ec_tk *tk, int expected, ...) @@ -95,7 +96,7 @@ out: return ret; } -int ec_test_check_tk_complete(const struct ec_tk *tk, ...) +int ec_test_check_tk_complete(struct ec_tk *tk, ...) { struct ec_completed_tk *c = NULL; struct ec_completed_tk_elt *elt; @@ -175,14 +176,19 @@ out: return ret; } -int ec_test_all(void) +static int launch_test(const char *name) { struct ec_test *test; int ret = 0; + unsigned int count = 0; TAILQ_FOREACH(test, &test_list, next) { + if (name != NULL && strcmp(name, test->name)) + continue; + ec_log(EC_LOG_INFO, "== starting test %-20s\n", test->name); + count++; if (test->test() == 0) { ec_log(EC_LOG_INFO, "== test %-20s success\n", test->name); @@ -193,5 +199,18 @@ int ec_test_all(void) } } + if (name != NULL && count == 0) + ec_log(EC_LOG_WARNING, "== test %s not found\n", name); + return ret; } + +int ec_test_all(void) +{ + return launch_test(NULL); +} + +int ec_test_one(const char *name) +{ + return launch_test(name); +} diff --git a/lib/ecoli_test.h b/lib/ecoli_test.h index a05d4cf..cf41f46 100644 --- a/lib/ecoli_test.h +++ b/lib/ecoli_test.h @@ -33,9 +33,11 @@ #include #include -#define EC_REGISTER_TEST(t) \ - static void ec_init_##t(void); \ - static void __attribute__((constructor, used)) ec_init_##t(void) \ +// XXX check if already exists? +#define EC_TEST_REGISTER(t) \ + static void ec_test_init_##t(void); \ + static void __attribute__((constructor, used)) \ + ec_test_init_##t(void) \ { \ ec_test_register(&t); \ } @@ -66,6 +68,7 @@ struct ec_test { void ec_test_register(struct ec_test *test); int ec_test_all(void); +int ec_test_one(const char *name); /* expected == -1 means no match */ int ec_test_check_tk_parse(struct ec_tk *tk, int expected, ...); @@ -88,7 +91,7 @@ int ec_test_check_tk_parse(struct ec_tk *tk, int expected, ...); ret_; \ }) -int ec_test_check_tk_complete(const struct ec_tk *tk, ...); +int ec_test_check_tk_complete(struct ec_tk *tk, ...); #define EC_TEST_CHECK_TK_COMPLETE(tk, args...) ({ \ int ret_ = ec_test_check_tk_complete(tk, args); \ diff --git a/lib/ecoli_tk.c b/lib/ecoli_tk.c index 9d877c0..7fed400 100644 --- a/lib/ecoli_tk.c +++ b/lib/ecoli_tk.c @@ -37,7 +37,39 @@ #include #include -struct ec_tk *ec_tk_new(const char *id, const struct ec_tk_ops *ops, +static struct ec_tk_type_list tk_type_list = + TAILQ_HEAD_INITIALIZER(tk_type_list); + +struct ec_tk_type *ec_tk_type_lookup(const char *name) +{ + struct ec_tk_type *type; + + TAILQ_FOREACH(type, &tk_type_list, next) { + if (!strcmp(name, type->name)) + return type; + } + + return NULL; +} + +int ec_tk_type_register(struct ec_tk_type *type) +{ + if (ec_tk_type_lookup(type->name) != NULL) + return -EEXIST; + TAILQ_INSERT_TAIL(&tk_type_list, type, next); + + return 0; +} + +void ec_tk_type_dump(FILE *out) +{ + struct ec_tk_type *type; + + TAILQ_FOREACH(type, &tk_type_list, next) + fprintf(out, "%s\n", type->name); +} + +struct ec_tk *ec_tk_new(const char *id, const struct ec_tk_type *type, size_t size) { struct ec_tk *tk = NULL; @@ -45,14 +77,14 @@ struct ec_tk *ec_tk_new(const char *id, const struct ec_tk_ops *ops, assert(size >= sizeof(*tk)); - ec_log(EC_LOG_DEBUG, "create node type=%s id=%s\n", ops->typename, id); + ec_log(EC_LOG_DEBUG, "create node type=%s id=%s\n", type->name, id); tk = ec_calloc(1, size); if (tk == NULL) goto fail; TAILQ_INIT(&tk->children); - tk->ops = ops; + tk->type = type; tk->refcnt = 1; if (id != NULL) { @@ -61,7 +93,7 @@ struct ec_tk *ec_tk_new(const char *id, const struct ec_tk_ops *ops, goto fail; } - snprintf(buf, sizeof(buf), "<%s>", ops->typename); + snprintf(buf, sizeof(buf), "<%s>", type->name); tk->desc = ec_strdup(buf); // XXX ec_asprintf ? if (tk->desc == NULL) goto fail; @@ -87,8 +119,8 @@ void ec_tk_free(struct ec_tk *tk) if (--tk->refcnt > 0) return; - if (tk->ops != NULL && tk->ops->free_priv != NULL) - tk->ops->free_priv(tk); + if (tk->type != NULL && tk->type->free_priv != NULL) + tk->type->free_priv(tk); ec_free(tk->id); ec_free(tk->desc); ec_free(tk->attrs); @@ -134,6 +166,42 @@ struct ec_tk *ec_tk_parent(const struct ec_tk *tk) return tk->parent; } +static void __ec_tk_dump(FILE *out, + const struct ec_tk *tk, size_t indent) +{ + struct ec_tk *child; + size_t i; + const char *id = "None", *typename = "None"; + + if (tk->id != NULL) + id = tk->id; + typename = tk->type->name; + + /* XXX enhance */ + for (i = 0; i < indent; i++) { + if (i % 2) + fprintf(out, " "); + else + fprintf(out, "|"); + } + + fprintf(out, "tk_type=%s id=%s\n", typename, id); + TAILQ_FOREACH(child, &tk->children, next) + __ec_tk_dump(out, child, indent + 2); +} + +void ec_tk_dump(FILE *out, const struct ec_tk *tk) +{ + fprintf(out, "------------------- tk dump:\n"); //XXX + + if (tk == NULL) { + fprintf(out, "tk is NULL\n"); + return; + } + + __ec_tk_dump(out, tk, 0); +} + struct ec_parsed_tk *ec_tk_parse(struct ec_tk *tk, const char *str) { struct ec_strvec *strvec = NULL; @@ -166,9 +234,9 @@ struct ec_parsed_tk *ec_tk_parse_tokens(struct ec_tk *tk, int ret; /* build the node if required */ - if (tk->ops->build != NULL) { + if (tk->type->build != NULL) { if ((tk->flags & EC_TK_F_BUILT) == 0) { - ret = tk->ops->build(tk); + ret = tk->type->build(tk); if (ret < 0) { errno = -ret; return NULL; @@ -177,12 +245,12 @@ struct ec_parsed_tk *ec_tk_parse_tokens(struct ec_tk *tk, } tk->flags |= EC_TK_F_BUILT; - if (tk->ops->parse == NULL) { + if (tk->type->parse == NULL) { errno = ENOTSUP; return NULL; } - parsed_tk = tk->ops->parse(tk, strvec); + parsed_tk = tk->type->parse(tk, strvec); return parsed_tk; } @@ -246,15 +314,9 @@ static void __ec_parsed_tk_dump(FILE *out, if (parsed_tk->tk != NULL) { if (parsed_tk->tk->id != NULL) id = parsed_tk->tk->id; - typename = parsed_tk->tk->ops->typename; + typename = parsed_tk->tk->type->name; } - /* XXX remove this debug hack */ - if (!strcmp(typename, "str") || !strcmp(typename, "int")) - fprintf(out, ">>> "); - else - fprintf(out, " "); - /* XXX enhance */ for (i = 0; i < indent; i++) { if (i % 2) @@ -277,7 +339,7 @@ static void __ec_parsed_tk_dump(FILE *out, void ec_parsed_tk_dump(FILE *out, const struct ec_parsed_tk *parsed_tk) { - fprintf(out, "------------------- dump:\n"); + fprintf(out, "------------------- parsed_tk dump:\n"); //XXX if (parsed_tk == NULL) { fprintf(out, "parsed_tk is NULL, error in parse\n"); @@ -297,6 +359,12 @@ void ec_parsed_tk_add_child(struct ec_parsed_tk *parsed_tk, TAILQ_INSERT_TAIL(&parsed_tk->children, child, next); } +void ec_parsed_tk_del_child(struct ec_parsed_tk *parsed_tk, + struct ec_parsed_tk *child) +{ + TAILQ_REMOVE(&parsed_tk->children, child, next); +} + struct ec_parsed_tk *ec_parsed_tk_find_first(struct ec_parsed_tk *parsed_tk, const char *id) { @@ -388,7 +456,7 @@ struct ec_completed_tk_elt *ec_completed_tk_elt_new(const struct ec_tk *tk, * suggestion: tk->op() is internal, user calls the function * other idea: have 2 functions */ -struct ec_completed_tk *ec_tk_complete(const struct ec_tk *tk, +struct ec_completed_tk *ec_tk_complete(struct ec_tk *tk, const char *str) { struct ec_strvec *strvec = NULL; @@ -438,10 +506,29 @@ struct ec_completed_tk *ec_tk_default_complete(const struct ec_tk *gen_tk, return completed_tk; } -struct ec_completed_tk *ec_tk_complete_tokens(const struct ec_tk *tk, +struct ec_completed_tk *ec_tk_complete_tokens(struct ec_tk *tk, const struct ec_strvec *strvec) { - return tk->ops->complete(tk, strvec); + int ret; + + /* build the node if required */ + if (tk->type->build != NULL) { + if ((tk->flags & EC_TK_F_BUILT) == 0) { + ret = tk->type->build(tk); + if (ret < 0) { + errno = -ret; + return NULL; + } + } + } + tk->flags |= EC_TK_F_BUILT; + + if (tk->type->complete == NULL) { + errno = ENOTSUP; + return NULL; + } + + return tk->type->complete(tk, strvec); } /* count the number of identical chars at the beginning of 2 strings */ @@ -529,7 +616,7 @@ void ec_completed_tk_dump(FILE *out, const struct ec_completed_tk *completed_tk) TAILQ_FOREACH(elt, &completed_tk->elts, next) { fprintf(out, "add=<%s>, tk=%p, tk_type=%s\n", - elt->add, elt->tk, elt->tk->ops->typename); + elt->add, elt->tk, elt->tk->type->name); } } @@ -612,8 +699,8 @@ void ec_completed_tk_iter_free(struct ec_completed_tk_iter *iter) const char *ec_tk_desc(const struct ec_tk *tk) { - if (tk->ops->desc != NULL) - return tk->ops->desc(tk); + if (tk->type->desc != NULL) + return tk->type->desc(tk); return tk->desc; } diff --git a/lib/ecoli_tk.h b/lib/ecoli_tk.h index a86266c..f420a52 100644 --- a/lib/ecoli_tk.h +++ b/lib/ecoli_tk.h @@ -49,8 +49,23 @@ typedef struct ec_completed_tk *(*ec_tk_complete_t)(const struct ec_tk *tk, typedef const char * (*ec_tk_desc_t)(const struct ec_tk *); typedef void (*ec_tk_free_priv_t)(struct ec_tk *); -struct ec_tk_ops { - const char *typename; +#define EC_TK_TYPE_REGISTER(t) \ + static void ec_tk_init_##t(void); \ + static void __attribute__((constructor, used)) \ + ec_tk_init_##t(void) \ + { \ + if (ec_tk_type_register(&t) < 0) \ + fprintf(stderr, "cannot register %s\n", t.name); \ + } + +TAILQ_HEAD(ec_tk_type_list, ec_tk_type); + +/** + * A structure describing a tk type. + */ +struct ec_tk_type { + TAILQ_ENTRY(ec_tk_type) next; /**< Next in list. */ + const char *name; /**< Tk type name. */ ec_tk_build_t build; /* (re)build the node, called by generic parse */ ec_tk_parse_t parse; ec_tk_complete_t complete; @@ -58,10 +73,36 @@ struct ec_tk_ops { ec_tk_free_priv_t free_priv; }; +/** + * Register a token type. + * + * @param type + * A pointer to a ec_test structure describing the test + * to be registered. + * @return + * 0 on success, negative value on error. + */ +int ec_tk_type_register(struct ec_tk_type *type); + +/** + * Lookup token type by name + * + * @param name + * The name of the token type to search. + * @return + * The token type if found, or NULL on error. + */ +struct ec_tk_type *ec_tk_type_lookup(const char *name); + +/** + * Dump registered log types + */ +void ec_tk_type_dump(FILE *out); + TAILQ_HEAD(ec_tk_list, ec_tk); struct ec_tk { - const struct ec_tk_ops *ops; + const struct ec_tk_type *type; char *id; char *desc; struct ec_keyval *attrs; @@ -75,7 +116,7 @@ struct ec_tk { struct ec_tk_list children; }; -struct ec_tk *ec_tk_new(const char *id, const struct ec_tk_ops *ops, +struct ec_tk *ec_tk_new(const char *id, const struct ec_tk_type *type, size_t priv_size); void ec_tk_free(struct ec_tk *tk); @@ -83,7 +124,9 @@ void ec_tk_free(struct ec_tk *tk); struct ec_keyval *ec_tk_attrs(const struct ec_tk *tk); struct ec_tk *ec_tk_parent(const struct ec_tk *tk); const char *ec_tk_id(const struct ec_tk *tk); +const char *ec_tk_desc(const struct ec_tk *tk); +void ec_tk_dump(FILE *out, const struct ec_tk *tk); struct ec_tk *ec_tk_find(struct ec_tk *tk, const char *id); /* XXX split this file ? */ @@ -127,6 +170,8 @@ struct ec_parsed_tk *ec_tk_parse_tokens(struct ec_tk *tk, void ec_parsed_tk_add_child(struct ec_parsed_tk *parsed_tk, struct ec_parsed_tk *child); +void ec_parsed_tk_del_child(struct ec_parsed_tk *parsed_tk, + struct ec_parsed_tk *child); void ec_parsed_tk_dump(FILE *out, const struct ec_parsed_tk *parsed_tk); struct ec_parsed_tk *ec_parsed_tk_find_first(struct ec_parsed_tk *parsed_tk, @@ -156,9 +201,9 @@ struct ec_completed_tk { * return a completed_tk object filled with elts * return NULL on error (nomem?) */ -struct ec_completed_tk *ec_tk_complete(const struct ec_tk *tk, +struct ec_completed_tk *ec_tk_complete(struct ec_tk *tk, const char *str); -struct ec_completed_tk *ec_tk_complete_tokens(const struct ec_tk *tk, +struct ec_completed_tk *ec_tk_complete_tokens(struct ec_tk *tk, const struct ec_strvec *strvec); struct ec_completed_tk *ec_completed_tk_new(void); struct ec_completed_tk_elt *ec_completed_tk_elt_new(const struct ec_tk *tk, @@ -203,6 +248,4 @@ const struct ec_completed_tk_elt *ec_completed_tk_iter_next( void ec_completed_tk_iter_free(struct ec_completed_tk_iter *iter); -const char *ec_tk_desc(const struct ec_tk *tk); - #endif diff --git a/lib/ecoli_tk_cmd.c b/lib/ecoli_tk_cmd.c new file mode 100644 index 0000000..f5f1575 --- /dev/null +++ b/lib/ecoli_tk_cmd.c @@ -0,0 +1,499 @@ +/* + * Copyright (c) 2016-2017, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct ec_tk_cmd { + struct ec_tk gen; + char *cmd_str; /* the command string. */ + struct ec_tk *cmd; /* the command token. */ + struct ec_tk *lex; /* the lexer token. */ + struct ec_tk *expr; /* the expression parser. */ + struct ec_tk **table; /* table of tk referenced in command. */ + unsigned int len; /* len of the table. */ +}; + +static int +ec_tk_cmd_eval_var(void **result, void *userctx, + const struct ec_parsed_tk *var) +{ + const struct ec_strvec *vec; + struct ec_tk_cmd *tk = userctx; + struct ec_tk *eval = NULL; + const char *str, *id; + unsigned int i; + + (void)userctx; + + /* get parsed string vector, it should contain only one str */ + vec = ec_parsed_tk_strvec(var); + if (ec_strvec_len(vec) != 1) + return -EINVAL; + str = ec_strvec_val(vec, 0); + + for (i = 0; i < tk->len; i++) { + id = ec_tk_id(tk->table[i]); + printf("i=%d id=%s\n", i, id); + if (id == NULL) + continue; + if (strcmp(str, id)) + continue; + /* if id matches, use a tk provided by the user... */ + eval = ec_tk_clone(tk->table[i]); + if (eval == NULL) + return -ENOMEM; + break; + } + + /* ...or create a string token */ + if (eval == NULL) { + eval = ec_tk_str(NULL, str); + if (eval == NULL) + return -ENOMEM; + } + + printf("eval var %s %p\n", str, eval); + *result = eval; + + return 0; +} + +static int +ec_tk_cmd_eval_pre_op(void **result, void *userctx, void *operand, + const struct ec_parsed_tk *operator) +{ + (void)result; + (void)userctx; + (void)operand; + (void)operator; + + return -EINVAL; +} + +static int +ec_tk_cmd_eval_post_op(void **result, void *userctx, void *operand, + const struct ec_parsed_tk *operator) +{ + const struct ec_strvec *vec; + struct ec_tk *eval = operand;; + + (void)userctx; + + /* get parsed string vector, it should contain only one str */ + vec = ec_parsed_tk_strvec(operator); + if (ec_strvec_len(vec) != 1) + return -EINVAL; + + if (!strcmp(ec_strvec_val(vec, 0), "*")) + eval = NULL; //XXX + else + return -EINVAL; + + printf("eval post_op %p\n", eval); + *result = eval; + + return 0; +} + +static int +ec_tk_cmd_eval_bin_op(void **result, void *userctx, void *operand1, + const struct ec_parsed_tk *operator, void *operand2) + +{ + const struct ec_strvec *vec; + struct ec_tk *out = NULL; + struct ec_tk *in1 = operand1; + struct ec_tk *in2 = operand2; + + (void)userctx; + + printf("eval bin_op %p %p\n", in1, in2); + + /* get parsed string vector, it should contain only one str */ + vec = ec_parsed_tk_strvec(operator); + if (ec_strvec_len(vec) != 1) + return -EINVAL; + + if (!strcmp(ec_strvec_val(vec, 0), "|")) { + out = EC_TK_OR(NULL, ec_tk_clone(in1), ec_tk_clone(in2)); + if (out == NULL) + return -EINVAL; + ec_tk_free(in1); + ec_tk_free(in2); + *result = out; + } else if (!strcmp(ec_strvec_val(vec, 0), ",")) { + ec_tk_free(in1); + ec_tk_free(in2); + *result = NULL; //XXX + } else { + return -EINVAL; + } + + return 0; +} + +static int +ec_tk_cmd_eval_parenthesis(void **result, void *userctx, + const struct ec_parsed_tk *open_paren, + const struct ec_parsed_tk *close_paren, + void *value) +{ + const struct ec_strvec *vec; + struct ec_tk *in = value;; + struct ec_tk *out = NULL;; + + (void)userctx; + (void)close_paren; + + /* get parsed string vector, it should contain only one str */ + vec = ec_parsed_tk_strvec(open_paren); + if (ec_strvec_len(vec) != 1) + return -EINVAL; + + if (!strcmp(ec_strvec_val(vec, 0), "[")) { + out = ec_tk_option_new(NULL, ec_tk_clone(in)); + if (out == NULL) + return -EINVAL; + ec_tk_free(in); + } else { + return -EINVAL; + } + + printf("eval paren\n"); + *result = out; + + return 0; +} + +static void +ec_tk_cmd_eval_free(void *result, void *userctx) +{ + (void)userctx; + ec_free(result); +} + +static const struct ec_tk_expr_eval_ops test_ops = { + .eval_var = ec_tk_cmd_eval_var, + .eval_pre_op = ec_tk_cmd_eval_pre_op, + .eval_post_op = ec_tk_cmd_eval_post_op, + .eval_bin_op = ec_tk_cmd_eval_bin_op, + .eval_parenthesis = ec_tk_cmd_eval_parenthesis, + .eval_free = ec_tk_cmd_eval_free, +}; + +static struct ec_parsed_tk *ec_tk_cmd_parse(const struct ec_tk *gen_tk, + const struct ec_strvec *strvec) +{ + struct ec_tk_cmd *tk = (struct ec_tk_cmd *)gen_tk; + + return ec_tk_parse_tokens(tk->cmd, strvec); +} + +static struct ec_completed_tk *ec_tk_cmd_complete(const struct ec_tk *gen_tk, + const struct ec_strvec *strvec) +{ + struct ec_tk_cmd *tk = (struct ec_tk_cmd *)gen_tk; + + return ec_tk_complete_tokens(tk->cmd, strvec); +} + +static void ec_tk_cmd_free_priv(struct ec_tk *gen_tk) +{ + struct ec_tk_cmd *tk = (struct ec_tk_cmd *)gen_tk; + unsigned int i; + + ec_free(tk->cmd_str); + ec_tk_free(tk->cmd); + ec_tk_free(tk->expr); + ec_tk_free(tk->lex); + for (i = 0; i < tk->len; i++) + ec_tk_free(tk->table[i]); + ec_free(tk->table); +} + +static int ec_tk_cmd_build(struct ec_tk *gen_tk) +{ + struct ec_tk *expr = NULL, *lex = NULL, *cmd = NULL; + struct ec_parsed_tk *p, *child; + struct ec_tk_cmd *tk = (struct ec_tk_cmd *)gen_tk; + void *result; + int ret; + + /* build the expression parser */ + ret = -ENOMEM; + expr = ec_tk_expr("expr"); + if (expr == NULL) + goto fail; + ret = ec_tk_expr_set_val_tk(expr, ec_tk_re(NULL, "[a-zA-Z0-9]+")); + if (ret < 0) + goto fail; + ret = ec_tk_expr_add_bin_op(expr, ec_tk_str(NULL, ",")); + if (ret < 0) + goto fail; + ret = ec_tk_expr_add_bin_op(expr, ec_tk_str(NULL, "|")); + if (ret < 0) + goto fail; + ret = ec_tk_expr_add_post_op(expr, ec_tk_str(NULL, "+")); + if (ret < 0) + goto fail; + ret = ec_tk_expr_add_post_op(expr, ec_tk_str(NULL, "*")); + if (ret < 0) + goto fail; + ret = ec_tk_expr_add_parenthesis(expr, ec_tk_str(NULL, "["), + ec_tk_str(NULL, "]")); + if (ret < 0) + goto fail; + ec_tk_expr_add_parenthesis(expr, ec_tk_str(NULL, "("), + ec_tk_str(NULL, ")")); + if (ret < 0) + goto fail; + + /* prepend a lexer and a "many" to the expression token */ + ret = -ENOMEM; + lex = ec_tk_re_lex(NULL, + ec_tk_many(NULL, ec_tk_clone(expr), 1, 0)); + if (lex == NULL) + goto fail; + + ret = ec_tk_re_lex_add(lex, "[a-zA-Z0-9]+", 1); + if (ret < 0) + goto fail; + ret = ec_tk_re_lex_add(lex, "[*|,()]", 1); + if (ret < 0) + goto fail; + ret = ec_tk_re_lex_add(lex, "\\[", 1); + if (ret < 0) + goto fail; + ret = ec_tk_re_lex_add(lex, "\\]", 1); + if (ret < 0) + goto fail; + ret = ec_tk_re_lex_add(lex, "[ ]+", 0); + if (ret < 0) + goto fail; + + /* parse the command expression */ + ret = -ENOMEM; + p = ec_tk_parse(lex, tk->cmd_str); + if (p == NULL) + goto fail; + + ret = -EINVAL; + if (!ec_parsed_tk_matches(p)) + goto fail; + if (TAILQ_EMPTY(&p->children)) + goto fail; + if (TAILQ_EMPTY(&TAILQ_FIRST(&p->children)->children)) + goto fail; + + ret = -ENOMEM; + cmd = ec_tk_seq(NULL); + if (cmd == NULL) + goto fail; + + TAILQ_FOREACH(child, &TAILQ_FIRST(&p->children)->children, next) { + ret = ec_tk_expr_eval(&result, expr, child, + &test_ops, tk); + if (ret < 0) + goto fail; + ret = ec_tk_seq_add(cmd, result); + if (ret < 0) + goto fail; + } + ec_parsed_tk_free(p); + ec_tk_dump(stdout, cmd); + + ec_tk_free(tk->expr); + tk->expr = expr; + ec_tk_free(tk->lex); + tk->lex = lex; + ec_tk_free(tk->cmd); + tk->cmd = cmd; + + return 0; + +fail: + ec_tk_free(expr); + ec_tk_free(lex); + ec_tk_free(cmd); + return ret; +} + +static struct ec_tk_type ec_tk_cmd_type = { + .name = "cmd", + .build = ec_tk_cmd_build, + .parse = ec_tk_cmd_parse, + .complete = ec_tk_cmd_complete, + .free_priv = ec_tk_cmd_free_priv, +}; + +EC_TK_TYPE_REGISTER(ec_tk_cmd_type); + +int ec_tk_cmd_add_child(struct ec_tk *gen_tk, struct ec_tk *child) +{ + struct ec_tk_cmd *tk = (struct ec_tk_cmd *)gen_tk; + struct ec_tk **table; + + // XXX check tk type + + assert(tk != NULL); + + printf("add child %s\n", child->id); + if (child == NULL) + return -EINVAL; + + gen_tk->flags &= ~EC_TK_F_BUILT; + + table = ec_realloc(tk->table, (tk->len + 1) * sizeof(*tk->table)); + if (table == NULL) { + ec_tk_free(child); + return -ENOMEM; + } + + tk->table = table; + table[tk->len] = child; + tk->len++; + + child->parent = gen_tk; + TAILQ_INSERT_TAIL(&gen_tk->children, child, next); // XXX really needed? + + return 0; +} + +struct ec_tk *ec_tk_cmd(const char *id, const char *cmd_str) +{ + struct ec_tk *gen_tk = NULL; + struct ec_tk_cmd *tk = NULL; + + gen_tk = ec_tk_new(id, &ec_tk_cmd_type, sizeof(*tk)); + if (gen_tk == NULL) + goto fail; + + tk = (struct ec_tk_cmd *)gen_tk; + tk->cmd_str = ec_strdup(cmd_str); + if (tk->cmd_str == NULL) + goto fail; + + return gen_tk; + +fail: + ec_tk_free(gen_tk); + return NULL; +} + +struct ec_tk *__ec_tk_cmd(const char *id, const char *cmd, ...) +{ + struct ec_tk *gen_tk = NULL; + struct ec_tk_cmd *tk = NULL; + struct ec_tk *child; + va_list ap; + int fail = 0; + + va_start(ap, cmd); + + gen_tk = ec_tk_cmd(id, cmd); + tk = (struct ec_tk_cmd *)gen_tk; + if (tk == NULL) + fail = 1;; + + for (child = va_arg(ap, struct ec_tk *); + child != EC_TK_ENDLIST; + child = va_arg(ap, struct ec_tk *)) { + + /* on error, don't quit the loop to avoid leaks */ + if (fail == 1 || child == NULL || + ec_tk_cmd_add_child(&tk->gen, child) < 0) { + fail = 1; + ec_tk_free(child); + } + } + + if (fail == 1) + goto fail; + + va_end(ap); + return gen_tk; + +fail: + ec_tk_free(gen_tk); /* will also free children */ + va_end(ap); + return NULL; +} + +static int ec_tk_cmd_testcase(void) +{ + struct ec_tk *tk; + int ret = 0; + + tk = EC_TK_CMD(NULL, + "add [toto] x | y", + ec_tk_int("x", 0, 10, 10), + ec_tk_int("y", 20, 30, 10) + ); + if (tk == NULL) { + ec_log(EC_LOG_ERR, "cannot create tk\n"); + return -1; + } + ret |= EC_TEST_CHECK_TK_PARSE(tk, 2, "add", "1"); + ret |= EC_TEST_CHECK_TK_PARSE(tk, 2, "add", "23"); + ret |= EC_TEST_CHECK_TK_PARSE(tk, 3, "add", "toto", "23"); + ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "add", "15"); + ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "foo"); + ec_tk_free(tk); + + // XXX completion + + return ret; +} + +static struct ec_test ec_tk_cmd_test = { + .name = "tk_cmd", + .test = ec_tk_cmd_testcase, +}; + +EC_TEST_REGISTER(ec_tk_cmd_test); diff --git a/lib/ecoli_tk_cmd.h b/lib/ecoli_tk_cmd.h new file mode 100644 index 0000000..f324ff4 --- /dev/null +++ b/lib/ecoli_tk_cmd.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2016-2017, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ECOLI_TK_CMD_ +#define ECOLI_TK_CMD_ + +#include + +#define EC_TK_CMD(args...) __ec_tk_cmd(args, EC_TK_ENDLIST) + +struct ec_tk *__ec_tk_cmd(const char *id, const char *cmd_str, ...); + +struct ec_tk *ec_tk_cmd(const char *id, const char *cmd_str); + +/* child is consumed */ +int ec_tk_cmd_add_child(struct ec_tk *tk, struct ec_tk *child); + +#endif diff --git a/lib/ecoli_tk_empty.c b/lib/ecoli_tk_empty.c index ee6a4e6..9077c44 100644 --- a/lib/ecoli_tk_empty.c +++ b/lib/ecoli_tk_empty.c @@ -65,15 +65,17 @@ static struct ec_parsed_tk *ec_tk_empty_parse(const struct ec_tk *gen_tk, return NULL; } -static struct ec_tk_ops ec_tk_empty_ops = { - .typename = "empty", +static struct ec_tk_type ec_tk_empty_type = { + .name = "empty", .parse = ec_tk_empty_parse, .complete = ec_tk_default_complete, }; +EC_TK_TYPE_REGISTER(ec_tk_empty_type); + struct ec_tk *ec_tk_empty_new(const char *id) { - return ec_tk_new(id, &ec_tk_empty_ops, + return ec_tk_new(id, &ec_tk_empty_type, sizeof(struct ec_tk_empty)); } @@ -116,4 +118,4 @@ static struct ec_test ec_tk_empty_test = { .test = ec_tk_empty_testcase, }; -EC_REGISTER_TEST(ec_tk_empty_test); +EC_TEST_REGISTER(ec_tk_empty_test); diff --git a/lib/ecoli_tk_expr.c b/lib/ecoli_tk_expr.c index 7f1c3cb..40dbd1b 100644 --- a/lib/ecoli_tk_expr.c +++ b/lib/ecoli_tk_expr.c @@ -219,20 +219,22 @@ fail: return ret; } -static struct ec_tk_ops ec_tk_expr_ops = { - .typename = "expr", +static struct ec_tk_type ec_tk_expr_type = { + .name = "expr", .build = ec_tk_expr_build, .parse = ec_tk_expr_parse, .complete = ec_tk_expr_complete, .free_priv = ec_tk_expr_free_priv, }; +EC_TK_TYPE_REGISTER(ec_tk_expr_type); + struct ec_tk *ec_tk_expr(const char *id) { struct ec_tk_expr *tk = NULL; struct ec_tk *gen_tk = NULL; - gen_tk = ec_tk_new(id, &ec_tk_expr_ops, sizeof(*tk)); + gen_tk = ec_tk_new(id, &ec_tk_expr_type, sizeof(*tk)); if (gen_tk == NULL) return NULL; tk = (struct ec_tk_expr *)gen_tk; @@ -415,7 +417,8 @@ enum expr_tk_type { BIN_OP, PRE_OP, POST_OP, - PAREN, + PAREN_OPEN, + PAREN_CLOSE, }; static enum expr_tk_type get_tk_type(const struct ec_tk *expr_gen_tk, const struct ec_tk *check_tk) @@ -439,190 +442,169 @@ static enum expr_tk_type get_tk_type(const struct ec_tk *expr_gen_tk, return POST_OP; } - /* for (i = 0; i < expr_tk->paren_len; i++) { */ - /* if (check_tk == expr_tk->open_ops[i]) */ - /* return PAREN; */ - /* } */ + for (i = 0; i < expr_tk->paren_len; i++) { + if (check_tk == expr_tk->open_ops[i]) + return PAREN_OPEN; + } + for (i = 0; i < expr_tk->paren_len; i++) { + if (check_tk == expr_tk->close_ops[i]) + return PAREN_CLOSE; + } return NONE; } -struct eval_state { - void *userctx; - bool has_lval; - struct ec_tk_expr_eval_result lval; - const struct ec_parsed_tk **op_stack; - size_t op_stack_size; - size_t op_stack_len; +struct result { + bool has_val; + void *val; + const struct ec_parsed_tk *op; + enum expr_tk_type op_type; }; -static struct eval_state *eval_state_new(void *userctx) -{ - struct eval_state *state; - - state = ec_calloc(1, sizeof(*state)); - state->userctx = userctx; - return state; -} - -static void eval_state_free(struct eval_state *state) -{ - if (state == NULL) - return; - ec_free(state->op_stack); - ec_free(state); -} - -static int eval_state_add_val(struct eval_state *state, +/* merge x and y results in x */ +static int merge_results(void *userctx, const struct ec_tk_expr_eval_ops *ops, - const struct ec_tk *expr_gen_tk, - const struct ec_parsed_tk *parsed_tk) + struct result *x, const struct result *y) { - struct ec_tk_expr_eval_result val; - const struct ec_parsed_tk *op_parsed_tk; - enum expr_tk_type type; + int ret; - (void)ops; - (void)state; - printf("push val: "); - ec_parsed_tk_dump(stdout, parsed_tk); + if (y->has_val == 0 && y->op == NULL) + return 0; + if (x->has_val == 0 && x->op == NULL) { + *x = *y; + return 0; + } - val = ops->eval_var(state->userctx, parsed_tk); - if (val.code != 0) - return val.code; // XXX free val? + if (x->has_val && x->op == NULL && y->has_val && y->op != NULL) { + ret = ops->eval_bin_op(&x->val, userctx, x->val, y->op, y->val); + if (ret < 0) + return ret; - /* first value, it is the left operand */ - if (state->has_lval == 0) { - state->lval = val; - state->has_lval = 1; return 0; } - /* if we have a right operand without operator... fail */ - if (state->op_stack_len == 0) - return -EINVAL; // XXX free val? - - while (state->op_stack_len > 0) { - state->op_stack_len--; - op_parsed_tk = state->op_stack[state->op_stack_len]; - type = get_tk_type(expr_gen_tk, op_parsed_tk->tk); - if (type == PRE_OP) { - state->lval = ops->eval_pre_op(state->userctx, - val, op_parsed_tk); - if (val.code != 0) - return val.code; // XXX free val? - } else if (type == BIN_OP) { - state->lval = ops->eval_bin_op(state->userctx, - state->lval, op_parsed_tk, val); - if (state->lval.code != 0) - return state->lval.code; // XXX free val? - break; - } else { - // ? + if (x->has_val == 0 && x->op != NULL && y->has_val && y->op == NULL) { + if (x->op_type == PRE_OP) { + ret = ops->eval_pre_op(&x->val, userctx, y->val, x->op); + if (ret < 0) + return ret; + x->has_val = true; + x->op_type = NONE; + x->op = NULL; + return 0; + } else if (x->op_type == BIN_OP) { + x->val = y->val; + x->has_val = true; + return 0; } } - return 0; -} - -static int eval_state_add_op(struct eval_state *state, - const struct ec_tk_expr_eval_ops *ops, - const struct ec_tk *expr_gen_tk, - const struct ec_parsed_tk *parsed_tk) -{ - enum expr_tk_type type; - - (void)ops; - (void)state; - - printf("push op: "); - ec_parsed_tk_dump(stdout, parsed_tk); + if (x->has_val && x->op == NULL && y->has_val == 0 && y->op != NULL) { + ret = ops->eval_post_op(&x->val, userctx, x->val, y->op); + if (ret < 0) + return ret; - type = get_tk_type(expr_gen_tk, parsed_tk->tk); - if (type == POST_OP) { - if (state->has_lval == 0) - return -EINVAL; - state->lval = ops->eval_post_op(state->userctx, - state->lval, parsed_tk); - if (state->lval.code != 0) - return state->lval.code; return 0; } - if (state->op_stack_len >= state->op_stack_size) { - const struct ec_parsed_tk **op_stack; - - op_stack = ec_realloc(state->op_stack, - (state->op_stack_len + 1) * sizeof(*op_stack)); - if (op_stack == NULL) - return -ENOMEM; - state->op_stack = op_stack; - state->op_stack_size = state->op_stack_len + 1; - } - - state->op_stack[state->op_stack_len] = parsed_tk; - state->op_stack_len++; - - return 0; + assert(true); /* we should not get here */ + return -EINVAL; } -static int eval_expression(struct eval_state *state, +static int eval_expression(struct result *result, + void *userctx, const struct ec_tk_expr_eval_ops *ops, const struct ec_tk *expr_gen_tk, const struct ec_parsed_tk *parsed_tk) { + struct ec_parsed_tk *open = NULL, *close = NULL; + struct result child_result; struct ec_parsed_tk *child; enum expr_tk_type type; int ret; + memset(result, 0, sizeof(*result)); + memset(&child_result, 0, sizeof(child_result)); + type = get_tk_type(expr_gen_tk, parsed_tk->tk); if (type == VAL) { - ret = eval_state_add_val(state, ops, expr_gen_tk, parsed_tk); - if (ret < 0) - return ret; - } else if (type != NONE) { - ret = eval_state_add_op(state, ops, expr_gen_tk, parsed_tk); + ret = ops->eval_var(&result->val, userctx, parsed_tk); if (ret < 0) - return ret; + goto fail; + result->has_val = 1; + } else if (type == PRE_OP || type == POST_OP || type == BIN_OP) { + result->op = parsed_tk; + result->op_type = type; } TAILQ_FOREACH(child, &parsed_tk->children, next) { - ret = eval_expression(state, ops, expr_gen_tk, child); + + type = get_tk_type(expr_gen_tk, child->tk); + if (type == PAREN_OPEN) { + open = child; + continue; + } else if (type == PAREN_CLOSE) { + close = child; + continue; + } + + ret = eval_expression(&child_result, userctx, ops, + expr_gen_tk, child); if (ret < 0) - return ret; + goto fail; + + ret = merge_results(userctx, ops, result, &child_result); + if (ret < 0) + goto fail; + + memset(&child_result, 0, sizeof(child_result)); + } + + if (open != NULL && close != NULL) { + ret = ops->eval_parenthesis(&result->val, userctx, open, close, + result->val); + if (ret < 0) + goto fail; } return 0; + +fail: + if (result->has_val) + ops->eval_free(result->val, userctx); + if (child_result.has_val) + ops->eval_free(child_result.val, userctx); + memset(result, 0, sizeof(*result)); + + return ret; } -struct ec_tk_expr_eval_result ec_tk_expr_eval(const struct ec_tk *tk, +int ec_tk_expr_eval(void **user_result, const struct ec_tk *tk, struct ec_parsed_tk *parsed, const struct ec_tk_expr_eval_ops *ops, void *userctx) { - struct ec_tk_expr_eval_result res = { -EINVAL, NULL }; - struct eval_state *state; + struct result result; int ret; - if (ops == NULL) - return res; - - ec_parsed_tk_dump(stdout, parsed); + if (ops == NULL || ops->eval_var == NULL || ops->eval_pre_op == NULL || + ops->eval_post_op == NULL || ops->eval_bin_op == NULL || + ops->eval_parenthesis == NULL || ops->eval_free == NULL) + return -EINVAL; - res.code = -ENOMEM; - state = eval_state_new(&userctx); - if (state == NULL) - return res; + if (!ec_parsed_tk_matches(parsed)) + return -EINVAL; - ret = eval_expression(state, ops, tk, parsed); -// XXX check if op stack len is 0 ? - res = state->lval; - eval_state_free(state); + ec_parsed_tk_dump(stdout, parsed); //XXX + ret = eval_expression(&result, userctx, ops, tk, parsed); + if (ret < 0) + return ret; - if (ret < 0 && res.code == 0) - res.code = ret; + assert(result.has_val); + assert(result.op == NULL); + *user_result = result.val; - return res; + return 0; } /* the test case is in a separate file ecoli_tk_expr_test.c */ diff --git a/lib/ecoli_tk_expr.h b/lib/ecoli_tk_expr.h index d24e0a2..d2efc24 100644 --- a/lib/ecoli_tk_expr.h +++ b/lib/ecoli_tk_expr.h @@ -32,32 +32,66 @@ /* XXX remove the _new for all other tokens */ -/* XXX explain the free policy in case of error or success */ -struct ec_tk_expr_eval_result { - int code; /* 0 on success, negative on error */ - void *parsed; -}; - -typedef struct ec_tk_expr_eval_result (*ec_tk_expr_eval_var_t)( - void *userctx, const struct ec_parsed_tk *var); +/** + * Callback function type for evaluating a variable + * + * @param result + * On success, this pointer must be set by the user to point + * to a user structure describing the evaluated result. + * @param userctx + * A user-defined context passed to all callback functions, which + * can be used to maintain a state or store global information. + * @param var + * The parsed token referencing the variable. + * @return + * 0 on success (*result must be set), or -errno on error (*result + * is undefined). + */ +typedef int (*ec_tk_expr_eval_var_t)( + void **result, void *userctx, + const struct ec_parsed_tk *var); -typedef struct ec_tk_expr_eval_result (*ec_tk_expr_eval_pre_op_t)( - void *userctx, struct ec_tk_expr_eval_result operand, +/** + * Callback function type for evaluating a prefix-operator + * + * @param result + * On success, this pointer must be set by the user to point + * to a user structure describing the evaluated result. + * @param userctx + * A user-defined context passed to all callback functions, which + * can be used to maintain a state or store global information. + * @param operand + * The evaluated expression on which the operation should be applied. + * @param var + * The parsed token referencing the operator. + * @return + * 0 on success (*result must be set, operand is freed), + * or -errno on error (*result is undefined, operand is not freed). + */ +typedef int (*ec_tk_expr_eval_pre_op_t)( + void **result, void *userctx, + void *operand, const struct ec_parsed_tk *operator); -typedef struct ec_tk_expr_eval_result (*ec_tk_expr_eval_post_op_t)( - void *userctx, struct ec_tk_expr_eval_result operand, +typedef int (*ec_tk_expr_eval_post_op_t)( + void **result, void *userctx, + void *operand, const struct ec_parsed_tk *operator); -typedef struct ec_tk_expr_eval_result (*ec_tk_expr_eval_bin_op_t)( - void *userctx, struct ec_tk_expr_eval_result operand1, +typedef int (*ec_tk_expr_eval_bin_op_t)( + void **result, void *userctx, + void *operand1, const struct ec_parsed_tk *operator, - struct ec_tk_expr_eval_result operand2); + void *operand2); + +typedef int (*ec_tk_expr_eval_parenthesis_t)( + void **result, void *userctx, + const struct ec_parsed_tk *open_paren, + const struct ec_parsed_tk *close_paren, + void * value); -typedef struct ec_tk_expr_eval_result (*ec_tk_expr_eval_parenthesis_t)( - void *userctx, const struct ec_parsed_tk *operator_str, - const struct ec_parsed_tk *operand_str, - void *operand); +typedef void (*ec_tk_expr_eval_free_t)( + void *result, void *userctx); struct ec_tk *ec_tk_expr(const char *id); @@ -74,9 +108,10 @@ struct ec_tk_expr_eval_ops { ec_tk_expr_eval_post_op_t eval_post_op; ec_tk_expr_eval_bin_op_t eval_bin_op; ec_tk_expr_eval_parenthesis_t eval_parenthesis; + ec_tk_expr_eval_free_t eval_free; }; -struct ec_tk_expr_eval_result ec_tk_expr_eval(const struct ec_tk *tk, +int ec_tk_expr_eval(void **result, const struct ec_tk *tk, struct ec_parsed_tk *parsed, const struct ec_tk_expr_eval_ops *ops, void *userctx); diff --git a/lib/ecoli_tk_expr_test.c b/lib/ecoli_tk_expr_test.c index 858ffcf..be28797 100644 --- a/lib/ecoli_tk_expr_test.c +++ b/lib/ecoli_tk_expr_test.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -41,11 +42,11 @@ struct my_eval_result { int val; }; -static struct ec_tk_expr_eval_result -ec_tk_expr_test_eval_var(void *userctx, const struct ec_parsed_tk *var) +static int +ec_tk_expr_test_eval_var(void **result, void *userctx, + const struct ec_parsed_tk *var) { const struct ec_strvec *vec; - struct ec_tk_expr_eval_result res = { .code = -EINVAL, .parsed = NULL }; struct my_eval_result *eval; (void)userctx; @@ -53,122 +54,120 @@ ec_tk_expr_test_eval_var(void *userctx, const struct ec_parsed_tk *var) /* get parsed string vector, it should contain only one str */ vec = ec_parsed_tk_strvec(var); if (ec_strvec_len(vec) != 1) - return res; + return -EINVAL; - res.code = -ENOMEM; eval = ec_malloc(sizeof(*eval)); if (eval == NULL) - return res; + return -ENOMEM; eval->val = atoi(ec_strvec_val(vec, 0)); // XXX use strtol printf("eval var %d\n", eval->val); - res.code = 0; - res.parsed = eval; + *result = eval; - return res; + return 0; } -static struct ec_tk_expr_eval_result -ec_tk_expr_test_eval_pre_op(void *userctx, - struct ec_tk_expr_eval_result operand, +static int +ec_tk_expr_test_eval_pre_op(void **result, void *userctx, void *operand, const struct ec_parsed_tk *operator) { const struct ec_strvec *vec; - struct ec_tk_expr_eval_result res = { .code = -EINVAL, .parsed = NULL }; - struct my_eval_result *eval = operand.parsed;; + struct my_eval_result *eval = operand;; (void)userctx; /* get parsed string vector, it should contain only one str */ vec = ec_parsed_tk_strvec(operator); - if (ec_strvec_len(vec) != 1) { - ec_free(eval); - return res; - } + if (ec_strvec_len(vec) != 1) + return -EINVAL; - if (!strcmp(ec_strvec_val(vec, 0), "~")) { + if (!strcmp(ec_strvec_val(vec, 0), "!")) eval->val = !eval->val; - } else { - ec_free(eval); - return res; - } + else + return -EINVAL; printf("eval pre_op %d\n", eval->val); - res.code = 0; - res.parsed = eval; + *result = eval; - return res; + return 0; } -static struct ec_tk_expr_eval_result -ec_tk_expr_test_eval_post_op(void *userctx, - struct ec_tk_expr_eval_result operand, +static int +ec_tk_expr_test_eval_post_op(void **result, void *userctx, void *operand, const struct ec_parsed_tk *operator) { const struct ec_strvec *vec; - struct ec_tk_expr_eval_result res = { .code = -EINVAL, .parsed = NULL }; - struct my_eval_result *eval = operand.parsed;; + struct my_eval_result *eval = operand;; (void)userctx; /* get parsed string vector, it should contain only one str */ vec = ec_parsed_tk_strvec(operator); - if (ec_strvec_len(vec) != 1) { - ec_free(eval); - return res; - } + if (ec_strvec_len(vec) != 1) + return -EINVAL; - if (!strcmp(ec_strvec_val(vec, 0), "!")) { + if (!strcmp(ec_strvec_val(vec, 0), "^")) eval->val = eval->val * eval->val; - } else { - ec_free(eval); - return res; - } + else + return -EINVAL; printf("eval post_op %d\n", eval->val); - res.code = 0; - res.parsed = eval; + *result = eval; - return res; + return 0; } -static struct ec_tk_expr_eval_result -ec_tk_expr_test_eval_bin_op(void *userctx, - struct ec_tk_expr_eval_result operand1, - const struct ec_parsed_tk *operator, - struct ec_tk_expr_eval_result operand2) +static int +ec_tk_expr_test_eval_bin_op(void **result, void *userctx, void *operand1, + const struct ec_parsed_tk *operator, void *operand2) { const struct ec_strvec *vec; - struct ec_tk_expr_eval_result res = { .code = -EINVAL, .parsed = NULL }; - struct my_eval_result *eval1 = operand1.parsed;; - struct my_eval_result *eval2 = operand2.parsed;; + struct my_eval_result *eval1 = operand1;; + struct my_eval_result *eval2 = operand2;; (void)userctx; /* get parsed string vector, it should contain only one str */ vec = ec_parsed_tk_strvec(operator); - if (ec_strvec_len(vec) != 1) { - ec_free(eval1); - ec_free(eval2); - return res; - } + if (ec_strvec_len(vec) != 1) + return -EINVAL; - if (!strcmp(ec_strvec_val(vec, 0), "+")) { + if (!strcmp(ec_strvec_val(vec, 0), "+")) eval1->val = eval1->val + eval2->val; - } else if (!strcmp(ec_strvec_val(vec, 0), "*")) { + else if (!strcmp(ec_strvec_val(vec, 0), "*")) eval1->val = eval1->val * eval2->val; - } else { - ec_free(eval1); - ec_free(eval2); - return res; - } + else + return -EINVAL; printf("eval bin_op %d\n", eval1->val); - res.code = 0; - res.parsed = eval1; + ec_free(eval2); + *result = eval1; + + return 0; +} + +static int +ec_tk_expr_test_eval_parenthesis(void **result, void *userctx, + const struct ec_parsed_tk *open_paren, + const struct ec_parsed_tk *close_paren, + void *value) +{ + (void)userctx; + (void)open_paren; + (void)close_paren; + + printf("eval paren\n"); + *result = value; + + return 0; +} - return res; +static void +ec_tk_expr_test_eval_free(void *result, void *userctx) +{ + (void)userctx; + ec_free(result); } static const struct ec_tk_expr_eval_ops test_ops = { @@ -176,40 +175,40 @@ static const struct ec_tk_expr_eval_ops test_ops = { .eval_pre_op = ec_tk_expr_test_eval_pre_op, .eval_post_op = ec_tk_expr_test_eval_post_op, .eval_bin_op = ec_tk_expr_test_eval_bin_op, + .eval_parenthesis = ec_tk_expr_test_eval_parenthesis, + .eval_free = ec_tk_expr_test_eval_free, }; static int ec_tk_expr_test_eval(struct ec_tk *lex_tk, const struct ec_tk *expr_tk, const char *str, int val) { - struct ec_tk_expr_eval_result res; struct ec_parsed_tk *p; + void *result; struct my_eval_result *eval; int ret; /* XXX check tk type (again and again) */ - p = ec_tk_parse(lex_tk, str); if (p == NULL) return -1; - res = ec_tk_expr_eval(expr_tk, p, &test_ops, NULL); + ret = ec_tk_expr_eval(&result, expr_tk, p, &test_ops, NULL); ec_parsed_tk_free(p); - if (res.code < 0) - return res.code; + if (ret < 0) + return -1; /* the parsed value is an integer */ - eval = res.parsed; - if (eval == NULL) { + eval = result; + assert(eval != NULL); + + printf("result: %d (expected %d)\n", eval->val, val); + if (eval->val == val) + ret = 0; + else ret = -1; - } else { - printf("result: %d (expected %d)\n", eval->val, val); - if (eval->val == val) - ret = 0; - else - ret = -1; - } + ec_free(eval); return ret; @@ -227,42 +226,42 @@ static int ec_tk_expr_testcase(void) ec_tk_expr_set_val_tk(tk, ec_tk_int(NULL, 0, UCHAR_MAX, 0)); ec_tk_expr_add_bin_op(tk, ec_tk_str(NULL, "+")); ec_tk_expr_add_bin_op(tk, ec_tk_str(NULL, "*")); - ec_tk_expr_add_pre_op(tk, ec_tk_str(NULL, "~")); - ec_tk_expr_add_post_op(tk, ec_tk_str(NULL, "!")); + ec_tk_expr_add_pre_op(tk, ec_tk_str(NULL, "!")); /* not */ + ec_tk_expr_add_post_op(tk, ec_tk_str(NULL, "^")); /* square */ ec_tk_expr_add_parenthesis(tk, ec_tk_str(NULL, "("), ec_tk_str(NULL, ")")); ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "1"); + ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "1", "1"); ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "1", "*"); ret |= EC_TEST_CHECK_TK_PARSE(tk, 3, "1", "*", "1"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 4, "1", "+", "~", "1"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 4, "1", "!", "+", "1"); + ret |= EC_TEST_CHECK_TK_PARSE(tk, 3, "1", "*", "1", "*"); + ret |= EC_TEST_CHECK_TK_PARSE(tk, 4, "1", "+", "!", "1"); + ret |= EC_TEST_CHECK_TK_PARSE(tk, 4, "1", "^", "+", "1"); ret |= EC_TEST_CHECK_TK_PARSE(tk, 5, "1", "*", "1", "*", "1"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 5, "1", "*", "1", "*", "1", "*", "1"); ret |= EC_TEST_CHECK_TK_PARSE(tk, 5, "1", "*", "1", "+", "1"); + ret |= EC_TEST_CHECK_TK_PARSE(tk, 7, "1", "*", "1", "*", "1", "*", "1"); ret |= EC_TEST_CHECK_TK_PARSE( - tk, 10, "~", "(", "1", "*", "(", "1", "+", "1", ")", ")"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 5, "1", "+", "~", "1", "!"); + tk, 10, "!", "(", "1", "*", "(", "1", "+", "1", ")", ")"); + ret |= EC_TEST_CHECK_TK_PARSE(tk, 5, "1", "+", "!", "1", "^"); /* prepend a lexer to the expression token */ lex_tk = ec_tk_re_lex(NULL, ec_tk_clone(tk)); if (lex_tk == NULL) goto fail; - ret |= ec_tk_re_lex_add(lex_tk, "^[0-9]+", 1); /* vars */ - ret |= ec_tk_re_lex_add(lex_tk, "^[+*~!()]", 1); /* operators */ - ret |= ec_tk_re_lex_add(lex_tk, "^[ ]+", 0); /* spaces */ - if (ret != 0) - goto fail; + ret |= ec_tk_re_lex_add(lex_tk, "[0-9]+", 1); /* vars */ + ret |= ec_tk_re_lex_add(lex_tk, "[+*!^()]", 1); /* operators */ + ret |= ec_tk_re_lex_add(lex_tk, "[ ]+", 0); /* spaces */ /* valid expressions */ - ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "~1"); - ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "1!"); - ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "1! + 1"); - ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "1 + 4 * (2 + 3!)!"); + ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "!1"); + ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "1^"); + ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "1^ + 1"); + ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "1 + 4 * (2 + 3^)^"); ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "(1)"); - ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "3*~3+~3*(2+ 2)"); - ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "~~(~1)! + ~(4 + (2*3))"); - ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "(1 + 1)! * 1!"); + ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "3*!3+!3*(2+ 2)"); + ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "!!(!1)^ + !(4 + (2*3))"); + ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "(1 + 1)^ * 1^"); /* invalid expressions */ ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, -1, ""); @@ -273,31 +272,18 @@ static int ec_tk_expr_testcase(void) ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, -1, "1+"); ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, -1, "1+*1"); ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, -1, "1+(1*1"); - ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, -1, "1+~1~1)"); - if (ret) - exit(ret); + ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, -1, "1+!1!1)"); + + ret |= ec_tk_expr_test_eval(lex_tk, tk, "1^", 1); + ret |= ec_tk_expr_test_eval(lex_tk, tk, "2^", 4); + ret |= ec_tk_expr_test_eval(lex_tk, tk, "!1", 0); + ret |= ec_tk_expr_test_eval(lex_tk, tk, "!0", 1); - ret |= ec_tk_expr_test_eval(lex_tk, tk, "2!", 4); ret |= ec_tk_expr_test_eval(lex_tk, tk, "1+1", 2); ret |= ec_tk_expr_test_eval(lex_tk, tk, "1+1*2", 4); - - // XXX marche pas: - // le "*" s'applique avant le "!" final - ret |= ec_tk_expr_test_eval(lex_tk, tk, "2 * 2!", 8); - /* ret |= ec_tk_expr_test_eval(lex_tk, tk, "(1 + ~0)! * ~0!", 4); */ - /* ret |= ec_tk_expr_test_eval(lex_tk, tk, "(1 + ~1) * 3", 3); */ - /* ret |= ec_tk_expr_test_eval(lex_tk, tk, "~1", 0); */ - printf("exit\n"); - exit(ret); - - /* - idee: - eval_expression() retourne soit: - - NONE - - *_OP, parsed_tk - - VAL, eval(parsed_tk) - */ - + ret |= ec_tk_expr_test_eval(lex_tk, tk, "2 * 2^", 8); + ret |= ec_tk_expr_test_eval(lex_tk, tk, "(1 + !0)^ * !0^", 4); + ret |= ec_tk_expr_test_eval(lex_tk, tk, "(1 + !1) * 3", 3); ec_tk_free(tk); ec_tk_free(lex_tk); @@ -315,4 +301,4 @@ static struct ec_test ec_tk_expr_test = { .test = ec_tk_expr_testcase, }; -EC_REGISTER_TEST(ec_tk_expr_test); +EC_TEST_REGISTER(ec_tk_expr_test); diff --git a/lib/ecoli_tk_int.c b/lib/ecoli_tk_int.c index bd0155d..a3510b3 100644 --- a/lib/ecoli_tk_int.c +++ b/lib/ecoli_tk_int.c @@ -101,19 +101,21 @@ static struct ec_parsed_tk *ec_tk_int_parse(const struct ec_tk *gen_tk, return NULL; } -static struct ec_tk_ops ec_tk_int_ops = { - .typename = "int", +static struct ec_tk_type ec_tk_int_type = { + .name = "int", .parse = ec_tk_int_parse, .complete = ec_tk_default_complete, }; +EC_TK_TYPE_REGISTER(ec_tk_int_type); + struct ec_tk *ec_tk_int(const char *id, long long int min, long long int max, unsigned int base) { struct ec_tk *gen_tk = NULL; struct ec_tk_int *tk = NULL; - gen_tk = ec_tk_new(id, &ec_tk_int_ops, sizeof(*tk)); + gen_tk = ec_tk_new(id, &ec_tk_int_type, sizeof(*tk)); if (gen_tk == NULL) return NULL; tk = (struct ec_tk_int *)gen_tk; @@ -225,4 +227,4 @@ static struct ec_test ec_tk_int_test = { .test = ec_tk_int_testcase, }; -EC_REGISTER_TEST(ec_tk_int_test); +EC_TEST_REGISTER(ec_tk_int_test); diff --git a/lib/ecoli_tk_int.h b/lib/ecoli_tk_int.h index b7fa6a6..156ebf1 100644 --- a/lib/ecoli_tk_int.h +++ b/lib/ecoli_tk_int.h @@ -30,6 +30,8 @@ #include +// XXX remove min, max, base from new(), and add ec_tk_int_set_limits() + +// XXX ec_tk_int_set_base() ? struct ec_tk *ec_tk_int(const char *id, long long int min, long long int max, unsigned int base); long long ec_tk_int_getval(struct ec_tk *tk, const char *str); diff --git a/lib/ecoli_tk_many.c b/lib/ecoli_tk_many.c index ecd54b8..09e5b51 100644 --- a/lib/ecoli_tk_many.c +++ b/lib/ecoli_tk_many.c @@ -109,7 +109,7 @@ fail: return NULL; } -#if 0 +#if 0 //XXX missing tk_many_complete static struct ec_completed_tk *ec_tk_many_complete(const struct ec_tk *gen_tk, const struct ec_strvec *strvec) { @@ -172,14 +172,16 @@ static void ec_tk_many_free_priv(struct ec_tk *gen_tk) ec_tk_free(tk->child); } -static struct ec_tk_ops ec_tk_many_ops = { - .typename = "many", +static struct ec_tk_type ec_tk_many_type = { + .name = "many", .parse = ec_tk_many_parse, .complete = ec_tk_default_complete, //XXX .complete = ec_tk_many_complete, .free_priv = ec_tk_many_free_priv, }; +EC_TK_TYPE_REGISTER(ec_tk_many_type); + struct ec_tk *ec_tk_many(const char *id, struct ec_tk *child, unsigned int min, unsigned int max) { @@ -188,7 +190,7 @@ struct ec_tk *ec_tk_many(const char *id, struct ec_tk *child, if (child == NULL) return NULL; - tk = (struct ec_tk_many *)ec_tk_new(id, &ec_tk_many_ops, + tk = (struct ec_tk_many *)ec_tk_new(id, &ec_tk_many_type, sizeof(*tk)); if (tk == NULL) { ec_tk_free(child); @@ -252,4 +254,4 @@ static struct ec_test ec_tk_many_test = { .test = ec_tk_many_testcase, }; -EC_REGISTER_TEST(ec_tk_many_test); +EC_TEST_REGISTER(ec_tk_many_test); diff --git a/lib/ecoli_tk_option.c b/lib/ecoli_tk_option.c index 1170f7e..0038a52 100644 --- a/lib/ecoli_tk_option.c +++ b/lib/ecoli_tk_option.c @@ -94,29 +94,35 @@ static void ec_tk_option_free_priv(struct ec_tk *gen_tk) ec_tk_free(tk->child); } -static struct ec_tk_ops ec_tk_option_ops = { - .typename = "option", +static struct ec_tk_type ec_tk_option_type = { + .name = "option", .parse = ec_tk_option_parse, .complete = ec_tk_option_complete, .free_priv = ec_tk_option_free_priv, }; +EC_TK_TYPE_REGISTER(ec_tk_option_type); + struct ec_tk *ec_tk_option_new(const char *id, struct ec_tk *child) { + struct ec_tk *gen_tk = NULL; struct ec_tk_option *tk = NULL; if (child == NULL) return NULL; - tk = (struct ec_tk_option *)ec_tk_new(id, &ec_tk_option_ops, - sizeof(*tk)); - if (tk == NULL) { + gen_tk = ec_tk_new(id, &ec_tk_option_type, sizeof(*tk)); + if (gen_tk == NULL) { ec_tk_free(child); return NULL; } + tk = (struct ec_tk_option *)gen_tk; tk->child = child; + child->parent = gen_tk; + TAILQ_INSERT_TAIL(&gen_tk->children, child, next); + return &tk->gen; } @@ -164,4 +170,4 @@ static struct ec_test ec_tk_option_test = { .test = ec_tk_option_testcase, }; -EC_REGISTER_TEST(ec_tk_option_test); +EC_TEST_REGISTER(ec_tk_option_test); diff --git a/lib/ecoli_tk_or.c b/lib/ecoli_tk_or.c index 47264c5..30e2de4 100644 --- a/lib/ecoli_tk_or.c +++ b/lib/ecoli_tk_or.c @@ -121,13 +121,6 @@ static void ec_tk_or_free_priv(struct ec_tk *gen_tk) ec_free(tk->table); } -static struct ec_tk_ops ec_tk_or_ops = { - .typename = "or", - .parse = ec_tk_or_parse, - .complete = ec_tk_or_complete, - .free_priv = ec_tk_or_free_priv, -}; - int ec_tk_or_add(struct ec_tk *gen_tk, struct ec_tk *child) { struct ec_tk_or *tk = (struct ec_tk_or *)gen_tk; @@ -156,12 +149,21 @@ int ec_tk_or_add(struct ec_tk *gen_tk, struct ec_tk *child) return 0; } +static struct ec_tk_type ec_tk_or_type = { + .name = "tk_or", + .parse = ec_tk_or_parse, + .complete = ec_tk_or_complete, + .free_priv = ec_tk_or_free_priv, +}; + +EC_TK_TYPE_REGISTER(ec_tk_or_type); + struct ec_tk *ec_tk_or(const char *id) { struct ec_tk *gen_tk = NULL; struct ec_tk_or *tk = NULL; - gen_tk = ec_tk_new(id, &ec_tk_or_ops, sizeof(*tk)); + gen_tk = ec_tk_new(id, &ec_tk_or_type, sizeof(*tk)); if (gen_tk == NULL) return NULL; @@ -283,4 +285,4 @@ static struct ec_test ec_tk_or_test = { .test = ec_tk_or_testcase, }; -EC_REGISTER_TEST(ec_tk_or_test); +EC_TEST_REGISTER(ec_tk_or_test); diff --git a/lib/ecoli_tk_or.h b/lib/ecoli_tk_or.h index a4e1555..c34b808 100644 --- a/lib/ecoli_tk_or.h +++ b/lib/ecoli_tk_or.h @@ -41,7 +41,6 @@ struct ec_tk *__ec_tk_or(const char *id, ...); struct ec_tk *ec_tk_or(const char *id); /* child is consumed */ -/* all token given in the list will be freed when freeing this one */ int ec_tk_or_add(struct ec_tk *tk, struct ec_tk *child); diff --git a/lib/ecoli_tk_re.c b/lib/ecoli_tk_re.c new file mode 100644 index 0000000..e5aec25 --- /dev/null +++ b/lib/ecoli_tk_re.c @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2016, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +struct ec_tk_re { + struct ec_tk gen; + char *re_str; + regex_t re; +}; + +static struct ec_parsed_tk *ec_tk_re_parse(const struct ec_tk *gen_tk, + const struct ec_strvec *strvec) +{ + struct ec_tk_re *tk = (struct ec_tk_re *)gen_tk; + struct ec_strvec *match_strvec; + struct ec_parsed_tk *parsed_tk = NULL; + const char *str; + regmatch_t pos; + + parsed_tk = ec_parsed_tk_new(); + if (parsed_tk == NULL) + goto fail; + + if (ec_strvec_len(strvec) == 0) + return parsed_tk; + + str = ec_strvec_val(strvec, 0); + if (regexec(&tk->re, str, 1, &pos, 0) != 0) + return parsed_tk; + if (pos.rm_so != 0 || pos.rm_eo != (int)strlen(str)) + return parsed_tk; + + match_strvec = ec_strvec_ndup(strvec, 0, 1); + if (match_strvec == NULL) + goto fail; + + ec_parsed_tk_set_match(parsed_tk, gen_tk, match_strvec); + + return parsed_tk; + + fail: + ec_parsed_tk_free(parsed_tk); + return NULL; +} + +static void ec_tk_re_free_priv(struct ec_tk *gen_tk) +{ + struct ec_tk_re *tk = (struct ec_tk_re *)gen_tk; + + ec_free(tk->re_str); + regfree(&tk->re); +} + +static struct ec_tk_type ec_tk_re_type = { + .name = "re", + .parse = ec_tk_re_parse, + .complete = ec_tk_default_complete, + .free_priv = ec_tk_re_free_priv, +}; + +EC_TK_TYPE_REGISTER(ec_tk_re_type); + +struct ec_tk *ec_tk_re_new(const char *id) +{ + struct ec_tk *gen_tk = NULL; + + gen_tk = ec_tk_new(id, &ec_tk_re_type, sizeof(struct ec_tk_re)); + if (gen_tk == NULL) + return NULL; + + return gen_tk; +} + +int ec_tk_re_set_regexp(struct ec_tk *gen_tk, const char *str) +{ + struct ec_tk_re *tk = (struct ec_tk_re *)gen_tk; + int ret; + + if (str == NULL) + return -EINVAL; + if (tk->re_str != NULL) // XXX allow to replace + return -EEXIST; + + ret = -ENOMEM; + tk->re_str = ec_strdup(str); + if (tk->re_str == NULL) + goto fail; + + ret = -EINVAL; + if (regcomp(&tk->re, tk->re_str, REG_EXTENDED) != 0) + goto fail; + + return 0; + +fail: + ec_free(tk->re_str); + return ret; +} + +struct ec_tk *ec_tk_re(const char *id, const char *re_str) +{ + struct ec_tk *gen_tk = NULL; + + gen_tk = ec_tk_re_new(id); + if (gen_tk == NULL) + goto fail; + + if (ec_tk_re_set_regexp(gen_tk, re_str) < 0) + goto fail; + + return gen_tk; + +fail: + ec_tk_free(gen_tk); + return NULL; +} + +static int ec_tk_re_testcase(void) +{ + struct ec_tk *tk; + int ret = 0; + + tk = ec_tk_re(NULL, "fo+|bar"); + if (tk == NULL) { + ec_log(EC_LOG_ERR, "cannot create tk\n"); + return -1; + } + ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo"); + ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo", "bar"); + ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "bar"); + ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "foobar"); + ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, " foo"); + ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, ""); + ec_tk_free(tk); + + return ret; +} + +static struct ec_test ec_tk_re_test = { + .name = "tk_re", + .test = ec_tk_re_testcase, +}; + +EC_TEST_REGISTER(ec_tk_re_test); diff --git a/lib/ecoli_tk_re.h b/lib/ecoli_tk_re.h new file mode 100644 index 0000000..dfd4c1a --- /dev/null +++ b/lib/ecoli_tk_re.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2016, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ECOLI_TK_RE_ +#define ECOLI_TK_RE_ + +#include + +struct ec_tk *ec_tk_re(const char *id, const char *str); + +struct ec_tk *ec_tk_re_new(const char *id); + +/* re is duplicated */ +int ec_tk_re_set_regexp(struct ec_tk *tk, const char *re); + +#endif diff --git a/lib/ecoli_tk_re_lex.c b/lib/ecoli_tk_re_lex.c index 60ba145..fb1d73c 100644 --- a/lib/ecoli_tk_re_lex.c +++ b/lib/ecoli_tk_re_lex.c @@ -107,6 +107,8 @@ static struct ec_parsed_tk *ec_tk_re_lex_parse(const struct ec_tk *gen_tk, if (new_vec == NULL) goto fail; + printf("--------\n"); + ec_strvec_dump(stdout, new_vec); child_parsed_tk = ec_tk_parse_tokens(tk->child, new_vec); if (child_parsed_tk == NULL) goto fail; @@ -150,13 +152,15 @@ static void ec_tk_re_lex_free_priv(struct ec_tk *gen_tk) ec_tk_free(tk->child); } -static struct ec_tk_ops ec_tk_re_lex_ops = { - .typename = "re_lex", +static struct ec_tk_type ec_tk_re_lex_type = { + .name = "re_lex", .parse = ec_tk_re_lex_parse, //.complete = ec_tk_re_lex_complete, //XXX .free_priv = ec_tk_re_lex_free_priv, }; +EC_TK_TYPE_REGISTER(ec_tk_re_lex_type); + int ec_tk_re_lex_add(struct ec_tk *gen_tk, const char *pattern, int keep) { struct ec_tk_re_lex *tk = (struct ec_tk_re_lex *)gen_tk; @@ -206,7 +210,7 @@ struct ec_tk *ec_tk_re_lex(const char *id, struct ec_tk *child) if (child == NULL) return NULL; - tk = (struct ec_tk_re_lex *)ec_tk_new(id, &ec_tk_re_lex_ops, + tk = (struct ec_tk_re_lex *)ec_tk_new(id, &ec_tk_re_lex_type, sizeof(*tk)); if (tk == NULL) { ec_tk_free(child); @@ -239,12 +243,12 @@ static int ec_tk_re_lex_testcase(void) } /* XXX add ^ automatically ? */ - ret |= ec_tk_re_lex_add(tk, "^[a-zA-Z]+", 1); - ret |= ec_tk_re_lex_add(tk, "^[0-9]+", 1); - ret |= ec_tk_re_lex_add(tk, "^=", 1); - ret |= ec_tk_re_lex_add(tk, "^-", 1); - ret |= ec_tk_re_lex_add(tk, "^\\+", 1); - ret |= ec_tk_re_lex_add(tk, "^[ ]+", 0); + ret |= ec_tk_re_lex_add(tk, "[a-zA-Z]+", 1); + ret |= ec_tk_re_lex_add(tk, "[0-9]+", 1); + ret |= ec_tk_re_lex_add(tk, "=", 1); + ret |= ec_tk_re_lex_add(tk, "-", 1); + ret |= ec_tk_re_lex_add(tk, "\\+", 1); + ret |= ec_tk_re_lex_add(tk, "[ ]+", 0); if (ret != 0) { ec_log(EC_LOG_ERR, "cannot add regexp to token\n"); ec_tk_free(tk); @@ -266,4 +270,4 @@ static struct ec_test ec_tk_re_lex_test = { .test = ec_tk_re_lex_testcase, }; -EC_REGISTER_TEST(ec_tk_re_lex_test); +EC_TEST_REGISTER(ec_tk_re_lex_test); diff --git a/lib/ecoli_tk_seq.c b/lib/ecoli_tk_seq.c index 807ff22..91bb91c 100644 --- a/lib/ecoli_tk_seq.c +++ b/lib/ecoli_tk_seq.c @@ -76,6 +76,7 @@ static struct ec_parsed_tk *ec_tk_seq_parse(const struct ec_tk *gen_tk, if (!ec_parsed_tk_matches(child_parsed_tk)) { ec_parsed_tk_free(child_parsed_tk); + // XXX ec_parsed_tk_free_children needed? see subset.c ec_parsed_tk_free_children(parsed_tk); return parsed_tk; } @@ -162,13 +163,15 @@ static void ec_tk_seq_free_priv(struct ec_tk *gen_tk) ec_free(tk->table); } -static struct ec_tk_ops ec_tk_seq_ops = { - .typename = "seq", +static struct ec_tk_type ec_tk_seq_type = { + .name = "seq", .parse = ec_tk_seq_parse, .complete = ec_tk_seq_complete, .free_priv = ec_tk_seq_free_priv, }; +EC_TK_TYPE_REGISTER(ec_tk_seq_type); + int ec_tk_seq_add(struct ec_tk *gen_tk, struct ec_tk *child) { struct ec_tk_seq *tk = (struct ec_tk_seq *)gen_tk; @@ -204,7 +207,7 @@ struct ec_tk *ec_tk_seq(const char *id) struct ec_tk *gen_tk = NULL; struct ec_tk_seq *tk = NULL; - gen_tk = ec_tk_new(id, &ec_tk_seq_ops, sizeof(*tk)); + gen_tk = ec_tk_new(id, &ec_tk_seq_type, sizeof(*tk)); if (gen_tk == NULL) return NULL; @@ -332,4 +335,4 @@ static struct ec_test ec_tk_seq_test = { .test = ec_tk_seq_testcase, }; -EC_REGISTER_TEST(ec_tk_seq_test); +EC_TEST_REGISTER(ec_tk_seq_test); diff --git a/lib/ecoli_tk_seq.h b/lib/ecoli_tk_seq.h index ffc9fa3..a401ed2 100644 --- a/lib/ecoli_tk_seq.h +++ b/lib/ecoli_tk_seq.h @@ -41,7 +41,7 @@ struct ec_tk *__ec_tk_seq(const char *id, ...); struct ec_tk *ec_tk_seq(const char *id); /* child is consumed */ -/* all token given in the list will be freed when freeing this one */ +// XXX add_child? int ec_tk_seq_add(struct ec_tk *tk, struct ec_tk *child); #endif diff --git a/lib/ecoli_tk_sh_lex.c b/lib/ecoli_tk_sh_lex.c index ab66e60..add0186 100644 --- a/lib/ecoli_tk_sh_lex.c +++ b/lib/ecoli_tk_sh_lex.c @@ -371,13 +371,15 @@ static void ec_tk_sh_lex_free_priv(struct ec_tk *gen_tk) ec_tk_free(tk->child); } -static struct ec_tk_ops ec_tk_sh_lex_ops = { - .typename = "sh_lex", +static struct ec_tk_type ec_tk_sh_lex_type = { + .name = "sh_lex", .parse = ec_tk_sh_lex_parse, .complete = ec_tk_sh_lex_complete, .free_priv = ec_tk_sh_lex_free_priv, }; +EC_TK_TYPE_REGISTER(ec_tk_sh_lex_type); + struct ec_tk *ec_tk_sh_lex_new(const char *id, struct ec_tk *child) { struct ec_tk_sh_lex *tk = NULL; @@ -385,7 +387,7 @@ struct ec_tk *ec_tk_sh_lex_new(const char *id, struct ec_tk *child) if (child == NULL) return NULL; - tk = (struct ec_tk_sh_lex *)ec_tk_new(id, &ec_tk_sh_lex_ops, + tk = (struct ec_tk_sh_lex *)ec_tk_new(id, &ec_tk_sh_lex_type, sizeof(*tk)); if (tk == NULL) { ec_tk_free(child); @@ -494,4 +496,4 @@ static struct ec_test ec_tk_sh_lex_test = { .test = ec_tk_sh_lex_testcase, }; -EC_REGISTER_TEST(ec_tk_sh_lex_test); +EC_TEST_REGISTER(ec_tk_sh_lex_test); diff --git a/lib/ecoli_tk_space.c b/lib/ecoli_tk_space.c index fc3a5b3..8b26d99 100644 --- a/lib/ecoli_tk_space.c +++ b/lib/ecoli_tk_space.c @@ -75,15 +75,17 @@ static struct ec_parsed_tk *ec_tk_space_parse(const struct ec_tk *gen_tk, return NULL; } -static struct ec_tk_ops ec_tk_space_ops = { - .typename = "space", +static struct ec_tk_type ec_tk_space_type = { + .name = "space", .parse = ec_tk_space_parse, .complete = ec_tk_default_complete, }; +EC_TK_TYPE_REGISTER(ec_tk_space_type); + struct ec_tk *ec_tk_space_new(const char *id) { - return ec_tk_new(id, &ec_tk_space_ops, sizeof(struct ec_tk_space)); + return ec_tk_new(id, &ec_tk_space_type, sizeof(struct ec_tk_space)); } static int ec_tk_space_testcase(void) @@ -131,4 +133,4 @@ static struct ec_test ec_tk_space_test = { .test = ec_tk_space_testcase, }; -EC_REGISTER_TEST(ec_tk_space_test); +EC_TEST_REGISTER(ec_tk_space_test); diff --git a/lib/ecoli_tk_str.c b/lib/ecoli_tk_str.c index 0644905..3a8aa9b 100644 --- a/lib/ecoli_tk_str.c +++ b/lib/ecoli_tk_str.c @@ -129,19 +129,21 @@ static void ec_tk_str_free_priv(struct ec_tk *gen_tk) ec_free(tk->string); } -static const struct ec_tk_ops ec_tk_str_ops = { - .typename = "str", +static struct ec_tk_type ec_tk_str_type = { + .name = "str", .parse = ec_tk_str_parse, .complete = ec_tk_str_complete, .desc = ec_tk_str_desc, .free_priv = ec_tk_str_free_priv, }; +EC_TK_TYPE_REGISTER(ec_tk_str_type); + struct ec_tk *ec_tk_str_new(const char *id) { struct ec_tk *gen_tk = NULL; - gen_tk = ec_tk_new(id, &ec_tk_str_ops, sizeof(struct ec_tk_str)); + gen_tk = ec_tk_new(id, &ec_tk_str_type, sizeof(struct ec_tk_str)); if (gen_tk == NULL) return NULL; @@ -155,7 +157,7 @@ int ec_tk_str_set_str(struct ec_tk *gen_tk, const char *str) if (str == NULL) return -EINVAL; if (tk->string != NULL) - return -EEXIST; + return -EEXIST; // XXX allow to replace tk->string = ec_strdup(str); if (tk->string == NULL) @@ -189,6 +191,7 @@ static int ec_tk_str_testcase(void) struct ec_tk *tk; int ret = 0; + /* XXX use EC_NO_ID instead of NULL */ tk = ec_tk_str(NULL, "foo"); if (tk == NULL) { ec_log(EC_LOG_ERR, "cannot create tk\n"); @@ -256,4 +259,4 @@ static struct ec_test ec_tk_str_test = { .test = ec_tk_str_testcase, }; -EC_REGISTER_TEST(ec_tk_str_test); +EC_TEST_REGISTER(ec_tk_str_test); diff --git a/lib/ecoli_tk_subset.c b/lib/ecoli_tk_subset.c new file mode 100644 index 0000000..1eee0a8 --- /dev/null +++ b/lib/ecoli_tk_subset.c @@ -0,0 +1,465 @@ +/* + * Copyright (c) 2016-2017, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +struct ec_tk_subset { + struct ec_tk gen; + struct ec_tk **table; + unsigned int len; +}; + +struct parse_result { + struct ec_parsed_tk **parsed_table; /* list of parsed tk */ + size_t parsed_table_len; /* number of parsed tk */ + size_t len; /* consumed strings */ +}; + +static int __ec_tk_subset_parse(struct ec_tk **table, size_t table_len, + const struct ec_strvec *strvec, + struct parse_result *out) +{ + struct ec_tk **child_table; + struct ec_parsed_tk *child_parsed_tk = NULL; + struct ec_strvec *childvec = NULL; + size_t i, j, len = 0; + struct parse_result best_result, result; + int ret; + + if (table_len == 0) + return 0; + + memset(&best_result, 0, sizeof(best_result)); + best_result.parsed_table = + ec_calloc(table_len, sizeof(*best_result.parsed_table[0])); + if (best_result.parsed_table == NULL) + goto fail; + + child_table = ec_calloc(table_len - 1, sizeof(*child_table)); + if (child_table == NULL) + goto fail; + + for (i = 0; i < table_len; i++) { + /* try to parse elt i */ + child_parsed_tk = ec_tk_parse_tokens(table[i], strvec); + if (child_parsed_tk == NULL) + goto fail; + + if (!ec_parsed_tk_matches(child_parsed_tk)) { + ec_parsed_tk_free(child_parsed_tk); + child_parsed_tk = NULL; + continue; + } + + /* build a new table without elt i */ + for (j = 0; j < table_len; j++) { + if (j < i) + child_table[j] = table[j]; + else if (j > i) + child_table[j - 1] = table[j]; + } + + /* build a new strvec */ + len = ec_parsed_tk_len(child_parsed_tk); + childvec = ec_strvec_ndup(strvec, len, + ec_strvec_len(strvec) - len); + if (childvec == NULL) + goto fail; + + memset(&result, 0, sizeof(result)); + + ret = __ec_tk_subset_parse(child_table, table_len - 1, + childvec, &result); + ec_strvec_free(childvec); + childvec = NULL; + if (ret < 0) + goto fail; + + /* if result is not the best, ignore */ + if (result.parsed_table_len + 1 <= + best_result.parsed_table_len) { + ec_parsed_tk_free(child_parsed_tk); + child_parsed_tk = NULL; + for (j = 0; j < result.parsed_table_len; j++) + ec_parsed_tk_free(result.parsed_table[j]); + ec_free(result.parsed_table); + memset(&result, 0, sizeof(result)); + continue; + } + + /* replace the previous best result */ + for (j = 0; j < best_result.parsed_table_len; j++) + ec_parsed_tk_free(best_result.parsed_table[j]); + best_result.parsed_table[0] = child_parsed_tk; + child_parsed_tk = NULL; + for (j = 0; j < result.parsed_table_len; j++) + best_result.parsed_table[j+1] = result.parsed_table[j]; + best_result.parsed_table_len = result.parsed_table_len + 1; + best_result.len = len + result.len; + ec_free(result.parsed_table); + } + + *out = best_result; + ec_free(child_table); + + return 0; + + fail: + ec_parsed_tk_free(child_parsed_tk); + ec_strvec_free(childvec); + for (j = 0; j < best_result.parsed_table_len; j++) + ec_parsed_tk_free(best_result.parsed_table[j]); + ec_free(best_result.parsed_table); + ec_free(child_table); + return -1; +} + +static struct ec_parsed_tk *ec_tk_subset_parse(const struct ec_tk *gen_tk, + const struct ec_strvec *strvec) +{ + struct ec_tk_subset *tk = (struct ec_tk_subset *)gen_tk; + struct ec_parsed_tk *parsed_tk = NULL; + struct parse_result result; + struct ec_strvec *match_strvec; + size_t i; + int ret; + + printf("---------parse\n"); + ec_strvec_dump(stdout, strvec); + + memset(&result, 0, sizeof(result)); + + parsed_tk = ec_parsed_tk_new(); + if (parsed_tk == NULL) + goto fail; + + ret = __ec_tk_subset_parse(tk->table, tk->len, strvec, &result); + if (ret < 0) + goto fail; + + if (result.parsed_table_len == 0) { + ec_free(result.parsed_table); + return parsed_tk; + } + + for (i = 0; i < result.parsed_table_len; i++) { + ec_parsed_tk_add_child(parsed_tk, result.parsed_table[i]); + result.parsed_table[i] = NULL; + } + ec_free(result.parsed_table); + result.parsed_table = NULL; + + match_strvec = ec_strvec_ndup(strvec, 0, result.len); + if (match_strvec == NULL) + goto fail; + + ec_parsed_tk_set_match(parsed_tk, gen_tk, match_strvec); + + return parsed_tk; + + fail: + for (i = 0; i < result.parsed_table_len; i++) + ec_parsed_tk_free(result.parsed_table[i]); + ec_free(result.parsed_table); + ec_parsed_tk_free(parsed_tk); + + return NULL; +} + +static struct ec_completed_tk *ec_tk_subset_complete(const struct ec_tk *gen_tk, + const struct ec_strvec *strvec) +{ + (void)gen_tk; + (void)strvec; +#if 0 + struct ec_tk_subset *tk = (struct ec_tk_subset *)gen_tk; + struct ec_completed_tk *completed_tk = NULL, *child_completed_tk; + struct ec_parsed_tk **child_parsed_tab = NULL; + struct ec_strvec *childvec = NULL; + size_t i, len; + + completed_tk = ec_completed_tk_new(); + if (completed_tk == NULL) + goto fail; + + child_parsed_tab = __ec_tk_subset_parse(gen_tk, strvec, &len); + if (child_parsed_tab == NULL) + goto fail; + + childvec = ec_strvec_ndup(strvec, len, ec_strvec_len(strvec) - len); + for (i = 0; i < tk->len; i++) { + if (child_parsed_tab[i] != NULL) + continue; + + child_completed_tk = ec_tk_complete_tokens(tk->table[i], + childvec); + + if (child_completed_tk == NULL) // XXX fail instead? + continue; + + ec_completed_tk_merge(completed_tk, child_completed_tk); + } + ec_strvec_free(childvec); + if (len > 0) { + childvec = ec_strvec_ndup(strvec, len - 1, + ec_strvec_len(strvec) - len + 1); + for (i = 0; i < tk->len; i++) { + if (child_parsed_tab[i] != NULL) + continue; + + child_completed_tk = ec_tk_complete_tokens(tk->table[i], + childvec); + + if (child_completed_tk == NULL) // XXX fail instead? + continue; + + ec_completed_tk_merge(completed_tk, child_completed_tk); + } + ec_strvec_free(childvec); + } + + for (i = 0; i < tk->len; i++) + ec_parsed_tk_free(child_parsed_tab[i]); + ec_free(child_parsed_tab); + + return completed_tk; + +fail: + ec_strvec_free(childvec); + for (i = 0; i < tk->len; i++) + ec_parsed_tk_free(child_parsed_tab[i]); + ec_free(child_parsed_tab); + ec_completed_tk_free(completed_tk); +#endif + return NULL; +} + +static void ec_tk_subset_free_priv(struct ec_tk *gen_tk) +{ + struct ec_tk_subset *tk = (struct ec_tk_subset *)gen_tk; + unsigned int i; + + for (i = 0; i < tk->len; i++) + ec_tk_free(tk->table[i]); + ec_free(tk->table); +} + +int ec_tk_subset_add(struct ec_tk *gen_tk, struct ec_tk *child) +{ + struct ec_tk_subset *tk = (struct ec_tk_subset *)gen_tk; + struct ec_tk **table; + + assert(tk != NULL); + + if (child == NULL) + return -EINVAL; + + gen_tk->flags &= ~EC_TK_F_BUILT; + + table = ec_realloc(tk->table, (tk->len + 1) * sizeof(*tk->table)); + if (table == NULL) { + ec_tk_free(child); + return -1; + } + + tk->table = table; + table[tk->len] = child; + tk->len++; + + child->parent = gen_tk; + TAILQ_INSERT_TAIL(&gen_tk->children, child, next); + + return 0; +} + +static struct ec_tk_type ec_tk_subset_type = { + .name = "tk_subset", + .parse = ec_tk_subset_parse, + .complete = ec_tk_subset_complete, + .free_priv = ec_tk_subset_free_priv, +}; + +EC_TK_TYPE_REGISTER(ec_tk_subset_type); + +struct ec_tk *ec_tk_subset(const char *id) +{ + struct ec_tk *gen_tk = NULL; + struct ec_tk_subset *tk = NULL; + + gen_tk = ec_tk_new(id, &ec_tk_subset_type, sizeof(*tk)); + if (gen_tk == NULL) + return NULL; + + tk = (struct ec_tk_subset *)gen_tk; + tk->table = NULL; + tk->len = 0; + + return gen_tk; +} + +struct ec_tk *__ec_tk_subset(const char *id, ...) +{ + struct ec_tk *gen_tk = NULL; + struct ec_tk_subset *tk = NULL; + struct ec_tk *child; + va_list ap; + int fail = 0; + + va_start(ap, id); + + gen_tk = ec_tk_subset(id); + tk = (struct ec_tk_subset *)gen_tk; + if (tk == NULL) + fail = 1;; + + for (child = va_arg(ap, struct ec_tk *); + child != EC_TK_ENDLIST; + child = va_arg(ap, struct ec_tk *)) { + + /* on error, don't quit the loop to avoid leaks */ + if (fail == 1 || child == NULL || + ec_tk_subset_add(gen_tk, child) < 0) { + fail = 1; + ec_tk_free(child); + } + } + + if (fail == 1) + goto fail; + + va_end(ap); + return gen_tk; + +fail: + ec_tk_free(gen_tk); /* will also free children */ + va_end(ap); + return NULL; +} + +static int ec_tk_subset_testcase(void) +{ + struct ec_tk *tk; + int ret = 0; + + tk = EC_TK_SUBSET(NULL, + EC_TK_OR(NULL, + ec_tk_str(NULL, "foo"), + ec_tk_str(NULL, "bar")), + ec_tk_str(NULL, "bar"), + ec_tk_str(NULL, "toto") + ); + if (tk == NULL) { + ec_log(EC_LOG_ERR, "cannot create tk\n"); + return -1; + } + ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo"); + ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "bar"); + ret |= EC_TEST_CHECK_TK_PARSE(tk, 2, "foo", "bar", "titi"); + ret |= EC_TEST_CHECK_TK_PARSE(tk, 3, "bar", "foo", "toto"); + ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo", "foo"); + ret |= EC_TEST_CHECK_TK_PARSE(tk, 2, "bar", "bar"); + ret |= EC_TEST_CHECK_TK_PARSE(tk, 2, "bar", "foo"); + ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, " "); + ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "foox"); + ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "titi"); + ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, ""); + ec_tk_free(tk); + + /* test completion */ + tk = EC_TK_SUBSET(NULL, + ec_tk_str(NULL, "foo"), + ec_tk_str(NULL, "bar"), + ec_tk_str(NULL, "bar2"), + ec_tk_str(NULL, "toto"), + ec_tk_str(NULL, "titi") + ); + if (tk == NULL) { + ec_log(EC_LOG_ERR, "cannot create tk\n"); + return -1; + } + ret |= EC_TEST_CHECK_TK_COMPLETE(tk, + "", EC_TK_ENDLIST, + "foo", "bar", "bar2", "toto", "titi", EC_TK_ENDLIST, + ""); + ret |= EC_TEST_CHECK_TK_COMPLETE(tk, + "bar", "bar2", "", EC_TK_ENDLIST, + "foo", "toto", "titi", EC_TK_ENDLIST, + ""); + ret |= EC_TEST_CHECK_TK_COMPLETE(tk, + "f", EC_TK_ENDLIST, + "oo", EC_TK_ENDLIST, + "oo"); + ret |= EC_TEST_CHECK_TK_COMPLETE(tk, + "b", EC_TK_ENDLIST, + "ar", "ar2", EC_TK_ENDLIST, + "ar"); + ret |= EC_TEST_CHECK_TK_COMPLETE(tk, + "bar", EC_TK_ENDLIST, + "", "2", EC_TK_ENDLIST, + ""); + ret |= EC_TEST_CHECK_TK_COMPLETE(tk, + "bar", "b", EC_TK_ENDLIST, + "bar2", EC_TK_ENDLIST, + "ar2"); + ret |= EC_TEST_CHECK_TK_COMPLETE(tk, + "t", EC_TK_ENDLIST, + "oto", "iti", EC_TK_ENDLIST, + ""); + ret |= EC_TEST_CHECK_TK_COMPLETE(tk, + "to", EC_TK_ENDLIST, + "to", EC_TK_ENDLIST, + "to"); + ret |= EC_TEST_CHECK_TK_COMPLETE(tk, + "x", EC_TK_ENDLIST, + EC_TK_ENDLIST, + ""); + ec_tk_free(tk); + + return ret; +} + +static struct ec_test ec_tk_subset_test = { + .name = "tk_subset", + .test = ec_tk_subset_testcase, +}; + +EC_TEST_REGISTER(ec_tk_subset_test); diff --git a/lib/ecoli_tk_subset.h b/lib/ecoli_tk_subset.h new file mode 100644 index 0000000..aac3014 --- /dev/null +++ b/lib/ecoli_tk_subset.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2016-2017, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ECOLI_TK_SUBSET_ +#define ECOLI_TK_SUBSET_ + +#include + +#define EC_TK_SUBSET(args...) __ec_tk_subset(args, EC_TK_ENDLIST) + +/* list must be terminated with EC_TK_ENDLIST */ +/* all token given in the list will be freed when freeing this one */ +/* avoid using this function directly, prefer the macro EC_TK_SUBSET() or + * ec_tk_subset() + ec_tk_subset_add() */ +struct ec_tk *__ec_tk_subset(const char *id, ...); + +struct ec_tk *ec_tk_subset(const char *id); + +/* child is consumed */ +int ec_tk_subset_add(struct ec_tk *tk, struct ec_tk *child); + +#endif diff --git a/lib/ecoli_tk_weakref.c b/lib/ecoli_tk_weakref.c index 5aee97c..cd68d25 100644 --- a/lib/ecoli_tk_weakref.c +++ b/lib/ecoli_tk_weakref.c @@ -62,18 +62,20 @@ static struct ec_completed_tk *ec_tk_weakref_complete(const struct ec_tk *gen_tk return ec_tk_complete_tokens(tk->child, strvec); } -static struct ec_tk_ops ec_tk_weakref_ops = { - .typename = "weakref", +static struct ec_tk_type ec_tk_weakref_type = { + .name = "weakref", .parse = ec_tk_weakref_parse, .complete = ec_tk_weakref_complete, }; +EC_TK_TYPE_REGISTER(ec_tk_weakref_type); + struct ec_tk *ec_tk_weakref_empty(const char *id) { struct ec_tk_weakref *tk = NULL; tk = (struct ec_tk_weakref *)ec_tk_new(id, - &ec_tk_weakref_ops, sizeof(*tk)); + &ec_tk_weakref_type, sizeof(*tk)); if (tk == NULL) return NULL; @@ -129,4 +131,4 @@ static struct ec_test ec_tk_weakref_test = { .test = ec_tk_weakref_testcase, }; -EC_REGISTER_TEST(ec_tk_weakref_test); +EC_TEST_REGISTER(ec_tk_weakref_test); diff --git a/lib/main-readline.c b/lib/main-readline.c index 7a0d5b8..e797953 100644 --- a/lib/main-readline.c +++ b/lib/main-readline.c @@ -42,6 +42,8 @@ #include #include #include +#include +#include static struct ec_tk *commands; @@ -177,16 +179,17 @@ static int create_commands(void) cmd = EC_TK_SEQ(NULL, ec_tk_str(NULL, "hello"), - EC_TK_OR(NULL, - EC_TK_OR("name", - ec_tk_str(NULL, "john"), - ec_tk_str(NULL, "johnny"), - ec_tk_str(NULL, "mike") - ), - ec_tk_int("int", 0, 10, 10) - ) + EC_TK_OR("name", + ec_tk_str(NULL, "john"), + ec_tk_str(NULL, "johnny"), + ec_tk_str(NULL, "mike") + ), + ec_tk_option_new(NULL, ec_tk_int("int", 0, 10, 10)) ); - ec_keyval_set(ec_tk_attrs(cmd), "help", "say hello to someone", NULL); + if (cmd == NULL) + goto fail; + ec_keyval_set(ec_tk_attrs(cmd), "help", + "say hello to someone several times", NULL); ec_keyval_set(ec_tk_attrs(ec_tk_find(cmd, "name")), "help", "the name of the person", NULL); ec_keyval_set(ec_tk_attrs(ec_tk_find(cmd, "int")), @@ -194,6 +197,17 @@ static int create_commands(void) if (ec_tk_or_add(cmdlist, cmd) < 0) goto fail; +#if 0 + cmd = EC_TK_CMD(NULL, "good morning john|johnny|mike [count]", + ec_tk_int("count", 0, 10, 10)); + if (cmd == NULL) + goto fail; + ec_keyval_set(ec_tk_attrs(cmd), "help", + "say good morning to someone several times", NULL); + if (ec_tk_or_add(cmdlist, cmd) < 0) + goto fail; +#endif + cmd = EC_TK_SEQ(NULL, ec_tk_str(NULL, "bye") ); diff --git a/lib/main.c b/lib/main.c index d856955..1f36b69 100644 --- a/lib/main.c +++ b/lib/main.c @@ -102,9 +102,9 @@ parse_int(const char *s, int min, int max, int *ret, unsigned int base) return 0; } -static void parse_args(int argc, char **argv) +static int parse_args(int argc, char **argv) { - int opt; + int ret, opt; while ((opt = getopt_long(argc, argv, ec_short_options, ec_long_options, NULL)) != EOF) { @@ -142,9 +142,14 @@ static void parse_args(int argc, char **argv) default: usage(argv[0]); - exit(1); + return -1; } } + + ret = optind - 1; + optind = 1; + + return ret; } TAILQ_HEAD(debug_alloc_hdr_list, debug_alloc_hdr); @@ -355,9 +360,15 @@ static int debug_log(unsigned int level, void *opaque, const char *str) int main(int argc, char **argv) { - int ret, leaks; + int i, ret = 0, leaks; + + ret = parse_args(argc, argv); + if (ret < 0) + return 1; + + argc -= ret; + argv += ret; - parse_args(argc, argv); srandom(seed); ec_log_register(debug_log, NULL); @@ -369,7 +380,13 @@ int main(int argc, char **argv) return -1; } - ret = ec_test_all(); + ret = 0; + if (argc <= 1) { + ret = ec_test_all(); + } else { + for (i = 1; i < argc; i++) + ret |= ec_test_one(argv[i]); + } ec_malloc_unregister(); leaks = debug_alloc_dump_leaks(); diff --git a/lib/test.sh b/lib/test.sh index b71086e..3d97e77 100755 --- a/lib/test.sh +++ b/lib/test.sh @@ -4,7 +4,7 @@ set -e SEED=100 while [ ${SEED} -gt 0 ]; do - CMD="./build/test --random-alloc-fail=10 --seed=${SEED}" + CMD="./build/test --random-alloc-fail=1 --seed=${SEED}" ${CMD} --log-level=0 || ( echo "=== test failed, replay seed=${SEED} with logs ===" && ${CMD} --log-level=6 || diff --git a/lib/todo.txt b/lib/todo.txt index befa74c..a82ba6c 100644 --- a/lib/todo.txt +++ b/lib/todo.txt @@ -1,9 +1,95 @@ -- evaluate expression tree in ec_tk_expr +tk_cmd +====== + +X evaluate expression tree in ec_tk_expr - cmd token -- node regexp -- node which always matches -- yaml interface to create nodes +- example +- tk_re + +cleanup +======= + - check XXX in code +- remove the _new() functions +- add a tk vector type: will be used in several nodes (ex: or, seq, ...) +- check allocation model everywhere +- checkpatch? +- use linux style (update .emacs) +- better logs +- return values +- missing static / const +- license: "s/neither the name...may/the names of its contributors may not/" +- check all completion nodes +- split ecoli_tk.h +- cache results when appropriate? +- size_t or unsigned int? + +logs +==== + +- register log types + +yaml +==== + +X register nodes by name +- yaml interface to create nodes +- example + +examples +======== + - example which parses arguments (argc/argv) - example that acts as bash completion (ip link ?) - calculator example (var assignation, expression evaluation) +- example with libedit +- mini script language +- configuration file + +doc +=== + +- overview +- add api doc in .h +- generate automatic api doc +- architecture +- coding rules, process +- each node +- allocation model +- say that it stops at first match (no ambigous support) + +build framework +=============== + +- .map files for API +- split libs, tests and examples +- add make help +- add make config +- -fvisibility= + +tests +===== + +- complete automatic tests with "make test" + +new nodes +========= + +- regexp +- node which always matches +- file + partial completion +- ether, ip, network +- fusion node: need to match several children, same for completion +- float + +encoding +======== + +- support utf-8 and other encodings +- example +- documentation + +netconf example +=============== + +- demonstration example that parses yang file and generate cli -- 2.39.5