]> git.droids-corp.org - protos/libecoli.git/commitdiff
save
authorOlivier Matz <zer0@droids-corp.org>
Thu, 15 Jun 2017 20:24:50 +0000 (22:24 +0200)
committerOlivier Matz <zer0@droids-corp.org>
Thu, 15 Jun 2017 20:24:50 +0000 (22:24 +0200)
71 files changed:
lib/Makefile
lib/ecoli_node.c [new file with mode: 0644]
lib/ecoli_node.h [new file with mode: 0644]
lib/ecoli_node_cmd.c [new file with mode: 0644]
lib/ecoli_node_cmd.h [new file with mode: 0644]
lib/ecoli_node_empty.c [new file with mode: 0644]
lib/ecoli_node_empty.h [new file with mode: 0644]
lib/ecoli_node_expr.c [new file with mode: 0644]
lib/ecoli_node_expr.h [new file with mode: 0644]
lib/ecoli_node_expr_test.c [new file with mode: 0644]
lib/ecoli_node_int.c [new file with mode: 0644]
lib/ecoli_node_int.h [new file with mode: 0644]
lib/ecoli_node_many.c [new file with mode: 0644]
lib/ecoli_node_many.h [new file with mode: 0644]
lib/ecoli_node_option.c [new file with mode: 0644]
lib/ecoli_node_option.h [new file with mode: 0644]
lib/ecoli_node_or.c [new file with mode: 0644]
lib/ecoli_node_or.h [new file with mode: 0644]
lib/ecoli_node_re.c [new file with mode: 0644]
lib/ecoli_node_re.h [new file with mode: 0644]
lib/ecoli_node_re_lex.c [new file with mode: 0644]
lib/ecoli_node_re_lex.h [new file with mode: 0644]
lib/ecoli_node_seq.c [new file with mode: 0644]
lib/ecoli_node_seq.h [new file with mode: 0644]
lib/ecoli_node_sh_lex.c [new file with mode: 0644]
lib/ecoli_node_sh_lex.h [new file with mode: 0644]
lib/ecoli_node_space.c [new file with mode: 0644]
lib/ecoli_node_space.h [new file with mode: 0644]
lib/ecoli_node_str.c [new file with mode: 0644]
lib/ecoli_node_str.h [new file with mode: 0644]
lib/ecoli_node_subset.c [new file with mode: 0644]
lib/ecoli_node_subset.h [new file with mode: 0644]
lib/ecoli_node_weakref.c [new file with mode: 0644]
lib/ecoli_node_weakref.h [new file with mode: 0644]
lib/ecoli_test.c
lib/ecoli_test.h
lib/ecoli_tk.c [deleted file]
lib/ecoli_tk.h [deleted file]
lib/ecoli_tk_cmd.c [deleted file]
lib/ecoli_tk_cmd.h [deleted file]
lib/ecoli_tk_empty.c [deleted file]
lib/ecoli_tk_empty.h [deleted file]
lib/ecoli_tk_expr.c [deleted file]
lib/ecoli_tk_expr.h [deleted file]
lib/ecoli_tk_expr_test.c [deleted file]
lib/ecoli_tk_int.c [deleted file]
lib/ecoli_tk_int.h [deleted file]
lib/ecoli_tk_many.c [deleted file]
lib/ecoli_tk_many.h [deleted file]
lib/ecoli_tk_option.c [deleted file]
lib/ecoli_tk_option.h [deleted file]
lib/ecoli_tk_or.c [deleted file]
lib/ecoli_tk_or.h [deleted file]
lib/ecoli_tk_re.c [deleted file]
lib/ecoli_tk_re.h [deleted file]
lib/ecoli_tk_re_lex.c [deleted file]
lib/ecoli_tk_re_lex.h [deleted file]
lib/ecoli_tk_seq.c [deleted file]
lib/ecoli_tk_seq.h [deleted file]
lib/ecoli_tk_sh_lex.c [deleted file]
lib/ecoli_tk_sh_lex.h [deleted file]
lib/ecoli_tk_space.c [deleted file]
lib/ecoli_tk_space.h [deleted file]
lib/ecoli_tk_str.c [deleted file]
lib/ecoli_tk_str.h [deleted file]
lib/ecoli_tk_subset.c [deleted file]
lib/ecoli_tk_subset.h [deleted file]
lib/ecoli_tk_weakref.c [deleted file]
lib/ecoli_tk_weakref.h [deleted file]
lib/main-readline.c
lib/todo.txt

index d868c399d49ed5073ec40421552a665cb98f16ea..da9961a760da23a34372c297b775620eb137ba71 100644 (file)
@@ -39,23 +39,23 @@ srcs += ecoli_log.c
 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
-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
+srcs += ecoli_node.c
+srcs += ecoli_node_cmd.c
+srcs += ecoli_node_empty.c
+srcs += ecoli_node_expr.c
+srcs += ecoli_node_expr_test.c
+srcs += ecoli_node_int.c
+srcs += ecoli_node_many.c
+srcs += ecoli_node_option.c
+srcs += ecoli_node_or.c
+srcs += ecoli_node_re.c
+srcs += ecoli_node_re_lex.c
+srcs += ecoli_node_seq.c
+srcs += ecoli_node_sh_lex.c
+srcs += ecoli_node_space.c
+srcs += ecoli_node_str.c
+srcs += ecoli_node_subset.c
+srcs += ecoli_node_weakref.c
 
 shlib-y-$(O)libecoli.so := $(srcs)
 
diff --git a/lib/ecoli_node.c b/lib/ecoli_node.c
new file mode 100644 (file)
index 0000000..c9315fa
--- /dev/null
@@ -0,0 +1,719 @@
+/*
+ * 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 <assert.h>
+#include <errno.h>
+
+#include <ecoli_malloc.h>
+#include <ecoli_strvec.h>
+#include <ecoli_keyval.h>
+#include <ecoli_log.h>
+#include <ecoli_node.h>
+
+static struct ec_node_type_list node_type_list =
+       TAILQ_HEAD_INITIALIZER(node_type_list);
+
+struct ec_node_type *ec_node_type_lookup(const char *name)
+{
+       struct ec_node_type *type;
+
+       TAILQ_FOREACH(type, &node_type_list, next) {
+               if (!strcmp(name, type->name))
+                       return type;
+       }
+
+       return NULL;
+}
+
+int ec_node_type_register(struct ec_node_type *type)
+{
+       if (ec_node_type_lookup(type->name) != NULL)
+               return -EEXIST;
+       if (type->size < sizeof(struct ec_node))
+               return -EINVAL;
+
+       TAILQ_INSERT_TAIL(&node_type_list, type, next);
+
+       return 0;
+}
+
+void ec_node_type_dump(FILE *out)
+{
+       struct ec_node_type *type;
+
+       TAILQ_FOREACH(type, &node_type_list, next)
+               fprintf(out, "%s\n", type->name);
+}
+
+struct ec_node *__ec_node_new(const struct ec_node_type *type, const char *id)
+{
+       struct ec_node *node = NULL;
+       char buf[256]; // XXX
+
+       ec_log(EC_LOG_DEBUG, "create node type=%s id=%s\n", type->name, id);
+
+       node = ec_calloc(1, type->size);
+       if (node == NULL)
+               goto fail;
+
+       TAILQ_INIT(&node->children);
+       node->type = type;
+       node->refcnt = 1;
+
+       if (id != NULL) {
+               node->id = ec_strdup(id);
+               if (node->id == NULL)
+                       goto fail;
+       }
+
+       snprintf(buf, sizeof(buf), "<%s>", type->name);
+       node->desc = ec_strdup(buf); // XXX ec_asprintf ?
+       if (node->desc == NULL)
+               goto fail;
+
+       node->attrs = ec_keyval_new();
+       if (node->attrs == NULL)
+               goto fail;
+
+       return node;
+
+ fail:
+       ec_node_free(node);
+       return NULL;
+}
+
+struct ec_node *ec_node_new(const char *typename, const char *id)
+{
+       struct ec_node_type *type;
+
+       type = ec_node_type_lookup(typename);
+       if (type == NULL) {
+               ec_log(EC_LOG_ERR, "type=%s does not exist\n", typename);
+               return NULL;
+       }
+
+       return __ec_node_new(type, id);
+}
+
+void ec_node_free(struct ec_node *node)
+{
+       if (node == NULL)
+               return;
+
+       assert(node->refcnt > 0);
+
+       if (--node->refcnt > 0)
+               return;
+
+       if (node->type != NULL && node->type->free_priv != NULL)
+               node->type->free_priv(node);
+       ec_free(node->id);
+       ec_free(node->desc);
+       ec_free(node->attrs);
+       ec_free(node);
+}
+
+struct ec_node *ec_node_clone(struct ec_node *node)
+{
+       if (node != NULL)
+               node->refcnt++;
+       return node;
+}
+
+struct ec_node *ec_node_find(struct ec_node *node, const char *id)
+{
+       struct ec_node *child, *ret;
+       const char *node_id = ec_node_id(node);
+
+       if (id != NULL && node_id != NULL && !strcmp(node_id, id))
+               return node;
+
+       TAILQ_FOREACH(child, &node->children, next) {
+               ret = ec_node_find(child, id);
+               if (ret != NULL)
+                       return ret;
+       }
+
+       return NULL;
+}
+
+struct ec_keyval *ec_node_attrs(const struct ec_node *node)
+{
+       return node->attrs;
+}
+
+const char *ec_node_id(const struct ec_node *node)
+{
+       return node->id;
+}
+
+struct ec_node *ec_node_parent(const struct ec_node *node)
+{
+       return node->parent;
+}
+
+static void __ec_node_dump(FILE *out,
+       const struct ec_node *node, size_t indent)
+{
+       struct ec_node *child;
+       size_t i;
+       const char *id = "None", *typename = "None";
+
+       if (node->id != NULL)
+               id = node->id;
+       typename = node->type->name;
+
+       /* XXX enhance */
+       for (i = 0; i < indent; i++) {
+               if (i % 2)
+                       fprintf(out, " ");
+               else
+                       fprintf(out, "|");
+       }
+
+       fprintf(out, "node_type=%s id=%s\n", typename, id);
+       TAILQ_FOREACH(child, &node->children, next)
+               __ec_node_dump(out, child, indent + 2);
+}
+
+void ec_node_dump(FILE *out, const struct ec_node *node)
+{
+       fprintf(out, "------------------- node dump:\n"); //XXX
+
+       if (node == NULL) {
+               fprintf(out, "node is NULL\n");
+               return;
+       }
+
+       __ec_node_dump(out, node, 0);
+}
+
+struct ec_parsed *ec_node_parse(struct ec_node *node, const char *str)
+{
+       struct ec_strvec *strvec = NULL;
+       struct ec_parsed *parsed;
+
+       errno = ENOMEM;
+       strvec = ec_strvec_new();
+       if (strvec == NULL)
+               goto fail;
+
+       if (ec_strvec_add(strvec, str) < 0)
+               goto fail;
+
+       parsed = ec_node_parse_strvec(node, strvec);
+       if (parsed == NULL)
+               goto fail;
+
+       ec_strvec_free(strvec);
+       return parsed;
+
+ fail:
+       ec_strvec_free(strvec);
+       return NULL;
+}
+
+struct ec_parsed *ec_node_parse_strvec(struct ec_node *node,
+       const struct ec_strvec *strvec)
+{
+       struct ec_parsed *parsed;
+       int ret;
+
+       /* build the node if required */
+       if (node->type->build != NULL) {
+               if ((node->flags & EC_NODE_F_BUILT) == 0) {
+                       ret = node->type->build(node);
+                       if (ret < 0) {
+                               errno = -ret;
+                               return NULL;
+                       }
+               }
+       }
+       node->flags |= EC_NODE_F_BUILT;
+
+       if (node->type->parse == NULL) {
+               errno = ENOTSUP;
+               return NULL;
+       }
+
+       parsed = node->type->parse(node, strvec);
+
+       return parsed;
+}
+
+
+struct ec_parsed *ec_parsed_new(void)
+{
+       struct ec_parsed *parsed = NULL;
+
+       parsed = ec_calloc(1, sizeof(*parsed));
+       if (parsed == NULL)
+               goto fail;
+
+       TAILQ_INIT(&parsed->children);
+
+       return parsed;
+
+ fail:
+       return NULL;
+}
+
+void ec_parsed_set_match(struct ec_parsed *parsed,
+       const struct ec_node *node, struct ec_strvec *strvec)
+{
+       parsed->node = node;
+       parsed->strvec = strvec;
+}
+
+void ec_parsed_free_children(struct ec_parsed *parsed)
+{
+       struct ec_parsed *child;
+
+       if (parsed == NULL)
+               return;
+
+       while (!TAILQ_EMPTY(&parsed->children)) {
+               child = TAILQ_FIRST(&parsed->children);
+               TAILQ_REMOVE(&parsed->children, child, next);
+               ec_parsed_free(child);
+       }
+}
+
+void ec_parsed_free(struct ec_parsed *parsed)
+{
+       if (parsed == NULL)
+               return;
+
+       ec_parsed_free_children(parsed);
+       ec_strvec_free(parsed->strvec);
+       ec_free(parsed);
+}
+
+static void __ec_parsed_dump(FILE *out,
+       const struct ec_parsed *parsed, size_t indent)
+{
+       struct ec_parsed *child;
+       const struct ec_strvec *vec;
+       size_t i;
+       const char *id = "None", *typename = "None";
+
+       if (parsed->node != NULL) {
+               if (parsed->node->id != NULL)
+                       id = parsed->node->id;
+               typename = parsed->node->type->name;
+       }
+
+       /* XXX enhance */
+       for (i = 0; i < indent; i++) {
+               if (i % 2)
+                       fprintf(out, " ");
+               else
+                       fprintf(out, "|");
+       }
+
+       fprintf(out, "node_type=%s id=%s vec=[", typename, id);
+       vec = ec_parsed_strvec(parsed);
+       for (i = 0; i < ec_strvec_len(vec); i++)
+               fprintf(out, "%s<%s>",
+                       i == 0 ? "" : ",",
+                       ec_strvec_val(vec, i));
+       fprintf(out, "]\n");
+
+       TAILQ_FOREACH(child, &parsed->children, next)
+               __ec_parsed_dump(out, child, indent + 2);
+}
+
+void ec_parsed_dump(FILE *out, const struct ec_parsed *parsed)
+{
+       fprintf(out, "------------------- parsed dump:\n"); //XXX
+
+       if (parsed == NULL) {
+               fprintf(out, "parsed is NULL, error in parse\n");
+               return;
+       }
+       if (!ec_parsed_matches(parsed)) {
+               fprintf(out, "no match\n");
+               return;
+       }
+
+       __ec_parsed_dump(out, parsed, 0);
+}
+
+void ec_parsed_add_child(struct ec_parsed *parsed,
+       struct ec_parsed *child)
+{
+       TAILQ_INSERT_TAIL(&parsed->children, child, next);
+}
+
+void ec_parsed_del_child(struct ec_parsed *parsed,
+       struct ec_parsed *child)
+{
+       TAILQ_REMOVE(&parsed->children, child, next);
+}
+
+struct ec_parsed *ec_parsed_find_first(struct ec_parsed *parsed,
+       const char *id)
+{
+       struct ec_parsed *child, *ret;
+
+       if (parsed == NULL)
+               return NULL;
+
+       if (parsed->node != NULL &&
+                       parsed->node->id != NULL &&
+                       !strcmp(parsed->node->id, id))
+               return parsed;
+
+       TAILQ_FOREACH(child, &parsed->children, next) {
+               ret = ec_parsed_find_first(child, id);
+               if (ret != NULL)
+                       return ret;
+       }
+
+       return NULL;
+}
+
+const struct ec_strvec *ec_parsed_strvec(
+       const struct ec_parsed *parsed)
+{
+       if (parsed == NULL || parsed->strvec == NULL)
+               return NULL;
+
+       return parsed->strvec;
+}
+
+/* number of parsed strings in the vector */
+size_t ec_parsed_len(const struct ec_parsed *parsed)
+{
+       if (parsed == NULL || parsed->strvec == NULL)
+               return 0;
+
+       return ec_strvec_len(parsed->strvec);
+}
+
+size_t ec_parsed_matches(const struct ec_parsed *parsed)
+{
+       if (parsed == NULL)
+               return 0;
+
+       if (parsed->node == NULL && TAILQ_EMPTY(&parsed->children))
+               return 0;
+
+       return 1;
+}
+
+struct ec_completed *ec_completed_new(void)
+{
+       struct ec_completed *completed = NULL;
+
+       completed = ec_calloc(1, sizeof(*completed));
+       if (completed == NULL)
+               return NULL;
+
+       TAILQ_INIT(&completed->elts);
+       completed->count_match = 0;
+
+       return completed;
+}
+
+struct ec_completed_elt *ec_completed_elt_new(const struct ec_node *node,
+       const char *add)
+{
+       struct ec_completed_elt *elt = NULL;
+
+       elt = ec_calloc(1, sizeof(*elt));
+       if (elt == NULL)
+               return NULL;
+
+       elt->node = node;
+       if (add != NULL) {
+               elt->add = ec_strdup(add);
+               if (elt->add == NULL) {
+                       ec_completed_elt_free(elt);
+                       return NULL;
+               }
+       }
+
+       return elt;
+}
+
+/* XXX define when to use ec_node_complete() or node->complete()
+ * (same for parse)
+ * suggestion: node->op() is internal, user calls the function
+ * other idea: have 2 functions
+ */
+struct ec_completed *ec_node_complete(struct ec_node *node,
+       const char *str)
+{
+       struct ec_strvec *strvec = NULL;
+       struct ec_completed *completed;
+
+       errno = ENOMEM;
+       strvec = ec_strvec_new();
+       if (strvec == NULL)
+               goto fail;
+
+       if (ec_strvec_add(strvec, str) < 0)
+               goto fail;
+
+       completed = ec_node_complete_strvec(node, strvec);
+       if (completed == NULL)
+               goto fail;
+
+       ec_strvec_free(strvec);
+       return completed;
+
+ fail:
+       ec_strvec_free(strvec);
+       return NULL;
+}
+
+/* default completion function: return a no-match element */
+struct ec_completed *ec_node_default_complete(const struct ec_node *gen_node,
+       const struct ec_strvec *strvec)
+{
+       struct ec_completed *completed;
+       struct ec_completed_elt *completed_elt;
+
+       (void)strvec;
+
+       completed = ec_completed_new();
+       if (completed == NULL)
+               return NULL;
+
+       completed_elt = ec_completed_elt_new(gen_node, NULL);
+       if (completed_elt == NULL) {
+               ec_completed_free(completed);
+               return NULL;
+       }
+
+       ec_completed_add_elt(completed, completed_elt);
+
+       return completed;
+}
+
+struct ec_completed *ec_node_complete_strvec(struct ec_node *node,
+       const struct ec_strvec *strvec)
+{
+       int ret;
+
+       /* build the node if required */
+       if (node->type->build != NULL) {
+               if ((node->flags & EC_NODE_F_BUILT) == 0) {
+                       ret = node->type->build(node);
+                       if (ret < 0) {
+                               errno = -ret;
+                               return NULL;
+                       }
+               }
+       }
+       node->flags |= EC_NODE_F_BUILT;
+
+       if (node->type->complete == NULL) {
+               errno = ENOTSUP;
+               return NULL;
+       }
+
+       return node->type->complete(node, strvec);
+}
+
+/* count the number of identical chars at the beginning of 2 strings */
+static size_t strcmp_count(const char *s1, const char *s2)
+{
+       size_t i = 0;
+
+       while (s1[i] && s2[i] && s1[i] == s2[i])
+               i++;
+
+       return i;
+}
+
+void ec_completed_add_elt(
+       struct ec_completed *completed, struct ec_completed_elt *elt)
+{
+       size_t n;
+
+       TAILQ_INSERT_TAIL(&completed->elts, elt, next);
+       completed->count++;
+       if (elt->add != NULL)
+               completed->count_match++;
+       if (elt->add != NULL) {
+               if (completed->smallest_start == NULL) {
+                       completed->smallest_start = ec_strdup(elt->add);
+               } else {
+                       n = strcmp_count(elt->add,
+                               completed->smallest_start);
+                       completed->smallest_start[n] = '\0';
+               }
+       }
+}
+
+void ec_completed_elt_free(struct ec_completed_elt *elt)
+{
+       ec_free(elt->add);
+       ec_free(elt);
+}
+
+void ec_completed_merge(struct ec_completed *completed1,
+       struct ec_completed *completed2)
+{
+       struct ec_completed_elt *elt;
+
+       assert(completed1 != NULL);
+       assert(completed2 != NULL);
+
+       while (!TAILQ_EMPTY(&completed2->elts)) {
+               elt = TAILQ_FIRST(&completed2->elts);
+               TAILQ_REMOVE(&completed2->elts, elt, next);
+               ec_completed_add_elt(completed1, elt);
+       }
+
+       ec_completed_free(completed2);
+}
+
+void ec_completed_free(struct ec_completed *completed)
+{
+       struct ec_completed_elt *elt;
+
+       if (completed == NULL)
+               return;
+
+       while (!TAILQ_EMPTY(&completed->elts)) {
+               elt = TAILQ_FIRST(&completed->elts);
+               TAILQ_REMOVE(&completed->elts, elt, next);
+               ec_completed_elt_free(elt);
+       }
+       ec_free(completed->smallest_start);
+       ec_free(completed);
+}
+
+void ec_completed_dump(FILE *out, const struct ec_completed *completed)
+{
+       struct ec_completed_elt *elt;
+
+       if (completed == NULL || completed->count == 0) {
+               fprintf(out, "no completion\n");
+               return;
+       }
+
+       fprintf(out, "completion: count=%u match=%u smallest_start=<%s>\n",
+               completed->count, completed->count_match,
+               completed->smallest_start);
+
+       TAILQ_FOREACH(elt, &completed->elts, next) {
+               fprintf(out, "add=<%s>, node=%p, node_type=%s\n",
+                       elt->add, elt->node, elt->node->type->name);
+       }
+}
+
+const char *ec_completed_smallest_start(
+       const struct ec_completed *completed)
+{
+       if (completed == NULL || completed->smallest_start == NULL)
+               return "";
+
+       return completed->smallest_start;
+}
+
+unsigned int ec_completed_count(
+       const struct ec_completed *completed,
+       enum ec_completed_filter_flags flags)
+{
+       unsigned int count = 0;
+
+       if (completed == NULL)
+               return count;
+
+       if (flags & EC_MATCH)
+               count += completed->count_match;
+       if (flags & EC_NO_MATCH)
+               count += (completed->count - completed->count_match); //XXX
+
+       return count;
+}
+
+struct ec_completed_iter *
+ec_completed_iter_new(struct ec_completed *completed,
+       enum ec_completed_filter_flags flags)
+{
+       struct ec_completed_iter *iter;
+
+       iter = ec_calloc(1, sizeof(*iter));
+       if (iter == NULL)
+               return NULL;
+
+       iter->completed = completed;
+       iter->flags = flags;
+       iter->cur = NULL;
+
+       return iter;
+}
+
+const struct ec_completed_elt *ec_completed_iter_next(
+       struct ec_completed_iter *iter)
+{
+       if (iter->completed == NULL)
+               return NULL;
+
+       do {
+               if (iter->cur == NULL) {
+                       iter->cur = TAILQ_FIRST(&iter->completed->elts);
+               } else {
+                       iter->cur = TAILQ_NEXT(iter->cur, next);
+               }
+
+               if (iter->cur == NULL)
+                       break;
+
+               if (iter->cur->add == NULL &&
+                               (iter->flags & EC_NO_MATCH))
+                       break;
+
+               if (iter->cur->add != NULL &&
+                               (iter->flags & EC_MATCH))
+                       break;
+
+       } while (iter->cur != NULL);
+
+       return iter->cur;
+}
+
+void ec_completed_iter_free(struct ec_completed_iter *iter)
+{
+       ec_free(iter);
+}
+
+const char *ec_node_desc(const struct ec_node *node)
+{
+       if (node->type->desc != NULL)
+               return node->type->desc(node);
+
+       return node->desc;
+}
diff --git a/lib/ecoli_node.h b/lib/ecoli_node.h
new file mode 100644 (file)
index 0000000..ad24a45
--- /dev/null
@@ -0,0 +1,259 @@
+/*
+ * 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_NODE_
+#define ECOLI_NODE_
+
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <stdio.h>
+
+#define EC_NODE_ENDLIST ((void *)1)
+
+struct ec_node;
+struct ec_parsed;
+struct ec_strvec;
+struct ec_keyval;
+
+/* return 0 on success, else -errno. */
+typedef int (*ec_node_build_t)(struct ec_node *node);
+
+typedef struct ec_parsed *(*ec_node_parse_t)(const struct ec_node *node,
+       const struct ec_strvec *strvec);
+typedef struct ec_completed *(*ec_node_complete_t)(const struct ec_node *node,
+       const struct ec_strvec *strvec);
+typedef const char * (*ec_node_desc_t)(const struct ec_node *);
+typedef void (*ec_node_init_priv_t)(struct ec_node *);
+typedef void (*ec_node_free_priv_t)(struct ec_node *);
+
+#define EC_NODE_TYPE_REGISTER(t)                                               \
+       static void ec_node_init_##t(void);                             \
+       static void __attribute__((constructor, used))                  \
+       ec_node_init_##t(void)                                          \
+       {                                                               \
+               if (ec_node_type_register(&t) < 0)                      \
+                       fprintf(stderr, "cannot register %s\n", t.name); \
+       }
+
+TAILQ_HEAD(ec_node_type_list, ec_node_type);
+
+/**
+ * A structure describing a node type.
+ */
+struct ec_node_type {
+       TAILQ_ENTRY(ec_node_type) next;  /**< Next in list. */
+       const char *name;              /**< Node type name. */
+       ec_node_build_t build; /* (re)build the node, called by generic parse */
+       ec_node_parse_t parse;
+       ec_node_complete_t complete;
+       ec_node_desc_t desc;
+       size_t size;
+       ec_node_init_priv_t init_priv;
+       ec_node_free_priv_t free_priv;
+};
+
+/**
+ * Register a node 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_node_type_register(struct ec_node_type *type);
+
+/**
+ * Lookup node type by name
+ *
+ * @param name
+ *   The name of the node type to search.
+ * @return
+ *   The node type if found, or NULL on error.
+ */
+struct ec_node_type *ec_node_type_lookup(const char *name);
+
+/**
+ * Dump registered log types
+ */
+void ec_node_type_dump(FILE *out);
+
+TAILQ_HEAD(ec_node_list, ec_node);
+
+struct ec_node {
+       const struct ec_node_type *type;
+       char *id;
+       char *desc;
+       struct ec_keyval *attrs;
+       /* XXX ensure parent and child are properly set in all nodes */
+       struct ec_node *parent;
+       unsigned int refcnt;
+#define EC_NODE_F_BUILT 0x0001 /** set if configuration is built */
+       unsigned int flags;
+
+       TAILQ_ENTRY(ec_node) next;
+       struct ec_node_list children;
+};
+
+/* create a new node when the type is known, typically called from the node
+ * code */
+struct ec_node *__ec_node_new(const struct ec_node_type *type, const char *id);
+
+/* create a_new node node */
+struct ec_node *ec_node_new(const char *typename, const char *id);
+
+void ec_node_free(struct ec_node *node);
+
+/* XXX add more accessors */
+struct ec_keyval *ec_node_attrs(const struct ec_node *node);
+struct ec_node *ec_node_parent(const struct ec_node *node);
+const char *ec_node_id(const struct ec_node *node);
+const char *ec_node_desc(const struct ec_node *node);
+
+void ec_node_dump(FILE *out, const struct ec_node *node);
+struct ec_node *ec_node_find(struct ec_node *node, const char *id);
+
+/* XXX split this file ? */
+
+TAILQ_HEAD(ec_parsed_list, ec_parsed);
+
+/*
+  node == NULL + empty children list means "no match"
+*/
+struct ec_parsed {
+       TAILQ_ENTRY(ec_parsed) next;
+       struct ec_parsed_list children;
+       const struct ec_node *node;
+       struct ec_strvec *strvec;
+};
+
+struct ec_parsed *ec_parsed_new(void);
+void ec_parsed_free(struct ec_parsed *parsed);
+struct ec_node *ec_node_clone(struct ec_node *node);
+void ec_parsed_free_children(struct ec_parsed *parsed);
+
+const struct ec_strvec *ec_parsed_strvec(
+       const struct ec_parsed *parsed);
+
+void ec_parsed_set_match(struct ec_parsed *parsed,
+       const struct ec_node *node, struct ec_strvec *strvec);
+
+/* 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() ? */
+/* a NULL return value is an error, with errno set
+  ENOTSUP: no ->parse() operation
+*/
+struct ec_parsed *ec_node_parse(struct ec_node *node, const char *str);
+
+/* mostly internal to nodes */
+/* XXX it should not reset cache
+ * ... not sure... it is used by tests */
+struct ec_parsed *ec_node_parse_strvec(struct ec_node *node,
+       const struct ec_strvec *strvec);
+
+void ec_parsed_add_child(struct ec_parsed *parsed,
+       struct ec_parsed *child);
+void ec_parsed_del_child(struct ec_parsed *parsed,
+       struct ec_parsed *child);
+void ec_parsed_dump(FILE *out, const struct ec_parsed *parsed);
+
+struct ec_parsed *ec_parsed_find_first(struct ec_parsed *parsed,
+       const char *id);
+
+const char *ec_parsed_to_string(const struct ec_parsed *parsed);
+size_t ec_parsed_len(const struct ec_parsed *parsed);
+size_t ec_parsed_matches(const struct ec_parsed *parsed);
+
+struct ec_completed_elt {
+       TAILQ_ENTRY(ec_completed_elt) next;
+       const struct ec_node *node;
+       char *add;
+};
+
+TAILQ_HEAD(ec_completed_elt_list, ec_completed_elt);
+
+
+struct ec_completed {
+       struct ec_completed_elt_list elts;
+       unsigned count;
+       unsigned count_match;
+       char *smallest_start;
+};
+
+/*
+ * return a completed object filled with elts
+ * return NULL on error (nomem?)
+ */
+struct ec_completed *ec_node_complete(struct ec_node *node,
+       const char *str);
+struct ec_completed *ec_node_complete_strvec(struct ec_node *node,
+       const struct ec_strvec *strvec);
+struct ec_completed *ec_completed_new(void);
+struct ec_completed_elt *ec_completed_elt_new(const struct ec_node *node,
+       const char *add);
+void ec_completed_add_elt(struct ec_completed *completed,
+       struct ec_completed_elt *elt);
+void ec_completed_elt_free(struct ec_completed_elt *elt);
+void ec_completed_merge(struct ec_completed *completed1,
+       struct ec_completed *completed2);
+void ec_completed_free(struct ec_completed *completed);
+void ec_completed_dump(FILE *out,
+       const struct ec_completed *completed);
+struct ec_completed *ec_node_default_complete(const struct ec_node *gen_node,
+       const struct ec_strvec *strvec);
+
+/* cannot return NULL */
+const char *ec_completed_smallest_start(
+       const struct ec_completed *completed);
+
+enum ec_completed_filter_flags {
+       EC_MATCH = 1,
+       EC_NO_MATCH = 2,
+};
+
+unsigned int ec_completed_count(
+       const struct ec_completed *completed,
+       enum ec_completed_filter_flags flags);
+
+struct ec_completed_iter {
+       enum ec_completed_filter_flags flags;
+       const struct ec_completed *completed;
+       const struct ec_completed_elt *cur;
+};
+
+struct ec_completed_iter *
+ec_completed_iter_new(struct ec_completed *completed,
+       enum ec_completed_filter_flags flags);
+
+const struct ec_completed_elt *ec_completed_iter_next(
+       struct ec_completed_iter *iter);
+
+void ec_completed_iter_free(struct ec_completed_iter *iter);
+
+
+#endif
diff --git a/lib/ecoli_node_cmd.c b/lib/ecoli_node_cmd.c
new file mode 100644 (file)
index 0000000..d1d2c7d
--- /dev/null
@@ -0,0 +1,506 @@
+/*
+ * 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_node_expr.h>
+#include <ecoli_node_str.h>
+#include <ecoli_node_or.h>
+#include <ecoli_node_subset.h>
+#include <ecoli_node_int.h>
+#include <ecoli_node_many.h>
+#include <ecoli_node_seq.h>
+#include <ecoli_node_option.h>
+#include <ecoli_node_re.h>
+#include <ecoli_node_re_lex.h>
+#include <ecoli_node_cmd.h>
+
+struct ec_node_cmd {
+       struct ec_node gen;
+       char *cmd_str;           /* the command string. */
+       struct ec_node *cmd;       /* the command node. */
+       struct ec_node *lex;       /* the lexer node. */
+       struct ec_node *expr;      /* the expression parser. */
+       struct ec_node **table;    /* table of node referenced in command. */
+       unsigned int len;        /* len of the table. */
+};
+
+static int
+ec_node_cmd_eval_var(void **result, void *userctx,
+       const struct ec_parsed *var)
+{
+       const struct ec_strvec *vec;
+       struct ec_node_cmd *node = userctx;
+       struct ec_node *eval = NULL;
+       const char *str, *id;
+       unsigned int i;
+
+       (void)userctx;
+
+       /* get parsed string vector, it should contain only one str */
+       vec = ec_parsed_strvec(var);
+       if (ec_strvec_len(vec) != 1)
+               return -EINVAL;
+       str = ec_strvec_val(vec, 0);
+
+       for (i = 0; i < node->len; i++) {
+               id = ec_node_id(node->table[i]);
+               printf("i=%d id=%s\n", i, id);
+               if (id == NULL)
+                       continue;
+               if (strcmp(str, id))
+                       continue;
+               /* if id matches, use a node provided by the user... */
+               eval = ec_node_clone(node->table[i]);
+               if (eval == NULL)
+                       return -ENOMEM;
+               break;
+       }
+
+       /* ...or create a string node */
+       if (eval == NULL) {
+               eval = ec_node_str(NULL, str);
+               if (eval == NULL)
+                       return -ENOMEM;
+       }
+
+       printf("eval var %s %p\n", str, eval);
+       *result = eval;
+
+       return 0;
+}
+
+static int
+ec_node_cmd_eval_pre_op(void **result, void *userctx, void *operand,
+       const struct ec_parsed *operator)
+{
+       (void)result;
+       (void)userctx;
+       (void)operand;
+       (void)operator;
+
+       return -EINVAL;
+}
+
+static int
+ec_node_cmd_eval_post_op(void **result, void *userctx, void *operand,
+       const struct ec_parsed *operator)
+{
+       const struct ec_strvec *vec;
+       struct ec_node *eval = operand;;
+
+       (void)userctx;
+
+       /* get parsed string vector, it should contain only one str */
+       vec = ec_parsed_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_node_cmd_eval_bin_op(void **result, void *userctx, void *operand1,
+       const struct ec_parsed *operator, void *operand2)
+
+{
+       const struct ec_strvec *vec;
+       struct ec_node *out = NULL;
+       struct ec_node *in1 = operand1;
+       struct ec_node *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_strvec(operator);
+       if (ec_strvec_len(vec) != 1)
+               return -EINVAL;
+
+       if (!strcmp(ec_strvec_val(vec, 0), "|")) {
+               out = EC_NODE_OR(NULL, ec_node_clone(in1), ec_node_clone(in2));
+               if (out == NULL)
+                       return -EINVAL;
+               ec_node_free(in1);
+               ec_node_free(in2);
+               *result = out;
+       } else if (!strcmp(ec_strvec_val(vec, 0), ",")) {
+               out = EC_NODE_SUBSET(NULL, ec_node_clone(in1), ec_node_clone(in2));
+               if (out == NULL)
+                       return -EINVAL;
+               ec_node_free(in1);
+               ec_node_free(in2);
+               *result = out;
+       } else {
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int
+ec_node_cmd_eval_parenthesis(void **result, void *userctx,
+       const struct ec_parsed *open_paren,
+       const struct ec_parsed *close_paren,
+       void *value)
+{
+       const struct ec_strvec *vec;
+       struct ec_node *in = value;;
+       struct ec_node *out = NULL;;
+
+       (void)userctx;
+       (void)close_paren;
+
+       /* get parsed string vector, it should contain only one str */
+       vec = ec_parsed_strvec(open_paren);
+       if (ec_strvec_len(vec) != 1)
+               return -EINVAL;
+
+       if (!strcmp(ec_strvec_val(vec, 0), "[")) {
+               out = ec_node_option(NULL, ec_node_clone(in));
+               if (out == NULL)
+                       return -EINVAL;
+               ec_node_free(in);
+       } else if (!strcmp(ec_strvec_val(vec, 0), "(")) {
+               out = in;
+       } else {
+               return -EINVAL;
+       }
+
+       printf("eval paren\n");
+       *result = out;
+
+       return 0;
+}
+
+static void
+ec_node_cmd_eval_free(void *result, void *userctx)
+{
+       (void)userctx;
+       ec_free(result);
+}
+
+static const struct ec_node_expr_eval_ops test_ops = {
+       .eval_var = ec_node_cmd_eval_var,
+       .eval_pre_op = ec_node_cmd_eval_pre_op,
+       .eval_post_op = ec_node_cmd_eval_post_op,
+       .eval_bin_op = ec_node_cmd_eval_bin_op,
+       .eval_parenthesis = ec_node_cmd_eval_parenthesis,
+       .eval_free = ec_node_cmd_eval_free,
+};
+
+static struct ec_parsed *ec_node_cmd_parse(const struct ec_node *gen_node,
+       const struct ec_strvec *strvec)
+{
+       struct ec_node_cmd *node = (struct ec_node_cmd *)gen_node;
+
+       return ec_node_parse_strvec(node->cmd, strvec);
+}
+
+static struct ec_completed *ec_node_cmd_complete(const struct ec_node *gen_node,
+       const struct ec_strvec *strvec)
+{
+       struct ec_node_cmd *node = (struct ec_node_cmd *)gen_node;
+
+       return ec_node_complete_strvec(node->cmd, strvec);
+}
+
+static void ec_node_cmd_free_priv(struct ec_node *gen_node)
+{
+       struct ec_node_cmd *node = (struct ec_node_cmd *)gen_node;
+       unsigned int i;
+
+       ec_free(node->cmd_str);
+       ec_node_free(node->cmd);
+       ec_node_free(node->expr);
+       ec_node_free(node->lex);
+       for (i = 0; i < node->len; i++)
+               ec_node_free(node->table[i]);
+       ec_free(node->table);
+}
+
+static int ec_node_cmd_build(struct ec_node *gen_node)
+{
+       struct ec_node *expr = NULL, *lex = NULL, *cmd = NULL;
+       struct ec_parsed *p, *child;
+       struct ec_node_cmd *node = (struct ec_node_cmd *)gen_node;
+       void *result;
+       int ret;
+
+       /* build the expression parser */
+       ret = -ENOMEM;
+       expr = ec_node_new("expr", "expr");
+       if (expr == NULL)
+               goto fail;
+       ret = ec_node_expr_set_val_node(expr, ec_node_re(NULL, "[a-zA-Z0-9]+"));
+       if (ret < 0)
+               goto fail;
+       ret = ec_node_expr_add_bin_op(expr, ec_node_str(NULL, ","));
+       if (ret < 0)
+               goto fail;
+       ret = ec_node_expr_add_bin_op(expr, ec_node_str(NULL, "|"));
+       if (ret < 0)
+               goto fail;
+       ret = ec_node_expr_add_post_op(expr, ec_node_str(NULL, "+"));
+       if (ret < 0)
+               goto fail;
+       ret = ec_node_expr_add_post_op(expr, ec_node_str(NULL, "*"));
+       if (ret < 0)
+               goto fail;
+       ret = ec_node_expr_add_parenthesis(expr, ec_node_str(NULL, "["),
+               ec_node_str(NULL, "]"));
+       if (ret < 0)
+               goto fail;
+       ec_node_expr_add_parenthesis(expr, ec_node_str(NULL, "("),
+               ec_node_str(NULL, ")"));
+       if (ret < 0)
+               goto fail;
+
+       /* prepend a lexer and a "many" to the expression node */
+       ret = -ENOMEM;
+       lex = ec_node_re_lex(NULL,
+               ec_node_many(NULL, ec_node_clone(expr), 1, 0));
+       if (lex == NULL)
+               goto fail;
+
+       ret = ec_node_re_lex_add(lex, "[a-zA-Z0-9]+", 1);
+       if (ret < 0)
+               goto fail;
+       ret = ec_node_re_lex_add(lex, "[*|,()]", 1);
+       if (ret < 0)
+               goto fail;
+       ret = ec_node_re_lex_add(lex, "\\[", 1);
+       if (ret < 0)
+               goto fail;
+       ret = ec_node_re_lex_add(lex, "\\]", 1);
+       if (ret < 0)
+               goto fail;
+       ret = ec_node_re_lex_add(lex, "[         ]+", 0);
+       if (ret < 0)
+               goto fail;
+
+       /* parse the command expression */
+       ret = -ENOMEM;
+       p = ec_node_parse(lex, node->cmd_str);
+       if (p == NULL)
+               goto fail;
+
+       ret = -EINVAL;
+       if (!ec_parsed_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_node_new("seq", NULL);
+       if (cmd == NULL)
+               goto fail;
+
+       TAILQ_FOREACH(child, &TAILQ_FIRST(&p->children)->children, next) {
+               ret = ec_node_expr_eval(&result, expr, child,
+                       &test_ops, node);
+               if (ret < 0)
+                       goto fail;
+               ret = ec_node_seq_add(cmd, result);
+               if (ret < 0)
+                       goto fail;
+       }
+       ec_parsed_free(p);
+       ec_node_dump(stdout, cmd);
+
+       ec_node_free(node->expr);
+       node->expr = expr;
+       ec_node_free(node->lex);
+       node->lex = lex;
+       ec_node_free(node->cmd);
+       node->cmd = cmd;
+
+       return 0;
+
+fail:
+       ec_node_free(expr);
+       ec_node_free(lex);
+       ec_node_free(cmd);
+       return ret;
+}
+
+static struct ec_node_type ec_node_cmd_type = {
+       .name = "cmd",
+       .build = ec_node_cmd_build,
+       .parse = ec_node_cmd_parse,
+       .complete = ec_node_cmd_complete,
+       .size = sizeof(struct ec_node_cmd),
+       .free_priv = ec_node_cmd_free_priv,
+};
+
+EC_NODE_TYPE_REGISTER(ec_node_cmd_type);
+
+int ec_node_cmd_add_child(struct ec_node *gen_node, struct ec_node *child)
+{
+       struct ec_node_cmd *node = (struct ec_node_cmd *)gen_node;
+       struct ec_node **table;
+
+       // XXX check node type
+
+       assert(node != NULL);
+
+       printf("add child %s\n", child->id);
+       if (child == NULL)
+               return -EINVAL;
+
+       gen_node->flags &= ~EC_NODE_F_BUILT;
+
+       table = ec_realloc(node->table, (node->len + 1) * sizeof(*node->table));
+       if (table == NULL) {
+               ec_node_free(child);
+               return -ENOMEM;
+       }
+
+       node->table = table;
+       table[node->len] = child;
+       node->len++;
+
+       child->parent = gen_node;
+       TAILQ_INSERT_TAIL(&gen_node->children, child, next); // XXX really needed?
+
+       return 0;
+}
+
+struct ec_node *ec_node_cmd(const char *id, const char *cmd_str)
+{
+       struct ec_node *gen_node = NULL;
+       struct ec_node_cmd *node = NULL;
+
+       gen_node = __ec_node_new(&ec_node_cmd_type, id);
+       if (gen_node == NULL)
+               goto fail;
+
+       node = (struct ec_node_cmd *)gen_node;
+       node->cmd_str = ec_strdup(cmd_str);
+       if (node->cmd_str == NULL)
+               goto fail;
+
+       return gen_node;
+
+fail:
+       ec_node_free(gen_node);
+       return NULL;
+}
+
+struct ec_node *__ec_node_cmd(const char *id, const char *cmd, ...)
+{
+       struct ec_node *gen_node = NULL;
+       struct ec_node_cmd *node = NULL;
+       struct ec_node *child;
+       va_list ap;
+       int fail = 0;
+
+       va_start(ap, cmd);
+
+       gen_node = ec_node_cmd(id, cmd);
+       node = (struct ec_node_cmd *)gen_node;
+       if (node == NULL)
+               fail = 1;;
+
+       for (child = va_arg(ap, struct ec_node *);
+            child != EC_NODE_ENDLIST;
+            child = va_arg(ap, struct ec_node *)) {
+
+               /* on error, don't quit the loop to avoid leaks */
+               if (fail == 1 || child == NULL ||
+                               ec_node_cmd_add_child(&node->gen, child) < 0) {
+                       fail = 1;
+                       ec_node_free(child);
+               }
+       }
+
+       if (fail == 1)
+               goto fail;
+
+       va_end(ap);
+       return gen_node;
+
+fail:
+       ec_node_free(gen_node); /* will also free children */
+       va_end(ap);
+       return NULL;
+}
+
+static int ec_node_cmd_testcase(void)
+{
+       struct ec_node *node;
+       int ret = 0;
+
+       node = EC_NODE_CMD(NULL,
+               "command [option] (subset1, subset2) x | y",
+               ec_node_int("x", 0, 10, 10),
+               ec_node_int("y", 20, 30, 10)
+       );
+       if (node == NULL) {
+               ec_log(EC_LOG_ERR, "cannot create node\n");
+               return -1;
+       }
+       ret |= EC_TEST_CHECK_PARSE(node, 2, "command", "1");
+       ret |= EC_TEST_CHECK_PARSE(node, 2, "command", "23");
+       ret |= EC_TEST_CHECK_PARSE(node, 3, "command", "option", "23");
+       ret |= EC_TEST_CHECK_PARSE(node, -1, "command", "15");
+       ret |= EC_TEST_CHECK_PARSE(node, -1, "foo");
+       ec_node_free(node);
+
+       // XXX completion
+
+       return ret;
+}
+
+static struct ec_test ec_node_cmd_test = {
+       .name = "node_cmd",
+       .test = ec_node_cmd_testcase,
+};
+
+EC_TEST_REGISTER(ec_node_cmd_test);
diff --git a/lib/ecoli_node_cmd.h b/lib/ecoli_node_cmd.h
new file mode 100644 (file)
index 0000000..2c36f38
--- /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_NODE_CMD_
+#define ECOLI_NODE_CMD_
+
+#include <ecoli_node.h>
+
+#define EC_NODE_CMD(args...) __ec_node_cmd(args, EC_NODE_ENDLIST)
+
+struct ec_node *__ec_node_cmd(const char *id, const char *cmd_str, ...);
+
+struct ec_node *ec_node_cmd(const char *id, const char *cmd_str);
+
+/* child is consumed */
+int ec_node_cmd_add_child(struct ec_node *node, struct ec_node *child);
+
+#endif
diff --git a/lib/ecoli_node_empty.c b/lib/ecoli_node_empty.c
new file mode 100644 (file)
index 0000000..186a106
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * 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 <ecoli_malloc.h>
+#include <ecoli_log.h>
+#include <ecoli_test.h>
+#include <ecoli_strvec.h>
+#include <ecoli_node.h>
+#include <ecoli_node_empty.h>
+
+struct ec_node_empty {
+       struct ec_node gen;
+};
+
+static struct ec_parsed *ec_node_empty_parse(const struct ec_node *gen_node,
+       const struct ec_strvec *strvec)
+{
+       struct ec_parsed *parsed;
+       struct ec_strvec *match_strvec;
+
+       (void)strvec;
+
+       parsed = ec_parsed_new();
+       if (parsed == NULL)
+               goto fail;
+
+       match_strvec = ec_strvec_new();
+       if (match_strvec == NULL)
+               goto fail;
+
+       ec_parsed_set_match(parsed, gen_node, match_strvec);
+
+       return parsed;
+
+ fail:
+       ec_parsed_free(parsed);
+       return NULL;
+}
+
+static struct ec_node_type ec_node_empty_type = {
+       .name = "empty",
+       .parse = ec_node_empty_parse,
+       .complete = ec_node_default_complete,
+       .size = sizeof(struct ec_node_empty),
+};
+
+EC_NODE_TYPE_REGISTER(ec_node_empty_type);
+
+static int ec_node_empty_testcase(void)
+{
+       struct ec_node *node;
+       int ret = 0;
+
+       node = ec_node_new("empty", NULL);
+       if (node == NULL) {
+               ec_log(EC_LOG_ERR, "cannot create node\n");
+               return -1;
+       }
+       ret |= EC_TEST_CHECK_PARSE(node, 0, "foo");
+       ret |= EC_TEST_CHECK_PARSE(node, 0);
+       ret |= EC_TEST_CHECK_PARSE(node, 0, "foo", "bar");
+       ec_node_free(node);
+
+       /* never completes */
+       node = ec_node_new("empty", NULL);
+       if (node == NULL) {
+               ec_log(EC_LOG_ERR, "cannot create node\n");
+               return -1;
+       }
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "", EC_NODE_ENDLIST,
+               EC_NODE_ENDLIST,
+               "");
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "foo", EC_NODE_ENDLIST,
+               EC_NODE_ENDLIST,
+               "");
+       ec_node_free(node);
+
+       return ret;
+}
+
+static struct ec_test ec_node_empty_test = {
+       .name = "node_empty",
+       .test = ec_node_empty_testcase,
+};
+
+EC_TEST_REGISTER(ec_node_empty_test);
diff --git a/lib/ecoli_node_empty.h b/lib/ecoli_node_empty.h
new file mode 100644 (file)
index 0000000..0ef2197
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+/**
+ * This node always matches an empty string vector
+ */
+
+#ifndef ECOLI_NODE_EMPTY_
+#define ECOLI_NODE_EMPTY_
+
+struct ec_node *ec_node_empty(const char *id);
+
+#endif
diff --git a/lib/ecoli_node_expr.c b/lib/ecoli_node_expr.c
new file mode 100644 (file)
index 0000000..f80e3ba
--- /dev/null
@@ -0,0 +1,598 @@
+/*
+ * 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 <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <errno.h>
+
+#include <ecoli_malloc.h>
+#include <ecoli_log.h>
+#include <ecoli_test.h>
+#include <ecoli_strvec.h>
+#include <ecoli_node.h>
+#include <ecoli_node_seq.h>
+#include <ecoli_node_many.h>
+#include <ecoli_node_or.h>
+#include <ecoli_node_weakref.h>
+#include <ecoli_node_expr.h>
+
+struct ec_node_expr {
+       struct ec_node gen;
+
+       /* the built node */
+       struct ec_node *child;
+
+       /* the configuration nodes */
+       struct ec_node *val_node;
+       struct ec_node **bin_ops;
+       unsigned int bin_ops_len;
+       struct ec_node **pre_ops;
+       unsigned int pre_ops_len;
+       struct ec_node **post_ops;
+       unsigned int post_ops_len;
+       struct ec_node **open_ops;
+       struct ec_node **close_ops;
+       unsigned int paren_len;
+};
+
+static struct ec_parsed *ec_node_expr_parse(const struct ec_node *gen_node,
+       const struct ec_strvec *strvec)
+{
+       struct ec_node_expr *node = (struct ec_node_expr *)gen_node;
+
+       return ec_node_parse_strvec(node->child, strvec);
+}
+
+static struct ec_completed *ec_node_expr_complete(const struct ec_node *gen_node,
+       const struct ec_strvec *strvec)
+{
+       struct ec_node_expr *node = (struct ec_node_expr *)gen_node;
+
+       return ec_node_complete_strvec(node->child, strvec);
+}
+
+static void ec_node_expr_free_priv(struct ec_node *gen_node)
+{
+       struct ec_node_expr *node = (struct ec_node_expr *)gen_node;
+       unsigned int i;
+
+       ec_log(EC_LOG_DEBUG, "free %p %p %p\n", node, node->child, node->val_node);
+       ec_node_free(node->val_node);
+
+       for (i = 0; i < node->bin_ops_len; i++)
+               ec_node_free(node->bin_ops[i]);
+       ec_free(node->bin_ops);
+       for (i = 0; i < node->pre_ops_len; i++)
+               ec_node_free(node->pre_ops[i]);
+       ec_free(node->pre_ops);
+       for (i = 0; i < node->post_ops_len; i++)
+               ec_node_free(node->post_ops[i]);
+       ec_free(node->post_ops);
+       for (i = 0; i < node->paren_len; i++) {
+               ec_node_free(node->open_ops[i]);
+               ec_node_free(node->close_ops[i]);
+       }
+       ec_free(node->open_ops);
+       ec_free(node->close_ops);
+
+       ec_node_free(node->child);
+}
+
+static int ec_node_expr_build(struct ec_node *gen_node)
+{
+       struct ec_node_expr *node = (struct ec_node_expr *)gen_node;
+       struct ec_node *term = NULL, *expr = NULL, *next = NULL,
+               *pre_op = NULL, *post_op = NULL,
+               *post = NULL, *weak = NULL;
+       unsigned int i;
+       int ret;
+
+       if (node->val_node == NULL)
+               return -EINVAL;
+       if (node->bin_ops_len == 0 && node->pre_ops_len == 0 &&
+                       node->post_ops_len == 0)
+               return -EINVAL;
+
+       /* create the object, we will initialize it later: this is
+        * needed because we have a circular dependency */
+       ret = -ENOMEM;
+       weak = ec_node_new("weakref", "weak");
+       if (weak == NULL)
+               return -1;
+
+       /* prefix unary operators */
+       pre_op = ec_node_new("or", "pre-op");
+       if (pre_op == NULL)
+               goto fail;
+       for (i = 0; i < node->pre_ops_len; i++) {
+               if (ec_node_or_add(pre_op, ec_node_clone(node->pre_ops[i])) < 0)
+                       goto fail;
+       }
+
+       /* suffix unary operators */
+       post_op = ec_node_new("or", "post-op");
+       if (post_op == NULL)
+               goto fail;
+       for (i = 0; i < node->post_ops_len; i++) {
+               if (ec_node_or_add(post_op, ec_node_clone(node->post_ops[i])) < 0)
+                       goto fail;
+       }
+
+       post = ec_node_new("or", "post");
+       if (post == NULL)
+               goto fail;
+       if (ec_node_or_add(post, ec_node_clone(node->val_node)) < 0)
+               goto fail;
+       if (ec_node_or_add(post,
+               EC_NODE_SEQ(NULL,
+                       ec_node_clone(pre_op),
+                       ec_node_clone(weak))) < 0)
+               goto fail;
+       for (i = 0; i < node->paren_len; i++) {
+               if (ec_node_or_add(post, EC_NODE_SEQ(NULL,
+                                       ec_node_clone(node->open_ops[i]),
+                                       ec_node_clone(weak),
+                                       ec_node_clone(node->close_ops[i]))) < 0)
+                       goto fail;
+       }
+       term = EC_NODE_SEQ("term",
+               ec_node_clone(post),
+               ec_node_many(NULL, ec_node_clone(post_op), 0, 0)
+       );
+       if (term == NULL)
+               goto fail;
+
+       for (i = 0; i < node->bin_ops_len; i++) {
+               next = EC_NODE_SEQ("next",
+                       ec_node_clone(term),
+                       ec_node_many(NULL,
+                               EC_NODE_SEQ(NULL,
+                                       ec_node_clone(node->bin_ops[i]),
+                                       ec_node_clone(term)
+                               ),
+                               0, 0
+                       )
+               );
+               ec_node_free(term);
+               term = next;
+               if (term == NULL)
+                       goto fail;
+       }
+       expr = term;
+       term = NULL;
+
+       /* free the initial references */
+       ec_node_free(pre_op);
+       pre_op = NULL;
+       ec_node_free(post_op);
+       post_op = NULL;
+       ec_node_free(post);
+       post = NULL;
+
+       /* no need to clone here, the node is not consumed */
+       if (ec_node_weakref_set(weak, expr) < 0)
+               goto fail;
+       ec_node_free(weak);
+       weak = NULL;
+
+       node->child = expr;
+
+       return 0;
+
+fail:
+       ec_node_free(term);
+       ec_node_free(expr);
+       ec_node_free(pre_op);
+       ec_node_free(post_op);
+       ec_node_free(post);
+       ec_node_free(weak);
+
+       return ret;
+}
+
+static struct ec_node_type ec_node_expr_type = {
+       .name = "expr",
+       .build = ec_node_expr_build,
+       .parse = ec_node_expr_parse,
+       .complete = ec_node_expr_complete,
+       .size = sizeof(struct ec_node_expr),
+       .free_priv = ec_node_expr_free_priv,
+};
+
+EC_NODE_TYPE_REGISTER(ec_node_expr_type);
+
+int ec_node_expr_set_val_node(struct ec_node *gen_node, struct ec_node *val_node)
+{
+       struct ec_node_expr *node = (struct ec_node_expr *)gen_node;
+       int ret;
+
+       ret = -EINVAL;
+       if (val_node == NULL)
+               goto fail;
+       ret = -EPERM;
+       if (gen_node->flags & EC_NODE_F_BUILT)
+               goto fail;
+       ret = -EEXIST;
+       if (node->val_node != NULL)
+               goto fail;
+
+       node->val_node = val_node;
+       gen_node->flags &= ~EC_NODE_F_BUILT;
+
+       return 0;
+
+fail:
+       ec_node_free(val_node);
+       return ret;
+}
+
+/* add a binary operator */
+int ec_node_expr_add_bin_op(struct ec_node *gen_node, struct ec_node *op)
+{
+       struct ec_node_expr *node = (struct ec_node_expr *)gen_node;
+       struct ec_node **bin_ops;
+       int ret;
+
+       // XXX check node type
+
+       ret = -EINVAL;
+       if (node == NULL || op == NULL)
+               goto fail;
+       ret = -EPERM;
+       if (gen_node->flags & EC_NODE_F_BUILT)
+               goto fail;
+
+       ret = -ENOMEM;
+       bin_ops = ec_realloc(node->bin_ops,
+               (node->bin_ops_len + 1) * sizeof(*node->bin_ops));
+       if (bin_ops == NULL)
+               goto fail;;
+
+       node->bin_ops = bin_ops;
+       bin_ops[node->bin_ops_len] = op;
+       node->bin_ops_len++;
+       gen_node->flags &= ~EC_NODE_F_BUILT;
+
+       return 0;
+
+fail:
+       ec_node_free(op);
+       return ret;
+}
+
+/* add a unary pre-operator */
+int ec_node_expr_add_pre_op(struct ec_node *gen_node, struct ec_node *op)
+{
+       struct ec_node_expr *node = (struct ec_node_expr *)gen_node;
+       struct ec_node **pre_ops;
+       int ret;
+
+       // XXX check node type
+
+       ret = -EINVAL;
+       if (node == NULL || op == NULL)
+               goto fail;
+       ret = -EPERM;
+       if (gen_node->flags & EC_NODE_F_BUILT)
+               goto fail;
+
+       ret = -ENOMEM;
+       pre_ops = ec_realloc(node->pre_ops,
+               (node->pre_ops_len + 1) * sizeof(*node->pre_ops));
+       if (pre_ops == NULL)
+               goto fail;
+
+       node->pre_ops = pre_ops;
+       pre_ops[node->pre_ops_len] = op;
+       node->pre_ops_len++;
+       gen_node->flags &= ~EC_NODE_F_BUILT;
+
+       return 0;
+
+fail:
+       ec_node_free(op);
+       return ret;
+}
+
+/* add a unary post-operator */
+int ec_node_expr_add_post_op(struct ec_node *gen_node, struct ec_node *op)
+{
+       struct ec_node_expr *node = (struct ec_node_expr *)gen_node;
+       struct ec_node **post_ops;
+       int ret;
+
+       // XXX check node type
+
+       ret = -EINVAL;
+       if (node == NULL || op == NULL)
+               goto fail;
+       ret = -EPERM;
+       if (gen_node->flags & EC_NODE_F_BUILT)
+               goto fail;
+
+       ret = -ENOMEM;
+       post_ops = ec_realloc(node->post_ops,
+               (node->post_ops_len + 1) * sizeof(*node->post_ops));
+       if (post_ops == NULL)
+               goto fail;
+
+       node->post_ops = post_ops;
+       post_ops[node->post_ops_len] = op;
+       node->post_ops_len++;
+       gen_node->flags &= ~EC_NODE_F_BUILT;
+
+       return 0;
+
+fail:
+       ec_node_free(op);
+       return ret;
+}
+
+/* add parenthesis symbols */
+int ec_node_expr_add_parenthesis(struct ec_node *gen_node,
+       struct ec_node *open, struct ec_node *close)
+{
+       struct ec_node_expr *node = (struct ec_node_expr *)gen_node;
+       struct ec_node **open_ops, **close_ops;
+       int ret;
+
+       // XXX check node type
+
+       ret = -EINVAL;
+       if (node == NULL || open == NULL || close == NULL)
+               goto fail;
+       ret = -EPERM;
+       if (gen_node->flags & EC_NODE_F_BUILT)
+               goto fail;;
+
+       ret = -ENOMEM;
+       open_ops = ec_realloc(node->open_ops,
+               (node->paren_len + 1) * sizeof(*node->open_ops));
+       if (open_ops == NULL)
+               goto fail;
+       close_ops = ec_realloc(node->close_ops,
+               (node->paren_len + 1) * sizeof(*node->close_ops));
+       if (close_ops == NULL)
+               goto fail;
+
+       node->open_ops = open_ops;
+       node->close_ops = close_ops;
+       open_ops[node->paren_len] = open;
+       close_ops[node->paren_len] = close;
+       node->paren_len++;
+       gen_node->flags &= ~EC_NODE_F_BUILT;
+
+       return 0;
+
+fail:
+       ec_node_free(open);
+       ec_node_free(close);
+       return ret;
+}
+
+enum expr_node_type {
+       NONE,
+       VAL,
+       BIN_OP,
+       PRE_OP,
+       POST_OP,
+       PAREN_OPEN,
+       PAREN_CLOSE,
+};
+static enum expr_node_type get_node_type(const struct ec_node *expr_gen_node,
+       const struct ec_node *check)
+{
+       struct ec_node_expr *expr_node = (struct ec_node_expr *)expr_gen_node;
+       size_t i;
+
+       if (check == expr_node->val_node)
+               return VAL;
+
+       for (i = 0; i < expr_node->bin_ops_len; i++) {
+               if (check == expr_node->bin_ops[i])
+                       return BIN_OP;
+       }
+       for (i = 0; i < expr_node->pre_ops_len; i++) {
+               if (check == expr_node->pre_ops[i])
+                       return PRE_OP;
+       }
+       for (i = 0; i < expr_node->post_ops_len; i++) {
+               if (check == expr_node->post_ops[i])
+                       return POST_OP;
+       }
+
+       for (i = 0; i < expr_node->paren_len; i++) {
+               if (check == expr_node->open_ops[i])
+                       return PAREN_OPEN;
+       }
+       for (i = 0; i < expr_node->paren_len; i++) {
+               if (check == expr_node->close_ops[i])
+                       return PAREN_CLOSE;
+       }
+
+       return NONE;
+}
+
+struct result {
+       bool has_val;
+       void *val;
+       const struct ec_parsed *op;
+       enum expr_node_type op_type;
+};
+
+/* merge x and y results in x */
+static int merge_results(void *userctx,
+       const struct ec_node_expr_eval_ops *ops,
+       struct result *x, const struct result *y)
+{
+       int ret;
+
+       if (y->has_val == 0 && y->op == NULL)
+               return 0;
+       if (x->has_val == 0 && x->op == NULL) {
+               *x = *y;
+               return 0;
+       }
+
+       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;
+
+               return 0;
+       }
+
+       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;
+               }
+       }
+
+       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;
+
+               return 0;
+       }
+
+       assert(true); /* we should not get here */
+       return -EINVAL;
+}
+
+static int eval_expression(struct result *result,
+       void *userctx,
+       const struct ec_node_expr_eval_ops *ops,
+       const struct ec_node *expr_gen_node,
+       const struct ec_parsed *parsed)
+
+{
+       struct ec_parsed *open = NULL, *close = NULL;
+       struct result child_result;
+       struct ec_parsed *child;
+       enum expr_node_type type;
+       int ret;
+
+       memset(result, 0, sizeof(*result));
+       memset(&child_result, 0, sizeof(child_result));
+
+       type = get_node_type(expr_gen_node, parsed->node);
+       if (type == VAL) {
+               ret = ops->eval_var(&result->val, userctx, parsed);
+               if (ret < 0)
+                       goto fail;
+               result->has_val = 1;
+       } else if (type == PRE_OP || type == POST_OP || type == BIN_OP) {
+               result->op = parsed;
+               result->op_type = type;
+       }
+
+       TAILQ_FOREACH(child, &parsed->children, next) {
+
+               type = get_node_type(expr_gen_node, child->node);
+               if (type == PAREN_OPEN) {
+                       open = child;
+                       continue;
+               } else if (type == PAREN_CLOSE) {
+                       close = child;
+                       continue;
+               }
+
+               ret = eval_expression(&child_result, userctx, ops,
+                       expr_gen_node, child);
+               if (ret < 0)
+                       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;
+}
+
+int ec_node_expr_eval(void **user_result, const struct ec_node *node,
+       struct ec_parsed *parsed, const struct ec_node_expr_eval_ops *ops,
+       void *userctx)
+{
+       struct result result;
+       int ret;
+
+       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;
+
+       if (!ec_parsed_matches(parsed))
+               return -EINVAL;
+
+       ec_parsed_dump(stdout, parsed); //XXX
+       ret = eval_expression(&result, userctx, ops, node, parsed);
+       if (ret < 0)
+               return ret;
+
+       assert(result.has_val);
+       assert(result.op == NULL);
+       *user_result = result.val;
+
+       return 0;
+}
+
+/* the test case is in a separate file ecoli_node_expr_test.c */
diff --git a/lib/ecoli_node_expr.h b/lib/ecoli_node_expr.h
new file mode 100644 (file)
index 0000000..651ac7c
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * 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_NODE_EXPR_
+#define ECOLI_NODE_EXPR_
+
+#include <ecoli_node.h>
+
+/* XXX remove the _new for all other nodes */
+
+/**
+ * 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 result referencing the variable.
+ * @return
+ *   0 on success (*result must be set), or -errno on error (*result
+ *   is undefined).
+ */
+typedef int (*ec_node_expr_eval_var_t)(
+       void **result, void *userctx,
+       const struct ec_parsed *var);
+
+/**
+ * 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 result 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_node_expr_eval_pre_op_t)(
+       void **result, void *userctx,
+       void *operand,
+       const struct ec_parsed *operator);
+
+typedef int (*ec_node_expr_eval_post_op_t)(
+       void **result, void *userctx,
+       void *operand,
+       const struct ec_parsed *operator);
+
+typedef int (*ec_node_expr_eval_bin_op_t)(
+       void **result, void *userctx,
+       void *operand1,
+       const struct ec_parsed *operator,
+       void *operand2);
+
+typedef int (*ec_node_expr_eval_parenthesis_t)(
+       void **result, void *userctx,
+       const struct ec_parsed *open_paren,
+       const struct ec_parsed *close_paren,
+       void * value);
+
+typedef void (*ec_node_expr_eval_free_t)(
+       void *result, void *userctx);
+
+
+struct ec_node *ec_node_expr(const char *id);
+int ec_node_expr_set_val_node(struct ec_node *gen_node, struct ec_node *val_node);
+int ec_node_expr_add_bin_op(struct ec_node *gen_node, struct ec_node *op);
+int ec_node_expr_add_pre_op(struct ec_node *gen_node, struct ec_node *op);
+int ec_node_expr_add_post_op(struct ec_node *gen_node, struct ec_node *op);
+int ec_node_expr_add_parenthesis(struct ec_node *gen_node,
+       struct ec_node *open, struct ec_node *close);
+
+struct ec_node_expr_eval_ops {
+       ec_node_expr_eval_var_t eval_var;
+       ec_node_expr_eval_pre_op_t eval_pre_op;
+       ec_node_expr_eval_post_op_t eval_post_op;
+       ec_node_expr_eval_bin_op_t eval_bin_op;
+       ec_node_expr_eval_parenthesis_t eval_parenthesis;
+       ec_node_expr_eval_free_t eval_free;
+};
+
+int ec_node_expr_eval(void **result, const struct ec_node *node,
+       struct ec_parsed *parsed, const struct ec_node_expr_eval_ops *ops,
+       void *userctx);
+
+#endif
diff --git a/lib/ecoli_node_expr_test.c b/lib/ecoli_node_expr_test.c
new file mode 100644 (file)
index 0000000..ac3e5ba
--- /dev/null
@@ -0,0 +1,304 @@
+/*
+ * 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 <errno.h>
+#include <limits.h>
+#include <stdint.h>
+#include <assert.h>
+
+#include <ecoli_malloc.h>
+#include <ecoli_strvec.h>
+#include <ecoli_test.h>
+#include <ecoli_node_int.h>
+#include <ecoli_node_str.h>
+#include <ecoli_node_re_lex.h>
+#include <ecoli_node_expr.h>
+
+struct my_eval_result {
+       int val;
+};
+
+static int
+ec_node_expr_test_eval_var(void **result, void *userctx,
+       const struct ec_parsed *var)
+{
+       const struct ec_strvec *vec;
+       struct my_eval_result *eval;
+
+       (void)userctx;
+
+       /* get parsed string vector, it should contain only one str */
+       vec = ec_parsed_strvec(var);
+       if (ec_strvec_len(vec) != 1)
+               return -EINVAL;
+
+       eval = ec_malloc(sizeof(*eval));
+       if (eval == NULL)
+               return -ENOMEM;
+
+       eval->val = atoi(ec_strvec_val(vec, 0)); // XXX use strtol
+       printf("eval var %d\n", eval->val);
+       *result = eval;
+
+       return 0;
+}
+
+static int
+ec_node_expr_test_eval_pre_op(void **result, void *userctx, void *operand,
+       const struct ec_parsed *operator)
+{
+       const struct ec_strvec *vec;
+       struct my_eval_result *eval = operand;;
+
+       (void)userctx;
+
+       /* get parsed string vector, it should contain only one str */
+       vec = ec_parsed_strvec(operator);
+       if (ec_strvec_len(vec) != 1)
+               return -EINVAL;
+
+       if (!strcmp(ec_strvec_val(vec, 0), "!"))
+               eval->val = !eval->val;
+       else
+               return -EINVAL;
+
+       printf("eval pre_op %d\n", eval->val);
+       *result = eval;
+
+       return 0;
+}
+
+static int
+ec_node_expr_test_eval_post_op(void **result, void *userctx, void *operand,
+       const struct ec_parsed *operator)
+{
+       const struct ec_strvec *vec;
+       struct my_eval_result *eval = operand;;
+
+       (void)userctx;
+
+       /* get parsed string vector, it should contain only one str */
+       vec = ec_parsed_strvec(operator);
+       if (ec_strvec_len(vec) != 1)
+               return -EINVAL;
+
+       if (!strcmp(ec_strvec_val(vec, 0), "^"))
+               eval->val = eval->val * eval->val;
+       else
+               return -EINVAL;
+
+       printf("eval post_op %d\n", eval->val);
+       *result = eval;
+
+       return 0;
+}
+
+static int
+ec_node_expr_test_eval_bin_op(void **result, void *userctx, void *operand1,
+       const struct ec_parsed *operator, void *operand2)
+
+{
+       const struct ec_strvec *vec;
+       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_strvec(operator);
+       if (ec_strvec_len(vec) != 1)
+               return -EINVAL;
+
+       if (!strcmp(ec_strvec_val(vec, 0), "+"))
+               eval1->val = eval1->val + eval2->val;
+       else if (!strcmp(ec_strvec_val(vec, 0), "*"))
+               eval1->val = eval1->val * eval2->val;
+       else
+               return -EINVAL;
+
+       printf("eval bin_op %d\n", eval1->val);
+       ec_free(eval2);
+       *result = eval1;
+
+       return 0;
+}
+
+static int
+ec_node_expr_test_eval_parenthesis(void **result, void *userctx,
+       const struct ec_parsed *open_paren,
+       const struct ec_parsed *close_paren,
+       void *value)
+{
+       (void)userctx;
+       (void)open_paren;
+       (void)close_paren;
+
+       printf("eval paren\n");
+       *result = value;
+
+       return 0;
+}
+
+static void
+ec_node_expr_test_eval_free(void *result, void *userctx)
+{
+       (void)userctx;
+       ec_free(result);
+}
+
+static const struct ec_node_expr_eval_ops test_ops = {
+       .eval_var = ec_node_expr_test_eval_var,
+       .eval_pre_op = ec_node_expr_test_eval_pre_op,
+       .eval_post_op = ec_node_expr_test_eval_post_op,
+       .eval_bin_op = ec_node_expr_test_eval_bin_op,
+       .eval_parenthesis = ec_node_expr_test_eval_parenthesis,
+       .eval_free = ec_node_expr_test_eval_free,
+};
+
+static int ec_node_expr_test_eval(struct ec_node *lex_node,
+       const struct ec_node *expr_node,
+       const char *str, int val)
+{
+       struct ec_parsed *p;
+       void *result;
+       struct my_eval_result *eval;
+       int ret;
+
+       /* XXX check node type (again and again) */
+
+       p = ec_node_parse(lex_node, str);
+       if (p == NULL)
+               return -1;
+
+       ret = ec_node_expr_eval(&result, expr_node, p, &test_ops, NULL);
+       ec_parsed_free(p);
+       if (ret < 0)
+               return -1;
+
+       /* the parsed value is an integer */
+       eval = result;
+       assert(eval != NULL);
+
+       printf("result: %d (expected %d)\n", eval->val, val);
+       if (eval->val == val)
+               ret = 0;
+       else
+               ret = -1;
+
+       ec_free(eval);
+
+       return ret;
+}
+
+static int ec_node_expr_testcase(void)
+{
+       struct ec_node *node = NULL, *lex_node = NULL;
+       int ret = 0;
+
+       node = ec_node_new("expr", "my_expr");
+       if (node == NULL)
+               return -1;
+
+       ec_node_expr_set_val_node(node, ec_node_int(NULL, 0, UCHAR_MAX, 0));
+       ec_node_expr_add_bin_op(node, ec_node_str(NULL, "+"));
+       ec_node_expr_add_bin_op(node, ec_node_str(NULL, "*"));
+       ec_node_expr_add_pre_op(node, ec_node_str(NULL, "!"));  /* not */
+       ec_node_expr_add_post_op(node, ec_node_str(NULL, "^")); /* square */
+       ec_node_expr_add_parenthesis(node, ec_node_str(NULL, "("),
+               ec_node_str(NULL, ")"));
+       ret |= EC_TEST_CHECK_PARSE(node, 1, "1");
+       ret |= EC_TEST_CHECK_PARSE(node, 1, "1", "1");
+       ret |= EC_TEST_CHECK_PARSE(node, 1, "1", "*");
+       ret |= EC_TEST_CHECK_PARSE(node, 3, "1", "*", "1");
+       ret |= EC_TEST_CHECK_PARSE(node, 3, "1", "*", "1", "*");
+       ret |= EC_TEST_CHECK_PARSE(node, 4, "1", "+", "!", "1");
+       ret |= EC_TEST_CHECK_PARSE(node, 4, "1", "^", "+", "1");
+       ret |= EC_TEST_CHECK_PARSE(node, 5, "1", "*", "1", "*", "1");
+       ret |= EC_TEST_CHECK_PARSE(node, 5, "1", "*", "1", "+", "1");
+       ret |= EC_TEST_CHECK_PARSE(node, 7, "1", "*", "1", "*", "1", "*", "1");
+       ret |= EC_TEST_CHECK_PARSE(
+               node, 10, "!", "(", "1", "*", "(", "1", "+", "1", ")", ")");
+       ret |= EC_TEST_CHECK_PARSE(node, 5, "1", "+", "!", "1", "^");
+
+       /* prepend a lexer to the expression node */
+       lex_node = ec_node_re_lex(NULL, ec_node_clone(node));
+       if (lex_node == NULL)
+               goto fail;
+
+       ret |= ec_node_re_lex_add(lex_node, "[0-9]+", 1); /* vars */
+       ret |= ec_node_re_lex_add(lex_node, "[+*!^()]", 1); /* operators */
+       ret |= ec_node_re_lex_add(lex_node, "[  ]+", 0); /* spaces */
+
+       /* valid expressions */
+       ret |= EC_TEST_CHECK_PARSE(lex_node, 1, "!1");
+       ret |= EC_TEST_CHECK_PARSE(lex_node, 1, "1^");
+       ret |= EC_TEST_CHECK_PARSE(lex_node, 1, "1^ + 1");
+       ret |= EC_TEST_CHECK_PARSE(lex_node, 1, "1 + 4 * (2 + 3^)^");
+       ret |= EC_TEST_CHECK_PARSE(lex_node, 1, "(1)");
+       ret |= EC_TEST_CHECK_PARSE(lex_node, 1, "3*!3+!3*(2+ 2)");
+       ret |= EC_TEST_CHECK_PARSE(lex_node, 1, "!!(!1)^ + !(4 + (2*3))");
+       ret |= EC_TEST_CHECK_PARSE(lex_node, 1, "(1 + 1)^ * 1^");
+
+       /* invalid expressions */
+       ret |= EC_TEST_CHECK_PARSE(lex_node, -1, "");
+       ret |= EC_TEST_CHECK_PARSE(lex_node, -1, "()");
+       ret |= EC_TEST_CHECK_PARSE(lex_node, -1, "(");
+       ret |= EC_TEST_CHECK_PARSE(lex_node, -1, ")");
+       ret |= EC_TEST_CHECK_PARSE(lex_node, -1, "+1");
+       ret |= EC_TEST_CHECK_PARSE(lex_node, -1, "1+");
+       ret |= EC_TEST_CHECK_PARSE(lex_node, -1, "1+*1");
+       ret |= EC_TEST_CHECK_PARSE(lex_node, -1, "1+(1*1");
+       ret |= EC_TEST_CHECK_PARSE(lex_node, -1, "1+!1!1)");
+
+       ret |= ec_node_expr_test_eval(lex_node, node, "1^", 1);
+       ret |= ec_node_expr_test_eval(lex_node, node, "2^", 4);
+       ret |= ec_node_expr_test_eval(lex_node, node, "!1", 0);
+       ret |= ec_node_expr_test_eval(lex_node, node, "!0", 1);
+
+       ret |= ec_node_expr_test_eval(lex_node, node, "1+1", 2);
+       ret |= ec_node_expr_test_eval(lex_node, node, "1+1*2", 4);
+       ret |= ec_node_expr_test_eval(lex_node, node, "2 * 2^", 8);
+       ret |= ec_node_expr_test_eval(lex_node, node, "(1 + !0)^ * !0^", 4);
+       ret |= ec_node_expr_test_eval(lex_node, node, "(1 + !1) * 3", 3);
+
+       ec_node_free(node);
+       ec_node_free(lex_node);
+
+       return ret;
+
+fail:
+       ec_node_free(lex_node);
+       ec_node_free(node);
+       return -1;
+}
+
+static struct ec_test ec_node_expr_test = {
+       .name = "expr",
+       .test = ec_node_expr_testcase,
+};
+
+EC_TEST_REGISTER(ec_node_expr_test);
diff --git a/lib/ecoli_node_int.c b/lib/ecoli_node_int.c
new file mode 100644 (file)
index 0000000..2391b29
--- /dev/null
@@ -0,0 +1,240 @@
+/*
+ * 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 <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <limits.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include <ecoli_log.h>
+#include <ecoli_malloc.h>
+#include <ecoli_strvec.h>
+#include <ecoli_node.h>
+#include <ecoli_node_int.h>
+#include <ecoli_test.h>
+
+struct ec_node_int {
+       struct ec_node gen;
+       bool check_min;
+       long long int min;
+       bool check_max;
+       long long int max;
+       unsigned int base;
+};
+
+static int parse_llint(struct ec_node_int *node, const char *str,
+       long long *val)
+{
+       char *endptr;
+
+       errno = 0;
+       *val = strtoll(str, &endptr, node->base);
+
+       /* out of range */
+       if ((errno == ERANGE && (*val == LLONG_MAX || *val == LLONG_MIN)) ||
+                       (errno != 0 && *val == 0))
+               return -1;
+
+       if (node->check_min && *val < node->min)
+               return -1;
+
+       if (node->check_max && *val > node->max)
+               return -1;
+
+       if (*endptr != 0)
+               return -1;
+
+       return 0;
+}
+
+static struct ec_parsed *ec_node_int_parse(const struct ec_node *gen_node,
+       const struct ec_strvec *strvec)
+{
+       struct ec_node_int *node = (struct ec_node_int *)gen_node;
+       struct ec_parsed *parsed;
+       struct ec_strvec *match_strvec;
+       const char *str;
+       long long val;
+
+       parsed = ec_parsed_new();
+       if (parsed == NULL)
+               goto fail;
+
+       if (ec_strvec_len(strvec) == 0)
+               return parsed;
+
+       str = ec_strvec_val(strvec, 0);
+       if (parse_llint(node, str, &val) < 0)
+               return parsed;
+
+       match_strvec = ec_strvec_ndup(strvec, 0, 1);
+       if (match_strvec == NULL)
+               goto fail;
+
+       ec_parsed_set_match(parsed, gen_node, match_strvec);
+
+       return parsed;
+
+ fail:
+       ec_parsed_free(parsed);
+       return NULL;
+}
+
+static struct ec_node_type ec_node_int_type = {
+       .name = "int",
+       .parse = ec_node_int_parse,
+       .complete = ec_node_default_complete,
+       .size = sizeof(struct ec_node_int),
+};
+
+EC_NODE_TYPE_REGISTER(ec_node_int_type);
+
+struct ec_node *ec_node_int(const char *id, long long int min,
+       long long int max, unsigned int base)
+{
+       struct ec_node *gen_node = NULL;
+       struct ec_node_int *node = NULL;
+
+       gen_node = __ec_node_new(&ec_node_int_type, id);
+       if (gen_node == NULL)
+               return NULL;
+       node = (struct ec_node_int *)gen_node;
+
+       node->check_min = true;
+       node->min = min;
+       node->check_max = true;
+       node->max = max;
+       node->base = base;
+
+       return &node->gen;
+}
+
+long long ec_node_int_getval(struct ec_node *gen_node, const char *str)
+{
+       struct ec_node_int *node = (struct ec_node_int *)gen_node;
+       long long val = 0;
+
+       // XXX check type here
+       // if gen_node->type != int fail
+
+       parse_llint(node, str, &val);
+
+       return val;
+}
+
+static int ec_node_int_testcase(void)
+{
+       struct ec_parsed *p;
+       struct ec_node *node;
+       const char *s;
+       int ret = 0;
+
+       node = ec_node_int(NULL, 0, 256, 0);
+       if (node == NULL) {
+               ec_log(EC_LOG_ERR, "cannot create node\n");
+               return -1;
+       }
+       ret |= EC_TEST_CHECK_PARSE(node, 1, "0");
+       ret |= EC_TEST_CHECK_PARSE(node, 1, "256", "foo");
+       ret |= EC_TEST_CHECK_PARSE(node, 1, "0x100");
+       ret |= EC_TEST_CHECK_PARSE(node, 1, " 1");
+       ret |= EC_TEST_CHECK_PARSE(node, -1, "-1");
+       ret |= EC_TEST_CHECK_PARSE(node, -1, "0x101");
+
+       p = ec_node_parse(node, "0");
+       s = ec_strvec_val(ec_parsed_strvec(p), 0);
+       EC_TEST_ASSERT(s != NULL && ec_node_int_getval(node, s) == 0);
+       ec_parsed_free(p);
+
+       p = ec_node_parse(node, "10");
+       s = ec_strvec_val(ec_parsed_strvec(p), 0);
+       EC_TEST_ASSERT(s != NULL && ec_node_int_getval(node, s) == 10);
+       ec_parsed_free(p);
+       ec_node_free(node);
+
+       node = ec_node_int(NULL, -1, LLONG_MAX, 16);
+       if (node == NULL) {
+               ec_log(EC_LOG_ERR, "cannot create node\n");
+               return -1;
+       }
+       ret |= EC_TEST_CHECK_PARSE(node, 1, "0");
+       ret |= EC_TEST_CHECK_PARSE(node, 1, "-1");
+       ret |= EC_TEST_CHECK_PARSE(node, 1, "7fffffffffffffff");
+       ret |= EC_TEST_CHECK_PARSE(node, 1, "0x7fffffffffffffff");
+       ret |= EC_TEST_CHECK_PARSE(node, -1, "-2");
+
+       p = ec_node_parse(node, "10");
+       s = ec_strvec_val(ec_parsed_strvec(p), 0);
+       EC_TEST_ASSERT(s != NULL && ec_node_int_getval(node, s) == 16);
+       ec_parsed_free(p);
+       ec_node_free(node);
+
+       node = ec_node_int(NULL, LLONG_MIN, 0, 10);
+       if (node == NULL) {
+               ec_log(EC_LOG_ERR, "cannot create node\n");
+               return -1;
+       }
+       ret |= EC_TEST_CHECK_PARSE(node, 1, "0");
+       ret |= EC_TEST_CHECK_PARSE(node, 1, "-1");
+       ret |= EC_TEST_CHECK_PARSE(node, 1, "-9223372036854775808");
+       ret |= EC_TEST_CHECK_PARSE(node, -1, "0x0");
+       ret |= EC_TEST_CHECK_PARSE(node, -1, "1");
+       ec_node_free(node);
+
+       /* test completion */
+       node = ec_node_int(NULL, 0, 10, 0);
+       if (node == NULL) {
+               ec_log(EC_LOG_ERR, "cannot create node\n");
+               return -1;
+       }
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "", EC_NODE_ENDLIST,
+               EC_NODE_ENDLIST,
+               "");
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "x", EC_NODE_ENDLIST,
+               EC_NODE_ENDLIST,
+               "");
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "1", EC_NODE_ENDLIST,
+               EC_NODE_ENDLIST,
+               "");
+       ec_node_free(node);
+
+       return ret;
+}
+
+static struct ec_test ec_node_int_test = {
+       .name = "node_int",
+       .test = ec_node_int_testcase,
+};
+
+EC_TEST_REGISTER(ec_node_int_test);
diff --git a/lib/ecoli_node_int.h b/lib/ecoli_node_int.h
new file mode 100644 (file)
index 0000000..d03dd3d
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * 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_NODE_INT_
+#define ECOLI_NODE_INT_
+
+#include <ecoli_node.h>
+
+// XXX remove min, max, base from new(), and add ec_node_int_set_limits() +
+// XXX ec_node_int_set_base() ?
+struct ec_node *ec_node_int(const char *id, long long int min,
+       long long int max, unsigned int base);
+long long ec_node_int_getval(struct ec_node *node, const char *str);
+
+#endif
diff --git a/lib/ecoli_node_many.c b/lib/ecoli_node_many.c
new file mode 100644 (file)
index 0000000..7eb359b
--- /dev/null
@@ -0,0 +1,257 @@
+/*
+ * 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 <assert.h>
+#include <stdarg.h>
+
+#include <ecoli_malloc.h>
+#include <ecoli_log.h>
+#include <ecoli_test.h>
+#include <ecoli_strvec.h>
+#include <ecoli_node.h>
+#include <ecoli_node_str.h>
+#include <ecoli_node_option.h>
+#include <ecoli_node_many.h>
+
+struct ec_node_many {
+       struct ec_node gen;
+       unsigned int min;
+       unsigned int max;
+       struct ec_node *child;
+};
+
+static struct ec_parsed *ec_node_many_parse(const struct ec_node *gen_node,
+       const struct ec_strvec *strvec)
+{
+       struct ec_node_many *node = (struct ec_node_many *)gen_node;
+       struct ec_parsed *parsed, *child_parsed;
+       struct ec_strvec *match_strvec;
+       struct ec_strvec *childvec = NULL;
+       size_t off = 0, len, count;
+
+       parsed = ec_parsed_new();
+       if (parsed == NULL)
+               goto fail;
+
+       for (count = 0; node->max == 0 || count < node->max; count++) {
+               childvec = ec_strvec_ndup(strvec, off,
+                       ec_strvec_len(strvec) - off);
+               if (childvec == NULL)
+                       goto fail;
+
+               child_parsed = ec_node_parse_strvec(node->child, childvec);
+               if (child_parsed == NULL)
+                       goto fail;
+
+               ec_strvec_free(childvec);
+               childvec = NULL;
+
+               if (!ec_parsed_matches(child_parsed)) {
+                       ec_parsed_free(child_parsed);
+                       break;
+               }
+
+               ec_parsed_add_child(parsed, child_parsed);
+
+               /* it matches an empty strvec, no need to continue */
+               len = ec_parsed_len(child_parsed);
+               if (len == 0) {
+                       ec_parsed_free(child_parsed);
+                       break;
+               }
+
+               off += len;
+       }
+
+       if (count < node->min) {
+               ec_parsed_free_children(parsed);
+               return parsed;
+       }
+
+       match_strvec = ec_strvec_ndup(strvec, 0, off);
+       if (match_strvec == NULL)
+               goto fail;
+
+       ec_parsed_set_match(parsed, gen_node, match_strvec);
+
+       return parsed;
+
+fail:
+       ec_strvec_free(childvec);
+       ec_parsed_free(parsed);
+       return NULL;
+}
+
+#if 0 //XXX missing node_many_complete
+static struct ec_completed *ec_node_many_complete(const struct ec_node *gen_node,
+       const struct ec_strvec *strvec)
+{
+       struct ec_node_many *node = (struct ec_node_many *)gen_node;
+       struct ec_completed *completed, *child_completed;
+       struct ec_strvec *childvec;
+       struct ec_parsed *parsed;
+       size_t len = 0;
+       unsigned int i;
+
+       completed = ec_completed_new();
+       if (completed == NULL)
+               return NULL;
+
+       if (node->len == 0)
+               return completed;
+
+       for (i = 0; i < node->len; i++) {
+               childvec = ec_strvec_ndup(strvec, len,
+                       ec_strvec_len(strvec) - len);
+               if (childvec == NULL)
+                       goto fail; // XXX fail ?
+
+               child_completed = ec_node_complete_strvec(node->table[i],
+                       childvec);
+               if (child_completed == NULL)
+                       goto fail;
+
+               ec_completed_merge(completed, child_completed);
+
+               parsed = ec_node_parse_strvec(node->table[i], childvec);
+               if (parsed == NULL)
+                       goto fail;
+
+               ec_strvec_free(childvec);
+               childvec = NULL;
+
+               if (!ec_parsed_matches(parsed)) {
+                       ec_parsed_free(parsed);
+                       break;
+               }
+
+               len += ec_strvec_len(parsed->strvec);
+               ec_parsed_free(parsed);
+       }
+
+       return completed;
+
+fail:
+       ec_strvec_free(childvec);
+       ec_completed_free(completed);
+       return NULL;
+}
+#endif
+
+static void ec_node_many_free_priv(struct ec_node *gen_node)
+{
+       struct ec_node_many *node = (struct ec_node_many *)gen_node;
+
+       ec_node_free(node->child);
+}
+
+static struct ec_node_type ec_node_many_type = {
+       .name = "many",
+       .parse = ec_node_many_parse,
+       .complete = ec_node_default_complete,
+//XXX  .complete = ec_node_many_complete,
+       .size = sizeof(struct ec_node_many),
+       .free_priv = ec_node_many_free_priv,
+};
+
+EC_NODE_TYPE_REGISTER(ec_node_many_type);
+
+struct ec_node *ec_node_many(const char *id, struct ec_node *child,
+       unsigned int min, unsigned int max)
+{
+       struct ec_node_many *node = NULL;
+
+       if (child == NULL)
+               return NULL;
+
+       node = (struct ec_node_many *)__ec_node_new(&ec_node_many_type, id);
+       if (node == NULL) {
+               ec_node_free(child);
+               return NULL;
+       }
+
+       node->child = child;
+       node->min = min;
+       node->max = max;
+
+       return &node->gen;
+}
+
+static int ec_node_many_testcase(void)
+{
+       struct ec_node *node;
+       int ret = 0;
+
+       node = ec_node_many(NULL, ec_node_str(NULL, "foo"), 0, 0);
+       if (node == NULL) {
+               ec_log(EC_LOG_ERR, "cannot create node\n");
+               return -1;
+       }
+       ret |= EC_TEST_CHECK_PARSE(node, 0, "bar");
+       ret |= EC_TEST_CHECK_PARSE(node, 1, "foo", "bar");
+       ret |= EC_TEST_CHECK_PARSE(node, 2, "foo", "foo", "bar");
+       ret |= EC_TEST_CHECK_PARSE(node, 0);
+       ec_node_free(node);
+
+       node = ec_node_many(NULL, ec_node_str(NULL, "foo"), 1, 0);
+       if (node == NULL) {
+               ec_log(EC_LOG_ERR, "cannot create node\n");
+               return -1;
+       }
+       ret |= EC_TEST_CHECK_PARSE(node, -1, "bar");
+       ret |= EC_TEST_CHECK_PARSE(node, 1, "foo", "bar");
+       ret |= EC_TEST_CHECK_PARSE(node, 2, "foo", "foo", "bar");
+       ret |= EC_TEST_CHECK_PARSE(node, -1);
+       ec_node_free(node);
+
+       node = ec_node_many(NULL, ec_node_str(NULL, "foo"), 1, 2);
+       if (node == NULL) {
+               ec_log(EC_LOG_ERR, "cannot create node\n");
+               return -1;
+       }
+       ret |= EC_TEST_CHECK_PARSE(node, -1, "bar");
+       ret |= EC_TEST_CHECK_PARSE(node, 1, "foo", "bar");
+       ret |= EC_TEST_CHECK_PARSE(node, 2, "foo", "foo", "bar");
+       ret |= EC_TEST_CHECK_PARSE(node, 2, "foo", "foo", "foo");
+       ret |= EC_TEST_CHECK_PARSE(node, -1);
+       ec_node_free(node);
+
+       /* test completion */
+       /* XXX */
+
+       return ret;
+}
+
+static struct ec_test ec_node_many_test = {
+       .name = "many",
+       .test = ec_node_many_testcase,
+};
+
+EC_TEST_REGISTER(ec_node_many_test);
diff --git a/lib/ecoli_node_many.h b/lib/ecoli_node_many.h
new file mode 100644 (file)
index 0000000..e562c4d
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * 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_NODE_MANY_
+#define ECOLI_NODE_MANY_
+
+/*
+ * if min == max == 0, there is no limit
+ */
+struct ec_node *ec_node_many(const char *id, struct ec_node *child,
+       unsigned int min, unsigned int max);
+
+#endif
diff --git a/lib/ecoli_node_option.c b/lib/ecoli_node_option.c
new file mode 100644 (file)
index 0000000..39c1004
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * 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 <assert.h>
+#include <stdarg.h>
+
+#include <ecoli_malloc.h>
+#include <ecoli_log.h>
+#include <ecoli_strvec.h>
+#include <ecoli_node.h>
+#include <ecoli_node_option.h>
+#include <ecoli_node_str.h>
+#include <ecoli_test.h>
+
+struct ec_node_option {
+       struct ec_node gen;
+       struct ec_node *child;
+};
+
+static struct ec_parsed *ec_node_option_parse(const struct ec_node *gen_node,
+       const struct ec_strvec *strvec)
+{
+       struct ec_node_option *node = (struct ec_node_option *)gen_node;
+       struct ec_parsed *parsed = NULL, *child_parsed;
+       struct ec_strvec *match_strvec;
+
+       parsed = ec_parsed_new();
+       if (parsed == NULL)
+               goto fail;
+
+       child_parsed = ec_node_parse_strvec(node->child, strvec);
+       if (child_parsed == NULL)
+               goto fail;
+
+       if (ec_parsed_matches(child_parsed)) {
+               ec_parsed_add_child(parsed, child_parsed);
+               match_strvec = ec_strvec_dup(child_parsed->strvec);
+       } else {
+               ec_parsed_free(child_parsed);
+               match_strvec = ec_strvec_new();
+       }
+
+       if (match_strvec == NULL)
+               goto fail;
+
+       ec_parsed_set_match(parsed, gen_node, match_strvec);
+
+       return parsed;
+
+ fail:
+       ec_parsed_free(parsed);
+       return NULL;
+}
+
+static struct ec_completed *ec_node_option_complete(const struct ec_node *gen_node,
+       const struct ec_strvec *strvec)
+{
+       struct ec_node_option *node = (struct ec_node_option *)gen_node;
+
+       return ec_node_complete_strvec(node->child, strvec);
+}
+
+static void ec_node_option_free_priv(struct ec_node *gen_node)
+{
+       struct ec_node_option *node = (struct ec_node_option *)gen_node;
+
+       ec_node_free(node->child);
+}
+
+static struct ec_node_type ec_node_option_type = {
+       .name = "option",
+       .parse = ec_node_option_parse,
+       .complete = ec_node_option_complete,
+       .size = sizeof(struct ec_node_option),
+       .free_priv = ec_node_option_free_priv,
+};
+
+EC_NODE_TYPE_REGISTER(ec_node_option_type);
+
+struct ec_node *ec_node_option(const char *id, struct ec_node *child)
+{
+       struct ec_node *gen_node = NULL;
+       struct ec_node_option *node = NULL;
+
+       if (child == NULL)
+               return NULL;
+
+       gen_node = __ec_node_new(&ec_node_option_type, id);
+       if (gen_node == NULL) {
+               ec_node_free(child);
+               return NULL;
+       }
+       node = (struct ec_node_option *)gen_node;
+
+       node->child = child;
+
+       child->parent = gen_node;
+       TAILQ_INSERT_TAIL(&gen_node->children, child, next);
+
+       return &node->gen;
+}
+
+static int ec_node_option_testcase(void)
+{
+       struct ec_node *node;
+       int ret = 0;
+
+       node = ec_node_option(NULL, ec_node_str(NULL, "foo"));
+       if (node == NULL) {
+               ec_log(EC_LOG_ERR, "cannot create node\n");
+               return -1;
+       }
+       ret |= EC_TEST_CHECK_PARSE(node, 1, "foo");
+       ret |= EC_TEST_CHECK_PARSE(node, 1, "foo", "bar");
+       ret |= EC_TEST_CHECK_PARSE(node, 0, "bar");
+       ret |= EC_TEST_CHECK_PARSE(node, 0);
+       ec_node_free(node);
+
+       /* test completion */
+       node = ec_node_option(NULL, ec_node_str(NULL, "foo"));
+       if (node == NULL) {
+               ec_log(EC_LOG_ERR, "cannot create node\n");
+               return -1;
+       }
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "", EC_NODE_ENDLIST,
+               "foo", EC_NODE_ENDLIST,
+               "foo");
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "f", EC_NODE_ENDLIST,
+               "oo", EC_NODE_ENDLIST,
+               "oo");
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "b", EC_NODE_ENDLIST,
+               EC_NODE_ENDLIST,
+               "");
+       ec_node_free(node);
+
+       return ret;
+}
+
+static struct ec_test ec_node_option_test = {
+       .name = "node_option",
+       .test = ec_node_option_testcase,
+};
+
+EC_TEST_REGISTER(ec_node_option_test);
diff --git a/lib/ecoli_node_option.h b/lib/ecoli_node_option.h
new file mode 100644 (file)
index 0000000..3b6e3a2
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * 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_NODE_OPTION_
+#define ECOLI_NODE_OPTION_
+
+#include <ecoli_node.h>
+
+struct ec_node *ec_node_option(const char *id, struct ec_node *node);
+
+#endif
diff --git a/lib/ecoli_node_or.c b/lib/ecoli_node_or.c
new file mode 100644 (file)
index 0000000..d1e4863
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+ * 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 <assert.h>
+#include <stdarg.h>
+#include <errno.h>
+
+#include <ecoli_malloc.h>
+#include <ecoli_log.h>
+#include <ecoli_strvec.h>
+#include <ecoli_node.h>
+#include <ecoli_node_or.h>
+#include <ecoli_node_str.h>
+#include <ecoli_test.h>
+
+struct ec_node_or {
+       struct ec_node gen;
+       struct ec_node **table;
+       unsigned int len;
+};
+
+static struct ec_parsed *ec_node_or_parse(const struct ec_node *gen_node,
+       const struct ec_strvec *strvec)
+{
+       struct ec_node_or *node = (struct ec_node_or *)gen_node;
+       struct ec_parsed *parsed, *child_parsed = NULL;
+       struct ec_strvec *match_strvec;
+       unsigned int i;
+
+       parsed = ec_parsed_new();
+       if (parsed == NULL)
+               goto fail;
+
+       for (i = 0; i < node->len; i++) {
+               child_parsed = ec_node_parse_strvec(node->table[i], strvec);
+               if (child_parsed == NULL)
+                       goto fail;
+               if (ec_parsed_matches(child_parsed))
+                       break;
+               ec_parsed_free(child_parsed);
+               child_parsed = NULL;
+       }
+
+       /* no match */
+       if (i == node->len)
+               return parsed;
+
+       match_strvec = ec_strvec_dup(child_parsed->strvec);
+       if (match_strvec == NULL)
+               goto fail;
+
+       ec_parsed_set_match(parsed, gen_node, match_strvec);
+       ec_parsed_add_child(parsed, child_parsed);
+
+       return parsed;
+
+ fail:
+       ec_parsed_free(child_parsed);
+       ec_parsed_free(parsed);
+       return NULL;
+}
+
+static struct ec_completed *ec_node_or_complete(const struct ec_node *gen_node,
+       const struct ec_strvec *strvec)
+{
+       struct ec_node_or *node = (struct ec_node_or *)gen_node;
+       struct ec_completed *completed, *child_completed;
+       size_t n;
+
+       completed = ec_completed_new();
+       if (completed == NULL)
+               return NULL;
+
+       for (n = 0; n < node->len; n++) {
+               child_completed = ec_node_complete_strvec(node->table[n],
+                       strvec);
+
+               if (child_completed == NULL) // XXX fail instead?
+                       continue;
+
+               ec_completed_merge(completed, child_completed);
+       }
+
+       return completed;
+}
+
+static void ec_node_or_free_priv(struct ec_node *gen_node)
+{
+       struct ec_node_or *node = (struct ec_node_or *)gen_node;
+       unsigned int i;
+
+       for (i = 0; i < node->len; i++)
+               ec_node_free(node->table[i]);
+       ec_free(node->table);
+}
+
+int ec_node_or_add(struct ec_node *gen_node, struct ec_node *child)
+{
+       struct ec_node_or *node = (struct ec_node_or *)gen_node;
+       struct ec_node **table;
+
+       assert(node != NULL);
+
+       if (child == NULL)
+               return -EINVAL;
+
+       gen_node->flags &= ~EC_NODE_F_BUILT;
+
+       table = ec_realloc(node->table, (node->len + 1) * sizeof(*node->table));
+       if (table == NULL) {
+               ec_node_free(child);
+               return -1;
+       }
+
+       node->table = table;
+       table[node->len] = child;
+       node->len++;
+
+       child->parent = gen_node;
+       TAILQ_INSERT_TAIL(&gen_node->children, child, next);
+
+       return 0;
+}
+
+static struct ec_node_type ec_node_or_type = {
+       .name = "or",
+       .parse = ec_node_or_parse,
+       .complete = ec_node_or_complete,
+       .size = sizeof(struct ec_node_or),
+       .free_priv = ec_node_or_free_priv,
+};
+
+EC_NODE_TYPE_REGISTER(ec_node_or_type);
+
+struct ec_node *__ec_node_or(const char *id, ...)
+{
+       struct ec_node *gen_node = NULL;
+       struct ec_node_or *node = NULL;
+       struct ec_node *child;
+       va_list ap;
+       int fail = 0;
+
+       va_start(ap, id);
+
+       gen_node = __ec_node_new(&ec_node_or_type, id);
+       node = (struct ec_node_or *)gen_node;
+       if (node == NULL)
+               fail = 1;;
+
+       for (child = va_arg(ap, struct ec_node *);
+            child != EC_NODE_ENDLIST;
+            child = va_arg(ap, struct ec_node *)) {
+
+               /* on error, don't quit the loop to avoid leaks */
+               if (fail == 1 || child == NULL ||
+                               ec_node_or_add(gen_node, child) < 0) {
+                       fail = 1;
+                       ec_node_free(child);
+               }
+       }
+
+       if (fail == 1)
+               goto fail;
+
+       va_end(ap);
+       return gen_node;
+
+fail:
+       ec_node_free(gen_node); /* will also free children */
+       va_end(ap);
+       return NULL;
+}
+
+static int ec_node_or_testcase(void)
+{
+       struct ec_node *node;
+       int ret = 0;
+
+       node = EC_NODE_OR(NULL,
+               ec_node_str(NULL, "foo"),
+               ec_node_str(NULL, "bar")
+       );
+       if (node == NULL) {
+               ec_log(EC_LOG_ERR, "cannot create node\n");
+               return -1;
+       }
+       ret |= EC_TEST_CHECK_PARSE(node, 1, "foo");
+       ret |= EC_TEST_CHECK_PARSE(node, 1, "bar");
+       ret |= EC_TEST_CHECK_PARSE(node, 1, "foo", "bar");
+       ret |= EC_TEST_CHECK_PARSE(node, -1, " ");
+       ret |= EC_TEST_CHECK_PARSE(node, -1, "foox");
+       ret |= EC_TEST_CHECK_PARSE(node, -1, "toto");
+       ret |= EC_TEST_CHECK_PARSE(node, -1, "");
+       ec_node_free(node);
+
+       /* test completion */
+       node = EC_NODE_OR(NULL,
+               ec_node_str(NULL, "foo"),
+               ec_node_str(NULL, "bar"),
+               ec_node_str(NULL, "bar2"),
+               ec_node_str(NULL, "toto"),
+               ec_node_str(NULL, "titi")
+       );
+       if (node == NULL) {
+               ec_log(EC_LOG_ERR, "cannot create node\n");
+               return -1;
+       }
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "", EC_NODE_ENDLIST,
+               "foo", "bar", "bar2", "toto", "titi", EC_NODE_ENDLIST,
+               "");
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "f", EC_NODE_ENDLIST,
+               "oo", EC_NODE_ENDLIST,
+               "oo");
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "b", EC_NODE_ENDLIST,
+               "ar", "ar2", EC_NODE_ENDLIST,
+               "ar");
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "bar", EC_NODE_ENDLIST,
+               "", "2", EC_NODE_ENDLIST,
+               "");
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "t", EC_NODE_ENDLIST,
+               "oto", "iti", EC_NODE_ENDLIST,
+               "");
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "to", EC_NODE_ENDLIST,
+               "to", EC_NODE_ENDLIST,
+               "to");
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "x", EC_NODE_ENDLIST,
+               EC_NODE_ENDLIST,
+               "");
+       ec_node_free(node);
+
+       return ret;
+}
+
+static struct ec_test ec_node_or_test = {
+       .name = "node_or",
+       .test = ec_node_or_testcase,
+};
+
+EC_TEST_REGISTER(ec_node_or_test);
diff --git a/lib/ecoli_node_or.h b/lib/ecoli_node_or.h
new file mode 100644 (file)
index 0000000..788cf37
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * 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_NODE_OR_
+#define ECOLI_NODE_OR_
+
+#include <ecoli_node.h>
+
+#define EC_NODE_OR(args...) __ec_node_or(args, EC_NODE_ENDLIST)
+
+/* list must be terminated with EC_NODE_ENDLIST */
+/* all nodes given in the list will be freed when freeing this one */
+/* avoid using this function directly, prefer the macro EC_NODE_OR() or
+ * ec_node_or() + ec_node_or_add() */
+struct ec_node *__ec_node_or(const char *id, ...);
+
+struct ec_node *ec_node_or(const char *id);
+
+/* child is consumed */
+int ec_node_or_add(struct ec_node *node, struct ec_node *child);
+
+
+#endif
diff --git a/lib/ecoli_node_re.c b/lib/ecoli_node_re.c
new file mode 100644 (file)
index 0000000..d5c9b49
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * 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_node.h>
+#include <ecoli_node_re.h>
+
+struct ec_node_re {
+       struct ec_node gen;
+       char *re_str;
+       regex_t re;
+};
+
+static struct ec_parsed *ec_node_re_parse(const struct ec_node *gen_node,
+       const struct ec_strvec *strvec)
+{
+       struct ec_node_re *node = (struct ec_node_re *)gen_node;
+       struct ec_strvec *match_strvec;
+       struct ec_parsed *parsed = NULL;
+       const char *str;
+       regmatch_t pos;
+
+       parsed = ec_parsed_new();
+       if (parsed == NULL)
+               goto fail;
+
+       if (ec_strvec_len(strvec) == 0)
+               return parsed;
+
+       str = ec_strvec_val(strvec, 0);
+       if (regexec(&node->re, str, 1, &pos, 0) != 0)
+               return parsed;
+       if (pos.rm_so != 0 || pos.rm_eo != (int)strlen(str))
+               return parsed;
+
+       match_strvec = ec_strvec_ndup(strvec, 0, 1);
+       if (match_strvec == NULL)
+               goto fail;
+
+       ec_parsed_set_match(parsed, gen_node, match_strvec);
+
+       return parsed;
+
+ fail:
+       ec_parsed_free(parsed);
+       return NULL;
+}
+
+static void ec_node_re_free_priv(struct ec_node *gen_node)
+{
+       struct ec_node_re *node = (struct ec_node_re *)gen_node;
+
+       ec_free(node->re_str);
+       regfree(&node->re);
+}
+
+static struct ec_node_type ec_node_re_type = {
+       .name = "re",
+       .parse = ec_node_re_parse,
+       .complete = ec_node_default_complete,
+       .size = sizeof(struct ec_node_re),
+       .free_priv = ec_node_re_free_priv,
+};
+
+EC_NODE_TYPE_REGISTER(ec_node_re_type);
+
+int ec_node_re_set_regexp(struct ec_node *gen_node, const char *str)
+{
+       struct ec_node_re *node = (struct ec_node_re *)gen_node;
+       int ret;
+
+       if (str == NULL)
+               return -EINVAL;
+       if (node->re_str != NULL) // XXX allow to replace
+               return -EEXIST;
+
+       ret = -ENOMEM;
+       node->re_str = ec_strdup(str);
+       if (node->re_str == NULL)
+               goto fail;
+
+       ret = -EINVAL;
+       if (regcomp(&node->re, node->re_str, REG_EXTENDED) != 0)
+               goto fail;
+
+       return 0;
+
+fail:
+       ec_free(node->re_str);
+       return ret;
+}
+
+struct ec_node *ec_node_re(const char *id, const char *re_str)
+{
+       struct ec_node *gen_node = NULL;
+
+       gen_node = __ec_node_new(&ec_node_re_type, id);
+       if (gen_node == NULL)
+               goto fail;
+
+       if (ec_node_re_set_regexp(gen_node, re_str) < 0)
+               goto fail;
+
+       return gen_node;
+
+fail:
+       ec_node_free(gen_node);
+       return NULL;
+}
+
+static int ec_node_re_testcase(void)
+{
+       struct ec_node *node;
+       int ret = 0;
+
+       node = ec_node_re(NULL, "fo+|bar");
+       if (node == NULL) {
+               ec_log(EC_LOG_ERR, "cannot create node\n");
+               return -1;
+       }
+       ret |= EC_TEST_CHECK_PARSE(node, 1, "foo");
+       ret |= EC_TEST_CHECK_PARSE(node, 1, "foo", "bar");
+       ret |= EC_TEST_CHECK_PARSE(node, 1, "bar");
+       ret |= EC_TEST_CHECK_PARSE(node, -1, "foobar");
+       ret |= EC_TEST_CHECK_PARSE(node, -1, " foo");
+       ret |= EC_TEST_CHECK_PARSE(node, -1, "");
+       ec_node_free(node);
+
+       return ret;
+}
+
+static struct ec_test ec_node_re_test = {
+       .name = "node_re",
+       .test = ec_node_re_testcase,
+};
+
+EC_TEST_REGISTER(ec_node_re_test);
diff --git a/lib/ecoli_node_re.h b/lib/ecoli_node_re.h
new file mode 100644 (file)
index 0000000..e411678
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * 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_NODE_RE_
+#define ECOLI_NODE_RE_
+
+#include <ecoli_node.h>
+
+struct ec_node *ec_node_re(const char *id, const char *str);
+
+/* re is duplicated */
+int ec_node_re_set_regexp(struct ec_node *node, const char *re);
+
+#endif
diff --git a/lib/ecoli_node_re_lex.c b/lib/ecoli_node_re_lex.c
new file mode 100644 (file)
index 0000000..f580b72
--- /dev/null
@@ -0,0 +1,273 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <regex.h>
+#include <errno.h>
+
+#include <ecoli_malloc.h>
+#include <ecoli_log.h>
+#include <ecoli_test.h>
+#include <ecoli_strvec.h>
+#include <ecoli_node.h>
+#include <ecoli_node_many.h>
+#include <ecoli_node_or.h>
+#include <ecoli_node_str.h>
+#include <ecoli_node_int.h>
+#include <ecoli_node_re_lex.h>
+
+struct regexp_pattern {
+       char *pattern;
+       regex_t r;
+       bool keep;
+};
+
+struct ec_node_re_lex {
+       struct ec_node gen;
+       struct ec_node *child;
+       struct regexp_pattern *table;
+       size_t len;
+};
+
+static struct ec_strvec *
+tokenize(struct regexp_pattern *table, size_t table_len, const char *str)
+{
+       struct ec_strvec *strvec = NULL;
+       char *dup = NULL;
+       char c;
+       size_t len, off = 0;
+       size_t i;
+       int ret;
+       regmatch_t pos;
+
+       dup = ec_strdup(str);
+       if (dup == NULL)
+               goto fail;
+
+       strvec = ec_strvec_new();
+       if (strvec == NULL)
+               goto fail;
+
+       len = strlen(dup);
+       while (off < len) {
+               for (i = 0; i < table_len; i++) {
+                       ret = regexec(&table[i].r, &dup[off], 1, &pos, 0);
+                       if (ret != 0)
+                               continue;
+                       if (pos.rm_so != 0 || pos.rm_eo == 0) {
+                               ret = -1;
+                               continue;
+                       }
+
+                       if (table[i].keep == 0)
+                               break;
+
+                       c = dup[pos.rm_eo + off];
+                       dup[pos.rm_eo + off] = '\0';
+                       ec_log(EC_LOG_DEBUG, "re_lex match <%s>\n", &dup[off]);
+                       if (ec_strvec_add(strvec, &dup[off]) < 0)
+                               goto fail;
+
+                       dup[pos.rm_eo + off] = c;
+                       break;
+               }
+
+               if (ret != 0)
+                       goto fail;
+
+               off += pos.rm_eo;
+       }
+
+       ec_free(dup);
+       return strvec;
+
+fail:
+       ec_free(dup);
+       ec_strvec_free(strvec);
+       return NULL;
+}
+
+static struct ec_parsed *ec_node_re_lex_parse(const struct ec_node *gen_node,
+       const struct ec_strvec *strvec)
+{
+       struct ec_node_re_lex *node = (struct ec_node_re_lex *)gen_node;
+       struct ec_strvec *new_vec = NULL, *match_strvec;
+       struct ec_parsed *parsed = NULL, *child_parsed;
+       const char *str;
+
+       parsed = ec_parsed_new();
+       if (parsed == NULL)
+               return NULL;
+
+       if (ec_strvec_len(strvec) == 0)
+               return parsed;
+
+       str = ec_strvec_val(strvec, 0);
+       new_vec = tokenize(node->table, node->len, str);
+       if (new_vec == NULL)
+               goto fail;
+
+       printf("--------\n");
+       ec_strvec_dump(stdout, new_vec);
+       child_parsed = ec_node_parse_strvec(node->child, new_vec);
+       if (child_parsed == NULL)
+               goto fail;
+
+       if (!ec_parsed_matches(child_parsed) ||
+                       ec_parsed_len(child_parsed) !=
+                               ec_strvec_len(new_vec)) {
+               ec_strvec_free(new_vec);
+               ec_parsed_free(child_parsed);
+               return parsed;
+       }
+       ec_strvec_free(new_vec);
+       new_vec = NULL;
+
+       ec_parsed_add_child(parsed, child_parsed);
+       match_strvec = ec_strvec_ndup(strvec, 0, 1);
+       if (match_strvec == NULL)
+               goto fail;
+       ec_parsed_set_match(parsed, gen_node, match_strvec);
+
+       return parsed;
+
+ fail:
+       ec_strvec_free(new_vec);
+       ec_parsed_free(parsed);
+
+       return NULL;
+}
+
+static void ec_node_re_lex_free_priv(struct ec_node *gen_node)
+{
+       struct ec_node_re_lex *node = (struct ec_node_re_lex *)gen_node;
+       unsigned int i;
+
+       for (i = 0; i < node->len; i++) {
+               ec_free(node->table[i].pattern);
+               regfree(&node->table[i].r);
+       }
+
+       ec_free(node->table);
+       ec_node_free(node->child);
+}
+
+static struct ec_node_type ec_node_re_lex_type = {
+       .name = "re_lex",
+       .parse = ec_node_re_lex_parse,
+       //.complete = ec_node_re_lex_complete, //XXX
+       .size = sizeof(struct ec_node_re_lex),
+       .free_priv = ec_node_re_lex_free_priv,
+};
+
+EC_NODE_TYPE_REGISTER(ec_node_re_lex_type);
+
+int ec_node_re_lex_add(struct ec_node *gen_node, const char *pattern, int keep)
+{
+       struct ec_node_re_lex *node = (struct ec_node_re_lex *)gen_node;
+       struct regexp_pattern *table;
+       int ret;
+       char *pat_dup = NULL;
+
+       ret = -ENOMEM;
+       pat_dup = ec_strdup(pattern);
+       if (pat_dup == NULL)
+               goto fail;
+
+       ret = -ENOMEM;
+       table = ec_realloc(node->table, sizeof(*table) * (node->len + 1));
+       if (table == NULL)
+               goto fail;
+
+       ret = regcomp(&table[node->len].r, pattern, REG_EXTENDED);
+       if (ret != 0) {
+               ec_log(EC_LOG_ERR,
+                       "Regular expression <%s> compilation failed: %d\n",
+                       pattern, ret);
+               if (ret == REG_ESPACE)
+                       ret = -ENOMEM;
+               else
+                       ret = -EINVAL;
+
+               goto fail;
+       }
+
+       table[node->len].pattern = pat_dup;
+       table[node->len].keep = keep;
+       node->len++;
+       node->table = table;
+
+       return 0;
+
+fail:
+       ec_free(pat_dup);
+       return ret;
+}
+
+struct ec_node *ec_node_re_lex(const char *id, struct ec_node *child)
+{
+       struct ec_node_re_lex *node = NULL;
+
+       if (child == NULL)
+               return NULL;
+
+       node = (struct ec_node_re_lex *)__ec_node_new(&ec_node_re_lex_type, id);
+       if (node == NULL) {
+               ec_node_free(child);
+               return NULL;
+       }
+
+       node->child = child;
+
+       return &node->gen;
+}
+
+
+static int ec_node_re_lex_testcase(void)
+{
+       struct ec_node *node;
+       int ret = 0;
+
+       node = ec_node_re_lex(NULL,
+               ec_node_many(NULL,
+                       EC_NODE_OR(NULL,
+                               ec_node_str(NULL, "foo"),
+                               ec_node_str(NULL, "bar"),
+                               ec_node_int(NULL, 0, 1000, 0)
+                       ), 0, 0
+               )
+       );
+       if (node == NULL) {
+               ec_log(EC_LOG_ERR, "cannot create node\n");
+               return -1;
+       }
+
+       /* XXX add ^ automatically ? */
+       ret |= ec_node_re_lex_add(node, "[a-zA-Z]+", 1);
+       ret |= ec_node_re_lex_add(node, "[0-9]+", 1);
+       ret |= ec_node_re_lex_add(node, "=", 1);
+       ret |= ec_node_re_lex_add(node, "-", 1);
+       ret |= ec_node_re_lex_add(node, "\\+", 1);
+       ret |= ec_node_re_lex_add(node, "[      ]+", 0);
+       if (ret != 0) {
+               ec_log(EC_LOG_ERR, "cannot add regexp to node\n");
+               ec_node_free(node);
+               return -1;
+       }
+
+       ret |= EC_TEST_CHECK_PARSE(node, 1, "  foo bar  324 bar234");
+       ret |= EC_TEST_CHECK_PARSE(node, 1, "foo bar324");
+       ret |= EC_TEST_CHECK_PARSE(node, 1, "");
+       ret |= EC_TEST_CHECK_PARSE(node, -1, "foobar");
+
+       ec_node_free(node);
+
+       return ret;
+}
+
+static struct ec_test ec_node_re_lex_test = {
+       .name = "node_re_lex",
+       .test = ec_node_re_lex_testcase,
+};
+
+EC_TEST_REGISTER(ec_node_re_lex_test);
diff --git a/lib/ecoli_node_re_lex.h b/lib/ecoli_node_re_lex.h
new file mode 100644 (file)
index 0000000..fb72786
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * 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_NODE_RE_LEX_
+#define ECOLI_NODE_RE_LEX_
+
+#include <ecoli_node.h>
+
+struct ec_node *ec_node_re_lex(const char *id, struct ec_node *child);
+
+int ec_node_re_lex_add(struct ec_node *gen_node, const char *pattern, int keep);
+
+#endif
diff --git a/lib/ecoli_node_seq.c b/lib/ecoli_node_seq.c
new file mode 100644 (file)
index 0000000..11312eb
--- /dev/null
@@ -0,0 +1,323 @@
+/*
+ * 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 <assert.h>
+#include <stdarg.h>
+#include <errno.h>
+
+#include <ecoli_malloc.h>
+#include <ecoli_log.h>
+#include <ecoli_test.h>
+#include <ecoli_strvec.h>
+#include <ecoli_node.h>
+#include <ecoli_node_str.h>
+#include <ecoli_node_option.h>
+#include <ecoli_node_seq.h>
+
+struct ec_node_seq {
+       struct ec_node gen;
+       struct ec_node **table;
+       unsigned int len;
+};
+
+static struct ec_parsed *ec_node_seq_parse(const struct ec_node *gen_node,
+       const struct ec_strvec *strvec)
+{
+       struct ec_node_seq *node = (struct ec_node_seq *)gen_node;
+       struct ec_parsed *parsed, *child_parsed;
+       struct ec_strvec *match_strvec;
+       struct ec_strvec *childvec = NULL;
+       size_t len = 0;
+       unsigned int i;
+
+       parsed = ec_parsed_new();
+       if (parsed == NULL)
+               goto fail;
+
+       for (i = 0; i < node->len; i++) {
+               childvec = ec_strvec_ndup(strvec, len,
+                       ec_strvec_len(strvec) - len);
+               if (childvec == NULL)
+                       goto fail;
+
+               child_parsed = ec_node_parse_strvec(node->table[i], childvec);
+               if (child_parsed == NULL)
+                       goto fail;
+
+               ec_strvec_free(childvec);
+               childvec = NULL;
+
+               if (!ec_parsed_matches(child_parsed)) {
+                       ec_parsed_free(child_parsed);
+                       // XXX ec_parsed_free_children needed? see subset.c
+                       ec_parsed_free_children(parsed);
+                       return parsed;
+               }
+
+               ec_parsed_add_child(parsed, child_parsed);
+               len += ec_parsed_len(child_parsed);
+       }
+
+       match_strvec = ec_strvec_ndup(strvec, 0, len);
+       if (match_strvec == NULL)
+               goto fail;
+
+       ec_parsed_set_match(parsed, gen_node, match_strvec);
+
+       return parsed;
+
+fail:
+       ec_strvec_free(childvec);
+       ec_parsed_free(parsed);
+       return NULL;
+}
+
+static struct ec_completed *ec_node_seq_complete(const struct ec_node *gen_node,
+       const struct ec_strvec *strvec)
+{
+       struct ec_node_seq *node = (struct ec_node_seq *)gen_node;
+       struct ec_completed *completed, *child_completed;
+       struct ec_strvec *childvec = NULL;
+       struct ec_parsed *parsed;
+       size_t len = 0;
+       unsigned int i;
+
+       completed = ec_completed_new();
+       if (completed == NULL)
+               return NULL;
+
+       if (node->len == 0)
+               return completed;
+
+       for (i = 0; i < node->len && len < ec_strvec_len(strvec); i++) {
+               childvec = ec_strvec_ndup(strvec, len,
+                       ec_strvec_len(strvec) - len);
+               if (childvec == NULL)
+                       goto fail;
+
+               child_completed = ec_node_complete_strvec(node->table[i],
+                       childvec);
+               if (child_completed == NULL)
+                       goto fail;
+
+               ec_completed_merge(completed, child_completed);
+
+               parsed = ec_node_parse_strvec(node->table[i], childvec);
+               if (parsed == NULL)
+                       goto fail;
+
+               ec_strvec_free(childvec);
+               childvec = NULL;
+
+               if (!ec_parsed_matches(parsed)) {
+                       ec_parsed_free(parsed);
+                       break;
+               }
+
+               len += ec_strvec_len(parsed->strvec);
+               ec_parsed_free(parsed);
+       }
+
+       return completed;
+
+fail:
+       ec_strvec_free(childvec);
+       ec_completed_free(completed);
+       return NULL;
+}
+
+static void ec_node_seq_free_priv(struct ec_node *gen_node)
+{
+       struct ec_node_seq *node = (struct ec_node_seq *)gen_node;
+       unsigned int i;
+
+       for (i = 0; i < node->len; i++)
+               ec_node_free(node->table[i]);
+       ec_free(node->table);
+}
+
+static struct ec_node_type ec_node_seq_type = {
+       .name = "seq",
+       .parse = ec_node_seq_parse,
+       .complete = ec_node_seq_complete,
+       .size = sizeof(struct ec_node_seq),
+       .free_priv = ec_node_seq_free_priv,
+};
+
+EC_NODE_TYPE_REGISTER(ec_node_seq_type);
+
+int ec_node_seq_add(struct ec_node *gen_node, struct ec_node *child)
+{
+       struct ec_node_seq *node = (struct ec_node_seq *)gen_node;
+       struct ec_node **table;
+
+       // XXX check node type
+
+       assert(node != NULL);
+
+       if (child == NULL)
+               return -EINVAL;
+
+       gen_node->flags &= ~EC_NODE_F_BUILT;
+
+       table = ec_realloc(node->table, (node->len + 1) * sizeof(*node->table));
+       if (table == NULL) {
+               ec_node_free(child);
+               return -1;
+       }
+
+       node->table = table;
+       table[node->len] = child;
+       node->len++;
+
+       child->parent = gen_node;
+       TAILQ_INSERT_TAIL(&gen_node->children, child, next); // XXX really needed?
+
+       return 0;
+}
+
+struct ec_node *__ec_node_seq(const char *id, ...)
+{
+       struct ec_node *gen_node = NULL;
+       struct ec_node_seq *node = NULL;
+       struct ec_node *child;
+       va_list ap;
+       int fail = 0;
+
+       va_start(ap, id);
+
+       gen_node = __ec_node_new(&ec_node_seq_type, id);
+       node = (struct ec_node_seq *)gen_node;
+       if (node == NULL)
+               fail = 1;;
+
+       for (child = va_arg(ap, struct ec_node *);
+            child != EC_NODE_ENDLIST;
+            child = va_arg(ap, struct ec_node *)) {
+
+               /* on error, don't quit the loop to avoid leaks */
+               if (fail == 1 || child == NULL ||
+                               ec_node_seq_add(&node->gen, child) < 0) {
+                       fail = 1;
+                       ec_node_free(child);
+               }
+       }
+
+       if (fail == 1)
+               goto fail;
+
+       va_end(ap);
+       return gen_node;
+
+fail:
+       ec_node_free(gen_node); /* will also free children */
+       va_end(ap);
+       return NULL;
+}
+
+static int ec_node_seq_testcase(void)
+{
+       struct ec_node *node;
+       int ret = 0;
+
+       node = EC_NODE_SEQ(NULL,
+               ec_node_str(NULL, "foo"),
+               ec_node_str(NULL, "bar")
+       );
+       if (node == NULL) {
+               ec_log(EC_LOG_ERR, "cannot create node\n");
+               return -1;
+       }
+       ret |= EC_TEST_CHECK_PARSE(node, 2, "foo", "bar");
+       ret |= EC_TEST_CHECK_PARSE(node, 2, "foo", "bar", "toto");
+       ret |= EC_TEST_CHECK_PARSE(node, -1, "foo");
+       ret |= EC_TEST_CHECK_PARSE(node, -1, "foox", "bar");
+       ret |= EC_TEST_CHECK_PARSE(node, -1, "foo", "barx");
+       ret |= EC_TEST_CHECK_PARSE(node, -1, "bar", "foo");
+       ret |= EC_TEST_CHECK_PARSE(node, -1, "", "foo");
+       ec_node_free(node);
+
+       /* test completion */
+       node = EC_NODE_SEQ(NULL,
+               ec_node_str(NULL, "foo"),
+               ec_node_option(NULL, ec_node_str(NULL, "toto")),
+               ec_node_str(NULL, "bar")
+       );
+       if (node == NULL) {
+               ec_log(EC_LOG_ERR, "cannot create node\n");
+               return -1;
+       }
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "", EC_NODE_ENDLIST,
+               "foo", EC_NODE_ENDLIST,
+               "foo");
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "f", EC_NODE_ENDLIST,
+               "oo", EC_NODE_ENDLIST,
+               "oo");
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "foo", EC_NODE_ENDLIST,
+               "", EC_NODE_ENDLIST,
+               "");
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "foo", "", EC_NODE_ENDLIST,
+               "bar", "toto", EC_NODE_ENDLIST,
+               "");
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "foo", "t", EC_NODE_ENDLIST,
+               "oto", EC_NODE_ENDLIST,
+               "oto");
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "foo", "b", EC_NODE_ENDLIST,
+               "ar", EC_NODE_ENDLIST,
+               "ar");
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "foo", "bar", EC_NODE_ENDLIST,
+               "", EC_NODE_ENDLIST,
+               "");
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "x", EC_NODE_ENDLIST,
+               EC_NODE_ENDLIST,
+               "");
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "foobarx", EC_NODE_ENDLIST,
+               EC_NODE_ENDLIST,
+               "");
+       ec_node_free(node);
+
+       return ret;
+}
+
+static struct ec_test ec_node_seq_test = {
+       .name = "node_seq",
+       .test = ec_node_seq_testcase,
+};
+
+EC_TEST_REGISTER(ec_node_seq_test);
diff --git a/lib/ecoli_node_seq.h b/lib/ecoli_node_seq.h
new file mode 100644 (file)
index 0000000..741539c
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * 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_NODE_SEQ_
+#define ECOLI_NODE_SEQ_
+
+#include <ecoli_node.h>
+
+#define EC_NODE_SEQ(args...) __ec_node_seq(args, EC_NODE_ENDLIST)
+
+/* list must be terminated with EC_NODE_ENDLIST */
+/* all nodes given in the list will be freed when freeing this one */
+/* avoid using this function directly, prefer the macro EC_NODE_SEQ() or
+ * ec_node_seq() + ec_node_seq_add() */
+struct ec_node *__ec_node_seq(const char *id, ...);
+
+struct ec_node *ec_node_seq(const char *id);
+
+/* child is consumed */
+// XXX add_child?
+int ec_node_seq_add(struct ec_node *node, struct ec_node *child);
+
+#endif
diff --git a/lib/ecoli_node_sh_lex.c b/lib/ecoli_node_sh_lex.c
new file mode 100644 (file)
index 0000000..549faec
--- /dev/null
@@ -0,0 +1,457 @@
+/*
+ * 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 <assert.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include <ecoli_malloc.h>
+#include <ecoli_log.h>
+#include <ecoli_test.h>
+#include <ecoli_strvec.h>
+#include <ecoli_node.h>
+#include <ecoli_node_seq.h>
+#include <ecoli_node_str.h>
+#include <ecoli_node_option.h>
+#include <ecoli_node_sh_lex.h>
+
+struct ec_node_sh_lex {
+       struct ec_node gen;
+       struct ec_node *child;
+};
+
+static size_t eat_spaces(const char *str)
+{
+       size_t i = 0;
+
+       /* skip spaces */
+       while (isblank(str[i]))
+               i++;
+
+       return i;
+}
+
+/*
+ * Allocate a new string which is a copy of the input string with quotes
+ * removed. If quotes are not closed properly, set missing_quote to the
+ * missing quote char.
+ */
+static char *unquote_str(const char *str, size_t n, int allow_missing_quote,
+       char *missing_quote)
+{
+       unsigned s = 1, d = 0;
+       char quote = str[0];
+       char *dst;
+       int closed = 0;
+
+       dst = ec_malloc(n);
+       if (dst == NULL) {
+               errno = ENOMEM;
+               return NULL;
+       }
+
+       /* copy string and remove quotes */
+       while (s < n && d < n && str[s] != '\0') {
+               if (str[s] == '\\' && str[s+1] == quote) {
+                       dst[d++] = quote;
+                       s += 2;
+                       continue;
+               }
+               if (str[s] == '\\' && str[s+1] == '\\') {
+                       dst[d++] = '\\';
+                       s += 2;
+                       continue;
+               }
+               if (str[s] == quote) {
+                       s++;
+                       closed = 1;
+                       break;
+               }
+               dst[d++] = str[s++];
+       }
+
+       /* not enough room in dst buffer (should not happen) */
+       if (d >= n) {
+               ec_free(dst);
+               errno = EMSGSIZE;
+               return NULL;
+       }
+
+       /* quote not closed */
+       if (closed == 0) {
+               if (missing_quote != NULL)
+                       *missing_quote = str[0];
+               if (allow_missing_quote == 0) {
+                       ec_free(dst);
+                       errno = EINVAL;
+                       return NULL;
+               }
+       }
+       dst[d++] = '\0';
+
+       return dst;
+}
+
+static size_t eat_quoted_str(const char *str)
+{
+       size_t i = 0;
+       char quote = str[0];
+
+       while (str[i] != '\0') {
+               if (str[i] != '\\' && str[i+1] == quote)
+                       return i + 2;
+               i++;
+       }
+
+       /* unclosed quote, will be detected later */
+       return i;
+}
+
+static size_t eat_str(const char *str)
+{
+       size_t i = 0;
+
+       /* skip spaces */
+       while (!isblank(str[i]) && str[i] != '\0')
+               i++;
+
+       return i;
+}
+
+static struct ec_strvec *tokenize(const char *str, int completion,
+       int allow_missing_quote, char *missing_quote)
+{
+       struct ec_strvec *strvec = NULL;
+       size_t off = 0, len, suboff, sublen;
+       char *word = NULL, *concat = NULL, *tmp;
+       int last_is_space = 1;
+
+//     printf("str=%s\n", str);
+
+       strvec = ec_strvec_new();
+       if (strvec == NULL)
+               goto fail;
+
+       while (str[off] != '\0') {
+               len = eat_spaces(&str[off]);
+               if (len > 0)
+                       last_is_space = 1;
+//             printf("space=%zd\n", len);
+               off += len;
+
+               len = 0;
+               suboff = off;
+               while (str[suboff] != '\0') {
+                       last_is_space = 0;
+                       if (str[suboff] == '"' || str[suboff] == '\'') {
+                               sublen = eat_quoted_str(&str[suboff]);
+//                             printf("sublen=%zd\n", sublen);
+                               word = unquote_str(&str[suboff], sublen,
+                                       allow_missing_quote, missing_quote);
+                       } else {
+                               sublen = eat_str(&str[suboff]);
+//                             printf("sublen=%zd\n", sublen);
+                               if (sublen == 0)
+                                       break;
+                               word = ec_strndup(&str[suboff], sublen);
+                       }
+
+                       if (word == NULL)
+                               goto fail;
+//                     printf("word=%s\n", word);
+
+                       len += sublen;
+                       suboff += sublen;
+
+                       if (concat == NULL) {
+                               concat = word;
+                               word = NULL;
+                       } else {
+                               tmp = ec_realloc(concat, len + 1);
+                               if (tmp == NULL)
+                                       goto fail;
+                               concat = tmp;
+                               strcat(concat, word);
+                               ec_free(word);
+                               word = NULL;
+                       }
+               }
+
+               if (concat != NULL) {
+                       if (ec_strvec_add(strvec, concat) < 0)
+                               goto fail;
+                       ec_free(concat);
+                       concat = NULL;
+               }
+
+               /* XXX remove all printf comments */
+//             printf("str off=%zd len=%zd\n", off, len);
+               off += len;
+       }
+
+       /* in completion mode, append an empty string in the vector if
+        * the input string ends with space */
+       if (completion && last_is_space) {
+               if (ec_strvec_add(strvec, "") < 0)
+                       goto fail;
+       }
+
+       return strvec;
+
+ fail:
+       ec_free(word);
+       ec_free(concat);
+       ec_strvec_free(strvec);
+       return NULL;
+}
+
+static struct ec_parsed *ec_node_sh_lex_parse(const struct ec_node *gen_node,
+       const struct ec_strvec *strvec)
+{
+       struct ec_node_sh_lex *node = (struct ec_node_sh_lex *)gen_node;
+       struct ec_strvec *new_vec = NULL, *match_strvec;
+       struct ec_parsed *parsed = NULL, *child_parsed;
+       const char *str;
+
+       parsed = ec_parsed_new();
+       if (parsed == NULL)
+               return NULL;
+
+       if (ec_strvec_len(strvec) == 0)
+               return parsed;
+
+       str = ec_strvec_val(strvec, 0);
+       new_vec = tokenize(str, 0, 0, NULL);
+       if (new_vec == NULL)
+               goto fail;
+
+       child_parsed = ec_node_parse_strvec(node->child, new_vec);
+       if (child_parsed == NULL)
+               goto fail;
+
+       if (!ec_parsed_matches(child_parsed) ||
+                       ec_parsed_len(child_parsed) !=
+                               ec_strvec_len(new_vec)) {
+               ec_strvec_free(new_vec);
+               ec_parsed_free(child_parsed);
+               return parsed;
+       }
+       ec_strvec_free(new_vec);
+       new_vec = NULL;
+
+       ec_parsed_add_child(parsed, child_parsed);
+       match_strvec = ec_strvec_ndup(strvec, 0, 1);
+       if (match_strvec == NULL)
+               goto fail;
+       ec_parsed_set_match(parsed, gen_node, match_strvec);
+
+       return parsed;
+
+ fail:
+       ec_strvec_free(new_vec);
+       ec_parsed_free(parsed);
+
+       return NULL;
+}
+
+static struct ec_completed *ec_node_sh_lex_complete(const struct ec_node *gen_node,
+       const struct ec_strvec *strvec)
+{
+       struct ec_node_sh_lex *node = (struct ec_node_sh_lex *)gen_node;
+       struct ec_completed *completed, *child_completed = NULL;
+       struct ec_strvec *new_vec = NULL;
+       const char *str;
+       char missing_quote;
+
+//     printf("==================\n");
+       completed = ec_completed_new();
+       if (completed == NULL)
+               return NULL;
+
+       if (ec_strvec_len(strvec) != 1)
+               return completed;
+
+       str = ec_strvec_val(strvec, 0);
+       new_vec = tokenize(str, 1, 1, &missing_quote);
+       if (new_vec == NULL)
+               goto fail;
+
+//     ec_strvec_dump(new_vec, stdout);
+
+       child_completed = ec_node_complete_strvec(node->child, new_vec);
+       if (child_completed == NULL)
+               goto fail;
+
+       ec_strvec_free(new_vec);
+       new_vec = NULL;
+       ec_completed_merge(completed, child_completed);
+
+       return completed;
+
+ fail:
+       ec_strvec_free(new_vec);
+       ec_completed_free(completed);
+       return NULL;
+}
+
+static void ec_node_sh_lex_free_priv(struct ec_node *gen_node)
+{
+       struct ec_node_sh_lex *node = (struct ec_node_sh_lex *)gen_node;
+
+       ec_node_free(node->child);
+}
+
+static struct ec_node_type ec_node_sh_lex_type = {
+       .name = "sh_lex",
+       .parse = ec_node_sh_lex_parse,
+       .complete = ec_node_sh_lex_complete,
+       .size = sizeof(struct ec_node_sh_lex),
+       .free_priv = ec_node_sh_lex_free_priv,
+};
+
+EC_NODE_TYPE_REGISTER(ec_node_sh_lex_type);
+
+struct ec_node *ec_node_sh_lex(const char *id, struct ec_node *child)
+{
+       struct ec_node_sh_lex *node = NULL;
+
+       if (child == NULL)
+               return NULL;
+
+       node = (struct ec_node_sh_lex *)__ec_node_new(&ec_node_sh_lex_type, id);
+       if (node == NULL) {
+               ec_node_free(child);
+               return NULL;
+       }
+
+       node->child = child;
+
+       return &node->gen;
+}
+
+static int ec_node_sh_lex_testcase(void)
+{
+       struct ec_node *node;
+       int ret = 0;
+
+       node = ec_node_sh_lex(NULL,
+               EC_NODE_SEQ(NULL,
+                       ec_node_str(NULL, "foo"),
+                       ec_node_option(NULL,
+                               ec_node_str(NULL, "toto")
+                       ),
+                       ec_node_str(NULL, "bar")
+               )
+       );
+       if (node == NULL) {
+               ec_log(EC_LOG_ERR, "cannot create node\n");
+               return -1;
+       }
+       ret |= EC_TEST_CHECK_PARSE(node, 1, "foo bar");
+       ret |= EC_TEST_CHECK_PARSE(node, 1, "  foo   bar");
+       ret |= EC_TEST_CHECK_PARSE(node, 1, "  'foo' \"bar\"");
+       ret |= EC_TEST_CHECK_PARSE(node, 1, "  'f'oo 'toto' bar");
+       ec_node_free(node);
+
+       /* test completion */
+       node = ec_node_sh_lex(NULL,
+               EC_NODE_SEQ(NULL,
+                       ec_node_str(NULL, "foo"),
+                       ec_node_option(NULL,
+                               ec_node_str(NULL, "toto")
+                       ),
+                       ec_node_str(NULL, "bar"),
+                       ec_node_str(NULL, "titi")
+               )
+       );
+       if (node == NULL) {
+               ec_log(EC_LOG_ERR, "cannot create node\n");
+               return -1;
+       }
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "", EC_NODE_ENDLIST,
+               "foo", EC_NODE_ENDLIST,
+               "foo");
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               " ", EC_NODE_ENDLIST,
+               "foo", EC_NODE_ENDLIST,
+               "foo");
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "f", EC_NODE_ENDLIST,
+               "oo", EC_NODE_ENDLIST,
+               "oo");
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "foo", EC_NODE_ENDLIST,
+               "", EC_NODE_ENDLIST,
+               "");
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "foo ", EC_NODE_ENDLIST,
+               "bar", "toto", EC_NODE_ENDLIST,
+               "");
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "foo t", EC_NODE_ENDLIST,
+               "oto", EC_NODE_ENDLIST,
+               "oto");
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "foo b", EC_NODE_ENDLIST,
+               "ar", EC_NODE_ENDLIST,
+               "ar");
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "foo bar", EC_NODE_ENDLIST,
+               "", EC_NODE_ENDLIST,
+               "");
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "foo bar ", EC_NODE_ENDLIST,
+               "titi", EC_NODE_ENDLIST,
+               "titi");
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "foo toto bar ", EC_NODE_ENDLIST,
+               "titi", EC_NODE_ENDLIST,
+               "titi");
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "x", EC_NODE_ENDLIST,
+               EC_NODE_ENDLIST,
+               "");
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "foo barx", EC_NODE_ENDLIST,
+               EC_NODE_ENDLIST,
+               "");
+
+       ec_node_free(node);
+       return ret;
+}
+
+static struct ec_test ec_node_sh_lex_test = {
+       .name = "node_sh_lex",
+       .test = ec_node_sh_lex_testcase,
+};
+
+EC_TEST_REGISTER(ec_node_sh_lex_test);
diff --git a/lib/ecoli_node_sh_lex.h b/lib/ecoli_node_sh_lex.h
new file mode 100644 (file)
index 0000000..2fc2806
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * 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_NODE_SHLEX_
+#define ECOLI_NODE_SHLEX_
+
+#include <ecoli_node.h>
+
+struct ec_node *ec_node_sh_lex(const char *id, struct ec_node *child);
+
+#endif
diff --git a/lib/ecoli_node_space.c b/lib/ecoli_node_space.c
new file mode 100644 (file)
index 0000000..3efce53
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * 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 <ctype.h>
+
+#include <ecoli_log.h>
+#include <ecoli_test.h>
+#include <ecoli_malloc.h>
+#include <ecoli_strvec.h>
+#include <ecoli_node.h>
+#include <ecoli_node_space.h>
+
+struct ec_node_space {
+       struct ec_node gen;
+};
+
+static struct ec_parsed *ec_node_space_parse(const struct ec_node *gen_node,
+       const struct ec_strvec *strvec)
+{
+       struct ec_parsed *parsed = NULL;
+       struct ec_strvec *match_strvec;
+       const char *str;
+       size_t len = 0;
+
+       parsed = ec_parsed_new();
+       if (parsed == NULL)
+               goto fail;
+
+       if (ec_strvec_len(strvec) == 0)
+               return parsed;
+
+       str = ec_strvec_val(strvec, 0);
+       while (isspace(str[len]))
+               len++;
+       if (len == 0 || len != strlen(str))
+               return parsed;
+
+       match_strvec = ec_strvec_ndup(strvec, 0, 1);
+       if (match_strvec == NULL)
+               goto fail;
+
+       ec_parsed_set_match(parsed, gen_node, match_strvec);
+
+       return parsed;
+
+ fail:
+       ec_parsed_free(parsed);
+       return NULL;
+}
+
+static struct ec_node_type ec_node_space_type = {
+       .name = "space",
+       .parse = ec_node_space_parse,
+       .complete = ec_node_default_complete,
+       .size = sizeof(struct ec_node_space),
+};
+
+EC_NODE_TYPE_REGISTER(ec_node_space_type);
+
+static int ec_node_space_testcase(void)
+{
+       struct ec_node *node;
+       int ret = 0;
+
+       node = ec_node_new("space", NULL);
+       if (node == NULL) {
+               ec_log(EC_LOG_ERR, "cannot create node\n");
+               return -1;
+       }
+       ret |= EC_TEST_CHECK_PARSE(node, 1, " ");
+       ret |= EC_TEST_CHECK_PARSE(node, 1, " ", "foo");
+       ret |= EC_TEST_CHECK_PARSE(node, -1, "");
+       ret |= EC_TEST_CHECK_PARSE(node, -1, " foo");
+       ret |= EC_TEST_CHECK_PARSE(node, -1, "foo ");
+       ec_node_free(node);
+
+       /* test completion */
+       node = ec_node_new("space", NULL);
+       if (node == NULL) {
+               ec_log(EC_LOG_ERR, "cannot create node\n");
+               return -1;
+       }
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "", EC_NODE_ENDLIST,
+               EC_NODE_ENDLIST,
+               "");
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               " ", EC_NODE_ENDLIST,
+               EC_NODE_ENDLIST,
+               "");
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "foo", EC_NODE_ENDLIST,
+               EC_NODE_ENDLIST,
+               "");
+       ec_node_free(node);
+
+       return ret;
+}
+
+static struct ec_test ec_node_space_test = {
+       .name = "space",
+       .test = ec_node_space_testcase,
+};
+
+EC_TEST_REGISTER(ec_node_space_test);
diff --git a/lib/ecoli_node_space.h b/lib/ecoli_node_space.h
new file mode 100644 (file)
index 0000000..becefcb
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+/**
+ * This node matches one string in the vector if it is only composed of
+ * spaces, as interpreted by isspace().
+ */
+
+#ifndef ECOLI_NODE_SPACE_
+#define ECOLI_NODE_SPACE_
+
+/* no API for now, since there is no specific configuration for this node */
+
+#endif
diff --git a/lib/ecoli_node_str.c b/lib/ecoli_node_str.c
new file mode 100644 (file)
index 0000000..c30b9dd
--- /dev/null
@@ -0,0 +1,254 @@
+/*
+ * 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 <ecoli_log.h>
+#include <ecoli_malloc.h>
+#include <ecoli_test.h>
+#include <ecoli_strvec.h>
+#include <ecoli_node.h>
+#include <ecoli_node_str.h>
+
+struct ec_node_str {
+       struct ec_node gen;
+       char *string;
+       unsigned len;
+};
+
+static struct ec_parsed *ec_node_str_parse(const struct ec_node *gen_node,
+       const struct ec_strvec *strvec)
+{
+       struct ec_node_str *node = (struct ec_node_str *)gen_node;
+       struct ec_strvec *match_strvec;
+       struct ec_parsed *parsed = NULL;
+       const char *str;
+
+       parsed = ec_parsed_new();
+       if (parsed == NULL)
+               goto fail;
+
+       if (ec_strvec_len(strvec) == 0)
+               return parsed;
+
+       str = ec_strvec_val(strvec, 0);
+       if (strcmp(str, node->string) != 0)
+               return parsed;
+
+       match_strvec = ec_strvec_ndup(strvec, 0, 1);
+       if (match_strvec == NULL)
+               goto fail;
+
+       ec_parsed_set_match(parsed, gen_node, match_strvec);
+
+       return parsed;
+
+ fail:
+       ec_parsed_free(parsed);
+       return NULL;
+}
+
+static struct ec_completed *ec_node_str_complete(const struct ec_node *gen_node,
+       const struct ec_strvec *strvec)
+{
+       struct ec_node_str *node = (struct ec_node_str *)gen_node;
+       struct ec_completed *completed;
+       struct ec_completed_elt *completed_elt;
+       const char *str, *add;
+       size_t n = 0;
+
+       completed = ec_completed_new();
+       if (completed == NULL)
+               return NULL;
+
+       if (ec_strvec_len(strvec) != 1)
+               return completed;
+
+       str = ec_strvec_val(strvec, 0);
+       for (n = 0; n < node->len; n++) {
+               if (str[n] != node->string[n])
+                       break;
+       }
+
+       if (str[n] != '\0')
+               add = NULL;
+       else
+               add = node->string + n;
+
+       completed_elt = ec_completed_elt_new(gen_node, add);
+       if (completed_elt == NULL) {
+               ec_completed_free(completed);
+               return NULL;
+       }
+
+       ec_completed_add_elt(completed, completed_elt);
+
+       return completed;
+}
+
+static const char *ec_node_str_desc(const struct ec_node *gen_node)
+{
+       struct ec_node_str *node = (struct ec_node_str *)gen_node;
+
+       return node->string;
+}
+
+static void ec_node_str_free_priv(struct ec_node *gen_node)
+{
+       struct ec_node_str *node = (struct ec_node_str *)gen_node;
+
+       ec_free(node->string);
+}
+
+static struct ec_node_type ec_node_str_type = {
+       .name = "str",
+       .parse = ec_node_str_parse,
+       .complete = ec_node_str_complete,
+       .desc = ec_node_str_desc,
+       .size = sizeof(struct ec_node_str),
+       .free_priv = ec_node_str_free_priv,
+};
+
+EC_NODE_TYPE_REGISTER(ec_node_str_type);
+
+int ec_node_str_set_str(struct ec_node *gen_node, const char *str)
+{
+       struct ec_node_str *node = (struct ec_node_str *)gen_node;
+
+       if (str == NULL)
+               return -EINVAL;
+       if (node->string != NULL)
+               return -EEXIST; // XXX allow to replace
+
+       node->string = ec_strdup(str);
+       if (node->string == NULL)
+               return -ENOMEM;
+
+       node->len = strlen(node->string);
+
+       return 0;
+}
+
+struct ec_node *ec_node_str(const char *id, const char *str)
+{
+       struct ec_node *gen_node = NULL;
+
+       gen_node = __ec_node_new(&ec_node_str_type, id);
+       if (gen_node == NULL)
+               goto fail;
+
+       if (ec_node_str_set_str(gen_node, str) < 0)
+               goto fail;
+
+       return gen_node;
+
+fail:
+       ec_node_free(gen_node);
+       return NULL;
+}
+
+static int ec_node_str_testcase(void)
+{
+       struct ec_node *node;
+       int ret = 0;
+
+       /* XXX use EC_NO_ID instead of NULL */
+       node = ec_node_str(NULL, "foo");
+       if (node == NULL) {
+               ec_log(EC_LOG_ERR, "cannot create node\n");
+               return -1;
+       }
+       ret |= EC_TEST_CHECK_PARSE(node, 1, "foo");
+       ret |= EC_TEST_CHECK_PARSE(node, 1, "foo", "bar");
+       ret |= EC_TEST_CHECK_PARSE(node, -1, "foobar");
+       ret |= EC_TEST_CHECK_PARSE(node, -1, " foo");
+       ret |= EC_TEST_CHECK_PARSE(node, -1, "");
+       ec_node_free(node);
+
+       node = ec_node_str(NULL, "Здравствуйте");
+       if (node == NULL) {
+               ec_log(EC_LOG_ERR, "cannot create node\n");
+               return -1;
+       }
+       ret |= EC_TEST_CHECK_PARSE(node, 1, "Здравствуйте");
+       ret |= EC_TEST_CHECK_PARSE(node, 1, "Здравствуйте",
+               "John!");
+       ret |= EC_TEST_CHECK_PARSE(node, -1, "foo");
+       ret |= EC_TEST_CHECK_PARSE(node, -1, "");
+       ec_node_free(node);
+
+       /* an empty string node always matches */
+       node = ec_node_str(NULL, "");
+       if (node == NULL) {
+               ec_log(EC_LOG_ERR, "cannot create node\n");
+               return -1;
+       }
+       ret |= EC_TEST_CHECK_PARSE(node, 1, "");
+       ret |= EC_TEST_CHECK_PARSE(node, 1, "", "foo");
+       ret |= EC_TEST_CHECK_PARSE(node, -1, "foo");
+       ec_node_free(node);
+
+       /* test completion */
+       node = ec_node_str(NULL, "foo");
+       if (node == NULL) {
+               ec_log(EC_LOG_ERR, "cannot create node\n");
+               return -1;
+       }
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               EC_NODE_ENDLIST,
+               EC_NODE_ENDLIST,
+               "");
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "", EC_NODE_ENDLIST,
+               "foo", EC_NODE_ENDLIST,
+               "foo");
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "f", EC_NODE_ENDLIST,
+               "oo", EC_NODE_ENDLIST,
+               "oo");
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "foo", EC_NODE_ENDLIST,
+               "", EC_NODE_ENDLIST,
+               "");
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "x", EC_NODE_ENDLIST,
+               EC_NODE_ENDLIST,
+               "");
+       ec_node_free(node);
+
+       return ret;
+}
+
+static struct ec_test ec_node_str_test = {
+       .name = "node_str",
+       .test = ec_node_str_testcase,
+};
+
+EC_TEST_REGISTER(ec_node_str_test);
diff --git a/lib/ecoli_node_str.h b/lib/ecoli_node_str.h
new file mode 100644 (file)
index 0000000..611851e
--- /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_NODE_STR_
+#define ECOLI_NODE_STR_
+
+#include <ecoli_node.h>
+
+struct ec_node *ec_node_str(const char *id, const char *str);
+
+struct ec_node *ec_node_str_new(const char *id);
+
+/* str is duplicated */
+int ec_node_str_set_str(struct ec_node *node, const char *str);
+
+#endif
diff --git a/lib/ecoli_node_subset.c b/lib/ecoli_node_subset.c
new file mode 100644 (file)
index 0000000..ff9ecca
--- /dev/null
@@ -0,0 +1,479 @@
+/*
+ * 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_node.h>
+#include <ecoli_node_subset.h>
+#include <ecoli_node_str.h>
+#include <ecoli_node_or.h>
+#include <ecoli_test.h>
+
+struct ec_node_subset {
+       struct ec_node gen;
+       struct ec_node **table;
+       unsigned int len;
+};
+
+struct parse_result {
+       struct ec_parsed **parsed_table; /* list of parsed node */
+       size_t parsed_table_len;            /* number of parsed node */
+       size_t len;                         /* consumed strings */
+};
+
+static int __ec_node_subset_parse(struct ec_node **table, size_t table_len,
+                               const struct ec_strvec *strvec,
+                               struct parse_result *out)
+{
+       struct ec_node **child_table;
+       struct ec_parsed *child_parsed = 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 = ec_node_parse_strvec(table[i], strvec);
+               if (child_parsed == NULL)
+                       goto fail;
+
+               if (!ec_parsed_matches(child_parsed)) {
+                       ec_parsed_free(child_parsed);
+                       child_parsed = 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_len(child_parsed);
+               childvec = ec_strvec_ndup(strvec, len,
+                                       ec_strvec_len(strvec) - len);
+               if (childvec == NULL)
+                       goto fail;
+
+               memset(&result, 0, sizeof(result));
+
+               ret = __ec_node_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_free(child_parsed);
+                       child_parsed = NULL;
+                       for (j = 0; j < result.parsed_table_len; j++)
+                               ec_parsed_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_free(best_result.parsed_table[j]);
+               best_result.parsed_table[0] = child_parsed;
+               child_parsed = 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_free(child_parsed);
+       ec_strvec_free(childvec);
+       for (j = 0; j < best_result.parsed_table_len; j++)
+               ec_parsed_free(best_result.parsed_table[j]);
+       ec_free(best_result.parsed_table);
+       ec_free(child_table);
+       return -1;
+}
+
+static struct ec_parsed *ec_node_subset_parse(const struct ec_node *gen_node,
+       const struct ec_strvec *strvec)
+{
+       struct ec_node_subset *node = (struct ec_node_subset *)gen_node;
+       struct ec_parsed *parsed = NULL;
+       struct parse_result result;
+       struct ec_strvec *match_strvec;
+       size_t i;
+       int ret;
+
+       memset(&result, 0, sizeof(result));
+
+       parsed = ec_parsed_new();
+       if (parsed == NULL)
+               goto fail;
+
+       ret = __ec_node_subset_parse(node->table, node->len, strvec, &result);
+       if (ret < 0)
+               goto fail;
+
+       /* if no child node matches, return a matching empty strvec */
+       if (result.parsed_table_len == 0) {
+               ec_free(result.parsed_table);
+               match_strvec = ec_strvec_new();
+               if (match_strvec == NULL)
+                       goto fail;
+               ec_parsed_set_match(parsed, gen_node, match_strvec);
+               return parsed;
+       }
+
+       for (i = 0; i < result.parsed_table_len; i++) {
+               ec_parsed_add_child(parsed, 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_set_match(parsed, gen_node, match_strvec);
+
+       return parsed;
+
+ fail:
+       for (i = 0; i < result.parsed_table_len; i++)
+               ec_parsed_free(result.parsed_table[i]);
+       ec_free(result.parsed_table);
+       ec_parsed_free(parsed);
+
+       return NULL;
+}
+
+static struct ec_completed *
+__ec_node_subset_complete(struct ec_node **table, size_t table_len,
+       const struct ec_strvec *strvec)
+{
+       struct ec_completed *completed = NULL;
+       struct ec_completed *child_completed = NULL;
+       struct ec_strvec *childvec = NULL;
+       struct ec_parsed *parsed = NULL;
+       struct ec_node *save;
+       size_t i, len;
+
+       /*
+        * example with table = [a, b, c]
+        * subset_complete([a,b,c], strvec) returns:
+        *   complete(a, strvec) + complete(b, strvec) + complete(c, strvec) +
+        *   + __subset_complete([b, c], childvec) if a matches
+        *   + __subset_complete([a, c], childvec) if b matches
+        *   + __subset_complete([a, b], childvec) if c matches
+        */
+
+       completed = ec_completed_new();
+       if (completed == NULL)
+               goto fail;
+
+       /* first, try to complete with each node of the table */
+       for (i = 0; i < table_len; i++) {
+               if (table[i] == NULL)
+                       continue;
+
+               child_completed = ec_node_complete_strvec(table[i],
+                       strvec);
+
+               if (child_completed == NULL)
+                       goto fail;
+
+               ec_completed_merge(completed, child_completed);
+               child_completed = NULL;
+       }
+
+       /* then, if a node matches, advance in strvec and try to complete with
+        * all the other nodes */
+       for (i = 0; i < table_len; i++) {
+               if (table[i] == NULL)
+                       continue;
+
+               parsed = ec_node_parse_strvec(table[i], strvec);
+               if (parsed == NULL)
+                       goto fail;
+
+               if (!ec_parsed_matches(parsed)) {
+                       ec_parsed_free(parsed);
+                       parsed = NULL;
+                       continue;
+               }
+
+               len = ec_parsed_len(parsed);
+               childvec = ec_strvec_ndup(strvec, len,
+                                       ec_strvec_len(strvec) - len);
+               if (childvec == NULL)
+                       goto fail;
+
+               save = table[i];
+               table[i] = NULL;
+               child_completed = __ec_node_subset_complete(table,
+                                                       table_len, childvec);
+               table[i] = save;
+               ec_strvec_free(childvec);
+               childvec = NULL;
+
+               if (child_completed == NULL)
+                       goto fail;
+
+               ec_completed_merge(completed, child_completed);
+               child_completed = NULL;
+
+               ec_parsed_free(parsed);
+               parsed = NULL;
+       }
+
+       return completed;
+fail:
+       ec_parsed_free(parsed);
+       ec_completed_free(child_completed);
+       ec_completed_free(completed);
+
+       return NULL;
+}
+
+static struct ec_completed *ec_node_subset_complete(const struct ec_node *gen_node,
+       const struct ec_strvec *strvec)
+{
+       struct ec_node_subset *node = (struct ec_node_subset *)gen_node;
+
+       return __ec_node_subset_complete(node->table, node->len, strvec);
+}
+
+static void ec_node_subset_free_priv(struct ec_node *gen_node)
+{
+       struct ec_node_subset *node = (struct ec_node_subset *)gen_node;
+       unsigned int i;
+
+       for (i = 0; i < node->len; i++)
+               ec_node_free(node->table[i]);
+       ec_free(node->table);
+}
+
+int ec_node_subset_add(struct ec_node *gen_node, struct ec_node *child)
+{
+       struct ec_node_subset *node = (struct ec_node_subset *)gen_node;
+       struct ec_node **table;
+
+       assert(node != NULL);
+
+       if (child == NULL)
+               return -EINVAL;
+
+       gen_node->flags &= ~EC_NODE_F_BUILT;
+
+       table = ec_realloc(node->table, (node->len + 1) * sizeof(*node->table));
+       if (table == NULL) {
+               ec_node_free(child);
+               return -1;
+       }
+
+       node->table = table;
+       table[node->len] = child;
+       node->len++;
+
+       child->parent = gen_node;
+       TAILQ_INSERT_TAIL(&gen_node->children, child, next);
+
+       return 0;
+}
+
+static struct ec_node_type ec_node_subset_type = {
+       .name = "subset",
+       .parse = ec_node_subset_parse,
+       .complete = ec_node_subset_complete,
+       .size = sizeof(struct ec_node_subset),
+       .free_priv = ec_node_subset_free_priv,
+};
+
+EC_NODE_TYPE_REGISTER(ec_node_subset_type);
+
+struct ec_node *__ec_node_subset(const char *id, ...)
+{
+       struct ec_node *gen_node = NULL;
+       struct ec_node_subset *node = NULL;
+       struct ec_node *child;
+       va_list ap;
+       int fail = 0;
+
+       va_start(ap, id);
+
+       gen_node = __ec_node_new(&ec_node_subset_type, id);
+       node = (struct ec_node_subset *)gen_node;
+       if (node == NULL)
+               fail = 1;;
+
+       for (child = va_arg(ap, struct ec_node *);
+            child != EC_NODE_ENDLIST;
+            child = va_arg(ap, struct ec_node *)) {
+
+               /* on error, don't quit the loop to avoid leaks */
+               if (fail == 1 || child == NULL ||
+                               ec_node_subset_add(gen_node, child) < 0) {
+                       fail = 1;
+                       ec_node_free(child);
+               }
+       }
+
+       if (fail == 1)
+               goto fail;
+
+       va_end(ap);
+       return gen_node;
+
+fail:
+       ec_node_free(gen_node); /* will also free children */
+       va_end(ap);
+       return NULL;
+}
+
+static int ec_node_subset_testcase(void)
+{
+       struct ec_node *node;
+       int ret = 0;
+
+       node = EC_NODE_SUBSET(NULL,
+               EC_NODE_OR(NULL,
+                       ec_node_str(NULL, "foo"),
+                       ec_node_str(NULL, "bar")),
+               ec_node_str(NULL, "bar"),
+               ec_node_str(NULL, "toto")
+       );
+       if (node == NULL) {
+               ec_log(EC_LOG_ERR, "cannot create node\n");
+               return -1;
+       }
+       ret |= EC_TEST_CHECK_PARSE(node, 0);
+       ret |= EC_TEST_CHECK_PARSE(node, 1, "foo");
+       ret |= EC_TEST_CHECK_PARSE(node, 1, "bar");
+       ret |= EC_TEST_CHECK_PARSE(node, 2, "foo", "bar", "titi");
+       ret |= EC_TEST_CHECK_PARSE(node, 3, "bar", "foo", "toto");
+       ret |= EC_TEST_CHECK_PARSE(node, 1, "foo", "foo");
+       ret |= EC_TEST_CHECK_PARSE(node, 2, "bar", "bar");
+       ret |= EC_TEST_CHECK_PARSE(node, 2, "bar", "foo");
+       ret |= EC_TEST_CHECK_PARSE(node, 0, " ");
+       ret |= EC_TEST_CHECK_PARSE(node, 0, "foox");
+       ec_node_free(node);
+
+       /* test completion */
+       node = EC_NODE_SUBSET(NULL,
+               ec_node_str(NULL, "foo"),
+               ec_node_str(NULL, "bar"),
+               ec_node_str(NULL, "bar2"),
+               ec_node_str(NULL, "toto"),
+               ec_node_str(NULL, "titi")
+       );
+       if (node == NULL) {
+               ec_log(EC_LOG_ERR, "cannot create node\n");
+               return -1;
+       }
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "", EC_NODE_ENDLIST,
+               "foo", "bar", "bar2", "toto", "titi", EC_NODE_ENDLIST,
+               "");
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "bar", "bar2", "", EC_NODE_ENDLIST,
+               "foo", "toto", "titi", EC_NODE_ENDLIST,
+               "");
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "f", EC_NODE_ENDLIST,
+               "oo", EC_NODE_ENDLIST,
+               "oo");
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "b", EC_NODE_ENDLIST,
+               "ar", "ar2", EC_NODE_ENDLIST,
+               "ar");
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "bar", EC_NODE_ENDLIST,
+               "", "2", EC_NODE_ENDLIST,
+               "");
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "bar", "b", EC_NODE_ENDLIST,
+               "ar2", EC_NODE_ENDLIST,
+               "ar2");
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "t", EC_NODE_ENDLIST,
+               "oto", "iti", EC_NODE_ENDLIST,
+               "");
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "to", EC_NODE_ENDLIST,
+               "to", EC_NODE_ENDLIST,
+               "to");
+       ret |= EC_TEST_CHECK_COMPLETE(node,
+               "x", EC_NODE_ENDLIST,
+               EC_NODE_ENDLIST,
+               "");
+       ec_node_free(node);
+
+       return ret;
+}
+
+static struct ec_test ec_node_subset_test = {
+       .name = "node_subset",
+       .test = ec_node_subset_testcase,
+};
+
+EC_TEST_REGISTER(ec_node_subset_test);
diff --git a/lib/ecoli_node_subset.h b/lib/ecoli_node_subset.h
new file mode 100644 (file)
index 0000000..9d350fb
--- /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_NODE_SUBSET_
+#define ECOLI_NODE_SUBSET_
+
+#include <ecoli_node.h>
+
+#define EC_NODE_SUBSET(args...) __ec_node_subset(args, EC_NODE_ENDLIST)
+
+/* list must be terminated with EC_NODE_ENDLIST */
+/* all nodes given in the list will be freed when freeing this one */
+/* avoid using this function directly, prefer the macro EC_NODE_SUBSET() or
+ * ec_node_subset() + ec_node_subset_add() */
+struct ec_node *__ec_node_subset(const char *id, ...);
+
+struct ec_node *ec_node_subset(const char *id);
+
+/* child is consumed */
+int ec_node_subset_add(struct ec_node *node, struct ec_node *child);
+
+#endif
diff --git a/lib/ecoli_node_weakref.c b/lib/ecoli_node_weakref.c
new file mode 100644 (file)
index 0000000..7370962
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * 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 <assert.h>
+#include <stdarg.h>
+#include <errno.h>
+
+#include <ecoli_malloc.h>
+#include <ecoli_log.h>
+#include <ecoli_test.h>
+#include <ecoli_strvec.h>
+#include <ecoli_node.h>
+#include <ecoli_node_str.h>
+#include <ecoli_node_option.h>
+#include <ecoli_node_weakref.h>
+
+struct ec_node_weakref {
+       struct ec_node gen;
+       struct ec_node *child;
+};
+
+static struct ec_parsed *ec_node_weakref_parse(const struct ec_node *gen_node,
+       const struct ec_strvec *strvec)
+{
+       struct ec_node_weakref *node = (struct ec_node_weakref *)gen_node;
+
+       return ec_node_parse_strvec(node->child, strvec);
+}
+
+static struct ec_completed *ec_node_weakref_complete(const struct ec_node *gen_node,
+       const struct ec_strvec *strvec)
+{
+       struct ec_node_weakref *node = (struct ec_node_weakref *)gen_node;
+
+       return ec_node_complete_strvec(node->child, strvec);
+}
+
+static struct ec_node_type ec_node_weakref_type = {
+       .name = "weakref",
+       .parse = ec_node_weakref_parse,
+       .complete = ec_node_weakref_complete,
+       .size = sizeof(struct ec_node_weakref),
+};
+
+EC_NODE_TYPE_REGISTER(ec_node_weakref_type);
+
+int ec_node_weakref_set(struct ec_node *gen_node, struct ec_node *child)
+{
+       struct ec_node_weakref *node = (struct ec_node_weakref *)gen_node;
+
+       // XXX check node type
+
+       assert(node != NULL);
+
+       if (child == NULL)
+               return -EINVAL;
+
+       gen_node->flags &= ~EC_NODE_F_BUILT;
+
+       node->child = child;
+
+       child->parent = gen_node;
+       TAILQ_INSERT_TAIL(&gen_node->children, child, next); // XXX really needed?
+
+       return 0;
+}
+
+struct ec_node *ec_node_weakref(const char *id, struct ec_node *child)
+{
+       struct ec_node *gen_node = NULL;
+
+       if (child == NULL)
+               return NULL;
+
+       gen_node = __ec_node_new(&ec_node_weakref_type, id);
+       if (gen_node == NULL)
+               return NULL;
+
+       ec_node_weakref_set(gen_node, child);
+
+       return gen_node;
+}
+
+static int ec_node_weakref_testcase(void)
+{
+       return 0;
+}
+
+static struct ec_test ec_node_weakref_test = {
+       .name = "node_weakref",
+       .test = ec_node_weakref_testcase,
+};
+
+EC_TEST_REGISTER(ec_node_weakref_test);
diff --git a/lib/ecoli_node_weakref.h b/lib/ecoli_node_weakref.h
new file mode 100644 (file)
index 0000000..c29799f
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * 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_NODE_BYPASS_
+#define ECOLI_NODE_BYPASS_
+
+#include <ecoli_node.h>
+
+/* a node that just behaves like its child and that does not free
+ * its child when freed.
+ *
+ * useful to create cyclic graphs of nodes:
+ *   creating a loop (with clones) result in something that is not
+ *   freeable, due to reference counters
+ *
+ * Example:
+ *  val = int(0, 10)
+ *  op = str("!")
+ *  expr = or()
+ *  seq = seq(clone(op), clone(expr))
+ *  expr.add(clone(seq))
+ *  expr.add(clone(val))
+ *  free(val)
+ *  free(op)
+ *  free(seq)
+ *
+ * FAIL: expr cannot be freed due to cyclic refs
+ * The references are like this:
+ *
+ *                   val
+ *                    ^
+ *                    |
+ *        $user ---> expr ---> seq ---> op
+ *                        <---
+ *
+ * It is solved with:
+ *  val = int(0, 10)
+ *  op = str("!")
+ *  expr = or()
+ *  weak = weak(expr)
+ *  seq = seq(clone(op), clone(weak))
+ *  expr.add(clone(seq))
+ *  expr.add(clone(val))
+ *  free(val)
+ *  free(op)
+ *  free(weak)
+ *  free(seq)
+ *
+ *
+ *                   val
+ *                    ^
+ *                    |
+ *        $user ---> expr ---------------> seq ---> op
+ *                        <- - - weak <---
+ *
+ * The node expr can be freed.
+ */
+
+/* on error, child is *not* freed */
+struct ec_node *ec_node_weakref(const char *id, struct ec_node *child);
+
+struct ec_node *ec_node_weakref_empty(const char *id);
+
+/* on error, child is *not* freed */
+int ec_node_weakref_set(struct ec_node *node, struct ec_node *child);
+
+#endif
index 2ac659b8bb16b4252103c8174aacd78bbc99ba09..b4dd728f1b04f2d9872689b3e3726544706e562f 100644 (file)
@@ -34,7 +34,7 @@
 #include <ecoli_malloc.h>
 #include <ecoli_test.h>
 #include <ecoli_strvec.h>
-#include <ecoli_tk.h>
+#include <ecoli_node.h>
 
 static struct ec_test_list test_list = TAILQ_HEAD_INITIALIZER(test_list);
 
@@ -45,9 +45,9 @@ void ec_test_register(struct ec_test *test)
        // XXX check if already exist, like for type
 }
 
-int ec_test_check_tk_parse(struct ec_tk *tk, int expected, ...)
+int ec_test_check_parse(struct ec_node *tk, int expected, ...)
 {
-       struct ec_parsed_tk *p;
+       struct ec_parsed *p;
        struct ec_strvec *vec = NULL;
        const char *s;
        int ret = -1, match;
@@ -61,7 +61,7 @@ int ec_test_check_tk_parse(struct ec_tk *tk, int expected, ...)
                goto out;
 
        for (s = va_arg(ap, const char *);
-            s != EC_TK_ENDLIST;
+            s != EC_NODE_ENDLIST;
             s = va_arg(ap, const char *)) {
                if (s == NULL)
                        goto out;
@@ -70,14 +70,14 @@ int ec_test_check_tk_parse(struct ec_tk *tk, int expected, ...)
                        goto out;
        }
 
-       p = ec_tk_parse_tokens(tk, vec);
+       p = ec_node_parse_strvec(tk, vec);
        /* XXX only for debug */
-       ec_parsed_tk_dump(stdout, p);
+       ec_parsed_dump(stdout, p);
        if (p == NULL) {
-               ec_log(EC_LOG_ERR, "parsed_tk is NULL\n");
+               ec_log(EC_LOG_ERR, "parsed is NULL\n");
        }
-       if (ec_parsed_tk_matches(p))
-               match = ec_parsed_tk_len(p);
+       if (ec_parsed_matches(p))
+               match = ec_parsed_len(p);
        else
                match = -1;
        if (expected == match) {
@@ -88,7 +88,7 @@ int ec_test_check_tk_parse(struct ec_tk *tk, int expected, ...)
                        match, expected);
        }
 
-       ec_parsed_tk_free(p);
+       ec_parsed_free(p);
 
 out:
        ec_strvec_free(vec);
@@ -96,10 +96,10 @@ out:
        return ret;
 }
 
-int ec_test_check_tk_complete(struct ec_tk *tk, ...)
+int ec_test_check_complete(struct ec_node *tk, ...)
 {
-       struct ec_completed_tk *c = NULL;
-       struct ec_completed_tk_elt *elt;
+       struct ec_completed *c = NULL;
+       struct ec_completed_elt *elt;
        struct ec_strvec *vec = NULL;
        const char *s, *expected;
        int ret = 0;
@@ -114,7 +114,7 @@ int ec_test_check_tk_complete(struct ec_tk *tk, ...)
                goto out;
 
        for (s = va_arg(ap, const char *);
-            s != EC_TK_ENDLIST;
+            s != EC_NODE_ENDLIST;
             s = va_arg(ap, const char *)) {
                if (s == NULL)
                        goto out;
@@ -123,14 +123,14 @@ int ec_test_check_tk_complete(struct ec_tk *tk, ...)
                        goto out;
        }
 
-       c = ec_tk_complete_tokens(tk, vec);
+       c = ec_node_complete_strvec(tk, vec);
        if (c == NULL) {
                ret = -1;
                goto out;
        }
 
        for (s = va_arg(ap, const char *);
-            s != EC_TK_ENDLIST;
+            s != EC_NODE_ENDLIST;
             s = va_arg(ap, const char *)) {
                if (s == NULL) {
                        ret = -1;
@@ -151,17 +151,17 @@ int ec_test_check_tk_complete(struct ec_tk *tk, ...)
                }
        }
 
-       if (count != ec_completed_tk_count(c, EC_MATCH)) {
+       if (count != ec_completed_count(c, EC_MATCH)) {
                ec_log(EC_LOG_ERR,
                        "nb_completion (%d) does not match (%d)\n",
-                       count, ec_completed_tk_count(c, EC_MATCH));
-               ec_completed_tk_dump(stdout, c);
+                       count, ec_completed_count(c, EC_MATCH));
+               ec_completed_dump(stdout, c);
                ret = -1;
        }
 
 
        expected = va_arg(ap, const char *);
-       s = ec_completed_tk_smallest_start(c);
+       s = ec_completed_smallest_start(c);
        if (strcmp(s, expected)) {
                ret = -1;
                ec_log(EC_LOG_ERR,
@@ -171,7 +171,7 @@ int ec_test_check_tk_complete(struct ec_tk *tk, ...)
 
 out:
        ec_strvec_free(vec);
-       ec_completed_tk_free(c);
+       ec_completed_free(c);
        va_end(ap);
        return ret;
 }
index cf41f46180aca9e3dd7b364a850dca7f7118d139..01158379a8b598350d6902474cb2a5f500033eb8 100644 (file)
@@ -31,7 +31,7 @@
 #include <sys/queue.h>
 
 #include <ecoli_log.h>
-#include <ecoli_tk.h>
+#include <ecoli_node.h>
 
 // XXX check if already exists?
 #define EC_TEST_REGISTER(t)                                            \
@@ -71,7 +71,7 @@ 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, ...);
+int ec_test_check_parse(struct ec_node *node, int expected, ...);
 
 #define EC_TEST_ERR(fmt, ...)                                          \
        ec_log(EC_LOG_ERR, "%s:%d: error: " fmt "\n",                   \
@@ -83,18 +83,18 @@ int ec_test_check_tk_parse(struct ec_tk *tk, int expected, ...);
                        EC_TEST_ERR("assertion failure: " #cond);       \
        } while (0)
 
-/* tk, input, [expected1, expected2, ...] */
-#define EC_TEST_CHECK_TK_PARSE(tk, args...) ({         \
-       int ret_ = ec_test_check_tk_parse(tk, args, EC_TK_ENDLIST);     \
+/* node, input, [expected1, expected2, ...] */
+#define EC_TEST_CHECK_PARSE(node, args...) ({                          \
+       int ret_ = ec_test_check_parse(node, args, EC_NODE_ENDLIST);    \
        if (ret_)                                                       \
                EC_TEST_ERR("parse test failed");                       \
        ret_;                                                           \
 })
 
-int ec_test_check_tk_complete(struct ec_tk *tk, ...);
+int ec_test_check_complete(struct ec_node *node, ...);
 
-#define EC_TEST_CHECK_TK_COMPLETE(tk, args...) ({                      \
-       int ret_ = ec_test_check_tk_complete(tk, args);                 \
+#define EC_TEST_CHECK_COMPLETE(node, args...) ({                       \
+       int ret_ = ec_test_check_complete(node, args);                  \
        if (ret_)                                                       \
                EC_TEST_ERR("complete test failed");                    \
        ret_;                                                           \
diff --git a/lib/ecoli_tk.c b/lib/ecoli_tk.c
deleted file mode 100644 (file)
index 5867fbe..0000000
+++ /dev/null
@@ -1,719 +0,0 @@
-/*
- * 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 <assert.h>
-#include <errno.h>
-
-#include <ecoli_malloc.h>
-#include <ecoli_strvec.h>
-#include <ecoli_keyval.h>
-#include <ecoli_log.h>
-#include <ecoli_tk.h>
-
-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;
-       if (type->size < sizeof(struct ec_tk))
-               return -EINVAL;
-
-       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 struct ec_tk_type *type, const char *id)
-{
-       struct ec_tk *tk = NULL;
-       char buf[256]; // XXX
-
-       ec_log(EC_LOG_DEBUG, "create node type=%s id=%s\n", type->name, id);
-
-       tk = ec_calloc(1, type->size);
-       if (tk == NULL)
-               goto fail;
-
-       TAILQ_INIT(&tk->children);
-       tk->type = type;
-       tk->refcnt = 1;
-
-       if (id != NULL) {
-               tk->id = ec_strdup(id);
-               if (tk->id == NULL)
-                       goto fail;
-       }
-
-       snprintf(buf, sizeof(buf), "<%s>", type->name);
-       tk->desc = ec_strdup(buf); // XXX ec_asprintf ?
-       if (tk->desc == NULL)
-               goto fail;
-
-       tk->attrs = ec_keyval_new();
-       if (tk->attrs == NULL)
-               goto fail;
-
-       return tk;
-
- fail:
-       ec_tk_free(tk);
-       return NULL;
-}
-
-struct ec_tk *ec_tk_new(const char *typename, const char *id)
-{
-       struct ec_tk_type *type;
-
-       type = ec_tk_type_lookup(typename);
-       if (type == NULL) {
-               ec_log(EC_LOG_ERR, "type=%s does not exist\n", typename);
-               return NULL;
-       }
-
-       return __ec_tk_new(type, id);
-}
-
-void ec_tk_free(struct ec_tk *tk)
-{
-       if (tk == NULL)
-               return;
-
-       assert(tk->refcnt > 0);
-
-       if (--tk->refcnt > 0)
-               return;
-
-       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);
-       ec_free(tk);
-}
-
-struct ec_tk *ec_tk_clone(struct ec_tk *tk)
-{
-       if (tk != NULL)
-               tk->refcnt++;
-       return tk;
-}
-
-struct ec_tk *ec_tk_find(struct ec_tk *tk, const char *id)
-{
-       struct ec_tk *child, *ret;
-       const char *tk_id = ec_tk_id(tk);
-
-       if (id != NULL && tk_id != NULL && !strcmp(tk_id, id))
-               return tk;
-
-       TAILQ_FOREACH(child, &tk->children, next) {
-               ret = ec_tk_find(child, id);
-               if (ret != NULL)
-                       return ret;
-       }
-
-       return NULL;
-}
-
-struct ec_keyval *ec_tk_attrs(const struct ec_tk *tk)
-{
-       return tk->attrs;
-}
-
-const char *ec_tk_id(const struct ec_tk *tk)
-{
-       return tk->id;
-}
-
-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;
-       struct ec_parsed_tk *parsed_tk;
-
-       errno = ENOMEM;
-       strvec = ec_strvec_new();
-       if (strvec == NULL)
-               goto fail;
-
-       if (ec_strvec_add(strvec, str) < 0)
-               goto fail;
-
-       parsed_tk = ec_tk_parse_tokens(tk, strvec);
-       if (parsed_tk == NULL)
-               goto fail;
-
-       ec_strvec_free(strvec);
-       return parsed_tk;
-
- fail:
-       ec_strvec_free(strvec);
-       return NULL;
-}
-
-struct ec_parsed_tk *ec_tk_parse_tokens(struct ec_tk *tk,
-       const struct ec_strvec *strvec)
-{
-       struct ec_parsed_tk *parsed_tk;
-       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->parse == NULL) {
-               errno = ENOTSUP;
-               return NULL;
-       }
-
-       parsed_tk = tk->type->parse(tk, strvec);
-
-       return parsed_tk;
-}
-
-
-struct ec_parsed_tk *ec_parsed_tk_new(void)
-{
-       struct ec_parsed_tk *parsed_tk = NULL;
-
-       parsed_tk = ec_calloc(1, sizeof(*parsed_tk));
-       if (parsed_tk == NULL)
-               goto fail;
-
-       TAILQ_INIT(&parsed_tk->children);
-
-       return parsed_tk;
-
- fail:
-       return NULL;
-}
-
-void ec_parsed_tk_set_match(struct ec_parsed_tk *parsed_tk,
-       const struct ec_tk *tk, struct ec_strvec *strvec)
-{
-       parsed_tk->tk = tk;
-       parsed_tk->strvec = strvec;
-}
-
-void ec_parsed_tk_free_children(struct ec_parsed_tk *parsed_tk)
-{
-       struct ec_parsed_tk *child;
-
-       if (parsed_tk == NULL)
-               return;
-
-       while (!TAILQ_EMPTY(&parsed_tk->children)) {
-               child = TAILQ_FIRST(&parsed_tk->children);
-               TAILQ_REMOVE(&parsed_tk->children, child, next);
-               ec_parsed_tk_free(child);
-       }
-}
-
-void ec_parsed_tk_free(struct ec_parsed_tk *parsed_tk)
-{
-       if (parsed_tk == NULL)
-               return;
-
-       ec_parsed_tk_free_children(parsed_tk);
-       ec_strvec_free(parsed_tk->strvec);
-       ec_free(parsed_tk);
-}
-
-static void __ec_parsed_tk_dump(FILE *out,
-       const struct ec_parsed_tk *parsed_tk, size_t indent)
-{
-       struct ec_parsed_tk *child;
-       const struct ec_strvec *vec;
-       size_t i;
-       const char *id = "None", *typename = "None";
-
-       if (parsed_tk->tk != NULL) {
-               if (parsed_tk->tk->id != NULL)
-                       id = parsed_tk->tk->id;
-               typename = parsed_tk->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 vec=[", typename, id);
-       vec = ec_parsed_tk_strvec(parsed_tk);
-       for (i = 0; i < ec_strvec_len(vec); i++)
-               fprintf(out, "%s<%s>",
-                       i == 0 ? "" : ",",
-                       ec_strvec_val(vec, i));
-       fprintf(out, "]\n");
-
-       TAILQ_FOREACH(child, &parsed_tk->children, next)
-               __ec_parsed_tk_dump(out, child, indent + 2);
-}
-
-void ec_parsed_tk_dump(FILE *out, const struct ec_parsed_tk *parsed_tk)
-{
-       fprintf(out, "------------------- parsed_tk dump:\n"); //XXX
-
-       if (parsed_tk == NULL) {
-               fprintf(out, "parsed_tk is NULL, error in parse\n");
-               return;
-       }
-       if (!ec_parsed_tk_matches(parsed_tk)) {
-               fprintf(out, "no match\n");
-               return;
-       }
-
-       __ec_parsed_tk_dump(out, parsed_tk, 0);
-}
-
-void ec_parsed_tk_add_child(struct ec_parsed_tk *parsed_tk,
-       struct ec_parsed_tk *child)
-{
-       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)
-{
-       struct ec_parsed_tk *child, *ret;
-
-       if (parsed_tk == NULL)
-               return NULL;
-
-       if (parsed_tk->tk != NULL &&
-                       parsed_tk->tk->id != NULL &&
-                       !strcmp(parsed_tk->tk->id, id))
-               return parsed_tk;
-
-       TAILQ_FOREACH(child, &parsed_tk->children, next) {
-               ret = ec_parsed_tk_find_first(child, id);
-               if (ret != NULL)
-                       return ret;
-       }
-
-       return NULL;
-}
-
-const struct ec_strvec *ec_parsed_tk_strvec(
-       const struct ec_parsed_tk *parsed_tk)
-{
-       if (parsed_tk == NULL || parsed_tk->strvec == NULL)
-               return NULL;
-
-       return parsed_tk->strvec;
-}
-
-/* number of parsed tokens */
-size_t ec_parsed_tk_len(const struct ec_parsed_tk *parsed_tk)
-{
-       if (parsed_tk == NULL || parsed_tk->strvec == NULL)
-               return 0;
-
-       return ec_strvec_len(parsed_tk->strvec);
-}
-
-size_t ec_parsed_tk_matches(const struct ec_parsed_tk *parsed_tk)
-{
-       if (parsed_tk == NULL)
-               return 0;
-
-       if (parsed_tk->tk == NULL && TAILQ_EMPTY(&parsed_tk->children))
-               return 0;
-
-       return 1;
-}
-
-struct ec_completed_tk *ec_completed_tk_new(void)
-{
-       struct ec_completed_tk *completed_tk = NULL;
-
-       completed_tk = ec_calloc(1, sizeof(*completed_tk));
-       if (completed_tk == NULL)
-               return NULL;
-
-       TAILQ_INIT(&completed_tk->elts);
-       completed_tk->count_match = 0;
-
-       return completed_tk;
-}
-
-struct ec_completed_tk_elt *ec_completed_tk_elt_new(const struct ec_tk *tk,
-       const char *add)
-{
-       struct ec_completed_tk_elt *elt = NULL;
-
-       elt = ec_calloc(1, sizeof(*elt));
-       if (elt == NULL)
-               return NULL;
-
-       elt->tk = tk;
-       if (add != NULL) {
-               elt->add = ec_strdup(add);
-               if (elt->add == 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
- * other idea: have 2 functions
- */
-struct ec_completed_tk *ec_tk_complete(struct ec_tk *tk,
-       const char *str)
-{
-       struct ec_strvec *strvec = NULL;
-       struct ec_completed_tk *completed_tk;
-
-       errno = ENOMEM;
-       strvec = ec_strvec_new();
-       if (strvec == NULL)
-               goto fail;
-
-       if (ec_strvec_add(strvec, str) < 0)
-               goto fail;
-
-       completed_tk = ec_tk_complete_tokens(tk, strvec);
-       if (completed_tk == NULL)
-               goto fail;
-
-       ec_strvec_free(strvec);
-       return completed_tk;
-
- fail:
-       ec_strvec_free(strvec);
-       return NULL;
-}
-
-/* default completion function: return a no-match element */
-struct ec_completed_tk *ec_tk_default_complete(const struct ec_tk *gen_tk,
-       const struct ec_strvec *strvec)
-{
-       struct ec_completed_tk *completed_tk;
-       struct ec_completed_tk_elt *completed_tk_elt;
-
-       (void)strvec;
-
-       completed_tk = ec_completed_tk_new();
-       if (completed_tk == NULL)
-               return NULL;
-
-       completed_tk_elt = ec_completed_tk_elt_new(gen_tk, NULL);
-       if (completed_tk_elt == NULL) {
-               ec_completed_tk_free(completed_tk);
-               return NULL;
-       }
-
-       ec_completed_tk_add_elt(completed_tk, completed_tk_elt);
-
-       return completed_tk;
-}
-
-struct ec_completed_tk *ec_tk_complete_tokens(struct ec_tk *tk,
-       const struct ec_strvec *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 */
-static size_t strcmp_count(const char *s1, const char *s2)
-{
-       size_t i = 0;
-
-       while (s1[i] && s2[i] && s1[i] == s2[i])
-               i++;
-
-       return i;
-}
-
-void ec_completed_tk_add_elt(
-       struct ec_completed_tk *completed_tk, struct ec_completed_tk_elt *elt)
-{
-       size_t n;
-
-       TAILQ_INSERT_TAIL(&completed_tk->elts, elt, next);
-       completed_tk->count++;
-       if (elt->add != NULL)
-               completed_tk->count_match++;
-       if (elt->add != NULL) {
-               if (completed_tk->smallest_start == NULL) {
-                       completed_tk->smallest_start = ec_strdup(elt->add);
-               } else {
-                       n = strcmp_count(elt->add,
-                               completed_tk->smallest_start);
-                       completed_tk->smallest_start[n] = '\0';
-               }
-       }
-}
-
-void ec_completed_tk_elt_free(struct ec_completed_tk_elt *elt)
-{
-       ec_free(elt->add);
-       ec_free(elt);
-}
-
-void ec_completed_tk_merge(struct ec_completed_tk *completed_tk1,
-       struct ec_completed_tk *completed_tk2)
-{
-       struct ec_completed_tk_elt *elt;
-
-       assert(completed_tk1 != NULL);
-       assert(completed_tk2 != NULL);
-
-       while (!TAILQ_EMPTY(&completed_tk2->elts)) {
-               elt = TAILQ_FIRST(&completed_tk2->elts);
-               TAILQ_REMOVE(&completed_tk2->elts, elt, next);
-               ec_completed_tk_add_elt(completed_tk1, elt);
-       }
-
-       ec_completed_tk_free(completed_tk2);
-}
-
-void ec_completed_tk_free(struct ec_completed_tk *completed_tk)
-{
-       struct ec_completed_tk_elt *elt;
-
-       if (completed_tk == NULL)
-               return;
-
-       while (!TAILQ_EMPTY(&completed_tk->elts)) {
-               elt = TAILQ_FIRST(&completed_tk->elts);
-               TAILQ_REMOVE(&completed_tk->elts, elt, next);
-               ec_completed_tk_elt_free(elt);
-       }
-       ec_free(completed_tk->smallest_start);
-       ec_free(completed_tk);
-}
-
-void ec_completed_tk_dump(FILE *out, const struct ec_completed_tk *completed_tk)
-{
-       struct ec_completed_tk_elt *elt;
-
-       if (completed_tk == NULL || completed_tk->count == 0) {
-               fprintf(out, "no completion\n");
-               return;
-       }
-
-       fprintf(out, "completion: count=%u match=%u smallest_start=<%s>\n",
-               completed_tk->count, completed_tk->count_match,
-               completed_tk->smallest_start);
-
-       TAILQ_FOREACH(elt, &completed_tk->elts, next) {
-               fprintf(out, "add=<%s>, tk=%p, tk_type=%s\n",
-                       elt->add, elt->tk, elt->tk->type->name);
-       }
-}
-
-const char *ec_completed_tk_smallest_start(
-       const struct ec_completed_tk *completed_tk)
-{
-       if (completed_tk == NULL || completed_tk->smallest_start == NULL)
-               return "";
-
-       return completed_tk->smallest_start;
-}
-
-unsigned int ec_completed_tk_count(
-       const struct ec_completed_tk *completed_tk,
-       enum ec_completed_tk_filter_flags flags)
-{
-       unsigned int count = 0;
-
-       if (completed_tk == NULL)
-               return count;
-
-       if (flags & EC_MATCH)
-               count += completed_tk->count_match;
-       if (flags & EC_NO_MATCH)
-               count += (completed_tk->count - completed_tk->count_match); //XXX
-
-       return count;
-}
-
-struct ec_completed_tk_iter *
-ec_completed_tk_iter_new(struct ec_completed_tk *completed_tk,
-       enum ec_completed_tk_filter_flags flags)
-{
-       struct ec_completed_tk_iter *iter;
-
-       iter = ec_calloc(1, sizeof(*iter));
-       if (iter == NULL)
-               return NULL;
-
-       iter->completed_tk = completed_tk;
-       iter->flags = flags;
-       iter->cur = NULL;
-
-       return iter;
-}
-
-const struct ec_completed_tk_elt *ec_completed_tk_iter_next(
-       struct ec_completed_tk_iter *iter)
-{
-       if (iter->completed_tk == NULL)
-               return NULL;
-
-       do {
-               if (iter->cur == NULL) {
-                       iter->cur = TAILQ_FIRST(&iter->completed_tk->elts);
-               } else {
-                       iter->cur = TAILQ_NEXT(iter->cur, next);
-               }
-
-               if (iter->cur == NULL)
-                       break;
-
-               if (iter->cur->add == NULL &&
-                               (iter->flags & EC_NO_MATCH))
-                       break;
-
-               if (iter->cur->add != NULL &&
-                               (iter->flags & EC_MATCH))
-                       break;
-
-       } while (iter->cur != NULL);
-
-       return iter->cur;
-}
-
-void ec_completed_tk_iter_free(struct ec_completed_tk_iter *iter)
-{
-       ec_free(iter);
-}
-
-const char *ec_tk_desc(const struct ec_tk *tk)
-{
-       if (tk->type->desc != NULL)
-               return tk->type->desc(tk);
-
-       return tk->desc;
-}
diff --git a/lib/ecoli_tk.h b/lib/ecoli_tk.h
deleted file mode 100644 (file)
index cdcf625..0000000
+++ /dev/null
@@ -1,259 +0,0 @@
-/*
- * 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_
-#define ECOLI_TK_
-
-#include <sys/queue.h>
-#include <sys/types.h>
-#include <stdio.h>
-
-#define EC_TK_ENDLIST ((void *)1)
-
-struct ec_tk;
-struct ec_parsed_tk;
-struct ec_strvec;
-struct ec_keyval;
-
-/* return 0 on success, else -errno. */
-typedef int (*ec_tk_build_t)(struct ec_tk *tk);
-
-typedef struct ec_parsed_tk *(*ec_tk_parse_t)(const struct ec_tk *tk,
-       const struct ec_strvec *strvec);
-typedef struct ec_completed_tk *(*ec_tk_complete_t)(const struct ec_tk *tk,
-       const struct ec_strvec *strvec);
-typedef const char * (*ec_tk_desc_t)(const struct ec_tk *);
-typedef void (*ec_tk_init_priv_t)(struct ec_tk *);
-typedef void (*ec_tk_free_priv_t)(struct ec_tk *);
-
-#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;
-       ec_tk_desc_t desc;
-       size_t size;
-       ec_tk_init_priv_t init_priv;
-       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_type *type;
-       char *id;
-       char *desc;
-       struct ec_keyval *attrs;
-       /* XXX ensure parent and child are properly set in all nodes */
-       struct ec_tk *parent;
-       unsigned int refcnt;
-#define EC_TK_F_BUILT 0x0001 /** set if configuration is built */
-       unsigned int flags;
-
-       TAILQ_ENTRY(ec_tk) next;
-       struct ec_tk_list children;
-};
-
-/* create a new token when the type is known, typically called from the node
- * code */
-struct ec_tk *__ec_tk_new(const struct ec_tk_type *type, const char *id);
-
-/* create a_new token node */
-struct ec_tk *ec_tk_new(const char *typename, const char *id);
-
-void ec_tk_free(struct ec_tk *tk);
-
-/* XXX add more accessors */
-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 ? */
-
-TAILQ_HEAD(ec_parsed_tk_list, ec_parsed_tk);
-
-/*
-  tk == NULL + empty children list means "no match"
-*/
-struct ec_parsed_tk {
-       TAILQ_ENTRY(ec_parsed_tk) next;
-       struct ec_parsed_tk_list children;
-       const struct ec_tk *tk;
-       struct ec_strvec *strvec;
-};
-
-struct ec_parsed_tk *ec_parsed_tk_new(void);
-void ec_parsed_tk_free(struct ec_parsed_tk *parsed_tk);
-struct ec_tk *ec_tk_clone(struct ec_tk *tk);
-void ec_parsed_tk_free_children(struct ec_parsed_tk *parsed_tk);
-
-const struct ec_strvec *ec_parsed_tk_strvec(
-       const struct ec_parsed_tk *parsed_tk);
-
-void ec_parsed_tk_set_match(struct ec_parsed_tk *parsed_tk,
-       const struct ec_tk *tk, struct ec_strvec *strvec);
-
-/* 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() ? */
-/* a NULL return value is an error, with errno set
-  ENOTSUP: no ->parse() operation
-*/
-struct ec_parsed_tk *ec_tk_parse(struct ec_tk *tk, const char *str);
-
-/* mostly internal to tokens */
-/* XXX it should not reset cache
- * ... not sure... it is used by tests */
-struct ec_parsed_tk *ec_tk_parse_tokens(struct ec_tk *tk,
-       const struct ec_strvec *strvec);
-
-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,
-       const char *id);
-
-const char *ec_parsed_tk_to_string(const struct ec_parsed_tk *parsed_tk);
-size_t ec_parsed_tk_len(const struct ec_parsed_tk *parsed_tk);
-size_t ec_parsed_tk_matches(const struct ec_parsed_tk *parsed_tk);
-
-struct ec_completed_tk_elt {
-       TAILQ_ENTRY(ec_completed_tk_elt) next;
-       const struct ec_tk *tk;
-       char *add;
-};
-
-TAILQ_HEAD(ec_completed_tk_elt_list, ec_completed_tk_elt);
-
-
-struct ec_completed_tk {
-       struct ec_completed_tk_elt_list elts;
-       unsigned count;
-       unsigned count_match;
-       char *smallest_start;
-};
-
-/*
- * return a completed_tk object filled with elts
- * return NULL on error (nomem?)
- */
-struct ec_completed_tk *ec_tk_complete(struct ec_tk *tk,
-       const char *str);
-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,
-       const char *add);
-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);
-void ec_completed_tk_merge(struct ec_completed_tk *completed_tk1,
-       struct ec_completed_tk *completed_tk2);
-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);
-struct ec_completed_tk *ec_tk_default_complete(const struct ec_tk *gen_tk,
-       const struct ec_strvec *strvec);
-
-/* cannot return NULL */
-const char *ec_completed_tk_smallest_start(
-       const struct ec_completed_tk *completed_tk);
-
-enum ec_completed_tk_filter_flags {
-       EC_MATCH = 1,
-       EC_NO_MATCH = 2,
-};
-
-unsigned int ec_completed_tk_count(
-       const struct ec_completed_tk *completed_tk,
-       enum ec_completed_tk_filter_flags flags);
-
-struct ec_completed_tk_iter {
-       enum ec_completed_tk_filter_flags flags;
-       const struct ec_completed_tk *completed_tk;
-       const struct ec_completed_tk_elt *cur;
-};
-
-struct ec_completed_tk_iter *
-ec_completed_tk_iter_new(struct ec_completed_tk *completed_tk,
-       enum ec_completed_tk_filter_flags flags);
-
-const struct ec_completed_tk_elt *ec_completed_tk_iter_next(
-       struct ec_completed_tk_iter *iter);
-
-void ec_completed_tk_iter_free(struct ec_completed_tk_iter *iter);
-
-
-#endif
diff --git a/lib/ecoli_tk_cmd.c b/lib/ecoli_tk_cmd.c
deleted file mode 100644 (file)
index 03ccb60..0000000
+++ /dev/null
@@ -1,506 +0,0 @@
-/*
- * 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_subset.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), ",")) {
-               out = EC_TK_SUBSET(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 {
-               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 if (!strcmp(ec_strvec_val(vec, 0), "(")) {
-               out = 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_new("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_new("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,
-       .size = sizeof(struct ec_tk_cmd),
-       .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(&ec_tk_cmd_type, id);
-       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,
-               "command [option] (subset1, subset2) 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, "command", "1");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, 2, "command", "23");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, 3, "command", "option", "23");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "command", "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
deleted file mode 100644 (file)
index f324ff4..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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
diff --git a/lib/ecoli_tk_empty.c b/lib/ecoli_tk_empty.c
deleted file mode 100644 (file)
index a2b366c..0000000
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * 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 <ecoli_malloc.h>
-#include <ecoli_log.h>
-#include <ecoli_test.h>
-#include <ecoli_strvec.h>
-#include <ecoli_tk.h>
-#include <ecoli_tk_empty.h>
-
-struct ec_tk_empty {
-       struct ec_tk gen;
-};
-
-static struct ec_parsed_tk *ec_tk_empty_parse(const struct ec_tk *gen_tk,
-       const struct ec_strvec *strvec)
-{
-       struct ec_parsed_tk *parsed_tk;
-       struct ec_strvec *match_strvec;
-
-       (void)strvec;
-
-       parsed_tk = ec_parsed_tk_new();
-       if (parsed_tk == NULL)
-               goto fail;
-
-       match_strvec = ec_strvec_new();
-       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 struct ec_tk_type ec_tk_empty_type = {
-       .name = "empty",
-       .parse = ec_tk_empty_parse,
-       .complete = ec_tk_default_complete,
-       .size = sizeof(struct ec_tk_empty),
-};
-
-EC_TK_TYPE_REGISTER(ec_tk_empty_type);
-
-static int ec_tk_empty_testcase(void)
-{
-       struct ec_tk *tk;
-       int ret = 0;
-
-       tk = ec_tk_new("empty", NULL);
-       if (tk == NULL) {
-               ec_log(EC_LOG_ERR, "cannot create tk\n");
-               return -1;
-       }
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, 0, "foo");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, 0);
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, 0, "foo", "bar");
-       ec_tk_free(tk);
-
-       /* never completes */
-       tk = ec_tk_new("empty", NULL);
-       if (tk == NULL) {
-               ec_log(EC_LOG_ERR, "cannot create tk\n");
-               return -1;
-       }
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
-               "", EC_TK_ENDLIST,
-               EC_TK_ENDLIST,
-               "");
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
-               "foo", EC_TK_ENDLIST,
-               EC_TK_ENDLIST,
-               "");
-       ec_tk_free(tk);
-
-       return ret;
-}
-
-static struct ec_test ec_tk_empty_test = {
-       .name = "tk_empty",
-       .test = ec_tk_empty_testcase,
-};
-
-EC_TEST_REGISTER(ec_tk_empty_test);
diff --git a/lib/ecoli_tk_empty.h b/lib/ecoli_tk_empty.h
deleted file mode 100644 (file)
index 6482c4b..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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.
- */
-
-/**
- * This token always matches an empty string vector
- */
-
-#ifndef ECOLI_TK_EMPTY_
-#define ECOLI_TK_EMPTY_
-
-struct ec_tk *ec_tk_empty(const char *id);
-
-#endif
diff --git a/lib/ecoli_tk_expr.c b/lib/ecoli_tk_expr.c
deleted file mode 100644 (file)
index 661e756..0000000
+++ /dev/null
@@ -1,598 +0,0 @@
-/*
- * 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 <stdbool.h>
-#include <stdint.h>
-#include <string.h>
-#include <assert.h>
-#include <stdarg.h>
-#include <errno.h>
-
-#include <ecoli_malloc.h>
-#include <ecoli_log.h>
-#include <ecoli_test.h>
-#include <ecoli_strvec.h>
-#include <ecoli_tk.h>
-#include <ecoli_tk_seq.h>
-#include <ecoli_tk_many.h>
-#include <ecoli_tk_or.h>
-#include <ecoli_tk_weakref.h>
-#include <ecoli_tk_expr.h>
-
-struct ec_tk_expr {
-       struct ec_tk gen;
-
-       /* the built node */
-       struct ec_tk *child;
-
-       /* the configuration nodes */
-       struct ec_tk *val_tk;
-       struct ec_tk **bin_ops;
-       unsigned int bin_ops_len;
-       struct ec_tk **pre_ops;
-       unsigned int pre_ops_len;
-       struct ec_tk **post_ops;
-       unsigned int post_ops_len;
-       struct ec_tk **open_ops;
-       struct ec_tk **close_ops;
-       unsigned int paren_len;
-};
-
-static struct ec_parsed_tk *ec_tk_expr_parse(const struct ec_tk *gen_tk,
-       const struct ec_strvec *strvec)
-{
-       struct ec_tk_expr *tk = (struct ec_tk_expr *)gen_tk;
-
-       return ec_tk_parse_tokens(tk->child, strvec);
-}
-
-static struct ec_completed_tk *ec_tk_expr_complete(const struct ec_tk *gen_tk,
-       const struct ec_strvec *strvec)
-{
-       struct ec_tk_expr *tk = (struct ec_tk_expr *)gen_tk;
-
-       return ec_tk_complete_tokens(tk->child, strvec);
-}
-
-static void ec_tk_expr_free_priv(struct ec_tk *gen_tk)
-{
-       struct ec_tk_expr *tk = (struct ec_tk_expr *)gen_tk;
-       unsigned int i;
-
-       ec_log(EC_LOG_DEBUG, "free %p %p %p\n", tk, tk->child, tk->val_tk);
-       ec_tk_free(tk->val_tk);
-
-       for (i = 0; i < tk->bin_ops_len; i++)
-               ec_tk_free(tk->bin_ops[i]);
-       ec_free(tk->bin_ops);
-       for (i = 0; i < tk->pre_ops_len; i++)
-               ec_tk_free(tk->pre_ops[i]);
-       ec_free(tk->pre_ops);
-       for (i = 0; i < tk->post_ops_len; i++)
-               ec_tk_free(tk->post_ops[i]);
-       ec_free(tk->post_ops);
-       for (i = 0; i < tk->paren_len; i++) {
-               ec_tk_free(tk->open_ops[i]);
-               ec_tk_free(tk->close_ops[i]);
-       }
-       ec_free(tk->open_ops);
-       ec_free(tk->close_ops);
-
-       ec_tk_free(tk->child);
-}
-
-static int ec_tk_expr_build(struct ec_tk *gen_tk)
-{
-       struct ec_tk_expr *tk = (struct ec_tk_expr *)gen_tk;
-       struct ec_tk *term = NULL, *expr = NULL, *next = NULL,
-               *pre_op = NULL, *post_op = NULL,
-               *post = NULL, *weak = NULL;
-       unsigned int i;
-       int ret;
-
-       if (tk->val_tk == NULL)
-               return -EINVAL;
-       if (tk->bin_ops_len == 0 && tk->pre_ops_len == 0 &&
-                       tk->post_ops_len == 0)
-               return -EINVAL;
-
-       /* create the object, we will initialize it later: this is
-        * needed because we have a circular dependency */
-       ret = -ENOMEM;
-       weak = ec_tk_new("weakref", "weak");
-       if (weak == NULL)
-               return -1;
-
-       /* prefix unary operators */
-       pre_op = ec_tk_new("or", "pre-op");
-       if (pre_op == NULL)
-               goto fail;
-       for (i = 0; i < tk->pre_ops_len; i++) {
-               if (ec_tk_or_add(pre_op, ec_tk_clone(tk->pre_ops[i])) < 0)
-                       goto fail;
-       }
-
-       /* suffix unary operators */
-       post_op = ec_tk_new("or", "post-op");
-       if (post_op == NULL)
-               goto fail;
-       for (i = 0; i < tk->post_ops_len; i++) {
-               if (ec_tk_or_add(post_op, ec_tk_clone(tk->post_ops[i])) < 0)
-                       goto fail;
-       }
-
-       post = ec_tk_new("or", "post");
-       if (post == NULL)
-               goto fail;
-       if (ec_tk_or_add(post, ec_tk_clone(tk->val_tk)) < 0)
-               goto fail;
-       if (ec_tk_or_add(post,
-               EC_TK_SEQ(NULL,
-                       ec_tk_clone(pre_op),
-                       ec_tk_clone(weak))) < 0)
-               goto fail;
-       for (i = 0; i < tk->paren_len; i++) {
-               if (ec_tk_or_add(post, EC_TK_SEQ(NULL,
-                                       ec_tk_clone(tk->open_ops[i]),
-                                       ec_tk_clone(weak),
-                                       ec_tk_clone(tk->close_ops[i]))) < 0)
-                       goto fail;
-       }
-       term = EC_TK_SEQ("term",
-               ec_tk_clone(post),
-               ec_tk_many(NULL, ec_tk_clone(post_op), 0, 0)
-       );
-       if (term == NULL)
-               goto fail;
-
-       for (i = 0; i < tk->bin_ops_len; i++) {
-               next = EC_TK_SEQ("next",
-                       ec_tk_clone(term),
-                       ec_tk_many(NULL,
-                               EC_TK_SEQ(NULL,
-                                       ec_tk_clone(tk->bin_ops[i]),
-                                       ec_tk_clone(term)
-                               ),
-                               0, 0
-                       )
-               );
-               ec_tk_free(term);
-               term = next;
-               if (term == NULL)
-                       goto fail;
-       }
-       expr = term;
-       term = NULL;
-
-       /* free the initial references */
-       ec_tk_free(pre_op);
-       pre_op = NULL;
-       ec_tk_free(post_op);
-       post_op = NULL;
-       ec_tk_free(post);
-       post = NULL;
-
-       /* no need to clone here, the node is not consumed */
-       if (ec_tk_weakref_set(weak, expr) < 0)
-               goto fail;
-       ec_tk_free(weak);
-       weak = NULL;
-
-       tk->child = expr;
-
-       return 0;
-
-fail:
-       ec_tk_free(term);
-       ec_tk_free(expr);
-       ec_tk_free(pre_op);
-       ec_tk_free(post_op);
-       ec_tk_free(post);
-       ec_tk_free(weak);
-
-       return ret;
-}
-
-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,
-       .size = sizeof(struct ec_tk_expr),
-       .free_priv = ec_tk_expr_free_priv,
-};
-
-EC_TK_TYPE_REGISTER(ec_tk_expr_type);
-
-int ec_tk_expr_set_val_tk(struct ec_tk *gen_tk, struct ec_tk *val_tk)
-{
-       struct ec_tk_expr *tk = (struct ec_tk_expr *)gen_tk;
-       int ret;
-
-       ret = -EINVAL;
-       if (val_tk == NULL)
-               goto fail;
-       ret = -EPERM;
-       if (gen_tk->flags & EC_TK_F_BUILT)
-               goto fail;
-       ret = -EEXIST;
-       if (tk->val_tk != NULL)
-               goto fail;
-
-       tk->val_tk = val_tk;
-       gen_tk->flags &= ~EC_TK_F_BUILT;
-
-       return 0;
-
-fail:
-       ec_tk_free(val_tk);
-       return ret;
-}
-
-/* add a binary operator */
-int ec_tk_expr_add_bin_op(struct ec_tk *gen_tk, struct ec_tk *op)
-{
-       struct ec_tk_expr *tk = (struct ec_tk_expr *)gen_tk;
-       struct ec_tk **bin_ops;
-       int ret;
-
-       // XXX check tk type
-
-       ret = -EINVAL;
-       if (tk == NULL || op == NULL)
-               goto fail;
-       ret = -EPERM;
-       if (gen_tk->flags & EC_TK_F_BUILT)
-               goto fail;
-
-       ret = -ENOMEM;
-       bin_ops = ec_realloc(tk->bin_ops,
-               (tk->bin_ops_len + 1) * sizeof(*tk->bin_ops));
-       if (bin_ops == NULL)
-               goto fail;;
-
-       tk->bin_ops = bin_ops;
-       bin_ops[tk->bin_ops_len] = op;
-       tk->bin_ops_len++;
-       gen_tk->flags &= ~EC_TK_F_BUILT;
-
-       return 0;
-
-fail:
-       ec_tk_free(op);
-       return ret;
-}
-
-/* add a unary pre-operator */
-int ec_tk_expr_add_pre_op(struct ec_tk *gen_tk, struct ec_tk *op)
-{
-       struct ec_tk_expr *tk = (struct ec_tk_expr *)gen_tk;
-       struct ec_tk **pre_ops;
-       int ret;
-
-       // XXX check tk type
-
-       ret = -EINVAL;
-       if (tk == NULL || op == NULL)
-               goto fail;
-       ret = -EPERM;
-       if (gen_tk->flags & EC_TK_F_BUILT)
-               goto fail;
-
-       ret = -ENOMEM;
-       pre_ops = ec_realloc(tk->pre_ops,
-               (tk->pre_ops_len + 1) * sizeof(*tk->pre_ops));
-       if (pre_ops == NULL)
-               goto fail;
-
-       tk->pre_ops = pre_ops;
-       pre_ops[tk->pre_ops_len] = op;
-       tk->pre_ops_len++;
-       gen_tk->flags &= ~EC_TK_F_BUILT;
-
-       return 0;
-
-fail:
-       ec_tk_free(op);
-       return ret;
-}
-
-/* add a unary post-operator */
-int ec_tk_expr_add_post_op(struct ec_tk *gen_tk, struct ec_tk *op)
-{
-       struct ec_tk_expr *tk = (struct ec_tk_expr *)gen_tk;
-       struct ec_tk **post_ops;
-       int ret;
-
-       // XXX check tk type
-
-       ret = -EINVAL;
-       if (tk == NULL || op == NULL)
-               goto fail;
-       ret = -EPERM;
-       if (gen_tk->flags & EC_TK_F_BUILT)
-               goto fail;
-
-       ret = -ENOMEM;
-       post_ops = ec_realloc(tk->post_ops,
-               (tk->post_ops_len + 1) * sizeof(*tk->post_ops));
-       if (post_ops == NULL)
-               goto fail;
-
-       tk->post_ops = post_ops;
-       post_ops[tk->post_ops_len] = op;
-       tk->post_ops_len++;
-       gen_tk->flags &= ~EC_TK_F_BUILT;
-
-       return 0;
-
-fail:
-       ec_tk_free(op);
-       return ret;
-}
-
-/* add parenthesis symbols */
-int ec_tk_expr_add_parenthesis(struct ec_tk *gen_tk,
-       struct ec_tk *open, struct ec_tk *close)
-{
-       struct ec_tk_expr *tk = (struct ec_tk_expr *)gen_tk;
-       struct ec_tk **open_ops, **close_ops;
-       int ret;
-
-       // XXX check tk type
-
-       ret = -EINVAL;
-       if (tk == NULL || open == NULL || close == NULL)
-               goto fail;
-       ret = -EPERM;
-       if (gen_tk->flags & EC_TK_F_BUILT)
-               goto fail;;
-
-       ret = -ENOMEM;
-       open_ops = ec_realloc(tk->open_ops,
-               (tk->paren_len + 1) * sizeof(*tk->open_ops));
-       if (open_ops == NULL)
-               goto fail;
-       close_ops = ec_realloc(tk->close_ops,
-               (tk->paren_len + 1) * sizeof(*tk->close_ops));
-       if (close_ops == NULL)
-               goto fail;
-
-       tk->open_ops = open_ops;
-       tk->close_ops = close_ops;
-       open_ops[tk->paren_len] = open;
-       close_ops[tk->paren_len] = close;
-       tk->paren_len++;
-       gen_tk->flags &= ~EC_TK_F_BUILT;
-
-       return 0;
-
-fail:
-       ec_tk_free(open);
-       ec_tk_free(close);
-       return ret;
-}
-
-enum expr_tk_type {
-       NONE,
-       VAL,
-       BIN_OP,
-       PRE_OP,
-       POST_OP,
-       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)
-{
-       struct ec_tk_expr *expr_tk = (struct ec_tk_expr *)expr_gen_tk;
-       size_t i;
-
-       if (check_tk == expr_tk->val_tk)
-               return VAL;
-
-       for (i = 0; i < expr_tk->bin_ops_len; i++) {
-               if (check_tk == expr_tk->bin_ops[i])
-                       return BIN_OP;
-       }
-       for (i = 0; i < expr_tk->pre_ops_len; i++) {
-               if (check_tk == expr_tk->pre_ops[i])
-                       return PRE_OP;
-       }
-       for (i = 0; i < expr_tk->post_ops_len; i++) {
-               if (check_tk == expr_tk->post_ops[i])
-                       return POST_OP;
-       }
-
-       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 result {
-       bool has_val;
-       void *val;
-       const struct ec_parsed_tk *op;
-       enum expr_tk_type op_type;
-};
-
-/* merge x and y results in x */
-static int merge_results(void *userctx,
-       const struct ec_tk_expr_eval_ops *ops,
-       struct result *x, const struct result *y)
-{
-       int ret;
-
-       if (y->has_val == 0 && y->op == NULL)
-               return 0;
-       if (x->has_val == 0 && x->op == NULL) {
-               *x = *y;
-               return 0;
-       }
-
-       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;
-
-               return 0;
-       }
-
-       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;
-               }
-       }
-
-       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;
-
-               return 0;
-       }
-
-       assert(true); /* we should not get here */
-       return -EINVAL;
-}
-
-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 = ops->eval_var(&result->val, userctx, parsed_tk);
-               if (ret < 0)
-                       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) {
-
-               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)
-                       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;
-}
-
-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 result result;
-       int ret;
-
-       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;
-
-       if (!ec_parsed_tk_matches(parsed))
-               return -EINVAL;
-
-       ec_parsed_tk_dump(stdout, parsed); //XXX
-       ret = eval_expression(&result, userctx, ops, tk, parsed);
-       if (ret < 0)
-               return ret;
-
-       assert(result.has_val);
-       assert(result.op == NULL);
-       *user_result = result.val;
-
-       return 0;
-}
-
-/* the test case is in a separate file ecoli_tk_expr_test.c */
diff --git a/lib/ecoli_tk_expr.h b/lib/ecoli_tk_expr.h
deleted file mode 100644 (file)
index d2efc24..0000000
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * 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_EXPR_
-#define ECOLI_TK_EXPR_
-
-#include <ecoli_tk.h>
-
-/* XXX remove the _new for all other tokens */
-
-/**
- * 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);
-
-/**
- * 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 int (*ec_tk_expr_eval_post_op_t)(
-       void **result, void *userctx,
-       void *operand,
-       const struct ec_parsed_tk *operator);
-
-typedef int (*ec_tk_expr_eval_bin_op_t)(
-       void **result, void *userctx,
-       void *operand1,
-       const struct ec_parsed_tk *operator,
-       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 void (*ec_tk_expr_eval_free_t)(
-       void *result, void *userctx);
-
-
-struct ec_tk *ec_tk_expr(const char *id);
-int ec_tk_expr_set_val_tk(struct ec_tk *gen_tk, struct ec_tk *val_tk);
-int ec_tk_expr_add_bin_op(struct ec_tk *gen_tk, struct ec_tk *op);
-int ec_tk_expr_add_pre_op(struct ec_tk *gen_tk, struct ec_tk *op);
-int ec_tk_expr_add_post_op(struct ec_tk *gen_tk, struct ec_tk *op);
-int ec_tk_expr_add_parenthesis(struct ec_tk *gen_tk,
-       struct ec_tk *open, struct ec_tk *close);
-
-struct ec_tk_expr_eval_ops {
-       ec_tk_expr_eval_var_t eval_var;
-       ec_tk_expr_eval_pre_op_t eval_pre_op;
-       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;
-};
-
-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);
-
-#endif
diff --git a/lib/ecoli_tk_expr_test.c b/lib/ecoli_tk_expr_test.c
deleted file mode 100644 (file)
index 4a00cdf..0000000
+++ /dev/null
@@ -1,304 +0,0 @@
-/*
- * 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 <errno.h>
-#include <limits.h>
-#include <stdint.h>
-#include <assert.h>
-
-#include <ecoli_malloc.h>
-#include <ecoli_strvec.h>
-#include <ecoli_test.h>
-#include <ecoli_tk_int.h>
-#include <ecoli_tk_str.h>
-#include <ecoli_tk_re_lex.h>
-#include <ecoli_tk_expr.h>
-
-struct my_eval_result {
-       int val;
-};
-
-static int
-ec_tk_expr_test_eval_var(void **result, void *userctx,
-       const struct ec_parsed_tk *var)
-{
-       const struct ec_strvec *vec;
-       struct my_eval_result *eval;
-
-       (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;
-
-       eval = ec_malloc(sizeof(*eval));
-       if (eval == NULL)
-               return -ENOMEM;
-
-       eval->val = atoi(ec_strvec_val(vec, 0)); // XXX use strtol
-       printf("eval var %d\n", eval->val);
-       *result = eval;
-
-       return 0;
-}
-
-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 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)
-               return -EINVAL;
-
-       if (!strcmp(ec_strvec_val(vec, 0), "!"))
-               eval->val = !eval->val;
-       else
-               return -EINVAL;
-
-       printf("eval pre_op %d\n", eval->val);
-       *result = eval;
-
-       return 0;
-}
-
-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 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)
-               return -EINVAL;
-
-       if (!strcmp(ec_strvec_val(vec, 0), "^"))
-               eval->val = eval->val * eval->val;
-       else
-               return -EINVAL;
-
-       printf("eval post_op %d\n", eval->val);
-       *result = eval;
-
-       return 0;
-}
-
-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 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)
-               return -EINVAL;
-
-       if (!strcmp(ec_strvec_val(vec, 0), "+"))
-               eval1->val = eval1->val + eval2->val;
-       else if (!strcmp(ec_strvec_val(vec, 0), "*"))
-               eval1->val = eval1->val * eval2->val;
-       else
-               return -EINVAL;
-
-       printf("eval bin_op %d\n", eval1->val);
-       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;
-}
-
-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 = {
-       .eval_var = ec_tk_expr_test_eval_var,
-       .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_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;
-
-       ret = ec_tk_expr_eval(&result, expr_tk, p, &test_ops, NULL);
-       ec_parsed_tk_free(p);
-       if (ret < 0)
-               return -1;
-
-       /* the parsed value is an integer */
-       eval = result;
-       assert(eval != NULL);
-
-       printf("result: %d (expected %d)\n", eval->val, val);
-       if (eval->val == val)
-               ret = 0;
-       else
-               ret = -1;
-
-       ec_free(eval);
-
-       return ret;
-}
-
-static int ec_tk_expr_testcase(void)
-{
-       struct ec_tk *tk = NULL, *lex_tk = NULL;
-       int ret = 0;
-
-       tk = ec_tk_new("expr", "my_expr");
-       if (tk == NULL)
-               return -1;
-
-       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, "!"));  /* 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, 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");
-       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", "^");
-
-       /* 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 */
-
-       /* 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, "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, "");
-       ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, -1, "()");
-       ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, -1, "(");
-       ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, -1, ")");
-       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+(1*1");
-       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, "1+1", 2);
-       ret |= ec_tk_expr_test_eval(lex_tk, tk, "1+1*2", 4);
-       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);
-
-       return ret;
-
-fail:
-       ec_tk_free(lex_tk);
-       ec_tk_free(tk);
-       return -1;
-}
-
-static struct ec_test ec_tk_expr_test = {
-       .name = "tk_expr",
-       .test = ec_tk_expr_testcase,
-};
-
-EC_TEST_REGISTER(ec_tk_expr_test);
diff --git a/lib/ecoli_tk_int.c b/lib/ecoli_tk_int.c
deleted file mode 100644 (file)
index 905f516..0000000
+++ /dev/null
@@ -1,240 +0,0 @@
-/*
- * 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 <string.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <stdbool.h>
-#include <limits.h>
-#include <ctype.h>
-#include <errno.h>
-
-#include <ecoli_log.h>
-#include <ecoli_malloc.h>
-#include <ecoli_strvec.h>
-#include <ecoli_tk.h>
-#include <ecoli_tk_int.h>
-#include <ecoli_test.h>
-
-struct ec_tk_int {
-       struct ec_tk gen;
-       bool check_min;
-       long long int min;
-       bool check_max;
-       long long int max;
-       unsigned int base;
-};
-
-static int parse_llint(struct ec_tk_int *tk, const char *str,
-       long long *val)
-{
-       char *endptr;
-
-       errno = 0;
-       *val = strtoll(str, &endptr, tk->base);
-
-       /* out of range */
-       if ((errno == ERANGE && (*val == LLONG_MAX || *val == LLONG_MIN)) ||
-                       (errno != 0 && *val == 0))
-               return -1;
-
-       if (tk->check_min && *val < tk->min)
-               return -1;
-
-       if (tk->check_max && *val > tk->max)
-               return -1;
-
-       if (*endptr != 0)
-               return -1;
-
-       return 0;
-}
-
-static struct ec_parsed_tk *ec_tk_int_parse(const struct ec_tk *gen_tk,
-       const struct ec_strvec *strvec)
-{
-       struct ec_tk_int *tk = (struct ec_tk_int *)gen_tk;
-       struct ec_parsed_tk *parsed_tk;
-       struct ec_strvec *match_strvec;
-       const char *str;
-       long long val;
-
-       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 (parse_llint(tk, str, &val) < 0)
-               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 struct ec_tk_type ec_tk_int_type = {
-       .name = "int",
-       .parse = ec_tk_int_parse,
-       .complete = ec_tk_default_complete,
-       .size = sizeof(struct ec_tk_int),
-};
-
-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(&ec_tk_int_type, id);
-       if (gen_tk == NULL)
-               return NULL;
-       tk = (struct ec_tk_int *)gen_tk;
-
-       tk->check_min = true;
-       tk->min = min;
-       tk->check_max = true;
-       tk->max = max;
-       tk->base = base;
-
-       return &tk->gen;
-}
-
-long long ec_tk_int_getval(struct ec_tk *gen_tk, const char *str)
-{
-       struct ec_tk_int *tk = (struct ec_tk_int *)gen_tk;
-       long long val = 0;
-
-       // XXX check type here
-       // if gen_tk->type != int fail
-
-       parse_llint(tk, str, &val);
-
-       return val;
-}
-
-static int ec_tk_int_testcase(void)
-{
-       struct ec_parsed_tk *p;
-       struct ec_tk *tk;
-       const char *s;
-       int ret = 0;
-
-       tk = ec_tk_int(NULL, 0, 256, 0);
-       if (tk == NULL) {
-               ec_log(EC_LOG_ERR, "cannot create tk\n");
-               return -1;
-       }
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "0");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "256", "foo");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "0x100");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, " 1");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "-1");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "0x101");
-
-       p = ec_tk_parse(tk, "0");
-       s = ec_strvec_val(ec_parsed_tk_strvec(p), 0);
-       EC_TEST_ASSERT(s != NULL && ec_tk_int_getval(tk, s) == 0);
-       ec_parsed_tk_free(p);
-
-       p = ec_tk_parse(tk, "10");
-       s = ec_strvec_val(ec_parsed_tk_strvec(p), 0);
-       EC_TEST_ASSERT(s != NULL && ec_tk_int_getval(tk, s) == 10);
-       ec_parsed_tk_free(p);
-       ec_tk_free(tk);
-
-       tk = ec_tk_int(NULL, -1, LLONG_MAX, 16);
-       if (tk == NULL) {
-               ec_log(EC_LOG_ERR, "cannot create tk\n");
-               return -1;
-       }
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "0");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "-1");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "7fffffffffffffff");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "0x7fffffffffffffff");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "-2");
-
-       p = ec_tk_parse(tk, "10");
-       s = ec_strvec_val(ec_parsed_tk_strvec(p), 0);
-       EC_TEST_ASSERT(s != NULL && ec_tk_int_getval(tk, s) == 16);
-       ec_parsed_tk_free(p);
-       ec_tk_free(tk);
-
-       tk = ec_tk_int(NULL, LLONG_MIN, 0, 10);
-       if (tk == NULL) {
-               ec_log(EC_LOG_ERR, "cannot create tk\n");
-               return -1;
-       }
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "0");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "-1");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "-9223372036854775808");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "0x0");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "1");
-       ec_tk_free(tk);
-
-       /* test completion */
-       tk = ec_tk_int(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,
-               "", EC_TK_ENDLIST,
-               EC_TK_ENDLIST,
-               "");
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
-               "x", EC_TK_ENDLIST,
-               EC_TK_ENDLIST,
-               "");
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
-               "1", EC_TK_ENDLIST,
-               EC_TK_ENDLIST,
-               "");
-       ec_tk_free(tk);
-
-       return ret;
-}
-
-static struct ec_test ec_tk_int_test = {
-       .name = "tk_int",
-       .test = ec_tk_int_testcase,
-};
-
-EC_TEST_REGISTER(ec_tk_int_test);
diff --git a/lib/ecoli_tk_int.h b/lib/ecoli_tk_int.h
deleted file mode 100644 (file)
index 156ebf1..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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_INT_
-#define ECOLI_TK_INT_
-
-#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);
-
-#endif
diff --git a/lib/ecoli_tk_many.c b/lib/ecoli_tk_many.c
deleted file mode 100644 (file)
index 235f22a..0000000
+++ /dev/null
@@ -1,257 +0,0 @@
-/*
- * 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 <assert.h>
-#include <stdarg.h>
-
-#include <ecoli_malloc.h>
-#include <ecoli_log.h>
-#include <ecoli_test.h>
-#include <ecoli_strvec.h>
-#include <ecoli_tk.h>
-#include <ecoli_tk_str.h>
-#include <ecoli_tk_option.h>
-#include <ecoli_tk_many.h>
-
-struct ec_tk_many {
-       struct ec_tk gen;
-       unsigned int min;
-       unsigned int max;
-       struct ec_tk *child;
-};
-
-static struct ec_parsed_tk *ec_tk_many_parse(const struct ec_tk *gen_tk,
-       const struct ec_strvec *strvec)
-{
-       struct ec_tk_many *tk = (struct ec_tk_many *)gen_tk;
-       struct ec_parsed_tk *parsed_tk, *child_parsed_tk;
-       struct ec_strvec *match_strvec;
-       struct ec_strvec *childvec = NULL;
-       size_t off = 0, len, count;
-
-       parsed_tk = ec_parsed_tk_new();
-       if (parsed_tk == NULL)
-               goto fail;
-
-       for (count = 0; tk->max == 0 || count < tk->max; count++) {
-               childvec = ec_strvec_ndup(strvec, off,
-                       ec_strvec_len(strvec) - off);
-               if (childvec == NULL)
-                       goto fail;
-
-               child_parsed_tk = ec_tk_parse_tokens(tk->child, childvec);
-               if (child_parsed_tk == NULL)
-                       goto fail;
-
-               ec_strvec_free(childvec);
-               childvec = NULL;
-
-               if (!ec_parsed_tk_matches(child_parsed_tk)) {
-                       ec_parsed_tk_free(child_parsed_tk);
-                       break;
-               }
-
-               ec_parsed_tk_add_child(parsed_tk, child_parsed_tk);
-
-               /* it matches "no token", no need to continue */
-               len = ec_parsed_tk_len(child_parsed_tk);
-               if (len == 0) {
-                       ec_parsed_tk_free(child_parsed_tk);
-                       break;
-               }
-
-               off += len;
-       }
-
-       if (count < tk->min) {
-               ec_parsed_tk_free_children(parsed_tk);
-               return parsed_tk;
-       }
-
-       match_strvec = ec_strvec_ndup(strvec, 0, off);
-       if (match_strvec == NULL)
-               goto fail;
-
-       ec_parsed_tk_set_match(parsed_tk, gen_tk, match_strvec);
-
-       return parsed_tk;
-
-fail:
-       ec_strvec_free(childvec);
-       ec_parsed_tk_free(parsed_tk);
-       return NULL;
-}
-
-#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)
-{
-       struct ec_tk_many *tk = (struct ec_tk_many *)gen_tk;
-       struct ec_completed_tk *completed_tk, *child_completed_tk;
-       struct ec_strvec *childvec;
-       struct ec_parsed_tk *parsed_tk;
-       size_t len = 0;
-       unsigned int i;
-
-       completed_tk = ec_completed_tk_new();
-       if (completed_tk == NULL)
-               return NULL;
-
-       if (tk->len == 0)
-               return completed_tk;
-
-       for (i = 0; i < tk->len; i++) {
-               childvec = ec_strvec_ndup(strvec, len,
-                       ec_strvec_len(strvec) - len);
-               if (childvec == NULL)
-                       goto fail; // XXX fail ?
-
-               child_completed_tk = ec_tk_complete_tokens(tk->table[i],
-                       childvec);
-               if (child_completed_tk == NULL)
-                       goto fail;
-
-               ec_completed_tk_merge(completed_tk, child_completed_tk);
-
-               parsed_tk = ec_tk_parse_tokens(tk->table[i], childvec);
-               if (parsed_tk == NULL)
-                       goto fail;
-
-               ec_strvec_free(childvec);
-               childvec = NULL;
-
-               if (!ec_parsed_tk_matches(parsed_tk)) {
-                       ec_parsed_tk_free(parsed_tk);
-                       break;
-               }
-
-               len += ec_strvec_len(parsed_tk->strvec);
-               ec_parsed_tk_free(parsed_tk);
-       }
-
-       return completed_tk;
-
-fail:
-       ec_strvec_free(childvec);
-       ec_completed_tk_free(completed_tk);
-       return NULL;
-}
-#endif
-
-static void ec_tk_many_free_priv(struct ec_tk *gen_tk)
-{
-       struct ec_tk_many *tk = (struct ec_tk_many *)gen_tk;
-
-       ec_tk_free(tk->child);
-}
-
-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,
-       .size = sizeof(struct ec_tk_many),
-       .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)
-{
-       struct ec_tk_many *tk = NULL;
-
-       if (child == NULL)
-               return NULL;
-
-       tk = (struct ec_tk_many *)__ec_tk_new(&ec_tk_many_type, id);
-       if (tk == NULL) {
-               ec_tk_free(child);
-               return NULL;
-       }
-
-       tk->child = child;
-       tk->min = min;
-       tk->max = max;
-
-       return &tk->gen;
-}
-
-static int ec_tk_many_testcase(void)
-{
-       struct ec_tk *tk;
-       int ret = 0;
-
-       tk = ec_tk_many(NULL, ec_tk_str(NULL, "foo"), 0, 0);
-       if (tk == NULL) {
-               ec_log(EC_LOG_ERR, "cannot create tk\n");
-               return -1;
-       }
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, 0, "bar");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo", "bar");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, 2, "foo", "foo", "bar");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, 0);
-       ec_tk_free(tk);
-
-       tk = ec_tk_many(NULL, ec_tk_str(NULL, "foo"), 1, 0);
-       if (tk == NULL) {
-               ec_log(EC_LOG_ERR, "cannot create tk\n");
-               return -1;
-       }
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "bar");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo", "bar");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, 2, "foo", "foo", "bar");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1);
-       ec_tk_free(tk);
-
-       tk = ec_tk_many(NULL, ec_tk_str(NULL, "foo"), 1, 2);
-       if (tk == NULL) {
-               ec_log(EC_LOG_ERR, "cannot create tk\n");
-               return -1;
-       }
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "bar");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo", "bar");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, 2, "foo", "foo", "bar");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, 2, "foo", "foo", "foo");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1);
-       ec_tk_free(tk);
-
-       /* test completion */
-       /* XXX */
-
-       return ret;
-}
-
-static struct ec_test ec_tk_many_test = {
-       .name = "many",
-       .test = ec_tk_many_testcase,
-};
-
-EC_TEST_REGISTER(ec_tk_many_test);
diff --git a/lib/ecoli_tk_many.h b/lib/ecoli_tk_many.h
deleted file mode 100644 (file)
index 107e4c7..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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_MANY_
-#define ECOLI_TK_MANY_
-
-/*
- * if min == max == 0, there is no limit
- */
-struct ec_tk *ec_tk_many(const char *id, struct ec_tk *child,
-       unsigned int min, unsigned int max);
-
-#endif
diff --git a/lib/ecoli_tk_option.c b/lib/ecoli_tk_option.c
deleted file mode 100644 (file)
index 080ca37..0000000
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * 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 <assert.h>
-#include <stdarg.h>
-
-#include <ecoli_malloc.h>
-#include <ecoli_log.h>
-#include <ecoli_strvec.h>
-#include <ecoli_tk.h>
-#include <ecoli_tk_option.h>
-#include <ecoli_tk_str.h>
-#include <ecoli_test.h>
-
-struct ec_tk_option {
-       struct ec_tk gen;
-       struct ec_tk *child;
-};
-
-static struct ec_parsed_tk *ec_tk_option_parse(const struct ec_tk *gen_tk,
-       const struct ec_strvec *strvec)
-{
-       struct ec_tk_option *tk = (struct ec_tk_option *)gen_tk;
-       struct ec_parsed_tk *parsed_tk = NULL, *child_parsed_tk;
-       struct ec_strvec *match_strvec;
-
-       parsed_tk = ec_parsed_tk_new();
-       if (parsed_tk == NULL)
-               goto fail;
-
-       child_parsed_tk = ec_tk_parse_tokens(tk->child, strvec);
-       if (child_parsed_tk == NULL)
-               goto fail;
-
-       if (ec_parsed_tk_matches(child_parsed_tk)) {
-               ec_parsed_tk_add_child(parsed_tk, child_parsed_tk);
-               match_strvec = ec_strvec_dup(child_parsed_tk->strvec);
-       } else {
-               ec_parsed_tk_free(child_parsed_tk);
-               match_strvec = ec_strvec_new();
-       }
-
-       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 struct ec_completed_tk *ec_tk_option_complete(const struct ec_tk *gen_tk,
-       const struct ec_strvec *strvec)
-{
-       struct ec_tk_option *tk = (struct ec_tk_option *)gen_tk;
-
-       return ec_tk_complete_tokens(tk->child, strvec);
-}
-
-static void ec_tk_option_free_priv(struct ec_tk *gen_tk)
-{
-       struct ec_tk_option *tk = (struct ec_tk_option *)gen_tk;
-
-       ec_tk_free(tk->child);
-}
-
-static struct ec_tk_type ec_tk_option_type = {
-       .name = "option",
-       .parse = ec_tk_option_parse,
-       .complete = ec_tk_option_complete,
-       .size = sizeof(struct ec_tk_option),
-       .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;
-
-       gen_tk = __ec_tk_new(&ec_tk_option_type, id);
-       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;
-}
-
-static int ec_tk_option_testcase(void)
-{
-       struct ec_tk *tk;
-       int ret = 0;
-
-       tk = ec_tk_option_new(NULL, ec_tk_str(NULL, "foo"));
-       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, 0, "bar");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, 0);
-       ec_tk_free(tk);
-
-       /* test completion */
-       tk = ec_tk_option_new(NULL, ec_tk_str(NULL, "foo"));
-       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", EC_TK_ENDLIST,
-               "foo");
-       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,
-               EC_TK_ENDLIST,
-               "");
-       ec_tk_free(tk);
-
-       return ret;
-}
-
-static struct ec_test ec_tk_option_test = {
-       .name = "tk_option",
-       .test = ec_tk_option_testcase,
-};
-
-EC_TEST_REGISTER(ec_tk_option_test);
diff --git a/lib/ecoli_tk_option.h b/lib/ecoli_tk_option.h
deleted file mode 100644 (file)
index 338749d..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * 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_OPTION_
-#define ECOLI_TK_OPTION_
-
-#include <ecoli_tk.h>
-
-struct ec_tk *ec_tk_option_new(const char *id, struct ec_tk *tk);
-
-#endif
diff --git a/lib/ecoli_tk_or.c b/lib/ecoli_tk_or.c
deleted file mode 100644 (file)
index 0debd9c..0000000
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
- * 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 <assert.h>
-#include <stdarg.h>
-#include <errno.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>
-
-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 = NULL;
-       struct ec_strvec *match_strvec;
-       unsigned int i;
-
-       parsed_tk = ec_parsed_tk_new();
-       if (parsed_tk == NULL)
-               goto fail;
-
-       for (i = 0; i < tk->len; i++) {
-               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;
-       }
-
-       /* 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);
-
-       return parsed_tk;
-
- fail:
-       ec_parsed_tk_free(child_parsed_tk);
-       ec_parsed_tk_free(parsed_tk);
-       return NULL;
-}
-
-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, *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_tokens(tk->table[n],
-                       strvec);
-
-               if (child_completed_tk == NULL) // XXX fail instead?
-                       continue;
-
-               ec_completed_tk_merge(completed_tk, child_completed_tk);
-       }
-
-       return completed_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;
-
-       for (i = 0; i < tk->len; i++)
-               ec_tk_free(tk->table[i]);
-       ec_free(tk->table);
-}
-
-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;
-       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_or_type = {
-       .name = "or",
-       .parse = ec_tk_or_parse,
-       .complete = ec_tk_or_complete,
-       .size = sizeof(struct ec_tk_or),
-       .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;
-       struct ec_tk *child;
-       va_list ap;
-       int fail = 0;
-
-       va_start(ap, id);
-
-       gen_tk = __ec_tk_new(&ec_tk_or_type, id);
-       tk = (struct ec_tk_or *)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_or_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_or_testcase(void)
-{
-       struct ec_tk *tk;
-       int ret = 0;
-
-       tk = EC_TK_OR(NULL,
-               ec_tk_str(NULL, "foo"),
-               ec_tk_str(NULL, "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, "bar");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo", "bar");
-       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, "toto");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "");
-       ec_tk_free(tk);
-
-       /* test completion */
-       tk = EC_TK_OR(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,
-               "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 ec_tk_or_test = {
-       .name = "tk_or",
-       .test = ec_tk_or_testcase,
-};
-
-EC_TEST_REGISTER(ec_tk_or_test);
diff --git a/lib/ecoli_tk_or.h b/lib/ecoli_tk_or.h
deleted file mode 100644 (file)
index c34b808..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * 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_OR_
-#define ECOLI_TK_OR_
-
-#include <ecoli_tk.h>
-
-#define EC_TK_OR(args...) __ec_tk_or(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_OR() or
- * ec_tk_or() + ec_tk_or_add() */
-struct ec_tk *__ec_tk_or(const char *id, ...);
-
-struct ec_tk *ec_tk_or(const char *id);
-
-/* child is consumed */
-int ec_tk_or_add(struct ec_tk *tk, struct ec_tk *child);
-
-
-#endif
diff --git a/lib/ecoli_tk_re.c b/lib/ecoli_tk_re.c
deleted file mode 100644 (file)
index fc5c184..0000000
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * 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,
-       .size = sizeof(struct ec_tk_re),
-       .free_priv = ec_tk_re_free_priv,
-};
-
-EC_TK_TYPE_REGISTER(ec_tk_re_type);
-
-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_new(&ec_tk_re_type, 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
deleted file mode 100644 (file)
index dfd4c1a..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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
diff --git a/lib/ecoli_tk_re_lex.c b/lib/ecoli_tk_re_lex.c
deleted file mode 100644 (file)
index 984ec3f..0000000
+++ /dev/null
@@ -1,273 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include <string.h>
-#include <regex.h>
-#include <errno.h>
-
-#include <ecoli_malloc.h>
-#include <ecoli_log.h>
-#include <ecoli_test.h>
-#include <ecoli_strvec.h>
-#include <ecoli_tk.h>
-#include <ecoli_tk_many.h>
-#include <ecoli_tk_or.h>
-#include <ecoli_tk_str.h>
-#include <ecoli_tk_int.h>
-#include <ecoli_tk_re_lex.h>
-
-struct regexp_pattern {
-       char *pattern;
-       regex_t r;
-       bool keep;
-};
-
-struct ec_tk_re_lex {
-       struct ec_tk gen;
-       struct ec_tk *child;
-       struct regexp_pattern *table;
-       size_t len;
-};
-
-static struct ec_strvec *
-tokenize(struct regexp_pattern *table, size_t table_len, const char *str)
-{
-       struct ec_strvec *strvec = NULL;
-       char *dup = NULL;
-       char c;
-       size_t len, off = 0;
-       size_t i;
-       int ret;
-       regmatch_t pos;
-
-       dup = ec_strdup(str);
-       if (dup == NULL)
-               goto fail;
-
-       strvec = ec_strvec_new();
-       if (strvec == NULL)
-               goto fail;
-
-       len = strlen(dup);
-       while (off < len) {
-               for (i = 0; i < table_len; i++) {
-                       ret = regexec(&table[i].r, &dup[off], 1, &pos, 0);
-                       if (ret != 0)
-                               continue;
-                       if (pos.rm_so != 0 || pos.rm_eo == 0) {
-                               ret = -1;
-                               continue;
-                       }
-
-                       if (table[i].keep == 0)
-                               break;
-
-                       c = dup[pos.rm_eo + off];
-                       dup[pos.rm_eo + off] = '\0';
-                       ec_log(EC_LOG_DEBUG, "re_lex match <%s>\n", &dup[off]);
-                       if (ec_strvec_add(strvec, &dup[off]) < 0)
-                               goto fail;
-
-                       dup[pos.rm_eo + off] = c;
-                       break;
-               }
-
-               if (ret != 0)
-                       goto fail;
-
-               off += pos.rm_eo;
-       }
-
-       ec_free(dup);
-       return strvec;
-
-fail:
-       ec_free(dup);
-       ec_strvec_free(strvec);
-       return NULL;
-}
-
-static struct ec_parsed_tk *ec_tk_re_lex_parse(const struct ec_tk *gen_tk,
-       const struct ec_strvec *strvec)
-{
-       struct ec_tk_re_lex *tk = (struct ec_tk_re_lex *)gen_tk;
-       struct ec_strvec *new_vec = NULL, *match_strvec;
-       struct ec_parsed_tk *parsed_tk = NULL, *child_parsed_tk;
-       const char *str;
-
-       parsed_tk = ec_parsed_tk_new();
-       if (parsed_tk == NULL)
-               return NULL;
-
-       if (ec_strvec_len(strvec) == 0)
-               return parsed_tk;
-
-       str = ec_strvec_val(strvec, 0);
-       new_vec = tokenize(tk->table, tk->len, str);
-       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;
-
-       if (!ec_parsed_tk_matches(child_parsed_tk) ||
-                       ec_parsed_tk_len(child_parsed_tk) !=
-                               ec_strvec_len(new_vec)) {
-               ec_strvec_free(new_vec);
-               ec_parsed_tk_free(child_parsed_tk);
-               return parsed_tk;
-       }
-       ec_strvec_free(new_vec);
-       new_vec = NULL;
-
-       ec_parsed_tk_add_child(parsed_tk, child_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_strvec_free(new_vec);
-       ec_parsed_tk_free(parsed_tk);
-
-       return NULL;
-}
-
-static void ec_tk_re_lex_free_priv(struct ec_tk *gen_tk)
-{
-       struct ec_tk_re_lex *tk = (struct ec_tk_re_lex *)gen_tk;
-       unsigned int i;
-
-       for (i = 0; i < tk->len; i++) {
-               ec_free(tk->table[i].pattern);
-               regfree(&tk->table[i].r);
-       }
-
-       ec_free(tk->table);
-       ec_tk_free(tk->child);
-}
-
-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
-       .size = sizeof(struct ec_tk_re_lex),
-       .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;
-       struct regexp_pattern *table;
-       int ret;
-       char *pat_dup = NULL;
-
-       ret = -ENOMEM;
-       pat_dup = ec_strdup(pattern);
-       if (pat_dup == NULL)
-               goto fail;
-
-       ret = -ENOMEM;
-       table = ec_realloc(tk->table, sizeof(*table) * (tk->len + 1));
-       if (table == NULL)
-               goto fail;
-
-       ret = regcomp(&table[tk->len].r, pattern, REG_EXTENDED);
-       if (ret != 0) {
-               ec_log(EC_LOG_ERR,
-                       "Regular expression <%s> compilation failed: %d\n",
-                       pattern, ret);
-               if (ret == REG_ESPACE)
-                       ret = -ENOMEM;
-               else
-                       ret = -EINVAL;
-
-               goto fail;
-       }
-
-       table[tk->len].pattern = pat_dup;
-       table[tk->len].keep = keep;
-       tk->len++;
-       tk->table = table;
-
-       return 0;
-
-fail:
-       ec_free(pat_dup);
-       return ret;
-}
-
-struct ec_tk *ec_tk_re_lex(const char *id, struct ec_tk *child)
-{
-       struct ec_tk_re_lex *tk = NULL;
-
-       if (child == NULL)
-               return NULL;
-
-       tk = (struct ec_tk_re_lex *)__ec_tk_new(&ec_tk_re_lex_type, id);
-       if (tk == NULL) {
-               ec_tk_free(child);
-               return NULL;
-       }
-
-       tk->child = child;
-
-       return &tk->gen;
-}
-
-
-static int ec_tk_re_lex_testcase(void)
-{
-       struct ec_tk *tk;
-       int ret = 0;
-
-       tk = ec_tk_re_lex(NULL,
-               ec_tk_many(NULL,
-                       EC_TK_OR(NULL,
-                               ec_tk_str(NULL, "foo"),
-                               ec_tk_str(NULL, "bar"),
-                               ec_tk_int(NULL, 0, 1000, 0)
-                       ), 0, 0
-               )
-       );
-       if (tk == NULL) {
-               ec_log(EC_LOG_ERR, "cannot create tk\n");
-               return -1;
-       }
-
-       /* 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);
-       if (ret != 0) {
-               ec_log(EC_LOG_ERR, "cannot add regexp to token\n");
-               ec_tk_free(tk);
-               return -1;
-       }
-
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "  foo bar  324 bar234");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo bar324");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "foobar");
-
-       ec_tk_free(tk);
-
-       return ret;
-}
-
-static struct ec_test ec_tk_re_lex_test = {
-       .name = "tk_re_lex",
-       .test = ec_tk_re_lex_testcase,
-};
-
-EC_TEST_REGISTER(ec_tk_re_lex_test);
diff --git a/lib/ecoli_tk_re_lex.h b/lib/ecoli_tk_re_lex.h
deleted file mode 100644 (file)
index b90ab1b..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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_RE_LEX_
-#define ECOLI_TK_RE_LEX_
-
-#include <ecoli_tk.h>
-
-struct ec_tk *ec_tk_re_lex(const char *id, struct ec_tk *child);
-
-int ec_tk_re_lex_add(struct ec_tk *gen_tk, const char *pattern, int keep);
-
-#endif
diff --git a/lib/ecoli_tk_seq.c b/lib/ecoli_tk_seq.c
deleted file mode 100644 (file)
index 17673d6..0000000
+++ /dev/null
@@ -1,323 +0,0 @@
-/*
- * 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 <assert.h>
-#include <stdarg.h>
-#include <errno.h>
-
-#include <ecoli_malloc.h>
-#include <ecoli_log.h>
-#include <ecoli_test.h>
-#include <ecoli_strvec.h>
-#include <ecoli_tk.h>
-#include <ecoli_tk_str.h>
-#include <ecoli_tk_option.h>
-#include <ecoli_tk_seq.h>
-
-struct ec_tk_seq {
-       struct ec_tk gen;
-       struct ec_tk **table;
-       unsigned int len;
-};
-
-static struct ec_parsed_tk *ec_tk_seq_parse(const struct ec_tk *gen_tk,
-       const struct ec_strvec *strvec)
-{
-       struct ec_tk_seq *tk = (struct ec_tk_seq *)gen_tk;
-       struct ec_parsed_tk *parsed_tk, *child_parsed_tk;
-       struct ec_strvec *match_strvec;
-       struct ec_strvec *childvec = NULL;
-       size_t len = 0;
-       unsigned int i;
-
-       parsed_tk = ec_parsed_tk_new();
-       if (parsed_tk == NULL)
-               goto fail;
-
-       for (i = 0; i < tk->len; i++) {
-               childvec = ec_strvec_ndup(strvec, len,
-                       ec_strvec_len(strvec) - len);
-               if (childvec == NULL)
-                       goto fail;
-
-               child_parsed_tk = ec_tk_parse_tokens(tk->table[i], childvec);
-               if (child_parsed_tk == NULL)
-                       goto fail;
-
-               ec_strvec_free(childvec);
-               childvec = NULL;
-
-               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;
-               }
-
-               ec_parsed_tk_add_child(parsed_tk, child_parsed_tk);
-               len += ec_parsed_tk_len(child_parsed_tk);
-       }
-
-       match_strvec = ec_strvec_ndup(strvec, 0, len);
-       if (match_strvec == NULL)
-               goto fail;
-
-       ec_parsed_tk_set_match(parsed_tk, gen_tk, match_strvec);
-
-       return parsed_tk;
-
-fail:
-       ec_strvec_free(childvec);
-       ec_parsed_tk_free(parsed_tk);
-       return NULL;
-}
-
-static struct ec_completed_tk *ec_tk_seq_complete(const struct ec_tk *gen_tk,
-       const struct ec_strvec *strvec)
-{
-       struct ec_tk_seq *tk = (struct ec_tk_seq *)gen_tk;
-       struct ec_completed_tk *completed_tk, *child_completed_tk;
-       struct ec_strvec *childvec = NULL;
-       struct ec_parsed_tk *parsed_tk;
-       size_t len = 0;
-       unsigned int i;
-
-       completed_tk = ec_completed_tk_new();
-       if (completed_tk == NULL)
-               return NULL;
-
-       if (tk->len == 0)
-               return completed_tk;
-
-       for (i = 0; i < tk->len && len < ec_strvec_len(strvec); i++) {
-               childvec = ec_strvec_ndup(strvec, len,
-                       ec_strvec_len(strvec) - len);
-               if (childvec == NULL)
-                       goto fail;
-
-               child_completed_tk = ec_tk_complete_tokens(tk->table[i],
-                       childvec);
-               if (child_completed_tk == NULL)
-                       goto fail;
-
-               ec_completed_tk_merge(completed_tk, child_completed_tk);
-
-               parsed_tk = ec_tk_parse_tokens(tk->table[i], childvec);
-               if (parsed_tk == NULL)
-                       goto fail;
-
-               ec_strvec_free(childvec);
-               childvec = NULL;
-
-               if (!ec_parsed_tk_matches(parsed_tk)) {
-                       ec_parsed_tk_free(parsed_tk);
-                       break;
-               }
-
-               len += ec_strvec_len(parsed_tk->strvec);
-               ec_parsed_tk_free(parsed_tk);
-       }
-
-       return completed_tk;
-
-fail:
-       ec_strvec_free(childvec);
-       ec_completed_tk_free(completed_tk);
-       return NULL;
-}
-
-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;
-
-       for (i = 0; i < tk->len; i++)
-               ec_tk_free(tk->table[i]);
-       ec_free(tk->table);
-}
-
-static struct ec_tk_type ec_tk_seq_type = {
-       .name = "seq",
-       .parse = ec_tk_seq_parse,
-       .complete = ec_tk_seq_complete,
-       .size = sizeof(struct ec_tk_seq),
-       .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;
-       struct ec_tk **table;
-
-       // XXX check tk type
-
-       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); // XXX really needed?
-
-       return 0;
-}
-
-struct ec_tk *__ec_tk_seq(const char *id, ...)
-{
-       struct ec_tk *gen_tk = NULL;
-       struct ec_tk_seq *tk = NULL;
-       struct ec_tk *child;
-       va_list ap;
-       int fail = 0;
-
-       va_start(ap, id);
-
-       gen_tk = __ec_tk_new(&ec_tk_seq_type, id);
-       tk = (struct ec_tk_seq *)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_seq_add(&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_seq_testcase(void)
-{
-       struct ec_tk *tk;
-       int ret = 0;
-
-       tk = EC_TK_SEQ(NULL,
-               ec_tk_str(NULL, "foo"),
-               ec_tk_str(NULL, "bar")
-       );
-       if (tk == NULL) {
-               ec_log(EC_LOG_ERR, "cannot create tk\n");
-               return -1;
-       }
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, 2, "foo", "bar");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, 2, "foo", "bar", "toto");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "foo");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "foox", "bar");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "foo", "barx");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "bar", "foo");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "", "foo");
-       ec_tk_free(tk);
-
-       /* test completion */
-       tk = EC_TK_SEQ(NULL,
-               ec_tk_str(NULL, "foo"),
-               ec_tk_option_new(NULL, ec_tk_str(NULL, "toto")),
-               ec_tk_str(NULL, "bar")
-       );
-       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", EC_TK_ENDLIST,
-               "foo");
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
-               "f", EC_TK_ENDLIST,
-               "oo", EC_TK_ENDLIST,
-               "oo");
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
-               "foo", EC_TK_ENDLIST,
-               "", EC_TK_ENDLIST,
-               "");
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
-               "foo", "", EC_TK_ENDLIST,
-               "bar", "toto", EC_TK_ENDLIST,
-               "");
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
-               "foo", "t", EC_TK_ENDLIST,
-               "oto", EC_TK_ENDLIST,
-               "oto");
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
-               "foo", "b", EC_TK_ENDLIST,
-               "ar", EC_TK_ENDLIST,
-               "ar");
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
-               "foo", "bar", EC_TK_ENDLIST,
-               "", EC_TK_ENDLIST,
-               "");
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
-               "x", EC_TK_ENDLIST,
-               EC_TK_ENDLIST,
-               "");
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
-               "foobarx", EC_TK_ENDLIST,
-               EC_TK_ENDLIST,
-               "");
-       ec_tk_free(tk);
-
-       return ret;
-}
-
-static struct ec_test ec_tk_seq_test = {
-       .name = "tk_seq",
-       .test = ec_tk_seq_testcase,
-};
-
-EC_TEST_REGISTER(ec_tk_seq_test);
diff --git a/lib/ecoli_tk_seq.h b/lib/ecoli_tk_seq.h
deleted file mode 100644 (file)
index a401ed2..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * 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_SEQ_
-#define ECOLI_TK_SEQ_
-
-#include <ecoli_tk.h>
-
-#define EC_TK_SEQ(args...) __ec_tk_seq(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_SEQ() or
- * ec_tk_seq() + ec_tk_seq_add() */
-struct ec_tk *__ec_tk_seq(const char *id, ...);
-
-struct ec_tk *ec_tk_seq(const char *id);
-
-/* child is consumed */
-// XXX add_child?
-int ec_tk_seq_add(struct ec_tk *tk, struct ec_tk *child);
-
-#endif
diff --git a/lib/ecoli_tk_sh_lex.c b/lib/ecoli_tk_sh_lex.c
deleted file mode 100644 (file)
index 204ed79..0000000
+++ /dev/null
@@ -1,499 +0,0 @@
-/*
- * 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 <assert.h>
-#include <stdarg.h>
-#include <ctype.h>
-#include <errno.h>
-
-#include <ecoli_malloc.h>
-#include <ecoli_log.h>
-#include <ecoli_test.h>
-#include <ecoli_strvec.h>
-#include <ecoli_tk.h>
-#include <ecoli_tk_seq.h>
-#include <ecoli_tk_str.h>
-#include <ecoli_tk_option.h>
-#include <ecoli_tk_sh_lex.h>
-
-struct ec_tk_sh_lex {
-       struct ec_tk gen;
-       struct ec_tk *child;
-};
-
-static size_t eat_spaces(const char *str)
-{
-       size_t i = 0;
-
-       /* skip spaces */
-       while (isblank(str[i]))
-               i++;
-
-       return i;
-}
-
-/*
- * Allocate a new string which is a copy of the input string with quotes
- * removed. If quotes are not closed properly, set missing_quote to the
- * missing quote char.
- */
-static char *unquote_str(const char *str, size_t n, int allow_missing_quote,
-       char *missing_quote)
-{
-       unsigned s = 1, d = 0;
-       char quote = str[0];
-       char *dst;
-       int closed = 0;
-
-       dst = ec_malloc(n);
-       if (dst == NULL) {
-               errno = ENOMEM;
-               return NULL;
-       }
-
-       /* copy token and remove quotes */
-       while (s < n && d < n && str[s] != '\0') {
-               if (str[s] == '\\' && str[s+1] == quote) {
-                       dst[d++] = quote;
-                       s += 2;
-                       continue;
-               }
-               if (str[s] == '\\' && str[s+1] == '\\') {
-                       dst[d++] = '\\';
-                       s += 2;
-                       continue;
-               }
-               if (str[s] == quote) {
-                       s++;
-                       closed = 1;
-                       break;
-               }
-               dst[d++] = str[s++];
-       }
-
-       /* not enough room in dst buffer (should not happen) */
-       if (d >= n) {
-               ec_free(dst);
-               errno = EMSGSIZE;
-               return NULL;
-       }
-
-       /* quote not closed */
-       if (closed == 0) {
-               if (missing_quote != NULL)
-                       *missing_quote = str[0];
-               if (allow_missing_quote == 0) {
-                       ec_free(dst);
-                       errno = EINVAL;
-                       return NULL;
-               }
-       }
-       dst[d++] = '\0';
-
-       return dst;
-}
-
-static size_t eat_quoted_str(const char *str)
-{
-       size_t i = 0;
-       char quote = str[0];
-
-       while (str[i] != '\0') {
-               if (str[i] != '\\' && str[i+1] == quote)
-                       return i + 2;
-               i++;
-       }
-
-       /* unclosed quote, will be detected later */
-       return i;
-}
-
-static size_t eat_str(const char *str)
-{
-       size_t i = 0;
-
-       /* skip spaces */
-       while (!isblank(str[i]) && str[i] != '\0')
-               i++;
-
-       return i;
-}
-
-static struct ec_strvec *tokenize(const char *str, int completion,
-       int allow_missing_quote, char *missing_quote)
-{
-       struct ec_strvec *strvec = NULL;
-       size_t off = 0, len, suboff, sublen;
-       char *word = NULL, *concat = NULL, *tmp;
-       int last_is_space = 1;
-
-//     printf("str=%s\n", str);
-
-       strvec = ec_strvec_new();
-       if (strvec == NULL)
-               goto fail;
-
-       while (str[off] != '\0') {
-               len = eat_spaces(&str[off]);
-               if (len > 0)
-                       last_is_space = 1;
-//             printf("space=%zd\n", len);
-               off += len;
-
-               len = 0;
-               suboff = off;
-               while (str[suboff] != '\0') {
-                       last_is_space = 0;
-                       if (str[suboff] == '"' || str[suboff] == '\'') {
-                               sublen = eat_quoted_str(&str[suboff]);
-//                             printf("sublen=%zd\n", sublen);
-                               word = unquote_str(&str[suboff], sublen,
-                                       allow_missing_quote, missing_quote);
-                       } else {
-                               sublen = eat_str(&str[suboff]);
-//                             printf("sublen=%zd\n", sublen);
-                               if (sublen == 0)
-                                       break;
-                               word = ec_strndup(&str[suboff], sublen);
-                       }
-
-                       if (word == NULL)
-                               goto fail;
-//                     printf("word=%s\n", word);
-
-                       len += sublen;
-                       suboff += sublen;
-
-                       if (concat == NULL) {
-                               concat = word;
-                               word = NULL;
-                       } else {
-                               tmp = ec_realloc(concat, len + 1);
-                               if (tmp == NULL)
-                                       goto fail;
-                               concat = tmp;
-                               strcat(concat, word);
-                               ec_free(word);
-                               word = NULL;
-                       }
-               }
-
-               if (concat != NULL) {
-                       if (ec_strvec_add(strvec, concat) < 0)
-                               goto fail;
-                       ec_free(concat);
-                       concat = NULL;
-               }
-
-               /* XXX remove all printf comments */
-//             printf("str off=%zd len=%zd\n", off, len);
-               off += len;
-       }
-
-       /* in completion mode, append an empty token if the string ends
-        * with space */
-       if (completion && last_is_space) {
-               if (ec_strvec_add(strvec, "") < 0)
-                       goto fail;
-       }
-
-       return strvec;
-
- fail:
-       ec_free(word);
-       ec_free(concat);
-       ec_strvec_free(strvec);
-       return NULL;
-}
-
-static struct ec_parsed_tk *ec_tk_sh_lex_parse(const struct ec_tk *gen_tk,
-       const struct ec_strvec *strvec)
-{
-       struct ec_tk_sh_lex *tk = (struct ec_tk_sh_lex *)gen_tk;
-       struct ec_strvec *new_vec = NULL, *match_strvec;
-       struct ec_parsed_tk *parsed_tk = NULL, *child_parsed_tk;
-       const char *str;
-
-       parsed_tk = ec_parsed_tk_new();
-       if (parsed_tk == NULL)
-               return NULL;
-
-       if (ec_strvec_len(strvec) == 0)
-               return parsed_tk;
-
-       str = ec_strvec_val(strvec, 0);
-       new_vec = tokenize(str, 0, 0, NULL);
-       if (new_vec == NULL)
-               goto fail;
-
-       child_parsed_tk = ec_tk_parse_tokens(tk->child, new_vec);
-       if (child_parsed_tk == NULL)
-               goto fail;
-
-       if (!ec_parsed_tk_matches(child_parsed_tk) ||
-                       ec_parsed_tk_len(child_parsed_tk) !=
-                               ec_strvec_len(new_vec)) {
-               ec_strvec_free(new_vec);
-               ec_parsed_tk_free(child_parsed_tk);
-               return parsed_tk;
-       }
-       ec_strvec_free(new_vec);
-       new_vec = NULL;
-
-       ec_parsed_tk_add_child(parsed_tk, child_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_strvec_free(new_vec);
-       ec_parsed_tk_free(parsed_tk);
-
-       return NULL;
-}
-
-static struct ec_completed_tk *ec_tk_sh_lex_complete(const struct ec_tk *gen_tk,
-       const struct ec_strvec *strvec)
-{
-       struct ec_tk_sh_lex *tk = (struct ec_tk_sh_lex *)gen_tk;
-       struct ec_completed_tk *completed_tk, *child_completed_tk = NULL;
-       struct ec_strvec *new_vec = NULL;
-       const char *str;
-       char missing_quote;
-
-//     printf("==================\n");
-       completed_tk = ec_completed_tk_new();
-       if (completed_tk == NULL)
-               return NULL;
-
-       if (ec_strvec_len(strvec) != 1)
-               return completed_tk;
-
-       str = ec_strvec_val(strvec, 0);
-       new_vec = tokenize(str, 1, 1, &missing_quote);
-       if (new_vec == NULL)
-               goto fail;
-
-//     ec_strvec_dump(new_vec, stdout);
-
-       child_completed_tk = ec_tk_complete_tokens(tk->child, new_vec);
-       if (child_completed_tk == NULL)
-               goto fail;
-
-       ec_strvec_free(new_vec);
-       new_vec = NULL;
-       ec_completed_tk_merge(completed_tk, child_completed_tk);
-
-       return completed_tk;
-
-
-#if 0
-       for (i = 0, t = &tokens[0]; i < tk->len; i++, t++) {
-               if (*(t + 1) != NULL) {
-                       child_parsed_tk = ec_tk_parse(tk->table[i], *t);
-                       if (child_parsed_tk == NULL)
-                               goto fail;
-
-                       if (strlen(child_parsed_tk->str) == 0)
-                               t--;
-                       else if (strlen(child_parsed_tk->str) != strlen(*t)) {
-                               ec_parsed_tk_free(child_parsed_tk);
-                               goto fail;
-                       }
-
-                       ec_parsed_tk_free(child_parsed_tk);
-               } else {
-                       child_completed_tk = ec_tk_complete(tk->table[i], *t);
-                       if (child_completed_tk == NULL) {
-                               ec_completed_tk_free(completed_tk);
-                               return NULL;
-                       }
-                       ec_completed_tk_merge(completed_tk, child_completed_tk);
-
-                       child_parsed_tk = ec_tk_parse(tk->table[i], "");
-                       if (child_parsed_tk == NULL)
-                               break;
-                       ec_parsed_tk_free(child_parsed_tk);
-                       t--;
-               }
-       }
-
-       if (tokens != NULL) {
-               for (t = &tokens[0]; *t != NULL; t++)
-                       ec_free(*t);
-               ec_free(tokens);
-               tokens = NULL;
-       }
-
-       ec_completed_tk_dump(stdout, completed_tk);
-#endif
-
- fail:
-       ec_strvec_free(new_vec);
-       ec_completed_tk_free(completed_tk);
-       return NULL;
-}
-
-static void ec_tk_sh_lex_free_priv(struct ec_tk *gen_tk)
-{
-       struct ec_tk_sh_lex *tk = (struct ec_tk_sh_lex *)gen_tk;
-
-       ec_tk_free(tk->child);
-}
-
-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,
-       .size = sizeof(struct ec_tk_sh_lex),
-       .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;
-
-       if (child == NULL)
-               return NULL;
-
-       tk = (struct ec_tk_sh_lex *)__ec_tk_new(&ec_tk_sh_lex_type, id);
-       if (tk == NULL) {
-               ec_tk_free(child);
-               return NULL;
-       }
-
-       tk->child = child;
-
-       return &tk->gen;
-}
-
-static int ec_tk_sh_lex_testcase(void)
-{
-       struct ec_tk *tk;
-       int ret = 0;
-
-       tk = ec_tk_sh_lex_new(NULL,
-               EC_TK_SEQ(NULL,
-                       ec_tk_str(NULL, "foo"),
-                       ec_tk_option_new(NULL,
-                               ec_tk_str(NULL, "toto")
-                       ),
-                       ec_tk_str(NULL, "bar")
-               )
-       );
-       if (tk == NULL) {
-               ec_log(EC_LOG_ERR, "cannot create tk\n");
-               return -1;
-       }
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo bar");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "  foo   bar");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "  'foo' \"bar\"");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "  'f'oo 'toto' bar");
-       ec_tk_free(tk);
-
-       /* test completion */
-       tk = ec_tk_sh_lex_new(NULL,
-               EC_TK_SEQ(NULL,
-                       ec_tk_str(NULL, "foo"),
-                       ec_tk_option_new(NULL,
-                               ec_tk_str(NULL, "toto")
-                       ),
-                       ec_tk_str(NULL, "bar"),
-                       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", EC_TK_ENDLIST,
-               "foo");
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
-               " ", EC_TK_ENDLIST,
-               "foo", EC_TK_ENDLIST,
-               "foo");
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
-               "f", EC_TK_ENDLIST,
-               "oo", EC_TK_ENDLIST,
-               "oo");
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
-               "foo", EC_TK_ENDLIST,
-               "", EC_TK_ENDLIST,
-               "");
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
-               "foo ", EC_TK_ENDLIST,
-               "bar", "toto", EC_TK_ENDLIST,
-               "");
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
-               "foo t", EC_TK_ENDLIST,
-               "oto", EC_TK_ENDLIST,
-               "oto");
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
-               "foo b", EC_TK_ENDLIST,
-               "ar", EC_TK_ENDLIST,
-               "ar");
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
-               "foo bar", EC_TK_ENDLIST,
-               "", EC_TK_ENDLIST,
-               "");
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
-               "foo bar ", EC_TK_ENDLIST,
-               "titi", EC_TK_ENDLIST,
-               "titi");
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
-               "foo toto bar ", EC_TK_ENDLIST,
-               "titi", EC_TK_ENDLIST,
-               "titi");
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
-               "x", EC_TK_ENDLIST,
-               EC_TK_ENDLIST,
-               "");
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
-               "foo barx", EC_TK_ENDLIST,
-               EC_TK_ENDLIST,
-               "");
-
-       ec_tk_free(tk);
-       return ret;
-}
-
-static struct ec_test ec_tk_sh_lex_test = {
-       .name = "tk_sh_lex",
-       .test = ec_tk_sh_lex_testcase,
-};
-
-EC_TEST_REGISTER(ec_tk_sh_lex_test);
diff --git a/lib/ecoli_tk_sh_lex.h b/lib/ecoli_tk_sh_lex.h
deleted file mode 100644 (file)
index 7031d1c..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * 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_SHLEX_
-#define ECOLI_TK_SHLEX_
-
-#include <ecoli_tk.h>
-
-struct ec_tk *ec_tk_sh_lex_new(const char *id, struct ec_tk *child);
-
-#endif
diff --git a/lib/ecoli_tk_space.c b/lib/ecoli_tk_space.c
deleted file mode 100644 (file)
index 2162afe..0000000
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * 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 <ctype.h>
-
-#include <ecoli_log.h>
-#include <ecoli_test.h>
-#include <ecoli_malloc.h>
-#include <ecoli_strvec.h>
-#include <ecoli_tk.h>
-#include <ecoli_tk_space.h>
-
-struct ec_tk_space {
-       struct ec_tk gen;
-};
-
-static struct ec_parsed_tk *ec_tk_space_parse(const struct ec_tk *gen_tk,
-       const struct ec_strvec *strvec)
-{
-       struct ec_parsed_tk *parsed_tk = NULL;
-       struct ec_strvec *match_strvec;
-       const char *str;
-       size_t len = 0;
-
-       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);
-       while (isspace(str[len]))
-               len++;
-       if (len == 0 || len != 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 struct ec_tk_type ec_tk_space_type = {
-       .name = "space",
-       .parse = ec_tk_space_parse,
-       .complete = ec_tk_default_complete,
-       .size = sizeof(struct ec_tk_space),
-};
-
-EC_TK_TYPE_REGISTER(ec_tk_space_type);
-
-static int ec_tk_space_testcase(void)
-{
-       struct ec_tk *tk;
-       int ret = 0;
-
-       tk = ec_tk_new("space", NULL);
-       if (tk == NULL) {
-               ec_log(EC_LOG_ERR, "cannot create tk\n");
-               return -1;
-       }
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, " ");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, " ", "foo");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, " foo");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "foo ");
-       ec_tk_free(tk);
-
-       /* test completion */
-       tk = ec_tk_new("space", NULL);
-       if (tk == NULL) {
-               ec_log(EC_LOG_ERR, "cannot create tk\n");
-               return -1;
-       }
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
-               "", EC_TK_ENDLIST,
-               EC_TK_ENDLIST,
-               "");
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
-               " ", EC_TK_ENDLIST,
-               EC_TK_ENDLIST,
-               "");
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
-               "foo", EC_TK_ENDLIST,
-               EC_TK_ENDLIST,
-               "");
-       ec_tk_free(tk);
-
-       return ret;
-}
-
-static struct ec_test ec_tk_space_test = {
-       .name = "tk_space",
-       .test = ec_tk_space_testcase,
-};
-
-EC_TEST_REGISTER(ec_tk_space_test);
diff --git a/lib/ecoli_tk_space.h b/lib/ecoli_tk_space.h
deleted file mode 100644 (file)
index 08aa48f..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * 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_SPACE_
-#define ECOLI_TK_SPACE_
-
-#include <ecoli_tk.h>
-
-struct ec_tk *ec_tk_space_new(const char *id);
-
-#endif
diff --git a/lib/ecoli_tk_str.c b/lib/ecoli_tk_str.c
deleted file mode 100644 (file)
index 997f717..0000000
+++ /dev/null
@@ -1,254 +0,0 @@
-/*
- * 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 <ecoli_log.h>
-#include <ecoli_malloc.h>
-#include <ecoli_test.h>
-#include <ecoli_strvec.h>
-#include <ecoli_tk.h>
-#include <ecoli_tk_str.h>
-
-struct ec_tk_str {
-       struct ec_tk gen;
-       char *string;
-       unsigned len;
-};
-
-static struct ec_parsed_tk *ec_tk_str_parse(const struct ec_tk *gen_tk,
-       const struct ec_strvec *strvec)
-{
-       struct ec_tk_str *tk = (struct ec_tk_str *)gen_tk;
-       struct ec_strvec *match_strvec;
-       struct ec_parsed_tk *parsed_tk = NULL;
-       const char *str;
-
-       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 (strcmp(str, tk->string) != 0)
-               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 struct ec_completed_tk *ec_tk_str_complete(const struct ec_tk *gen_tk,
-       const struct ec_strvec *strvec)
-{
-       struct ec_tk_str *tk = (struct ec_tk_str *)gen_tk;
-       struct ec_completed_tk *completed_tk;
-       struct ec_completed_tk_elt *completed_tk_elt;
-       const char *str, *add;
-       size_t n = 0;
-
-       completed_tk = ec_completed_tk_new();
-       if (completed_tk == NULL)
-               return NULL;
-
-       if (ec_strvec_len(strvec) != 1)
-               return completed_tk;
-
-       str = ec_strvec_val(strvec, 0);
-       for (n = 0; n < tk->len; n++) {
-               if (str[n] != tk->string[n])
-                       break;
-       }
-
-       if (str[n] != '\0')
-               add = NULL;
-       else
-               add = tk->string + n;
-
-       completed_tk_elt = ec_completed_tk_elt_new(gen_tk, add);
-       if (completed_tk_elt == NULL) {
-               ec_completed_tk_free(completed_tk);
-               return NULL;
-       }
-
-       ec_completed_tk_add_elt(completed_tk, completed_tk_elt);
-
-       return completed_tk;
-}
-
-static const char *ec_tk_str_desc(const struct ec_tk *gen_tk)
-{
-       struct ec_tk_str *tk = (struct ec_tk_str *)gen_tk;
-
-       return tk->string;
-}
-
-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_type ec_tk_str_type = {
-       .name = "str",
-       .parse = ec_tk_str_parse,
-       .complete = ec_tk_str_complete,
-       .desc = ec_tk_str_desc,
-       .size = sizeof(struct ec_tk_str),
-       .free_priv = ec_tk_str_free_priv,
-};
-
-EC_TK_TYPE_REGISTER(ec_tk_str_type);
-
-int ec_tk_str_set_str(struct ec_tk *gen_tk, const char *str)
-{
-       struct ec_tk_str *tk = (struct ec_tk_str *)gen_tk;
-
-       if (str == NULL)
-               return -EINVAL;
-       if (tk->string != NULL)
-               return -EEXIST; // XXX allow to replace
-
-       tk->string = ec_strdup(str);
-       if (tk->string == NULL)
-               return -ENOMEM;
-
-       tk->len = strlen(tk->string);
-
-       return 0;
-}
-
-struct ec_tk *ec_tk_str(const char *id, const char *str)
-{
-       struct ec_tk *gen_tk = NULL;
-
-       gen_tk = __ec_tk_new(&ec_tk_str_type, id);
-       if (gen_tk == NULL)
-               goto fail;
-
-       if (ec_tk_str_set_str(gen_tk, str) < 0)
-               goto fail;
-
-       return gen_tk;
-
-fail:
-       ec_tk_free(gen_tk);
-       return NULL;
-}
-
-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");
-               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, "foobar");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, " foo");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "");
-       ec_tk_free(tk);
-
-       tk = ec_tk_str(NULL, "Здравствуйте");
-       if (tk == NULL) {
-               ec_log(EC_LOG_ERR, "cannot create tk\n");
-               return -1;
-       }
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "Здравствуйте");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "Здравствуйте",
-               "John!");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "foo");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "");
-       ec_tk_free(tk);
-
-       /* an empty token string always matches */
-       tk = ec_tk_str(NULL, "");
-       if (tk == NULL) {
-               ec_log(EC_LOG_ERR, "cannot create tk\n");
-               return -1;
-       }
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "", "foo");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "foo");
-       ec_tk_free(tk);
-
-       /* test completion */
-       tk = ec_tk_str(NULL, "foo");
-       if (tk == NULL) {
-               ec_log(EC_LOG_ERR, "cannot create tk\n");
-               return -1;
-       }
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
-               EC_TK_ENDLIST,
-               EC_TK_ENDLIST,
-               "");
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
-               "", EC_TK_ENDLIST,
-               "foo", EC_TK_ENDLIST,
-               "foo");
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
-               "f", EC_TK_ENDLIST,
-               "oo", EC_TK_ENDLIST,
-               "oo");
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
-               "foo", EC_TK_ENDLIST,
-               "", EC_TK_ENDLIST,
-               "");
-       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_str_test = {
-       .name = "tk_str",
-       .test = ec_tk_str_testcase,
-};
-
-EC_TEST_REGISTER(ec_tk_str_test);
diff --git a/lib/ecoli_tk_str.h b/lib/ecoli_tk_str.h
deleted file mode 100644 (file)
index d4e2503..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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_STR_
-#define ECOLI_TK_STR_
-
-#include <ecoli_tk.h>
-
-struct ec_tk *ec_tk_str(const char *id, const char *str);
-
-struct ec_tk *ec_tk_str_new(const char *id);
-
-/* str is duplicated */
-int ec_tk_str_set_str(struct ec_tk *tk, const char *str);
-
-#endif
diff --git a/lib/ecoli_tk_subset.c b/lib/ecoli_tk_subset.c
deleted file mode 100644 (file)
index 6762eee..0000000
+++ /dev/null
@@ -1,479 +0,0 @@
-/*
- * 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;
-
-       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 no child tk matches, return a matching empty strvec */
-       if (result.parsed_table_len == 0) {
-               ec_free(result.parsed_table);
-               match_strvec = ec_strvec_new();
-               if (match_strvec == NULL)
-                       goto fail;
-               ec_parsed_tk_set_match(parsed_tk, gen_tk, match_strvec);
-               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(struct ec_tk **table, size_t table_len,
-       const struct ec_strvec *strvec)
-{
-       struct ec_completed_tk *completed_tk = NULL;
-       struct ec_completed_tk *child_completed_tk = NULL;
-       struct ec_strvec *childvec = NULL;
-       struct ec_parsed_tk *parsed_tk = NULL;
-       struct ec_tk *save;
-       size_t i, len;
-
-       /*
-        * example with table = [a, b, c]
-        * subset_complete([a,b,c], strvec) returns:
-        *   complete(a, strvec) + complete(b, strvec) + complete(c, strvec) +
-        *   + __subset_complete([b, c], childvec) if a matches
-        *   + __subset_complete([a, c], childvec) if b matches
-        *   + __subset_complete([a, b], childvec) if c matches
-        */
-
-       completed_tk = ec_completed_tk_new();
-       if (completed_tk == NULL)
-               goto fail;
-
-       /* first, try to complete with each token of the table */
-       for (i = 0; i < table_len; i++) {
-               if (table[i] == NULL)
-                       continue;
-
-               child_completed_tk = ec_tk_complete_tokens(table[i],
-                       strvec);
-
-               if (child_completed_tk == NULL)
-                       goto fail;
-
-               ec_completed_tk_merge(completed_tk, child_completed_tk);
-               child_completed_tk = NULL;
-       }
-
-       /* then, if a token matches, advance in strvec and try to complete with
-        * all the other tokens */
-       for (i = 0; i < table_len; i++) {
-               if (table[i] == NULL)
-                       continue;
-
-               parsed_tk = ec_tk_parse_tokens(table[i], strvec);
-               if (parsed_tk == NULL)
-                       goto fail;
-
-               if (!ec_parsed_tk_matches(parsed_tk)) {
-                       ec_parsed_tk_free(parsed_tk);
-                       parsed_tk = NULL;
-                       continue;
-               }
-
-               len = ec_parsed_tk_len(parsed_tk);
-               childvec = ec_strvec_ndup(strvec, len,
-                                       ec_strvec_len(strvec) - len);
-               if (childvec == NULL)
-                       goto fail;
-
-               save = table[i];
-               table[i] = NULL;
-               child_completed_tk = __ec_tk_subset_complete(table,
-                                                       table_len, childvec);
-               table[i] = save;
-               ec_strvec_free(childvec);
-               childvec = NULL;
-
-               if (child_completed_tk == NULL)
-                       goto fail;
-
-               ec_completed_tk_merge(completed_tk, child_completed_tk);
-               child_completed_tk = NULL;
-
-               ec_parsed_tk_free(parsed_tk);
-               parsed_tk = NULL;
-       }
-
-       return completed_tk;
-fail:
-       ec_parsed_tk_free(parsed_tk);
-       ec_completed_tk_free(child_completed_tk);
-       ec_completed_tk_free(completed_tk);
-
-       return NULL;
-}
-
-static struct ec_completed_tk *ec_tk_subset_complete(const struct ec_tk *gen_tk,
-       const struct ec_strvec *strvec)
-{
-       struct ec_tk_subset *tk = (struct ec_tk_subset *)gen_tk;
-
-       return __ec_tk_subset_complete(tk->table, tk->len, strvec);
-}
-
-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 = "subset",
-       .parse = ec_tk_subset_parse,
-       .complete = ec_tk_subset_complete,
-       .size = sizeof(struct ec_tk_subset),
-       .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;
-       struct ec_tk *child;
-       va_list ap;
-       int fail = 0;
-
-       va_start(ap, id);
-
-       gen_tk = __ec_tk_new(&ec_tk_subset_type, 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, 0);
-       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, 0, " ");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, 0, "foox");
-       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,
-               "ar2", 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
deleted file mode 100644 (file)
index aac3014..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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
diff --git a/lib/ecoli_tk_weakref.c b/lib/ecoli_tk_weakref.c
deleted file mode 100644 (file)
index 6ce9a32..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * 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 <assert.h>
-#include <stdarg.h>
-#include <errno.h>
-
-#include <ecoli_malloc.h>
-#include <ecoli_log.h>
-#include <ecoli_test.h>
-#include <ecoli_strvec.h>
-#include <ecoli_tk.h>
-#include <ecoli_tk_str.h>
-#include <ecoli_tk_option.h>
-#include <ecoli_tk_weakref.h>
-
-struct ec_tk_weakref {
-       struct ec_tk gen;
-       struct ec_tk *child;
-};
-
-static struct ec_parsed_tk *ec_tk_weakref_parse(const struct ec_tk *gen_tk,
-       const struct ec_strvec *strvec)
-{
-       struct ec_tk_weakref *tk = (struct ec_tk_weakref *)gen_tk;
-
-       return ec_tk_parse_tokens(tk->child, strvec);
-}
-
-static struct ec_completed_tk *ec_tk_weakref_complete(const struct ec_tk *gen_tk,
-       const struct ec_strvec *strvec)
-{
-       struct ec_tk_weakref *tk = (struct ec_tk_weakref *)gen_tk;
-
-       return ec_tk_complete_tokens(tk->child, strvec);
-}
-
-static struct ec_tk_type ec_tk_weakref_type = {
-       .name = "weakref",
-       .parse = ec_tk_weakref_parse,
-       .complete = ec_tk_weakref_complete,
-       .size = sizeof(struct ec_tk_weakref),
-};
-
-EC_TK_TYPE_REGISTER(ec_tk_weakref_type);
-
-int ec_tk_weakref_set(struct ec_tk *gen_tk, struct ec_tk *child)
-{
-       struct ec_tk_weakref *tk = (struct ec_tk_weakref *)gen_tk;
-
-       // XXX check tk type
-
-       assert(tk != NULL);
-
-       if (child == NULL)
-               return -EINVAL;
-
-       gen_tk->flags &= ~EC_TK_F_BUILT;
-
-       tk->child = child;
-
-       child->parent = gen_tk;
-       TAILQ_INSERT_TAIL(&gen_tk->children, child, next); // XXX really needed?
-
-       return 0;
-}
-
-struct ec_tk *ec_tk_weakref(const char *id, struct ec_tk *child)
-{
-       struct ec_tk *gen_tk = NULL;
-
-       if (child == NULL)
-               return NULL;
-
-       gen_tk = __ec_tk_new(&ec_tk_weakref_type, id);
-       if (gen_tk == NULL)
-               return NULL;
-
-       ec_tk_weakref_set(gen_tk, child);
-
-       return gen_tk;
-}
-
-static int ec_tk_weakref_testcase(void)
-{
-       return 0;
-}
-
-static struct ec_test ec_tk_weakref_test = {
-       .name = "tk_weakref",
-       .test = ec_tk_weakref_testcase,
-};
-
-EC_TEST_REGISTER(ec_tk_weakref_test);
diff --git a/lib/ecoli_tk_weakref.h b/lib/ecoli_tk_weakref.h
deleted file mode 100644 (file)
index c911400..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * 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_BYPASS_
-#define ECOLI_TK_BYPASS_
-
-#include <ecoli_tk.h>
-
-/* a tk that just behaves like its child and that does not free
- * its child when freed.
- *
- * useful to create cyclic graphs of tokens:
- *   creating a loop (with clones) result in something that is not
- *   freeable, due to reference counters
- *
- * Example:
- *  val = int(0, 10)
- *  op = str("!")
- *  expr = or()
- *  seq = seq(clone(op), clone(expr))
- *  expr.add(clone(seq))
- *  expr.add(clone(val))
- *  free(val)
- *  free(op)
- *  free(seq)
- *
- * FAIL: expr cannot be freed due to cyclic refs
- * The references are like this:
- *
- *                   val
- *                    ^
- *                    |
- *        $user ---> expr ---> seq ---> op
- *                        <---
- *
- * It is solved with:
- *  val = int(0, 10)
- *  op = str("!")
- *  expr = or()
- *  weak = weak(expr)
- *  seq = seq(clone(op), clone(weak))
- *  expr.add(clone(seq))
- *  expr.add(clone(val))
- *  free(val)
- *  free(op)
- *  free(weak)
- *  free(seq)
- *
- *
- *                   val
- *                    ^
- *                    |
- *        $user ---> expr ---------------> seq ---> op
- *                        <- - - weak <---
- *
- * The node expr can be freed.
- */
-
-/* on error, child is *not* freed */
-struct ec_tk *ec_tk_weakref(const char *id, struct ec_tk *child);
-
-struct ec_tk *ec_tk_weakref_empty(const char *id);
-
-/* on error, child is *not* freed */
-int ec_tk_weakref_set(struct ec_tk *tk, struct ec_tk *child);
-
-#endif
index 241d2c9b983ff0b765794b782aac3b5edc3a1ec1..9b61903f910a328fc3ab1fb3f6e86c7ed181fcf4 100644 (file)
 #include <readline/readline.h>
 #include <readline/history.h>
 
-#include <ecoli_tk.h>
+#include <ecoli_node.h>
 #include <ecoli_keyval.h>
 
-#include <ecoli_tk_str.h>
-#include <ecoli_tk_seq.h>
-#include <ecoli_tk_space.h>
-#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>
+#include <ecoli_node_str.h>
+#include <ecoli_node_seq.h>
+#include <ecoli_node_space.h>
+#include <ecoli_node_or.h>
+#include <ecoli_node_sh_lex.h>
+#include <ecoli_node_int.h>
+#include <ecoli_node_option.h>
+#include <ecoli_node_cmd.h>
 
-static struct ec_tk *commands;
+static struct ec_node *commands;
 
 static char *my_completion_entry(const char *s, int state)
 {
-       static struct ec_completed_tk *c;
-       static struct ec_completed_tk_iter *iter;
-       static const struct ec_completed_tk_elt *elt;
+       static struct ec_completed *c;
+       static struct ec_completed_iter *iter;
+       static const struct ec_completed_elt *elt;
        char *out_string;
 
 
        if (state == 0) {
                char *line;
 
-               ec_completed_tk_free(c);
+               ec_completed_free(c);
 
                line = strdup(rl_line_buffer);
                if (line == NULL)
                        return NULL;
                line[rl_point] = '\0';
 
-               c = ec_tk_complete(commands, line);
+               c = ec_node_complete(commands, line);
                free(line);
                if (c == NULL)
                        return NULL;
 
-               ec_completed_tk_iter_free(iter);
-               iter = ec_completed_tk_iter_new(c, EC_MATCH);
+               ec_completed_iter_free(iter);
+               iter = ec_completed_iter_new(c, EC_MATCH);
                if (iter == NULL)
                        return NULL;
        }
 
-       elt = ec_completed_tk_iter_next(iter);
+       elt = ec_completed_iter_next(iter);
        if (elt == NULL)
                return NULL;
 
@@ -98,19 +98,21 @@ static char **my_attempted_completion(const char *text, int start, int end)
 }
 
 /* this function builds the help string */
-static char *get_tk_help(const struct ec_tk *tk)
+static char *get_tk_help(const struct ec_node *node)
 {
-       const struct ec_tk *tk2;
+       const struct ec_node *node2;
        char *help = NULL;
        char *tk_help = NULL;
 
-       for (tk2 = tk; tk2 != NULL && tk_help == NULL; tk2 = ec_tk_parent(tk2))
-               tk_help = ec_keyval_get(ec_tk_attrs(tk2), "help");
+       for (node2 = node;
+            node2 != NULL && tk_help == NULL;
+            node2 = ec_node_parent(node2))
+               tk_help = ec_keyval_get(ec_node_attrs(node2), "help");
 
        if (tk_help == NULL)
                tk_help = "";
 
-       if (asprintf(&help, "%-20s %s", ec_tk_desc(tk), tk_help) < 0)
+       if (asprintf(&help, "%-20s %s", ec_node_desc(node), tk_help) < 0)
                return NULL;
 
        return help;
@@ -118,9 +120,9 @@ static char *get_tk_help(const struct ec_tk *tk)
 
 static int show_help(int ignore, int invoking_key)
 {
-       const struct ec_completed_tk_elt *elt;
-       struct ec_completed_tk_iter *iter;
-       struct ec_completed_tk *c;
+       const struct ec_completed_elt *elt;
+       struct ec_completed_iter *iter;
+       struct ec_completed *c;
        char *line;
        unsigned int count, i;
        char **helps = NULL;
@@ -133,29 +135,29 @@ static int show_help(int ignore, int invoking_key)
                return 1;
        line[rl_point] = '\0';
 
-       c = ec_tk_complete(commands, line);
+       c = ec_node_complete(commands, line);
        free(line);
        if (c == NULL)
                return 1;
-       //ec_completed_tk_dump(stdout, c);
+       //ec_completed_dump(stdout, c);
 
-       count = ec_completed_tk_count(c, EC_MATCH | EC_NO_MATCH);
+       count = ec_completed_count(c, EC_MATCH | EC_NO_MATCH);
        helps = calloc(count + 1, sizeof(char *));
        if (helps == NULL)
                return 1;
 
-       iter = ec_completed_tk_iter_new(c, EC_MATCH | EC_NO_MATCH);
+       iter = ec_completed_iter_new(c, EC_MATCH | EC_NO_MATCH);
        if (iter == NULL)
                goto fail;
 
        /* strangely, rl_display_match_list() expects first index at 1 */
-       for (i = 1, elt = ec_completed_tk_iter_next(iter);
+       for (i = 1, elt = ec_completed_iter_next(iter);
             i <= count && elt != NULL;
-            i++, elt = ec_completed_tk_iter_next(iter)) {
-               helps[i] = get_tk_help(elt->tk);
+            i++, elt = ec_completed_iter_next(iter)) {
+               helps[i] = get_tk_help(elt->node);
        }
 
-       ec_completed_tk_free(c);
+       ec_completed_free(c);
 
        rl_display_match_list(helps, count, 1000);
 
@@ -171,65 +173,65 @@ fail:
 
 static int create_commands(void)
 {
-       struct ec_tk *cmdlist = NULL, *cmd = NULL;
+       struct ec_node *cmdlist = NULL, *cmd = NULL;
 
-       cmdlist = ec_tk_new("or", NULL);
+       cmdlist = ec_node_new("or", NULL);
        if (cmdlist == NULL)
                goto fail;
 
-       cmd = EC_TK_SEQ(NULL,
-               ec_tk_str(NULL, "hello"),
-               EC_TK_OR("name",
-                       ec_tk_str(NULL, "john"),
-                       ec_tk_str(NULL, "johnny"),
-                       ec_tk_str(NULL, "mike")
+       cmd = EC_NODE_SEQ(NULL,
+               ec_node_str(NULL, "hello"),
+               EC_NODE_OR("name",
+                       ec_node_str(NULL, "john"),
+                       ec_node_str(NULL, "johnny"),
+                       ec_node_str(NULL, "mike")
                ),
-               ec_tk_option_new(NULL, ec_tk_int("int", 0, 10, 10))
+               ec_node_option(NULL, ec_node_int("int", 0, 10, 10))
        );
        if (cmd == NULL)
                goto fail;
-       ec_keyval_set(ec_tk_attrs(cmd), "help",
+       ec_keyval_set(ec_node_attrs(cmd), "help",
                "say hello to someone several times", NULL);
-       ec_keyval_set(ec_tk_attrs(ec_tk_find(cmd, "name")),
+       ec_keyval_set(ec_node_attrs(ec_node_find(cmd, "name")),
                "help", "the name of the person", NULL);
-       ec_keyval_set(ec_tk_attrs(ec_tk_find(cmd, "int")),
+       ec_keyval_set(ec_node_attrs(ec_node_find(cmd, "int")),
                "help", "an integer", NULL);
-       if (ec_tk_or_add(cmdlist, cmd) < 0)
+       if (ec_node_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));
+       cmd = EC_NODE_CMD(NULL, "good morning john|johnny|mike [count]",
+                       ec_node_int("count", 0, 10, 10));
        if (cmd == NULL)
                goto fail;
-       ec_keyval_set(ec_tk_attrs(cmd), "help",
+       ec_keyval_set(ec_node_attrs(cmd), "help",
                "say good morning to someone several times", NULL);
-       if (ec_tk_or_add(cmdlist, cmd) < 0)
+       if (ec_node_or_add(cmdlist, cmd) < 0)
                goto fail;
 #endif
 
-       cmd = EC_TK_SEQ(NULL,
-               ec_tk_str(NULL, "bye")
+       cmd = EC_NODE_SEQ(NULL,
+               ec_node_str(NULL, "bye")
        );
-       ec_keyval_set(ec_tk_attrs(cmd), "help", "say bye to someone", NULL);
-       if (ec_tk_or_add(cmdlist, cmd) < 0)
+       ec_keyval_set(ec_node_attrs(cmd), "help", "say bye to someone", NULL);
+       if (ec_node_or_add(cmdlist, cmd) < 0)
                goto fail;
 
-       commands = ec_tk_sh_lex_new(NULL, cmdlist);
+       commands = ec_node_sh_lex(NULL, cmdlist);
        if (commands == NULL)
                goto fail;
 
        return 0;
 
  fail:
-       fprintf(stderr, "cannot initialize tokens\n");
-       ec_tk_free(cmdlist);
+       fprintf(stderr, "cannot initialize nodes\n");
+       ec_node_free(cmdlist);
        return -1;
 }
 
 int main(void)
 {
-       struct ec_parsed_tk *p;
+       struct ec_parsed *p;
 //     const char *name;
        char *line;
 
@@ -245,14 +247,14 @@ int main(void)
                if (line == NULL)
                        break;
 
-               p = ec_tk_parse(commands, line);
-               ec_parsed_tk_dump(stdout, p);
+               p = ec_node_parse(commands, line);
+               ec_parsed_dump(stdout, p);
                add_history(line);
-               ec_parsed_tk_free(p);
+               ec_parsed_free(p);
        }
 
 
-       ec_tk_free(commands);
+       ec_node_free(commands);
        return 0;
 
 }
index 6791965cccf2a92755d51f79a5d4371ca5c63e52..7ed4ab30af0d1a95c1bd35104b58fff69e52ab8d 100644 (file)
@@ -24,13 +24,18 @@ X remove the _new() functions
 - split ecoli_tk.h
 - cache results when appropriate?
 - size_t or unsigned int?
-- rename:
+X rename:
   - ec_tk -> ec_node
   - ec_parsed_tk -> ec_parsed
   - ec_completed_tk -> ec_completed
   - tk, gen_tk, token, ... -> node
   - tokens -> input_str / input_strvec ?
 
+dependencies
+============
+
+- pass the current parsed state when parsing/completing
+
 logs
 ====