save
authorOlivier Matz <zer0@droids-corp.org>
Wed, 21 Dec 2016 16:55:02 +0000 (17:55 +0100)
committerOlivier Matz <zer0@droids-corp.org>
Wed, 21 Dec 2016 16:58:52 +0000 (17:58 +0100)
17 files changed:
lib/Makefile
lib/build/test [deleted file]
lib/ecoli_keyval.c
lib/ecoli_strvec.c
lib/ecoli_strvec.h
lib/ecoli_tk.c
lib/ecoli_tk.h
lib/ecoli_tk_expr.c [new file with mode: 0644]
lib/ecoli_tk_expr.h [new file with mode: 0644]
lib/ecoli_tk_int.c
lib/ecoli_tk_many.c
lib/ecoli_tk_or.c
lib/ecoli_tk_seq.c
lib/ecoli_tk_shlex.c
lib/ecoli_tk_space.c
lib/ecoli_tk_str.c
lib/main.c

index 9bbd73b..ba88840 100644 (file)
@@ -39,7 +39,7 @@ srcs += ecoli_tk_str.c ecoli_tk_seq.c
 srcs += ecoli_tk_space.c ecoli_tk_or.c ecoli_test.c
 srcs += ecoli_tk_empty.c ecoli_tk_int.c
 srcs += ecoli_tk_option.c ecoli_tk_many.c
-srcs += ecoli_tk_shlex.c
+srcs += ecoli_tk_shlex.c ecoli_tk_expr.c
 shlib-y-$(O)libecoli.so := $(srcs)
 
 ldflags-$(O)test = -rdynamic
diff --git a/lib/build/test b/lib/build/test
deleted file mode 100755 (executable)
index e389ed2..0000000
Binary files a/lib/build/test and /dev/null differ
index 8275715..7f87b01 100644 (file)
 #include <ecoli_test.h>
 #include <ecoli_keyval.h>
 
-struct ec_keyval {
-       size_t len;
-       struct ec_keyval_elt *vec;
-};
-
 struct ec_keyval_elt {
        char *key;
        void *val;
        ec_keyval_elt_free_t free;
 };
 
+struct ec_keyval {
+       size_t len;
+       struct ec_keyval_elt *vec;
+};
+
 struct ec_keyval *ec_keyval_new(void)
 {
        struct ec_keyval *keyval;
@@ -118,8 +118,7 @@ int ec_keyval_del(struct ec_keyval *keyval, const char *key)
 int ec_keyval_set(struct ec_keyval *keyval, const char *key, void *val,
        ec_keyval_elt_free_t free_cb)
 {
-       struct ec_keyval_elt *new_vec;
-       struct ec_keyval_elt *elt;
+       struct ec_keyval_elt *elt, *new_vec;
 
        assert(keyval != NULL);
        assert(key != NULL);
@@ -129,20 +128,25 @@ int ec_keyval_set(struct ec_keyval *keyval, const char *key, void *val,
        new_vec = ec_realloc(keyval->vec,
                sizeof(*keyval->vec) * (keyval->len + 1));
        if (new_vec == NULL)
-               return -ENOMEM;
+               goto fail;
 
        keyval->vec = new_vec;
 
        elt = &new_vec[keyval->len];
        elt->key = ec_strdup(key);
        if (elt->key == NULL)
-               return -ENOMEM;
+               goto fail;
 
        elt->val = val;
        elt->free = free_cb;
        keyval->len++;
 
        return 0;
+
+fail:
+       if (free_cb)
+               free_cb(val);
+       return -ENOMEM;
 }
 
 void ec_keyval_free(struct ec_keyval *keyval)
index da15502..3cc2055 100644 (file)
  */
 
 #include <sys/types.h>
+#include <stdio.h>
 #include <stdlib.h>
+#include <errno.h>
 
 #include <ecoli_malloc.h>
 #include <ecoli_strvec.h>
 
+struct ec_strvec_elt {
+       unsigned int refcnt;
+       char *str;
+};
+
+struct ec_strvec {
+       size_t len;
+       struct ec_strvec_elt **vec;
+};
+
 struct ec_strvec *ec_strvec_new(void)
 {
        struct ec_strvec *strvec;
@@ -44,23 +56,33 @@ struct ec_strvec *ec_strvec_new(void)
 
 int ec_strvec_add(struct ec_strvec *strvec, const char *s)
 {
-       char **new_vec;
+       struct ec_strvec_elt *elt, **new_vec;
 
        new_vec = ec_realloc(strvec->vec,
                sizeof(*strvec->vec) * (strvec->len + 1));
        if (new_vec == NULL)
-               return -1;
+               return -ENOMEM;
 
        strvec->vec = new_vec;
-       strvec->vec[strvec->len] = ec_strdup(s);
-       if (strvec->vec[strvec->len] == NULL)
-               return -1;
 
+       elt = ec_malloc(sizeof(*elt));
+       if (elt == NULL)
+               return -ENOMEM;
+
+       elt->str = ec_strdup(s);
+       if (elt->str == NULL) {
+               ec_free(elt);
+               return -ENOMEM;
+       }
+       elt->refcnt = 1;
+
+       new_vec[strvec->len] = elt;
        strvec->len++;
        return 0;
 }
 
-struct ec_strvec *ec_strvec_ndup(const struct ec_strvec *strvec, size_t len)
+struct ec_strvec *ec_strvec_ndup(const struct ec_strvec *strvec, size_t off,
+       size_t len)
 {
        struct ec_strvec *copy = NULL;
        size_t i, veclen;
@@ -69,20 +91,22 @@ struct ec_strvec *ec_strvec_ndup(const struct ec_strvec *strvec, size_t len)
        if (copy == NULL)
                goto fail;
 
+       veclen = ec_strvec_len(strvec);
+       if (off >= veclen)
+               len = 0;
+       else if (off + len > veclen)
+               len -= (veclen - off);
+
        if (len == 0)
                return copy;
 
-       veclen = ec_strvec_len(strvec);
-       if (len > veclen)
-               len = veclen;
        copy->vec = ec_calloc(len, sizeof(*copy->vec));
        if (copy->vec == NULL)
                goto fail;
 
        for (i = 0; i < len; i++) {
-               copy->vec[i] = ec_strdup(strvec->vec[i]);
-               if (copy->vec[i] == NULL)
-                       goto fail;
+               copy->vec[i] = strvec->vec[i + off];
+               copy->vec[i]->refcnt++;
                copy->len++;
        }
 
@@ -95,18 +119,25 @@ fail:
 
 struct ec_strvec *ec_strvec_dup(const struct ec_strvec *strvec)
 {
-       return ec_strvec_ndup(strvec, ec_strvec_len(strvec));
+       return ec_strvec_ndup(strvec, 0, ec_strvec_len(strvec));
 }
 
 void ec_strvec_free(struct ec_strvec *strvec)
 {
+       struct ec_strvec_elt *elt;
        size_t i;
 
        if (strvec == NULL)
                return;
 
-       for (i = 0; i < ec_strvec_len(strvec); i++)
-               ec_free(ec_strvec_val(strvec, i));
+       for (i = 0; i < ec_strvec_len(strvec); i++) {
+               elt = strvec->vec[i];
+               elt->refcnt--;
+               if (elt->refcnt == 0) {
+                       ec_free(elt->str);
+                       ec_free(elt);
+               }
+       }
 
        ec_free(strvec->vec);
        ec_free(strvec);
@@ -122,22 +153,10 @@ char *ec_strvec_val(const struct ec_strvec *strvec, size_t idx)
        if (strvec == NULL || idx >= strvec->len)
                return NULL;
 
-       return strvec->vec[idx];
-}
-
-int ec_strvec_slice(struct ec_strvec *strvec, const struct ec_strvec *from,
-       size_t off)
-{
-       if (off > from->len)
-               return -1;
-
-       strvec->len = from->len - off;
-       strvec->vec = &from->vec[off];
-
-       return 0;
+       return strvec->vec[idx]->str;
 }
 
-void ec_strvec_dump(const struct ec_strvec *strvec, FILE *out)
+void ec_strvec_dump(FILE *out, const struct ec_strvec *strvec)
 {
        size_t i;
 
@@ -148,7 +167,8 @@ void ec_strvec_dump(const struct ec_strvec *strvec, FILE *out)
 
        fprintf(out, "strvec:\n");
        for (i = 0; i < ec_strvec_len(strvec); i++)
-               fprintf(out, "  %zd: %s\n", i, strvec->vec[i]);
+               fprintf(out, "  %zd: %s (refcnt=%d)\n", i,
+                       strvec->vec[i]->str, strvec->vec[i]->refcnt);
 }
 
 /* XXX test case */
index a606177..6178777 100644 (file)
 #ifndef ECOLI_STRVEC_
 #define ECOLI_STRVEC_
 
-#include <stdio.h>
-
-struct ec_strvec {
-       size_t len;
-       char **vec;
-};
-
 struct ec_strvec *ec_strvec_new(void);
 int ec_strvec_add(struct ec_strvec *strvec, const char *s);
 struct ec_strvec *ec_strvec_dup(const struct ec_strvec *strvec);
-struct ec_strvec *ec_strvec_ndup(const struct ec_strvec *strvec, size_t len);
+struct ec_strvec *ec_strvec_ndup(const struct ec_strvec *strvec,
+       size_t off, size_t len);
 void ec_strvec_free(struct ec_strvec *strvec);
 size_t ec_strvec_len(const struct ec_strvec *strvec);
 char *ec_strvec_val(const struct ec_strvec *strvec, size_t idx);
-int ec_strvec_slice(struct ec_strvec *strvec, const struct ec_strvec *from,
-       size_t off);
-void ec_strvec_dump(const struct ec_strvec *strvec, FILE *out);
+void ec_strvec_dump(FILE *out, const struct ec_strvec *strvec);
 
 #endif
index fec8492..db28b53 100644 (file)
@@ -50,6 +50,7 @@ struct ec_tk *ec_tk_new(const char *id, const struct ec_tk_ops *ops,
 
        TAILQ_INIT(&tk->children);
        tk->ops = ops;
+       tk->refcnt = 1;
 
        if (id != NULL) {
                tk->id = ec_strdup(id);
@@ -69,9 +70,6 @@ struct ec_tk *ec_tk_new(const char *id, const struct ec_tk_ops *ops,
        return tk;
 
  fail:
-       ec_free(tk->attrs);
-       ec_free(tk->desc);
-       ec_free(tk->id);
        ec_tk_free(tk);
        return NULL;
 }
@@ -81,6 +79,9 @@ void ec_tk_free(struct ec_tk *tk)
        if (tk == NULL)
                return;
 
+       if (--tk->refcnt > 0)
+               return;
+
        if (tk->ops != NULL && tk->ops->free_priv != NULL)
                tk->ops->free_priv(tk);
        ec_free(tk->id);
@@ -89,6 +90,13 @@ void ec_tk_free(struct ec_tk *tk)
        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;
@@ -213,22 +221,27 @@ 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 *s, *id = "None", *typename = "None";
+       const char *id = "None", *typename = "None";
 
        /* XXX enhance */
        for (i = 0; i < indent; i++)
                fprintf(out, " ");
 
-       s = ec_parsed_tk_to_string(parsed_tk);
        if (parsed_tk->tk != NULL) {
                if (parsed_tk->tk->id != NULL)
                        id = parsed_tk->tk->id;
                typename = parsed_tk->tk->ops->typename;
        }
 
-       /* XXX we only display the first token */
-       fprintf(out, "tk_type=%s, id=%s, s=<%s>\n", typename, id, s);
+       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);
@@ -276,14 +289,13 @@ struct ec_parsed_tk *ec_parsed_tk_find_first(struct ec_parsed_tk *parsed_tk,
        return NULL;
 }
 
-/* XXX return NUL if it matches several tokens?
-   or add a parameter to join() the tokens ? */
-const char *ec_parsed_tk_to_string(const struct ec_parsed_tk *parsed_tk)
+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 ec_strvec_val(parsed_tk->strvec, 0);
+       return parsed_tk->strvec;
 }
 
 /* number of parsed tokens */
index 5d1bafb..b417a03 100644 (file)
@@ -63,6 +63,7 @@ struct ec_tk {
        struct ec_keyval *attrs;
        /* XXX ensure parent and child are properly set in all nodes */
        struct ec_tk *parent;
+       unsigned int refcnt;
 
        TAILQ_ENTRY(ec_tk) next;
        struct ec_tk_list children;
@@ -94,6 +95,12 @@ struct ec_parsed_tk {
 };
 
 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);
@@ -114,9 +121,7 @@ struct ec_parsed_tk *ec_tk_parse_tokens(const struct ec_tk *tk,
 
 void ec_parsed_tk_add_child(struct ec_parsed_tk *parsed_tk,
        struct ec_parsed_tk *child);
-void ec_parsed_tk_free_children(struct ec_parsed_tk *parsed_tk);
 void ec_parsed_tk_dump(FILE *out, const struct ec_parsed_tk *parsed_tk);
-void ec_parsed_tk_free(struct ec_parsed_tk *parsed_tk);
 
 struct ec_parsed_tk *ec_parsed_tk_find_first(struct ec_parsed_tk *parsed_tk,
        const char *id);
diff --git a/lib/ecoli_tk_expr.c b/lib/ecoli_tk_expr.c
new file mode 100644 (file)
index 0000000..5625727
--- /dev/null
@@ -0,0 +1,331 @@
+/*
+ * Copyright (c) 2016, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <errno.h>
+
+#include <ecoli_malloc.h>
+#include <ecoli_log.h>
+#include <ecoli_test.h>
+#include <ecoli_strvec.h>
+#include <ecoli_tk.h>
+#include <ecoli_tk_str.h>
+#include <ecoli_tk_seq.h>
+#include <ecoli_tk_many.h>
+#include <ecoli_tk_expr.h>
+
+struct ec_tk_expr {
+       struct ec_tk gen;
+       struct ec_tk *child;
+       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;
+};
+
+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;
+
+       ec_tk_free(tk->child);
+}
+
+static struct ec_tk_ops ec_tk_expr_ops = {
+       .typename = "expr",
+       .parse = ec_tk_expr_parse,
+       .complete = ec_tk_expr_complete,
+       .free_priv = ec_tk_expr_free_priv,
+};
+
+struct ec_tk *ec_tk_expr_new(const char *id)
+{
+       struct ec_tk_expr *tk = NULL;
+       struct ec_tk *gen_tk = NULL;
+
+       gen_tk = ec_tk_new(id, &ec_tk_expr_ops, sizeof(*tk));
+       if (gen_tk == NULL)
+               return NULL;
+       tk = (struct ec_tk_expr *)gen_tk;
+
+       return gen_tk;
+}
+
+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;
+
+       if (val_tk == NULL)
+               return -EINVAL;
+       if (tk->val_tk != NULL)
+               return -EEXIST;
+
+       tk->val_tk = val_tk;
+
+       return 0;
+}
+
+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;
+
+       // XXX check tk type
+
+       /* XXX use assert or check like this ? */
+       if (tk == NULL || op == NULL)
+               return -EINVAL;
+
+       bin_ops = ec_realloc(tk->bin_ops,
+               (tk->bin_ops_len + 1) * sizeof(*tk->bin_ops));
+       if (bin_ops == NULL)
+               return -1;
+
+       tk->bin_ops = bin_ops;
+       bin_ops[tk->bin_ops_len] = op;
+       tk->bin_ops_len++;
+
+       return 0;
+}
+
+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;
+
+       // XXX check tk type
+
+       /* XXX use assert or check like this ? */
+       if (tk == NULL || op == NULL)
+               return -EINVAL;
+
+       pre_ops = ec_realloc(tk->pre_ops,
+               (tk->pre_ops_len + 1) * sizeof(*tk->pre_ops));
+       if (pre_ops == NULL)
+               return -1;
+
+       tk->pre_ops = pre_ops;
+       pre_ops[tk->pre_ops_len] = op;
+       tk->pre_ops_len++;
+
+       return 0;
+}
+
+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;
+
+       // XXX check tk type
+
+       /* XXX use assert or check like this ? */
+       if (tk == NULL || op == NULL)
+               return -EINVAL;
+
+       post_ops = ec_realloc(tk->post_ops,
+               (tk->post_ops_len + 1) * sizeof(*tk->post_ops));
+       if (post_ops == NULL)
+               return -1;
+
+       tk->post_ops = post_ops;
+       post_ops[tk->post_ops_len] = op;
+       tk->post_ops_len++;
+
+       return 0;
+}
+
+int ec_tk_expr_start(struct ec_tk *gen_tk)
+{
+       struct ec_tk_expr *tk = (struct ec_tk_expr *)gen_tk;
+
+       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;
+
+       return 0;
+}
+
+struct ec_tk *ec_tk_expr(const char *id, struct ec_tk *val_tk,
+       const char *bin_ops)
+{
+       struct ec_tk_expr *tk = NULL;
+       struct ec_tk *gen_tk = NULL, *child = NULL, *sub_expr = NULL;
+       char *op = NULL;
+
+       gen_tk = ec_tk_expr_new(id);
+       if (gen_tk == NULL)
+               goto fail;
+       tk = (struct ec_tk_expr *)gen_tk;
+
+       if (bin_ops == NULL || bin_ops[0] == '\0') {
+               child = val_tk;
+       } else {
+               /* recursively create an expr tk without the first operator */
+               sub_expr = ec_tk_expr(NULL,
+                       ec_tk_clone(val_tk),
+                       &bin_ops[1]);
+               if (sub_expr == NULL)
+                       goto fail;
+
+               op = ec_strdup(bin_ops);
+               if (op == NULL)
+                       goto fail;
+               op[1] = '\0';
+
+               /* we match:
+                *   <subexpr> (<op> <subexpr>)*
+                */
+               child = ec_tk_seq_new_list(NULL,
+                       ec_tk_clone(sub_expr),
+                       ec_tk_many_new(NULL,
+                               ec_tk_seq_new_list(NULL,
+                                       ec_tk_str_new(NULL, op),
+                                       ec_tk_clone(sub_expr),
+                                       EC_TK_ENDLIST
+                               ),
+                               0, 0
+                       ),
+                       EC_TK_ENDLIST
+               );
+               ec_free(op);
+               op = NULL;
+
+               /* remove initial reference */
+               ec_tk_free(sub_expr);
+
+               if (child == NULL)
+                       goto fail;
+
+               ec_tk_free(val_tk);
+       }
+
+       tk->child = child;
+
+       return &tk->gen;
+
+fail:
+       ec_free(op);
+       ec_tk_free(child);
+       ec_tk_free(val_tk);
+       ec_tk_free(gen_tk);
+       return NULL;
+}
+
+static int ec_tk_expr_testcase(void)
+{
+       struct ec_tk *tk, *tk2, *val_tk;
+       int ret = 0;
+
+       val_tk = ec_tk_str_new(NULL, "val");
+       tk = ec_tk_seq_new_list(NULL,
+               ec_tk_clone(val_tk),
+               ec_tk_many_new(NULL,
+                       ec_tk_seq_new_list(NULL,
+                               ec_tk_str_new(NULL, "*"),
+                               ec_tk_clone(val_tk),
+                               EC_TK_ENDLIST
+                       ),
+                       0, 0
+               ),
+               EC_TK_ENDLIST
+       );
+       ec_tk_free(val_tk);
+       val_tk = NULL;
+
+       tk2 = ec_tk_seq_new_list(NULL,
+               ec_tk_clone(tk),
+               ec_tk_many_new(NULL,
+                       ec_tk_seq_new_list(NULL,
+                               ec_tk_str_new(NULL, "+"),
+                               ec_tk_clone(tk),
+                               EC_TK_ENDLIST
+                       ),
+                       0, 0
+               ),
+               EC_TK_ENDLIST
+       );
+
+
+//             "/*-+");
+       if (tk2 == NULL) {
+               ec_log(EC_LOG_ERR, "cannot create tk\n");
+               return -1;
+       }
+
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "val", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "val", "*", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 3, "val", "*", "val", EC_TK_ENDLIST);
+
+       ret |= EC_TEST_CHECK_TK_PARSE(tk2, 1, "val", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk2, 1, "val", "*", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk2, 3, "val", "*", "val", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk2, 3, "val", "+", "val", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk2, 5, "val", "*", "val", "+", "val",
+               EC_TK_ENDLIST);
+
+       ec_tk_free(tk);
+       ec_tk_free(tk2);
+
+       tk = ec_tk_expr(NULL, ec_tk_str_new(NULL, "val"), "*+");
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 5, "val", "*", "val", "+", "val",
+               EC_TK_ENDLIST);
+       ec_tk_free(tk);
+
+       return ret;
+}
+
+static struct ec_test ec_tk_expr_test = {
+       .name = "tk_expr",
+       .test = ec_tk_expr_testcase,
+};
+
+EC_REGISTER_TEST(ec_tk_expr_test);
diff --git a/lib/ecoli_tk_expr.h b/lib/ecoli_tk_expr.h
new file mode 100644 (file)
index 0000000..73bce00
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2016, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the University of California, Berkeley nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ECOLI_TK_EXPR_
+#define ECOLI_TK_EXPR_
+
+#include <ecoli_tk.h>
+
+/* XXX remove the _new for all other tokens */
+
+
+struct ec_tk *ec_tk_expr(const char *id, struct ec_tk *val_tk,
+       const char *bin_ops);
+
+struct ec_tk *ec_tk_expr_new(const char *id);
+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);
+
+
+#endif
index 57971ee..5e404e9 100644 (file)
@@ -88,7 +88,7 @@ static struct ec_parsed_tk *ec_tk_int_parse(const struct ec_tk *gen_tk,
        if (parse_llint(tk, str, &val) < 0)
                return parsed_tk;
 
-       match_strvec = ec_strvec_ndup(strvec, 1);
+       match_strvec = ec_strvec_ndup(strvec, 0, 1);
        if (match_strvec == NULL)
                goto fail;
 
@@ -156,12 +156,12 @@ static int ec_tk_int_testcase(void)
        ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "0x101", EC_TK_ENDLIST);
 
        p = ec_tk_parse(tk, "0");
-       s = ec_parsed_tk_to_string(p);
+       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_parsed_tk_to_string(p);
+       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);
@@ -178,7 +178,7 @@ static int ec_tk_int_testcase(void)
        ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "-2", EC_TK_ENDLIST);
 
        p = ec_tk_parse(tk, "10");
-       s = ec_parsed_tk_to_string(p);
+       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);
index 6d0b501..d93a6de 100644 (file)
@@ -53,7 +53,7 @@ static struct ec_parsed_tk *ec_tk_many_parse(const struct ec_tk *gen_tk,
        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;
+       struct ec_strvec *childvec = NULL;
        size_t off = 0, len, count;
 
        parsed_tk = ec_parsed_tk_new();
@@ -61,13 +61,18 @@ static struct ec_parsed_tk *ec_tk_many_parse(const struct ec_tk *gen_tk,
                goto fail;
 
        for (count = 0; tk->max == 0 || count < tk->max; count++) {
-               if (ec_strvec_slice(&childvec, strvec, off) < 0)
+               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);
+               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;
@@ -90,7 +95,7 @@ static struct ec_parsed_tk *ec_tk_many_parse(const struct ec_tk *gen_tk,
                return parsed_tk;
        }
 
-       match_strvec = ec_strvec_ndup(strvec, off);
+       match_strvec = ec_strvec_ndup(strvec, 0, off);
        if (match_strvec == NULL)
                goto fail;
 
@@ -98,7 +103,8 @@ static struct ec_parsed_tk *ec_tk_many_parse(const struct ec_tk *gen_tk,
 
        return parsed_tk;
 
- fail:
+fail:
+       ec_strvec_free(childvec);
        ec_parsed_tk_free(parsed_tk);
        return NULL;
 }
@@ -109,7 +115,7 @@ static struct ec_completed_tk *ec_tk_many_complete(const struct ec_tk *gen_tk,
 {
        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_strvec *childvec;
        struct ec_parsed_tk *parsed_tk;
        size_t len = 0;
        unsigned int i;
@@ -122,20 +128,25 @@ static struct ec_completed_tk *ec_tk_many_complete(const struct ec_tk *gen_tk,
                return completed_tk;
 
        for (i = 0; i < tk->len; i++) {
-               if (ec_strvec_slice(&childvec, strvec, len) < 0)
-                       return completed_tk; /* XXX fail ? */
+               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) {
-                       ec_completed_tk_free(completed_tk);
-                       return NULL;
-               }
+                       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);
+               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;
@@ -148,7 +159,8 @@ static struct ec_completed_tk *ec_tk_many_complete(const struct ec_tk *gen_tk,
        return completed_tk;
 
 fail:
-       /* XXX */
+       ec_strvec_free(childvec);
+       ec_completed_tk_free(completed_tk);
        return NULL;
 }
 #endif
index 2889147..55f3e69 100644 (file)
@@ -157,10 +157,13 @@ struct ec_tk *ec_tk_or_new_list(const char *id, ...)
        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 (child == NULL || ec_tk_or_add(&tk->gen, child) < 0)
+               /* on error, don't quit the loop to avoid leaks */
+               if (fail == 1 || child == NULL ||
+                               ec_tk_or_add(&tk->gen, child) < 0) {
                        fail = 1;
+                       ec_tk_free(child);
+               }
        }
 
        if (fail == 1)
index e606a09..6feb17d 100644 (file)
@@ -52,7 +52,7 @@ static struct ec_parsed_tk *ec_tk_seq_parse(const struct ec_tk *gen_tk,
        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;
+       struct ec_strvec *childvec = NULL;
        size_t len = 0;
        unsigned int i;
 
@@ -61,12 +61,18 @@ static struct ec_parsed_tk *ec_tk_seq_parse(const struct ec_tk *gen_tk,
                goto fail;
 
        for (i = 0; i < tk->len; i++) {
-               if (ec_strvec_slice(&childvec, strvec, len) < 0)
+               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);
+               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);
                        ec_parsed_tk_free_children(parsed_tk);
@@ -77,7 +83,7 @@ static struct ec_parsed_tk *ec_tk_seq_parse(const struct ec_tk *gen_tk,
                len += ec_parsed_tk_len(child_parsed_tk);
        }
 
-       match_strvec = ec_strvec_ndup(strvec, len);
+       match_strvec = ec_strvec_ndup(strvec, 0, len);
        if (match_strvec == NULL)
                goto fail;
 
@@ -85,7 +91,8 @@ static struct ec_parsed_tk *ec_tk_seq_parse(const struct ec_tk *gen_tk,
 
        return parsed_tk;
 
- fail:
+fail:
+       ec_strvec_free(childvec);
        ec_parsed_tk_free(parsed_tk);
        return NULL;
 }
@@ -95,7 +102,7 @@ static struct ec_completed_tk *ec_tk_seq_complete(const struct ec_tk *gen_tk,
 {
        struct ec_tk_seq *tk = (struct ec_tk_seq *)gen_tk;
        struct ec_completed_tk *completed_tk, *child_completed_tk;
-       struct ec_strvec childvec;
+       struct ec_strvec *childvec = NULL;
        struct ec_parsed_tk *parsed_tk;
        size_t len = 0;
        unsigned int i;
@@ -108,20 +115,25 @@ static struct ec_completed_tk *ec_tk_seq_complete(const struct ec_tk *gen_tk,
                return completed_tk;
 
        for (i = 0; i < tk->len && len < ec_strvec_len(strvec); i++) {
-               if (ec_strvec_slice(&childvec, strvec, len) < 0)
+               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) {
-                       ec_completed_tk_free(completed_tk);
-                       return NULL;
-               }
+                       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);
+               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;
@@ -134,7 +146,8 @@ static struct ec_completed_tk *ec_tk_seq_complete(const struct ec_tk *gen_tk,
        return completed_tk;
 
 fail:
-       /* XXX */
+       ec_strvec_free(childvec);
+       ec_completed_tk_free(completed_tk);
        return NULL;
 }
 
@@ -180,15 +193,18 @@ struct ec_tk *ec_tk_seq_new_list(const char *id, ...)
 
        tk = (struct ec_tk_seq *)ec_tk_seq_new(id);
        if (tk == NULL)
-               goto fail;
+               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 (child == NULL || ec_tk_seq_add(&tk->gen, child) < 0)
+               /* 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)
@@ -219,10 +235,10 @@ int ec_tk_seq_add(struct ec_tk *gen_tk, struct ec_tk *child)
 
        tk->table = table;
        table[tk->len] = child;
-       tk->len ++;
+       tk->len++;
 
        child->parent = gen_tk;
-       TAILQ_INSERT_TAIL(&gen_tk->children, child, next);
+       TAILQ_INSERT_TAIL(&gen_tk->children, child, next); // XXX really needed?
 
        return 0;
 }
index ba6d20a..0b53968 100644 (file)
@@ -267,7 +267,7 @@ static struct ec_parsed_tk *ec_tk_shlex_parse(const struct ec_tk *gen_tk,
        new_vec = NULL;
 
        ec_parsed_tk_add_child(parsed_tk, child_parsed_tk);
-       match_strvec = ec_strvec_ndup(strvec, 1);
+       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);
index 8ec1650..33f33d8 100644 (file)
@@ -62,7 +62,7 @@ static struct ec_parsed_tk *ec_tk_space_parse(const struct ec_tk *gen_tk,
        if (len == 0 || len != strlen(str))
                return parsed_tk;
 
-       match_strvec = ec_strvec_ndup(strvec, 1);
+       match_strvec = ec_strvec_ndup(strvec, 0, 1);
        if (match_strvec == NULL)
                goto fail;
 
index 6c650d3..f3999ff 100644 (file)
@@ -61,7 +61,7 @@ static struct ec_parsed_tk *ec_tk_str_parse(const struct ec_tk *gen_tk,
        if (strcmp(str, tk->string) != 0)
                return parsed_tk;
 
-       match_strvec = ec_strvec_ndup(strvec, 1);
+       match_strvec = ec_strvec_ndup(strvec, 0, 1);
        if (match_strvec == NULL)
                goto fail;
 
@@ -138,25 +138,27 @@ static const struct ec_tk_ops ec_tk_str_ops = {
 
 struct ec_tk *ec_tk_str_new(const char *id, const char *str)
 {
+       struct ec_tk *gen_tk = NULL;
        struct ec_tk_str *tk = NULL;
        char *s = NULL;
 
-       tk = (struct ec_tk_str *)ec_tk_new(id, &ec_tk_str_ops, sizeof(*tk));
-       if (tk == NULL)
+       gen_tk = ec_tk_new(id, &ec_tk_str_ops, sizeof(*tk));
+       if (gen_tk == NULL)
                goto fail;
 
        s = ec_strdup(str);
        if (s == NULL)
                goto fail;
 
+       tk = (struct ec_tk_str *)gen_tk;
        tk->string = s;
        tk->len = strlen(s);
 
-       return &tk->gen;
+       return gen_tk;
 
 fail:
        ec_free(s);
-       ec_free(tk);
+       ec_tk_free(gen_tk);
        return NULL;
 }
 
index d3d35b3..deffea3 100644 (file)
@@ -29,6 +29,7 @@
 #include <stdio.h>
 #include <assert.h>
 #include <getopt.h>
+#include <limits.h>
 #include <execinfo.h>
 
 #include <ecoli_log.h>
 
 static int log_level = EC_LOG_INFO;
 static int alloc_fail_proba = 0;
+static int seed = 0;
 
 static const char ec_short_options[] =
        "h"  /* help */
        "l:" /* log-level */
        "r:" /* random-alloc-fail */
+       "s:" /* seed */
        ;
 
 #define EC_OPT_HELP "help"
 #define EC_OPT_LOG_LEVEL "log-level"
 #define EC_OPT_RANDOM_ALLOC_FAIL "random-alloc-fail"
+#define EC_OPT_SEED "seed"
 
 static const struct option ec_long_options[] = {
        {EC_OPT_HELP, 1, NULL, 'h'},
        {EC_OPT_LOG_LEVEL, 1, NULL, 'l'},
        {EC_OPT_RANDOM_ALLOC_FAIL, 1, NULL, 'r'},
+       {EC_OPT_SEED, 1, NULL, 's'},
        {NULL, 0, NULL, 0}
 };
 
@@ -72,6 +77,9 @@ static void usage(const char *prgname)
                "      Cause malloc to fail randomly. This helps to debug\n"
                "      leaks or crashes in error cases. The probability is\n"
                "      between 0 and 100.\n"
+               "  -s <seed>\n"
+               "  --seed=<seed>\n"
+               "      Seeds the random number generator. Default is 0.\n"
                , prgname);
 }
 
@@ -123,6 +131,14 @@ static void parse_args(int argc, char **argv)
                        }
                        break;
 
+               case 's': /* seed */
+                       if (parse_int(optarg, 0, INT_MAX, &seed, 10) < 0) {
+                               printf("Invalid seed value\n");
+                               usage(argv[0]);
+                               exit(1);
+                       }
+                       break;
+
                default:
                        usage(argv[0]);
                        exit(1);
@@ -341,6 +357,7 @@ int main(int argc, char **argv)
        int ret, leaks;
 
        parse_args(argc, argv);
+       srandom(seed);
 
        ec_log_register(debug_log, NULL);