From 90d09f5806b457e758ac7f4fadf485d24162d38f Mon Sep 17 00:00:00 2001 From: Olivier Matz Date: Thu, 15 Jun 2017 22:43:24 +0200 Subject: [PATCH] save --- lib/Makefile | 2 + lib/ecoli_completed.c | 321 ++++++++++++++++++++++++ lib/ecoli_completed.h | 105 ++++++++ lib/ecoli_node.c | 495 ------------------------------------- lib/ecoli_node.h | 121 +-------- lib/ecoli_node_cmd.c | 3 + lib/ecoli_node_empty.c | 2 + lib/ecoli_node_expr.c | 2 + lib/ecoli_node_expr_test.c | 2 + lib/ecoli_node_int.c | 2 + lib/ecoli_node_many.c | 2 + lib/ecoli_node_option.c | 2 + lib/ecoli_node_or.c | 2 + lib/ecoli_node_re.c | 2 + lib/ecoli_node_re_lex.c | 1 + lib/ecoli_node_seq.c | 2 + lib/ecoli_node_sh_lex.c | 2 + lib/ecoli_node_space.c | 2 + lib/ecoli_node_str.c | 2 + lib/ecoli_node_subset.c | 2 + lib/ecoli_node_weakref.c | 2 + lib/ecoli_parsed.c | 253 +++++++++++++++++++ lib/ecoli_parsed.h | 86 +++++++ lib/ecoli_test.c | 3 + lib/main-readline.c | 3 +- lib/todo.txt | 2 + 26 files changed, 808 insertions(+), 615 deletions(-) create mode 100644 lib/ecoli_completed.c create mode 100644 lib/ecoli_completed.h create mode 100644 lib/ecoli_parsed.c create mode 100644 lib/ecoli_parsed.h diff --git a/lib/Makefile b/lib/Makefile index da9961a..cb35f82 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -34,6 +34,7 @@ CFLAGS = -g -O0 -Wall -Werror -W -Wextra -fPIC -Wmissing-prototypes CFLAGS += -I. srcs := +srcs += ecoli_completed.c srcs += ecoli_keyval.c srcs += ecoli_log.c srcs += ecoli_malloc.c @@ -56,6 +57,7 @@ srcs += ecoli_node_space.c srcs += ecoli_node_str.c srcs += ecoli_node_subset.c srcs += ecoli_node_weakref.c +srcs += ecoli_parsed.c shlib-y-$(O)libecoli.so := $(srcs) diff --git a/lib/ecoli_completed.c b/lib/ecoli_completed.c new file mode 100644 index 0000000..60588e5 --- /dev/null +++ b/lib/ecoli_completed.c @@ -0,0 +1,321 @@ +/* + * 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_completed *ec_completed(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(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(); + 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(); + if (completed == NULL) + return NULL; + + completed_elt = ec_completed_elt(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(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); +} diff --git a/lib/ecoli_completed.h b/lib/ecoli_completed.h new file mode 100644 index 0000000..d9fcdc3 --- /dev/null +++ b/lib/ecoli_completed.h @@ -0,0 +1,105 @@ +/* + * 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_COMPLETED_ +#define ECOLI_COMPLETED_ + +#include +#include +#include + +struct ec_node; + +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(void); +struct ec_completed_elt *ec_completed_elt(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(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.c b/lib/ecoli_node.c index da91173..1e09b5d 100644 --- a/lib/ecoli_node.c +++ b/lib/ecoli_node.c @@ -215,501 +215,6 @@ void ec_node_dump(FILE *out, const struct ec_node *node) __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(); - 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(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(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(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(); - 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(); - if (completed == NULL) - return NULL; - - completed_elt = ec_completed_elt(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(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) diff --git a/lib/ecoli_node.h b/lib/ecoli_node.h index 0933a20..27a52c8 100644 --- a/lib/ecoli_node.h +++ b/lib/ecoli_node.h @@ -36,6 +36,7 @@ struct ec_node; struct ec_parsed; +struct ec_completed; struct ec_strvec; struct ec_keyval; @@ -126,6 +127,7 @@ struct ec_node *__ec_node(const struct ec_node_type *type, const char *id); /* create a_new node node */ struct ec_node *ec_node(const char *typename, const char *id); +struct ec_node *ec_node_clone(struct ec_node *node); void ec_node_free(struct ec_node *node); /* XXX add more accessors */ @@ -137,123 +139,4 @@ 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(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(void); -struct ec_completed_elt *ec_completed_elt(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(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 index f6afbd2..2753339 100644 --- a/lib/ecoli_node_cmd.c +++ b/lib/ecoli_node_cmd.c @@ -38,6 +38,9 @@ #include #include #include +#include +#include +#include #include #include #include diff --git a/lib/ecoli_node_empty.c b/lib/ecoli_node_empty.c index c85d641..e52956e 100644 --- a/lib/ecoli_node_empty.c +++ b/lib/ecoli_node_empty.c @@ -34,6 +34,8 @@ #include #include #include +#include +#include #include struct ec_node_empty { diff --git a/lib/ecoli_node_expr.c b/lib/ecoli_node_expr.c index 8d2f698..672b62e 100644 --- a/lib/ecoli_node_expr.c +++ b/lib/ecoli_node_expr.c @@ -39,6 +39,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/lib/ecoli_node_expr_test.c b/lib/ecoli_node_expr_test.c index 2ce0669..ea8938b 100644 --- a/lib/ecoli_node_expr_test.c +++ b/lib/ecoli_node_expr_test.c @@ -33,6 +33,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/lib/ecoli_node_int.c b/lib/ecoli_node_int.c index 8c7be7c..bf73a13 100644 --- a/lib/ecoli_node_int.c +++ b/lib/ecoli_node_int.c @@ -38,6 +38,8 @@ #include #include #include +#include +#include #include #include diff --git a/lib/ecoli_node_many.c b/lib/ecoli_node_many.c index 6142b32..b5dff92 100644 --- a/lib/ecoli_node_many.c +++ b/lib/ecoli_node_many.c @@ -36,6 +36,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/lib/ecoli_node_option.c b/lib/ecoli_node_option.c index 01665b4..305734f 100644 --- a/lib/ecoli_node_option.c +++ b/lib/ecoli_node_option.c @@ -35,6 +35,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/lib/ecoli_node_or.c b/lib/ecoli_node_or.c index 932b01c..a4df91f 100644 --- a/lib/ecoli_node_or.c +++ b/lib/ecoli_node_or.c @@ -36,6 +36,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/lib/ecoli_node_re.c b/lib/ecoli_node_re.c index 00bd793..3d2a064 100644 --- a/lib/ecoli_node_re.c +++ b/lib/ecoli_node_re.c @@ -36,6 +36,8 @@ #include #include #include +#include +#include #include struct ec_node_re { diff --git a/lib/ecoli_node_re_lex.c b/lib/ecoli_node_re_lex.c index e0e6d00..e9e3ace 100644 --- a/lib/ecoli_node_re_lex.c +++ b/lib/ecoli_node_re_lex.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/lib/ecoli_node_seq.c b/lib/ecoli_node_seq.c index 4a975ad..cd103db 100644 --- a/lib/ecoli_node_seq.c +++ b/lib/ecoli_node_seq.c @@ -37,6 +37,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/lib/ecoli_node_sh_lex.c b/lib/ecoli_node_sh_lex.c index d0c5eb2..b92af1f 100644 --- a/lib/ecoli_node_sh_lex.c +++ b/lib/ecoli_node_sh_lex.c @@ -38,6 +38,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/lib/ecoli_node_space.c b/lib/ecoli_node_space.c index db3e9f5..be14348 100644 --- a/lib/ecoli_node_space.c +++ b/lib/ecoli_node_space.c @@ -35,6 +35,8 @@ #include #include #include +#include +#include #include struct ec_node_space { diff --git a/lib/ecoli_node_str.c b/lib/ecoli_node_str.c index 237f0fe..fe8c7bb 100644 --- a/lib/ecoli_node_str.c +++ b/lib/ecoli_node_str.c @@ -35,6 +35,8 @@ #include #include #include +#include +#include #include struct ec_node_str { diff --git a/lib/ecoli_node_subset.c b/lib/ecoli_node_subset.c index 53790f6..2150656 100644 --- a/lib/ecoli_node_subset.c +++ b/lib/ecoli_node_subset.c @@ -37,6 +37,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/lib/ecoli_node_weakref.c b/lib/ecoli_node_weakref.c index 7399562..603f02d 100644 --- a/lib/ecoli_node_weakref.c +++ b/lib/ecoli_node_weakref.c @@ -37,6 +37,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/lib/ecoli_parsed.c b/lib/ecoli_parsed.c new file mode 100644 index 0000000..b26b123 --- /dev/null +++ b/lib/ecoli_parsed.c @@ -0,0 +1,253 @@ +/* + * 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_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(); + 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(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; +} diff --git a/lib/ecoli_parsed.h b/lib/ecoli_parsed.h new file mode 100644 index 0000000..83ace08 --- /dev/null +++ b/lib/ecoli_parsed.h @@ -0,0 +1,86 @@ +/* + * 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_PARSED_ +#define ECOLI_PARSED_ + +#include +#include +#include + +struct ec_node; + +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(void); +void ec_parsed_free(struct ec_parsed *parsed); +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); + +#endif diff --git a/lib/ecoli_test.c b/lib/ecoli_test.c index 134fefa..6b002b8 100644 --- a/lib/ecoli_test.c +++ b/lib/ecoli_test.c @@ -35,6 +35,9 @@ #include #include #include +#include +#include +#include static struct ec_test_list test_list = TAILQ_HEAD_INITIALIZER(test_list); diff --git a/lib/main-readline.c b/lib/main-readline.c index 0fe0672..73df238 100644 --- a/lib/main-readline.c +++ b/lib/main-readline.c @@ -34,8 +34,9 @@ #include #include +#include +#include #include - #include #include #include diff --git a/lib/todo.txt b/lib/todo.txt index 7ed4ab3..a29054c 100644 --- a/lib/todo.txt +++ b/lib/todo.txt @@ -45,6 +45,8 @@ yaml ==== X register nodes by name +- interface to add attributes: all nodes must be configurable through a + generic api - yaml interface to create nodes - example -- 2.20.1