#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;
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;
struct ec_tk_or *tk = NULL;
struct ec_tk *child;
va_list ap;
+ int fail = 0;
va_start(ap, 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;
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 */
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);