From: Olivier Matz Date: Thu, 15 Jun 2017 20:24:50 +0000 (+0200) Subject: save X-Git-Url: http://git.droids-corp.org/?a=commitdiff_plain;h=575fd5b430d5f1557d9a6bdb19c037efaf023759;p=protos%2Flibecoli.git save --- diff --git a/lib/Makefile b/lib/Makefile index d868c39..da9961a 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -39,23 +39,23 @@ srcs += ecoli_log.c srcs += ecoli_malloc.c srcs += ecoli_strvec.c srcs += ecoli_test.c -srcs += ecoli_tk.c -srcs += ecoli_tk_cmd.c -srcs += ecoli_tk_empty.c -srcs += ecoli_tk_expr.c -srcs += ecoli_tk_expr_test.c -srcs += ecoli_tk_int.c -srcs += ecoli_tk_many.c -srcs += ecoli_tk_option.c -srcs += ecoli_tk_or.c -srcs += ecoli_tk_re.c -srcs += ecoli_tk_re_lex.c -srcs += ecoli_tk_seq.c -srcs += ecoli_tk_sh_lex.c -srcs += ecoli_tk_space.c -srcs += ecoli_tk_str.c -srcs += ecoli_tk_subset.c -srcs += ecoli_tk_weakref.c +srcs += ecoli_node.c +srcs += ecoli_node_cmd.c +srcs += ecoli_node_empty.c +srcs += ecoli_node_expr.c +srcs += ecoli_node_expr_test.c +srcs += ecoli_node_int.c +srcs += ecoli_node_many.c +srcs += ecoli_node_option.c +srcs += ecoli_node_or.c +srcs += ecoli_node_re.c +srcs += ecoli_node_re_lex.c +srcs += ecoli_node_seq.c +srcs += ecoli_node_sh_lex.c +srcs += ecoli_node_space.c +srcs += ecoli_node_str.c +srcs += ecoli_node_subset.c +srcs += ecoli_node_weakref.c shlib-y-$(O)libecoli.so := $(srcs) diff --git a/lib/ecoli_node.c b/lib/ecoli_node.c new file mode 100644 index 0000000..c9315fa --- /dev/null +++ b/lib/ecoli_node.c @@ -0,0 +1,719 @@ +/* + * Copyright (c) 2016, Olivier MATZ + * + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +static struct ec_node_type_list node_type_list = + TAILQ_HEAD_INITIALIZER(node_type_list); + +struct ec_node_type *ec_node_type_lookup(const char *name) +{ + struct ec_node_type *type; + + TAILQ_FOREACH(type, &node_type_list, next) { + if (!strcmp(name, type->name)) + return type; + } + + return NULL; +} + +int ec_node_type_register(struct ec_node_type *type) +{ + if (ec_node_type_lookup(type->name) != NULL) + return -EEXIST; + if (type->size < sizeof(struct ec_node)) + return -EINVAL; + + TAILQ_INSERT_TAIL(&node_type_list, type, next); + + return 0; +} + +void ec_node_type_dump(FILE *out) +{ + struct ec_node_type *type; + + TAILQ_FOREACH(type, &node_type_list, next) + fprintf(out, "%s\n", type->name); +} + +struct ec_node *__ec_node_new(const struct ec_node_type *type, const char *id) +{ + struct ec_node *node = NULL; + char buf[256]; // XXX + + ec_log(EC_LOG_DEBUG, "create node type=%s id=%s\n", type->name, id); + + node = ec_calloc(1, type->size); + if (node == NULL) + goto fail; + + TAILQ_INIT(&node->children); + node->type = type; + node->refcnt = 1; + + if (id != NULL) { + node->id = ec_strdup(id); + if (node->id == NULL) + goto fail; + } + + snprintf(buf, sizeof(buf), "<%s>", type->name); + node->desc = ec_strdup(buf); // XXX ec_asprintf ? + if (node->desc == NULL) + goto fail; + + node->attrs = ec_keyval_new(); + if (node->attrs == NULL) + goto fail; + + return node; + + fail: + ec_node_free(node); + return NULL; +} + +struct ec_node *ec_node_new(const char *typename, const char *id) +{ + struct ec_node_type *type; + + type = ec_node_type_lookup(typename); + if (type == NULL) { + ec_log(EC_LOG_ERR, "type=%s does not exist\n", typename); + return NULL; + } + + return __ec_node_new(type, id); +} + +void ec_node_free(struct ec_node *node) +{ + if (node == NULL) + return; + + assert(node->refcnt > 0); + + if (--node->refcnt > 0) + return; + + if (node->type != NULL && node->type->free_priv != NULL) + node->type->free_priv(node); + ec_free(node->id); + ec_free(node->desc); + ec_free(node->attrs); + ec_free(node); +} + +struct ec_node *ec_node_clone(struct ec_node *node) +{ + if (node != NULL) + node->refcnt++; + return node; +} + +struct ec_node *ec_node_find(struct ec_node *node, const char *id) +{ + struct ec_node *child, *ret; + const char *node_id = ec_node_id(node); + + if (id != NULL && node_id != NULL && !strcmp(node_id, id)) + return node; + + TAILQ_FOREACH(child, &node->children, next) { + ret = ec_node_find(child, id); + if (ret != NULL) + return ret; + } + + return NULL; +} + +struct ec_keyval *ec_node_attrs(const struct ec_node *node) +{ + return node->attrs; +} + +const char *ec_node_id(const struct ec_node *node) +{ + return node->id; +} + +struct ec_node *ec_node_parent(const struct ec_node *node) +{ + return node->parent; +} + +static void __ec_node_dump(FILE *out, + const struct ec_node *node, size_t indent) +{ + struct ec_node *child; + size_t i; + const char *id = "None", *typename = "None"; + + if (node->id != NULL) + id = node->id; + typename = node->type->name; + + /* XXX enhance */ + for (i = 0; i < indent; i++) { + if (i % 2) + fprintf(out, " "); + else + fprintf(out, "|"); + } + + fprintf(out, "node_type=%s id=%s\n", typename, id); + TAILQ_FOREACH(child, &node->children, next) + __ec_node_dump(out, child, indent + 2); +} + +void ec_node_dump(FILE *out, const struct ec_node *node) +{ + fprintf(out, "------------------- node dump:\n"); //XXX + + if (node == NULL) { + fprintf(out, "node is NULL\n"); + return; + } + + __ec_node_dump(out, node, 0); +} + +struct ec_parsed *ec_node_parse(struct ec_node *node, const char *str) +{ + struct ec_strvec *strvec = NULL; + struct ec_parsed *parsed; + + errno = ENOMEM; + strvec = ec_strvec_new(); + if (strvec == NULL) + goto fail; + + if (ec_strvec_add(strvec, str) < 0) + goto fail; + + parsed = ec_node_parse_strvec(node, strvec); + if (parsed == NULL) + goto fail; + + ec_strvec_free(strvec); + return parsed; + + fail: + ec_strvec_free(strvec); + return NULL; +} + +struct ec_parsed *ec_node_parse_strvec(struct ec_node *node, + const struct ec_strvec *strvec) +{ + struct ec_parsed *parsed; + int ret; + + /* build the node if required */ + if (node->type->build != NULL) { + if ((node->flags & EC_NODE_F_BUILT) == 0) { + ret = node->type->build(node); + if (ret < 0) { + errno = -ret; + return NULL; + } + } + } + node->flags |= EC_NODE_F_BUILT; + + if (node->type->parse == NULL) { + errno = ENOTSUP; + return NULL; + } + + parsed = node->type->parse(node, strvec); + + return parsed; +} + + +struct ec_parsed *ec_parsed_new(void) +{ + struct ec_parsed *parsed = NULL; + + parsed = ec_calloc(1, sizeof(*parsed)); + if (parsed == NULL) + goto fail; + + TAILQ_INIT(&parsed->children); + + return parsed; + + fail: + return NULL; +} + +void ec_parsed_set_match(struct ec_parsed *parsed, + const struct ec_node *node, struct ec_strvec *strvec) +{ + parsed->node = node; + parsed->strvec = strvec; +} + +void ec_parsed_free_children(struct ec_parsed *parsed) +{ + struct ec_parsed *child; + + if (parsed == NULL) + return; + + while (!TAILQ_EMPTY(&parsed->children)) { + child = TAILQ_FIRST(&parsed->children); + TAILQ_REMOVE(&parsed->children, child, next); + ec_parsed_free(child); + } +} + +void ec_parsed_free(struct ec_parsed *parsed) +{ + if (parsed == NULL) + return; + + ec_parsed_free_children(parsed); + ec_strvec_free(parsed->strvec); + ec_free(parsed); +} + +static void __ec_parsed_dump(FILE *out, + const struct ec_parsed *parsed, size_t indent) +{ + struct ec_parsed *child; + const struct ec_strvec *vec; + size_t i; + const char *id = "None", *typename = "None"; + + if (parsed->node != NULL) { + if (parsed->node->id != NULL) + id = parsed->node->id; + typename = parsed->node->type->name; + } + + /* XXX enhance */ + for (i = 0; i < indent; i++) { + if (i % 2) + fprintf(out, " "); + else + fprintf(out, "|"); + } + + fprintf(out, "node_type=%s id=%s vec=[", typename, id); + vec = ec_parsed_strvec(parsed); + for (i = 0; i < ec_strvec_len(vec); i++) + fprintf(out, "%s<%s>", + i == 0 ? "" : ",", + ec_strvec_val(vec, i)); + fprintf(out, "]\n"); + + TAILQ_FOREACH(child, &parsed->children, next) + __ec_parsed_dump(out, child, indent + 2); +} + +void ec_parsed_dump(FILE *out, const struct ec_parsed *parsed) +{ + fprintf(out, "------------------- parsed dump:\n"); //XXX + + if (parsed == NULL) { + fprintf(out, "parsed is NULL, error in parse\n"); + return; + } + if (!ec_parsed_matches(parsed)) { + fprintf(out, "no match\n"); + return; + } + + __ec_parsed_dump(out, parsed, 0); +} + +void ec_parsed_add_child(struct ec_parsed *parsed, + struct ec_parsed *child) +{ + TAILQ_INSERT_TAIL(&parsed->children, child, next); +} + +void ec_parsed_del_child(struct ec_parsed *parsed, + struct ec_parsed *child) +{ + TAILQ_REMOVE(&parsed->children, child, next); +} + +struct ec_parsed *ec_parsed_find_first(struct ec_parsed *parsed, + const char *id) +{ + struct ec_parsed *child, *ret; + + if (parsed == NULL) + return NULL; + + if (parsed->node != NULL && + parsed->node->id != NULL && + !strcmp(parsed->node->id, id)) + return parsed; + + TAILQ_FOREACH(child, &parsed->children, next) { + ret = ec_parsed_find_first(child, id); + if (ret != NULL) + return ret; + } + + return NULL; +} + +const struct ec_strvec *ec_parsed_strvec( + const struct ec_parsed *parsed) +{ + if (parsed == NULL || parsed->strvec == NULL) + return NULL; + + return parsed->strvec; +} + +/* number of parsed strings in the vector */ +size_t ec_parsed_len(const struct ec_parsed *parsed) +{ + if (parsed == NULL || parsed->strvec == NULL) + return 0; + + return ec_strvec_len(parsed->strvec); +} + +size_t ec_parsed_matches(const struct ec_parsed *parsed) +{ + if (parsed == NULL) + return 0; + + if (parsed->node == NULL && TAILQ_EMPTY(&parsed->children)) + return 0; + + return 1; +} + +struct ec_completed *ec_completed_new(void) +{ + struct ec_completed *completed = NULL; + + completed = ec_calloc(1, sizeof(*completed)); + if (completed == NULL) + return NULL; + + TAILQ_INIT(&completed->elts); + completed->count_match = 0; + + return completed; +} + +struct ec_completed_elt *ec_completed_elt_new(const struct ec_node *node, + const char *add) +{ + struct ec_completed_elt *elt = NULL; + + elt = ec_calloc(1, sizeof(*elt)); + if (elt == NULL) + return NULL; + + elt->node = node; + if (add != NULL) { + elt->add = ec_strdup(add); + if (elt->add == NULL) { + ec_completed_elt_free(elt); + return NULL; + } + } + + return elt; +} + +/* XXX define when to use ec_node_complete() or node->complete() + * (same for parse) + * suggestion: node->op() is internal, user calls the function + * other idea: have 2 functions + */ +struct ec_completed *ec_node_complete(struct ec_node *node, + const char *str) +{ + struct ec_strvec *strvec = NULL; + struct ec_completed *completed; + + errno = ENOMEM; + strvec = ec_strvec_new(); + if (strvec == NULL) + goto fail; + + if (ec_strvec_add(strvec, str) < 0) + goto fail; + + completed = ec_node_complete_strvec(node, strvec); + if (completed == NULL) + goto fail; + + ec_strvec_free(strvec); + return completed; + + fail: + ec_strvec_free(strvec); + return NULL; +} + +/* default completion function: return a no-match element */ +struct ec_completed *ec_node_default_complete(const struct ec_node *gen_node, + const struct ec_strvec *strvec) +{ + struct ec_completed *completed; + struct ec_completed_elt *completed_elt; + + (void)strvec; + + completed = ec_completed_new(); + if (completed == NULL) + return NULL; + + completed_elt = ec_completed_elt_new(gen_node, NULL); + if (completed_elt == NULL) { + ec_completed_free(completed); + return NULL; + } + + ec_completed_add_elt(completed, completed_elt); + + return completed; +} + +struct ec_completed *ec_node_complete_strvec(struct ec_node *node, + const struct ec_strvec *strvec) +{ + int ret; + + /* build the node if required */ + if (node->type->build != NULL) { + if ((node->flags & EC_NODE_F_BUILT) == 0) { + ret = node->type->build(node); + if (ret < 0) { + errno = -ret; + return NULL; + } + } + } + node->flags |= EC_NODE_F_BUILT; + + if (node->type->complete == NULL) { + errno = ENOTSUP; + return NULL; + } + + return node->type->complete(node, strvec); +} + +/* count the number of identical chars at the beginning of 2 strings */ +static size_t strcmp_count(const char *s1, const char *s2) +{ + size_t i = 0; + + while (s1[i] && s2[i] && s1[i] == s2[i]) + i++; + + return i; +} + +void ec_completed_add_elt( + struct ec_completed *completed, struct ec_completed_elt *elt) +{ + size_t n; + + TAILQ_INSERT_TAIL(&completed->elts, elt, next); + completed->count++; + if (elt->add != NULL) + completed->count_match++; + if (elt->add != NULL) { + if (completed->smallest_start == NULL) { + completed->smallest_start = ec_strdup(elt->add); + } else { + n = strcmp_count(elt->add, + completed->smallest_start); + completed->smallest_start[n] = '\0'; + } + } +} + +void ec_completed_elt_free(struct ec_completed_elt *elt) +{ + ec_free(elt->add); + ec_free(elt); +} + +void ec_completed_merge(struct ec_completed *completed1, + struct ec_completed *completed2) +{ + struct ec_completed_elt *elt; + + assert(completed1 != NULL); + assert(completed2 != NULL); + + while (!TAILQ_EMPTY(&completed2->elts)) { + elt = TAILQ_FIRST(&completed2->elts); + TAILQ_REMOVE(&completed2->elts, elt, next); + ec_completed_add_elt(completed1, elt); + } + + ec_completed_free(completed2); +} + +void ec_completed_free(struct ec_completed *completed) +{ + struct ec_completed_elt *elt; + + if (completed == NULL) + return; + + while (!TAILQ_EMPTY(&completed->elts)) { + elt = TAILQ_FIRST(&completed->elts); + TAILQ_REMOVE(&completed->elts, elt, next); + ec_completed_elt_free(elt); + } + ec_free(completed->smallest_start); + ec_free(completed); +} + +void ec_completed_dump(FILE *out, const struct ec_completed *completed) +{ + struct ec_completed_elt *elt; + + if (completed == NULL || completed->count == 0) { + fprintf(out, "no completion\n"); + return; + } + + fprintf(out, "completion: count=%u match=%u smallest_start=<%s>\n", + completed->count, completed->count_match, + completed->smallest_start); + + TAILQ_FOREACH(elt, &completed->elts, next) { + fprintf(out, "add=<%s>, node=%p, node_type=%s\n", + elt->add, elt->node, elt->node->type->name); + } +} + +const char *ec_completed_smallest_start( + const struct ec_completed *completed) +{ + if (completed == NULL || completed->smallest_start == NULL) + return ""; + + return completed->smallest_start; +} + +unsigned int ec_completed_count( + const struct ec_completed *completed, + enum ec_completed_filter_flags flags) +{ + unsigned int count = 0; + + if (completed == NULL) + return count; + + if (flags & EC_MATCH) + count += completed->count_match; + if (flags & EC_NO_MATCH) + count += (completed->count - completed->count_match); //XXX + + return count; +} + +struct ec_completed_iter * +ec_completed_iter_new(struct ec_completed *completed, + enum ec_completed_filter_flags flags) +{ + struct ec_completed_iter *iter; + + iter = ec_calloc(1, sizeof(*iter)); + if (iter == NULL) + return NULL; + + iter->completed = completed; + iter->flags = flags; + iter->cur = NULL; + + return iter; +} + +const struct ec_completed_elt *ec_completed_iter_next( + struct ec_completed_iter *iter) +{ + if (iter->completed == NULL) + return NULL; + + do { + if (iter->cur == NULL) { + iter->cur = TAILQ_FIRST(&iter->completed->elts); + } else { + iter->cur = TAILQ_NEXT(iter->cur, next); + } + + if (iter->cur == NULL) + break; + + if (iter->cur->add == NULL && + (iter->flags & EC_NO_MATCH)) + break; + + if (iter->cur->add != NULL && + (iter->flags & EC_MATCH)) + break; + + } while (iter->cur != NULL); + + return iter->cur; +} + +void ec_completed_iter_free(struct ec_completed_iter *iter) +{ + ec_free(iter); +} + +const char *ec_node_desc(const struct ec_node *node) +{ + if (node->type->desc != NULL) + return node->type->desc(node); + + return node->desc; +} diff --git a/lib/ecoli_node.h b/lib/ecoli_node.h new file mode 100644 index 0000000..ad24a45 --- /dev/null +++ b/lib/ecoli_node.h @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2016, Olivier MATZ + * + * 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 +#include +#include + +#define EC_NODE_ENDLIST ((void *)1) + +struct ec_node; +struct ec_parsed; +struct ec_strvec; +struct ec_keyval; + +/* return 0 on success, else -errno. */ +typedef int (*ec_node_build_t)(struct ec_node *node); + +typedef struct ec_parsed *(*ec_node_parse_t)(const struct ec_node *node, + const struct ec_strvec *strvec); +typedef struct ec_completed *(*ec_node_complete_t)(const struct ec_node *node, + const struct ec_strvec *strvec); +typedef const char * (*ec_node_desc_t)(const struct ec_node *); +typedef void (*ec_node_init_priv_t)(struct ec_node *); +typedef void (*ec_node_free_priv_t)(struct ec_node *); + +#define EC_NODE_TYPE_REGISTER(t) \ + static void ec_node_init_##t(void); \ + static void __attribute__((constructor, used)) \ + ec_node_init_##t(void) \ + { \ + if (ec_node_type_register(&t) < 0) \ + fprintf(stderr, "cannot register %s\n", t.name); \ + } + +TAILQ_HEAD(ec_node_type_list, ec_node_type); + +/** + * A structure describing a node type. + */ +struct ec_node_type { + TAILQ_ENTRY(ec_node_type) next; /**< Next in list. */ + const char *name; /**< Node type name. */ + ec_node_build_t build; /* (re)build the node, called by generic parse */ + ec_node_parse_t parse; + ec_node_complete_t complete; + ec_node_desc_t desc; + size_t size; + ec_node_init_priv_t init_priv; + ec_node_free_priv_t free_priv; +}; + +/** + * Register a node type. + * + * @param type + * A pointer to a ec_test structure describing the test + * to be registered. + * @return + * 0 on success, negative value on error. + */ +int ec_node_type_register(struct ec_node_type *type); + +/** + * Lookup node type by name + * + * @param name + * The name of the node type to search. + * @return + * The node type if found, or NULL on error. + */ +struct ec_node_type *ec_node_type_lookup(const char *name); + +/** + * Dump registered log types + */ +void ec_node_type_dump(FILE *out); + +TAILQ_HEAD(ec_node_list, ec_node); + +struct ec_node { + const struct ec_node_type *type; + char *id; + char *desc; + struct ec_keyval *attrs; + /* XXX ensure parent and child are properly set in all nodes */ + struct ec_node *parent; + unsigned int refcnt; +#define EC_NODE_F_BUILT 0x0001 /** set if configuration is built */ + unsigned int flags; + + TAILQ_ENTRY(ec_node) next; + struct ec_node_list children; +}; + +/* create a new node when the type is known, typically called from the node + * code */ +struct ec_node *__ec_node_new(const struct ec_node_type *type, const char *id); + +/* create a_new node node */ +struct ec_node *ec_node_new(const char *typename, const char *id); + +void ec_node_free(struct ec_node *node); + +/* XXX add more accessors */ +struct ec_keyval *ec_node_attrs(const struct ec_node *node); +struct ec_node *ec_node_parent(const struct ec_node *node); +const char *ec_node_id(const struct ec_node *node); +const char *ec_node_desc(const struct ec_node *node); + +void ec_node_dump(FILE *out, const struct ec_node *node); +struct ec_node *ec_node_find(struct ec_node *node, const char *id); + +/* XXX split this file ? */ + +TAILQ_HEAD(ec_parsed_list, ec_parsed); + +/* + node == NULL + empty children list means "no match" +*/ +struct ec_parsed { + TAILQ_ENTRY(ec_parsed) next; + struct ec_parsed_list children; + const struct ec_node *node; + struct ec_strvec *strvec; +}; + +struct ec_parsed *ec_parsed_new(void); +void ec_parsed_free(struct ec_parsed *parsed); +struct ec_node *ec_node_clone(struct ec_node *node); +void ec_parsed_free_children(struct ec_parsed *parsed); + +const struct ec_strvec *ec_parsed_strvec( + const struct ec_parsed *parsed); + +void ec_parsed_set_match(struct ec_parsed *parsed, + const struct ec_node *node, struct ec_strvec *strvec); + +/* XXX we could use a cache to store possible completions or match: the + * cache would be per-node, and would be reset for each call to parse() + * or complete() ? */ +/* a NULL return value is an error, with errno set + ENOTSUP: no ->parse() operation +*/ +struct ec_parsed *ec_node_parse(struct ec_node *node, const char *str); + +/* mostly internal to nodes */ +/* XXX it should not reset cache + * ... not sure... it is used by tests */ +struct ec_parsed *ec_node_parse_strvec(struct ec_node *node, + const struct ec_strvec *strvec); + +void ec_parsed_add_child(struct ec_parsed *parsed, + struct ec_parsed *child); +void ec_parsed_del_child(struct ec_parsed *parsed, + struct ec_parsed *child); +void ec_parsed_dump(FILE *out, const struct ec_parsed *parsed); + +struct ec_parsed *ec_parsed_find_first(struct ec_parsed *parsed, + const char *id); + +const char *ec_parsed_to_string(const struct ec_parsed *parsed); +size_t ec_parsed_len(const struct ec_parsed *parsed); +size_t ec_parsed_matches(const struct ec_parsed *parsed); + +struct ec_completed_elt { + TAILQ_ENTRY(ec_completed_elt) next; + const struct ec_node *node; + char *add; +}; + +TAILQ_HEAD(ec_completed_elt_list, ec_completed_elt); + + +struct ec_completed { + struct ec_completed_elt_list elts; + unsigned count; + unsigned count_match; + char *smallest_start; +}; + +/* + * return a completed object filled with elts + * return NULL on error (nomem?) + */ +struct ec_completed *ec_node_complete(struct ec_node *node, + const char *str); +struct ec_completed *ec_node_complete_strvec(struct ec_node *node, + const struct ec_strvec *strvec); +struct ec_completed *ec_completed_new(void); +struct ec_completed_elt *ec_completed_elt_new(const struct ec_node *node, + const char *add); +void ec_completed_add_elt(struct ec_completed *completed, + struct ec_completed_elt *elt); +void ec_completed_elt_free(struct ec_completed_elt *elt); +void ec_completed_merge(struct ec_completed *completed1, + struct ec_completed *completed2); +void ec_completed_free(struct ec_completed *completed); +void ec_completed_dump(FILE *out, + const struct ec_completed *completed); +struct ec_completed *ec_node_default_complete(const struct ec_node *gen_node, + const struct ec_strvec *strvec); + +/* cannot return NULL */ +const char *ec_completed_smallest_start( + const struct ec_completed *completed); + +enum ec_completed_filter_flags { + EC_MATCH = 1, + EC_NO_MATCH = 2, +}; + +unsigned int ec_completed_count( + const struct ec_completed *completed, + enum ec_completed_filter_flags flags); + +struct ec_completed_iter { + enum ec_completed_filter_flags flags; + const struct ec_completed *completed; + const struct ec_completed_elt *cur; +}; + +struct ec_completed_iter * +ec_completed_iter_new(struct ec_completed *completed, + enum ec_completed_filter_flags flags); + +const struct ec_completed_elt *ec_completed_iter_next( + struct ec_completed_iter *iter); + +void ec_completed_iter_free(struct ec_completed_iter *iter); + + +#endif diff --git a/lib/ecoli_node_cmd.c b/lib/ecoli_node_cmd.c new file mode 100644 index 0000000..d1d2c7d --- /dev/null +++ b/lib/ecoli_node_cmd.c @@ -0,0 +1,506 @@ +/* + * Copyright (c) 2016-2017, Olivier MATZ + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct ec_node_cmd { + struct ec_node gen; + char *cmd_str; /* the command string. */ + struct ec_node *cmd; /* the command node. */ + struct ec_node *lex; /* the lexer node. */ + struct ec_node *expr; /* the expression parser. */ + struct ec_node **table; /* table of node referenced in command. */ + unsigned int len; /* len of the table. */ +}; + +static int +ec_node_cmd_eval_var(void **result, void *userctx, + const struct ec_parsed *var) +{ + const struct ec_strvec *vec; + struct ec_node_cmd *node = userctx; + struct ec_node *eval = NULL; + const char *str, *id; + unsigned int i; + + (void)userctx; + + /* get parsed string vector, it should contain only one str */ + vec = ec_parsed_strvec(var); + if (ec_strvec_len(vec) != 1) + return -EINVAL; + str = ec_strvec_val(vec, 0); + + for (i = 0; i < node->len; i++) { + id = ec_node_id(node->table[i]); + printf("i=%d id=%s\n", i, id); + if (id == NULL) + continue; + if (strcmp(str, id)) + continue; + /* if id matches, use a node provided by the user... */ + eval = ec_node_clone(node->table[i]); + if (eval == NULL) + return -ENOMEM; + break; + } + + /* ...or create a string node */ + if (eval == NULL) { + eval = ec_node_str(NULL, str); + if (eval == NULL) + return -ENOMEM; + } + + printf("eval var %s %p\n", str, eval); + *result = eval; + + return 0; +} + +static int +ec_node_cmd_eval_pre_op(void **result, void *userctx, void *operand, + const struct ec_parsed *operator) +{ + (void)result; + (void)userctx; + (void)operand; + (void)operator; + + return -EINVAL; +} + +static int +ec_node_cmd_eval_post_op(void **result, void *userctx, void *operand, + const struct ec_parsed *operator) +{ + const struct ec_strvec *vec; + struct ec_node *eval = operand;; + + (void)userctx; + + /* get parsed string vector, it should contain only one str */ + vec = ec_parsed_strvec(operator); + if (ec_strvec_len(vec) != 1) + return -EINVAL; + + if (!strcmp(ec_strvec_val(vec, 0), "*")) + eval = NULL; //XXX + else + return -EINVAL; + + printf("eval post_op %p\n", eval); + *result = eval; + + return 0; +} + +static int +ec_node_cmd_eval_bin_op(void **result, void *userctx, void *operand1, + const struct ec_parsed *operator, void *operand2) + +{ + const struct ec_strvec *vec; + struct ec_node *out = NULL; + struct ec_node *in1 = operand1; + struct ec_node *in2 = operand2; + + (void)userctx; + + printf("eval bin_op %p %p\n", in1, in2); + + /* get parsed string vector, it should contain only one str */ + vec = ec_parsed_strvec(operator); + if (ec_strvec_len(vec) != 1) + return -EINVAL; + + if (!strcmp(ec_strvec_val(vec, 0), "|")) { + out = EC_NODE_OR(NULL, ec_node_clone(in1), ec_node_clone(in2)); + if (out == NULL) + return -EINVAL; + ec_node_free(in1); + ec_node_free(in2); + *result = out; + } else if (!strcmp(ec_strvec_val(vec, 0), ",")) { + out = EC_NODE_SUBSET(NULL, ec_node_clone(in1), ec_node_clone(in2)); + if (out == NULL) + return -EINVAL; + ec_node_free(in1); + ec_node_free(in2); + *result = out; + } else { + return -EINVAL; + } + + return 0; +} + +static int +ec_node_cmd_eval_parenthesis(void **result, void *userctx, + const struct ec_parsed *open_paren, + const struct ec_parsed *close_paren, + void *value) +{ + const struct ec_strvec *vec; + struct ec_node *in = value;; + struct ec_node *out = NULL;; + + (void)userctx; + (void)close_paren; + + /* get parsed string vector, it should contain only one str */ + vec = ec_parsed_strvec(open_paren); + if (ec_strvec_len(vec) != 1) + return -EINVAL; + + if (!strcmp(ec_strvec_val(vec, 0), "[")) { + out = ec_node_option(NULL, ec_node_clone(in)); + if (out == NULL) + return -EINVAL; + ec_node_free(in); + } else if (!strcmp(ec_strvec_val(vec, 0), "(")) { + out = in; + } else { + return -EINVAL; + } + + printf("eval paren\n"); + *result = out; + + return 0; +} + +static void +ec_node_cmd_eval_free(void *result, void *userctx) +{ + (void)userctx; + ec_free(result); +} + +static const struct ec_node_expr_eval_ops test_ops = { + .eval_var = ec_node_cmd_eval_var, + .eval_pre_op = ec_node_cmd_eval_pre_op, + .eval_post_op = ec_node_cmd_eval_post_op, + .eval_bin_op = ec_node_cmd_eval_bin_op, + .eval_parenthesis = ec_node_cmd_eval_parenthesis, + .eval_free = ec_node_cmd_eval_free, +}; + +static struct ec_parsed *ec_node_cmd_parse(const struct ec_node *gen_node, + const struct ec_strvec *strvec) +{ + struct ec_node_cmd *node = (struct ec_node_cmd *)gen_node; + + return ec_node_parse_strvec(node->cmd, strvec); +} + +static struct ec_completed *ec_node_cmd_complete(const struct ec_node *gen_node, + const struct ec_strvec *strvec) +{ + struct ec_node_cmd *node = (struct ec_node_cmd *)gen_node; + + return ec_node_complete_strvec(node->cmd, strvec); +} + +static void ec_node_cmd_free_priv(struct ec_node *gen_node) +{ + struct ec_node_cmd *node = (struct ec_node_cmd *)gen_node; + unsigned int i; + + ec_free(node->cmd_str); + ec_node_free(node->cmd); + ec_node_free(node->expr); + ec_node_free(node->lex); + for (i = 0; i < node->len; i++) + ec_node_free(node->table[i]); + ec_free(node->table); +} + +static int ec_node_cmd_build(struct ec_node *gen_node) +{ + struct ec_node *expr = NULL, *lex = NULL, *cmd = NULL; + struct ec_parsed *p, *child; + struct ec_node_cmd *node = (struct ec_node_cmd *)gen_node; + void *result; + int ret; + + /* build the expression parser */ + ret = -ENOMEM; + expr = ec_node_new("expr", "expr"); + if (expr == NULL) + goto fail; + ret = ec_node_expr_set_val_node(expr, ec_node_re(NULL, "[a-zA-Z0-9]+")); + if (ret < 0) + goto fail; + ret = ec_node_expr_add_bin_op(expr, ec_node_str(NULL, ",")); + if (ret < 0) + goto fail; + ret = ec_node_expr_add_bin_op(expr, ec_node_str(NULL, "|")); + if (ret < 0) + goto fail; + ret = ec_node_expr_add_post_op(expr, ec_node_str(NULL, "+")); + if (ret < 0) + goto fail; + ret = ec_node_expr_add_post_op(expr, ec_node_str(NULL, "*")); + if (ret < 0) + goto fail; + ret = ec_node_expr_add_parenthesis(expr, ec_node_str(NULL, "["), + ec_node_str(NULL, "]")); + if (ret < 0) + goto fail; + ec_node_expr_add_parenthesis(expr, ec_node_str(NULL, "("), + ec_node_str(NULL, ")")); + if (ret < 0) + goto fail; + + /* prepend a lexer and a "many" to the expression node */ + ret = -ENOMEM; + lex = ec_node_re_lex(NULL, + ec_node_many(NULL, ec_node_clone(expr), 1, 0)); + if (lex == NULL) + goto fail; + + ret = ec_node_re_lex_add(lex, "[a-zA-Z0-9]+", 1); + if (ret < 0) + goto fail; + ret = ec_node_re_lex_add(lex, "[*|,()]", 1); + if (ret < 0) + goto fail; + ret = ec_node_re_lex_add(lex, "\\[", 1); + if (ret < 0) + goto fail; + ret = ec_node_re_lex_add(lex, "\\]", 1); + if (ret < 0) + goto fail; + ret = ec_node_re_lex_add(lex, "[ ]+", 0); + if (ret < 0) + goto fail; + + /* parse the command expression */ + ret = -ENOMEM; + p = ec_node_parse(lex, node->cmd_str); + if (p == NULL) + goto fail; + + ret = -EINVAL; + if (!ec_parsed_matches(p)) + goto fail; + if (TAILQ_EMPTY(&p->children)) + goto fail; + if (TAILQ_EMPTY(&TAILQ_FIRST(&p->children)->children)) + goto fail; + + ret = -ENOMEM; + cmd = ec_node_new("seq", NULL); + if (cmd == NULL) + goto fail; + + TAILQ_FOREACH(child, &TAILQ_FIRST(&p->children)->children, next) { + ret = ec_node_expr_eval(&result, expr, child, + &test_ops, node); + if (ret < 0) + goto fail; + ret = ec_node_seq_add(cmd, result); + if (ret < 0) + goto fail; + } + ec_parsed_free(p); + ec_node_dump(stdout, cmd); + + ec_node_free(node->expr); + node->expr = expr; + ec_node_free(node->lex); + node->lex = lex; + ec_node_free(node->cmd); + node->cmd = cmd; + + return 0; + +fail: + ec_node_free(expr); + ec_node_free(lex); + ec_node_free(cmd); + return ret; +} + +static struct ec_node_type ec_node_cmd_type = { + .name = "cmd", + .build = ec_node_cmd_build, + .parse = ec_node_cmd_parse, + .complete = ec_node_cmd_complete, + .size = sizeof(struct ec_node_cmd), + .free_priv = ec_node_cmd_free_priv, +}; + +EC_NODE_TYPE_REGISTER(ec_node_cmd_type); + +int ec_node_cmd_add_child(struct ec_node *gen_node, struct ec_node *child) +{ + struct ec_node_cmd *node = (struct ec_node_cmd *)gen_node; + struct ec_node **table; + + // XXX check node type + + assert(node != NULL); + + printf("add child %s\n", child->id); + if (child == NULL) + return -EINVAL; + + gen_node->flags &= ~EC_NODE_F_BUILT; + + table = ec_realloc(node->table, (node->len + 1) * sizeof(*node->table)); + if (table == NULL) { + ec_node_free(child); + return -ENOMEM; + } + + node->table = table; + table[node->len] = child; + node->len++; + + child->parent = gen_node; + TAILQ_INSERT_TAIL(&gen_node->children, child, next); // XXX really needed? + + return 0; +} + +struct ec_node *ec_node_cmd(const char *id, const char *cmd_str) +{ + struct ec_node *gen_node = NULL; + struct ec_node_cmd *node = NULL; + + gen_node = __ec_node_new(&ec_node_cmd_type, id); + if (gen_node == NULL) + goto fail; + + node = (struct ec_node_cmd *)gen_node; + node->cmd_str = ec_strdup(cmd_str); + if (node->cmd_str == NULL) + goto fail; + + return gen_node; + +fail: + ec_node_free(gen_node); + return NULL; +} + +struct ec_node *__ec_node_cmd(const char *id, const char *cmd, ...) +{ + struct ec_node *gen_node = NULL; + struct ec_node_cmd *node = NULL; + struct ec_node *child; + va_list ap; + int fail = 0; + + va_start(ap, cmd); + + gen_node = ec_node_cmd(id, cmd); + node = (struct ec_node_cmd *)gen_node; + if (node == NULL) + fail = 1;; + + for (child = va_arg(ap, struct ec_node *); + child != EC_NODE_ENDLIST; + child = va_arg(ap, struct ec_node *)) { + + /* on error, don't quit the loop to avoid leaks */ + if (fail == 1 || child == NULL || + ec_node_cmd_add_child(&node->gen, child) < 0) { + fail = 1; + ec_node_free(child); + } + } + + if (fail == 1) + goto fail; + + va_end(ap); + return gen_node; + +fail: + ec_node_free(gen_node); /* will also free children */ + va_end(ap); + return NULL; +} + +static int ec_node_cmd_testcase(void) +{ + struct ec_node *node; + int ret = 0; + + node = EC_NODE_CMD(NULL, + "command [option] (subset1, subset2) x | y", + ec_node_int("x", 0, 10, 10), + ec_node_int("y", 20, 30, 10) + ); + if (node == NULL) { + ec_log(EC_LOG_ERR, "cannot create node\n"); + return -1; + } + ret |= EC_TEST_CHECK_PARSE(node, 2, "command", "1"); + ret |= EC_TEST_CHECK_PARSE(node, 2, "command", "23"); + ret |= EC_TEST_CHECK_PARSE(node, 3, "command", "option", "23"); + ret |= EC_TEST_CHECK_PARSE(node, -1, "command", "15"); + ret |= EC_TEST_CHECK_PARSE(node, -1, "foo"); + ec_node_free(node); + + // XXX completion + + return ret; +} + +static struct ec_test ec_node_cmd_test = { + .name = "node_cmd", + .test = ec_node_cmd_testcase, +}; + +EC_TEST_REGISTER(ec_node_cmd_test); diff --git a/lib/ecoli_node_cmd.h b/lib/ecoli_node_cmd.h new file mode 100644 index 0000000..2c36f38 --- /dev/null +++ b/lib/ecoli_node_cmd.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2016-2017, Olivier MATZ + * + * 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 + +#define EC_NODE_CMD(args...) __ec_node_cmd(args, EC_NODE_ENDLIST) + +struct ec_node *__ec_node_cmd(const char *id, const char *cmd_str, ...); + +struct ec_node *ec_node_cmd(const char *id, const char *cmd_str); + +/* child is consumed */ +int ec_node_cmd_add_child(struct ec_node *node, struct ec_node *child); + +#endif diff --git a/lib/ecoli_node_empty.c b/lib/ecoli_node_empty.c new file mode 100644 index 0000000..186a106 --- /dev/null +++ b/lib/ecoli_node_empty.c @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2016, Olivier MATZ + * + * 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 +#include +#include + +#include +#include +#include +#include +#include +#include + +struct ec_node_empty { + struct ec_node gen; +}; + +static struct ec_parsed *ec_node_empty_parse(const struct ec_node *gen_node, + const struct ec_strvec *strvec) +{ + struct ec_parsed *parsed; + struct ec_strvec *match_strvec; + + (void)strvec; + + parsed = ec_parsed_new(); + if (parsed == NULL) + goto fail; + + match_strvec = ec_strvec_new(); + if (match_strvec == NULL) + goto fail; + + ec_parsed_set_match(parsed, gen_node, match_strvec); + + return parsed; + + fail: + ec_parsed_free(parsed); + return NULL; +} + +static struct ec_node_type ec_node_empty_type = { + .name = "empty", + .parse = ec_node_empty_parse, + .complete = ec_node_default_complete, + .size = sizeof(struct ec_node_empty), +}; + +EC_NODE_TYPE_REGISTER(ec_node_empty_type); + +static int ec_node_empty_testcase(void) +{ + struct ec_node *node; + int ret = 0; + + node = ec_node_new("empty", NULL); + if (node == NULL) { + ec_log(EC_LOG_ERR, "cannot create node\n"); + return -1; + } + ret |= EC_TEST_CHECK_PARSE(node, 0, "foo"); + ret |= EC_TEST_CHECK_PARSE(node, 0); + ret |= EC_TEST_CHECK_PARSE(node, 0, "foo", "bar"); + ec_node_free(node); + + /* never completes */ + node = ec_node_new("empty", NULL); + if (node == NULL) { + ec_log(EC_LOG_ERR, "cannot create node\n"); + return -1; + } + ret |= EC_TEST_CHECK_COMPLETE(node, + "", EC_NODE_ENDLIST, + EC_NODE_ENDLIST, + ""); + ret |= EC_TEST_CHECK_COMPLETE(node, + "foo", EC_NODE_ENDLIST, + EC_NODE_ENDLIST, + ""); + ec_node_free(node); + + return ret; +} + +static struct ec_test ec_node_empty_test = { + .name = "node_empty", + .test = ec_node_empty_testcase, +}; + +EC_TEST_REGISTER(ec_node_empty_test); diff --git a/lib/ecoli_node_empty.h b/lib/ecoli_node_empty.h new file mode 100644 index 0000000..0ef2197 --- /dev/null +++ b/lib/ecoli_node_empty.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2016, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * This node always matches an empty string vector + */ + +#ifndef ECOLI_NODE_EMPTY_ +#define ECOLI_NODE_EMPTY_ + +struct ec_node *ec_node_empty(const char *id); + +#endif diff --git a/lib/ecoli_node_expr.c b/lib/ecoli_node_expr.c new file mode 100644 index 0000000..f80e3ba --- /dev/null +++ b/lib/ecoli_node_expr.c @@ -0,0 +1,598 @@ +/* + * Copyright (c) 2016-2017, Olivier MATZ + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct ec_node_expr { + struct ec_node gen; + + /* the built node */ + struct ec_node *child; + + /* the configuration nodes */ + struct ec_node *val_node; + struct ec_node **bin_ops; + unsigned int bin_ops_len; + struct ec_node **pre_ops; + unsigned int pre_ops_len; + struct ec_node **post_ops; + unsigned int post_ops_len; + struct ec_node **open_ops; + struct ec_node **close_ops; + unsigned int paren_len; +}; + +static struct ec_parsed *ec_node_expr_parse(const struct ec_node *gen_node, + const struct ec_strvec *strvec) +{ + struct ec_node_expr *node = (struct ec_node_expr *)gen_node; + + return ec_node_parse_strvec(node->child, strvec); +} + +static struct ec_completed *ec_node_expr_complete(const struct ec_node *gen_node, + const struct ec_strvec *strvec) +{ + struct ec_node_expr *node = (struct ec_node_expr *)gen_node; + + return ec_node_complete_strvec(node->child, strvec); +} + +static void ec_node_expr_free_priv(struct ec_node *gen_node) +{ + struct ec_node_expr *node = (struct ec_node_expr *)gen_node; + unsigned int i; + + ec_log(EC_LOG_DEBUG, "free %p %p %p\n", node, node->child, node->val_node); + ec_node_free(node->val_node); + + for (i = 0; i < node->bin_ops_len; i++) + ec_node_free(node->bin_ops[i]); + ec_free(node->bin_ops); + for (i = 0; i < node->pre_ops_len; i++) + ec_node_free(node->pre_ops[i]); + ec_free(node->pre_ops); + for (i = 0; i < node->post_ops_len; i++) + ec_node_free(node->post_ops[i]); + ec_free(node->post_ops); + for (i = 0; i < node->paren_len; i++) { + ec_node_free(node->open_ops[i]); + ec_node_free(node->close_ops[i]); + } + ec_free(node->open_ops); + ec_free(node->close_ops); + + ec_node_free(node->child); +} + +static int ec_node_expr_build(struct ec_node *gen_node) +{ + struct ec_node_expr *node = (struct ec_node_expr *)gen_node; + struct ec_node *term = NULL, *expr = NULL, *next = NULL, + *pre_op = NULL, *post_op = NULL, + *post = NULL, *weak = NULL; + unsigned int i; + int ret; + + if (node->val_node == NULL) + return -EINVAL; + if (node->bin_ops_len == 0 && node->pre_ops_len == 0 && + node->post_ops_len == 0) + return -EINVAL; + + /* create the object, we will initialize it later: this is + * needed because we have a circular dependency */ + ret = -ENOMEM; + weak = ec_node_new("weakref", "weak"); + if (weak == NULL) + return -1; + + /* prefix unary operators */ + pre_op = ec_node_new("or", "pre-op"); + if (pre_op == NULL) + goto fail; + for (i = 0; i < node->pre_ops_len; i++) { + if (ec_node_or_add(pre_op, ec_node_clone(node->pre_ops[i])) < 0) + goto fail; + } + + /* suffix unary operators */ + post_op = ec_node_new("or", "post-op"); + if (post_op == NULL) + goto fail; + for (i = 0; i < node->post_ops_len; i++) { + if (ec_node_or_add(post_op, ec_node_clone(node->post_ops[i])) < 0) + goto fail; + } + + post = ec_node_new("or", "post"); + if (post == NULL) + goto fail; + if (ec_node_or_add(post, ec_node_clone(node->val_node)) < 0) + goto fail; + if (ec_node_or_add(post, + EC_NODE_SEQ(NULL, + ec_node_clone(pre_op), + ec_node_clone(weak))) < 0) + goto fail; + for (i = 0; i < node->paren_len; i++) { + if (ec_node_or_add(post, EC_NODE_SEQ(NULL, + ec_node_clone(node->open_ops[i]), + ec_node_clone(weak), + ec_node_clone(node->close_ops[i]))) < 0) + goto fail; + } + term = EC_NODE_SEQ("term", + ec_node_clone(post), + ec_node_many(NULL, ec_node_clone(post_op), 0, 0) + ); + if (term == NULL) + goto fail; + + for (i = 0; i < node->bin_ops_len; i++) { + next = EC_NODE_SEQ("next", + ec_node_clone(term), + ec_node_many(NULL, + EC_NODE_SEQ(NULL, + ec_node_clone(node->bin_ops[i]), + ec_node_clone(term) + ), + 0, 0 + ) + ); + ec_node_free(term); + term = next; + if (term == NULL) + goto fail; + } + expr = term; + term = NULL; + + /* free the initial references */ + ec_node_free(pre_op); + pre_op = NULL; + ec_node_free(post_op); + post_op = NULL; + ec_node_free(post); + post = NULL; + + /* no need to clone here, the node is not consumed */ + if (ec_node_weakref_set(weak, expr) < 0) + goto fail; + ec_node_free(weak); + weak = NULL; + + node->child = expr; + + return 0; + +fail: + ec_node_free(term); + ec_node_free(expr); + ec_node_free(pre_op); + ec_node_free(post_op); + ec_node_free(post); + ec_node_free(weak); + + return ret; +} + +static struct ec_node_type ec_node_expr_type = { + .name = "expr", + .build = ec_node_expr_build, + .parse = ec_node_expr_parse, + .complete = ec_node_expr_complete, + .size = sizeof(struct ec_node_expr), + .free_priv = ec_node_expr_free_priv, +}; + +EC_NODE_TYPE_REGISTER(ec_node_expr_type); + +int ec_node_expr_set_val_node(struct ec_node *gen_node, struct ec_node *val_node) +{ + struct ec_node_expr *node = (struct ec_node_expr *)gen_node; + int ret; + + ret = -EINVAL; + if (val_node == NULL) + goto fail; + ret = -EPERM; + if (gen_node->flags & EC_NODE_F_BUILT) + goto fail; + ret = -EEXIST; + if (node->val_node != NULL) + goto fail; + + node->val_node = val_node; + gen_node->flags &= ~EC_NODE_F_BUILT; + + return 0; + +fail: + ec_node_free(val_node); + return ret; +} + +/* add a binary operator */ +int ec_node_expr_add_bin_op(struct ec_node *gen_node, struct ec_node *op) +{ + struct ec_node_expr *node = (struct ec_node_expr *)gen_node; + struct ec_node **bin_ops; + int ret; + + // XXX check node type + + ret = -EINVAL; + if (node == NULL || op == NULL) + goto fail; + ret = -EPERM; + if (gen_node->flags & EC_NODE_F_BUILT) + goto fail; + + ret = -ENOMEM; + bin_ops = ec_realloc(node->bin_ops, + (node->bin_ops_len + 1) * sizeof(*node->bin_ops)); + if (bin_ops == NULL) + goto fail;; + + node->bin_ops = bin_ops; + bin_ops[node->bin_ops_len] = op; + node->bin_ops_len++; + gen_node->flags &= ~EC_NODE_F_BUILT; + + return 0; + +fail: + ec_node_free(op); + return ret; +} + +/* add a unary pre-operator */ +int ec_node_expr_add_pre_op(struct ec_node *gen_node, struct ec_node *op) +{ + struct ec_node_expr *node = (struct ec_node_expr *)gen_node; + struct ec_node **pre_ops; + int ret; + + // XXX check node type + + ret = -EINVAL; + if (node == NULL || op == NULL) + goto fail; + ret = -EPERM; + if (gen_node->flags & EC_NODE_F_BUILT) + goto fail; + + ret = -ENOMEM; + pre_ops = ec_realloc(node->pre_ops, + (node->pre_ops_len + 1) * sizeof(*node->pre_ops)); + if (pre_ops == NULL) + goto fail; + + node->pre_ops = pre_ops; + pre_ops[node->pre_ops_len] = op; + node->pre_ops_len++; + gen_node->flags &= ~EC_NODE_F_BUILT; + + return 0; + +fail: + ec_node_free(op); + return ret; +} + +/* add a unary post-operator */ +int ec_node_expr_add_post_op(struct ec_node *gen_node, struct ec_node *op) +{ + struct ec_node_expr *node = (struct ec_node_expr *)gen_node; + struct ec_node **post_ops; + int ret; + + // XXX check node type + + ret = -EINVAL; + if (node == NULL || op == NULL) + goto fail; + ret = -EPERM; + if (gen_node->flags & EC_NODE_F_BUILT) + goto fail; + + ret = -ENOMEM; + post_ops = ec_realloc(node->post_ops, + (node->post_ops_len + 1) * sizeof(*node->post_ops)); + if (post_ops == NULL) + goto fail; + + node->post_ops = post_ops; + post_ops[node->post_ops_len] = op; + node->post_ops_len++; + gen_node->flags &= ~EC_NODE_F_BUILT; + + return 0; + +fail: + ec_node_free(op); + return ret; +} + +/* add parenthesis symbols */ +int ec_node_expr_add_parenthesis(struct ec_node *gen_node, + struct ec_node *open, struct ec_node *close) +{ + struct ec_node_expr *node = (struct ec_node_expr *)gen_node; + struct ec_node **open_ops, **close_ops; + int ret; + + // XXX check node type + + ret = -EINVAL; + if (node == NULL || open == NULL || close == NULL) + goto fail; + ret = -EPERM; + if (gen_node->flags & EC_NODE_F_BUILT) + goto fail;; + + ret = -ENOMEM; + open_ops = ec_realloc(node->open_ops, + (node->paren_len + 1) * sizeof(*node->open_ops)); + if (open_ops == NULL) + goto fail; + close_ops = ec_realloc(node->close_ops, + (node->paren_len + 1) * sizeof(*node->close_ops)); + if (close_ops == NULL) + goto fail; + + node->open_ops = open_ops; + node->close_ops = close_ops; + open_ops[node->paren_len] = open; + close_ops[node->paren_len] = close; + node->paren_len++; + gen_node->flags &= ~EC_NODE_F_BUILT; + + return 0; + +fail: + ec_node_free(open); + ec_node_free(close); + return ret; +} + +enum expr_node_type { + NONE, + VAL, + BIN_OP, + PRE_OP, + POST_OP, + PAREN_OPEN, + PAREN_CLOSE, +}; +static enum expr_node_type get_node_type(const struct ec_node *expr_gen_node, + const struct ec_node *check) +{ + struct ec_node_expr *expr_node = (struct ec_node_expr *)expr_gen_node; + size_t i; + + if (check == expr_node->val_node) + return VAL; + + for (i = 0; i < expr_node->bin_ops_len; i++) { + if (check == expr_node->bin_ops[i]) + return BIN_OP; + } + for (i = 0; i < expr_node->pre_ops_len; i++) { + if (check == expr_node->pre_ops[i]) + return PRE_OP; + } + for (i = 0; i < expr_node->post_ops_len; i++) { + if (check == expr_node->post_ops[i]) + return POST_OP; + } + + for (i = 0; i < expr_node->paren_len; i++) { + if (check == expr_node->open_ops[i]) + return PAREN_OPEN; + } + for (i = 0; i < expr_node->paren_len; i++) { + if (check == expr_node->close_ops[i]) + return PAREN_CLOSE; + } + + return NONE; +} + +struct result { + bool has_val; + void *val; + const struct ec_parsed *op; + enum expr_node_type op_type; +}; + +/* merge x and y results in x */ +static int merge_results(void *userctx, + const struct ec_node_expr_eval_ops *ops, + struct result *x, const struct result *y) +{ + int ret; + + if (y->has_val == 0 && y->op == NULL) + return 0; + if (x->has_val == 0 && x->op == NULL) { + *x = *y; + return 0; + } + + if (x->has_val && x->op == NULL && y->has_val && y->op != NULL) { + ret = ops->eval_bin_op(&x->val, userctx, x->val, y->op, y->val); + if (ret < 0) + return ret; + + return 0; + } + + if (x->has_val == 0 && x->op != NULL && y->has_val && y->op == NULL) { + if (x->op_type == PRE_OP) { + ret = ops->eval_pre_op(&x->val, userctx, y->val, x->op); + if (ret < 0) + return ret; + x->has_val = true; + x->op_type = NONE; + x->op = NULL; + return 0; + } else if (x->op_type == BIN_OP) { + x->val = y->val; + x->has_val = true; + return 0; + } + } + + if (x->has_val && x->op == NULL && y->has_val == 0 && y->op != NULL) { + ret = ops->eval_post_op(&x->val, userctx, x->val, y->op); + if (ret < 0) + return ret; + + return 0; + } + + assert(true); /* we should not get here */ + return -EINVAL; +} + +static int eval_expression(struct result *result, + void *userctx, + const struct ec_node_expr_eval_ops *ops, + const struct ec_node *expr_gen_node, + const struct ec_parsed *parsed) + +{ + struct ec_parsed *open = NULL, *close = NULL; + struct result child_result; + struct ec_parsed *child; + enum expr_node_type type; + int ret; + + memset(result, 0, sizeof(*result)); + memset(&child_result, 0, sizeof(child_result)); + + type = get_node_type(expr_gen_node, parsed->node); + if (type == VAL) { + ret = ops->eval_var(&result->val, userctx, parsed); + if (ret < 0) + goto fail; + result->has_val = 1; + } else if (type == PRE_OP || type == POST_OP || type == BIN_OP) { + result->op = parsed; + result->op_type = type; + } + + TAILQ_FOREACH(child, &parsed->children, next) { + + type = get_node_type(expr_gen_node, child->node); + if (type == PAREN_OPEN) { + open = child; + continue; + } else if (type == PAREN_CLOSE) { + close = child; + continue; + } + + ret = eval_expression(&child_result, userctx, ops, + expr_gen_node, child); + if (ret < 0) + goto fail; + + ret = merge_results(userctx, ops, result, &child_result); + if (ret < 0) + goto fail; + + memset(&child_result, 0, sizeof(child_result)); + } + + if (open != NULL && close != NULL) { + ret = ops->eval_parenthesis(&result->val, userctx, open, close, + result->val); + if (ret < 0) + goto fail; + } + + return 0; + +fail: + if (result->has_val) + ops->eval_free(result->val, userctx); + if (child_result.has_val) + ops->eval_free(child_result.val, userctx); + memset(result, 0, sizeof(*result)); + + return ret; +} + +int ec_node_expr_eval(void **user_result, const struct ec_node *node, + struct ec_parsed *parsed, const struct ec_node_expr_eval_ops *ops, + void *userctx) +{ + struct result result; + int ret; + + if (ops == NULL || ops->eval_var == NULL || ops->eval_pre_op == NULL || + ops->eval_post_op == NULL || ops->eval_bin_op == NULL || + ops->eval_parenthesis == NULL || ops->eval_free == NULL) + return -EINVAL; + + if (!ec_parsed_matches(parsed)) + return -EINVAL; + + ec_parsed_dump(stdout, parsed); //XXX + ret = eval_expression(&result, userctx, ops, node, parsed); + if (ret < 0) + return ret; + + assert(result.has_val); + assert(result.op == NULL); + *user_result = result.val; + + return 0; +} + +/* the test case is in a separate file ecoli_node_expr_test.c */ diff --git a/lib/ecoli_node_expr.h b/lib/ecoli_node_expr.h new file mode 100644 index 0000000..651ac7c --- /dev/null +++ b/lib/ecoli_node_expr.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2016, Olivier MATZ + * + * 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 + +/* XXX remove the _new for all other nodes */ + +/** + * Callback function type for evaluating a variable + * + * @param result + * On success, this pointer must be set by the user to point + * to a user structure describing the evaluated result. + * @param userctx + * A user-defined context passed to all callback functions, which + * can be used to maintain a state or store global information. + * @param var + * The parsed result referencing the variable. + * @return + * 0 on success (*result must be set), or -errno on error (*result + * is undefined). + */ +typedef int (*ec_node_expr_eval_var_t)( + void **result, void *userctx, + const struct ec_parsed *var); + +/** + * Callback function type for evaluating a prefix-operator + * + * @param result + * On success, this pointer must be set by the user to point + * to a user structure describing the evaluated result. + * @param userctx + * A user-defined context passed to all callback functions, which + * can be used to maintain a state or store global information. + * @param operand + * The evaluated expression on which the operation should be applied. + * @param var + * The parsed result referencing the operator. + * @return + * 0 on success (*result must be set, operand is freed), + * or -errno on error (*result is undefined, operand is not freed). + */ +typedef int (*ec_node_expr_eval_pre_op_t)( + void **result, void *userctx, + void *operand, + const struct ec_parsed *operator); + +typedef int (*ec_node_expr_eval_post_op_t)( + void **result, void *userctx, + void *operand, + const struct ec_parsed *operator); + +typedef int (*ec_node_expr_eval_bin_op_t)( + void **result, void *userctx, + void *operand1, + const struct ec_parsed *operator, + void *operand2); + +typedef int (*ec_node_expr_eval_parenthesis_t)( + void **result, void *userctx, + const struct ec_parsed *open_paren, + const struct ec_parsed *close_paren, + void * value); + +typedef void (*ec_node_expr_eval_free_t)( + void *result, void *userctx); + + +struct ec_node *ec_node_expr(const char *id); +int ec_node_expr_set_val_node(struct ec_node *gen_node, struct ec_node *val_node); +int ec_node_expr_add_bin_op(struct ec_node *gen_node, struct ec_node *op); +int ec_node_expr_add_pre_op(struct ec_node *gen_node, struct ec_node *op); +int ec_node_expr_add_post_op(struct ec_node *gen_node, struct ec_node *op); +int ec_node_expr_add_parenthesis(struct ec_node *gen_node, + struct ec_node *open, struct ec_node *close); + +struct ec_node_expr_eval_ops { + ec_node_expr_eval_var_t eval_var; + ec_node_expr_eval_pre_op_t eval_pre_op; + ec_node_expr_eval_post_op_t eval_post_op; + ec_node_expr_eval_bin_op_t eval_bin_op; + ec_node_expr_eval_parenthesis_t eval_parenthesis; + ec_node_expr_eval_free_t eval_free; +}; + +int ec_node_expr_eval(void **result, const struct ec_node *node, + struct ec_parsed *parsed, const struct ec_node_expr_eval_ops *ops, + void *userctx); + +#endif diff --git a/lib/ecoli_node_expr_test.c b/lib/ecoli_node_expr_test.c new file mode 100644 index 0000000..ac3e5ba --- /dev/null +++ b/lib/ecoli_node_expr_test.c @@ -0,0 +1,304 @@ +/* + * Copyright (c) 2016-2017, Olivier MATZ + * + * 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +struct my_eval_result { + int val; +}; + +static int +ec_node_expr_test_eval_var(void **result, void *userctx, + const struct ec_parsed *var) +{ + const struct ec_strvec *vec; + struct my_eval_result *eval; + + (void)userctx; + + /* get parsed string vector, it should contain only one str */ + vec = ec_parsed_strvec(var); + if (ec_strvec_len(vec) != 1) + return -EINVAL; + + eval = ec_malloc(sizeof(*eval)); + if (eval == NULL) + return -ENOMEM; + + eval->val = atoi(ec_strvec_val(vec, 0)); // XXX use strtol + printf("eval var %d\n", eval->val); + *result = eval; + + return 0; +} + +static int +ec_node_expr_test_eval_pre_op(void **result, void *userctx, void *operand, + const struct ec_parsed *operator) +{ + const struct ec_strvec *vec; + struct my_eval_result *eval = operand;; + + (void)userctx; + + /* get parsed string vector, it should contain only one str */ + vec = ec_parsed_strvec(operator); + if (ec_strvec_len(vec) != 1) + return -EINVAL; + + if (!strcmp(ec_strvec_val(vec, 0), "!")) + eval->val = !eval->val; + else + return -EINVAL; + + printf("eval pre_op %d\n", eval->val); + *result = eval; + + return 0; +} + +static int +ec_node_expr_test_eval_post_op(void **result, void *userctx, void *operand, + const struct ec_parsed *operator) +{ + const struct ec_strvec *vec; + struct my_eval_result *eval = operand;; + + (void)userctx; + + /* get parsed string vector, it should contain only one str */ + vec = ec_parsed_strvec(operator); + if (ec_strvec_len(vec) != 1) + return -EINVAL; + + if (!strcmp(ec_strvec_val(vec, 0), "^")) + eval->val = eval->val * eval->val; + else + return -EINVAL; + + printf("eval post_op %d\n", eval->val); + *result = eval; + + return 0; +} + +static int +ec_node_expr_test_eval_bin_op(void **result, void *userctx, void *operand1, + const struct ec_parsed *operator, void *operand2) + +{ + const struct ec_strvec *vec; + struct my_eval_result *eval1 = operand1;; + struct my_eval_result *eval2 = operand2;; + + (void)userctx; + + /* get parsed string vector, it should contain only one str */ + vec = ec_parsed_strvec(operator); + if (ec_strvec_len(vec) != 1) + return -EINVAL; + + if (!strcmp(ec_strvec_val(vec, 0), "+")) + eval1->val = eval1->val + eval2->val; + else if (!strcmp(ec_strvec_val(vec, 0), "*")) + eval1->val = eval1->val * eval2->val; + else + return -EINVAL; + + printf("eval bin_op %d\n", eval1->val); + ec_free(eval2); + *result = eval1; + + return 0; +} + +static int +ec_node_expr_test_eval_parenthesis(void **result, void *userctx, + const struct ec_parsed *open_paren, + const struct ec_parsed *close_paren, + void *value) +{ + (void)userctx; + (void)open_paren; + (void)close_paren; + + printf("eval paren\n"); + *result = value; + + return 0; +} + +static void +ec_node_expr_test_eval_free(void *result, void *userctx) +{ + (void)userctx; + ec_free(result); +} + +static const struct ec_node_expr_eval_ops test_ops = { + .eval_var = ec_node_expr_test_eval_var, + .eval_pre_op = ec_node_expr_test_eval_pre_op, + .eval_post_op = ec_node_expr_test_eval_post_op, + .eval_bin_op = ec_node_expr_test_eval_bin_op, + .eval_parenthesis = ec_node_expr_test_eval_parenthesis, + .eval_free = ec_node_expr_test_eval_free, +}; + +static int ec_node_expr_test_eval(struct ec_node *lex_node, + const struct ec_node *expr_node, + const char *str, int val) +{ + struct ec_parsed *p; + void *result; + struct my_eval_result *eval; + int ret; + + /* XXX check node type (again and again) */ + + p = ec_node_parse(lex_node, str); + if (p == NULL) + return -1; + + ret = ec_node_expr_eval(&result, expr_node, p, &test_ops, NULL); + ec_parsed_free(p); + if (ret < 0) + return -1; + + /* the parsed value is an integer */ + eval = result; + assert(eval != NULL); + + printf("result: %d (expected %d)\n", eval->val, val); + if (eval->val == val) + ret = 0; + else + ret = -1; + + ec_free(eval); + + return ret; +} + +static int ec_node_expr_testcase(void) +{ + struct ec_node *node = NULL, *lex_node = NULL; + int ret = 0; + + node = ec_node_new("expr", "my_expr"); + if (node == NULL) + return -1; + + ec_node_expr_set_val_node(node, ec_node_int(NULL, 0, UCHAR_MAX, 0)); + ec_node_expr_add_bin_op(node, ec_node_str(NULL, "+")); + ec_node_expr_add_bin_op(node, ec_node_str(NULL, "*")); + ec_node_expr_add_pre_op(node, ec_node_str(NULL, "!")); /* not */ + ec_node_expr_add_post_op(node, ec_node_str(NULL, "^")); /* square */ + ec_node_expr_add_parenthesis(node, ec_node_str(NULL, "("), + ec_node_str(NULL, ")")); + ret |= EC_TEST_CHECK_PARSE(node, 1, "1"); + ret |= EC_TEST_CHECK_PARSE(node, 1, "1", "1"); + ret |= EC_TEST_CHECK_PARSE(node, 1, "1", "*"); + ret |= EC_TEST_CHECK_PARSE(node, 3, "1", "*", "1"); + ret |= EC_TEST_CHECK_PARSE(node, 3, "1", "*", "1", "*"); + ret |= EC_TEST_CHECK_PARSE(node, 4, "1", "+", "!", "1"); + ret |= EC_TEST_CHECK_PARSE(node, 4, "1", "^", "+", "1"); + ret |= EC_TEST_CHECK_PARSE(node, 5, "1", "*", "1", "*", "1"); + ret |= EC_TEST_CHECK_PARSE(node, 5, "1", "*", "1", "+", "1"); + ret |= EC_TEST_CHECK_PARSE(node, 7, "1", "*", "1", "*", "1", "*", "1"); + ret |= EC_TEST_CHECK_PARSE( + node, 10, "!", "(", "1", "*", "(", "1", "+", "1", ")", ")"); + ret |= EC_TEST_CHECK_PARSE(node, 5, "1", "+", "!", "1", "^"); + + /* prepend a lexer to the expression node */ + lex_node = ec_node_re_lex(NULL, ec_node_clone(node)); + if (lex_node == NULL) + goto fail; + + ret |= ec_node_re_lex_add(lex_node, "[0-9]+", 1); /* vars */ + ret |= ec_node_re_lex_add(lex_node, "[+*!^()]", 1); /* operators */ + ret |= ec_node_re_lex_add(lex_node, "[ ]+", 0); /* spaces */ + + /* valid expressions */ + ret |= EC_TEST_CHECK_PARSE(lex_node, 1, "!1"); + ret |= EC_TEST_CHECK_PARSE(lex_node, 1, "1^"); + ret |= EC_TEST_CHECK_PARSE(lex_node, 1, "1^ + 1"); + ret |= EC_TEST_CHECK_PARSE(lex_node, 1, "1 + 4 * (2 + 3^)^"); + ret |= EC_TEST_CHECK_PARSE(lex_node, 1, "(1)"); + ret |= EC_TEST_CHECK_PARSE(lex_node, 1, "3*!3+!3*(2+ 2)"); + ret |= EC_TEST_CHECK_PARSE(lex_node, 1, "!!(!1)^ + !(4 + (2*3))"); + ret |= EC_TEST_CHECK_PARSE(lex_node, 1, "(1 + 1)^ * 1^"); + + /* invalid expressions */ + ret |= EC_TEST_CHECK_PARSE(lex_node, -1, ""); + ret |= EC_TEST_CHECK_PARSE(lex_node, -1, "()"); + ret |= EC_TEST_CHECK_PARSE(lex_node, -1, "("); + ret |= EC_TEST_CHECK_PARSE(lex_node, -1, ")"); + ret |= EC_TEST_CHECK_PARSE(lex_node, -1, "+1"); + ret |= EC_TEST_CHECK_PARSE(lex_node, -1, "1+"); + ret |= EC_TEST_CHECK_PARSE(lex_node, -1, "1+*1"); + ret |= EC_TEST_CHECK_PARSE(lex_node, -1, "1+(1*1"); + ret |= EC_TEST_CHECK_PARSE(lex_node, -1, "1+!1!1)"); + + ret |= ec_node_expr_test_eval(lex_node, node, "1^", 1); + ret |= ec_node_expr_test_eval(lex_node, node, "2^", 4); + ret |= ec_node_expr_test_eval(lex_node, node, "!1", 0); + ret |= ec_node_expr_test_eval(lex_node, node, "!0", 1); + + ret |= ec_node_expr_test_eval(lex_node, node, "1+1", 2); + ret |= ec_node_expr_test_eval(lex_node, node, "1+1*2", 4); + ret |= ec_node_expr_test_eval(lex_node, node, "2 * 2^", 8); + ret |= ec_node_expr_test_eval(lex_node, node, "(1 + !0)^ * !0^", 4); + ret |= ec_node_expr_test_eval(lex_node, node, "(1 + !1) * 3", 3); + + ec_node_free(node); + ec_node_free(lex_node); + + return ret; + +fail: + ec_node_free(lex_node); + ec_node_free(node); + return -1; +} + +static struct ec_test ec_node_expr_test = { + .name = "expr", + .test = ec_node_expr_testcase, +}; + +EC_TEST_REGISTER(ec_node_expr_test); diff --git a/lib/ecoli_node_int.c b/lib/ecoli_node_int.c new file mode 100644 index 0000000..2391b29 --- /dev/null +++ b/lib/ecoli_node_int.c @@ -0,0 +1,240 @@ +/* + * Copyright (c) 2016, Olivier MATZ + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +struct ec_node_int { + struct ec_node gen; + bool check_min; + long long int min; + bool check_max; + long long int max; + unsigned int base; +}; + +static int parse_llint(struct ec_node_int *node, const char *str, + long long *val) +{ + char *endptr; + + errno = 0; + *val = strtoll(str, &endptr, node->base); + + /* out of range */ + if ((errno == ERANGE && (*val == LLONG_MAX || *val == LLONG_MIN)) || + (errno != 0 && *val == 0)) + return -1; + + if (node->check_min && *val < node->min) + return -1; + + if (node->check_max && *val > node->max) + return -1; + + if (*endptr != 0) + return -1; + + return 0; +} + +static struct ec_parsed *ec_node_int_parse(const struct ec_node *gen_node, + const struct ec_strvec *strvec) +{ + struct ec_node_int *node = (struct ec_node_int *)gen_node; + struct ec_parsed *parsed; + struct ec_strvec *match_strvec; + const char *str; + long long val; + + parsed = ec_parsed_new(); + if (parsed == NULL) + goto fail; + + if (ec_strvec_len(strvec) == 0) + return parsed; + + str = ec_strvec_val(strvec, 0); + if (parse_llint(node, str, &val) < 0) + return parsed; + + match_strvec = ec_strvec_ndup(strvec, 0, 1); + if (match_strvec == NULL) + goto fail; + + ec_parsed_set_match(parsed, gen_node, match_strvec); + + return parsed; + + fail: + ec_parsed_free(parsed); + return NULL; +} + +static struct ec_node_type ec_node_int_type = { + .name = "int", + .parse = ec_node_int_parse, + .complete = ec_node_default_complete, + .size = sizeof(struct ec_node_int), +}; + +EC_NODE_TYPE_REGISTER(ec_node_int_type); + +struct ec_node *ec_node_int(const char *id, long long int min, + long long int max, unsigned int base) +{ + struct ec_node *gen_node = NULL; + struct ec_node_int *node = NULL; + + gen_node = __ec_node_new(&ec_node_int_type, id); + if (gen_node == NULL) + return NULL; + node = (struct ec_node_int *)gen_node; + + node->check_min = true; + node->min = min; + node->check_max = true; + node->max = max; + node->base = base; + + return &node->gen; +} + +long long ec_node_int_getval(struct ec_node *gen_node, const char *str) +{ + struct ec_node_int *node = (struct ec_node_int *)gen_node; + long long val = 0; + + // XXX check type here + // if gen_node->type != int fail + + parse_llint(node, str, &val); + + return val; +} + +static int ec_node_int_testcase(void) +{ + struct ec_parsed *p; + struct ec_node *node; + const char *s; + int ret = 0; + + node = ec_node_int(NULL, 0, 256, 0); + if (node == NULL) { + ec_log(EC_LOG_ERR, "cannot create node\n"); + return -1; + } + ret |= EC_TEST_CHECK_PARSE(node, 1, "0"); + ret |= EC_TEST_CHECK_PARSE(node, 1, "256", "foo"); + ret |= EC_TEST_CHECK_PARSE(node, 1, "0x100"); + ret |= EC_TEST_CHECK_PARSE(node, 1, " 1"); + ret |= EC_TEST_CHECK_PARSE(node, -1, "-1"); + ret |= EC_TEST_CHECK_PARSE(node, -1, "0x101"); + + p = ec_node_parse(node, "0"); + s = ec_strvec_val(ec_parsed_strvec(p), 0); + EC_TEST_ASSERT(s != NULL && ec_node_int_getval(node, s) == 0); + ec_parsed_free(p); + + p = ec_node_parse(node, "10"); + s = ec_strvec_val(ec_parsed_strvec(p), 0); + EC_TEST_ASSERT(s != NULL && ec_node_int_getval(node, s) == 10); + ec_parsed_free(p); + ec_node_free(node); + + node = ec_node_int(NULL, -1, LLONG_MAX, 16); + if (node == NULL) { + ec_log(EC_LOG_ERR, "cannot create node\n"); + return -1; + } + ret |= EC_TEST_CHECK_PARSE(node, 1, "0"); + ret |= EC_TEST_CHECK_PARSE(node, 1, "-1"); + ret |= EC_TEST_CHECK_PARSE(node, 1, "7fffffffffffffff"); + ret |= EC_TEST_CHECK_PARSE(node, 1, "0x7fffffffffffffff"); + ret |= EC_TEST_CHECK_PARSE(node, -1, "-2"); + + p = ec_node_parse(node, "10"); + s = ec_strvec_val(ec_parsed_strvec(p), 0); + EC_TEST_ASSERT(s != NULL && ec_node_int_getval(node, s) == 16); + ec_parsed_free(p); + ec_node_free(node); + + node = ec_node_int(NULL, LLONG_MIN, 0, 10); + if (node == NULL) { + ec_log(EC_LOG_ERR, "cannot create node\n"); + return -1; + } + ret |= EC_TEST_CHECK_PARSE(node, 1, "0"); + ret |= EC_TEST_CHECK_PARSE(node, 1, "-1"); + ret |= EC_TEST_CHECK_PARSE(node, 1, "-9223372036854775808"); + ret |= EC_TEST_CHECK_PARSE(node, -1, "0x0"); + ret |= EC_TEST_CHECK_PARSE(node, -1, "1"); + ec_node_free(node); + + /* test completion */ + node = ec_node_int(NULL, 0, 10, 0); + if (node == NULL) { + ec_log(EC_LOG_ERR, "cannot create node\n"); + return -1; + } + ret |= EC_TEST_CHECK_COMPLETE(node, + "", EC_NODE_ENDLIST, + EC_NODE_ENDLIST, + ""); + ret |= EC_TEST_CHECK_COMPLETE(node, + "x", EC_NODE_ENDLIST, + EC_NODE_ENDLIST, + ""); + ret |= EC_TEST_CHECK_COMPLETE(node, + "1", EC_NODE_ENDLIST, + EC_NODE_ENDLIST, + ""); + ec_node_free(node); + + return ret; +} + +static struct ec_test ec_node_int_test = { + .name = "node_int", + .test = ec_node_int_testcase, +}; + +EC_TEST_REGISTER(ec_node_int_test); diff --git a/lib/ecoli_node_int.h b/lib/ecoli_node_int.h new file mode 100644 index 0000000..d03dd3d --- /dev/null +++ b/lib/ecoli_node_int.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2016, Olivier MATZ + * + * 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 + +// XXX remove min, max, base from new(), and add ec_node_int_set_limits() + +// XXX ec_node_int_set_base() ? +struct ec_node *ec_node_int(const char *id, long long int min, + long long int max, unsigned int base); +long long ec_node_int_getval(struct ec_node *node, const char *str); + +#endif diff --git a/lib/ecoli_node_many.c b/lib/ecoli_node_many.c new file mode 100644 index 0000000..7eb359b --- /dev/null +++ b/lib/ecoli_node_many.c @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2016, Olivier MATZ + * + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +struct ec_node_many { + struct ec_node gen; + unsigned int min; + unsigned int max; + struct ec_node *child; +}; + +static struct ec_parsed *ec_node_many_parse(const struct ec_node *gen_node, + const struct ec_strvec *strvec) +{ + struct ec_node_many *node = (struct ec_node_many *)gen_node; + struct ec_parsed *parsed, *child_parsed; + struct ec_strvec *match_strvec; + struct ec_strvec *childvec = NULL; + size_t off = 0, len, count; + + parsed = ec_parsed_new(); + if (parsed == NULL) + goto fail; + + for (count = 0; node->max == 0 || count < node->max; count++) { + childvec = ec_strvec_ndup(strvec, off, + ec_strvec_len(strvec) - off); + if (childvec == NULL) + goto fail; + + child_parsed = ec_node_parse_strvec(node->child, childvec); + if (child_parsed == NULL) + goto fail; + + ec_strvec_free(childvec); + childvec = NULL; + + if (!ec_parsed_matches(child_parsed)) { + ec_parsed_free(child_parsed); + break; + } + + ec_parsed_add_child(parsed, child_parsed); + + /* it matches an empty strvec, no need to continue */ + len = ec_parsed_len(child_parsed); + if (len == 0) { + ec_parsed_free(child_parsed); + break; + } + + off += len; + } + + if (count < node->min) { + ec_parsed_free_children(parsed); + return parsed; + } + + match_strvec = ec_strvec_ndup(strvec, 0, off); + if (match_strvec == NULL) + goto fail; + + ec_parsed_set_match(parsed, gen_node, match_strvec); + + return parsed; + +fail: + ec_strvec_free(childvec); + ec_parsed_free(parsed); + return NULL; +} + +#if 0 //XXX missing node_many_complete +static struct ec_completed *ec_node_many_complete(const struct ec_node *gen_node, + const struct ec_strvec *strvec) +{ + struct ec_node_many *node = (struct ec_node_many *)gen_node; + struct ec_completed *completed, *child_completed; + struct ec_strvec *childvec; + struct ec_parsed *parsed; + size_t len = 0; + unsigned int i; + + completed = ec_completed_new(); + if (completed == NULL) + return NULL; + + if (node->len == 0) + return completed; + + for (i = 0; i < node->len; i++) { + childvec = ec_strvec_ndup(strvec, len, + ec_strvec_len(strvec) - len); + if (childvec == NULL) + goto fail; // XXX fail ? + + child_completed = ec_node_complete_strvec(node->table[i], + childvec); + if (child_completed == NULL) + goto fail; + + ec_completed_merge(completed, child_completed); + + parsed = ec_node_parse_strvec(node->table[i], childvec); + if (parsed == NULL) + goto fail; + + ec_strvec_free(childvec); + childvec = NULL; + + if (!ec_parsed_matches(parsed)) { + ec_parsed_free(parsed); + break; + } + + len += ec_strvec_len(parsed->strvec); + ec_parsed_free(parsed); + } + + return completed; + +fail: + ec_strvec_free(childvec); + ec_completed_free(completed); + return NULL; +} +#endif + +static void ec_node_many_free_priv(struct ec_node *gen_node) +{ + struct ec_node_many *node = (struct ec_node_many *)gen_node; + + ec_node_free(node->child); +} + +static struct ec_node_type ec_node_many_type = { + .name = "many", + .parse = ec_node_many_parse, + .complete = ec_node_default_complete, +//XXX .complete = ec_node_many_complete, + .size = sizeof(struct ec_node_many), + .free_priv = ec_node_many_free_priv, +}; + +EC_NODE_TYPE_REGISTER(ec_node_many_type); + +struct ec_node *ec_node_many(const char *id, struct ec_node *child, + unsigned int min, unsigned int max) +{ + struct ec_node_many *node = NULL; + + if (child == NULL) + return NULL; + + node = (struct ec_node_many *)__ec_node_new(&ec_node_many_type, id); + if (node == NULL) { + ec_node_free(child); + return NULL; + } + + node->child = child; + node->min = min; + node->max = max; + + return &node->gen; +} + +static int ec_node_many_testcase(void) +{ + struct ec_node *node; + int ret = 0; + + node = ec_node_many(NULL, ec_node_str(NULL, "foo"), 0, 0); + if (node == NULL) { + ec_log(EC_LOG_ERR, "cannot create node\n"); + return -1; + } + ret |= EC_TEST_CHECK_PARSE(node, 0, "bar"); + ret |= EC_TEST_CHECK_PARSE(node, 1, "foo", "bar"); + ret |= EC_TEST_CHECK_PARSE(node, 2, "foo", "foo", "bar"); + ret |= EC_TEST_CHECK_PARSE(node, 0); + ec_node_free(node); + + node = ec_node_many(NULL, ec_node_str(NULL, "foo"), 1, 0); + if (node == NULL) { + ec_log(EC_LOG_ERR, "cannot create node\n"); + return -1; + } + ret |= EC_TEST_CHECK_PARSE(node, -1, "bar"); + ret |= EC_TEST_CHECK_PARSE(node, 1, "foo", "bar"); + ret |= EC_TEST_CHECK_PARSE(node, 2, "foo", "foo", "bar"); + ret |= EC_TEST_CHECK_PARSE(node, -1); + ec_node_free(node); + + node = ec_node_many(NULL, ec_node_str(NULL, "foo"), 1, 2); + if (node == NULL) { + ec_log(EC_LOG_ERR, "cannot create node\n"); + return -1; + } + ret |= EC_TEST_CHECK_PARSE(node, -1, "bar"); + ret |= EC_TEST_CHECK_PARSE(node, 1, "foo", "bar"); + ret |= EC_TEST_CHECK_PARSE(node, 2, "foo", "foo", "bar"); + ret |= EC_TEST_CHECK_PARSE(node, 2, "foo", "foo", "foo"); + ret |= EC_TEST_CHECK_PARSE(node, -1); + ec_node_free(node); + + /* test completion */ + /* XXX */ + + return ret; +} + +static struct ec_test ec_node_many_test = { + .name = "many", + .test = ec_node_many_testcase, +}; + +EC_TEST_REGISTER(ec_node_many_test); diff --git a/lib/ecoli_node_many.h b/lib/ecoli_node_many.h new file mode 100644 index 0000000..e562c4d --- /dev/null +++ b/lib/ecoli_node_many.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2016, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ECOLI_NODE_MANY_ +#define ECOLI_NODE_MANY_ + +/* + * if min == max == 0, there is no limit + */ +struct ec_node *ec_node_many(const char *id, struct ec_node *child, + unsigned int min, unsigned int max); + +#endif diff --git a/lib/ecoli_node_option.c b/lib/ecoli_node_option.c new file mode 100644 index 0000000..39c1004 --- /dev/null +++ b/lib/ecoli_node_option.c @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2016, Olivier MATZ + * + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +struct ec_node_option { + struct ec_node gen; + struct ec_node *child; +}; + +static struct ec_parsed *ec_node_option_parse(const struct ec_node *gen_node, + const struct ec_strvec *strvec) +{ + struct ec_node_option *node = (struct ec_node_option *)gen_node; + struct ec_parsed *parsed = NULL, *child_parsed; + struct ec_strvec *match_strvec; + + parsed = ec_parsed_new(); + if (parsed == NULL) + goto fail; + + child_parsed = ec_node_parse_strvec(node->child, strvec); + if (child_parsed == NULL) + goto fail; + + if (ec_parsed_matches(child_parsed)) { + ec_parsed_add_child(parsed, child_parsed); + match_strvec = ec_strvec_dup(child_parsed->strvec); + } else { + ec_parsed_free(child_parsed); + match_strvec = ec_strvec_new(); + } + + if (match_strvec == NULL) + goto fail; + + ec_parsed_set_match(parsed, gen_node, match_strvec); + + return parsed; + + fail: + ec_parsed_free(parsed); + return NULL; +} + +static struct ec_completed *ec_node_option_complete(const struct ec_node *gen_node, + const struct ec_strvec *strvec) +{ + struct ec_node_option *node = (struct ec_node_option *)gen_node; + + return ec_node_complete_strvec(node->child, strvec); +} + +static void ec_node_option_free_priv(struct ec_node *gen_node) +{ + struct ec_node_option *node = (struct ec_node_option *)gen_node; + + ec_node_free(node->child); +} + +static struct ec_node_type ec_node_option_type = { + .name = "option", + .parse = ec_node_option_parse, + .complete = ec_node_option_complete, + .size = sizeof(struct ec_node_option), + .free_priv = ec_node_option_free_priv, +}; + +EC_NODE_TYPE_REGISTER(ec_node_option_type); + +struct ec_node *ec_node_option(const char *id, struct ec_node *child) +{ + struct ec_node *gen_node = NULL; + struct ec_node_option *node = NULL; + + if (child == NULL) + return NULL; + + gen_node = __ec_node_new(&ec_node_option_type, id); + if (gen_node == NULL) { + ec_node_free(child); + return NULL; + } + node = (struct ec_node_option *)gen_node; + + node->child = child; + + child->parent = gen_node; + TAILQ_INSERT_TAIL(&gen_node->children, child, next); + + return &node->gen; +} + +static int ec_node_option_testcase(void) +{ + struct ec_node *node; + int ret = 0; + + node = ec_node_option(NULL, ec_node_str(NULL, "foo")); + if (node == NULL) { + ec_log(EC_LOG_ERR, "cannot create node\n"); + return -1; + } + ret |= EC_TEST_CHECK_PARSE(node, 1, "foo"); + ret |= EC_TEST_CHECK_PARSE(node, 1, "foo", "bar"); + ret |= EC_TEST_CHECK_PARSE(node, 0, "bar"); + ret |= EC_TEST_CHECK_PARSE(node, 0); + ec_node_free(node); + + /* test completion */ + node = ec_node_option(NULL, ec_node_str(NULL, "foo")); + if (node == NULL) { + ec_log(EC_LOG_ERR, "cannot create node\n"); + return -1; + } + ret |= EC_TEST_CHECK_COMPLETE(node, + "", EC_NODE_ENDLIST, + "foo", EC_NODE_ENDLIST, + "foo"); + ret |= EC_TEST_CHECK_COMPLETE(node, + "f", EC_NODE_ENDLIST, + "oo", EC_NODE_ENDLIST, + "oo"); + ret |= EC_TEST_CHECK_COMPLETE(node, + "b", EC_NODE_ENDLIST, + EC_NODE_ENDLIST, + ""); + ec_node_free(node); + + return ret; +} + +static struct ec_test ec_node_option_test = { + .name = "node_option", + .test = ec_node_option_testcase, +}; + +EC_TEST_REGISTER(ec_node_option_test); diff --git a/lib/ecoli_node_option.h b/lib/ecoli_node_option.h new file mode 100644 index 0000000..3b6e3a2 --- /dev/null +++ b/lib/ecoli_node_option.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2016, Olivier MATZ + * + * 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 + +struct ec_node *ec_node_option(const char *id, struct ec_node *node); + +#endif diff --git a/lib/ecoli_node_or.c b/lib/ecoli_node_or.c new file mode 100644 index 0000000..d1e4863 --- /dev/null +++ b/lib/ecoli_node_or.c @@ -0,0 +1,273 @@ +/* + * Copyright (c) 2016, Olivier MATZ + * + * 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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +struct ec_node_or { + struct ec_node gen; + struct ec_node **table; + unsigned int len; +}; + +static struct ec_parsed *ec_node_or_parse(const struct ec_node *gen_node, + const struct ec_strvec *strvec) +{ + struct ec_node_or *node = (struct ec_node_or *)gen_node; + struct ec_parsed *parsed, *child_parsed = NULL; + struct ec_strvec *match_strvec; + unsigned int i; + + parsed = ec_parsed_new(); + if (parsed == NULL) + goto fail; + + for (i = 0; i < node->len; i++) { + child_parsed = ec_node_parse_strvec(node->table[i], strvec); + if (child_parsed == NULL) + goto fail; + if (ec_parsed_matches(child_parsed)) + break; + ec_parsed_free(child_parsed); + child_parsed = NULL; + } + + /* no match */ + if (i == node->len) + return parsed; + + match_strvec = ec_strvec_dup(child_parsed->strvec); + if (match_strvec == NULL) + goto fail; + + ec_parsed_set_match(parsed, gen_node, match_strvec); + ec_parsed_add_child(parsed, child_parsed); + + return parsed; + + fail: + ec_parsed_free(child_parsed); + ec_parsed_free(parsed); + return NULL; +} + +static struct ec_completed *ec_node_or_complete(const struct ec_node *gen_node, + const struct ec_strvec *strvec) +{ + struct ec_node_or *node = (struct ec_node_or *)gen_node; + struct ec_completed *completed, *child_completed; + size_t n; + + completed = ec_completed_new(); + if (completed == NULL) + return NULL; + + for (n = 0; n < node->len; n++) { + child_completed = ec_node_complete_strvec(node->table[n], + strvec); + + if (child_completed == NULL) // XXX fail instead? + continue; + + ec_completed_merge(completed, child_completed); + } + + return completed; +} + +static void ec_node_or_free_priv(struct ec_node *gen_node) +{ + struct ec_node_or *node = (struct ec_node_or *)gen_node; + unsigned int i; + + for (i = 0; i < node->len; i++) + ec_node_free(node->table[i]); + ec_free(node->table); +} + +int ec_node_or_add(struct ec_node *gen_node, struct ec_node *child) +{ + struct ec_node_or *node = (struct ec_node_or *)gen_node; + struct ec_node **table; + + assert(node != NULL); + + if (child == NULL) + return -EINVAL; + + gen_node->flags &= ~EC_NODE_F_BUILT; + + table = ec_realloc(node->table, (node->len + 1) * sizeof(*node->table)); + if (table == NULL) { + ec_node_free(child); + return -1; + } + + node->table = table; + table[node->len] = child; + node->len++; + + child->parent = gen_node; + TAILQ_INSERT_TAIL(&gen_node->children, child, next); + + return 0; +} + +static struct ec_node_type ec_node_or_type = { + .name = "or", + .parse = ec_node_or_parse, + .complete = ec_node_or_complete, + .size = sizeof(struct ec_node_or), + .free_priv = ec_node_or_free_priv, +}; + +EC_NODE_TYPE_REGISTER(ec_node_or_type); + +struct ec_node *__ec_node_or(const char *id, ...) +{ + struct ec_node *gen_node = NULL; + struct ec_node_or *node = NULL; + struct ec_node *child; + va_list ap; + int fail = 0; + + va_start(ap, id); + + gen_node = __ec_node_new(&ec_node_or_type, id); + node = (struct ec_node_or *)gen_node; + if (node == NULL) + fail = 1;; + + for (child = va_arg(ap, struct ec_node *); + child != EC_NODE_ENDLIST; + child = va_arg(ap, struct ec_node *)) { + + /* on error, don't quit the loop to avoid leaks */ + if (fail == 1 || child == NULL || + ec_node_or_add(gen_node, child) < 0) { + fail = 1; + ec_node_free(child); + } + } + + if (fail == 1) + goto fail; + + va_end(ap); + return gen_node; + +fail: + ec_node_free(gen_node); /* will also free children */ + va_end(ap); + return NULL; +} + +static int ec_node_or_testcase(void) +{ + struct ec_node *node; + int ret = 0; + + node = EC_NODE_OR(NULL, + ec_node_str(NULL, "foo"), + ec_node_str(NULL, "bar") + ); + if (node == NULL) { + ec_log(EC_LOG_ERR, "cannot create node\n"); + return -1; + } + ret |= EC_TEST_CHECK_PARSE(node, 1, "foo"); + ret |= EC_TEST_CHECK_PARSE(node, 1, "bar"); + ret |= EC_TEST_CHECK_PARSE(node, 1, "foo", "bar"); + ret |= EC_TEST_CHECK_PARSE(node, -1, " "); + ret |= EC_TEST_CHECK_PARSE(node, -1, "foox"); + ret |= EC_TEST_CHECK_PARSE(node, -1, "toto"); + ret |= EC_TEST_CHECK_PARSE(node, -1, ""); + ec_node_free(node); + + /* test completion */ + node = EC_NODE_OR(NULL, + ec_node_str(NULL, "foo"), + ec_node_str(NULL, "bar"), + ec_node_str(NULL, "bar2"), + ec_node_str(NULL, "toto"), + ec_node_str(NULL, "titi") + ); + if (node == NULL) { + ec_log(EC_LOG_ERR, "cannot create node\n"); + return -1; + } + ret |= EC_TEST_CHECK_COMPLETE(node, + "", EC_NODE_ENDLIST, + "foo", "bar", "bar2", "toto", "titi", EC_NODE_ENDLIST, + ""); + ret |= EC_TEST_CHECK_COMPLETE(node, + "f", EC_NODE_ENDLIST, + "oo", EC_NODE_ENDLIST, + "oo"); + ret |= EC_TEST_CHECK_COMPLETE(node, + "b", EC_NODE_ENDLIST, + "ar", "ar2", EC_NODE_ENDLIST, + "ar"); + ret |= EC_TEST_CHECK_COMPLETE(node, + "bar", EC_NODE_ENDLIST, + "", "2", EC_NODE_ENDLIST, + ""); + ret |= EC_TEST_CHECK_COMPLETE(node, + "t", EC_NODE_ENDLIST, + "oto", "iti", EC_NODE_ENDLIST, + ""); + ret |= EC_TEST_CHECK_COMPLETE(node, + "to", EC_NODE_ENDLIST, + "to", EC_NODE_ENDLIST, + "to"); + ret |= EC_TEST_CHECK_COMPLETE(node, + "x", EC_NODE_ENDLIST, + EC_NODE_ENDLIST, + ""); + ec_node_free(node); + + return ret; +} + +static struct ec_test ec_node_or_test = { + .name = "node_or", + .test = ec_node_or_testcase, +}; + +EC_TEST_REGISTER(ec_node_or_test); diff --git a/lib/ecoli_node_or.h b/lib/ecoli_node_or.h new file mode 100644 index 0000000..788cf37 --- /dev/null +++ b/lib/ecoli_node_or.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2016, Olivier MATZ + * + * 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 + +#define EC_NODE_OR(args...) __ec_node_or(args, EC_NODE_ENDLIST) + +/* list must be terminated with EC_NODE_ENDLIST */ +/* all nodes given in the list will be freed when freeing this one */ +/* avoid using this function directly, prefer the macro EC_NODE_OR() or + * ec_node_or() + ec_node_or_add() */ +struct ec_node *__ec_node_or(const char *id, ...); + +struct ec_node *ec_node_or(const char *id); + +/* child is consumed */ +int ec_node_or_add(struct ec_node *node, struct ec_node *child); + + +#endif diff --git a/lib/ecoli_node_re.c b/lib/ecoli_node_re.c new file mode 100644 index 0000000..d5c9b49 --- /dev/null +++ b/lib/ecoli_node_re.c @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2016, Olivier MATZ + * + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +struct ec_node_re { + struct ec_node gen; + char *re_str; + regex_t re; +}; + +static struct ec_parsed *ec_node_re_parse(const struct ec_node *gen_node, + const struct ec_strvec *strvec) +{ + struct ec_node_re *node = (struct ec_node_re *)gen_node; + struct ec_strvec *match_strvec; + struct ec_parsed *parsed = NULL; + const char *str; + regmatch_t pos; + + parsed = ec_parsed_new(); + if (parsed == NULL) + goto fail; + + if (ec_strvec_len(strvec) == 0) + return parsed; + + str = ec_strvec_val(strvec, 0); + if (regexec(&node->re, str, 1, &pos, 0) != 0) + return parsed; + if (pos.rm_so != 0 || pos.rm_eo != (int)strlen(str)) + return parsed; + + match_strvec = ec_strvec_ndup(strvec, 0, 1); + if (match_strvec == NULL) + goto fail; + + ec_parsed_set_match(parsed, gen_node, match_strvec); + + return parsed; + + fail: + ec_parsed_free(parsed); + return NULL; +} + +static void ec_node_re_free_priv(struct ec_node *gen_node) +{ + struct ec_node_re *node = (struct ec_node_re *)gen_node; + + ec_free(node->re_str); + regfree(&node->re); +} + +static struct ec_node_type ec_node_re_type = { + .name = "re", + .parse = ec_node_re_parse, + .complete = ec_node_default_complete, + .size = sizeof(struct ec_node_re), + .free_priv = ec_node_re_free_priv, +}; + +EC_NODE_TYPE_REGISTER(ec_node_re_type); + +int ec_node_re_set_regexp(struct ec_node *gen_node, const char *str) +{ + struct ec_node_re *node = (struct ec_node_re *)gen_node; + int ret; + + if (str == NULL) + return -EINVAL; + if (node->re_str != NULL) // XXX allow to replace + return -EEXIST; + + ret = -ENOMEM; + node->re_str = ec_strdup(str); + if (node->re_str == NULL) + goto fail; + + ret = -EINVAL; + if (regcomp(&node->re, node->re_str, REG_EXTENDED) != 0) + goto fail; + + return 0; + +fail: + ec_free(node->re_str); + return ret; +} + +struct ec_node *ec_node_re(const char *id, const char *re_str) +{ + struct ec_node *gen_node = NULL; + + gen_node = __ec_node_new(&ec_node_re_type, id); + if (gen_node == NULL) + goto fail; + + if (ec_node_re_set_regexp(gen_node, re_str) < 0) + goto fail; + + return gen_node; + +fail: + ec_node_free(gen_node); + return NULL; +} + +static int ec_node_re_testcase(void) +{ + struct ec_node *node; + int ret = 0; + + node = ec_node_re(NULL, "fo+|bar"); + if (node == NULL) { + ec_log(EC_LOG_ERR, "cannot create node\n"); + return -1; + } + ret |= EC_TEST_CHECK_PARSE(node, 1, "foo"); + ret |= EC_TEST_CHECK_PARSE(node, 1, "foo", "bar"); + ret |= EC_TEST_CHECK_PARSE(node, 1, "bar"); + ret |= EC_TEST_CHECK_PARSE(node, -1, "foobar"); + ret |= EC_TEST_CHECK_PARSE(node, -1, " foo"); + ret |= EC_TEST_CHECK_PARSE(node, -1, ""); + ec_node_free(node); + + return ret; +} + +static struct ec_test ec_node_re_test = { + .name = "node_re", + .test = ec_node_re_testcase, +}; + +EC_TEST_REGISTER(ec_node_re_test); diff --git a/lib/ecoli_node_re.h b/lib/ecoli_node_re.h new file mode 100644 index 0000000..e411678 --- /dev/null +++ b/lib/ecoli_node_re.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016, Olivier MATZ + * + * 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 + +struct ec_node *ec_node_re(const char *id, const char *str); + +/* re is duplicated */ +int ec_node_re_set_regexp(struct ec_node *node, const char *re); + +#endif diff --git a/lib/ecoli_node_re_lex.c b/lib/ecoli_node_re_lex.c new file mode 100644 index 0000000..f580b72 --- /dev/null +++ b/lib/ecoli_node_re_lex.c @@ -0,0 +1,273 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct regexp_pattern { + char *pattern; + regex_t r; + bool keep; +}; + +struct ec_node_re_lex { + struct ec_node gen; + struct ec_node *child; + struct regexp_pattern *table; + size_t len; +}; + +static struct ec_strvec * +tokenize(struct regexp_pattern *table, size_t table_len, const char *str) +{ + struct ec_strvec *strvec = NULL; + char *dup = NULL; + char c; + size_t len, off = 0; + size_t i; + int ret; + regmatch_t pos; + + dup = ec_strdup(str); + if (dup == NULL) + goto fail; + + strvec = ec_strvec_new(); + if (strvec == NULL) + goto fail; + + len = strlen(dup); + while (off < len) { + for (i = 0; i < table_len; i++) { + ret = regexec(&table[i].r, &dup[off], 1, &pos, 0); + if (ret != 0) + continue; + if (pos.rm_so != 0 || pos.rm_eo == 0) { + ret = -1; + continue; + } + + if (table[i].keep == 0) + break; + + c = dup[pos.rm_eo + off]; + dup[pos.rm_eo + off] = '\0'; + ec_log(EC_LOG_DEBUG, "re_lex match <%s>\n", &dup[off]); + if (ec_strvec_add(strvec, &dup[off]) < 0) + goto fail; + + dup[pos.rm_eo + off] = c; + break; + } + + if (ret != 0) + goto fail; + + off += pos.rm_eo; + } + + ec_free(dup); + return strvec; + +fail: + ec_free(dup); + ec_strvec_free(strvec); + return NULL; +} + +static struct ec_parsed *ec_node_re_lex_parse(const struct ec_node *gen_node, + const struct ec_strvec *strvec) +{ + struct ec_node_re_lex *node = (struct ec_node_re_lex *)gen_node; + struct ec_strvec *new_vec = NULL, *match_strvec; + struct ec_parsed *parsed = NULL, *child_parsed; + const char *str; + + parsed = ec_parsed_new(); + if (parsed == NULL) + return NULL; + + if (ec_strvec_len(strvec) == 0) + return parsed; + + str = ec_strvec_val(strvec, 0); + new_vec = tokenize(node->table, node->len, str); + if (new_vec == NULL) + goto fail; + + printf("--------\n"); + ec_strvec_dump(stdout, new_vec); + child_parsed = ec_node_parse_strvec(node->child, new_vec); + if (child_parsed == NULL) + goto fail; + + if (!ec_parsed_matches(child_parsed) || + ec_parsed_len(child_parsed) != + ec_strvec_len(new_vec)) { + ec_strvec_free(new_vec); + ec_parsed_free(child_parsed); + return parsed; + } + ec_strvec_free(new_vec); + new_vec = NULL; + + ec_parsed_add_child(parsed, child_parsed); + match_strvec = ec_strvec_ndup(strvec, 0, 1); + if (match_strvec == NULL) + goto fail; + ec_parsed_set_match(parsed, gen_node, match_strvec); + + return parsed; + + fail: + ec_strvec_free(new_vec); + ec_parsed_free(parsed); + + return NULL; +} + +static void ec_node_re_lex_free_priv(struct ec_node *gen_node) +{ + struct ec_node_re_lex *node = (struct ec_node_re_lex *)gen_node; + unsigned int i; + + for (i = 0; i < node->len; i++) { + ec_free(node->table[i].pattern); + regfree(&node->table[i].r); + } + + ec_free(node->table); + ec_node_free(node->child); +} + +static struct ec_node_type ec_node_re_lex_type = { + .name = "re_lex", + .parse = ec_node_re_lex_parse, + //.complete = ec_node_re_lex_complete, //XXX + .size = sizeof(struct ec_node_re_lex), + .free_priv = ec_node_re_lex_free_priv, +}; + +EC_NODE_TYPE_REGISTER(ec_node_re_lex_type); + +int ec_node_re_lex_add(struct ec_node *gen_node, const char *pattern, int keep) +{ + struct ec_node_re_lex *node = (struct ec_node_re_lex *)gen_node; + struct regexp_pattern *table; + int ret; + char *pat_dup = NULL; + + ret = -ENOMEM; + pat_dup = ec_strdup(pattern); + if (pat_dup == NULL) + goto fail; + + ret = -ENOMEM; + table = ec_realloc(node->table, sizeof(*table) * (node->len + 1)); + if (table == NULL) + goto fail; + + ret = regcomp(&table[node->len].r, pattern, REG_EXTENDED); + if (ret != 0) { + ec_log(EC_LOG_ERR, + "Regular expression <%s> compilation failed: %d\n", + pattern, ret); + if (ret == REG_ESPACE) + ret = -ENOMEM; + else + ret = -EINVAL; + + goto fail; + } + + table[node->len].pattern = pat_dup; + table[node->len].keep = keep; + node->len++; + node->table = table; + + return 0; + +fail: + ec_free(pat_dup); + return ret; +} + +struct ec_node *ec_node_re_lex(const char *id, struct ec_node *child) +{ + struct ec_node_re_lex *node = NULL; + + if (child == NULL) + return NULL; + + node = (struct ec_node_re_lex *)__ec_node_new(&ec_node_re_lex_type, id); + if (node == NULL) { + ec_node_free(child); + return NULL; + } + + node->child = child; + + return &node->gen; +} + + +static int ec_node_re_lex_testcase(void) +{ + struct ec_node *node; + int ret = 0; + + node = ec_node_re_lex(NULL, + ec_node_many(NULL, + EC_NODE_OR(NULL, + ec_node_str(NULL, "foo"), + ec_node_str(NULL, "bar"), + ec_node_int(NULL, 0, 1000, 0) + ), 0, 0 + ) + ); + if (node == NULL) { + ec_log(EC_LOG_ERR, "cannot create node\n"); + return -1; + } + + /* XXX add ^ automatically ? */ + ret |= ec_node_re_lex_add(node, "[a-zA-Z]+", 1); + ret |= ec_node_re_lex_add(node, "[0-9]+", 1); + ret |= ec_node_re_lex_add(node, "=", 1); + ret |= ec_node_re_lex_add(node, "-", 1); + ret |= ec_node_re_lex_add(node, "\\+", 1); + ret |= ec_node_re_lex_add(node, "[ ]+", 0); + if (ret != 0) { + ec_log(EC_LOG_ERR, "cannot add regexp to node\n"); + ec_node_free(node); + return -1; + } + + ret |= EC_TEST_CHECK_PARSE(node, 1, " foo bar 324 bar234"); + ret |= EC_TEST_CHECK_PARSE(node, 1, "foo bar324"); + ret |= EC_TEST_CHECK_PARSE(node, 1, ""); + ret |= EC_TEST_CHECK_PARSE(node, -1, "foobar"); + + ec_node_free(node); + + return ret; +} + +static struct ec_test ec_node_re_lex_test = { + .name = "node_re_lex", + .test = ec_node_re_lex_testcase, +}; + +EC_TEST_REGISTER(ec_node_re_lex_test); diff --git a/lib/ecoli_node_re_lex.h b/lib/ecoli_node_re_lex.h new file mode 100644 index 0000000..fb72786 --- /dev/null +++ b/lib/ecoli_node_re_lex.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2016-2017, Olivier MATZ + * + * 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 + +struct ec_node *ec_node_re_lex(const char *id, struct ec_node *child); + +int ec_node_re_lex_add(struct ec_node *gen_node, const char *pattern, int keep); + +#endif diff --git a/lib/ecoli_node_seq.c b/lib/ecoli_node_seq.c new file mode 100644 index 0000000..11312eb --- /dev/null +++ b/lib/ecoli_node_seq.c @@ -0,0 +1,323 @@ +/* + * Copyright (c) 2016, Olivier MATZ + * + * 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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +struct ec_node_seq { + struct ec_node gen; + struct ec_node **table; + unsigned int len; +}; + +static struct ec_parsed *ec_node_seq_parse(const struct ec_node *gen_node, + const struct ec_strvec *strvec) +{ + struct ec_node_seq *node = (struct ec_node_seq *)gen_node; + struct ec_parsed *parsed, *child_parsed; + struct ec_strvec *match_strvec; + struct ec_strvec *childvec = NULL; + size_t len = 0; + unsigned int i; + + parsed = ec_parsed_new(); + if (parsed == NULL) + goto fail; + + for (i = 0; i < node->len; i++) { + childvec = ec_strvec_ndup(strvec, len, + ec_strvec_len(strvec) - len); + if (childvec == NULL) + goto fail; + + child_parsed = ec_node_parse_strvec(node->table[i], childvec); + if (child_parsed == NULL) + goto fail; + + ec_strvec_free(childvec); + childvec = NULL; + + if (!ec_parsed_matches(child_parsed)) { + ec_parsed_free(child_parsed); + // XXX ec_parsed_free_children needed? see subset.c + ec_parsed_free_children(parsed); + return parsed; + } + + ec_parsed_add_child(parsed, child_parsed); + len += ec_parsed_len(child_parsed); + } + + match_strvec = ec_strvec_ndup(strvec, 0, len); + if (match_strvec == NULL) + goto fail; + + ec_parsed_set_match(parsed, gen_node, match_strvec); + + return parsed; + +fail: + ec_strvec_free(childvec); + ec_parsed_free(parsed); + return NULL; +} + +static struct ec_completed *ec_node_seq_complete(const struct ec_node *gen_node, + const struct ec_strvec *strvec) +{ + struct ec_node_seq *node = (struct ec_node_seq *)gen_node; + struct ec_completed *completed, *child_completed; + struct ec_strvec *childvec = NULL; + struct ec_parsed *parsed; + size_t len = 0; + unsigned int i; + + completed = ec_completed_new(); + if (completed == NULL) + return NULL; + + if (node->len == 0) + return completed; + + for (i = 0; i < node->len && len < ec_strvec_len(strvec); i++) { + childvec = ec_strvec_ndup(strvec, len, + ec_strvec_len(strvec) - len); + if (childvec == NULL) + goto fail; + + child_completed = ec_node_complete_strvec(node->table[i], + childvec); + if (child_completed == NULL) + goto fail; + + ec_completed_merge(completed, child_completed); + + parsed = ec_node_parse_strvec(node->table[i], childvec); + if (parsed == NULL) + goto fail; + + ec_strvec_free(childvec); + childvec = NULL; + + if (!ec_parsed_matches(parsed)) { + ec_parsed_free(parsed); + break; + } + + len += ec_strvec_len(parsed->strvec); + ec_parsed_free(parsed); + } + + return completed; + +fail: + ec_strvec_free(childvec); + ec_completed_free(completed); + return NULL; +} + +static void ec_node_seq_free_priv(struct ec_node *gen_node) +{ + struct ec_node_seq *node = (struct ec_node_seq *)gen_node; + unsigned int i; + + for (i = 0; i < node->len; i++) + ec_node_free(node->table[i]); + ec_free(node->table); +} + +static struct ec_node_type ec_node_seq_type = { + .name = "seq", + .parse = ec_node_seq_parse, + .complete = ec_node_seq_complete, + .size = sizeof(struct ec_node_seq), + .free_priv = ec_node_seq_free_priv, +}; + +EC_NODE_TYPE_REGISTER(ec_node_seq_type); + +int ec_node_seq_add(struct ec_node *gen_node, struct ec_node *child) +{ + struct ec_node_seq *node = (struct ec_node_seq *)gen_node; + struct ec_node **table; + + // XXX check node type + + assert(node != NULL); + + if (child == NULL) + return -EINVAL; + + gen_node->flags &= ~EC_NODE_F_BUILT; + + table = ec_realloc(node->table, (node->len + 1) * sizeof(*node->table)); + if (table == NULL) { + ec_node_free(child); + return -1; + } + + node->table = table; + table[node->len] = child; + node->len++; + + child->parent = gen_node; + TAILQ_INSERT_TAIL(&gen_node->children, child, next); // XXX really needed? + + return 0; +} + +struct ec_node *__ec_node_seq(const char *id, ...) +{ + struct ec_node *gen_node = NULL; + struct ec_node_seq *node = NULL; + struct ec_node *child; + va_list ap; + int fail = 0; + + va_start(ap, id); + + gen_node = __ec_node_new(&ec_node_seq_type, id); + node = (struct ec_node_seq *)gen_node; + if (node == NULL) + fail = 1;; + + for (child = va_arg(ap, struct ec_node *); + child != EC_NODE_ENDLIST; + child = va_arg(ap, struct ec_node *)) { + + /* on error, don't quit the loop to avoid leaks */ + if (fail == 1 || child == NULL || + ec_node_seq_add(&node->gen, child) < 0) { + fail = 1; + ec_node_free(child); + } + } + + if (fail == 1) + goto fail; + + va_end(ap); + return gen_node; + +fail: + ec_node_free(gen_node); /* will also free children */ + va_end(ap); + return NULL; +} + +static int ec_node_seq_testcase(void) +{ + struct ec_node *node; + int ret = 0; + + node = EC_NODE_SEQ(NULL, + ec_node_str(NULL, "foo"), + ec_node_str(NULL, "bar") + ); + if (node == NULL) { + ec_log(EC_LOG_ERR, "cannot create node\n"); + return -1; + } + ret |= EC_TEST_CHECK_PARSE(node, 2, "foo", "bar"); + ret |= EC_TEST_CHECK_PARSE(node, 2, "foo", "bar", "toto"); + ret |= EC_TEST_CHECK_PARSE(node, -1, "foo"); + ret |= EC_TEST_CHECK_PARSE(node, -1, "foox", "bar"); + ret |= EC_TEST_CHECK_PARSE(node, -1, "foo", "barx"); + ret |= EC_TEST_CHECK_PARSE(node, -1, "bar", "foo"); + ret |= EC_TEST_CHECK_PARSE(node, -1, "", "foo"); + ec_node_free(node); + + /* test completion */ + node = EC_NODE_SEQ(NULL, + ec_node_str(NULL, "foo"), + ec_node_option(NULL, ec_node_str(NULL, "toto")), + ec_node_str(NULL, "bar") + ); + if (node == NULL) { + ec_log(EC_LOG_ERR, "cannot create node\n"); + return -1; + } + ret |= EC_TEST_CHECK_COMPLETE(node, + "", EC_NODE_ENDLIST, + "foo", EC_NODE_ENDLIST, + "foo"); + ret |= EC_TEST_CHECK_COMPLETE(node, + "f", EC_NODE_ENDLIST, + "oo", EC_NODE_ENDLIST, + "oo"); + ret |= EC_TEST_CHECK_COMPLETE(node, + "foo", EC_NODE_ENDLIST, + "", EC_NODE_ENDLIST, + ""); + ret |= EC_TEST_CHECK_COMPLETE(node, + "foo", "", EC_NODE_ENDLIST, + "bar", "toto", EC_NODE_ENDLIST, + ""); + ret |= EC_TEST_CHECK_COMPLETE(node, + "foo", "t", EC_NODE_ENDLIST, + "oto", EC_NODE_ENDLIST, + "oto"); + ret |= EC_TEST_CHECK_COMPLETE(node, + "foo", "b", EC_NODE_ENDLIST, + "ar", EC_NODE_ENDLIST, + "ar"); + ret |= EC_TEST_CHECK_COMPLETE(node, + "foo", "bar", EC_NODE_ENDLIST, + "", EC_NODE_ENDLIST, + ""); + ret |= EC_TEST_CHECK_COMPLETE(node, + "x", EC_NODE_ENDLIST, + EC_NODE_ENDLIST, + ""); + ret |= EC_TEST_CHECK_COMPLETE(node, + "foobarx", EC_NODE_ENDLIST, + EC_NODE_ENDLIST, + ""); + ec_node_free(node); + + return ret; +} + +static struct ec_test ec_node_seq_test = { + .name = "node_seq", + .test = ec_node_seq_testcase, +}; + +EC_TEST_REGISTER(ec_node_seq_test); diff --git a/lib/ecoli_node_seq.h b/lib/ecoli_node_seq.h new file mode 100644 index 0000000..741539c --- /dev/null +++ b/lib/ecoli_node_seq.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2016, Olivier MATZ + * + * 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 + +#define EC_NODE_SEQ(args...) __ec_node_seq(args, EC_NODE_ENDLIST) + +/* list must be terminated with EC_NODE_ENDLIST */ +/* all nodes given in the list will be freed when freeing this one */ +/* avoid using this function directly, prefer the macro EC_NODE_SEQ() or + * ec_node_seq() + ec_node_seq_add() */ +struct ec_node *__ec_node_seq(const char *id, ...); + +struct ec_node *ec_node_seq(const char *id); + +/* child is consumed */ +// XXX add_child? +int ec_node_seq_add(struct ec_node *node, struct ec_node *child); + +#endif diff --git a/lib/ecoli_node_sh_lex.c b/lib/ecoli_node_sh_lex.c new file mode 100644 index 0000000..549faec --- /dev/null +++ b/lib/ecoli_node_sh_lex.c @@ -0,0 +1,457 @@ +/* + * Copyright (c) 2016, Olivier MATZ + * + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct ec_node_sh_lex { + struct ec_node gen; + struct ec_node *child; +}; + +static size_t eat_spaces(const char *str) +{ + size_t i = 0; + + /* skip spaces */ + while (isblank(str[i])) + i++; + + return i; +} + +/* + * Allocate a new string which is a copy of the input string with quotes + * removed. If quotes are not closed properly, set missing_quote to the + * missing quote char. + */ +static char *unquote_str(const char *str, size_t n, int allow_missing_quote, + char *missing_quote) +{ + unsigned s = 1, d = 0; + char quote = str[0]; + char *dst; + int closed = 0; + + dst = ec_malloc(n); + if (dst == NULL) { + errno = ENOMEM; + return NULL; + } + + /* copy string and remove quotes */ + while (s < n && d < n && str[s] != '\0') { + if (str[s] == '\\' && str[s+1] == quote) { + dst[d++] = quote; + s += 2; + continue; + } + if (str[s] == '\\' && str[s+1] == '\\') { + dst[d++] = '\\'; + s += 2; + continue; + } + if (str[s] == quote) { + s++; + closed = 1; + break; + } + dst[d++] = str[s++]; + } + + /* not enough room in dst buffer (should not happen) */ + if (d >= n) { + ec_free(dst); + errno = EMSGSIZE; + return NULL; + } + + /* quote not closed */ + if (closed == 0) { + if (missing_quote != NULL) + *missing_quote = str[0]; + if (allow_missing_quote == 0) { + ec_free(dst); + errno = EINVAL; + return NULL; + } + } + dst[d++] = '\0'; + + return dst; +} + +static size_t eat_quoted_str(const char *str) +{ + size_t i = 0; + char quote = str[0]; + + while (str[i] != '\0') { + if (str[i] != '\\' && str[i+1] == quote) + return i + 2; + i++; + } + + /* unclosed quote, will be detected later */ + return i; +} + +static size_t eat_str(const char *str) +{ + size_t i = 0; + + /* skip spaces */ + while (!isblank(str[i]) && str[i] != '\0') + i++; + + return i; +} + +static struct ec_strvec *tokenize(const char *str, int completion, + int allow_missing_quote, char *missing_quote) +{ + struct ec_strvec *strvec = NULL; + size_t off = 0, len, suboff, sublen; + char *word = NULL, *concat = NULL, *tmp; + int last_is_space = 1; + +// printf("str=%s\n", str); + + strvec = ec_strvec_new(); + if (strvec == NULL) + goto fail; + + while (str[off] != '\0') { + len = eat_spaces(&str[off]); + if (len > 0) + last_is_space = 1; +// printf("space=%zd\n", len); + off += len; + + len = 0; + suboff = off; + while (str[suboff] != '\0') { + last_is_space = 0; + if (str[suboff] == '"' || str[suboff] == '\'') { + sublen = eat_quoted_str(&str[suboff]); +// printf("sublen=%zd\n", sublen); + word = unquote_str(&str[suboff], sublen, + allow_missing_quote, missing_quote); + } else { + sublen = eat_str(&str[suboff]); +// printf("sublen=%zd\n", sublen); + if (sublen == 0) + break; + word = ec_strndup(&str[suboff], sublen); + } + + if (word == NULL) + goto fail; +// printf("word=%s\n", word); + + len += sublen; + suboff += sublen; + + if (concat == NULL) { + concat = word; + word = NULL; + } else { + tmp = ec_realloc(concat, len + 1); + if (tmp == NULL) + goto fail; + concat = tmp; + strcat(concat, word); + ec_free(word); + word = NULL; + } + } + + if (concat != NULL) { + if (ec_strvec_add(strvec, concat) < 0) + goto fail; + ec_free(concat); + concat = NULL; + } + + /* XXX remove all printf comments */ +// printf("str off=%zd len=%zd\n", off, len); + off += len; + } + + /* in completion mode, append an empty string in the vector if + * the input string ends with space */ + if (completion && last_is_space) { + if (ec_strvec_add(strvec, "") < 0) + goto fail; + } + + return strvec; + + fail: + ec_free(word); + ec_free(concat); + ec_strvec_free(strvec); + return NULL; +} + +static struct ec_parsed *ec_node_sh_lex_parse(const struct ec_node *gen_node, + const struct ec_strvec *strvec) +{ + struct ec_node_sh_lex *node = (struct ec_node_sh_lex *)gen_node; + struct ec_strvec *new_vec = NULL, *match_strvec; + struct ec_parsed *parsed = NULL, *child_parsed; + const char *str; + + parsed = ec_parsed_new(); + if (parsed == NULL) + return NULL; + + if (ec_strvec_len(strvec) == 0) + return parsed; + + str = ec_strvec_val(strvec, 0); + new_vec = tokenize(str, 0, 0, NULL); + if (new_vec == NULL) + goto fail; + + child_parsed = ec_node_parse_strvec(node->child, new_vec); + if (child_parsed == NULL) + goto fail; + + if (!ec_parsed_matches(child_parsed) || + ec_parsed_len(child_parsed) != + ec_strvec_len(new_vec)) { + ec_strvec_free(new_vec); + ec_parsed_free(child_parsed); + return parsed; + } + ec_strvec_free(new_vec); + new_vec = NULL; + + ec_parsed_add_child(parsed, child_parsed); + match_strvec = ec_strvec_ndup(strvec, 0, 1); + if (match_strvec == NULL) + goto fail; + ec_parsed_set_match(parsed, gen_node, match_strvec); + + return parsed; + + fail: + ec_strvec_free(new_vec); + ec_parsed_free(parsed); + + return NULL; +} + +static struct ec_completed *ec_node_sh_lex_complete(const struct ec_node *gen_node, + const struct ec_strvec *strvec) +{ + struct ec_node_sh_lex *node = (struct ec_node_sh_lex *)gen_node; + struct ec_completed *completed, *child_completed = NULL; + struct ec_strvec *new_vec = NULL; + const char *str; + char missing_quote; + +// printf("==================\n"); + completed = ec_completed_new(); + if (completed == NULL) + return NULL; + + if (ec_strvec_len(strvec) != 1) + return completed; + + str = ec_strvec_val(strvec, 0); + new_vec = tokenize(str, 1, 1, &missing_quote); + if (new_vec == NULL) + goto fail; + +// ec_strvec_dump(new_vec, stdout); + + child_completed = ec_node_complete_strvec(node->child, new_vec); + if (child_completed == NULL) + goto fail; + + ec_strvec_free(new_vec); + new_vec = NULL; + ec_completed_merge(completed, child_completed); + + return completed; + + fail: + ec_strvec_free(new_vec); + ec_completed_free(completed); + return NULL; +} + +static void ec_node_sh_lex_free_priv(struct ec_node *gen_node) +{ + struct ec_node_sh_lex *node = (struct ec_node_sh_lex *)gen_node; + + ec_node_free(node->child); +} + +static struct ec_node_type ec_node_sh_lex_type = { + .name = "sh_lex", + .parse = ec_node_sh_lex_parse, + .complete = ec_node_sh_lex_complete, + .size = sizeof(struct ec_node_sh_lex), + .free_priv = ec_node_sh_lex_free_priv, +}; + +EC_NODE_TYPE_REGISTER(ec_node_sh_lex_type); + +struct ec_node *ec_node_sh_lex(const char *id, struct ec_node *child) +{ + struct ec_node_sh_lex *node = NULL; + + if (child == NULL) + return NULL; + + node = (struct ec_node_sh_lex *)__ec_node_new(&ec_node_sh_lex_type, id); + if (node == NULL) { + ec_node_free(child); + return NULL; + } + + node->child = child; + + return &node->gen; +} + +static int ec_node_sh_lex_testcase(void) +{ + struct ec_node *node; + int ret = 0; + + node = ec_node_sh_lex(NULL, + EC_NODE_SEQ(NULL, + ec_node_str(NULL, "foo"), + ec_node_option(NULL, + ec_node_str(NULL, "toto") + ), + ec_node_str(NULL, "bar") + ) + ); + if (node == NULL) { + ec_log(EC_LOG_ERR, "cannot create node\n"); + return -1; + } + ret |= EC_TEST_CHECK_PARSE(node, 1, "foo bar"); + ret |= EC_TEST_CHECK_PARSE(node, 1, " foo bar"); + ret |= EC_TEST_CHECK_PARSE(node, 1, " 'foo' \"bar\""); + ret |= EC_TEST_CHECK_PARSE(node, 1, " 'f'oo 'toto' bar"); + ec_node_free(node); + + /* test completion */ + node = ec_node_sh_lex(NULL, + EC_NODE_SEQ(NULL, + ec_node_str(NULL, "foo"), + ec_node_option(NULL, + ec_node_str(NULL, "toto") + ), + ec_node_str(NULL, "bar"), + ec_node_str(NULL, "titi") + ) + ); + if (node == NULL) { + ec_log(EC_LOG_ERR, "cannot create node\n"); + return -1; + } + ret |= EC_TEST_CHECK_COMPLETE(node, + "", EC_NODE_ENDLIST, + "foo", EC_NODE_ENDLIST, + "foo"); + ret |= EC_TEST_CHECK_COMPLETE(node, + " ", EC_NODE_ENDLIST, + "foo", EC_NODE_ENDLIST, + "foo"); + ret |= EC_TEST_CHECK_COMPLETE(node, + "f", EC_NODE_ENDLIST, + "oo", EC_NODE_ENDLIST, + "oo"); + ret |= EC_TEST_CHECK_COMPLETE(node, + "foo", EC_NODE_ENDLIST, + "", EC_NODE_ENDLIST, + ""); + ret |= EC_TEST_CHECK_COMPLETE(node, + "foo ", EC_NODE_ENDLIST, + "bar", "toto", EC_NODE_ENDLIST, + ""); + ret |= EC_TEST_CHECK_COMPLETE(node, + "foo t", EC_NODE_ENDLIST, + "oto", EC_NODE_ENDLIST, + "oto"); + ret |= EC_TEST_CHECK_COMPLETE(node, + "foo b", EC_NODE_ENDLIST, + "ar", EC_NODE_ENDLIST, + "ar"); + ret |= EC_TEST_CHECK_COMPLETE(node, + "foo bar", EC_NODE_ENDLIST, + "", EC_NODE_ENDLIST, + ""); + ret |= EC_TEST_CHECK_COMPLETE(node, + "foo bar ", EC_NODE_ENDLIST, + "titi", EC_NODE_ENDLIST, + "titi"); + ret |= EC_TEST_CHECK_COMPLETE(node, + "foo toto bar ", EC_NODE_ENDLIST, + "titi", EC_NODE_ENDLIST, + "titi"); + ret |= EC_TEST_CHECK_COMPLETE(node, + "x", EC_NODE_ENDLIST, + EC_NODE_ENDLIST, + ""); + ret |= EC_TEST_CHECK_COMPLETE(node, + "foo barx", EC_NODE_ENDLIST, + EC_NODE_ENDLIST, + ""); + + ec_node_free(node); + return ret; +} + +static struct ec_test ec_node_sh_lex_test = { + .name = "node_sh_lex", + .test = ec_node_sh_lex_testcase, +}; + +EC_TEST_REGISTER(ec_node_sh_lex_test); diff --git a/lib/ecoli_node_sh_lex.h b/lib/ecoli_node_sh_lex.h new file mode 100644 index 0000000..2fc2806 --- /dev/null +++ b/lib/ecoli_node_sh_lex.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2016, Olivier MATZ + * + * 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 + +struct ec_node *ec_node_sh_lex(const char *id, struct ec_node *child); + +#endif diff --git a/lib/ecoli_node_space.c b/lib/ecoli_node_space.c new file mode 100644 index 0000000..3efce53 --- /dev/null +++ b/lib/ecoli_node_space.c @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2016, Olivier MATZ + * + * 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +struct ec_node_space { + struct ec_node gen; +}; + +static struct ec_parsed *ec_node_space_parse(const struct ec_node *gen_node, + const struct ec_strvec *strvec) +{ + struct ec_parsed *parsed = NULL; + struct ec_strvec *match_strvec; + const char *str; + size_t len = 0; + + parsed = ec_parsed_new(); + if (parsed == NULL) + goto fail; + + if (ec_strvec_len(strvec) == 0) + return parsed; + + str = ec_strvec_val(strvec, 0); + while (isspace(str[len])) + len++; + if (len == 0 || len != strlen(str)) + return parsed; + + match_strvec = ec_strvec_ndup(strvec, 0, 1); + if (match_strvec == NULL) + goto fail; + + ec_parsed_set_match(parsed, gen_node, match_strvec); + + return parsed; + + fail: + ec_parsed_free(parsed); + return NULL; +} + +static struct ec_node_type ec_node_space_type = { + .name = "space", + .parse = ec_node_space_parse, + .complete = ec_node_default_complete, + .size = sizeof(struct ec_node_space), +}; + +EC_NODE_TYPE_REGISTER(ec_node_space_type); + +static int ec_node_space_testcase(void) +{ + struct ec_node *node; + int ret = 0; + + node = ec_node_new("space", NULL); + if (node == NULL) { + ec_log(EC_LOG_ERR, "cannot create node\n"); + return -1; + } + ret |= EC_TEST_CHECK_PARSE(node, 1, " "); + ret |= EC_TEST_CHECK_PARSE(node, 1, " ", "foo"); + ret |= EC_TEST_CHECK_PARSE(node, -1, ""); + ret |= EC_TEST_CHECK_PARSE(node, -1, " foo"); + ret |= EC_TEST_CHECK_PARSE(node, -1, "foo "); + ec_node_free(node); + + /* test completion */ + node = ec_node_new("space", NULL); + if (node == NULL) { + ec_log(EC_LOG_ERR, "cannot create node\n"); + return -1; + } + ret |= EC_TEST_CHECK_COMPLETE(node, + "", EC_NODE_ENDLIST, + EC_NODE_ENDLIST, + ""); + ret |= EC_TEST_CHECK_COMPLETE(node, + " ", EC_NODE_ENDLIST, + EC_NODE_ENDLIST, + ""); + ret |= EC_TEST_CHECK_COMPLETE(node, + "foo", EC_NODE_ENDLIST, + EC_NODE_ENDLIST, + ""); + ec_node_free(node); + + return ret; +} + +static struct ec_test ec_node_space_test = { + .name = "space", + .test = ec_node_space_testcase, +}; + +EC_TEST_REGISTER(ec_node_space_test); diff --git a/lib/ecoli_node_space.h b/lib/ecoli_node_space.h new file mode 100644 index 0000000..becefcb --- /dev/null +++ b/lib/ecoli_node_space.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * This node matches one string in the vector if it is only composed of + * spaces, as interpreted by isspace(). + */ + +#ifndef ECOLI_NODE_SPACE_ +#define ECOLI_NODE_SPACE_ + +/* no API for now, since there is no specific configuration for this node */ + +#endif diff --git a/lib/ecoli_node_str.c b/lib/ecoli_node_str.c new file mode 100644 index 0000000..c30b9dd --- /dev/null +++ b/lib/ecoli_node_str.c @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2016, Olivier MATZ + * + * 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +struct ec_node_str { + struct ec_node gen; + char *string; + unsigned len; +}; + +static struct ec_parsed *ec_node_str_parse(const struct ec_node *gen_node, + const struct ec_strvec *strvec) +{ + struct ec_node_str *node = (struct ec_node_str *)gen_node; + struct ec_strvec *match_strvec; + struct ec_parsed *parsed = NULL; + const char *str; + + parsed = ec_parsed_new(); + if (parsed == NULL) + goto fail; + + if (ec_strvec_len(strvec) == 0) + return parsed; + + str = ec_strvec_val(strvec, 0); + if (strcmp(str, node->string) != 0) + return parsed; + + match_strvec = ec_strvec_ndup(strvec, 0, 1); + if (match_strvec == NULL) + goto fail; + + ec_parsed_set_match(parsed, gen_node, match_strvec); + + return parsed; + + fail: + ec_parsed_free(parsed); + return NULL; +} + +static struct ec_completed *ec_node_str_complete(const struct ec_node *gen_node, + const struct ec_strvec *strvec) +{ + struct ec_node_str *node = (struct ec_node_str *)gen_node; + struct ec_completed *completed; + struct ec_completed_elt *completed_elt; + const char *str, *add; + size_t n = 0; + + completed = ec_completed_new(); + if (completed == NULL) + return NULL; + + if (ec_strvec_len(strvec) != 1) + return completed; + + str = ec_strvec_val(strvec, 0); + for (n = 0; n < node->len; n++) { + if (str[n] != node->string[n]) + break; + } + + if (str[n] != '\0') + add = NULL; + else + add = node->string + n; + + completed_elt = ec_completed_elt_new(gen_node, add); + if (completed_elt == NULL) { + ec_completed_free(completed); + return NULL; + } + + ec_completed_add_elt(completed, completed_elt); + + return completed; +} + +static const char *ec_node_str_desc(const struct ec_node *gen_node) +{ + struct ec_node_str *node = (struct ec_node_str *)gen_node; + + return node->string; +} + +static void ec_node_str_free_priv(struct ec_node *gen_node) +{ + struct ec_node_str *node = (struct ec_node_str *)gen_node; + + ec_free(node->string); +} + +static struct ec_node_type ec_node_str_type = { + .name = "str", + .parse = ec_node_str_parse, + .complete = ec_node_str_complete, + .desc = ec_node_str_desc, + .size = sizeof(struct ec_node_str), + .free_priv = ec_node_str_free_priv, +}; + +EC_NODE_TYPE_REGISTER(ec_node_str_type); + +int ec_node_str_set_str(struct ec_node *gen_node, const char *str) +{ + struct ec_node_str *node = (struct ec_node_str *)gen_node; + + if (str == NULL) + return -EINVAL; + if (node->string != NULL) + return -EEXIST; // XXX allow to replace + + node->string = ec_strdup(str); + if (node->string == NULL) + return -ENOMEM; + + node->len = strlen(node->string); + + return 0; +} + +struct ec_node *ec_node_str(const char *id, const char *str) +{ + struct ec_node *gen_node = NULL; + + gen_node = __ec_node_new(&ec_node_str_type, id); + if (gen_node == NULL) + goto fail; + + if (ec_node_str_set_str(gen_node, str) < 0) + goto fail; + + return gen_node; + +fail: + ec_node_free(gen_node); + return NULL; +} + +static int ec_node_str_testcase(void) +{ + struct ec_node *node; + int ret = 0; + + /* XXX use EC_NO_ID instead of NULL */ + node = ec_node_str(NULL, "foo"); + if (node == NULL) { + ec_log(EC_LOG_ERR, "cannot create node\n"); + return -1; + } + ret |= EC_TEST_CHECK_PARSE(node, 1, "foo"); + ret |= EC_TEST_CHECK_PARSE(node, 1, "foo", "bar"); + ret |= EC_TEST_CHECK_PARSE(node, -1, "foobar"); + ret |= EC_TEST_CHECK_PARSE(node, -1, " foo"); + ret |= EC_TEST_CHECK_PARSE(node, -1, ""); + ec_node_free(node); + + node = ec_node_str(NULL, "Здравствуйте"); + if (node == NULL) { + ec_log(EC_LOG_ERR, "cannot create node\n"); + return -1; + } + ret |= EC_TEST_CHECK_PARSE(node, 1, "Здравствуйте"); + ret |= EC_TEST_CHECK_PARSE(node, 1, "Здравствуйте", + "John!"); + ret |= EC_TEST_CHECK_PARSE(node, -1, "foo"); + ret |= EC_TEST_CHECK_PARSE(node, -1, ""); + ec_node_free(node); + + /* an empty string node always matches */ + node = ec_node_str(NULL, ""); + if (node == NULL) { + ec_log(EC_LOG_ERR, "cannot create node\n"); + return -1; + } + ret |= EC_TEST_CHECK_PARSE(node, 1, ""); + ret |= EC_TEST_CHECK_PARSE(node, 1, "", "foo"); + ret |= EC_TEST_CHECK_PARSE(node, -1, "foo"); + ec_node_free(node); + + /* test completion */ + node = ec_node_str(NULL, "foo"); + if (node == NULL) { + ec_log(EC_LOG_ERR, "cannot create node\n"); + return -1; + } + ret |= EC_TEST_CHECK_COMPLETE(node, + EC_NODE_ENDLIST, + EC_NODE_ENDLIST, + ""); + ret |= EC_TEST_CHECK_COMPLETE(node, + "", EC_NODE_ENDLIST, + "foo", EC_NODE_ENDLIST, + "foo"); + ret |= EC_TEST_CHECK_COMPLETE(node, + "f", EC_NODE_ENDLIST, + "oo", EC_NODE_ENDLIST, + "oo"); + ret |= EC_TEST_CHECK_COMPLETE(node, + "foo", EC_NODE_ENDLIST, + "", EC_NODE_ENDLIST, + ""); + ret |= EC_TEST_CHECK_COMPLETE(node, + "x", EC_NODE_ENDLIST, + EC_NODE_ENDLIST, + ""); + ec_node_free(node); + + return ret; +} + +static struct ec_test ec_node_str_test = { + .name = "node_str", + .test = ec_node_str_testcase, +}; + +EC_TEST_REGISTER(ec_node_str_test); diff --git a/lib/ecoli_node_str.h b/lib/ecoli_node_str.h new file mode 100644 index 0000000..611851e --- /dev/null +++ b/lib/ecoli_node_str.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2016, Olivier MATZ + * + * 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 + +struct ec_node *ec_node_str(const char *id, const char *str); + +struct ec_node *ec_node_str_new(const char *id); + +/* str is duplicated */ +int ec_node_str_set_str(struct ec_node *node, const char *str); + +#endif diff --git a/lib/ecoli_node_subset.c b/lib/ecoli_node_subset.c new file mode 100644 index 0000000..ff9ecca --- /dev/null +++ b/lib/ecoli_node_subset.c @@ -0,0 +1,479 @@ +/* + * Copyright (c) 2016-2017, Olivier MATZ + * + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +struct ec_node_subset { + struct ec_node gen; + struct ec_node **table; + unsigned int len; +}; + +struct parse_result { + struct ec_parsed **parsed_table; /* list of parsed node */ + size_t parsed_table_len; /* number of parsed node */ + size_t len; /* consumed strings */ +}; + +static int __ec_node_subset_parse(struct ec_node **table, size_t table_len, + const struct ec_strvec *strvec, + struct parse_result *out) +{ + struct ec_node **child_table; + struct ec_parsed *child_parsed = NULL; + struct ec_strvec *childvec = NULL; + size_t i, j, len = 0; + struct parse_result best_result, result; + int ret; + + if (table_len == 0) + return 0; + + memset(&best_result, 0, sizeof(best_result)); + best_result.parsed_table = + ec_calloc(table_len, sizeof(*best_result.parsed_table[0])); + if (best_result.parsed_table == NULL) + goto fail; + + child_table = ec_calloc(table_len - 1, sizeof(*child_table)); + if (child_table == NULL) + goto fail; + + for (i = 0; i < table_len; i++) { + /* try to parse elt i */ + child_parsed = ec_node_parse_strvec(table[i], strvec); + if (child_parsed == NULL) + goto fail; + + if (!ec_parsed_matches(child_parsed)) { + ec_parsed_free(child_parsed); + child_parsed = NULL; + continue; + } + + /* build a new table without elt i */ + for (j = 0; j < table_len; j++) { + if (j < i) + child_table[j] = table[j]; + else if (j > i) + child_table[j - 1] = table[j]; + } + + /* build a new strvec */ + len = ec_parsed_len(child_parsed); + childvec = ec_strvec_ndup(strvec, len, + ec_strvec_len(strvec) - len); + if (childvec == NULL) + goto fail; + + memset(&result, 0, sizeof(result)); + + ret = __ec_node_subset_parse(child_table, table_len - 1, + childvec, &result); + ec_strvec_free(childvec); + childvec = NULL; + if (ret < 0) + goto fail; + + /* if result is not the best, ignore */ + if (result.parsed_table_len + 1 <= + best_result.parsed_table_len) { + ec_parsed_free(child_parsed); + child_parsed = NULL; + for (j = 0; j < result.parsed_table_len; j++) + ec_parsed_free(result.parsed_table[j]); + ec_free(result.parsed_table); + memset(&result, 0, sizeof(result)); + continue; + } + + /* replace the previous best result */ + for (j = 0; j < best_result.parsed_table_len; j++) + ec_parsed_free(best_result.parsed_table[j]); + best_result.parsed_table[0] = child_parsed; + child_parsed = NULL; + for (j = 0; j < result.parsed_table_len; j++) + best_result.parsed_table[j+1] = result.parsed_table[j]; + best_result.parsed_table_len = result.parsed_table_len + 1; + best_result.len = len + result.len; + ec_free(result.parsed_table); + } + + *out = best_result; + ec_free(child_table); + + return 0; + + fail: + ec_parsed_free(child_parsed); + ec_strvec_free(childvec); + for (j = 0; j < best_result.parsed_table_len; j++) + ec_parsed_free(best_result.parsed_table[j]); + ec_free(best_result.parsed_table); + ec_free(child_table); + return -1; +} + +static struct ec_parsed *ec_node_subset_parse(const struct ec_node *gen_node, + const struct ec_strvec *strvec) +{ + struct ec_node_subset *node = (struct ec_node_subset *)gen_node; + struct ec_parsed *parsed = NULL; + struct parse_result result; + struct ec_strvec *match_strvec; + size_t i; + int ret; + + memset(&result, 0, sizeof(result)); + + parsed = ec_parsed_new(); + if (parsed == NULL) + goto fail; + + ret = __ec_node_subset_parse(node->table, node->len, strvec, &result); + if (ret < 0) + goto fail; + + /* if no child node matches, return a matching empty strvec */ + if (result.parsed_table_len == 0) { + ec_free(result.parsed_table); + match_strvec = ec_strvec_new(); + if (match_strvec == NULL) + goto fail; + ec_parsed_set_match(parsed, gen_node, match_strvec); + return parsed; + } + + for (i = 0; i < result.parsed_table_len; i++) { + ec_parsed_add_child(parsed, result.parsed_table[i]); + result.parsed_table[i] = NULL; + } + ec_free(result.parsed_table); + result.parsed_table = NULL; + + match_strvec = ec_strvec_ndup(strvec, 0, result.len); + if (match_strvec == NULL) + goto fail; + + ec_parsed_set_match(parsed, gen_node, match_strvec); + + return parsed; + + fail: + for (i = 0; i < result.parsed_table_len; i++) + ec_parsed_free(result.parsed_table[i]); + ec_free(result.parsed_table); + ec_parsed_free(parsed); + + return NULL; +} + +static struct ec_completed * +__ec_node_subset_complete(struct ec_node **table, size_t table_len, + const struct ec_strvec *strvec) +{ + struct ec_completed *completed = NULL; + struct ec_completed *child_completed = NULL; + struct ec_strvec *childvec = NULL; + struct ec_parsed *parsed = NULL; + struct ec_node *save; + size_t i, len; + + /* + * example with table = [a, b, c] + * subset_complete([a,b,c], strvec) returns: + * complete(a, strvec) + complete(b, strvec) + complete(c, strvec) + + * + __subset_complete([b, c], childvec) if a matches + * + __subset_complete([a, c], childvec) if b matches + * + __subset_complete([a, b], childvec) if c matches + */ + + completed = ec_completed_new(); + if (completed == NULL) + goto fail; + + /* first, try to complete with each node of the table */ + for (i = 0; i < table_len; i++) { + if (table[i] == NULL) + continue; + + child_completed = ec_node_complete_strvec(table[i], + strvec); + + if (child_completed == NULL) + goto fail; + + ec_completed_merge(completed, child_completed); + child_completed = NULL; + } + + /* then, if a node matches, advance in strvec and try to complete with + * all the other nodes */ + for (i = 0; i < table_len; i++) { + if (table[i] == NULL) + continue; + + parsed = ec_node_parse_strvec(table[i], strvec); + if (parsed == NULL) + goto fail; + + if (!ec_parsed_matches(parsed)) { + ec_parsed_free(parsed); + parsed = NULL; + continue; + } + + len = ec_parsed_len(parsed); + childvec = ec_strvec_ndup(strvec, len, + ec_strvec_len(strvec) - len); + if (childvec == NULL) + goto fail; + + save = table[i]; + table[i] = NULL; + child_completed = __ec_node_subset_complete(table, + table_len, childvec); + table[i] = save; + ec_strvec_free(childvec); + childvec = NULL; + + if (child_completed == NULL) + goto fail; + + ec_completed_merge(completed, child_completed); + child_completed = NULL; + + ec_parsed_free(parsed); + parsed = NULL; + } + + return completed; +fail: + ec_parsed_free(parsed); + ec_completed_free(child_completed); + ec_completed_free(completed); + + return NULL; +} + +static struct ec_completed *ec_node_subset_complete(const struct ec_node *gen_node, + const struct ec_strvec *strvec) +{ + struct ec_node_subset *node = (struct ec_node_subset *)gen_node; + + return __ec_node_subset_complete(node->table, node->len, strvec); +} + +static void ec_node_subset_free_priv(struct ec_node *gen_node) +{ + struct ec_node_subset *node = (struct ec_node_subset *)gen_node; + unsigned int i; + + for (i = 0; i < node->len; i++) + ec_node_free(node->table[i]); + ec_free(node->table); +} + +int ec_node_subset_add(struct ec_node *gen_node, struct ec_node *child) +{ + struct ec_node_subset *node = (struct ec_node_subset *)gen_node; + struct ec_node **table; + + assert(node != NULL); + + if (child == NULL) + return -EINVAL; + + gen_node->flags &= ~EC_NODE_F_BUILT; + + table = ec_realloc(node->table, (node->len + 1) * sizeof(*node->table)); + if (table == NULL) { + ec_node_free(child); + return -1; + } + + node->table = table; + table[node->len] = child; + node->len++; + + child->parent = gen_node; + TAILQ_INSERT_TAIL(&gen_node->children, child, next); + + return 0; +} + +static struct ec_node_type ec_node_subset_type = { + .name = "subset", + .parse = ec_node_subset_parse, + .complete = ec_node_subset_complete, + .size = sizeof(struct ec_node_subset), + .free_priv = ec_node_subset_free_priv, +}; + +EC_NODE_TYPE_REGISTER(ec_node_subset_type); + +struct ec_node *__ec_node_subset(const char *id, ...) +{ + struct ec_node *gen_node = NULL; + struct ec_node_subset *node = NULL; + struct ec_node *child; + va_list ap; + int fail = 0; + + va_start(ap, id); + + gen_node = __ec_node_new(&ec_node_subset_type, id); + node = (struct ec_node_subset *)gen_node; + if (node == NULL) + fail = 1;; + + for (child = va_arg(ap, struct ec_node *); + child != EC_NODE_ENDLIST; + child = va_arg(ap, struct ec_node *)) { + + /* on error, don't quit the loop to avoid leaks */ + if (fail == 1 || child == NULL || + ec_node_subset_add(gen_node, child) < 0) { + fail = 1; + ec_node_free(child); + } + } + + if (fail == 1) + goto fail; + + va_end(ap); + return gen_node; + +fail: + ec_node_free(gen_node); /* will also free children */ + va_end(ap); + return NULL; +} + +static int ec_node_subset_testcase(void) +{ + struct ec_node *node; + int ret = 0; + + node = EC_NODE_SUBSET(NULL, + EC_NODE_OR(NULL, + ec_node_str(NULL, "foo"), + ec_node_str(NULL, "bar")), + ec_node_str(NULL, "bar"), + ec_node_str(NULL, "toto") + ); + if (node == NULL) { + ec_log(EC_LOG_ERR, "cannot create node\n"); + return -1; + } + ret |= EC_TEST_CHECK_PARSE(node, 0); + ret |= EC_TEST_CHECK_PARSE(node, 1, "foo"); + ret |= EC_TEST_CHECK_PARSE(node, 1, "bar"); + ret |= EC_TEST_CHECK_PARSE(node, 2, "foo", "bar", "titi"); + ret |= EC_TEST_CHECK_PARSE(node, 3, "bar", "foo", "toto"); + ret |= EC_TEST_CHECK_PARSE(node, 1, "foo", "foo"); + ret |= EC_TEST_CHECK_PARSE(node, 2, "bar", "bar"); + ret |= EC_TEST_CHECK_PARSE(node, 2, "bar", "foo"); + ret |= EC_TEST_CHECK_PARSE(node, 0, " "); + ret |= EC_TEST_CHECK_PARSE(node, 0, "foox"); + ec_node_free(node); + + /* test completion */ + node = EC_NODE_SUBSET(NULL, + ec_node_str(NULL, "foo"), + ec_node_str(NULL, "bar"), + ec_node_str(NULL, "bar2"), + ec_node_str(NULL, "toto"), + ec_node_str(NULL, "titi") + ); + if (node == NULL) { + ec_log(EC_LOG_ERR, "cannot create node\n"); + return -1; + } + ret |= EC_TEST_CHECK_COMPLETE(node, + "", EC_NODE_ENDLIST, + "foo", "bar", "bar2", "toto", "titi", EC_NODE_ENDLIST, + ""); + ret |= EC_TEST_CHECK_COMPLETE(node, + "bar", "bar2", "", EC_NODE_ENDLIST, + "foo", "toto", "titi", EC_NODE_ENDLIST, + ""); + ret |= EC_TEST_CHECK_COMPLETE(node, + "f", EC_NODE_ENDLIST, + "oo", EC_NODE_ENDLIST, + "oo"); + ret |= EC_TEST_CHECK_COMPLETE(node, + "b", EC_NODE_ENDLIST, + "ar", "ar2", EC_NODE_ENDLIST, + "ar"); + ret |= EC_TEST_CHECK_COMPLETE(node, + "bar", EC_NODE_ENDLIST, + "", "2", EC_NODE_ENDLIST, + ""); + ret |= EC_TEST_CHECK_COMPLETE(node, + "bar", "b", EC_NODE_ENDLIST, + "ar2", EC_NODE_ENDLIST, + "ar2"); + ret |= EC_TEST_CHECK_COMPLETE(node, + "t", EC_NODE_ENDLIST, + "oto", "iti", EC_NODE_ENDLIST, + ""); + ret |= EC_TEST_CHECK_COMPLETE(node, + "to", EC_NODE_ENDLIST, + "to", EC_NODE_ENDLIST, + "to"); + ret |= EC_TEST_CHECK_COMPLETE(node, + "x", EC_NODE_ENDLIST, + EC_NODE_ENDLIST, + ""); + ec_node_free(node); + + return ret; +} + +static struct ec_test ec_node_subset_test = { + .name = "node_subset", + .test = ec_node_subset_testcase, +}; + +EC_TEST_REGISTER(ec_node_subset_test); diff --git a/lib/ecoli_node_subset.h b/lib/ecoli_node_subset.h new file mode 100644 index 0000000..9d350fb --- /dev/null +++ b/lib/ecoli_node_subset.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2016-2017, Olivier MATZ + * + * 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 + +#define EC_NODE_SUBSET(args...) __ec_node_subset(args, EC_NODE_ENDLIST) + +/* list must be terminated with EC_NODE_ENDLIST */ +/* all nodes given in the list will be freed when freeing this one */ +/* avoid using this function directly, prefer the macro EC_NODE_SUBSET() or + * ec_node_subset() + ec_node_subset_add() */ +struct ec_node *__ec_node_subset(const char *id, ...); + +struct ec_node *ec_node_subset(const char *id); + +/* child is consumed */ +int ec_node_subset_add(struct ec_node *node, struct ec_node *child); + +#endif diff --git a/lib/ecoli_node_weakref.c b/lib/ecoli_node_weakref.c new file mode 100644 index 0000000..7370962 --- /dev/null +++ b/lib/ecoli_node_weakref.c @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2016, Olivier MATZ + * + * 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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +struct ec_node_weakref { + struct ec_node gen; + struct ec_node *child; +}; + +static struct ec_parsed *ec_node_weakref_parse(const struct ec_node *gen_node, + const struct ec_strvec *strvec) +{ + struct ec_node_weakref *node = (struct ec_node_weakref *)gen_node; + + return ec_node_parse_strvec(node->child, strvec); +} + +static struct ec_completed *ec_node_weakref_complete(const struct ec_node *gen_node, + const struct ec_strvec *strvec) +{ + struct ec_node_weakref *node = (struct ec_node_weakref *)gen_node; + + return ec_node_complete_strvec(node->child, strvec); +} + +static struct ec_node_type ec_node_weakref_type = { + .name = "weakref", + .parse = ec_node_weakref_parse, + .complete = ec_node_weakref_complete, + .size = sizeof(struct ec_node_weakref), +}; + +EC_NODE_TYPE_REGISTER(ec_node_weakref_type); + +int ec_node_weakref_set(struct ec_node *gen_node, struct ec_node *child) +{ + struct ec_node_weakref *node = (struct ec_node_weakref *)gen_node; + + // XXX check node type + + assert(node != NULL); + + if (child == NULL) + return -EINVAL; + + gen_node->flags &= ~EC_NODE_F_BUILT; + + node->child = child; + + child->parent = gen_node; + TAILQ_INSERT_TAIL(&gen_node->children, child, next); // XXX really needed? + + return 0; +} + +struct ec_node *ec_node_weakref(const char *id, struct ec_node *child) +{ + struct ec_node *gen_node = NULL; + + if (child == NULL) + return NULL; + + gen_node = __ec_node_new(&ec_node_weakref_type, id); + if (gen_node == NULL) + return NULL; + + ec_node_weakref_set(gen_node, child); + + return gen_node; +} + +static int ec_node_weakref_testcase(void) +{ + return 0; +} + +static struct ec_test ec_node_weakref_test = { + .name = "node_weakref", + .test = ec_node_weakref_testcase, +}; + +EC_TEST_REGISTER(ec_node_weakref_test); diff --git a/lib/ecoli_node_weakref.h b/lib/ecoli_node_weakref.h new file mode 100644 index 0000000..c29799f --- /dev/null +++ b/lib/ecoli_node_weakref.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2016, Olivier MATZ + * + * 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 + +/* 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 diff --git a/lib/ecoli_test.c b/lib/ecoli_test.c index 2ac659b..b4dd728 100644 --- a/lib/ecoli_test.c +++ b/lib/ecoli_test.c @@ -34,7 +34,7 @@ #include #include #include -#include +#include static struct ec_test_list test_list = TAILQ_HEAD_INITIALIZER(test_list); @@ -45,9 +45,9 @@ void ec_test_register(struct ec_test *test) // XXX check if already exist, like for type } -int ec_test_check_tk_parse(struct ec_tk *tk, int expected, ...) +int ec_test_check_parse(struct ec_node *tk, int expected, ...) { - struct ec_parsed_tk *p; + struct ec_parsed *p; struct ec_strvec *vec = NULL; const char *s; int ret = -1, match; @@ -61,7 +61,7 @@ int ec_test_check_tk_parse(struct ec_tk *tk, int expected, ...) goto out; for (s = va_arg(ap, const char *); - s != EC_TK_ENDLIST; + s != EC_NODE_ENDLIST; s = va_arg(ap, const char *)) { if (s == NULL) goto out; @@ -70,14 +70,14 @@ int ec_test_check_tk_parse(struct ec_tk *tk, int expected, ...) goto out; } - p = ec_tk_parse_tokens(tk, vec); + p = ec_node_parse_strvec(tk, vec); /* XXX only for debug */ - ec_parsed_tk_dump(stdout, p); + ec_parsed_dump(stdout, p); if (p == NULL) { - ec_log(EC_LOG_ERR, "parsed_tk is NULL\n"); + ec_log(EC_LOG_ERR, "parsed is NULL\n"); } - if (ec_parsed_tk_matches(p)) - match = ec_parsed_tk_len(p); + if (ec_parsed_matches(p)) + match = ec_parsed_len(p); else match = -1; if (expected == match) { @@ -88,7 +88,7 @@ int ec_test_check_tk_parse(struct ec_tk *tk, int expected, ...) match, expected); } - ec_parsed_tk_free(p); + ec_parsed_free(p); out: ec_strvec_free(vec); @@ -96,10 +96,10 @@ out: return ret; } -int ec_test_check_tk_complete(struct ec_tk *tk, ...) +int ec_test_check_complete(struct ec_node *tk, ...) { - struct ec_completed_tk *c = NULL; - struct ec_completed_tk_elt *elt; + struct ec_completed *c = NULL; + struct ec_completed_elt *elt; struct ec_strvec *vec = NULL; const char *s, *expected; int ret = 0; @@ -114,7 +114,7 @@ int ec_test_check_tk_complete(struct ec_tk *tk, ...) goto out; for (s = va_arg(ap, const char *); - s != EC_TK_ENDLIST; + s != EC_NODE_ENDLIST; s = va_arg(ap, const char *)) { if (s == NULL) goto out; @@ -123,14 +123,14 @@ int ec_test_check_tk_complete(struct ec_tk *tk, ...) goto out; } - c = ec_tk_complete_tokens(tk, vec); + c = ec_node_complete_strvec(tk, vec); if (c == NULL) { ret = -1; goto out; } for (s = va_arg(ap, const char *); - s != EC_TK_ENDLIST; + s != EC_NODE_ENDLIST; s = va_arg(ap, const char *)) { if (s == NULL) { ret = -1; @@ -151,17 +151,17 @@ int ec_test_check_tk_complete(struct ec_tk *tk, ...) } } - if (count != ec_completed_tk_count(c, EC_MATCH)) { + if (count != ec_completed_count(c, EC_MATCH)) { ec_log(EC_LOG_ERR, "nb_completion (%d) does not match (%d)\n", - count, ec_completed_tk_count(c, EC_MATCH)); - ec_completed_tk_dump(stdout, c); + count, ec_completed_count(c, EC_MATCH)); + ec_completed_dump(stdout, c); ret = -1; } expected = va_arg(ap, const char *); - s = ec_completed_tk_smallest_start(c); + s = ec_completed_smallest_start(c); if (strcmp(s, expected)) { ret = -1; ec_log(EC_LOG_ERR, @@ -171,7 +171,7 @@ int ec_test_check_tk_complete(struct ec_tk *tk, ...) out: ec_strvec_free(vec); - ec_completed_tk_free(c); + ec_completed_free(c); va_end(ap); return ret; } diff --git a/lib/ecoli_test.h b/lib/ecoli_test.h index cf41f46..0115837 100644 --- a/lib/ecoli_test.h +++ b/lib/ecoli_test.h @@ -31,7 +31,7 @@ #include #include -#include +#include // XXX check if already exists? #define EC_TEST_REGISTER(t) \ @@ -71,7 +71,7 @@ int ec_test_all(void); int ec_test_one(const char *name); /* expected == -1 means no match */ -int ec_test_check_tk_parse(struct ec_tk *tk, int expected, ...); +int ec_test_check_parse(struct ec_node *node, int expected, ...); #define EC_TEST_ERR(fmt, ...) \ ec_log(EC_LOG_ERR, "%s:%d: error: " fmt "\n", \ @@ -83,18 +83,18 @@ int ec_test_check_tk_parse(struct ec_tk *tk, int expected, ...); EC_TEST_ERR("assertion failure: " #cond); \ } while (0) -/* tk, input, [expected1, expected2, ...] */ -#define EC_TEST_CHECK_TK_PARSE(tk, args...) ({ \ - int ret_ = ec_test_check_tk_parse(tk, args, EC_TK_ENDLIST); \ +/* node, input, [expected1, expected2, ...] */ +#define EC_TEST_CHECK_PARSE(node, args...) ({ \ + int ret_ = ec_test_check_parse(node, args, EC_NODE_ENDLIST); \ if (ret_) \ EC_TEST_ERR("parse test failed"); \ ret_; \ }) -int ec_test_check_tk_complete(struct ec_tk *tk, ...); +int ec_test_check_complete(struct ec_node *node, ...); -#define EC_TEST_CHECK_TK_COMPLETE(tk, args...) ({ \ - int ret_ = ec_test_check_tk_complete(tk, args); \ +#define EC_TEST_CHECK_COMPLETE(node, args...) ({ \ + int ret_ = ec_test_check_complete(node, args); \ if (ret_) \ EC_TEST_ERR("complete test failed"); \ ret_; \ diff --git a/lib/ecoli_tk.c b/lib/ecoli_tk.c deleted file mode 100644 index 5867fbe..0000000 --- a/lib/ecoli_tk.c +++ /dev/null @@ -1,719 +0,0 @@ -/* - * Copyright (c) 2016, Olivier MATZ - * - * 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 -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -static struct ec_tk_type_list tk_type_list = - TAILQ_HEAD_INITIALIZER(tk_type_list); - -struct ec_tk_type *ec_tk_type_lookup(const char *name) -{ - struct ec_tk_type *type; - - TAILQ_FOREACH(type, &tk_type_list, next) { - if (!strcmp(name, type->name)) - return type; - } - - return NULL; -} - -int ec_tk_type_register(struct ec_tk_type *type) -{ - if (ec_tk_type_lookup(type->name) != NULL) - return -EEXIST; - if (type->size < sizeof(struct ec_tk)) - return -EINVAL; - - TAILQ_INSERT_TAIL(&tk_type_list, type, next); - - return 0; -} - -void ec_tk_type_dump(FILE *out) -{ - struct ec_tk_type *type; - - TAILQ_FOREACH(type, &tk_type_list, next) - fprintf(out, "%s\n", type->name); -} - -struct ec_tk *__ec_tk_new(const struct ec_tk_type *type, const char *id) -{ - struct ec_tk *tk = NULL; - char buf[256]; // XXX - - ec_log(EC_LOG_DEBUG, "create node type=%s id=%s\n", type->name, id); - - tk = ec_calloc(1, type->size); - if (tk == NULL) - goto fail; - - TAILQ_INIT(&tk->children); - tk->type = type; - tk->refcnt = 1; - - if (id != NULL) { - tk->id = ec_strdup(id); - if (tk->id == NULL) - goto fail; - } - - snprintf(buf, sizeof(buf), "<%s>", type->name); - tk->desc = ec_strdup(buf); // XXX ec_asprintf ? - if (tk->desc == NULL) - goto fail; - - tk->attrs = ec_keyval_new(); - if (tk->attrs == NULL) - goto fail; - - return tk; - - fail: - ec_tk_free(tk); - return NULL; -} - -struct ec_tk *ec_tk_new(const char *typename, const char *id) -{ - struct ec_tk_type *type; - - type = ec_tk_type_lookup(typename); - if (type == NULL) { - ec_log(EC_LOG_ERR, "type=%s does not exist\n", typename); - return NULL; - } - - return __ec_tk_new(type, id); -} - -void ec_tk_free(struct ec_tk *tk) -{ - if (tk == NULL) - return; - - assert(tk->refcnt > 0); - - if (--tk->refcnt > 0) - return; - - if (tk->type != NULL && tk->type->free_priv != NULL) - tk->type->free_priv(tk); - ec_free(tk->id); - ec_free(tk->desc); - ec_free(tk->attrs); - ec_free(tk); -} - -struct ec_tk *ec_tk_clone(struct ec_tk *tk) -{ - if (tk != NULL) - tk->refcnt++; - return tk; -} - -struct ec_tk *ec_tk_find(struct ec_tk *tk, const char *id) -{ - struct ec_tk *child, *ret; - const char *tk_id = ec_tk_id(tk); - - if (id != NULL && tk_id != NULL && !strcmp(tk_id, id)) - return tk; - - TAILQ_FOREACH(child, &tk->children, next) { - ret = ec_tk_find(child, id); - if (ret != NULL) - return ret; - } - - return NULL; -} - -struct ec_keyval *ec_tk_attrs(const struct ec_tk *tk) -{ - return tk->attrs; -} - -const char *ec_tk_id(const struct ec_tk *tk) -{ - return tk->id; -} - -struct ec_tk *ec_tk_parent(const struct ec_tk *tk) -{ - return tk->parent; -} - -static void __ec_tk_dump(FILE *out, - const struct ec_tk *tk, size_t indent) -{ - struct ec_tk *child; - size_t i; - const char *id = "None", *typename = "None"; - - if (tk->id != NULL) - id = tk->id; - typename = tk->type->name; - - /* XXX enhance */ - for (i = 0; i < indent; i++) { - if (i % 2) - fprintf(out, " "); - else - fprintf(out, "|"); - } - - fprintf(out, "tk_type=%s id=%s\n", typename, id); - TAILQ_FOREACH(child, &tk->children, next) - __ec_tk_dump(out, child, indent + 2); -} - -void ec_tk_dump(FILE *out, const struct ec_tk *tk) -{ - fprintf(out, "------------------- tk dump:\n"); //XXX - - if (tk == NULL) { - fprintf(out, "tk is NULL\n"); - return; - } - - __ec_tk_dump(out, tk, 0); -} - -struct ec_parsed_tk *ec_tk_parse(struct ec_tk *tk, const char *str) -{ - struct ec_strvec *strvec = NULL; - struct ec_parsed_tk *parsed_tk; - - errno = ENOMEM; - strvec = ec_strvec_new(); - if (strvec == NULL) - goto fail; - - if (ec_strvec_add(strvec, str) < 0) - goto fail; - - parsed_tk = ec_tk_parse_tokens(tk, strvec); - if (parsed_tk == NULL) - goto fail; - - ec_strvec_free(strvec); - return parsed_tk; - - fail: - ec_strvec_free(strvec); - return NULL; -} - -struct ec_parsed_tk *ec_tk_parse_tokens(struct ec_tk *tk, - const struct ec_strvec *strvec) -{ - struct ec_parsed_tk *parsed_tk; - int ret; - - /* build the node if required */ - if (tk->type->build != NULL) { - if ((tk->flags & EC_TK_F_BUILT) == 0) { - ret = tk->type->build(tk); - if (ret < 0) { - errno = -ret; - return NULL; - } - } - } - tk->flags |= EC_TK_F_BUILT; - - if (tk->type->parse == NULL) { - errno = ENOTSUP; - return NULL; - } - - parsed_tk = tk->type->parse(tk, strvec); - - return parsed_tk; -} - - -struct ec_parsed_tk *ec_parsed_tk_new(void) -{ - struct ec_parsed_tk *parsed_tk = NULL; - - parsed_tk = ec_calloc(1, sizeof(*parsed_tk)); - if (parsed_tk == NULL) - goto fail; - - TAILQ_INIT(&parsed_tk->children); - - return parsed_tk; - - fail: - return NULL; -} - -void ec_parsed_tk_set_match(struct ec_parsed_tk *parsed_tk, - const struct ec_tk *tk, struct ec_strvec *strvec) -{ - parsed_tk->tk = tk; - parsed_tk->strvec = strvec; -} - -void ec_parsed_tk_free_children(struct ec_parsed_tk *parsed_tk) -{ - struct ec_parsed_tk *child; - - if (parsed_tk == NULL) - return; - - while (!TAILQ_EMPTY(&parsed_tk->children)) { - child = TAILQ_FIRST(&parsed_tk->children); - TAILQ_REMOVE(&parsed_tk->children, child, next); - ec_parsed_tk_free(child); - } -} - -void ec_parsed_tk_free(struct ec_parsed_tk *parsed_tk) -{ - if (parsed_tk == NULL) - return; - - ec_parsed_tk_free_children(parsed_tk); - ec_strvec_free(parsed_tk->strvec); - ec_free(parsed_tk); -} - -static void __ec_parsed_tk_dump(FILE *out, - const struct ec_parsed_tk *parsed_tk, size_t indent) -{ - struct ec_parsed_tk *child; - const struct ec_strvec *vec; - size_t i; - const char *id = "None", *typename = "None"; - - if (parsed_tk->tk != NULL) { - if (parsed_tk->tk->id != NULL) - id = parsed_tk->tk->id; - typename = parsed_tk->tk->type->name; - } - - /* XXX enhance */ - for (i = 0; i < indent; i++) { - if (i % 2) - fprintf(out, " "); - else - fprintf(out, "|"); - } - - fprintf(out, "tk_type=%s id=%s vec=[", typename, id); - vec = ec_parsed_tk_strvec(parsed_tk); - for (i = 0; i < ec_strvec_len(vec); i++) - fprintf(out, "%s<%s>", - i == 0 ? "" : ",", - ec_strvec_val(vec, i)); - fprintf(out, "]\n"); - - TAILQ_FOREACH(child, &parsed_tk->children, next) - __ec_parsed_tk_dump(out, child, indent + 2); -} - -void ec_parsed_tk_dump(FILE *out, const struct ec_parsed_tk *parsed_tk) -{ - fprintf(out, "------------------- parsed_tk dump:\n"); //XXX - - if (parsed_tk == NULL) { - fprintf(out, "parsed_tk is NULL, error in parse\n"); - return; - } - if (!ec_parsed_tk_matches(parsed_tk)) { - fprintf(out, "no match\n"); - return; - } - - __ec_parsed_tk_dump(out, parsed_tk, 0); -} - -void ec_parsed_tk_add_child(struct ec_parsed_tk *parsed_tk, - struct ec_parsed_tk *child) -{ - TAILQ_INSERT_TAIL(&parsed_tk->children, child, next); -} - -void ec_parsed_tk_del_child(struct ec_parsed_tk *parsed_tk, - struct ec_parsed_tk *child) -{ - TAILQ_REMOVE(&parsed_tk->children, child, next); -} - -struct ec_parsed_tk *ec_parsed_tk_find_first(struct ec_parsed_tk *parsed_tk, - const char *id) -{ - struct ec_parsed_tk *child, *ret; - - if (parsed_tk == NULL) - return NULL; - - if (parsed_tk->tk != NULL && - parsed_tk->tk->id != NULL && - !strcmp(parsed_tk->tk->id, id)) - return parsed_tk; - - TAILQ_FOREACH(child, &parsed_tk->children, next) { - ret = ec_parsed_tk_find_first(child, id); - if (ret != NULL) - return ret; - } - - return NULL; -} - -const struct ec_strvec *ec_parsed_tk_strvec( - const struct ec_parsed_tk *parsed_tk) -{ - if (parsed_tk == NULL || parsed_tk->strvec == NULL) - return NULL; - - return parsed_tk->strvec; -} - -/* number of parsed tokens */ -size_t ec_parsed_tk_len(const struct ec_parsed_tk *parsed_tk) -{ - if (parsed_tk == NULL || parsed_tk->strvec == NULL) - return 0; - - return ec_strvec_len(parsed_tk->strvec); -} - -size_t ec_parsed_tk_matches(const struct ec_parsed_tk *parsed_tk) -{ - if (parsed_tk == NULL) - return 0; - - if (parsed_tk->tk == NULL && TAILQ_EMPTY(&parsed_tk->children)) - return 0; - - return 1; -} - -struct ec_completed_tk *ec_completed_tk_new(void) -{ - struct ec_completed_tk *completed_tk = NULL; - - completed_tk = ec_calloc(1, sizeof(*completed_tk)); - if (completed_tk == NULL) - return NULL; - - TAILQ_INIT(&completed_tk->elts); - completed_tk->count_match = 0; - - return completed_tk; -} - -struct ec_completed_tk_elt *ec_completed_tk_elt_new(const struct ec_tk *tk, - const char *add) -{ - struct ec_completed_tk_elt *elt = NULL; - - elt = ec_calloc(1, sizeof(*elt)); - if (elt == NULL) - return NULL; - - elt->tk = tk; - if (add != NULL) { - elt->add = ec_strdup(add); - if (elt->add == NULL) { - ec_completed_tk_elt_free(elt); - return NULL; - } - } - - return elt; -} - -/* XXX define when to use ec_tk_complete() or tk->complete() - * (same for parse) - * suggestion: tk->op() is internal, user calls the function - * other idea: have 2 functions - */ -struct ec_completed_tk *ec_tk_complete(struct ec_tk *tk, - const char *str) -{ - struct ec_strvec *strvec = NULL; - struct ec_completed_tk *completed_tk; - - errno = ENOMEM; - strvec = ec_strvec_new(); - if (strvec == NULL) - goto fail; - - if (ec_strvec_add(strvec, str) < 0) - goto fail; - - completed_tk = ec_tk_complete_tokens(tk, strvec); - if (completed_tk == NULL) - goto fail; - - ec_strvec_free(strvec); - return completed_tk; - - fail: - ec_strvec_free(strvec); - return NULL; -} - -/* default completion function: return a no-match element */ -struct ec_completed_tk *ec_tk_default_complete(const struct ec_tk *gen_tk, - const struct ec_strvec *strvec) -{ - struct ec_completed_tk *completed_tk; - struct ec_completed_tk_elt *completed_tk_elt; - - (void)strvec; - - completed_tk = ec_completed_tk_new(); - if (completed_tk == NULL) - return NULL; - - completed_tk_elt = ec_completed_tk_elt_new(gen_tk, NULL); - if (completed_tk_elt == NULL) { - ec_completed_tk_free(completed_tk); - return NULL; - } - - ec_completed_tk_add_elt(completed_tk, completed_tk_elt); - - return completed_tk; -} - -struct ec_completed_tk *ec_tk_complete_tokens(struct ec_tk *tk, - const struct ec_strvec *strvec) -{ - int ret; - - /* build the node if required */ - if (tk->type->build != NULL) { - if ((tk->flags & EC_TK_F_BUILT) == 0) { - ret = tk->type->build(tk); - if (ret < 0) { - errno = -ret; - return NULL; - } - } - } - tk->flags |= EC_TK_F_BUILT; - - if (tk->type->complete == NULL) { - errno = ENOTSUP; - return NULL; - } - - return tk->type->complete(tk, strvec); -} - -/* count the number of identical chars at the beginning of 2 strings */ -static size_t strcmp_count(const char *s1, const char *s2) -{ - size_t i = 0; - - while (s1[i] && s2[i] && s1[i] == s2[i]) - i++; - - return i; -} - -void ec_completed_tk_add_elt( - struct ec_completed_tk *completed_tk, struct ec_completed_tk_elt *elt) -{ - size_t n; - - TAILQ_INSERT_TAIL(&completed_tk->elts, elt, next); - completed_tk->count++; - if (elt->add != NULL) - completed_tk->count_match++; - if (elt->add != NULL) { - if (completed_tk->smallest_start == NULL) { - completed_tk->smallest_start = ec_strdup(elt->add); - } else { - n = strcmp_count(elt->add, - completed_tk->smallest_start); - completed_tk->smallest_start[n] = '\0'; - } - } -} - -void ec_completed_tk_elt_free(struct ec_completed_tk_elt *elt) -{ - ec_free(elt->add); - ec_free(elt); -} - -void ec_completed_tk_merge(struct ec_completed_tk *completed_tk1, - struct ec_completed_tk *completed_tk2) -{ - struct ec_completed_tk_elt *elt; - - assert(completed_tk1 != NULL); - assert(completed_tk2 != NULL); - - while (!TAILQ_EMPTY(&completed_tk2->elts)) { - elt = TAILQ_FIRST(&completed_tk2->elts); - TAILQ_REMOVE(&completed_tk2->elts, elt, next); - ec_completed_tk_add_elt(completed_tk1, elt); - } - - ec_completed_tk_free(completed_tk2); -} - -void ec_completed_tk_free(struct ec_completed_tk *completed_tk) -{ - struct ec_completed_tk_elt *elt; - - if (completed_tk == NULL) - return; - - while (!TAILQ_EMPTY(&completed_tk->elts)) { - elt = TAILQ_FIRST(&completed_tk->elts); - TAILQ_REMOVE(&completed_tk->elts, elt, next); - ec_completed_tk_elt_free(elt); - } - ec_free(completed_tk->smallest_start); - ec_free(completed_tk); -} - -void ec_completed_tk_dump(FILE *out, const struct ec_completed_tk *completed_tk) -{ - struct ec_completed_tk_elt *elt; - - if (completed_tk == NULL || completed_tk->count == 0) { - fprintf(out, "no completion\n"); - return; - } - - fprintf(out, "completion: count=%u match=%u smallest_start=<%s>\n", - completed_tk->count, completed_tk->count_match, - completed_tk->smallest_start); - - TAILQ_FOREACH(elt, &completed_tk->elts, next) { - fprintf(out, "add=<%s>, tk=%p, tk_type=%s\n", - elt->add, elt->tk, elt->tk->type->name); - } -} - -const char *ec_completed_tk_smallest_start( - const struct ec_completed_tk *completed_tk) -{ - if (completed_tk == NULL || completed_tk->smallest_start == NULL) - return ""; - - return completed_tk->smallest_start; -} - -unsigned int ec_completed_tk_count( - const struct ec_completed_tk *completed_tk, - enum ec_completed_tk_filter_flags flags) -{ - unsigned int count = 0; - - if (completed_tk == NULL) - return count; - - if (flags & EC_MATCH) - count += completed_tk->count_match; - if (flags & EC_NO_MATCH) - count += (completed_tk->count - completed_tk->count_match); //XXX - - return count; -} - -struct ec_completed_tk_iter * -ec_completed_tk_iter_new(struct ec_completed_tk *completed_tk, - enum ec_completed_tk_filter_flags flags) -{ - struct ec_completed_tk_iter *iter; - - iter = ec_calloc(1, sizeof(*iter)); - if (iter == NULL) - return NULL; - - iter->completed_tk = completed_tk; - iter->flags = flags; - iter->cur = NULL; - - return iter; -} - -const struct ec_completed_tk_elt *ec_completed_tk_iter_next( - struct ec_completed_tk_iter *iter) -{ - if (iter->completed_tk == NULL) - return NULL; - - do { - if (iter->cur == NULL) { - iter->cur = TAILQ_FIRST(&iter->completed_tk->elts); - } else { - iter->cur = TAILQ_NEXT(iter->cur, next); - } - - if (iter->cur == NULL) - break; - - if (iter->cur->add == NULL && - (iter->flags & EC_NO_MATCH)) - break; - - if (iter->cur->add != NULL && - (iter->flags & EC_MATCH)) - break; - - } while (iter->cur != NULL); - - return iter->cur; -} - -void ec_completed_tk_iter_free(struct ec_completed_tk_iter *iter) -{ - ec_free(iter); -} - -const char *ec_tk_desc(const struct ec_tk *tk) -{ - if (tk->type->desc != NULL) - return tk->type->desc(tk); - - return tk->desc; -} diff --git a/lib/ecoli_tk.h b/lib/ecoli_tk.h deleted file mode 100644 index cdcf625..0000000 --- a/lib/ecoli_tk.h +++ /dev/null @@ -1,259 +0,0 @@ -/* - * Copyright (c) 2016, Olivier MATZ - * - * 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 -#include -#include - -#define EC_TK_ENDLIST ((void *)1) - -struct ec_tk; -struct ec_parsed_tk; -struct ec_strvec; -struct ec_keyval; - -/* return 0 on success, else -errno. */ -typedef int (*ec_tk_build_t)(struct ec_tk *tk); - -typedef struct ec_parsed_tk *(*ec_tk_parse_t)(const struct ec_tk *tk, - const struct ec_strvec *strvec); -typedef struct ec_completed_tk *(*ec_tk_complete_t)(const struct ec_tk *tk, - const struct ec_strvec *strvec); -typedef const char * (*ec_tk_desc_t)(const struct ec_tk *); -typedef void (*ec_tk_init_priv_t)(struct ec_tk *); -typedef void (*ec_tk_free_priv_t)(struct ec_tk *); - -#define EC_TK_TYPE_REGISTER(t) \ - static void ec_tk_init_##t(void); \ - static void __attribute__((constructor, used)) \ - ec_tk_init_##t(void) \ - { \ - if (ec_tk_type_register(&t) < 0) \ - fprintf(stderr, "cannot register %s\n", t.name); \ - } - -TAILQ_HEAD(ec_tk_type_list, ec_tk_type); - -/** - * A structure describing a tk type. - */ -struct ec_tk_type { - TAILQ_ENTRY(ec_tk_type) next; /**< Next in list. */ - const char *name; /**< Tk type name. */ - ec_tk_build_t build; /* (re)build the node, called by generic parse */ - ec_tk_parse_t parse; - ec_tk_complete_t complete; - ec_tk_desc_t desc; - size_t size; - ec_tk_init_priv_t init_priv; - ec_tk_free_priv_t free_priv; -}; - -/** - * Register a token type. - * - * @param type - * A pointer to a ec_test structure describing the test - * to be registered. - * @return - * 0 on success, negative value on error. - */ -int ec_tk_type_register(struct ec_tk_type *type); - -/** - * Lookup token type by name - * - * @param name - * The name of the token type to search. - * @return - * The token type if found, or NULL on error. - */ -struct ec_tk_type *ec_tk_type_lookup(const char *name); - -/** - * Dump registered log types - */ -void ec_tk_type_dump(FILE *out); - -TAILQ_HEAD(ec_tk_list, ec_tk); - -struct ec_tk { - const struct ec_tk_type *type; - char *id; - char *desc; - struct ec_keyval *attrs; - /* XXX ensure parent and child are properly set in all nodes */ - struct ec_tk *parent; - unsigned int refcnt; -#define EC_TK_F_BUILT 0x0001 /** set if configuration is built */ - unsigned int flags; - - TAILQ_ENTRY(ec_tk) next; - struct ec_tk_list children; -}; - -/* create a new token when the type is known, typically called from the node - * code */ -struct ec_tk *__ec_tk_new(const struct ec_tk_type *type, const char *id); - -/* create a_new token node */ -struct ec_tk *ec_tk_new(const char *typename, const char *id); - -void ec_tk_free(struct ec_tk *tk); - -/* XXX add more accessors */ -struct ec_keyval *ec_tk_attrs(const struct ec_tk *tk); -struct ec_tk *ec_tk_parent(const struct ec_tk *tk); -const char *ec_tk_id(const struct ec_tk *tk); -const char *ec_tk_desc(const struct ec_tk *tk); - -void ec_tk_dump(FILE *out, const struct ec_tk *tk); -struct ec_tk *ec_tk_find(struct ec_tk *tk, const char *id); - -/* XXX split this file ? */ - -TAILQ_HEAD(ec_parsed_tk_list, ec_parsed_tk); - -/* - tk == NULL + empty children list means "no match" -*/ -struct ec_parsed_tk { - TAILQ_ENTRY(ec_parsed_tk) next; - struct ec_parsed_tk_list children; - const struct ec_tk *tk; - struct ec_strvec *strvec; -}; - -struct ec_parsed_tk *ec_parsed_tk_new(void); -void ec_parsed_tk_free(struct ec_parsed_tk *parsed_tk); -struct ec_tk *ec_tk_clone(struct ec_tk *tk); -void ec_parsed_tk_free_children(struct ec_parsed_tk *parsed_tk); - -const struct ec_strvec *ec_parsed_tk_strvec( - const struct ec_parsed_tk *parsed_tk); - -void ec_parsed_tk_set_match(struct ec_parsed_tk *parsed_tk, - const struct ec_tk *tk, struct ec_strvec *strvec); - -/* XXX we could use a cache to store possible completions or match: the - * cache would be per-node, and would be reset for each call to parse() - * or complete() ? */ -/* a NULL return value is an error, with errno set - ENOTSUP: no ->parse() operation -*/ -struct ec_parsed_tk *ec_tk_parse(struct ec_tk *tk, const char *str); - -/* mostly internal to tokens */ -/* XXX it should not reset cache - * ... not sure... it is used by tests */ -struct ec_parsed_tk *ec_tk_parse_tokens(struct ec_tk *tk, - const struct ec_strvec *strvec); - -void ec_parsed_tk_add_child(struct ec_parsed_tk *parsed_tk, - struct ec_parsed_tk *child); -void ec_parsed_tk_del_child(struct ec_parsed_tk *parsed_tk, - struct ec_parsed_tk *child); -void ec_parsed_tk_dump(FILE *out, const struct ec_parsed_tk *parsed_tk); - -struct ec_parsed_tk *ec_parsed_tk_find_first(struct ec_parsed_tk *parsed_tk, - const char *id); - -const char *ec_parsed_tk_to_string(const struct ec_parsed_tk *parsed_tk); -size_t ec_parsed_tk_len(const struct ec_parsed_tk *parsed_tk); -size_t ec_parsed_tk_matches(const struct ec_parsed_tk *parsed_tk); - -struct ec_completed_tk_elt { - TAILQ_ENTRY(ec_completed_tk_elt) next; - const struct ec_tk *tk; - char *add; -}; - -TAILQ_HEAD(ec_completed_tk_elt_list, ec_completed_tk_elt); - - -struct ec_completed_tk { - struct ec_completed_tk_elt_list elts; - unsigned count; - unsigned count_match; - char *smallest_start; -}; - -/* - * return a completed_tk object filled with elts - * return NULL on error (nomem?) - */ -struct ec_completed_tk *ec_tk_complete(struct ec_tk *tk, - const char *str); -struct ec_completed_tk *ec_tk_complete_tokens(struct ec_tk *tk, - const struct ec_strvec *strvec); -struct ec_completed_tk *ec_completed_tk_new(void); -struct ec_completed_tk_elt *ec_completed_tk_elt_new(const struct ec_tk *tk, - const char *add); -void ec_completed_tk_add_elt(struct ec_completed_tk *completed_tk, - struct ec_completed_tk_elt *elt); -void ec_completed_tk_elt_free(struct ec_completed_tk_elt *elt); -void ec_completed_tk_merge(struct ec_completed_tk *completed_tk1, - struct ec_completed_tk *completed_tk2); -void ec_completed_tk_free(struct ec_completed_tk *completed_tk); -void ec_completed_tk_dump(FILE *out, - const struct ec_completed_tk *completed_tk); -struct ec_completed_tk *ec_tk_default_complete(const struct ec_tk *gen_tk, - const struct ec_strvec *strvec); - -/* cannot return NULL */ -const char *ec_completed_tk_smallest_start( - const struct ec_completed_tk *completed_tk); - -enum ec_completed_tk_filter_flags { - EC_MATCH = 1, - EC_NO_MATCH = 2, -}; - -unsigned int ec_completed_tk_count( - const struct ec_completed_tk *completed_tk, - enum ec_completed_tk_filter_flags flags); - -struct ec_completed_tk_iter { - enum ec_completed_tk_filter_flags flags; - const struct ec_completed_tk *completed_tk; - const struct ec_completed_tk_elt *cur; -}; - -struct ec_completed_tk_iter * -ec_completed_tk_iter_new(struct ec_completed_tk *completed_tk, - enum ec_completed_tk_filter_flags flags); - -const struct ec_completed_tk_elt *ec_completed_tk_iter_next( - struct ec_completed_tk_iter *iter); - -void ec_completed_tk_iter_free(struct ec_completed_tk_iter *iter); - - -#endif diff --git a/lib/ecoli_tk_cmd.c b/lib/ecoli_tk_cmd.c deleted file mode 100644 index 03ccb60..0000000 --- a/lib/ecoli_tk_cmd.c +++ /dev/null @@ -1,506 +0,0 @@ -/* - * Copyright (c) 2016-2017, Olivier MATZ - * - * 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 -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct ec_tk_cmd { - struct ec_tk gen; - char *cmd_str; /* the command string. */ - struct ec_tk *cmd; /* the command token. */ - struct ec_tk *lex; /* the lexer token. */ - struct ec_tk *expr; /* the expression parser. */ - struct ec_tk **table; /* table of tk referenced in command. */ - unsigned int len; /* len of the table. */ -}; - -static int -ec_tk_cmd_eval_var(void **result, void *userctx, - const struct ec_parsed_tk *var) -{ - const struct ec_strvec *vec; - struct ec_tk_cmd *tk = userctx; - struct ec_tk *eval = NULL; - const char *str, *id; - unsigned int i; - - (void)userctx; - - /* get parsed string vector, it should contain only one str */ - vec = ec_parsed_tk_strvec(var); - if (ec_strvec_len(vec) != 1) - return -EINVAL; - str = ec_strvec_val(vec, 0); - - for (i = 0; i < tk->len; i++) { - id = ec_tk_id(tk->table[i]); - printf("i=%d id=%s\n", i, id); - if (id == NULL) - continue; - if (strcmp(str, id)) - continue; - /* if id matches, use a tk provided by the user... */ - eval = ec_tk_clone(tk->table[i]); - if (eval == NULL) - return -ENOMEM; - break; - } - - /* ...or create a string token */ - if (eval == NULL) { - eval = ec_tk_str(NULL, str); - if (eval == NULL) - return -ENOMEM; - } - - printf("eval var %s %p\n", str, eval); - *result = eval; - - return 0; -} - -static int -ec_tk_cmd_eval_pre_op(void **result, void *userctx, void *operand, - const struct ec_parsed_tk *operator) -{ - (void)result; - (void)userctx; - (void)operand; - (void)operator; - - return -EINVAL; -} - -static int -ec_tk_cmd_eval_post_op(void **result, void *userctx, void *operand, - const struct ec_parsed_tk *operator) -{ - const struct ec_strvec *vec; - struct ec_tk *eval = operand;; - - (void)userctx; - - /* get parsed string vector, it should contain only one str */ - vec = ec_parsed_tk_strvec(operator); - if (ec_strvec_len(vec) != 1) - return -EINVAL; - - if (!strcmp(ec_strvec_val(vec, 0), "*")) - eval = NULL; //XXX - else - return -EINVAL; - - printf("eval post_op %p\n", eval); - *result = eval; - - return 0; -} - -static int -ec_tk_cmd_eval_bin_op(void **result, void *userctx, void *operand1, - const struct ec_parsed_tk *operator, void *operand2) - -{ - const struct ec_strvec *vec; - struct ec_tk *out = NULL; - struct ec_tk *in1 = operand1; - struct ec_tk *in2 = operand2; - - (void)userctx; - - printf("eval bin_op %p %p\n", in1, in2); - - /* get parsed string vector, it should contain only one str */ - vec = ec_parsed_tk_strvec(operator); - if (ec_strvec_len(vec) != 1) - return -EINVAL; - - if (!strcmp(ec_strvec_val(vec, 0), "|")) { - out = EC_TK_OR(NULL, ec_tk_clone(in1), ec_tk_clone(in2)); - if (out == NULL) - return -EINVAL; - ec_tk_free(in1); - ec_tk_free(in2); - *result = out; - } else if (!strcmp(ec_strvec_val(vec, 0), ",")) { - out = EC_TK_SUBSET(NULL, ec_tk_clone(in1), ec_tk_clone(in2)); - if (out == NULL) - return -EINVAL; - ec_tk_free(in1); - ec_tk_free(in2); - *result = out; - } else { - return -EINVAL; - } - - return 0; -} - -static int -ec_tk_cmd_eval_parenthesis(void **result, void *userctx, - const struct ec_parsed_tk *open_paren, - const struct ec_parsed_tk *close_paren, - void *value) -{ - const struct ec_strvec *vec; - struct ec_tk *in = value;; - struct ec_tk *out = NULL;; - - (void)userctx; - (void)close_paren; - - /* get parsed string vector, it should contain only one str */ - vec = ec_parsed_tk_strvec(open_paren); - if (ec_strvec_len(vec) != 1) - return -EINVAL; - - if (!strcmp(ec_strvec_val(vec, 0), "[")) { - out = ec_tk_option_new(NULL, ec_tk_clone(in)); - if (out == NULL) - return -EINVAL; - ec_tk_free(in); - } else if (!strcmp(ec_strvec_val(vec, 0), "(")) { - out = in; - } else { - return -EINVAL; - } - - printf("eval paren\n"); - *result = out; - - return 0; -} - -static void -ec_tk_cmd_eval_free(void *result, void *userctx) -{ - (void)userctx; - ec_free(result); -} - -static const struct ec_tk_expr_eval_ops test_ops = { - .eval_var = ec_tk_cmd_eval_var, - .eval_pre_op = ec_tk_cmd_eval_pre_op, - .eval_post_op = ec_tk_cmd_eval_post_op, - .eval_bin_op = ec_tk_cmd_eval_bin_op, - .eval_parenthesis = ec_tk_cmd_eval_parenthesis, - .eval_free = ec_tk_cmd_eval_free, -}; - -static struct ec_parsed_tk *ec_tk_cmd_parse(const struct ec_tk *gen_tk, - const struct ec_strvec *strvec) -{ - struct ec_tk_cmd *tk = (struct ec_tk_cmd *)gen_tk; - - return ec_tk_parse_tokens(tk->cmd, strvec); -} - -static struct ec_completed_tk *ec_tk_cmd_complete(const struct ec_tk *gen_tk, - const struct ec_strvec *strvec) -{ - struct ec_tk_cmd *tk = (struct ec_tk_cmd *)gen_tk; - - return ec_tk_complete_tokens(tk->cmd, strvec); -} - -static void ec_tk_cmd_free_priv(struct ec_tk *gen_tk) -{ - struct ec_tk_cmd *tk = (struct ec_tk_cmd *)gen_tk; - unsigned int i; - - ec_free(tk->cmd_str); - ec_tk_free(tk->cmd); - ec_tk_free(tk->expr); - ec_tk_free(tk->lex); - for (i = 0; i < tk->len; i++) - ec_tk_free(tk->table[i]); - ec_free(tk->table); -} - -static int ec_tk_cmd_build(struct ec_tk *gen_tk) -{ - struct ec_tk *expr = NULL, *lex = NULL, *cmd = NULL; - struct ec_parsed_tk *p, *child; - struct ec_tk_cmd *tk = (struct ec_tk_cmd *)gen_tk; - void *result; - int ret; - - /* build the expression parser */ - ret = -ENOMEM; - expr = ec_tk_new("expr", "expr"); - if (expr == NULL) - goto fail; - ret = ec_tk_expr_set_val_tk(expr, ec_tk_re(NULL, "[a-zA-Z0-9]+")); - if (ret < 0) - goto fail; - ret = ec_tk_expr_add_bin_op(expr, ec_tk_str(NULL, ",")); - if (ret < 0) - goto fail; - ret = ec_tk_expr_add_bin_op(expr, ec_tk_str(NULL, "|")); - if (ret < 0) - goto fail; - ret = ec_tk_expr_add_post_op(expr, ec_tk_str(NULL, "+")); - if (ret < 0) - goto fail; - ret = ec_tk_expr_add_post_op(expr, ec_tk_str(NULL, "*")); - if (ret < 0) - goto fail; - ret = ec_tk_expr_add_parenthesis(expr, ec_tk_str(NULL, "["), - ec_tk_str(NULL, "]")); - if (ret < 0) - goto fail; - ec_tk_expr_add_parenthesis(expr, ec_tk_str(NULL, "("), - ec_tk_str(NULL, ")")); - if (ret < 0) - goto fail; - - /* prepend a lexer and a "many" to the expression token */ - ret = -ENOMEM; - lex = ec_tk_re_lex(NULL, - ec_tk_many(NULL, ec_tk_clone(expr), 1, 0)); - if (lex == NULL) - goto fail; - - ret = ec_tk_re_lex_add(lex, "[a-zA-Z0-9]+", 1); - if (ret < 0) - goto fail; - ret = ec_tk_re_lex_add(lex, "[*|,()]", 1); - if (ret < 0) - goto fail; - ret = ec_tk_re_lex_add(lex, "\\[", 1); - if (ret < 0) - goto fail; - ret = ec_tk_re_lex_add(lex, "\\]", 1); - if (ret < 0) - goto fail; - ret = ec_tk_re_lex_add(lex, "[ ]+", 0); - if (ret < 0) - goto fail; - - /* parse the command expression */ - ret = -ENOMEM; - p = ec_tk_parse(lex, tk->cmd_str); - if (p == NULL) - goto fail; - - ret = -EINVAL; - if (!ec_parsed_tk_matches(p)) - goto fail; - if (TAILQ_EMPTY(&p->children)) - goto fail; - if (TAILQ_EMPTY(&TAILQ_FIRST(&p->children)->children)) - goto fail; - - ret = -ENOMEM; - cmd = ec_tk_new("seq", NULL); - if (cmd == NULL) - goto fail; - - TAILQ_FOREACH(child, &TAILQ_FIRST(&p->children)->children, next) { - ret = ec_tk_expr_eval(&result, expr, child, - &test_ops, tk); - if (ret < 0) - goto fail; - ret = ec_tk_seq_add(cmd, result); - if (ret < 0) - goto fail; - } - ec_parsed_tk_free(p); - ec_tk_dump(stdout, cmd); - - ec_tk_free(tk->expr); - tk->expr = expr; - ec_tk_free(tk->lex); - tk->lex = lex; - ec_tk_free(tk->cmd); - tk->cmd = cmd; - - return 0; - -fail: - ec_tk_free(expr); - ec_tk_free(lex); - ec_tk_free(cmd); - return ret; -} - -static struct ec_tk_type ec_tk_cmd_type = { - .name = "cmd", - .build = ec_tk_cmd_build, - .parse = ec_tk_cmd_parse, - .complete = ec_tk_cmd_complete, - .size = sizeof(struct ec_tk_cmd), - .free_priv = ec_tk_cmd_free_priv, -}; - -EC_TK_TYPE_REGISTER(ec_tk_cmd_type); - -int ec_tk_cmd_add_child(struct ec_tk *gen_tk, struct ec_tk *child) -{ - struct ec_tk_cmd *tk = (struct ec_tk_cmd *)gen_tk; - struct ec_tk **table; - - // XXX check tk type - - assert(tk != NULL); - - printf("add child %s\n", child->id); - if (child == NULL) - return -EINVAL; - - gen_tk->flags &= ~EC_TK_F_BUILT; - - table = ec_realloc(tk->table, (tk->len + 1) * sizeof(*tk->table)); - if (table == NULL) { - ec_tk_free(child); - return -ENOMEM; - } - - tk->table = table; - table[tk->len] = child; - tk->len++; - - child->parent = gen_tk; - TAILQ_INSERT_TAIL(&gen_tk->children, child, next); // XXX really needed? - - return 0; -} - -struct ec_tk *ec_tk_cmd(const char *id, const char *cmd_str) -{ - struct ec_tk *gen_tk = NULL; - struct ec_tk_cmd *tk = NULL; - - gen_tk = __ec_tk_new(&ec_tk_cmd_type, id); - if (gen_tk == NULL) - goto fail; - - tk = (struct ec_tk_cmd *)gen_tk; - tk->cmd_str = ec_strdup(cmd_str); - if (tk->cmd_str == NULL) - goto fail; - - return gen_tk; - -fail: - ec_tk_free(gen_tk); - return NULL; -} - -struct ec_tk *__ec_tk_cmd(const char *id, const char *cmd, ...) -{ - struct ec_tk *gen_tk = NULL; - struct ec_tk_cmd *tk = NULL; - struct ec_tk *child; - va_list ap; - int fail = 0; - - va_start(ap, cmd); - - gen_tk = ec_tk_cmd(id, cmd); - tk = (struct ec_tk_cmd *)gen_tk; - if (tk == NULL) - fail = 1;; - - for (child = va_arg(ap, struct ec_tk *); - child != EC_TK_ENDLIST; - child = va_arg(ap, struct ec_tk *)) { - - /* on error, don't quit the loop to avoid leaks */ - if (fail == 1 || child == NULL || - ec_tk_cmd_add_child(&tk->gen, child) < 0) { - fail = 1; - ec_tk_free(child); - } - } - - if (fail == 1) - goto fail; - - va_end(ap); - return gen_tk; - -fail: - ec_tk_free(gen_tk); /* will also free children */ - va_end(ap); - return NULL; -} - -static int ec_tk_cmd_testcase(void) -{ - struct ec_tk *tk; - int ret = 0; - - tk = EC_TK_CMD(NULL, - "command [option] (subset1, subset2) x | y", - ec_tk_int("x", 0, 10, 10), - ec_tk_int("y", 20, 30, 10) - ); - if (tk == NULL) { - ec_log(EC_LOG_ERR, "cannot create tk\n"); - return -1; - } - ret |= EC_TEST_CHECK_TK_PARSE(tk, 2, "command", "1"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 2, "command", "23"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 3, "command", "option", "23"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "command", "15"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "foo"); - ec_tk_free(tk); - - // XXX completion - - return ret; -} - -static struct ec_test ec_tk_cmd_test = { - .name = "tk_cmd", - .test = ec_tk_cmd_testcase, -}; - -EC_TEST_REGISTER(ec_tk_cmd_test); diff --git a/lib/ecoli_tk_cmd.h b/lib/ecoli_tk_cmd.h deleted file mode 100644 index f324ff4..0000000 --- a/lib/ecoli_tk_cmd.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2016-2017, Olivier MATZ - * - * 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 - -#define EC_TK_CMD(args...) __ec_tk_cmd(args, EC_TK_ENDLIST) - -struct ec_tk *__ec_tk_cmd(const char *id, const char *cmd_str, ...); - -struct ec_tk *ec_tk_cmd(const char *id, const char *cmd_str); - -/* child is consumed */ -int ec_tk_cmd_add_child(struct ec_tk *tk, struct ec_tk *child); - -#endif diff --git a/lib/ecoli_tk_empty.c b/lib/ecoli_tk_empty.c deleted file mode 100644 index a2b366c..0000000 --- a/lib/ecoli_tk_empty.c +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (c) 2016, Olivier MATZ - * - * 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 -#include -#include - -#include -#include -#include -#include -#include -#include - -struct ec_tk_empty { - struct ec_tk gen; -}; - -static struct ec_parsed_tk *ec_tk_empty_parse(const struct ec_tk *gen_tk, - const struct ec_strvec *strvec) -{ - struct ec_parsed_tk *parsed_tk; - struct ec_strvec *match_strvec; - - (void)strvec; - - parsed_tk = ec_parsed_tk_new(); - if (parsed_tk == NULL) - goto fail; - - match_strvec = ec_strvec_new(); - if (match_strvec == NULL) - goto fail; - - ec_parsed_tk_set_match(parsed_tk, gen_tk, match_strvec); - - return parsed_tk; - - fail: - ec_parsed_tk_free(parsed_tk); - return NULL; -} - -static struct ec_tk_type ec_tk_empty_type = { - .name = "empty", - .parse = ec_tk_empty_parse, - .complete = ec_tk_default_complete, - .size = sizeof(struct ec_tk_empty), -}; - -EC_TK_TYPE_REGISTER(ec_tk_empty_type); - -static int ec_tk_empty_testcase(void) -{ - struct ec_tk *tk; - int ret = 0; - - tk = ec_tk_new("empty", NULL); - if (tk == NULL) { - ec_log(EC_LOG_ERR, "cannot create tk\n"); - return -1; - } - ret |= EC_TEST_CHECK_TK_PARSE(tk, 0, "foo"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 0); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 0, "foo", "bar"); - ec_tk_free(tk); - - /* never completes */ - tk = ec_tk_new("empty", NULL); - if (tk == NULL) { - ec_log(EC_LOG_ERR, "cannot create tk\n"); - return -1; - } - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "", EC_TK_ENDLIST, - EC_TK_ENDLIST, - ""); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "foo", EC_TK_ENDLIST, - EC_TK_ENDLIST, - ""); - ec_tk_free(tk); - - return ret; -} - -static struct ec_test ec_tk_empty_test = { - .name = "tk_empty", - .test = ec_tk_empty_testcase, -}; - -EC_TEST_REGISTER(ec_tk_empty_test); diff --git a/lib/ecoli_tk_empty.h b/lib/ecoli_tk_empty.h deleted file mode 100644 index 6482c4b..0000000 --- a/lib/ecoli_tk_empty.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2016, Olivier MATZ - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the University of California, Berkeley nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * This token always matches an empty string vector - */ - -#ifndef ECOLI_TK_EMPTY_ -#define ECOLI_TK_EMPTY_ - -struct ec_tk *ec_tk_empty(const char *id); - -#endif diff --git a/lib/ecoli_tk_expr.c b/lib/ecoli_tk_expr.c deleted file mode 100644 index 661e756..0000000 --- a/lib/ecoli_tk_expr.c +++ /dev/null @@ -1,598 +0,0 @@ -/* - * Copyright (c) 2016-2017, Olivier MATZ - * - * 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 -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct ec_tk_expr { - struct ec_tk gen; - - /* the built node */ - struct ec_tk *child; - - /* the configuration nodes */ - struct ec_tk *val_tk; - struct ec_tk **bin_ops; - unsigned int bin_ops_len; - struct ec_tk **pre_ops; - unsigned int pre_ops_len; - struct ec_tk **post_ops; - unsigned int post_ops_len; - struct ec_tk **open_ops; - struct ec_tk **close_ops; - unsigned int paren_len; -}; - -static struct ec_parsed_tk *ec_tk_expr_parse(const struct ec_tk *gen_tk, - const struct ec_strvec *strvec) -{ - struct ec_tk_expr *tk = (struct ec_tk_expr *)gen_tk; - - return ec_tk_parse_tokens(tk->child, strvec); -} - -static struct ec_completed_tk *ec_tk_expr_complete(const struct ec_tk *gen_tk, - const struct ec_strvec *strvec) -{ - struct ec_tk_expr *tk = (struct ec_tk_expr *)gen_tk; - - return ec_tk_complete_tokens(tk->child, strvec); -} - -static void ec_tk_expr_free_priv(struct ec_tk *gen_tk) -{ - struct ec_tk_expr *tk = (struct ec_tk_expr *)gen_tk; - unsigned int i; - - ec_log(EC_LOG_DEBUG, "free %p %p %p\n", tk, tk->child, tk->val_tk); - ec_tk_free(tk->val_tk); - - for (i = 0; i < tk->bin_ops_len; i++) - ec_tk_free(tk->bin_ops[i]); - ec_free(tk->bin_ops); - for (i = 0; i < tk->pre_ops_len; i++) - ec_tk_free(tk->pre_ops[i]); - ec_free(tk->pre_ops); - for (i = 0; i < tk->post_ops_len; i++) - ec_tk_free(tk->post_ops[i]); - ec_free(tk->post_ops); - for (i = 0; i < tk->paren_len; i++) { - ec_tk_free(tk->open_ops[i]); - ec_tk_free(tk->close_ops[i]); - } - ec_free(tk->open_ops); - ec_free(tk->close_ops); - - ec_tk_free(tk->child); -} - -static int ec_tk_expr_build(struct ec_tk *gen_tk) -{ - struct ec_tk_expr *tk = (struct ec_tk_expr *)gen_tk; - struct ec_tk *term = NULL, *expr = NULL, *next = NULL, - *pre_op = NULL, *post_op = NULL, - *post = NULL, *weak = NULL; - unsigned int i; - int ret; - - if (tk->val_tk == NULL) - return -EINVAL; - if (tk->bin_ops_len == 0 && tk->pre_ops_len == 0 && - tk->post_ops_len == 0) - return -EINVAL; - - /* create the object, we will initialize it later: this is - * needed because we have a circular dependency */ - ret = -ENOMEM; - weak = ec_tk_new("weakref", "weak"); - if (weak == NULL) - return -1; - - /* prefix unary operators */ - pre_op = ec_tk_new("or", "pre-op"); - if (pre_op == NULL) - goto fail; - for (i = 0; i < tk->pre_ops_len; i++) { - if (ec_tk_or_add(pre_op, ec_tk_clone(tk->pre_ops[i])) < 0) - goto fail; - } - - /* suffix unary operators */ - post_op = ec_tk_new("or", "post-op"); - if (post_op == NULL) - goto fail; - for (i = 0; i < tk->post_ops_len; i++) { - if (ec_tk_or_add(post_op, ec_tk_clone(tk->post_ops[i])) < 0) - goto fail; - } - - post = ec_tk_new("or", "post"); - if (post == NULL) - goto fail; - if (ec_tk_or_add(post, ec_tk_clone(tk->val_tk)) < 0) - goto fail; - if (ec_tk_or_add(post, - EC_TK_SEQ(NULL, - ec_tk_clone(pre_op), - ec_tk_clone(weak))) < 0) - goto fail; - for (i = 0; i < tk->paren_len; i++) { - if (ec_tk_or_add(post, EC_TK_SEQ(NULL, - ec_tk_clone(tk->open_ops[i]), - ec_tk_clone(weak), - ec_tk_clone(tk->close_ops[i]))) < 0) - goto fail; - } - term = EC_TK_SEQ("term", - ec_tk_clone(post), - ec_tk_many(NULL, ec_tk_clone(post_op), 0, 0) - ); - if (term == NULL) - goto fail; - - for (i = 0; i < tk->bin_ops_len; i++) { - next = EC_TK_SEQ("next", - ec_tk_clone(term), - ec_tk_many(NULL, - EC_TK_SEQ(NULL, - ec_tk_clone(tk->bin_ops[i]), - ec_tk_clone(term) - ), - 0, 0 - ) - ); - ec_tk_free(term); - term = next; - if (term == NULL) - goto fail; - } - expr = term; - term = NULL; - - /* free the initial references */ - ec_tk_free(pre_op); - pre_op = NULL; - ec_tk_free(post_op); - post_op = NULL; - ec_tk_free(post); - post = NULL; - - /* no need to clone here, the node is not consumed */ - if (ec_tk_weakref_set(weak, expr) < 0) - goto fail; - ec_tk_free(weak); - weak = NULL; - - tk->child = expr; - - return 0; - -fail: - ec_tk_free(term); - ec_tk_free(expr); - ec_tk_free(pre_op); - ec_tk_free(post_op); - ec_tk_free(post); - ec_tk_free(weak); - - return ret; -} - -static struct ec_tk_type ec_tk_expr_type = { - .name = "expr", - .build = ec_tk_expr_build, - .parse = ec_tk_expr_parse, - .complete = ec_tk_expr_complete, - .size = sizeof(struct ec_tk_expr), - .free_priv = ec_tk_expr_free_priv, -}; - -EC_TK_TYPE_REGISTER(ec_tk_expr_type); - -int ec_tk_expr_set_val_tk(struct ec_tk *gen_tk, struct ec_tk *val_tk) -{ - struct ec_tk_expr *tk = (struct ec_tk_expr *)gen_tk; - int ret; - - ret = -EINVAL; - if (val_tk == NULL) - goto fail; - ret = -EPERM; - if (gen_tk->flags & EC_TK_F_BUILT) - goto fail; - ret = -EEXIST; - if (tk->val_tk != NULL) - goto fail; - - tk->val_tk = val_tk; - gen_tk->flags &= ~EC_TK_F_BUILT; - - return 0; - -fail: - ec_tk_free(val_tk); - return ret; -} - -/* add a binary operator */ -int ec_tk_expr_add_bin_op(struct ec_tk *gen_tk, struct ec_tk *op) -{ - struct ec_tk_expr *tk = (struct ec_tk_expr *)gen_tk; - struct ec_tk **bin_ops; - int ret; - - // XXX check tk type - - ret = -EINVAL; - if (tk == NULL || op == NULL) - goto fail; - ret = -EPERM; - if (gen_tk->flags & EC_TK_F_BUILT) - goto fail; - - ret = -ENOMEM; - bin_ops = ec_realloc(tk->bin_ops, - (tk->bin_ops_len + 1) * sizeof(*tk->bin_ops)); - if (bin_ops == NULL) - goto fail;; - - tk->bin_ops = bin_ops; - bin_ops[tk->bin_ops_len] = op; - tk->bin_ops_len++; - gen_tk->flags &= ~EC_TK_F_BUILT; - - return 0; - -fail: - ec_tk_free(op); - return ret; -} - -/* add a unary pre-operator */ -int ec_tk_expr_add_pre_op(struct ec_tk *gen_tk, struct ec_tk *op) -{ - struct ec_tk_expr *tk = (struct ec_tk_expr *)gen_tk; - struct ec_tk **pre_ops; - int ret; - - // XXX check tk type - - ret = -EINVAL; - if (tk == NULL || op == NULL) - goto fail; - ret = -EPERM; - if (gen_tk->flags & EC_TK_F_BUILT) - goto fail; - - ret = -ENOMEM; - pre_ops = ec_realloc(tk->pre_ops, - (tk->pre_ops_len + 1) * sizeof(*tk->pre_ops)); - if (pre_ops == NULL) - goto fail; - - tk->pre_ops = pre_ops; - pre_ops[tk->pre_ops_len] = op; - tk->pre_ops_len++; - gen_tk->flags &= ~EC_TK_F_BUILT; - - return 0; - -fail: - ec_tk_free(op); - return ret; -} - -/* add a unary post-operator */ -int ec_tk_expr_add_post_op(struct ec_tk *gen_tk, struct ec_tk *op) -{ - struct ec_tk_expr *tk = (struct ec_tk_expr *)gen_tk; - struct ec_tk **post_ops; - int ret; - - // XXX check tk type - - ret = -EINVAL; - if (tk == NULL || op == NULL) - goto fail; - ret = -EPERM; - if (gen_tk->flags & EC_TK_F_BUILT) - goto fail; - - ret = -ENOMEM; - post_ops = ec_realloc(tk->post_ops, - (tk->post_ops_len + 1) * sizeof(*tk->post_ops)); - if (post_ops == NULL) - goto fail; - - tk->post_ops = post_ops; - post_ops[tk->post_ops_len] = op; - tk->post_ops_len++; - gen_tk->flags &= ~EC_TK_F_BUILT; - - return 0; - -fail: - ec_tk_free(op); - return ret; -} - -/* add parenthesis symbols */ -int ec_tk_expr_add_parenthesis(struct ec_tk *gen_tk, - struct ec_tk *open, struct ec_tk *close) -{ - struct ec_tk_expr *tk = (struct ec_tk_expr *)gen_tk; - struct ec_tk **open_ops, **close_ops; - int ret; - - // XXX check tk type - - ret = -EINVAL; - if (tk == NULL || open == NULL || close == NULL) - goto fail; - ret = -EPERM; - if (gen_tk->flags & EC_TK_F_BUILT) - goto fail;; - - ret = -ENOMEM; - open_ops = ec_realloc(tk->open_ops, - (tk->paren_len + 1) * sizeof(*tk->open_ops)); - if (open_ops == NULL) - goto fail; - close_ops = ec_realloc(tk->close_ops, - (tk->paren_len + 1) * sizeof(*tk->close_ops)); - if (close_ops == NULL) - goto fail; - - tk->open_ops = open_ops; - tk->close_ops = close_ops; - open_ops[tk->paren_len] = open; - close_ops[tk->paren_len] = close; - tk->paren_len++; - gen_tk->flags &= ~EC_TK_F_BUILT; - - return 0; - -fail: - ec_tk_free(open); - ec_tk_free(close); - return ret; -} - -enum expr_tk_type { - NONE, - VAL, - BIN_OP, - PRE_OP, - POST_OP, - PAREN_OPEN, - PAREN_CLOSE, -}; -static enum expr_tk_type get_tk_type(const struct ec_tk *expr_gen_tk, - const struct ec_tk *check_tk) -{ - struct ec_tk_expr *expr_tk = (struct ec_tk_expr *)expr_gen_tk; - size_t i; - - if (check_tk == expr_tk->val_tk) - return VAL; - - for (i = 0; i < expr_tk->bin_ops_len; i++) { - if (check_tk == expr_tk->bin_ops[i]) - return BIN_OP; - } - for (i = 0; i < expr_tk->pre_ops_len; i++) { - if (check_tk == expr_tk->pre_ops[i]) - return PRE_OP; - } - for (i = 0; i < expr_tk->post_ops_len; i++) { - if (check_tk == expr_tk->post_ops[i]) - return POST_OP; - } - - for (i = 0; i < expr_tk->paren_len; i++) { - if (check_tk == expr_tk->open_ops[i]) - return PAREN_OPEN; - } - for (i = 0; i < expr_tk->paren_len; i++) { - if (check_tk == expr_tk->close_ops[i]) - return PAREN_CLOSE; - } - - return NONE; -} - -struct result { - bool has_val; - void *val; - const struct ec_parsed_tk *op; - enum expr_tk_type op_type; -}; - -/* merge x and y results in x */ -static int merge_results(void *userctx, - const struct ec_tk_expr_eval_ops *ops, - struct result *x, const struct result *y) -{ - int ret; - - if (y->has_val == 0 && y->op == NULL) - return 0; - if (x->has_val == 0 && x->op == NULL) { - *x = *y; - return 0; - } - - if (x->has_val && x->op == NULL && y->has_val && y->op != NULL) { - ret = ops->eval_bin_op(&x->val, userctx, x->val, y->op, y->val); - if (ret < 0) - return ret; - - return 0; - } - - if (x->has_val == 0 && x->op != NULL && y->has_val && y->op == NULL) { - if (x->op_type == PRE_OP) { - ret = ops->eval_pre_op(&x->val, userctx, y->val, x->op); - if (ret < 0) - return ret; - x->has_val = true; - x->op_type = NONE; - x->op = NULL; - return 0; - } else if (x->op_type == BIN_OP) { - x->val = y->val; - x->has_val = true; - return 0; - } - } - - if (x->has_val && x->op == NULL && y->has_val == 0 && y->op != NULL) { - ret = ops->eval_post_op(&x->val, userctx, x->val, y->op); - if (ret < 0) - return ret; - - return 0; - } - - assert(true); /* we should not get here */ - return -EINVAL; -} - -static int eval_expression(struct result *result, - void *userctx, - const struct ec_tk_expr_eval_ops *ops, - const struct ec_tk *expr_gen_tk, - const struct ec_parsed_tk *parsed_tk) - -{ - struct ec_parsed_tk *open = NULL, *close = NULL; - struct result child_result; - struct ec_parsed_tk *child; - enum expr_tk_type type; - int ret; - - memset(result, 0, sizeof(*result)); - memset(&child_result, 0, sizeof(child_result)); - - type = get_tk_type(expr_gen_tk, parsed_tk->tk); - if (type == VAL) { - ret = ops->eval_var(&result->val, userctx, parsed_tk); - if (ret < 0) - goto fail; - result->has_val = 1; - } else if (type == PRE_OP || type == POST_OP || type == BIN_OP) { - result->op = parsed_tk; - result->op_type = type; - } - - TAILQ_FOREACH(child, &parsed_tk->children, next) { - - type = get_tk_type(expr_gen_tk, child->tk); - if (type == PAREN_OPEN) { - open = child; - continue; - } else if (type == PAREN_CLOSE) { - close = child; - continue; - } - - ret = eval_expression(&child_result, userctx, ops, - expr_gen_tk, child); - if (ret < 0) - goto fail; - - ret = merge_results(userctx, ops, result, &child_result); - if (ret < 0) - goto fail; - - memset(&child_result, 0, sizeof(child_result)); - } - - if (open != NULL && close != NULL) { - ret = ops->eval_parenthesis(&result->val, userctx, open, close, - result->val); - if (ret < 0) - goto fail; - } - - return 0; - -fail: - if (result->has_val) - ops->eval_free(result->val, userctx); - if (child_result.has_val) - ops->eval_free(child_result.val, userctx); - memset(result, 0, sizeof(*result)); - - return ret; -} - -int ec_tk_expr_eval(void **user_result, const struct ec_tk *tk, - struct ec_parsed_tk *parsed, const struct ec_tk_expr_eval_ops *ops, - void *userctx) -{ - struct result result; - int ret; - - if (ops == NULL || ops->eval_var == NULL || ops->eval_pre_op == NULL || - ops->eval_post_op == NULL || ops->eval_bin_op == NULL || - ops->eval_parenthesis == NULL || ops->eval_free == NULL) - return -EINVAL; - - if (!ec_parsed_tk_matches(parsed)) - return -EINVAL; - - ec_parsed_tk_dump(stdout, parsed); //XXX - ret = eval_expression(&result, userctx, ops, tk, parsed); - if (ret < 0) - return ret; - - assert(result.has_val); - assert(result.op == NULL); - *user_result = result.val; - - return 0; -} - -/* the test case is in a separate file ecoli_tk_expr_test.c */ diff --git a/lib/ecoli_tk_expr.h b/lib/ecoli_tk_expr.h deleted file mode 100644 index d2efc24..0000000 --- a/lib/ecoli_tk_expr.h +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright (c) 2016, Olivier MATZ - * - * 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 - -/* XXX remove the _new for all other tokens */ - -/** - * Callback function type for evaluating a variable - * - * @param result - * On success, this pointer must be set by the user to point - * to a user structure describing the evaluated result. - * @param userctx - * A user-defined context passed to all callback functions, which - * can be used to maintain a state or store global information. - * @param var - * The parsed token referencing the variable. - * @return - * 0 on success (*result must be set), or -errno on error (*result - * is undefined). - */ -typedef int (*ec_tk_expr_eval_var_t)( - void **result, void *userctx, - const struct ec_parsed_tk *var); - -/** - * Callback function type for evaluating a prefix-operator - * - * @param result - * On success, this pointer must be set by the user to point - * to a user structure describing the evaluated result. - * @param userctx - * A user-defined context passed to all callback functions, which - * can be used to maintain a state or store global information. - * @param operand - * The evaluated expression on which the operation should be applied. - * @param var - * The parsed token referencing the operator. - * @return - * 0 on success (*result must be set, operand is freed), - * or -errno on error (*result is undefined, operand is not freed). - */ -typedef int (*ec_tk_expr_eval_pre_op_t)( - void **result, void *userctx, - void *operand, - const struct ec_parsed_tk *operator); - -typedef int (*ec_tk_expr_eval_post_op_t)( - void **result, void *userctx, - void *operand, - const struct ec_parsed_tk *operator); - -typedef int (*ec_tk_expr_eval_bin_op_t)( - void **result, void *userctx, - void *operand1, - const struct ec_parsed_tk *operator, - void *operand2); - -typedef int (*ec_tk_expr_eval_parenthesis_t)( - void **result, void *userctx, - const struct ec_parsed_tk *open_paren, - const struct ec_parsed_tk *close_paren, - void * value); - -typedef void (*ec_tk_expr_eval_free_t)( - void *result, void *userctx); - - -struct ec_tk *ec_tk_expr(const char *id); -int ec_tk_expr_set_val_tk(struct ec_tk *gen_tk, struct ec_tk *val_tk); -int ec_tk_expr_add_bin_op(struct ec_tk *gen_tk, struct ec_tk *op); -int ec_tk_expr_add_pre_op(struct ec_tk *gen_tk, struct ec_tk *op); -int ec_tk_expr_add_post_op(struct ec_tk *gen_tk, struct ec_tk *op); -int ec_tk_expr_add_parenthesis(struct ec_tk *gen_tk, - struct ec_tk *open, struct ec_tk *close); - -struct ec_tk_expr_eval_ops { - ec_tk_expr_eval_var_t eval_var; - ec_tk_expr_eval_pre_op_t eval_pre_op; - ec_tk_expr_eval_post_op_t eval_post_op; - ec_tk_expr_eval_bin_op_t eval_bin_op; - ec_tk_expr_eval_parenthesis_t eval_parenthesis; - ec_tk_expr_eval_free_t eval_free; -}; - -int ec_tk_expr_eval(void **result, const struct ec_tk *tk, - struct ec_parsed_tk *parsed, const struct ec_tk_expr_eval_ops *ops, - void *userctx); - -#endif diff --git a/lib/ecoli_tk_expr_test.c b/lib/ecoli_tk_expr_test.c deleted file mode 100644 index 4a00cdf..0000000 --- a/lib/ecoli_tk_expr_test.c +++ /dev/null @@ -1,304 +0,0 @@ -/* - * Copyright (c) 2016-2017, Olivier MATZ - * - * 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 -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -struct my_eval_result { - int val; -}; - -static int -ec_tk_expr_test_eval_var(void **result, void *userctx, - const struct ec_parsed_tk *var) -{ - const struct ec_strvec *vec; - struct my_eval_result *eval; - - (void)userctx; - - /* get parsed string vector, it should contain only one str */ - vec = ec_parsed_tk_strvec(var); - if (ec_strvec_len(vec) != 1) - return -EINVAL; - - eval = ec_malloc(sizeof(*eval)); - if (eval == NULL) - return -ENOMEM; - - eval->val = atoi(ec_strvec_val(vec, 0)); // XXX use strtol - printf("eval var %d\n", eval->val); - *result = eval; - - return 0; -} - -static int -ec_tk_expr_test_eval_pre_op(void **result, void *userctx, void *operand, - const struct ec_parsed_tk *operator) -{ - const struct ec_strvec *vec; - struct my_eval_result *eval = operand;; - - (void)userctx; - - /* get parsed string vector, it should contain only one str */ - vec = ec_parsed_tk_strvec(operator); - if (ec_strvec_len(vec) != 1) - return -EINVAL; - - if (!strcmp(ec_strvec_val(vec, 0), "!")) - eval->val = !eval->val; - else - return -EINVAL; - - printf("eval pre_op %d\n", eval->val); - *result = eval; - - return 0; -} - -static int -ec_tk_expr_test_eval_post_op(void **result, void *userctx, void *operand, - const struct ec_parsed_tk *operator) -{ - const struct ec_strvec *vec; - struct my_eval_result *eval = operand;; - - (void)userctx; - - /* get parsed string vector, it should contain only one str */ - vec = ec_parsed_tk_strvec(operator); - if (ec_strvec_len(vec) != 1) - return -EINVAL; - - if (!strcmp(ec_strvec_val(vec, 0), "^")) - eval->val = eval->val * eval->val; - else - return -EINVAL; - - printf("eval post_op %d\n", eval->val); - *result = eval; - - return 0; -} - -static int -ec_tk_expr_test_eval_bin_op(void **result, void *userctx, void *operand1, - const struct ec_parsed_tk *operator, void *operand2) - -{ - const struct ec_strvec *vec; - struct my_eval_result *eval1 = operand1;; - struct my_eval_result *eval2 = operand2;; - - (void)userctx; - - /* get parsed string vector, it should contain only one str */ - vec = ec_parsed_tk_strvec(operator); - if (ec_strvec_len(vec) != 1) - return -EINVAL; - - if (!strcmp(ec_strvec_val(vec, 0), "+")) - eval1->val = eval1->val + eval2->val; - else if (!strcmp(ec_strvec_val(vec, 0), "*")) - eval1->val = eval1->val * eval2->val; - else - return -EINVAL; - - printf("eval bin_op %d\n", eval1->val); - ec_free(eval2); - *result = eval1; - - return 0; -} - -static int -ec_tk_expr_test_eval_parenthesis(void **result, void *userctx, - const struct ec_parsed_tk *open_paren, - const struct ec_parsed_tk *close_paren, - void *value) -{ - (void)userctx; - (void)open_paren; - (void)close_paren; - - printf("eval paren\n"); - *result = value; - - return 0; -} - -static void -ec_tk_expr_test_eval_free(void *result, void *userctx) -{ - (void)userctx; - ec_free(result); -} - -static const struct ec_tk_expr_eval_ops test_ops = { - .eval_var = ec_tk_expr_test_eval_var, - .eval_pre_op = ec_tk_expr_test_eval_pre_op, - .eval_post_op = ec_tk_expr_test_eval_post_op, - .eval_bin_op = ec_tk_expr_test_eval_bin_op, - .eval_parenthesis = ec_tk_expr_test_eval_parenthesis, - .eval_free = ec_tk_expr_test_eval_free, -}; - -static int ec_tk_expr_test_eval(struct ec_tk *lex_tk, - const struct ec_tk *expr_tk, - const char *str, int val) -{ - struct ec_parsed_tk *p; - void *result; - struct my_eval_result *eval; - int ret; - - /* XXX check tk type (again and again) */ - - p = ec_tk_parse(lex_tk, str); - if (p == NULL) - return -1; - - ret = ec_tk_expr_eval(&result, expr_tk, p, &test_ops, NULL); - ec_parsed_tk_free(p); - if (ret < 0) - return -1; - - /* the parsed value is an integer */ - eval = result; - assert(eval != NULL); - - printf("result: %d (expected %d)\n", eval->val, val); - if (eval->val == val) - ret = 0; - else - ret = -1; - - ec_free(eval); - - return ret; -} - -static int ec_tk_expr_testcase(void) -{ - struct ec_tk *tk = NULL, *lex_tk = NULL; - int ret = 0; - - tk = ec_tk_new("expr", "my_expr"); - if (tk == NULL) - return -1; - - ec_tk_expr_set_val_tk(tk, ec_tk_int(NULL, 0, UCHAR_MAX, 0)); - ec_tk_expr_add_bin_op(tk, ec_tk_str(NULL, "+")); - ec_tk_expr_add_bin_op(tk, ec_tk_str(NULL, "*")); - ec_tk_expr_add_pre_op(tk, ec_tk_str(NULL, "!")); /* not */ - ec_tk_expr_add_post_op(tk, ec_tk_str(NULL, "^")); /* square */ - ec_tk_expr_add_parenthesis(tk, ec_tk_str(NULL, "("), - ec_tk_str(NULL, ")")); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "1"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "1", "1"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "1", "*"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 3, "1", "*", "1"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 3, "1", "*", "1", "*"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 4, "1", "+", "!", "1"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 4, "1", "^", "+", "1"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 5, "1", "*", "1", "*", "1"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 5, "1", "*", "1", "+", "1"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 7, "1", "*", "1", "*", "1", "*", "1"); - ret |= EC_TEST_CHECK_TK_PARSE( - tk, 10, "!", "(", "1", "*", "(", "1", "+", "1", ")", ")"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 5, "1", "+", "!", "1", "^"); - - /* prepend a lexer to the expression token */ - lex_tk = ec_tk_re_lex(NULL, ec_tk_clone(tk)); - if (lex_tk == NULL) - goto fail; - - ret |= ec_tk_re_lex_add(lex_tk, "[0-9]+", 1); /* vars */ - ret |= ec_tk_re_lex_add(lex_tk, "[+*!^()]", 1); /* operators */ - ret |= ec_tk_re_lex_add(lex_tk, "[ ]+", 0); /* spaces */ - - /* valid expressions */ - ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "!1"); - ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "1^"); - ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "1^ + 1"); - ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "1 + 4 * (2 + 3^)^"); - ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "(1)"); - ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "3*!3+!3*(2+ 2)"); - ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "!!(!1)^ + !(4 + (2*3))"); - ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "(1 + 1)^ * 1^"); - - /* invalid expressions */ - ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, -1, ""); - ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, -1, "()"); - ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, -1, "("); - ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, -1, ")"); - ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, -1, "+1"); - ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, -1, "1+"); - ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, -1, "1+*1"); - ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, -1, "1+(1*1"); - ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, -1, "1+!1!1)"); - - ret |= ec_tk_expr_test_eval(lex_tk, tk, "1^", 1); - ret |= ec_tk_expr_test_eval(lex_tk, tk, "2^", 4); - ret |= ec_tk_expr_test_eval(lex_tk, tk, "!1", 0); - ret |= ec_tk_expr_test_eval(lex_tk, tk, "!0", 1); - - ret |= ec_tk_expr_test_eval(lex_tk, tk, "1+1", 2); - ret |= ec_tk_expr_test_eval(lex_tk, tk, "1+1*2", 4); - ret |= ec_tk_expr_test_eval(lex_tk, tk, "2 * 2^", 8); - ret |= ec_tk_expr_test_eval(lex_tk, tk, "(1 + !0)^ * !0^", 4); - ret |= ec_tk_expr_test_eval(lex_tk, tk, "(1 + !1) * 3", 3); - - ec_tk_free(tk); - ec_tk_free(lex_tk); - - return ret; - -fail: - ec_tk_free(lex_tk); - ec_tk_free(tk); - return -1; -} - -static struct ec_test ec_tk_expr_test = { - .name = "tk_expr", - .test = ec_tk_expr_testcase, -}; - -EC_TEST_REGISTER(ec_tk_expr_test); diff --git a/lib/ecoli_tk_int.c b/lib/ecoli_tk_int.c deleted file mode 100644 index 905f516..0000000 --- a/lib/ecoli_tk_int.c +++ /dev/null @@ -1,240 +0,0 @@ -/* - * Copyright (c) 2016, Olivier MATZ - * - * 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 -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -struct ec_tk_int { - struct ec_tk gen; - bool check_min; - long long int min; - bool check_max; - long long int max; - unsigned int base; -}; - -static int parse_llint(struct ec_tk_int *tk, const char *str, - long long *val) -{ - char *endptr; - - errno = 0; - *val = strtoll(str, &endptr, tk->base); - - /* out of range */ - if ((errno == ERANGE && (*val == LLONG_MAX || *val == LLONG_MIN)) || - (errno != 0 && *val == 0)) - return -1; - - if (tk->check_min && *val < tk->min) - return -1; - - if (tk->check_max && *val > tk->max) - return -1; - - if (*endptr != 0) - return -1; - - return 0; -} - -static struct ec_parsed_tk *ec_tk_int_parse(const struct ec_tk *gen_tk, - const struct ec_strvec *strvec) -{ - struct ec_tk_int *tk = (struct ec_tk_int *)gen_tk; - struct ec_parsed_tk *parsed_tk; - struct ec_strvec *match_strvec; - const char *str; - long long val; - - parsed_tk = ec_parsed_tk_new(); - if (parsed_tk == NULL) - goto fail; - - if (ec_strvec_len(strvec) == 0) - return parsed_tk; - - str = ec_strvec_val(strvec, 0); - if (parse_llint(tk, str, &val) < 0) - return parsed_tk; - - match_strvec = ec_strvec_ndup(strvec, 0, 1); - if (match_strvec == NULL) - goto fail; - - ec_parsed_tk_set_match(parsed_tk, gen_tk, match_strvec); - - return parsed_tk; - - fail: - ec_parsed_tk_free(parsed_tk); - return NULL; -} - -static struct ec_tk_type ec_tk_int_type = { - .name = "int", - .parse = ec_tk_int_parse, - .complete = ec_tk_default_complete, - .size = sizeof(struct ec_tk_int), -}; - -EC_TK_TYPE_REGISTER(ec_tk_int_type); - -struct ec_tk *ec_tk_int(const char *id, long long int min, - long long int max, unsigned int base) -{ - struct ec_tk *gen_tk = NULL; - struct ec_tk_int *tk = NULL; - - gen_tk = __ec_tk_new(&ec_tk_int_type, id); - if (gen_tk == NULL) - return NULL; - tk = (struct ec_tk_int *)gen_tk; - - tk->check_min = true; - tk->min = min; - tk->check_max = true; - tk->max = max; - tk->base = base; - - return &tk->gen; -} - -long long ec_tk_int_getval(struct ec_tk *gen_tk, const char *str) -{ - struct ec_tk_int *tk = (struct ec_tk_int *)gen_tk; - long long val = 0; - - // XXX check type here - // if gen_tk->type != int fail - - parse_llint(tk, str, &val); - - return val; -} - -static int ec_tk_int_testcase(void) -{ - struct ec_parsed_tk *p; - struct ec_tk *tk; - const char *s; - int ret = 0; - - tk = ec_tk_int(NULL, 0, 256, 0); - if (tk == NULL) { - ec_log(EC_LOG_ERR, "cannot create tk\n"); - return -1; - } - ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "0"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "256", "foo"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "0x100"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, " 1"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "-1"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "0x101"); - - p = ec_tk_parse(tk, "0"); - s = ec_strvec_val(ec_parsed_tk_strvec(p), 0); - EC_TEST_ASSERT(s != NULL && ec_tk_int_getval(tk, s) == 0); - ec_parsed_tk_free(p); - - p = ec_tk_parse(tk, "10"); - s = ec_strvec_val(ec_parsed_tk_strvec(p), 0); - EC_TEST_ASSERT(s != NULL && ec_tk_int_getval(tk, s) == 10); - ec_parsed_tk_free(p); - ec_tk_free(tk); - - tk = ec_tk_int(NULL, -1, LLONG_MAX, 16); - if (tk == NULL) { - ec_log(EC_LOG_ERR, "cannot create tk\n"); - return -1; - } - ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "0"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "-1"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "7fffffffffffffff"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "0x7fffffffffffffff"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "-2"); - - p = ec_tk_parse(tk, "10"); - s = ec_strvec_val(ec_parsed_tk_strvec(p), 0); - EC_TEST_ASSERT(s != NULL && ec_tk_int_getval(tk, s) == 16); - ec_parsed_tk_free(p); - ec_tk_free(tk); - - tk = ec_tk_int(NULL, LLONG_MIN, 0, 10); - if (tk == NULL) { - ec_log(EC_LOG_ERR, "cannot create tk\n"); - return -1; - } - ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "0"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "-1"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "-9223372036854775808"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "0x0"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "1"); - ec_tk_free(tk); - - /* test completion */ - tk = ec_tk_int(NULL, 0, 10, 0); - if (tk == NULL) { - ec_log(EC_LOG_ERR, "cannot create tk\n"); - return -1; - } - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "", EC_TK_ENDLIST, - EC_TK_ENDLIST, - ""); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "x", EC_TK_ENDLIST, - EC_TK_ENDLIST, - ""); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "1", EC_TK_ENDLIST, - EC_TK_ENDLIST, - ""); - ec_tk_free(tk); - - return ret; -} - -static struct ec_test ec_tk_int_test = { - .name = "tk_int", - .test = ec_tk_int_testcase, -}; - -EC_TEST_REGISTER(ec_tk_int_test); diff --git a/lib/ecoli_tk_int.h b/lib/ecoli_tk_int.h deleted file mode 100644 index 156ebf1..0000000 --- a/lib/ecoli_tk_int.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2016, Olivier MATZ - * - * 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 - -// XXX remove min, max, base from new(), and add ec_tk_int_set_limits() + -// XXX ec_tk_int_set_base() ? -struct ec_tk *ec_tk_int(const char *id, long long int min, - long long int max, unsigned int base); -long long ec_tk_int_getval(struct ec_tk *tk, const char *str); - -#endif diff --git a/lib/ecoli_tk_many.c b/lib/ecoli_tk_many.c deleted file mode 100644 index 235f22a..0000000 --- a/lib/ecoli_tk_many.c +++ /dev/null @@ -1,257 +0,0 @@ -/* - * Copyright (c) 2016, Olivier MATZ - * - * 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 -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -struct ec_tk_many { - struct ec_tk gen; - unsigned int min; - unsigned int max; - struct ec_tk *child; -}; - -static struct ec_parsed_tk *ec_tk_many_parse(const struct ec_tk *gen_tk, - const struct ec_strvec *strvec) -{ - struct ec_tk_many *tk = (struct ec_tk_many *)gen_tk; - struct ec_parsed_tk *parsed_tk, *child_parsed_tk; - struct ec_strvec *match_strvec; - struct ec_strvec *childvec = NULL; - size_t off = 0, len, count; - - parsed_tk = ec_parsed_tk_new(); - if (parsed_tk == NULL) - goto fail; - - for (count = 0; tk->max == 0 || count < tk->max; count++) { - childvec = ec_strvec_ndup(strvec, off, - ec_strvec_len(strvec) - off); - if (childvec == NULL) - goto fail; - - child_parsed_tk = ec_tk_parse_tokens(tk->child, childvec); - if (child_parsed_tk == NULL) - goto fail; - - ec_strvec_free(childvec); - childvec = NULL; - - if (!ec_parsed_tk_matches(child_parsed_tk)) { - ec_parsed_tk_free(child_parsed_tk); - break; - } - - ec_parsed_tk_add_child(parsed_tk, child_parsed_tk); - - /* it matches "no token", no need to continue */ - len = ec_parsed_tk_len(child_parsed_tk); - if (len == 0) { - ec_parsed_tk_free(child_parsed_tk); - break; - } - - off += len; - } - - if (count < tk->min) { - ec_parsed_tk_free_children(parsed_tk); - return parsed_tk; - } - - match_strvec = ec_strvec_ndup(strvec, 0, off); - if (match_strvec == NULL) - goto fail; - - ec_parsed_tk_set_match(parsed_tk, gen_tk, match_strvec); - - return parsed_tk; - -fail: - ec_strvec_free(childvec); - ec_parsed_tk_free(parsed_tk); - return NULL; -} - -#if 0 //XXX missing tk_many_complete -static struct ec_completed_tk *ec_tk_many_complete(const struct ec_tk *gen_tk, - const struct ec_strvec *strvec) -{ - struct ec_tk_many *tk = (struct ec_tk_many *)gen_tk; - struct ec_completed_tk *completed_tk, *child_completed_tk; - struct ec_strvec *childvec; - struct ec_parsed_tk *parsed_tk; - size_t len = 0; - unsigned int i; - - completed_tk = ec_completed_tk_new(); - if (completed_tk == NULL) - return NULL; - - if (tk->len == 0) - return completed_tk; - - for (i = 0; i < tk->len; i++) { - childvec = ec_strvec_ndup(strvec, len, - ec_strvec_len(strvec) - len); - if (childvec == NULL) - goto fail; // XXX fail ? - - child_completed_tk = ec_tk_complete_tokens(tk->table[i], - childvec); - if (child_completed_tk == NULL) - goto fail; - - ec_completed_tk_merge(completed_tk, child_completed_tk); - - parsed_tk = ec_tk_parse_tokens(tk->table[i], childvec); - if (parsed_tk == NULL) - goto fail; - - ec_strvec_free(childvec); - childvec = NULL; - - if (!ec_parsed_tk_matches(parsed_tk)) { - ec_parsed_tk_free(parsed_tk); - break; - } - - len += ec_strvec_len(parsed_tk->strvec); - ec_parsed_tk_free(parsed_tk); - } - - return completed_tk; - -fail: - ec_strvec_free(childvec); - ec_completed_tk_free(completed_tk); - return NULL; -} -#endif - -static void ec_tk_many_free_priv(struct ec_tk *gen_tk) -{ - struct ec_tk_many *tk = (struct ec_tk_many *)gen_tk; - - ec_tk_free(tk->child); -} - -static struct ec_tk_type ec_tk_many_type = { - .name = "many", - .parse = ec_tk_many_parse, - .complete = ec_tk_default_complete, -//XXX .complete = ec_tk_many_complete, - .size = sizeof(struct ec_tk_many), - .free_priv = ec_tk_many_free_priv, -}; - -EC_TK_TYPE_REGISTER(ec_tk_many_type); - -struct ec_tk *ec_tk_many(const char *id, struct ec_tk *child, - unsigned int min, unsigned int max) -{ - struct ec_tk_many *tk = NULL; - - if (child == NULL) - return NULL; - - tk = (struct ec_tk_many *)__ec_tk_new(&ec_tk_many_type, id); - if (tk == NULL) { - ec_tk_free(child); - return NULL; - } - - tk->child = child; - tk->min = min; - tk->max = max; - - return &tk->gen; -} - -static int ec_tk_many_testcase(void) -{ - struct ec_tk *tk; - int ret = 0; - - tk = ec_tk_many(NULL, ec_tk_str(NULL, "foo"), 0, 0); - if (tk == NULL) { - ec_log(EC_LOG_ERR, "cannot create tk\n"); - return -1; - } - ret |= EC_TEST_CHECK_TK_PARSE(tk, 0, "bar"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo", "bar"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 2, "foo", "foo", "bar"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 0); - ec_tk_free(tk); - - tk = ec_tk_many(NULL, ec_tk_str(NULL, "foo"), 1, 0); - if (tk == NULL) { - ec_log(EC_LOG_ERR, "cannot create tk\n"); - return -1; - } - ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "bar"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo", "bar"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 2, "foo", "foo", "bar"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, -1); - ec_tk_free(tk); - - tk = ec_tk_many(NULL, ec_tk_str(NULL, "foo"), 1, 2); - if (tk == NULL) { - ec_log(EC_LOG_ERR, "cannot create tk\n"); - return -1; - } - ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "bar"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo", "bar"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 2, "foo", "foo", "bar"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 2, "foo", "foo", "foo"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, -1); - ec_tk_free(tk); - - /* test completion */ - /* XXX */ - - return ret; -} - -static struct ec_test ec_tk_many_test = { - .name = "many", - .test = ec_tk_many_testcase, -}; - -EC_TEST_REGISTER(ec_tk_many_test); diff --git a/lib/ecoli_tk_many.h b/lib/ecoli_tk_many.h deleted file mode 100644 index 107e4c7..0000000 --- a/lib/ecoli_tk_many.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2016, Olivier MATZ - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the University of California, Berkeley nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef ECOLI_TK_MANY_ -#define ECOLI_TK_MANY_ - -/* - * if min == max == 0, there is no limit - */ -struct ec_tk *ec_tk_many(const char *id, struct ec_tk *child, - unsigned int min, unsigned int max); - -#endif diff --git a/lib/ecoli_tk_option.c b/lib/ecoli_tk_option.c deleted file mode 100644 index 080ca37..0000000 --- a/lib/ecoli_tk_option.c +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright (c) 2016, Olivier MATZ - * - * 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 -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -struct ec_tk_option { - struct ec_tk gen; - struct ec_tk *child; -}; - -static struct ec_parsed_tk *ec_tk_option_parse(const struct ec_tk *gen_tk, - const struct ec_strvec *strvec) -{ - struct ec_tk_option *tk = (struct ec_tk_option *)gen_tk; - struct ec_parsed_tk *parsed_tk = NULL, *child_parsed_tk; - struct ec_strvec *match_strvec; - - parsed_tk = ec_parsed_tk_new(); - if (parsed_tk == NULL) - goto fail; - - child_parsed_tk = ec_tk_parse_tokens(tk->child, strvec); - if (child_parsed_tk == NULL) - goto fail; - - if (ec_parsed_tk_matches(child_parsed_tk)) { - ec_parsed_tk_add_child(parsed_tk, child_parsed_tk); - match_strvec = ec_strvec_dup(child_parsed_tk->strvec); - } else { - ec_parsed_tk_free(child_parsed_tk); - match_strvec = ec_strvec_new(); - } - - if (match_strvec == NULL) - goto fail; - - ec_parsed_tk_set_match(parsed_tk, gen_tk, match_strvec); - - return parsed_tk; - - fail: - ec_parsed_tk_free(parsed_tk); - return NULL; -} - -static struct ec_completed_tk *ec_tk_option_complete(const struct ec_tk *gen_tk, - const struct ec_strvec *strvec) -{ - struct ec_tk_option *tk = (struct ec_tk_option *)gen_tk; - - return ec_tk_complete_tokens(tk->child, strvec); -} - -static void ec_tk_option_free_priv(struct ec_tk *gen_tk) -{ - struct ec_tk_option *tk = (struct ec_tk_option *)gen_tk; - - ec_tk_free(tk->child); -} - -static struct ec_tk_type ec_tk_option_type = { - .name = "option", - .parse = ec_tk_option_parse, - .complete = ec_tk_option_complete, - .size = sizeof(struct ec_tk_option), - .free_priv = ec_tk_option_free_priv, -}; - -EC_TK_TYPE_REGISTER(ec_tk_option_type); - -struct ec_tk *ec_tk_option_new(const char *id, struct ec_tk *child) -{ - struct ec_tk *gen_tk = NULL; - struct ec_tk_option *tk = NULL; - - if (child == NULL) - return NULL; - - gen_tk = __ec_tk_new(&ec_tk_option_type, id); - if (gen_tk == NULL) { - ec_tk_free(child); - return NULL; - } - tk = (struct ec_tk_option *)gen_tk; - - tk->child = child; - - child->parent = gen_tk; - TAILQ_INSERT_TAIL(&gen_tk->children, child, next); - - return &tk->gen; -} - -static int ec_tk_option_testcase(void) -{ - struct ec_tk *tk; - int ret = 0; - - tk = ec_tk_option_new(NULL, ec_tk_str(NULL, "foo")); - if (tk == NULL) { - ec_log(EC_LOG_ERR, "cannot create tk\n"); - return -1; - } - ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo", "bar"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 0, "bar"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 0); - ec_tk_free(tk); - - /* test completion */ - tk = ec_tk_option_new(NULL, ec_tk_str(NULL, "foo")); - if (tk == NULL) { - ec_log(EC_LOG_ERR, "cannot create tk\n"); - return -1; - } - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "", EC_TK_ENDLIST, - "foo", EC_TK_ENDLIST, - "foo"); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "f", EC_TK_ENDLIST, - "oo", EC_TK_ENDLIST, - "oo"); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "b", EC_TK_ENDLIST, - EC_TK_ENDLIST, - ""); - ec_tk_free(tk); - - return ret; -} - -static struct ec_test ec_tk_option_test = { - .name = "tk_option", - .test = ec_tk_option_testcase, -}; - -EC_TEST_REGISTER(ec_tk_option_test); diff --git a/lib/ecoli_tk_option.h b/lib/ecoli_tk_option.h deleted file mode 100644 index 338749d..0000000 --- a/lib/ecoli_tk_option.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2016, Olivier MATZ - * - * 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 - -struct ec_tk *ec_tk_option_new(const char *id, struct ec_tk *tk); - -#endif diff --git a/lib/ecoli_tk_or.c b/lib/ecoli_tk_or.c deleted file mode 100644 index 0debd9c..0000000 --- a/lib/ecoli_tk_or.c +++ /dev/null @@ -1,273 +0,0 @@ -/* - * Copyright (c) 2016, Olivier MATZ - * - * 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 -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -struct ec_tk_or { - struct ec_tk gen; - struct ec_tk **table; - unsigned int len; -}; - -static struct ec_parsed_tk *ec_tk_or_parse(const struct ec_tk *gen_tk, - const struct ec_strvec *strvec) -{ - struct ec_tk_or *tk = (struct ec_tk_or *)gen_tk; - struct ec_parsed_tk *parsed_tk, *child_parsed_tk = NULL; - struct ec_strvec *match_strvec; - unsigned int i; - - parsed_tk = ec_parsed_tk_new(); - if (parsed_tk == NULL) - goto fail; - - for (i = 0; i < tk->len; i++) { - child_parsed_tk = ec_tk_parse_tokens(tk->table[i], strvec); - if (child_parsed_tk == NULL) - goto fail; - if (ec_parsed_tk_matches(child_parsed_tk)) - break; - ec_parsed_tk_free(child_parsed_tk); - child_parsed_tk = NULL; - } - - /* no match */ - if (i == tk->len) - return parsed_tk; - - match_strvec = ec_strvec_dup(child_parsed_tk->strvec); - if (match_strvec == NULL) - goto fail; - - ec_parsed_tk_set_match(parsed_tk, gen_tk, match_strvec); - ec_parsed_tk_add_child(parsed_tk, child_parsed_tk); - - return parsed_tk; - - fail: - ec_parsed_tk_free(child_parsed_tk); - ec_parsed_tk_free(parsed_tk); - return NULL; -} - -static struct ec_completed_tk *ec_tk_or_complete(const struct ec_tk *gen_tk, - const struct ec_strvec *strvec) -{ - struct ec_tk_or *tk = (struct ec_tk_or *)gen_tk; - struct ec_completed_tk *completed_tk, *child_completed_tk; - size_t n; - - completed_tk = ec_completed_tk_new(); - if (completed_tk == NULL) - return NULL; - - for (n = 0; n < tk->len; n++) { - child_completed_tk = ec_tk_complete_tokens(tk->table[n], - strvec); - - if (child_completed_tk == NULL) // XXX fail instead? - continue; - - ec_completed_tk_merge(completed_tk, child_completed_tk); - } - - return completed_tk; -} - -static void ec_tk_or_free_priv(struct ec_tk *gen_tk) -{ - struct ec_tk_or *tk = (struct ec_tk_or *)gen_tk; - unsigned int i; - - for (i = 0; i < tk->len; i++) - ec_tk_free(tk->table[i]); - ec_free(tk->table); -} - -int ec_tk_or_add(struct ec_tk *gen_tk, struct ec_tk *child) -{ - struct ec_tk_or *tk = (struct ec_tk_or *)gen_tk; - struct ec_tk **table; - - assert(tk != NULL); - - if (child == NULL) - return -EINVAL; - - gen_tk->flags &= ~EC_TK_F_BUILT; - - table = ec_realloc(tk->table, (tk->len + 1) * sizeof(*tk->table)); - if (table == NULL) { - ec_tk_free(child); - return -1; - } - - tk->table = table; - table[tk->len] = child; - tk->len++; - - child->parent = gen_tk; - TAILQ_INSERT_TAIL(&gen_tk->children, child, next); - - return 0; -} - -static struct ec_tk_type ec_tk_or_type = { - .name = "or", - .parse = ec_tk_or_parse, - .complete = ec_tk_or_complete, - .size = sizeof(struct ec_tk_or), - .free_priv = ec_tk_or_free_priv, -}; - -EC_TK_TYPE_REGISTER(ec_tk_or_type); - -struct ec_tk *__ec_tk_or(const char *id, ...) -{ - struct ec_tk *gen_tk = NULL; - struct ec_tk_or *tk = NULL; - struct ec_tk *child; - va_list ap; - int fail = 0; - - va_start(ap, id); - - gen_tk = __ec_tk_new(&ec_tk_or_type, id); - tk = (struct ec_tk_or *)gen_tk; - if (tk == NULL) - fail = 1;; - - for (child = va_arg(ap, struct ec_tk *); - child != EC_TK_ENDLIST; - child = va_arg(ap, struct ec_tk *)) { - - /* on error, don't quit the loop to avoid leaks */ - if (fail == 1 || child == NULL || - ec_tk_or_add(gen_tk, child) < 0) { - fail = 1; - ec_tk_free(child); - } - } - - if (fail == 1) - goto fail; - - va_end(ap); - return gen_tk; - -fail: - ec_tk_free(gen_tk); /* will also free children */ - va_end(ap); - return NULL; -} - -static int ec_tk_or_testcase(void) -{ - struct ec_tk *tk; - int ret = 0; - - tk = EC_TK_OR(NULL, - ec_tk_str(NULL, "foo"), - ec_tk_str(NULL, "bar") - ); - if (tk == NULL) { - ec_log(EC_LOG_ERR, "cannot create tk\n"); - return -1; - } - ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "bar"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo", "bar"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, " "); - ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "foox"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "toto"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, ""); - ec_tk_free(tk); - - /* test completion */ - tk = EC_TK_OR(NULL, - ec_tk_str(NULL, "foo"), - ec_tk_str(NULL, "bar"), - ec_tk_str(NULL, "bar2"), - ec_tk_str(NULL, "toto"), - ec_tk_str(NULL, "titi") - ); - if (tk == NULL) { - ec_log(EC_LOG_ERR, "cannot create tk\n"); - return -1; - } - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "", EC_TK_ENDLIST, - "foo", "bar", "bar2", "toto", "titi", EC_TK_ENDLIST, - ""); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "f", EC_TK_ENDLIST, - "oo", EC_TK_ENDLIST, - "oo"); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "b", EC_TK_ENDLIST, - "ar", "ar2", EC_TK_ENDLIST, - "ar"); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "bar", EC_TK_ENDLIST, - "", "2", EC_TK_ENDLIST, - ""); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "t", EC_TK_ENDLIST, - "oto", "iti", EC_TK_ENDLIST, - ""); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "to", EC_TK_ENDLIST, - "to", EC_TK_ENDLIST, - "to"); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "x", EC_TK_ENDLIST, - EC_TK_ENDLIST, - ""); - ec_tk_free(tk); - - return ret; -} - -static struct ec_test ec_tk_or_test = { - .name = "tk_or", - .test = ec_tk_or_testcase, -}; - -EC_TEST_REGISTER(ec_tk_or_test); diff --git a/lib/ecoli_tk_or.h b/lib/ecoli_tk_or.h deleted file mode 100644 index c34b808..0000000 --- a/lib/ecoli_tk_or.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2016, Olivier MATZ - * - * 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 - -#define EC_TK_OR(args...) __ec_tk_or(args, EC_TK_ENDLIST) - -/* list must be terminated with EC_TK_ENDLIST */ -/* all token given in the list will be freed when freeing this one */ -/* avoid using this function directly, prefer the macro EC_TK_OR() or - * ec_tk_or() + ec_tk_or_add() */ -struct ec_tk *__ec_tk_or(const char *id, ...); - -struct ec_tk *ec_tk_or(const char *id); - -/* child is consumed */ -int ec_tk_or_add(struct ec_tk *tk, struct ec_tk *child); - - -#endif diff --git a/lib/ecoli_tk_re.c b/lib/ecoli_tk_re.c deleted file mode 100644 index fc5c184..0000000 --- a/lib/ecoli_tk_re.c +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (c) 2016, Olivier MATZ - * - * 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 -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -struct ec_tk_re { - struct ec_tk gen; - char *re_str; - regex_t re; -}; - -static struct ec_parsed_tk *ec_tk_re_parse(const struct ec_tk *gen_tk, - const struct ec_strvec *strvec) -{ - struct ec_tk_re *tk = (struct ec_tk_re *)gen_tk; - struct ec_strvec *match_strvec; - struct ec_parsed_tk *parsed_tk = NULL; - const char *str; - regmatch_t pos; - - parsed_tk = ec_parsed_tk_new(); - if (parsed_tk == NULL) - goto fail; - - if (ec_strvec_len(strvec) == 0) - return parsed_tk; - - str = ec_strvec_val(strvec, 0); - if (regexec(&tk->re, str, 1, &pos, 0) != 0) - return parsed_tk; - if (pos.rm_so != 0 || pos.rm_eo != (int)strlen(str)) - return parsed_tk; - - match_strvec = ec_strvec_ndup(strvec, 0, 1); - if (match_strvec == NULL) - goto fail; - - ec_parsed_tk_set_match(parsed_tk, gen_tk, match_strvec); - - return parsed_tk; - - fail: - ec_parsed_tk_free(parsed_tk); - return NULL; -} - -static void ec_tk_re_free_priv(struct ec_tk *gen_tk) -{ - struct ec_tk_re *tk = (struct ec_tk_re *)gen_tk; - - ec_free(tk->re_str); - regfree(&tk->re); -} - -static struct ec_tk_type ec_tk_re_type = { - .name = "re", - .parse = ec_tk_re_parse, - .complete = ec_tk_default_complete, - .size = sizeof(struct ec_tk_re), - .free_priv = ec_tk_re_free_priv, -}; - -EC_TK_TYPE_REGISTER(ec_tk_re_type); - -int ec_tk_re_set_regexp(struct ec_tk *gen_tk, const char *str) -{ - struct ec_tk_re *tk = (struct ec_tk_re *)gen_tk; - int ret; - - if (str == NULL) - return -EINVAL; - if (tk->re_str != NULL) // XXX allow to replace - return -EEXIST; - - ret = -ENOMEM; - tk->re_str = ec_strdup(str); - if (tk->re_str == NULL) - goto fail; - - ret = -EINVAL; - if (regcomp(&tk->re, tk->re_str, REG_EXTENDED) != 0) - goto fail; - - return 0; - -fail: - ec_free(tk->re_str); - return ret; -} - -struct ec_tk *ec_tk_re(const char *id, const char *re_str) -{ - struct ec_tk *gen_tk = NULL; - - gen_tk = __ec_tk_new(&ec_tk_re_type, id); - if (gen_tk == NULL) - goto fail; - - if (ec_tk_re_set_regexp(gen_tk, re_str) < 0) - goto fail; - - return gen_tk; - -fail: - ec_tk_free(gen_tk); - return NULL; -} - -static int ec_tk_re_testcase(void) -{ - struct ec_tk *tk; - int ret = 0; - - tk = ec_tk_re(NULL, "fo+|bar"); - if (tk == NULL) { - ec_log(EC_LOG_ERR, "cannot create tk\n"); - return -1; - } - ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo", "bar"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "bar"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "foobar"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, " foo"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, ""); - ec_tk_free(tk); - - return ret; -} - -static struct ec_test ec_tk_re_test = { - .name = "tk_re", - .test = ec_tk_re_testcase, -}; - -EC_TEST_REGISTER(ec_tk_re_test); diff --git a/lib/ecoli_tk_re.h b/lib/ecoli_tk_re.h deleted file mode 100644 index dfd4c1a..0000000 --- a/lib/ecoli_tk_re.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2016, Olivier MATZ - * - * 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 - -struct ec_tk *ec_tk_re(const char *id, const char *str); - -struct ec_tk *ec_tk_re_new(const char *id); - -/* re is duplicated */ -int ec_tk_re_set_regexp(struct ec_tk *tk, const char *re); - -#endif diff --git a/lib/ecoli_tk_re_lex.c b/lib/ecoli_tk_re_lex.c deleted file mode 100644 index 984ec3f..0000000 --- a/lib/ecoli_tk_re_lex.c +++ /dev/null @@ -1,273 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct regexp_pattern { - char *pattern; - regex_t r; - bool keep; -}; - -struct ec_tk_re_lex { - struct ec_tk gen; - struct ec_tk *child; - struct regexp_pattern *table; - size_t len; -}; - -static struct ec_strvec * -tokenize(struct regexp_pattern *table, size_t table_len, const char *str) -{ - struct ec_strvec *strvec = NULL; - char *dup = NULL; - char c; - size_t len, off = 0; - size_t i; - int ret; - regmatch_t pos; - - dup = ec_strdup(str); - if (dup == NULL) - goto fail; - - strvec = ec_strvec_new(); - if (strvec == NULL) - goto fail; - - len = strlen(dup); - while (off < len) { - for (i = 0; i < table_len; i++) { - ret = regexec(&table[i].r, &dup[off], 1, &pos, 0); - if (ret != 0) - continue; - if (pos.rm_so != 0 || pos.rm_eo == 0) { - ret = -1; - continue; - } - - if (table[i].keep == 0) - break; - - c = dup[pos.rm_eo + off]; - dup[pos.rm_eo + off] = '\0'; - ec_log(EC_LOG_DEBUG, "re_lex match <%s>\n", &dup[off]); - if (ec_strvec_add(strvec, &dup[off]) < 0) - goto fail; - - dup[pos.rm_eo + off] = c; - break; - } - - if (ret != 0) - goto fail; - - off += pos.rm_eo; - } - - ec_free(dup); - return strvec; - -fail: - ec_free(dup); - ec_strvec_free(strvec); - return NULL; -} - -static struct ec_parsed_tk *ec_tk_re_lex_parse(const struct ec_tk *gen_tk, - const struct ec_strvec *strvec) -{ - struct ec_tk_re_lex *tk = (struct ec_tk_re_lex *)gen_tk; - struct ec_strvec *new_vec = NULL, *match_strvec; - struct ec_parsed_tk *parsed_tk = NULL, *child_parsed_tk; - const char *str; - - parsed_tk = ec_parsed_tk_new(); - if (parsed_tk == NULL) - return NULL; - - if (ec_strvec_len(strvec) == 0) - return parsed_tk; - - str = ec_strvec_val(strvec, 0); - new_vec = tokenize(tk->table, tk->len, str); - if (new_vec == NULL) - goto fail; - - printf("--------\n"); - ec_strvec_dump(stdout, new_vec); - child_parsed_tk = ec_tk_parse_tokens(tk->child, new_vec); - if (child_parsed_tk == NULL) - goto fail; - - if (!ec_parsed_tk_matches(child_parsed_tk) || - ec_parsed_tk_len(child_parsed_tk) != - ec_strvec_len(new_vec)) { - ec_strvec_free(new_vec); - ec_parsed_tk_free(child_parsed_tk); - return parsed_tk; - } - ec_strvec_free(new_vec); - new_vec = NULL; - - ec_parsed_tk_add_child(parsed_tk, child_parsed_tk); - match_strvec = ec_strvec_ndup(strvec, 0, 1); - if (match_strvec == NULL) - goto fail; - ec_parsed_tk_set_match(parsed_tk, gen_tk, match_strvec); - - return parsed_tk; - - fail: - ec_strvec_free(new_vec); - ec_parsed_tk_free(parsed_tk); - - return NULL; -} - -static void ec_tk_re_lex_free_priv(struct ec_tk *gen_tk) -{ - struct ec_tk_re_lex *tk = (struct ec_tk_re_lex *)gen_tk; - unsigned int i; - - for (i = 0; i < tk->len; i++) { - ec_free(tk->table[i].pattern); - regfree(&tk->table[i].r); - } - - ec_free(tk->table); - ec_tk_free(tk->child); -} - -static struct ec_tk_type ec_tk_re_lex_type = { - .name = "re_lex", - .parse = ec_tk_re_lex_parse, - //.complete = ec_tk_re_lex_complete, //XXX - .size = sizeof(struct ec_tk_re_lex), - .free_priv = ec_tk_re_lex_free_priv, -}; - -EC_TK_TYPE_REGISTER(ec_tk_re_lex_type); - -int ec_tk_re_lex_add(struct ec_tk *gen_tk, const char *pattern, int keep) -{ - struct ec_tk_re_lex *tk = (struct ec_tk_re_lex *)gen_tk; - struct regexp_pattern *table; - int ret; - char *pat_dup = NULL; - - ret = -ENOMEM; - pat_dup = ec_strdup(pattern); - if (pat_dup == NULL) - goto fail; - - ret = -ENOMEM; - table = ec_realloc(tk->table, sizeof(*table) * (tk->len + 1)); - if (table == NULL) - goto fail; - - ret = regcomp(&table[tk->len].r, pattern, REG_EXTENDED); - if (ret != 0) { - ec_log(EC_LOG_ERR, - "Regular expression <%s> compilation failed: %d\n", - pattern, ret); - if (ret == REG_ESPACE) - ret = -ENOMEM; - else - ret = -EINVAL; - - goto fail; - } - - table[tk->len].pattern = pat_dup; - table[tk->len].keep = keep; - tk->len++; - tk->table = table; - - return 0; - -fail: - ec_free(pat_dup); - return ret; -} - -struct ec_tk *ec_tk_re_lex(const char *id, struct ec_tk *child) -{ - struct ec_tk_re_lex *tk = NULL; - - if (child == NULL) - return NULL; - - tk = (struct ec_tk_re_lex *)__ec_tk_new(&ec_tk_re_lex_type, id); - if (tk == NULL) { - ec_tk_free(child); - return NULL; - } - - tk->child = child; - - return &tk->gen; -} - - -static int ec_tk_re_lex_testcase(void) -{ - struct ec_tk *tk; - int ret = 0; - - tk = ec_tk_re_lex(NULL, - ec_tk_many(NULL, - EC_TK_OR(NULL, - ec_tk_str(NULL, "foo"), - ec_tk_str(NULL, "bar"), - ec_tk_int(NULL, 0, 1000, 0) - ), 0, 0 - ) - ); - if (tk == NULL) { - ec_log(EC_LOG_ERR, "cannot create tk\n"); - return -1; - } - - /* XXX add ^ automatically ? */ - ret |= ec_tk_re_lex_add(tk, "[a-zA-Z]+", 1); - ret |= ec_tk_re_lex_add(tk, "[0-9]+", 1); - ret |= ec_tk_re_lex_add(tk, "=", 1); - ret |= ec_tk_re_lex_add(tk, "-", 1); - ret |= ec_tk_re_lex_add(tk, "\\+", 1); - ret |= ec_tk_re_lex_add(tk, "[ ]+", 0); - if (ret != 0) { - ec_log(EC_LOG_ERR, "cannot add regexp to token\n"); - ec_tk_free(tk); - return -1; - } - - ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, " foo bar 324 bar234"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo bar324"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, ""); - ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "foobar"); - - ec_tk_free(tk); - - return ret; -} - -static struct ec_test ec_tk_re_lex_test = { - .name = "tk_re_lex", - .test = ec_tk_re_lex_testcase, -}; - -EC_TEST_REGISTER(ec_tk_re_lex_test); diff --git a/lib/ecoli_tk_re_lex.h b/lib/ecoli_tk_re_lex.h deleted file mode 100644 index b90ab1b..0000000 --- a/lib/ecoli_tk_re_lex.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2016-2017, Olivier MATZ - * - * 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 - -struct ec_tk *ec_tk_re_lex(const char *id, struct ec_tk *child); - -int ec_tk_re_lex_add(struct ec_tk *gen_tk, const char *pattern, int keep); - -#endif diff --git a/lib/ecoli_tk_seq.c b/lib/ecoli_tk_seq.c deleted file mode 100644 index 17673d6..0000000 --- a/lib/ecoli_tk_seq.c +++ /dev/null @@ -1,323 +0,0 @@ -/* - * Copyright (c) 2016, Olivier MATZ - * - * 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 -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -struct ec_tk_seq { - struct ec_tk gen; - struct ec_tk **table; - unsigned int len; -}; - -static struct ec_parsed_tk *ec_tk_seq_parse(const struct ec_tk *gen_tk, - const struct ec_strvec *strvec) -{ - struct ec_tk_seq *tk = (struct ec_tk_seq *)gen_tk; - struct ec_parsed_tk *parsed_tk, *child_parsed_tk; - struct ec_strvec *match_strvec; - struct ec_strvec *childvec = NULL; - size_t len = 0; - unsigned int i; - - parsed_tk = ec_parsed_tk_new(); - if (parsed_tk == NULL) - goto fail; - - for (i = 0; i < tk->len; i++) { - childvec = ec_strvec_ndup(strvec, len, - ec_strvec_len(strvec) - len); - if (childvec == NULL) - goto fail; - - child_parsed_tk = ec_tk_parse_tokens(tk->table[i], childvec); - if (child_parsed_tk == NULL) - goto fail; - - ec_strvec_free(childvec); - childvec = NULL; - - if (!ec_parsed_tk_matches(child_parsed_tk)) { - ec_parsed_tk_free(child_parsed_tk); - // XXX ec_parsed_tk_free_children needed? see subset.c - ec_parsed_tk_free_children(parsed_tk); - return parsed_tk; - } - - ec_parsed_tk_add_child(parsed_tk, child_parsed_tk); - len += ec_parsed_tk_len(child_parsed_tk); - } - - match_strvec = ec_strvec_ndup(strvec, 0, len); - if (match_strvec == NULL) - goto fail; - - ec_parsed_tk_set_match(parsed_tk, gen_tk, match_strvec); - - return parsed_tk; - -fail: - ec_strvec_free(childvec); - ec_parsed_tk_free(parsed_tk); - return NULL; -} - -static struct ec_completed_tk *ec_tk_seq_complete(const struct ec_tk *gen_tk, - const struct ec_strvec *strvec) -{ - struct ec_tk_seq *tk = (struct ec_tk_seq *)gen_tk; - struct ec_completed_tk *completed_tk, *child_completed_tk; - struct ec_strvec *childvec = NULL; - struct ec_parsed_tk *parsed_tk; - size_t len = 0; - unsigned int i; - - completed_tk = ec_completed_tk_new(); - if (completed_tk == NULL) - return NULL; - - if (tk->len == 0) - return completed_tk; - - for (i = 0; i < tk->len && len < ec_strvec_len(strvec); i++) { - childvec = ec_strvec_ndup(strvec, len, - ec_strvec_len(strvec) - len); - if (childvec == NULL) - goto fail; - - child_completed_tk = ec_tk_complete_tokens(tk->table[i], - childvec); - if (child_completed_tk == NULL) - goto fail; - - ec_completed_tk_merge(completed_tk, child_completed_tk); - - parsed_tk = ec_tk_parse_tokens(tk->table[i], childvec); - if (parsed_tk == NULL) - goto fail; - - ec_strvec_free(childvec); - childvec = NULL; - - if (!ec_parsed_tk_matches(parsed_tk)) { - ec_parsed_tk_free(parsed_tk); - break; - } - - len += ec_strvec_len(parsed_tk->strvec); - ec_parsed_tk_free(parsed_tk); - } - - return completed_tk; - -fail: - ec_strvec_free(childvec); - ec_completed_tk_free(completed_tk); - return NULL; -} - -static void ec_tk_seq_free_priv(struct ec_tk *gen_tk) -{ - struct ec_tk_seq *tk = (struct ec_tk_seq *)gen_tk; - unsigned int i; - - for (i = 0; i < tk->len; i++) - ec_tk_free(tk->table[i]); - ec_free(tk->table); -} - -static struct ec_tk_type ec_tk_seq_type = { - .name = "seq", - .parse = ec_tk_seq_parse, - .complete = ec_tk_seq_complete, - .size = sizeof(struct ec_tk_seq), - .free_priv = ec_tk_seq_free_priv, -}; - -EC_TK_TYPE_REGISTER(ec_tk_seq_type); - -int ec_tk_seq_add(struct ec_tk *gen_tk, struct ec_tk *child) -{ - struct ec_tk_seq *tk = (struct ec_tk_seq *)gen_tk; - struct ec_tk **table; - - // XXX check tk type - - assert(tk != NULL); - - if (child == NULL) - return -EINVAL; - - gen_tk->flags &= ~EC_TK_F_BUILT; - - table = ec_realloc(tk->table, (tk->len + 1) * sizeof(*tk->table)); - if (table == NULL) { - ec_tk_free(child); - return -1; - } - - tk->table = table; - table[tk->len] = child; - tk->len++; - - child->parent = gen_tk; - TAILQ_INSERT_TAIL(&gen_tk->children, child, next); // XXX really needed? - - return 0; -} - -struct ec_tk *__ec_tk_seq(const char *id, ...) -{ - struct ec_tk *gen_tk = NULL; - struct ec_tk_seq *tk = NULL; - struct ec_tk *child; - va_list ap; - int fail = 0; - - va_start(ap, id); - - gen_tk = __ec_tk_new(&ec_tk_seq_type, id); - tk = (struct ec_tk_seq *)gen_tk; - if (tk == NULL) - fail = 1;; - - for (child = va_arg(ap, struct ec_tk *); - child != EC_TK_ENDLIST; - child = va_arg(ap, struct ec_tk *)) { - - /* on error, don't quit the loop to avoid leaks */ - if (fail == 1 || child == NULL || - ec_tk_seq_add(&tk->gen, child) < 0) { - fail = 1; - ec_tk_free(child); - } - } - - if (fail == 1) - goto fail; - - va_end(ap); - return gen_tk; - -fail: - ec_tk_free(gen_tk); /* will also free children */ - va_end(ap); - return NULL; -} - -static int ec_tk_seq_testcase(void) -{ - struct ec_tk *tk; - int ret = 0; - - tk = EC_TK_SEQ(NULL, - ec_tk_str(NULL, "foo"), - ec_tk_str(NULL, "bar") - ); - if (tk == NULL) { - ec_log(EC_LOG_ERR, "cannot create tk\n"); - return -1; - } - ret |= EC_TEST_CHECK_TK_PARSE(tk, 2, "foo", "bar"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 2, "foo", "bar", "toto"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "foo"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "foox", "bar"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "foo", "barx"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "bar", "foo"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "", "foo"); - ec_tk_free(tk); - - /* test completion */ - tk = EC_TK_SEQ(NULL, - ec_tk_str(NULL, "foo"), - ec_tk_option_new(NULL, ec_tk_str(NULL, "toto")), - ec_tk_str(NULL, "bar") - ); - if (tk == NULL) { - ec_log(EC_LOG_ERR, "cannot create tk\n"); - return -1; - } - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "", EC_TK_ENDLIST, - "foo", EC_TK_ENDLIST, - "foo"); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "f", EC_TK_ENDLIST, - "oo", EC_TK_ENDLIST, - "oo"); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "foo", EC_TK_ENDLIST, - "", EC_TK_ENDLIST, - ""); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "foo", "", EC_TK_ENDLIST, - "bar", "toto", EC_TK_ENDLIST, - ""); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "foo", "t", EC_TK_ENDLIST, - "oto", EC_TK_ENDLIST, - "oto"); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "foo", "b", EC_TK_ENDLIST, - "ar", EC_TK_ENDLIST, - "ar"); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "foo", "bar", EC_TK_ENDLIST, - "", EC_TK_ENDLIST, - ""); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "x", EC_TK_ENDLIST, - EC_TK_ENDLIST, - ""); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "foobarx", EC_TK_ENDLIST, - EC_TK_ENDLIST, - ""); - ec_tk_free(tk); - - return ret; -} - -static struct ec_test ec_tk_seq_test = { - .name = "tk_seq", - .test = ec_tk_seq_testcase, -}; - -EC_TEST_REGISTER(ec_tk_seq_test); diff --git a/lib/ecoli_tk_seq.h b/lib/ecoli_tk_seq.h deleted file mode 100644 index a401ed2..0000000 --- a/lib/ecoli_tk_seq.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2016, Olivier MATZ - * - * 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 - -#define EC_TK_SEQ(args...) __ec_tk_seq(args, EC_TK_ENDLIST) - -/* list must be terminated with EC_TK_ENDLIST */ -/* all token given in the list will be freed when freeing this one */ -/* avoid using this function directly, prefer the macro EC_TK_SEQ() or - * ec_tk_seq() + ec_tk_seq_add() */ -struct ec_tk *__ec_tk_seq(const char *id, ...); - -struct ec_tk *ec_tk_seq(const char *id); - -/* child is consumed */ -// XXX add_child? -int ec_tk_seq_add(struct ec_tk *tk, struct ec_tk *child); - -#endif diff --git a/lib/ecoli_tk_sh_lex.c b/lib/ecoli_tk_sh_lex.c deleted file mode 100644 index 204ed79..0000000 --- a/lib/ecoli_tk_sh_lex.c +++ /dev/null @@ -1,499 +0,0 @@ -/* - * Copyright (c) 2016, Olivier MATZ - * - * 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 -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct ec_tk_sh_lex { - struct ec_tk gen; - struct ec_tk *child; -}; - -static size_t eat_spaces(const char *str) -{ - size_t i = 0; - - /* skip spaces */ - while (isblank(str[i])) - i++; - - return i; -} - -/* - * Allocate a new string which is a copy of the input string with quotes - * removed. If quotes are not closed properly, set missing_quote to the - * missing quote char. - */ -static char *unquote_str(const char *str, size_t n, int allow_missing_quote, - char *missing_quote) -{ - unsigned s = 1, d = 0; - char quote = str[0]; - char *dst; - int closed = 0; - - dst = ec_malloc(n); - if (dst == NULL) { - errno = ENOMEM; - return NULL; - } - - /* copy token and remove quotes */ - while (s < n && d < n && str[s] != '\0') { - if (str[s] == '\\' && str[s+1] == quote) { - dst[d++] = quote; - s += 2; - continue; - } - if (str[s] == '\\' && str[s+1] == '\\') { - dst[d++] = '\\'; - s += 2; - continue; - } - if (str[s] == quote) { - s++; - closed = 1; - break; - } - dst[d++] = str[s++]; - } - - /* not enough room in dst buffer (should not happen) */ - if (d >= n) { - ec_free(dst); - errno = EMSGSIZE; - return NULL; - } - - /* quote not closed */ - if (closed == 0) { - if (missing_quote != NULL) - *missing_quote = str[0]; - if (allow_missing_quote == 0) { - ec_free(dst); - errno = EINVAL; - return NULL; - } - } - dst[d++] = '\0'; - - return dst; -} - -static size_t eat_quoted_str(const char *str) -{ - size_t i = 0; - char quote = str[0]; - - while (str[i] != '\0') { - if (str[i] != '\\' && str[i+1] == quote) - return i + 2; - i++; - } - - /* unclosed quote, will be detected later */ - return i; -} - -static size_t eat_str(const char *str) -{ - size_t i = 0; - - /* skip spaces */ - while (!isblank(str[i]) && str[i] != '\0') - i++; - - return i; -} - -static struct ec_strvec *tokenize(const char *str, int completion, - int allow_missing_quote, char *missing_quote) -{ - struct ec_strvec *strvec = NULL; - size_t off = 0, len, suboff, sublen; - char *word = NULL, *concat = NULL, *tmp; - int last_is_space = 1; - -// printf("str=%s\n", str); - - strvec = ec_strvec_new(); - if (strvec == NULL) - goto fail; - - while (str[off] != '\0') { - len = eat_spaces(&str[off]); - if (len > 0) - last_is_space = 1; -// printf("space=%zd\n", len); - off += len; - - len = 0; - suboff = off; - while (str[suboff] != '\0') { - last_is_space = 0; - if (str[suboff] == '"' || str[suboff] == '\'') { - sublen = eat_quoted_str(&str[suboff]); -// printf("sublen=%zd\n", sublen); - word = unquote_str(&str[suboff], sublen, - allow_missing_quote, missing_quote); - } else { - sublen = eat_str(&str[suboff]); -// printf("sublen=%zd\n", sublen); - if (sublen == 0) - break; - word = ec_strndup(&str[suboff], sublen); - } - - if (word == NULL) - goto fail; -// printf("word=%s\n", word); - - len += sublen; - suboff += sublen; - - if (concat == NULL) { - concat = word; - word = NULL; - } else { - tmp = ec_realloc(concat, len + 1); - if (tmp == NULL) - goto fail; - concat = tmp; - strcat(concat, word); - ec_free(word); - word = NULL; - } - } - - if (concat != NULL) { - if (ec_strvec_add(strvec, concat) < 0) - goto fail; - ec_free(concat); - concat = NULL; - } - - /* XXX remove all printf comments */ -// printf("str off=%zd len=%zd\n", off, len); - off += len; - } - - /* in completion mode, append an empty token if the string ends - * with space */ - if (completion && last_is_space) { - if (ec_strvec_add(strvec, "") < 0) - goto fail; - } - - return strvec; - - fail: - ec_free(word); - ec_free(concat); - ec_strvec_free(strvec); - return NULL; -} - -static struct ec_parsed_tk *ec_tk_sh_lex_parse(const struct ec_tk *gen_tk, - const struct ec_strvec *strvec) -{ - struct ec_tk_sh_lex *tk = (struct ec_tk_sh_lex *)gen_tk; - struct ec_strvec *new_vec = NULL, *match_strvec; - struct ec_parsed_tk *parsed_tk = NULL, *child_parsed_tk; - const char *str; - - parsed_tk = ec_parsed_tk_new(); - if (parsed_tk == NULL) - return NULL; - - if (ec_strvec_len(strvec) == 0) - return parsed_tk; - - str = ec_strvec_val(strvec, 0); - new_vec = tokenize(str, 0, 0, NULL); - if (new_vec == NULL) - goto fail; - - child_parsed_tk = ec_tk_parse_tokens(tk->child, new_vec); - if (child_parsed_tk == NULL) - goto fail; - - if (!ec_parsed_tk_matches(child_parsed_tk) || - ec_parsed_tk_len(child_parsed_tk) != - ec_strvec_len(new_vec)) { - ec_strvec_free(new_vec); - ec_parsed_tk_free(child_parsed_tk); - return parsed_tk; - } - ec_strvec_free(new_vec); - new_vec = NULL; - - ec_parsed_tk_add_child(parsed_tk, child_parsed_tk); - match_strvec = ec_strvec_ndup(strvec, 0, 1); - if (match_strvec == NULL) - goto fail; - ec_parsed_tk_set_match(parsed_tk, gen_tk, match_strvec); - - return parsed_tk; - - fail: - ec_strvec_free(new_vec); - ec_parsed_tk_free(parsed_tk); - - return NULL; -} - -static struct ec_completed_tk *ec_tk_sh_lex_complete(const struct ec_tk *gen_tk, - const struct ec_strvec *strvec) -{ - struct ec_tk_sh_lex *tk = (struct ec_tk_sh_lex *)gen_tk; - struct ec_completed_tk *completed_tk, *child_completed_tk = NULL; - struct ec_strvec *new_vec = NULL; - const char *str; - char missing_quote; - -// printf("==================\n"); - completed_tk = ec_completed_tk_new(); - if (completed_tk == NULL) - return NULL; - - if (ec_strvec_len(strvec) != 1) - return completed_tk; - - str = ec_strvec_val(strvec, 0); - new_vec = tokenize(str, 1, 1, &missing_quote); - if (new_vec == NULL) - goto fail; - -// ec_strvec_dump(new_vec, stdout); - - child_completed_tk = ec_tk_complete_tokens(tk->child, new_vec); - if (child_completed_tk == NULL) - goto fail; - - ec_strvec_free(new_vec); - new_vec = NULL; - ec_completed_tk_merge(completed_tk, child_completed_tk); - - return completed_tk; - - -#if 0 - for (i = 0, t = &tokens[0]; i < tk->len; i++, t++) { - if (*(t + 1) != NULL) { - child_parsed_tk = ec_tk_parse(tk->table[i], *t); - if (child_parsed_tk == NULL) - goto fail; - - if (strlen(child_parsed_tk->str) == 0) - t--; - else if (strlen(child_parsed_tk->str) != strlen(*t)) { - ec_parsed_tk_free(child_parsed_tk); - goto fail; - } - - ec_parsed_tk_free(child_parsed_tk); - } else { - child_completed_tk = ec_tk_complete(tk->table[i], *t); - if (child_completed_tk == NULL) { - ec_completed_tk_free(completed_tk); - return NULL; - } - ec_completed_tk_merge(completed_tk, child_completed_tk); - - child_parsed_tk = ec_tk_parse(tk->table[i], ""); - if (child_parsed_tk == NULL) - break; - ec_parsed_tk_free(child_parsed_tk); - t--; - } - } - - if (tokens != NULL) { - for (t = &tokens[0]; *t != NULL; t++) - ec_free(*t); - ec_free(tokens); - tokens = NULL; - } - - ec_completed_tk_dump(stdout, completed_tk); -#endif - - fail: - ec_strvec_free(new_vec); - ec_completed_tk_free(completed_tk); - return NULL; -} - -static void ec_tk_sh_lex_free_priv(struct ec_tk *gen_tk) -{ - struct ec_tk_sh_lex *tk = (struct ec_tk_sh_lex *)gen_tk; - - ec_tk_free(tk->child); -} - -static struct ec_tk_type ec_tk_sh_lex_type = { - .name = "sh_lex", - .parse = ec_tk_sh_lex_parse, - .complete = ec_tk_sh_lex_complete, - .size = sizeof(struct ec_tk_sh_lex), - .free_priv = ec_tk_sh_lex_free_priv, -}; - -EC_TK_TYPE_REGISTER(ec_tk_sh_lex_type); - -struct ec_tk *ec_tk_sh_lex_new(const char *id, struct ec_tk *child) -{ - struct ec_tk_sh_lex *tk = NULL; - - if (child == NULL) - return NULL; - - tk = (struct ec_tk_sh_lex *)__ec_tk_new(&ec_tk_sh_lex_type, id); - if (tk == NULL) { - ec_tk_free(child); - return NULL; - } - - tk->child = child; - - return &tk->gen; -} - -static int ec_tk_sh_lex_testcase(void) -{ - struct ec_tk *tk; - int ret = 0; - - tk = ec_tk_sh_lex_new(NULL, - EC_TK_SEQ(NULL, - ec_tk_str(NULL, "foo"), - ec_tk_option_new(NULL, - ec_tk_str(NULL, "toto") - ), - ec_tk_str(NULL, "bar") - ) - ); - if (tk == NULL) { - ec_log(EC_LOG_ERR, "cannot create tk\n"); - return -1; - } - ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo bar"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, " foo bar"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, " 'foo' \"bar\""); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, " 'f'oo 'toto' bar"); - ec_tk_free(tk); - - /* test completion */ - tk = ec_tk_sh_lex_new(NULL, - EC_TK_SEQ(NULL, - ec_tk_str(NULL, "foo"), - ec_tk_option_new(NULL, - ec_tk_str(NULL, "toto") - ), - ec_tk_str(NULL, "bar"), - ec_tk_str(NULL, "titi") - ) - ); - if (tk == NULL) { - ec_log(EC_LOG_ERR, "cannot create tk\n"); - return -1; - } - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "", EC_TK_ENDLIST, - "foo", EC_TK_ENDLIST, - "foo"); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - " ", EC_TK_ENDLIST, - "foo", EC_TK_ENDLIST, - "foo"); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "f", EC_TK_ENDLIST, - "oo", EC_TK_ENDLIST, - "oo"); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "foo", EC_TK_ENDLIST, - "", EC_TK_ENDLIST, - ""); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "foo ", EC_TK_ENDLIST, - "bar", "toto", EC_TK_ENDLIST, - ""); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "foo t", EC_TK_ENDLIST, - "oto", EC_TK_ENDLIST, - "oto"); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "foo b", EC_TK_ENDLIST, - "ar", EC_TK_ENDLIST, - "ar"); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "foo bar", EC_TK_ENDLIST, - "", EC_TK_ENDLIST, - ""); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "foo bar ", EC_TK_ENDLIST, - "titi", EC_TK_ENDLIST, - "titi"); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "foo toto bar ", EC_TK_ENDLIST, - "titi", EC_TK_ENDLIST, - "titi"); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "x", EC_TK_ENDLIST, - EC_TK_ENDLIST, - ""); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "foo barx", EC_TK_ENDLIST, - EC_TK_ENDLIST, - ""); - - ec_tk_free(tk); - return ret; -} - -static struct ec_test ec_tk_sh_lex_test = { - .name = "tk_sh_lex", - .test = ec_tk_sh_lex_testcase, -}; - -EC_TEST_REGISTER(ec_tk_sh_lex_test); diff --git a/lib/ecoli_tk_sh_lex.h b/lib/ecoli_tk_sh_lex.h deleted file mode 100644 index 7031d1c..0000000 --- a/lib/ecoli_tk_sh_lex.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2016, Olivier MATZ - * - * 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 - -struct ec_tk *ec_tk_sh_lex_new(const char *id, struct ec_tk *child); - -#endif diff --git a/lib/ecoli_tk_space.c b/lib/ecoli_tk_space.c deleted file mode 100644 index 2162afe..0000000 --- a/lib/ecoli_tk_space.c +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (c) 2016, Olivier MATZ - * - * 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 -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -struct ec_tk_space { - struct ec_tk gen; -}; - -static struct ec_parsed_tk *ec_tk_space_parse(const struct ec_tk *gen_tk, - const struct ec_strvec *strvec) -{ - struct ec_parsed_tk *parsed_tk = NULL; - struct ec_strvec *match_strvec; - const char *str; - size_t len = 0; - - parsed_tk = ec_parsed_tk_new(); - if (parsed_tk == NULL) - goto fail; - - if (ec_strvec_len(strvec) == 0) - return parsed_tk; - - str = ec_strvec_val(strvec, 0); - while (isspace(str[len])) - len++; - if (len == 0 || len != strlen(str)) - return parsed_tk; - - match_strvec = ec_strvec_ndup(strvec, 0, 1); - if (match_strvec == NULL) - goto fail; - - ec_parsed_tk_set_match(parsed_tk, gen_tk, match_strvec); - - return parsed_tk; - - fail: - ec_parsed_tk_free(parsed_tk); - return NULL; -} - -static struct ec_tk_type ec_tk_space_type = { - .name = "space", - .parse = ec_tk_space_parse, - .complete = ec_tk_default_complete, - .size = sizeof(struct ec_tk_space), -}; - -EC_TK_TYPE_REGISTER(ec_tk_space_type); - -static int ec_tk_space_testcase(void) -{ - struct ec_tk *tk; - int ret = 0; - - tk = ec_tk_new("space", NULL); - if (tk == NULL) { - ec_log(EC_LOG_ERR, "cannot create tk\n"); - return -1; - } - ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, " "); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, " ", "foo"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, ""); - ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, " foo"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "foo "); - ec_tk_free(tk); - - /* test completion */ - tk = ec_tk_new("space", NULL); - if (tk == NULL) { - ec_log(EC_LOG_ERR, "cannot create tk\n"); - return -1; - } - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "", EC_TK_ENDLIST, - EC_TK_ENDLIST, - ""); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - " ", EC_TK_ENDLIST, - EC_TK_ENDLIST, - ""); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "foo", EC_TK_ENDLIST, - EC_TK_ENDLIST, - ""); - ec_tk_free(tk); - - return ret; -} - -static struct ec_test ec_tk_space_test = { - .name = "tk_space", - .test = ec_tk_space_testcase, -}; - -EC_TEST_REGISTER(ec_tk_space_test); diff --git a/lib/ecoli_tk_space.h b/lib/ecoli_tk_space.h deleted file mode 100644 index 08aa48f..0000000 --- a/lib/ecoli_tk_space.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2016, Olivier MATZ - * - * 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 - -struct ec_tk *ec_tk_space_new(const char *id); - -#endif diff --git a/lib/ecoli_tk_str.c b/lib/ecoli_tk_str.c deleted file mode 100644 index 997f717..0000000 --- a/lib/ecoli_tk_str.c +++ /dev/null @@ -1,254 +0,0 @@ -/* - * Copyright (c) 2016, Olivier MATZ - * - * 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 -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -struct ec_tk_str { - struct ec_tk gen; - char *string; - unsigned len; -}; - -static struct ec_parsed_tk *ec_tk_str_parse(const struct ec_tk *gen_tk, - const struct ec_strvec *strvec) -{ - struct ec_tk_str *tk = (struct ec_tk_str *)gen_tk; - struct ec_strvec *match_strvec; - struct ec_parsed_tk *parsed_tk = NULL; - const char *str; - - parsed_tk = ec_parsed_tk_new(); - if (parsed_tk == NULL) - goto fail; - - if (ec_strvec_len(strvec) == 0) - return parsed_tk; - - str = ec_strvec_val(strvec, 0); - if (strcmp(str, tk->string) != 0) - return parsed_tk; - - match_strvec = ec_strvec_ndup(strvec, 0, 1); - if (match_strvec == NULL) - goto fail; - - ec_parsed_tk_set_match(parsed_tk, gen_tk, match_strvec); - - return parsed_tk; - - fail: - ec_parsed_tk_free(parsed_tk); - return NULL; -} - -static struct ec_completed_tk *ec_tk_str_complete(const struct ec_tk *gen_tk, - const struct ec_strvec *strvec) -{ - struct ec_tk_str *tk = (struct ec_tk_str *)gen_tk; - struct ec_completed_tk *completed_tk; - struct ec_completed_tk_elt *completed_tk_elt; - const char *str, *add; - size_t n = 0; - - completed_tk = ec_completed_tk_new(); - if (completed_tk == NULL) - return NULL; - - if (ec_strvec_len(strvec) != 1) - return completed_tk; - - str = ec_strvec_val(strvec, 0); - for (n = 0; n < tk->len; n++) { - if (str[n] != tk->string[n]) - break; - } - - if (str[n] != '\0') - add = NULL; - else - add = tk->string + n; - - completed_tk_elt = ec_completed_tk_elt_new(gen_tk, add); - if (completed_tk_elt == NULL) { - ec_completed_tk_free(completed_tk); - return NULL; - } - - ec_completed_tk_add_elt(completed_tk, completed_tk_elt); - - return completed_tk; -} - -static const char *ec_tk_str_desc(const struct ec_tk *gen_tk) -{ - struct ec_tk_str *tk = (struct ec_tk_str *)gen_tk; - - return tk->string; -} - -static void ec_tk_str_free_priv(struct ec_tk *gen_tk) -{ - struct ec_tk_str *tk = (struct ec_tk_str *)gen_tk; - - ec_free(tk->string); -} - -static struct ec_tk_type ec_tk_str_type = { - .name = "str", - .parse = ec_tk_str_parse, - .complete = ec_tk_str_complete, - .desc = ec_tk_str_desc, - .size = sizeof(struct ec_tk_str), - .free_priv = ec_tk_str_free_priv, -}; - -EC_TK_TYPE_REGISTER(ec_tk_str_type); - -int ec_tk_str_set_str(struct ec_tk *gen_tk, const char *str) -{ - struct ec_tk_str *tk = (struct ec_tk_str *)gen_tk; - - if (str == NULL) - return -EINVAL; - if (tk->string != NULL) - return -EEXIST; // XXX allow to replace - - tk->string = ec_strdup(str); - if (tk->string == NULL) - return -ENOMEM; - - tk->len = strlen(tk->string); - - return 0; -} - -struct ec_tk *ec_tk_str(const char *id, const char *str) -{ - struct ec_tk *gen_tk = NULL; - - gen_tk = __ec_tk_new(&ec_tk_str_type, id); - if (gen_tk == NULL) - goto fail; - - if (ec_tk_str_set_str(gen_tk, str) < 0) - goto fail; - - return gen_tk; - -fail: - ec_tk_free(gen_tk); - return NULL; -} - -static int ec_tk_str_testcase(void) -{ - struct ec_tk *tk; - int ret = 0; - - /* XXX use EC_NO_ID instead of NULL */ - tk = ec_tk_str(NULL, "foo"); - if (tk == NULL) { - ec_log(EC_LOG_ERR, "cannot create tk\n"); - return -1; - } - ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo", "bar"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "foobar"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, " foo"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, ""); - ec_tk_free(tk); - - tk = ec_tk_str(NULL, "Здравствуйте"); - if (tk == NULL) { - ec_log(EC_LOG_ERR, "cannot create tk\n"); - return -1; - } - ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "Здравствуйте"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "Здравствуйте", - "John!"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "foo"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, ""); - ec_tk_free(tk); - - /* an empty token string always matches */ - tk = ec_tk_str(NULL, ""); - if (tk == NULL) { - ec_log(EC_LOG_ERR, "cannot create tk\n"); - return -1; - } - ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, ""); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "", "foo"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "foo"); - ec_tk_free(tk); - - /* test completion */ - tk = ec_tk_str(NULL, "foo"); - if (tk == NULL) { - ec_log(EC_LOG_ERR, "cannot create tk\n"); - return -1; - } - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - EC_TK_ENDLIST, - EC_TK_ENDLIST, - ""); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "", EC_TK_ENDLIST, - "foo", EC_TK_ENDLIST, - "foo"); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "f", EC_TK_ENDLIST, - "oo", EC_TK_ENDLIST, - "oo"); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "foo", EC_TK_ENDLIST, - "", EC_TK_ENDLIST, - ""); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "x", EC_TK_ENDLIST, - EC_TK_ENDLIST, - ""); - ec_tk_free(tk); - - return ret; -} - -static struct ec_test ec_tk_str_test = { - .name = "tk_str", - .test = ec_tk_str_testcase, -}; - -EC_TEST_REGISTER(ec_tk_str_test); diff --git a/lib/ecoli_tk_str.h b/lib/ecoli_tk_str.h deleted file mode 100644 index d4e2503..0000000 --- a/lib/ecoli_tk_str.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2016, Olivier MATZ - * - * 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 - -struct ec_tk *ec_tk_str(const char *id, const char *str); - -struct ec_tk *ec_tk_str_new(const char *id); - -/* str is duplicated */ -int ec_tk_str_set_str(struct ec_tk *tk, const char *str); - -#endif diff --git a/lib/ecoli_tk_subset.c b/lib/ecoli_tk_subset.c deleted file mode 100644 index 6762eee..0000000 --- a/lib/ecoli_tk_subset.c +++ /dev/null @@ -1,479 +0,0 @@ -/* - * Copyright (c) 2016-2017, Olivier MATZ - * - * 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 -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -struct ec_tk_subset { - struct ec_tk gen; - struct ec_tk **table; - unsigned int len; -}; - -struct parse_result { - struct ec_parsed_tk **parsed_table; /* list of parsed tk */ - size_t parsed_table_len; /* number of parsed tk */ - size_t len; /* consumed strings */ -}; - -static int __ec_tk_subset_parse(struct ec_tk **table, size_t table_len, - const struct ec_strvec *strvec, - struct parse_result *out) -{ - struct ec_tk **child_table; - struct ec_parsed_tk *child_parsed_tk = NULL; - struct ec_strvec *childvec = NULL; - size_t i, j, len = 0; - struct parse_result best_result, result; - int ret; - - if (table_len == 0) - return 0; - - memset(&best_result, 0, sizeof(best_result)); - best_result.parsed_table = - ec_calloc(table_len, sizeof(*best_result.parsed_table[0])); - if (best_result.parsed_table == NULL) - goto fail; - - child_table = ec_calloc(table_len - 1, sizeof(*child_table)); - if (child_table == NULL) - goto fail; - - for (i = 0; i < table_len; i++) { - /* try to parse elt i */ - child_parsed_tk = ec_tk_parse_tokens(table[i], strvec); - if (child_parsed_tk == NULL) - goto fail; - - if (!ec_parsed_tk_matches(child_parsed_tk)) { - ec_parsed_tk_free(child_parsed_tk); - child_parsed_tk = NULL; - continue; - } - - /* build a new table without elt i */ - for (j = 0; j < table_len; j++) { - if (j < i) - child_table[j] = table[j]; - else if (j > i) - child_table[j - 1] = table[j]; - } - - /* build a new strvec */ - len = ec_parsed_tk_len(child_parsed_tk); - childvec = ec_strvec_ndup(strvec, len, - ec_strvec_len(strvec) - len); - if (childvec == NULL) - goto fail; - - memset(&result, 0, sizeof(result)); - - ret = __ec_tk_subset_parse(child_table, table_len - 1, - childvec, &result); - ec_strvec_free(childvec); - childvec = NULL; - if (ret < 0) - goto fail; - - /* if result is not the best, ignore */ - if (result.parsed_table_len + 1 <= - best_result.parsed_table_len) { - ec_parsed_tk_free(child_parsed_tk); - child_parsed_tk = NULL; - for (j = 0; j < result.parsed_table_len; j++) - ec_parsed_tk_free(result.parsed_table[j]); - ec_free(result.parsed_table); - memset(&result, 0, sizeof(result)); - continue; - } - - /* replace the previous best result */ - for (j = 0; j < best_result.parsed_table_len; j++) - ec_parsed_tk_free(best_result.parsed_table[j]); - best_result.parsed_table[0] = child_parsed_tk; - child_parsed_tk = NULL; - for (j = 0; j < result.parsed_table_len; j++) - best_result.parsed_table[j+1] = result.parsed_table[j]; - best_result.parsed_table_len = result.parsed_table_len + 1; - best_result.len = len + result.len; - ec_free(result.parsed_table); - } - - *out = best_result; - ec_free(child_table); - - return 0; - - fail: - ec_parsed_tk_free(child_parsed_tk); - ec_strvec_free(childvec); - for (j = 0; j < best_result.parsed_table_len; j++) - ec_parsed_tk_free(best_result.parsed_table[j]); - ec_free(best_result.parsed_table); - ec_free(child_table); - return -1; -} - -static struct ec_parsed_tk *ec_tk_subset_parse(const struct ec_tk *gen_tk, - const struct ec_strvec *strvec) -{ - struct ec_tk_subset *tk = (struct ec_tk_subset *)gen_tk; - struct ec_parsed_tk *parsed_tk = NULL; - struct parse_result result; - struct ec_strvec *match_strvec; - size_t i; - int ret; - - memset(&result, 0, sizeof(result)); - - parsed_tk = ec_parsed_tk_new(); - if (parsed_tk == NULL) - goto fail; - - ret = __ec_tk_subset_parse(tk->table, tk->len, strvec, &result); - if (ret < 0) - goto fail; - - /* if no child tk matches, return a matching empty strvec */ - if (result.parsed_table_len == 0) { - ec_free(result.parsed_table); - match_strvec = ec_strvec_new(); - if (match_strvec == NULL) - goto fail; - ec_parsed_tk_set_match(parsed_tk, gen_tk, match_strvec); - return parsed_tk; - } - - for (i = 0; i < result.parsed_table_len; i++) { - ec_parsed_tk_add_child(parsed_tk, result.parsed_table[i]); - result.parsed_table[i] = NULL; - } - ec_free(result.parsed_table); - result.parsed_table = NULL; - - match_strvec = ec_strvec_ndup(strvec, 0, result.len); - if (match_strvec == NULL) - goto fail; - - ec_parsed_tk_set_match(parsed_tk, gen_tk, match_strvec); - - return parsed_tk; - - fail: - for (i = 0; i < result.parsed_table_len; i++) - ec_parsed_tk_free(result.parsed_table[i]); - ec_free(result.parsed_table); - ec_parsed_tk_free(parsed_tk); - - return NULL; -} - -static struct ec_completed_tk * -__ec_tk_subset_complete(struct ec_tk **table, size_t table_len, - const struct ec_strvec *strvec) -{ - struct ec_completed_tk *completed_tk = NULL; - struct ec_completed_tk *child_completed_tk = NULL; - struct ec_strvec *childvec = NULL; - struct ec_parsed_tk *parsed_tk = NULL; - struct ec_tk *save; - size_t i, len; - - /* - * example with table = [a, b, c] - * subset_complete([a,b,c], strvec) returns: - * complete(a, strvec) + complete(b, strvec) + complete(c, strvec) + - * + __subset_complete([b, c], childvec) if a matches - * + __subset_complete([a, c], childvec) if b matches - * + __subset_complete([a, b], childvec) if c matches - */ - - completed_tk = ec_completed_tk_new(); - if (completed_tk == NULL) - goto fail; - - /* first, try to complete with each token of the table */ - for (i = 0; i < table_len; i++) { - if (table[i] == NULL) - continue; - - child_completed_tk = ec_tk_complete_tokens(table[i], - strvec); - - if (child_completed_tk == NULL) - goto fail; - - ec_completed_tk_merge(completed_tk, child_completed_tk); - child_completed_tk = NULL; - } - - /* then, if a token matches, advance in strvec and try to complete with - * all the other tokens */ - for (i = 0; i < table_len; i++) { - if (table[i] == NULL) - continue; - - parsed_tk = ec_tk_parse_tokens(table[i], strvec); - if (parsed_tk == NULL) - goto fail; - - if (!ec_parsed_tk_matches(parsed_tk)) { - ec_parsed_tk_free(parsed_tk); - parsed_tk = NULL; - continue; - } - - len = ec_parsed_tk_len(parsed_tk); - childvec = ec_strvec_ndup(strvec, len, - ec_strvec_len(strvec) - len); - if (childvec == NULL) - goto fail; - - save = table[i]; - table[i] = NULL; - child_completed_tk = __ec_tk_subset_complete(table, - table_len, childvec); - table[i] = save; - ec_strvec_free(childvec); - childvec = NULL; - - if (child_completed_tk == NULL) - goto fail; - - ec_completed_tk_merge(completed_tk, child_completed_tk); - child_completed_tk = NULL; - - ec_parsed_tk_free(parsed_tk); - parsed_tk = NULL; - } - - return completed_tk; -fail: - ec_parsed_tk_free(parsed_tk); - ec_completed_tk_free(child_completed_tk); - ec_completed_tk_free(completed_tk); - - return NULL; -} - -static struct ec_completed_tk *ec_tk_subset_complete(const struct ec_tk *gen_tk, - const struct ec_strvec *strvec) -{ - struct ec_tk_subset *tk = (struct ec_tk_subset *)gen_tk; - - return __ec_tk_subset_complete(tk->table, tk->len, strvec); -} - -static void ec_tk_subset_free_priv(struct ec_tk *gen_tk) -{ - struct ec_tk_subset *tk = (struct ec_tk_subset *)gen_tk; - unsigned int i; - - for (i = 0; i < tk->len; i++) - ec_tk_free(tk->table[i]); - ec_free(tk->table); -} - -int ec_tk_subset_add(struct ec_tk *gen_tk, struct ec_tk *child) -{ - struct ec_tk_subset *tk = (struct ec_tk_subset *)gen_tk; - struct ec_tk **table; - - assert(tk != NULL); - - if (child == NULL) - return -EINVAL; - - gen_tk->flags &= ~EC_TK_F_BUILT; - - table = ec_realloc(tk->table, (tk->len + 1) * sizeof(*tk->table)); - if (table == NULL) { - ec_tk_free(child); - return -1; - } - - tk->table = table; - table[tk->len] = child; - tk->len++; - - child->parent = gen_tk; - TAILQ_INSERT_TAIL(&gen_tk->children, child, next); - - return 0; -} - -static struct ec_tk_type ec_tk_subset_type = { - .name = "subset", - .parse = ec_tk_subset_parse, - .complete = ec_tk_subset_complete, - .size = sizeof(struct ec_tk_subset), - .free_priv = ec_tk_subset_free_priv, -}; - -EC_TK_TYPE_REGISTER(ec_tk_subset_type); - -struct ec_tk *__ec_tk_subset(const char *id, ...) -{ - struct ec_tk *gen_tk = NULL; - struct ec_tk_subset *tk = NULL; - struct ec_tk *child; - va_list ap; - int fail = 0; - - va_start(ap, id); - - gen_tk = __ec_tk_new(&ec_tk_subset_type, id); - tk = (struct ec_tk_subset *)gen_tk; - if (tk == NULL) - fail = 1;; - - for (child = va_arg(ap, struct ec_tk *); - child != EC_TK_ENDLIST; - child = va_arg(ap, struct ec_tk *)) { - - /* on error, don't quit the loop to avoid leaks */ - if (fail == 1 || child == NULL || - ec_tk_subset_add(gen_tk, child) < 0) { - fail = 1; - ec_tk_free(child); - } - } - - if (fail == 1) - goto fail; - - va_end(ap); - return gen_tk; - -fail: - ec_tk_free(gen_tk); /* will also free children */ - va_end(ap); - return NULL; -} - -static int ec_tk_subset_testcase(void) -{ - struct ec_tk *tk; - int ret = 0; - - tk = EC_TK_SUBSET(NULL, - EC_TK_OR(NULL, - ec_tk_str(NULL, "foo"), - ec_tk_str(NULL, "bar")), - ec_tk_str(NULL, "bar"), - ec_tk_str(NULL, "toto") - ); - if (tk == NULL) { - ec_log(EC_LOG_ERR, "cannot create tk\n"); - return -1; - } - ret |= EC_TEST_CHECK_TK_PARSE(tk, 0); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "bar"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 2, "foo", "bar", "titi"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 3, "bar", "foo", "toto"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo", "foo"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 2, "bar", "bar"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 2, "bar", "foo"); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 0, " "); - ret |= EC_TEST_CHECK_TK_PARSE(tk, 0, "foox"); - ec_tk_free(tk); - - /* test completion */ - tk = EC_TK_SUBSET(NULL, - ec_tk_str(NULL, "foo"), - ec_tk_str(NULL, "bar"), - ec_tk_str(NULL, "bar2"), - ec_tk_str(NULL, "toto"), - ec_tk_str(NULL, "titi") - ); - if (tk == NULL) { - ec_log(EC_LOG_ERR, "cannot create tk\n"); - return -1; - } - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "", EC_TK_ENDLIST, - "foo", "bar", "bar2", "toto", "titi", EC_TK_ENDLIST, - ""); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "bar", "bar2", "", EC_TK_ENDLIST, - "foo", "toto", "titi", EC_TK_ENDLIST, - ""); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "f", EC_TK_ENDLIST, - "oo", EC_TK_ENDLIST, - "oo"); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "b", EC_TK_ENDLIST, - "ar", "ar2", EC_TK_ENDLIST, - "ar"); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "bar", EC_TK_ENDLIST, - "", "2", EC_TK_ENDLIST, - ""); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "bar", "b", EC_TK_ENDLIST, - "ar2", EC_TK_ENDLIST, - "ar2"); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "t", EC_TK_ENDLIST, - "oto", "iti", EC_TK_ENDLIST, - ""); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "to", EC_TK_ENDLIST, - "to", EC_TK_ENDLIST, - "to"); - ret |= EC_TEST_CHECK_TK_COMPLETE(tk, - "x", EC_TK_ENDLIST, - EC_TK_ENDLIST, - ""); - ec_tk_free(tk); - - return ret; -} - -static struct ec_test ec_tk_subset_test = { - .name = "tk_subset", - .test = ec_tk_subset_testcase, -}; - -EC_TEST_REGISTER(ec_tk_subset_test); diff --git a/lib/ecoli_tk_subset.h b/lib/ecoli_tk_subset.h deleted file mode 100644 index aac3014..0000000 --- a/lib/ecoli_tk_subset.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2016-2017, Olivier MATZ - * - * 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 - -#define EC_TK_SUBSET(args...) __ec_tk_subset(args, EC_TK_ENDLIST) - -/* list must be terminated with EC_TK_ENDLIST */ -/* all token given in the list will be freed when freeing this one */ -/* avoid using this function directly, prefer the macro EC_TK_SUBSET() or - * ec_tk_subset() + ec_tk_subset_add() */ -struct ec_tk *__ec_tk_subset(const char *id, ...); - -struct ec_tk *ec_tk_subset(const char *id); - -/* child is consumed */ -int ec_tk_subset_add(struct ec_tk *tk, struct ec_tk *child); - -#endif diff --git a/lib/ecoli_tk_weakref.c b/lib/ecoli_tk_weakref.c deleted file mode 100644 index 6ce9a32..0000000 --- a/lib/ecoli_tk_weakref.c +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (c) 2016, Olivier MATZ - * - * 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 -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -struct ec_tk_weakref { - struct ec_tk gen; - struct ec_tk *child; -}; - -static struct ec_parsed_tk *ec_tk_weakref_parse(const struct ec_tk *gen_tk, - const struct ec_strvec *strvec) -{ - struct ec_tk_weakref *tk = (struct ec_tk_weakref *)gen_tk; - - return ec_tk_parse_tokens(tk->child, strvec); -} - -static struct ec_completed_tk *ec_tk_weakref_complete(const struct ec_tk *gen_tk, - const struct ec_strvec *strvec) -{ - struct ec_tk_weakref *tk = (struct ec_tk_weakref *)gen_tk; - - return ec_tk_complete_tokens(tk->child, strvec); -} - -static struct ec_tk_type ec_tk_weakref_type = { - .name = "weakref", - .parse = ec_tk_weakref_parse, - .complete = ec_tk_weakref_complete, - .size = sizeof(struct ec_tk_weakref), -}; - -EC_TK_TYPE_REGISTER(ec_tk_weakref_type); - -int ec_tk_weakref_set(struct ec_tk *gen_tk, struct ec_tk *child) -{ - struct ec_tk_weakref *tk = (struct ec_tk_weakref *)gen_tk; - - // XXX check tk type - - assert(tk != NULL); - - if (child == NULL) - return -EINVAL; - - gen_tk->flags &= ~EC_TK_F_BUILT; - - tk->child = child; - - child->parent = gen_tk; - TAILQ_INSERT_TAIL(&gen_tk->children, child, next); // XXX really needed? - - return 0; -} - -struct ec_tk *ec_tk_weakref(const char *id, struct ec_tk *child) -{ - struct ec_tk *gen_tk = NULL; - - if (child == NULL) - return NULL; - - gen_tk = __ec_tk_new(&ec_tk_weakref_type, id); - if (gen_tk == NULL) - return NULL; - - ec_tk_weakref_set(gen_tk, child); - - return gen_tk; -} - -static int ec_tk_weakref_testcase(void) -{ - return 0; -} - -static struct ec_test ec_tk_weakref_test = { - .name = "tk_weakref", - .test = ec_tk_weakref_testcase, -}; - -EC_TEST_REGISTER(ec_tk_weakref_test); diff --git a/lib/ecoli_tk_weakref.h b/lib/ecoli_tk_weakref.h deleted file mode 100644 index c911400..0000000 --- a/lib/ecoli_tk_weakref.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (c) 2016, Olivier MATZ - * - * 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 - -/* 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 diff --git a/lib/main-readline.c b/lib/main-readline.c index 241d2c9..9b61903 100644 --- a/lib/main-readline.c +++ b/lib/main-readline.c @@ -33,50 +33,50 @@ #include #include -#include +#include #include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include -static struct ec_tk *commands; +static struct ec_node *commands; static char *my_completion_entry(const char *s, int state) { - static struct ec_completed_tk *c; - static struct ec_completed_tk_iter *iter; - static const struct ec_completed_tk_elt *elt; + static struct ec_completed *c; + static struct ec_completed_iter *iter; + static const struct ec_completed_elt *elt; char *out_string; if (state == 0) { char *line; - ec_completed_tk_free(c); + ec_completed_free(c); line = strdup(rl_line_buffer); if (line == NULL) return NULL; line[rl_point] = '\0'; - c = ec_tk_complete(commands, line); + c = ec_node_complete(commands, line); free(line); if (c == NULL) return NULL; - ec_completed_tk_iter_free(iter); - iter = ec_completed_tk_iter_new(c, EC_MATCH); + ec_completed_iter_free(iter); + iter = ec_completed_iter_new(c, EC_MATCH); if (iter == NULL) return NULL; } - elt = ec_completed_tk_iter_next(iter); + elt = ec_completed_iter_next(iter); if (elt == NULL) return NULL; @@ -98,19 +98,21 @@ static char **my_attempted_completion(const char *text, int start, int end) } /* this function builds the help string */ -static char *get_tk_help(const struct ec_tk *tk) +static char *get_tk_help(const struct ec_node *node) { - const struct ec_tk *tk2; + const struct ec_node *node2; char *help = NULL; char *tk_help = NULL; - for (tk2 = tk; tk2 != NULL && tk_help == NULL; tk2 = ec_tk_parent(tk2)) - tk_help = ec_keyval_get(ec_tk_attrs(tk2), "help"); + for (node2 = node; + node2 != NULL && tk_help == NULL; + node2 = ec_node_parent(node2)) + tk_help = ec_keyval_get(ec_node_attrs(node2), "help"); if (tk_help == NULL) tk_help = ""; - if (asprintf(&help, "%-20s %s", ec_tk_desc(tk), tk_help) < 0) + if (asprintf(&help, "%-20s %s", ec_node_desc(node), tk_help) < 0) return NULL; return help; @@ -118,9 +120,9 @@ static char *get_tk_help(const struct ec_tk *tk) static int show_help(int ignore, int invoking_key) { - const struct ec_completed_tk_elt *elt; - struct ec_completed_tk_iter *iter; - struct ec_completed_tk *c; + const struct ec_completed_elt *elt; + struct ec_completed_iter *iter; + struct ec_completed *c; char *line; unsigned int count, i; char **helps = NULL; @@ -133,29 +135,29 @@ static int show_help(int ignore, int invoking_key) return 1; line[rl_point] = '\0'; - c = ec_tk_complete(commands, line); + c = ec_node_complete(commands, line); free(line); if (c == NULL) return 1; - //ec_completed_tk_dump(stdout, c); + //ec_completed_dump(stdout, c); - count = ec_completed_tk_count(c, EC_MATCH | EC_NO_MATCH); + count = ec_completed_count(c, EC_MATCH | EC_NO_MATCH); helps = calloc(count + 1, sizeof(char *)); if (helps == NULL) return 1; - iter = ec_completed_tk_iter_new(c, EC_MATCH | EC_NO_MATCH); + iter = ec_completed_iter_new(c, EC_MATCH | EC_NO_MATCH); if (iter == NULL) goto fail; /* strangely, rl_display_match_list() expects first index at 1 */ - for (i = 1, elt = ec_completed_tk_iter_next(iter); + for (i = 1, elt = ec_completed_iter_next(iter); i <= count && elt != NULL; - i++, elt = ec_completed_tk_iter_next(iter)) { - helps[i] = get_tk_help(elt->tk); + i++, elt = ec_completed_iter_next(iter)) { + helps[i] = get_tk_help(elt->node); } - ec_completed_tk_free(c); + ec_completed_free(c); rl_display_match_list(helps, count, 1000); @@ -171,65 +173,65 @@ fail: static int create_commands(void) { - struct ec_tk *cmdlist = NULL, *cmd = NULL; + struct ec_node *cmdlist = NULL, *cmd = NULL; - cmdlist = ec_tk_new("or", NULL); + cmdlist = ec_node_new("or", NULL); if (cmdlist == NULL) goto fail; - cmd = EC_TK_SEQ(NULL, - ec_tk_str(NULL, "hello"), - EC_TK_OR("name", - ec_tk_str(NULL, "john"), - ec_tk_str(NULL, "johnny"), - ec_tk_str(NULL, "mike") + cmd = EC_NODE_SEQ(NULL, + ec_node_str(NULL, "hello"), + EC_NODE_OR("name", + ec_node_str(NULL, "john"), + ec_node_str(NULL, "johnny"), + ec_node_str(NULL, "mike") ), - ec_tk_option_new(NULL, ec_tk_int("int", 0, 10, 10)) + ec_node_option(NULL, ec_node_int("int", 0, 10, 10)) ); if (cmd == NULL) goto fail; - ec_keyval_set(ec_tk_attrs(cmd), "help", + ec_keyval_set(ec_node_attrs(cmd), "help", "say hello to someone several times", NULL); - ec_keyval_set(ec_tk_attrs(ec_tk_find(cmd, "name")), + ec_keyval_set(ec_node_attrs(ec_node_find(cmd, "name")), "help", "the name of the person", NULL); - ec_keyval_set(ec_tk_attrs(ec_tk_find(cmd, "int")), + ec_keyval_set(ec_node_attrs(ec_node_find(cmd, "int")), "help", "an integer", NULL); - if (ec_tk_or_add(cmdlist, cmd) < 0) + if (ec_node_or_add(cmdlist, cmd) < 0) goto fail; #if 0 - cmd = EC_TK_CMD(NULL, "good morning john|johnny|mike [count]", - ec_tk_int("count", 0, 10, 10)); + cmd = EC_NODE_CMD(NULL, "good morning john|johnny|mike [count]", + ec_node_int("count", 0, 10, 10)); if (cmd == NULL) goto fail; - ec_keyval_set(ec_tk_attrs(cmd), "help", + ec_keyval_set(ec_node_attrs(cmd), "help", "say good morning to someone several times", NULL); - if (ec_tk_or_add(cmdlist, cmd) < 0) + if (ec_node_or_add(cmdlist, cmd) < 0) goto fail; #endif - cmd = EC_TK_SEQ(NULL, - ec_tk_str(NULL, "bye") + cmd = EC_NODE_SEQ(NULL, + ec_node_str(NULL, "bye") ); - ec_keyval_set(ec_tk_attrs(cmd), "help", "say bye to someone", NULL); - if (ec_tk_or_add(cmdlist, cmd) < 0) + ec_keyval_set(ec_node_attrs(cmd), "help", "say bye to someone", NULL); + if (ec_node_or_add(cmdlist, cmd) < 0) goto fail; - commands = ec_tk_sh_lex_new(NULL, cmdlist); + commands = ec_node_sh_lex(NULL, cmdlist); if (commands == NULL) goto fail; return 0; fail: - fprintf(stderr, "cannot initialize tokens\n"); - ec_tk_free(cmdlist); + fprintf(stderr, "cannot initialize nodes\n"); + ec_node_free(cmdlist); return -1; } int main(void) { - struct ec_parsed_tk *p; + struct ec_parsed *p; // const char *name; char *line; @@ -245,14 +247,14 @@ int main(void) if (line == NULL) break; - p = ec_tk_parse(commands, line); - ec_parsed_tk_dump(stdout, p); + p = ec_node_parse(commands, line); + ec_parsed_dump(stdout, p); add_history(line); - ec_parsed_tk_free(p); + ec_parsed_free(p); } - ec_tk_free(commands); + ec_node_free(commands); return 0; } diff --git a/lib/todo.txt b/lib/todo.txt index 6791965..7ed4ab3 100644 --- a/lib/todo.txt +++ b/lib/todo.txt @@ -24,13 +24,18 @@ X remove the _new() functions - split ecoli_tk.h - cache results when appropriate? - size_t or unsigned int? -- rename: +X rename: - ec_tk -> ec_node - ec_parsed_tk -> ec_parsed - ec_completed_tk -> ec_completed - tk, gen_tk, token, ... -> node - tokens -> input_str / input_strvec ? +dependencies +============ + +- pass the current parsed state when parsing/completing + logs ====