completion
authorOlivier Matz <zer0@droids-corp.org>
Wed, 2 Nov 2016 08:04:48 +0000 (09:04 +0100)
committerOlivier Matz <zer0@droids-corp.org>
Wed, 2 Nov 2016 08:04:48 +0000 (09:04 +0100)
lib/build/test
lib/ecoli_test.c
lib/ecoli_test.h
lib/ecoli_tk.c
lib/ecoli_tk.h
lib/ecoli_tk_empty.c
lib/ecoli_tk_int.c
lib/ecoli_tk_or.c
lib/ecoli_tk_seq.c
lib/ecoli_tk_space.c
lib/ecoli_tk_str.c

index 2913edb..8ef7e3e 100755 (executable)
Binary files a/lib/build/test and b/lib/build/test differ
index 3f1c4fd..f6521da 100644 (file)
@@ -71,12 +71,12 @@ int ec_test_check_tk_parse(const struct ec_tk *tk, const char *input,
 int ec_test_check_tk_complete(const struct ec_tk *tk, const char *input,
        const char *expected)
 {
-       struct ec_completed_tk *p;
+       struct ec_completed_tk *c;
        const char *s;
        int ret = -1;
 
-       p = ec_tk_complete(tk, input);
-       s = ec_completed_tk_smallest_start(p);
+       c = ec_tk_complete(tk, input);
+       s = ec_completed_tk_smallest_start(c);
        if (s == NULL && expected == NULL)
                ret = 0;
        else if (s != NULL && expected != NULL &&
@@ -91,11 +91,61 @@ int ec_test_check_tk_complete(const struct ec_tk *tk, const char *input,
                        "tk should complete with <%s> but completes with <%s>\n",
                        expected, s);
 
-       ec_completed_tk_free(p);
+       ec_completed_tk_free(c);
 
        return ret;
 }
 
+int ec_test_check_tk_complete_list(const struct ec_tk *tk,
+       const char *input, ...)
+{
+       struct ec_completed_tk *c = NULL;
+       struct ec_completed_tk_elt *elt;
+       const char *s;
+       int ret = -1;
+       unsigned int count = 0;
+       va_list ap;
+
+       va_start(ap, input);
+
+       c = ec_tk_complete(tk, input);
+       if (c == NULL)
+               goto out;
+
+       for (s = va_arg(ap, const char *);
+            s != EC_TK_ENDLIST;
+            s = va_arg(ap, const char *)) {
+               if (s == NULL)
+                       goto out;
+
+               count++;
+               TAILQ_FOREACH(elt, &c->elts, next) {
+                       if (strcmp(elt->add, s) == 0)
+                               break;
+               }
+
+               if (elt == NULL) {
+                       ec_log(EC_LOG_ERR,
+                               "completion <%s> not in list\n", s);
+                       goto out;
+               }
+       }
+
+       if (count != ec_completed_tk_count(c)) {
+               ec_log(EC_LOG_ERR,
+                       "nb_completion (%d) does not match (%d)\n",
+                       count, ec_completed_tk_count(c));
+               goto out;
+       }
+
+       ret = 0;
+
+out:
+       ec_completed_tk_free(c);
+       va_end(ap);
+       return ret;
+}
+
 TAILQ_HEAD(debug_alloc_hdr_list, debug_alloc_hdr);
 static struct debug_alloc_hdr_list debug_alloc_hdr_list =
        TAILQ_HEAD_INITIALIZER(debug_alloc_hdr_list);
@@ -108,10 +158,15 @@ struct debug_alloc_hdr {
        unsigned int cookie;
 };
 
+struct debug_alloc_ftr {
+       unsigned int cookie;
+} __attribute__((packed));
+
 static void *debug_malloc(size_t size, const char *file, unsigned int line)
 {
        struct debug_alloc_hdr *hdr;
-       size_t new_size = size + sizeof(*hdr) + sizeof(unsigned int);
+       struct debug_alloc_ftr *ftr;
+       size_t new_size = size + sizeof(*hdr) + sizeof(*ftr);
        void *ret;
 
        hdr = malloc(new_size);
@@ -124,6 +179,9 @@ static void *debug_malloc(size_t size, const char *file, unsigned int line)
                hdr->cookie = 0x12345678;
                TAILQ_INSERT_TAIL(&debug_alloc_hdr_list, hdr, next);
                ret = hdr + 1;
+               ftr = (struct debug_alloc_ftr *)(
+                       (char *)hdr + size + sizeof(*hdr));
+               ftr->cookie = 0x12345678;
        }
 
        ec_log(EC_LOG_INFO, "%s:%d: info: malloc(%zd) -> %p\n",
@@ -135,6 +193,7 @@ static void *debug_malloc(size_t size, const char *file, unsigned int line)
 static void debug_free(void *ptr, const char *file, unsigned int line)
 {
        struct debug_alloc_hdr *hdr, *h;
+       struct debug_alloc_ftr *ftr;
 
        (void)file;
        (void)line;
@@ -151,6 +210,13 @@ static void debug_free(void *ptr, const char *file, unsigned int line)
                abort();
        }
 
+       ftr = (ptr + hdr->size);
+       if (ftr->cookie != 0x12345678) {
+               ec_log(EC_LOG_ERR, "%s:%d: error: free(%p): bad end cookie\n",
+                       file, line, ptr);
+               abort();
+       }
+
        TAILQ_FOREACH(h, &debug_alloc_hdr_list, next) {
                if (h == hdr)
                        break;
@@ -168,14 +234,24 @@ static void debug_free(void *ptr, const char *file, unsigned int line)
 
 void *debug_realloc(void *ptr, size_t size, const char *file, unsigned int line)
 {
-       struct debug_alloc_hdr *hdr = (ptr - sizeof(*hdr));
-       struct debug_alloc_hdr *h;
+       struct debug_alloc_hdr *hdr, *h;
+       struct debug_alloc_ftr *ftr;
        size_t new_size = size + sizeof(*hdr) + sizeof(unsigned int);
        void *ret;
 
        if (ptr != NULL) {
+               hdr =  (ptr - sizeof(*hdr));
                if (hdr->cookie != 0x12345678) {
-                       ec_log(EC_LOG_ERR, "%s:%d: error: realloc(%p): bad start cookie\n",
+                       ec_log(EC_LOG_ERR,
+                               "%s:%d: error: realloc(%p): bad start cookie\n",
+                               file, line, ptr);
+                       abort();
+               }
+
+               ftr = (ptr + hdr->size);
+               if (ftr->cookie != 0x12345678) {
+                       ec_log(EC_LOG_ERR,
+                               "%s:%d: error: realloc(%p): bad end cookie\n",
                                file, line, ptr);
                        abort();
                }
@@ -204,7 +280,7 @@ void *debug_realloc(void *ptr, size_t size, const char *file, unsigned int line)
                if (hdr == NULL)
                        ret = NULL;
                else
-                       ret= hdr + 1;
+                       ret = hdr + 1;
        }
 
        if (hdr != NULL) {
@@ -213,6 +289,9 @@ void *debug_realloc(void *ptr, size_t size, const char *file, unsigned int line)
                hdr->size = size;
                hdr->cookie = 0x12345678;
                TAILQ_INSERT_TAIL(&debug_alloc_hdr_list, hdr, next);
+               ftr = (struct debug_alloc_ftr *)(
+                       (char *)hdr + size + sizeof(*hdr));
+               ftr->cookie = 0x12345678;
        }
 
        ec_log(EC_LOG_INFO, "%s:%d: info: realloc(%p, %zd) -> %p\n",
@@ -243,7 +322,7 @@ int ec_test_all(void)
        TAILQ_INIT(&debug_alloc_hdr_list);
 
        /* register a new malloc to trac memleaks */
-       if (ec_malloc_register(debug_malloc, debug_free, debug_realloc) < 0) {
+       if (0 && ec_malloc_register(debug_malloc, debug_free, debug_realloc) < 0) {
                ec_log(EC_LOG_ERR, "cannot register new malloc\n");
                return -1;
        }
index 61d9950..45d1d4e 100644 (file)
@@ -30,6 +30,7 @@
 
 #include <sys/queue.h>
 
+#include <ecoli_log.h>
 #include <ecoli_tk.h>
 
 #define EC_REGISTER_TEST(t)                                            \
@@ -66,13 +67,12 @@ void ec_test_register(struct ec_test *test);
 
 int ec_test_all(void);
 
-// XXX could be a macro that display file:line
 int ec_test_check_tk_parse(const struct ec_tk *tk, const char *input,
        const char *expected);
 
 #define TEST_ERR()                                                     \
-               printf("%s:%d: error: test failed\n",                   \
-                       __FILE__, __LINE__);                            \
+       ec_log(EC_LOG_ERR, "%s:%d: error: test failed\n",               \
+               __FILE__, __LINE__);                                    \
 
 #define EC_TEST_CHECK_TK_PARSE(tk, input, expected) ({                 \
        int ret = ec_test_check_tk_parse(tk, input, expected);          \
@@ -91,4 +91,14 @@ int ec_test_check_tk_complete(const struct ec_tk *tk, const char *input,
        ret;                                                            \
 })
 
+int ec_test_check_tk_complete_list(const struct ec_tk *tk,
+       const char *input, ...);
+
+#define EC_TEST_CHECK_TK_COMPLETE_LIST(tk, input, expected...) ({      \
+       int ret = ec_test_check_tk_complete_list(tk, input, expected);  \
+       if (ret)                                                        \
+               TEST_ERR();                                             \
+       ret;                                                            \
+})
+
 #endif
index e814b68..cdb064e 100644 (file)
@@ -70,11 +70,15 @@ void ec_tk_free(struct ec_tk *tk)
        ec_free(tk);
 }
 
-struct ec_parsed_tk *ec_tk_parse(const struct ec_tk *token, const char *str)
+struct ec_parsed_tk *ec_tk_parse(const struct ec_tk *tk, const char *str)
 {
        struct ec_parsed_tk *parsed_tk;
 
-       parsed_tk = token->ops->parse(token, str);
+       /* by default, it does not match anything */
+       if (tk->ops->parse == NULL)
+               return NULL;
+
+       parsed_tk = tk->ops->parse(tk, str);
 
        return parsed_tk;
 }
@@ -174,16 +178,6 @@ const char *ec_parsed_tk_to_string(const struct ec_parsed_tk *parsed_tk)
        return parsed_tk->str;
 }
 
-struct ec_completed_tk *ec_tk_complete(const struct ec_tk *token,
-       const char *str)
-{
-       struct ec_completed_tk *completed_tk;
-
-       completed_tk = token->ops->complete(token, str);
-
-       return completed_tk;
-}
-
 struct ec_completed_tk *ec_completed_tk_new(void)
 {
        struct ec_completed_tk *completed_tk = NULL;
@@ -208,16 +202,41 @@ struct ec_completed_tk_elt *ec_completed_tk_elt_new(const struct ec_tk *tk,
                return NULL;
 
        elt->tk = tk;
-       elt->add = ec_strdup(add);
-       elt->full = ec_strdup(full);
-       if (elt->add == NULL || elt->full == NULL) {
-               ec_completed_tk_elt_free(elt);
-               return NULL;
+       if (add != NULL) {
+               elt->add = ec_strdup(add);
+               if (elt->add == NULL) {
+                       ec_completed_tk_elt_free(elt);
+                       return NULL;
+               }
+       }
+       if (full != NULL) {
+               elt->full = ec_strdup(full);
+               if (elt->full == NULL) {
+                       ec_completed_tk_elt_free(elt);
+                       return NULL;
+               }
        }
 
        return elt;
 }
 
+/* XXX define when to use ec_tk_complete() or tk->complete()
+ * (same for parse)
+ * suggestion: tk->op() is internal, user calls the function
+ */
+struct ec_completed_tk *ec_tk_complete(const struct ec_tk *tk,
+       const char *str)
+{
+       struct ec_completed_tk *completed_tk;
+
+       if (tk->ops->complete == NULL)
+               return ec_completed_tk_new();
+
+       completed_tk = tk->ops->complete(tk, str);
+
+       return completed_tk;
+}
+
 /* count the number of identical chars at the beginning of 2 strings */
 static size_t strcmp_count(const char *s1, const char *s2)
 {
@@ -305,8 +324,10 @@ void ec_completed_tk_dump(FILE *out, const struct ec_completed_tk *completed_tk)
        fprintf(out, "completion: count=%u smallest_start=<%s>\n",
                completed_tk->count, completed_tk->smallest_start);
 
-       TAILQ_FOREACH(elt, &completed_tk->elts, next)
-               fprintf(out, "add=<%s> full=<%s>\n", elt->add, elt->full);
+       TAILQ_FOREACH(elt, &completed_tk->elts, next) {
+               fprintf(out, "add=<%s>, full=<%s>, tk=%p\n",
+                       elt->add, elt->full, elt->tk);
+       }
 }
 
 const char *ec_completed_tk_smallest_start(
@@ -320,14 +341,8 @@ const char *ec_completed_tk_smallest_start(
 
 unsigned int ec_completed_tk_count(const struct ec_completed_tk *completed_tk)
 {
-       struct ec_completed_tk_elt *elt;
-       unsigned int count = 0;
-
        if (completed_tk == NULL)
                return 0;
 
-       TAILQ_FOREACH(elt, &completed_tk->elts, next)
-               count++;
-
-       return count;
+       return completed_tk->count;
 }
index 490c5e1..cc0a208 100644 (file)
@@ -69,6 +69,10 @@ struct ec_parsed_tk {
        char *str;
 };
 
+/* XXX we could use a cache to store possible completions or match: the
+ * cache would be per-node, and would be reset for each call to parse()
+ * or complete() ? */
+
 struct ec_parsed_tk *ec_parsed_tk_new(const struct ec_tk *tk);
 struct ec_parsed_tk *ec_tk_parse(const struct ec_tk *token, const char *str);
 void ec_parsed_tk_add_child(struct ec_parsed_tk *parsed_tk,
@@ -98,17 +102,16 @@ struct ec_completed_tk {
 };
 
 /*
- * return NULL if it does not match the beginning of the token
- * return "" if it matches but does not know how to complete
- * return "xyz" if it knows how to complete
+ * return a completed_tk object filled with elts
+ * return NULL on error (nomem?)
  */
 struct ec_completed_tk *ec_tk_complete(const struct ec_tk *token,
        const char *str);
 struct ec_completed_tk *ec_completed_tk_new(void);
 struct ec_completed_tk_elt *ec_completed_tk_elt_new(const struct ec_tk *tk,
        const char *add, const char *full);
-void ec_completed_tk_add_elt(
-       struct ec_completed_tk *completed_tk, struct ec_completed_tk_elt *elt);
+void ec_completed_tk_add_elt(struct ec_completed_tk *completed_tk,
+       struct ec_completed_tk_elt *elt);
 void ec_completed_tk_elt_free(struct ec_completed_tk_elt *elt);
 struct ec_completed_tk *ec_completed_tk_merge(
        struct ec_completed_tk *completed_tk1,
@@ -117,6 +120,7 @@ void ec_completed_tk_free(struct ec_completed_tk *completed_tk);
 void ec_completed_tk_dump(FILE *out,
        const struct ec_completed_tk *completed_tk);
 
+/* can return NULL */
 const char *ec_completed_tk_smallest_start(
        const struct ec_completed_tk *completed_tk);
 
index 61d29b5..e3e24f9 100644 (file)
 #include <stdlib.h>
 #include <string.h>
 
+#include <ecoli_malloc.h>
+#include <ecoli_log.h>
+#include <ecoli_test.h>
 #include <ecoli_tk.h>
 #include <ecoli_tk_empty.h>
 
-static struct ec_parsed_tk *parse(const struct ec_tk *gen_tk,
+static struct ec_parsed_tk *ec_tk_empty_parse(const struct ec_tk *gen_tk,
        const char *str)
 {
        struct ec_parsed_tk *parsed_tk;
@@ -42,17 +45,52 @@ static struct ec_parsed_tk *parse(const struct ec_tk *gen_tk,
                return NULL;
 
        (void)str;
-       parsed_tk->str = strdup("");
+       parsed_tk->str = ec_strdup("");
 
        return parsed_tk;
 }
 
-static struct ec_tk_ops empty_ops = {
-       .parse = parse,
+static struct ec_tk_ops ec_tk_empty_ops = {
+       .parse = ec_tk_empty_parse,
 };
 
 struct ec_tk *ec_tk_empty_new(const char *id)
 {
-       return ec_tk_new(id, &empty_ops, sizeof(struct ec_tk_empty));
+       return ec_tk_new(id, &ec_tk_empty_ops, sizeof(struct ec_tk_empty));
 }
 
+static int ec_tk_empty_testcase(void)
+{
+       struct ec_tk *tk;
+       int ret = 0;
+
+       /* all inputs match */
+       tk = ec_tk_empty_new(NULL);
+       if (tk == NULL) {
+               ec_log(EC_LOG_ERR, "cannot create tk\n");
+               return -1;
+       }
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, "foo", "");
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, " foo", "");
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, "", "");
+       ec_tk_free(tk);
+
+       /* never completes */
+       tk = ec_tk_empty_new(NULL);
+       if (tk == NULL) {
+               ec_log(EC_LOG_ERR, "cannot create tk\n");
+               return -1;
+       }
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "", NULL);
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foo", NULL);
+       ec_tk_free(tk);
+
+       return ret;
+}
+
+static struct ec_test ec_tk_empty_test = {
+       .name = "tk_empty",
+       .test = ec_tk_empty_testcase,
+};
+
+EC_REGISTER_TEST(ec_tk_empty_test);
index 6296b8b..8aeb6bc 100644 (file)
@@ -61,7 +61,7 @@ static size_t parse_llint(struct ec_tk_int *tk, const char *str,
        return endptr - str;
 }
 
-static struct ec_parsed_tk *parse(const struct ec_tk *gen_tk,
+static struct ec_parsed_tk *ec_tk_int_parse(const struct ec_tk *gen_tk,
        const char *str)
 {
        struct ec_tk_int *tk = (struct ec_tk_int *)gen_tk;
@@ -82,8 +82,8 @@ static struct ec_parsed_tk *parse(const struct ec_tk *gen_tk,
        return parsed_tk;
 }
 
-static struct ec_tk_ops int_ops = {
-       .parse = parse,
+static struct ec_tk_ops ec_tk_int_ops = {
+       .parse = ec_tk_int_parse,
 };
 
 struct ec_tk *ec_tk_int_new(const char *id, long long int min,
@@ -91,7 +91,7 @@ struct ec_tk *ec_tk_int_new(const char *id, long long int min,
 {
        struct ec_tk_int *tk = NULL;
 
-       tk = (struct ec_tk_int *)ec_tk_new(id, &int_ops, sizeof(*tk));
+       tk = (struct ec_tk_int *)ec_tk_new(id, &ec_tk_int_ops, sizeof(*tk));
        if (tk == NULL)
                return NULL;
 
@@ -115,7 +115,7 @@ long long ec_tk_int_getval(struct ec_tk *gen_tk, const char *str)
        return val;
 }
 
-static int testcase(void)
+static int ec_tk_int_testcase(void)
 {
        struct ec_parsed_tk *p;
        struct ec_tk *tk;
@@ -192,24 +192,23 @@ static int testcase(void)
        ret |= EC_TEST_CHECK_TK_PARSE(tk, "1", NULL);
        ec_tk_free(tk);
 
-       /* /\* test completion *\/ */
-       /* tk = ec_tk_int_new(NULL, "foo"); */
-       /* if (tk == NULL) { */
-       /*      ec_log(EC_LOG_ERR, "cannot create tk\n"); */
-       /*      return -1; */
-       /* } */
-       /* ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "", "foo"); */
-       /* ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "f", "oo"); */
-       /* ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foo", ""); */
-       /* ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "x", NULL); */
-       /* ec_tk_free(tk); */
+       /* test completion */
+       tk = ec_tk_int_new(NULL, 0, 10, 0);
+       if (tk == NULL) {
+               ec_log(EC_LOG_ERR, "cannot create tk\n");
+               return -1;
+       }
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "", NULL);
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "x", NULL);
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "1", NULL);
+       ec_tk_free(tk);
 
        return ret;
 }
 
-static struct ec_test test = {
+static struct ec_test ec_tk_int_test = {
        .name = "tk_int",
-       .test = testcase,
+       .test = ec_tk_int_testcase,
 };
 
-EC_REGISTER_TEST(test);
+EC_REGISTER_TEST(ec_tk_int_test);
index b498dcb..4c782dd 100644 (file)
@@ -38,7 +38,7 @@
 #include <ecoli_tk_str.h>
 #include <ecoli_test.h>
 
-static struct ec_parsed_tk *parse(const struct ec_tk *gen_tk,
+static struct ec_parsed_tk *ec_tk_or_parse(const struct ec_tk *gen_tk,
        const char *str)
 {
        struct ec_tk_or *tk = (struct ec_tk_or *)gen_tk;
@@ -70,7 +70,7 @@ static struct ec_parsed_tk *parse(const struct ec_tk *gen_tk,
        return NULL;
 }
 
-static struct ec_completed_tk *complete(const struct ec_tk *gen_tk,
+static struct ec_completed_tk *ec_tk_or_complete(const struct ec_tk *gen_tk,
        const char *str)
 {
        struct ec_tk_or *tk = (struct ec_tk_or *)gen_tk;
@@ -90,7 +90,7 @@ static struct ec_completed_tk *complete(const struct ec_tk *gen_tk,
        return completed_tk;
 }
 
-static void free_priv(struct ec_tk *gen_tk)
+static void ec_tk_or_free_priv(struct ec_tk *gen_tk)
 {
        struct ec_tk_or *tk = (struct ec_tk_or *)gen_tk;
        unsigned int i;
@@ -100,17 +100,17 @@ static void free_priv(struct ec_tk *gen_tk)
        ec_free(tk->table);
 }
 
-static struct ec_tk_ops or_ops = {
-       .parse = parse,
-       .complete = complete,
-       .free_priv = free_priv,
+static struct ec_tk_ops ec_tk_or_ops = {
+       .parse = ec_tk_or_parse,
+       .complete = ec_tk_or_complete,
+       .free_priv = ec_tk_or_free_priv,
 };
 
 struct ec_tk *ec_tk_or_new(const char *id)
 {
        struct ec_tk_or *tk = NULL;
 
-       tk = (struct ec_tk_or *)ec_tk_new(id, &or_ops, sizeof(*tk));
+       tk = (struct ec_tk_or *)ec_tk_new(id, &ec_tk_or_ops, sizeof(*tk));
        if (tk == NULL)
                return NULL;
 
@@ -169,7 +169,7 @@ int ec_tk_or_add(struct ec_tk *gen_tk, struct ec_tk *child)
        return 0;
 }
 
-static int testcase(void)
+static int ec_tk_or_testcase(void)
 {
        struct ec_tk *tk;
        int ret = 0;
@@ -207,14 +207,22 @@ static int testcase(void)
        ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "t", "");
        ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "to", "to");
        ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "x", NULL);
+       ret |= EC_TEST_CHECK_TK_COMPLETE_LIST(tk, "",
+               "foo", "bar", "bar2", "toto", "titi", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_COMPLETE_LIST(tk, "f",
+               "oo", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_COMPLETE_LIST(tk, "b",
+               "ar", "ar2", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_COMPLETE_LIST(tk, "t",
+               "oto", "iti", EC_TK_ENDLIST);
        ec_tk_free(tk);
 
        return ret;
 }
 
-static struct ec_test test = {
+static struct ec_test ec_tk_or_test = {
        .name = "tk_or",
-       .test = testcase,
+       .test = ec_tk_or_testcase,
 };
 
-EC_REGISTER_TEST(test);
+EC_REGISTER_TEST(ec_tk_or_test);
index 4c182c4..fb1b434 100644 (file)
 #include <stdarg.h>
 
 #include <ecoli_malloc.h>
+#include <ecoli_log.h>
+#include <ecoli_test.h>
 #include <ecoli_tk.h>
+#include <ecoli_tk_str.h>
 #include <ecoli_tk_seq.h>
 
 // XXX to handle the quote, it will be done in tk_shseq
 // it will unquote the string and parse each token separately
-static struct ec_parsed_tk *parse(const struct ec_tk *gen_tk,
+static struct ec_parsed_tk *ec_tk_seq_parse(const struct ec_tk *gen_tk,
        const char *str)
 {
        struct ec_tk_seq *tk = (struct ec_tk_seq *)gen_tk;
@@ -67,7 +70,34 @@ static struct ec_parsed_tk *parse(const struct ec_tk *gen_tk,
        return NULL;
 }
 
-static void free_priv(struct ec_tk *gen_tk)
+static struct ec_completed_tk *ec_tk_seq_complete(const struct ec_tk *gen_tk,
+       const char *str)
+{
+       struct ec_tk_seq *tk = (struct ec_tk_seq *)gen_tk;
+       struct ec_completed_tk *completed_tk;
+       struct ec_parsed_tk *parsed_tk;
+       size_t len = 0;
+       unsigned int i;
+
+       if (tk->len == 0)
+               return ec_completed_tk_new();
+
+       /* parse the first tokens */
+       for (i = 0; i < tk->len - 1; i++) {
+               parsed_tk = ec_tk_parse(tk->table[i], str + len);
+               if (parsed_tk == NULL)
+                       break;
+
+               len += strlen(parsed_tk->str);
+               ec_parsed_tk_free(parsed_tk);
+       }
+
+       completed_tk = ec_tk_complete(tk->table[i], str + len);
+
+       return completed_tk;
+}
+
+static void ec_tk_seq_free_priv(struct ec_tk *gen_tk)
 {
        struct ec_tk_seq *tk = (struct ec_tk_seq *)gen_tk;
        unsigned int i;
@@ -77,16 +107,17 @@ static void free_priv(struct ec_tk *gen_tk)
        ec_free(tk->table);
 }
 
-static struct ec_tk_ops seq_ops = {
-       .parse = parse,
-       .free_priv = free_priv,
+static struct ec_tk_ops ec_tk_seq_ops = {
+       .parse = ec_tk_seq_parse,
+       .complete = ec_tk_seq_complete,
+       .free_priv = ec_tk_seq_free_priv,
 };
 
 struct ec_tk *ec_tk_seq_new(const char *id)
 {
        struct ec_tk_seq *tk = NULL;
 
-       tk = (struct ec_tk_seq *)ec_tk_new(id, &seq_ops, sizeof(*tk));
+       tk = (struct ec_tk_seq *)ec_tk_new(id, &ec_tk_seq_ops, sizeof(*tk));
        if (tk == NULL)
                return NULL;
 
@@ -131,10 +162,12 @@ 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;
        struct ec_tk **table;
 
+       // XXX check tk type
+
        assert(tk != NULL);
        assert(child != NULL);
 
-       table = ec_realloc(tk->table, tk->len + 1);
+       table = ec_realloc(tk->table, (tk->len + 1) * sizeof(*tk->table));
        if (table == NULL)
                return -1;
 
@@ -145,3 +178,51 @@ int ec_tk_seq_add(struct ec_tk *gen_tk, struct ec_tk *child)
        return 0;
 }
 
+static int ec_tk_seq_testcase(void)
+{
+       struct ec_tk *tk;
+       int ret = 0;
+
+       /* all inputs starting with foo should match */
+       tk = ec_tk_seq_new_list(NULL,
+               ec_tk_str_new(NULL, "foo"),
+               ec_tk_str_new(NULL, "bar"),
+               EC_TK_ENDLIST);
+       if (tk == NULL) {
+               ec_log(EC_LOG_ERR, "cannot create tk\n");
+               return -1;
+       }
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, "foobar", "foobar");
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, "foobarxxx", "foobar");
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, " foobar", NULL);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, "foo", NULL);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, "bar", NULL);
+       ec_tk_free(tk);
+
+       /* test completion */
+       tk = ec_tk_seq_new_list(NULL,
+               ec_tk_str_new(NULL, "foo"),
+               ec_tk_str_new(NULL, "bar"),
+               EC_TK_ENDLIST);
+       if (tk == NULL) {
+               ec_log(EC_LOG_ERR, "cannot create tk\n");
+               return -1;
+       }
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "", "foo");
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "f", "oo");
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foo", "bar");
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foob", "ar");
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foobar", "");
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "x", NULL);
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foobarx", NULL);
+       ec_tk_free(tk);
+
+       return ret;
+}
+
+static struct ec_test ec_tk_seq_test = {
+       .name = "tk_seq",
+       .test = ec_tk_seq_testcase,
+};
+
+EC_REGISTER_TEST(ec_tk_seq_test);
index adc57c4..20facf8 100644 (file)
@@ -34,7 +34,7 @@
 #include <ecoli_tk.h>
 #include <ecoli_tk_space.h>
 
-static struct ec_parsed_tk *parse(const struct ec_tk *gen_tk,
+static struct ec_parsed_tk *ec_tk_space_parse(const struct ec_tk *gen_tk,
        const char *str)
 {
        struct ec_parsed_tk *parsed_tk;
@@ -55,12 +55,12 @@ static struct ec_parsed_tk *parse(const struct ec_tk *gen_tk,
        return parsed_tk;
 }
 
-static struct ec_tk_ops space_ops = {
-       .parse = parse,
+static struct ec_tk_ops ec_tk_space_ops = {
+       .parse = ec_tk_space_parse,
 };
 
 struct ec_tk *ec_tk_space_new(const char *id)
 {
-       return ec_tk_new(id, &space_ops, sizeof(struct ec_tk_space));
+       return ec_tk_new(id, &ec_tk_space_ops, sizeof(struct ec_tk_space));
 }
 
index f7b18fb..ad5d119 100644 (file)
@@ -35,7 +35,7 @@
 #include <ecoli_tk.h>
 #include <ecoli_tk_str.h>
 
-static struct ec_parsed_tk *parse(const struct ec_tk *gen_tk,
+static struct ec_parsed_tk *ec_tk_str_parse(const struct ec_tk *gen_tk,
        const char *str)
 {
        struct ec_tk_str *tk = (struct ec_tk_str *)gen_tk;
@@ -53,7 +53,7 @@ static struct ec_parsed_tk *parse(const struct ec_tk *gen_tk,
        return parsed_tk;
 }
 
-static struct ec_completed_tk *complete(const struct ec_tk *gen_tk,
+static struct ec_completed_tk *ec_tk_str_complete(const struct ec_tk *gen_tk,
        const char *str)
 {
        struct ec_tk_str *tk = (struct ec_tk_str *)gen_tk;
@@ -85,17 +85,17 @@ static struct ec_completed_tk *complete(const struct ec_tk *gen_tk,
        return completed_tk;
 }
 
-static void free_priv(struct ec_tk *gen_tk)
+static void ec_tk_str_free_priv(struct ec_tk *gen_tk)
 {
        struct ec_tk_str *tk = (struct ec_tk_str *)gen_tk;
 
        ec_free(tk->string);
 }
 
-static struct ec_tk_ops str_ops = {
-       .parse = parse,
-       .complete = complete,
-       .free_priv = free_priv,
+static struct ec_tk_ops ec_tk_str_ops = {
+       .parse = ec_tk_str_parse,
+       .complete = ec_tk_str_complete,
+       .free_priv = ec_tk_str_free_priv,
 };
 
 struct ec_tk *ec_tk_str_new(const char *id, const char *str)
@@ -103,7 +103,7 @@ struct ec_tk *ec_tk_str_new(const char *id, const char *str)
        struct ec_tk_str *tk = NULL;
        char *s = NULL;
 
-       tk = (struct ec_tk_str *)ec_tk_new(id, &str_ops, sizeof(*tk));
+       tk = (struct ec_tk_str *)ec_tk_new(id, &ec_tk_str_ops, sizeof(*tk));
        if (tk == NULL)
                goto fail;
 
@@ -122,7 +122,7 @@ fail:
        return NULL;
 }
 
-static int testcase(void)
+static int ec_tk_str_testcase(void)
 {
        struct ec_tk *tk;
        int ret = 0;
@@ -177,9 +177,9 @@ static int testcase(void)
        return ret;
 }
 
-static struct ec_test test = {
+static struct ec_test ec_tk_str_test = {
        .name = "tk_str",
-       .test = testcase,
+       .test = ec_tk_str_testcase,
 };
 
-EC_REGISTER_TEST(test);
+EC_REGISTER_TEST(ec_tk_str_test);