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)
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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
--- /dev/null
+#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);
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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
#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);
// 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;
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;
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) {
match, expected);
}
- ec_parsed_tk_free(p);
+ ec_parsed_free(p);
out:
ec_strvec_free(vec);
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;
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;
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;
}
}
- 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,
out:
ec_strvec_free(vec);
- ec_completed_tk_free(c);
+ ec_completed_free(c);
va_end(ap);
return ret;
}
#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) \
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", \
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_; \
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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
+++ /dev/null
-/*
- * 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);
+++ /dev/null
-/*
- * 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
+++ /dev/null
-/*
- * 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);
+++ /dev/null
-/*
- * 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
+++ /dev/null
-/*
- * 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 */
+++ /dev/null
-/*
- * 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
+++ /dev/null
-/*
- * 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);
+++ /dev/null
-/*
- * 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);
+++ /dev/null
-/*
- * 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
+++ /dev/null
-/*
- * 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);
+++ /dev/null
-/*
- * 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
+++ /dev/null
-/*
- * 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);
+++ /dev/null
-/*
- * 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
+++ /dev/null
-/*
- * 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);
+++ /dev/null
-/*
- * 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
+++ /dev/null
-/*
- * 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);
+++ /dev/null
-/*
- * 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
+++ /dev/null
-#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);
+++ /dev/null
-/*
- * 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
+++ /dev/null
-/*
- * 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);
+++ /dev/null
-/*
- * 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
+++ /dev/null
-/*
- * 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);
+++ /dev/null
-/*
- * 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
+++ /dev/null
-/*
- * 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);
+++ /dev/null
-/*
- * 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
+++ /dev/null
-/*
- * 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);
+++ /dev/null
-/*
- * 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
+++ /dev/null
-/*
- * 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);
+++ /dev/null
-/*
- * 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
+++ /dev/null
-/*
- * 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);
+++ /dev/null
-/*
- * 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
#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;
}
/* 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;
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;
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);
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;
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;
}
- 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
====