save
[protos/libecoli.git] / lib / ecoli_tk_or.c
index 9276c70..55f3e69 100644 (file)
 #include <stdarg.h>
 
 #include <ecoli_malloc.h>
+#include <ecoli_log.h>
+#include <ecoli_strvec.h>
 #include <ecoli_tk.h>
 #include <ecoli_tk_or.h>
 #include <ecoli_tk_str.h>
 #include <ecoli_test.h>
 
-static struct ec_parsed_tk *parse(const struct ec_tk *gen_tk,
-       const char *str)
+struct ec_tk_or {
+       struct ec_tk gen;
+       struct ec_tk **table;
+       unsigned int len;
+};
+
+static struct ec_parsed_tk *ec_tk_or_parse(const struct ec_tk *gen_tk,
+       const struct ec_strvec *strvec)
 {
        struct ec_tk_or *tk = (struct ec_tk_or *)gen_tk;
-       struct ec_parsed_tk *parsed_tk, *child_parsed_tk;
+       struct ec_parsed_tk *parsed_tk, *child_parsed_tk = NULL;
+       struct ec_strvec *match_strvec;
        unsigned int i;
 
-       parsed_tk = ec_parsed_tk_new(gen_tk);
+       parsed_tk = ec_parsed_tk_new();
        if (parsed_tk == NULL)
-               return NULL;
+               goto fail;
 
        for (i = 0; i < tk->len; i++) {
-               child_parsed_tk = ec_tk_parse(tk->table[i], str);
-               if (child_parsed_tk != NULL)
+               child_parsed_tk = ec_tk_parse_tokens(tk->table[i], strvec);
+               if (child_parsed_tk == NULL)
+                       goto fail;
+               if (ec_parsed_tk_matches(child_parsed_tk))
                        break;
+               ec_parsed_tk_free(child_parsed_tk);
+               child_parsed_tk = NULL;
        }
 
-       if (child_parsed_tk == NULL)
+       /* no match */
+       if (i == tk->len)
+               return parsed_tk;
+
+       match_strvec = ec_strvec_dup(child_parsed_tk->strvec);
+       if (match_strvec == NULL)
                goto fail;
 
+       ec_parsed_tk_set_match(parsed_tk, gen_tk, match_strvec);
        ec_parsed_tk_add_child(parsed_tk, child_parsed_tk);
 
-       parsed_tk->str = ec_strndup(child_parsed_tk->str,
-               strlen(child_parsed_tk->str));
-
        return parsed_tk;
 
  fail:
+       ec_parsed_tk_free(child_parsed_tk);
        ec_parsed_tk_free(parsed_tk);
        return NULL;
 }
 
-static struct ec_completed_tk *complete(const struct ec_tk *gen_tk,
-       const char *str)
+static struct ec_completed_tk *ec_tk_or_complete(const struct ec_tk *gen_tk,
+       const struct ec_strvec *strvec)
 {
        struct ec_tk_or *tk = (struct ec_tk_or *)gen_tk;
-       struct ec_completed_tk *completed_tk = NULL, *child_completed_tk;
+       struct ec_completed_tk *completed_tk, *child_completed_tk;
        size_t n;
 
+       completed_tk = ec_completed_tk_new();
+       if (completed_tk == NULL)
+               return NULL;
+
        for (n = 0; n < tk->len; n++) {
-               child_completed_tk = ec_tk_complete(tk->table[n], str);
+               child_completed_tk = ec_tk_complete_tokens(tk->table[n],
+                       strvec);
 
-               if (child_completed_tk == NULL)
+               if (child_completed_tk == NULL) // XXX fail instead?
                        continue;
 
-               completed_tk = ec_completed_tk_merge(completed_tk,
-                       child_completed_tk);
+               ec_completed_tk_merge(completed_tk, child_completed_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;
@@ -99,17 +120,18 @@ 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 = {
+       .typename = "or",
+       .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;
 
@@ -124,6 +146,7 @@ struct ec_tk *ec_tk_or_new_list(const char *id, ...)
        struct ec_tk_or *tk = NULL;
        struct ec_tk *child;
        va_list ap;
+       int fail = 0;
 
        va_start(ap, id);
 
@@ -134,12 +157,18 @@ struct ec_tk *ec_tk_or_new_list(const char *id, ...)
        for (child = va_arg(ap, struct ec_tk *);
             child != EC_TK_ENDLIST;
             child = va_arg(ap, struct ec_tk *)) {
-               if (child == NULL)
-                       goto fail;
 
-               ec_tk_or_add(&tk->gen, child);
+               /* on error, don't quit the loop to avoid leaks */
+               if (fail == 1 || child == NULL ||
+                               ec_tk_or_add(&tk->gen, child) < 0) {
+                       fail = 1;
+                       ec_tk_free(child);
+               }
        }
 
+       if (fail == 1)
+               goto fail;
+
        va_end(ap);
        return &tk->gen;
 
@@ -165,27 +194,32 @@ int ec_tk_or_add(struct ec_tk *gen_tk, struct ec_tk *child)
        table[tk->len] = child;
        tk->len ++;
 
+       child->parent = gen_tk;
+       TAILQ_INSERT_TAIL(&gen_tk->children, child, next);
+
        return 0;
 }
 
-static int testcase(void)
+static int ec_tk_or_testcase(void)
 {
        struct ec_tk *tk;
        int ret = 0;
 
-       /* all inputs starting with foo should match */
        tk = ec_tk_or_new_list(NULL,
                ec_tk_str_new(NULL, "foo"),
                ec_tk_str_new(NULL, "bar"),
                EC_TK_ENDLIST);
        if (tk == NULL) {
-               printf("cannot create tk\n");
+               ec_log(EC_LOG_ERR, "cannot create tk\n");
                return -1;
        }
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, "foo", "foo");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, "fooxxx", "foo");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, "bar", "bar");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, "oo", NULL);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "bar", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo", "bar", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, " ", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "foox", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "toto", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "", EC_TK_ENDLIST);
        ec_tk_free(tk);
 
        /* test completion */
@@ -197,23 +231,45 @@ static int testcase(void)
                ec_tk_str_new(NULL, "titi"),
                EC_TK_ENDLIST);
        if (tk == NULL) {
-               printf("cannot create tk\n");
+               ec_log(EC_LOG_ERR, "cannot create tk\n");
                return -1;
        }
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "", "");
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "f", "oo");
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "b", "ar");
-       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(tk,
+               "", EC_TK_ENDLIST,
+               "foo", "bar", "bar2", "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,
+               "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 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);