save
authorOlivier Matz <zer0@droids-corp.org>
Thu, 18 May 2017 18:15:43 +0000 (20:15 +0200)
committerOlivier Matz <zer0@droids-corp.org>
Thu, 18 May 2017 18:15:43 +0000 (20:15 +0200)
33 files changed:
lib/Makefile
lib/ecoli_keyval.c
lib/ecoli_test.c
lib/ecoli_test.h
lib/ecoli_tk.c
lib/ecoli_tk.h
lib/ecoli_tk_cmd.c [new file with mode: 0644]
lib/ecoli_tk_cmd.h [new file with mode: 0644]
lib/ecoli_tk_empty.c
lib/ecoli_tk_expr.c
lib/ecoli_tk_expr.h
lib/ecoli_tk_expr_test.c
lib/ecoli_tk_int.c
lib/ecoli_tk_int.h
lib/ecoli_tk_many.c
lib/ecoli_tk_option.c
lib/ecoli_tk_or.c
lib/ecoli_tk_or.h
lib/ecoli_tk_re.c [new file with mode: 0644]
lib/ecoli_tk_re.h [new file with mode: 0644]
lib/ecoli_tk_re_lex.c
lib/ecoli_tk_seq.c
lib/ecoli_tk_seq.h
lib/ecoli_tk_sh_lex.c
lib/ecoli_tk_space.c
lib/ecoli_tk_str.c
lib/ecoli_tk_subset.c [new file with mode: 0644]
lib/ecoli_tk_subset.h [new file with mode: 0644]
lib/ecoli_tk_weakref.c
lib/main-readline.c
lib/main.c
lib/test.sh
lib/todo.txt

index ecb030b..d868c39 100644 (file)
@@ -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)
index 7f87b01..0557bff 100644 (file)
@@ -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);
index fc82c69..2ac659b 100644 (file)
@@ -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);
+}
index a05d4cf..cf41f46 100644 (file)
 #include <ecoli_log.h>
 #include <ecoli_tk.h>
 
-#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);                 \
index 9d877c0..7fed400 100644 (file)
 #include <ecoli_log.h>
 #include <ecoli_tk.h>
 
-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;
 }
index a86266c..f420a52 100644 (file)
@@ -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 (file)
index 0000000..f5f1575
--- /dev/null
@@ -0,0 +1,499 @@
+/*
+ * Copyright (c) 2016-2017, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * 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 <sys/queue.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <limits.h>
+
+#include <ecoli_malloc.h>
+#include <ecoli_log.h>
+#include <ecoli_test.h>
+#include <ecoli_strvec.h>
+#include <ecoli_tk_expr.h>
+#include <ecoli_tk_str.h>
+#include <ecoli_tk_or.h>
+#include <ecoli_tk_int.h>
+#include <ecoli_tk_many.h>
+#include <ecoli_tk_seq.h>
+#include <ecoli_tk_option.h>
+#include <ecoli_tk_re.h>
+#include <ecoli_tk_re_lex.h>
+#include <ecoli_tk_cmd.h>
+
+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 (file)
index 0000000..f324ff4
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2016-2017, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * 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 <ecoli_tk.h>
+
+#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
index ee6a4e6..9077c44 100644 (file)
@@ -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);
index 7f1c3cb..40dbd1b 100644 (file)
@@ -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 */
index d24e0a2..d2efc24 100644 (file)
 
 /* 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);
 
index 858ffcf..be28797 100644 (file)
@@ -28,6 +28,7 @@
 #include <errno.h>
 #include <limits.h>
 #include <stdint.h>
+#include <assert.h>
 
 #include <ecoli_malloc.h>
 #include <ecoli_strvec.h>
@@ -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);
index bd0155d..a3510b3 100644 (file)
@@ -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);
index b7fa6a6..156ebf1 100644 (file)
@@ -30,6 +30,8 @@
 
 #include <ecoli_tk.h>
 
+// 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);
index ecd54b8..09e5b51 100644 (file)
@@ -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);
index 1170f7e..0038a52 100644 (file)
@@ -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);
index 47264c5..30e2de4 100644 (file)
@@ -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);
index a4e1555..c34b808 100644 (file)
@@ -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 (file)
index 0000000..e5aec25
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2016, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <regex.h>
+
+#include <ecoli_log.h>
+#include <ecoli_malloc.h>
+#include <ecoli_test.h>
+#include <ecoli_strvec.h>
+#include <ecoli_tk.h>
+#include <ecoli_tk_re.h>
+
+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 (file)
index 0000000..dfd4c1a
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2016, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * 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 <ecoli_tk.h>
+
+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
index 60ba145..fb1d73c 100644 (file)
@@ -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);
index 807ff22..91bb91c 100644 (file)
@@ -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);
index ffc9fa3..a401ed2 100644 (file)
@@ -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
index ab66e60..add0186 100644 (file)
@@ -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);
index fc3a5b3..8b26d99 100644 (file)
@@ -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);
index 0644905..3a8aa9b 100644 (file)
@@ -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 (file)
index 0000000..1eee0a8
--- /dev/null
@@ -0,0 +1,465 @@
+/*
+ * Copyright (c) 2016-2017, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <stdbool.h>
+
+#include <ecoli_malloc.h>
+#include <ecoli_log.h>
+#include <ecoli_strvec.h>
+#include <ecoli_tk.h>
+#include <ecoli_tk_subset.h>
+#include <ecoli_tk_str.h>
+#include <ecoli_tk_or.h>
+#include <ecoli_test.h>
+
+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 (file)
index 0000000..aac3014
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2016-2017, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * 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 <ecoli_tk.h>
+
+#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
index 5aee97c..cd68d25 100644 (file)
@@ -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);
index 7a0d5b8..e797953 100644 (file)
@@ -42,6 +42,8 @@
 #include <ecoli_tk_or.h>
 #include <ecoli_tk_sh_lex.h>
 #include <ecoli_tk_int.h>
+#include <ecoli_tk_option.h>
+#include <ecoli_tk_cmd.h>
 
 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")
        );
index d856955..1f36b69 100644 (file)
@@ -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();
index b71086e..3d97e77 100755 (executable)
@@ -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 ||
index befa74c..a82ba6c 100644 (file)
@@ -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