--- /dev/null
+/*
+ * Copyright (c) 2016, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the University of California, Berkeley nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <errno.h>
+
+#include <ecoli_malloc.h>
+#include <ecoli_log.h>
+#include <ecoli_test.h>
+#include <ecoli_strvec.h>
+#include <ecoli_tk.h>
+#include <ecoli_tk_str.h>
+#include <ecoli_tk_option.h>
+#include <ecoli_tk_bypass.h>
+
+struct ec_tk_bypass {
+ struct ec_tk gen;
+ struct ec_tk *child;
+};
+
+static struct ec_parsed_tk *ec_tk_bypass_parse(const struct ec_tk *gen_tk,
+ const struct ec_strvec *strvec)
+{
+ struct ec_tk_bypass *tk = (struct ec_tk_bypass *)gen_tk;
+
+ return ec_tk_parse_tokens(tk->child, strvec);
+}
+
+static struct ec_completed_tk *ec_tk_bypass_complete(const struct ec_tk *gen_tk,
+ const struct ec_strvec *strvec)
+{
+ struct ec_tk_bypass *tk = (struct ec_tk_bypass *)gen_tk;
+
+ return ec_tk_complete_tokens(tk->child, strvec);
+}
+
+static void ec_tk_bypass_free_priv(struct ec_tk *gen_tk)
+{
+ struct ec_tk_bypass *tk = (struct ec_tk_bypass *)gen_tk;
+
+ ec_tk_free(tk->child);
+}
+
+static struct ec_tk_ops ec_tk_bypass_ops = {
+ .typename = "bypass",
+ .parse = ec_tk_bypass_parse,
+ .complete = ec_tk_bypass_complete,
+ .free_priv = ec_tk_bypass_free_priv,
+};
+
+struct ec_tk *ec_tk_bypass_new(const char *id)
+{
+ struct ec_tk_bypass *tk = NULL;
+
+ tk = (struct ec_tk_bypass *)ec_tk_new(id,
+ &ec_tk_bypass_ops, sizeof(*tk));
+ if (tk == NULL)
+ return NULL;
+
+ tk->child = NULL;
+
+ return &tk->gen;
+}
+
+int ec_tk_bypass_set(struct ec_tk *gen_tk, struct ec_tk *child)
+{
+ struct ec_tk_bypass *tk = (struct ec_tk_bypass *)gen_tk;
+
+ // XXX check tk type
+
+ assert(tk != NULL);
+
+ if (child == NULL)
+ return -EINVAL;
+
+ if (gen_tk->flags & EC_TK_F_INITIALIZED) {
+ ec_tk_free(child);
+ return -EPERM;
+ }
+
+ 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_bypass_pop(struct ec_tk *gen_tk)
+{
+ struct ec_tk_bypass *tk = (struct ec_tk_bypass *)gen_tk;
+ struct ec_tk *child;
+
+ if (gen_tk->flags & EC_TK_F_INITIALIZED)
+ return NULL;
+
+ child = tk->child;
+ tk->child = NULL;
+
+ return child;
+}
+
+int ec_tk_bypass_start(struct ec_tk *gen_tk)
+{
+ struct ec_tk_bypass *tk = (struct ec_tk_bypass *)gen_tk;
+
+ if (gen_tk->flags & EC_TK_F_INITIALIZED)
+ return -EPERM;
+ if (tk->child == NULL)
+ return -EINVAL;
+
+ gen_tk->flags |= EC_TK_F_INITIALIZED;
+
+ return 0;
+}
+
+int ec_tk_bypass_stop(struct ec_tk *gen_tk)
+{
+ if (!(gen_tk->flags & EC_TK_F_INITIALIZED))
+ return -EPERM;
+
+ gen_tk->flags &= (~EC_TK_F_INITIALIZED);
+
+ return 0;
+}
+
+struct ec_tk *ec_tk_bypass(const char *id, struct ec_tk *child)
+{
+ struct ec_tk *gen_tk = NULL;
+
+ if (child == NULL)
+ return NULL;
+
+ gen_tk = ec_tk_bypass_new(id);
+ if (gen_tk == NULL) {
+ ec_tk_free(child);
+ return NULL;
+ }
+
+ ec_tk_bypass_set(gen_tk, child);
+ ec_tk_bypass_start(gen_tk);
+
+ return gen_tk;
+}
+
+static int ec_tk_bypass_testcase(void)
+{
+ return 0;
+}
+
+static struct ec_test ec_tk_bypass_test = {
+ .name = "tk_bypass",
+ .test = ec_tk_bypass_testcase,
+};
+
+EC_REGISTER_TEST(ec_tk_bypass_test);
--- /dev/null
+/*
+ * Copyright (c) 2016, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the University of California, Berkeley nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ECOLI_TK_BYPASS_
+#define ECOLI_TK_BYPASS_
+
+#include <ecoli_tk.h>
+
+/// XXX rename in loop ?
+// XXX + provide the helper to free ?
+
+/* a tk that just behaves like its child
+ * useful to create cyclic graphs of tokens:
+ * creating a loop (with clones) result in something that is not
+ * freeable, due to reference counters
+ * bypass node can solve the issue: before freeing the graph,
+ * the loop can be cut, falling back to a valid tree that can
+ * be freed.
+ *
+ * Example:
+ * seq = seq()
+ * i = int()
+ * seq_add(seq, i)
+ * seq_add(seq, clone(seq))
+ * FAIL, cannot be freed
+ *
+ * seq = seq()
+ * bypass = bypass(clone(seq))
+ * i = int()
+ * seq_add(seq, i)
+ * seq_add(seq, bypass)
+ *
+ * TO FREE:
+ * seq2 = bypass_del(bypass) // breaks the loop (seq2 == seq)
+ * free(bypass)
+ * free(seq2)
+ * free(seq)
+ */
+
+struct ec_tk *ec_tk_bypass(const char *id, struct ec_tk *child);
+
+struct ec_tk *ec_tk_bypass_new(const char *id);
+
+/* child is consumed */
+/* all token given in the list will be freed when freeing this one */
+int ec_tk_bypass_set(struct ec_tk *tk, struct ec_tk *child);
+
+struct ec_tk *ec_tk_bypass_pop(struct ec_tk *gen_tk);
+
+#endif
#include <ecoli_tk_many.h>
#include <ecoli_tk_or.h>
#include <ecoli_tk_expr.h>
+#include <ecoli_tk_bypass.h>
struct ec_tk_expr {
struct ec_tk gen;
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_tk_free(tk->child);
+ ec_tk_free(tk->val_tk);
+
+ for (i = 0; i < tk->bin_ops_len; i++)
+ ec_tk_free(tk->bin_ops[i]);
+ for (i = 0; i < tk->pre_ops_len; i++)
+ ec_tk_free(tk->pre_ops[i]);
+ for (i = 0; i < tk->post_ops_len; i++)
+ ec_tk_free(tk->post_ops[i]);
+ for (i = 0; i < tk->paren_len; i++) {
+ ec_tk_free(tk->open_ops[i]);
+ ec_tk_free(tk->close_ops[i]);
+ }
}
static struct ec_tk_ops ec_tk_expr_ops = {
{
struct ec_tk_expr *tk = (struct ec_tk_expr *)gen_tk;
struct ec_tk *term = NULL, *prev = NULL, *expr = NULL,
- *val = NULL, *pre_op = NULL, *post_op = NULL,
+ *pre_op = NULL, *post_op = NULL,
*post = NULL, *final = NULL, *next = NULL;
unsigned int i;
/* create the object, we will initialize it later: this is
* needed because we have a circular dependency */
- expr = ec_tk_seq_new("expr");
-
- val = ec_tk_int("val", 0, UCHAR_MAX, 0);
+ expr = ec_tk_bypass_new("expr");
/* prefix unary operators */
pre_op = ec_tk_or_new("pre-op");
goto fail;
term = ec_tk_or_new("term");
- if (ec_tk_or_add(term, ec_tk_clone(val)) < 0)
+ if (ec_tk_or_add(term, ec_tk_clone(tk->val_tk)) < 0)
goto fail;
if (ec_tk_or_add(term,
ec_tk_seq(NULL,
EC_TK_ENDLIST
);
- tk->child = final;
+ /* free the initial references */
+ ec_tk_free(pre_op);
+ pre_op = NULL;
+ ec_tk_free(post_op);
+ post_op = NULL;
+ ec_tk_free(term);
+ term = NULL;
+ ec_tk_free(post);
+ post = NULL;
+
+ if (ec_tk_bypass_set(expr, ec_tk_clone(final)) < 0)
+ goto fail;
+
+ ec_tk_free(final);
+ final = NULL;
+
+ tk->child = expr;
gen_tk->flags |= EC_TK_F_INITIALIZED;
return 0;
fail:
- ec_tk_free(val);
+ ec_tk_free(term);
+ ec_tk_free(expr);
ec_tk_free(pre_op);
ec_tk_free(post_op);
- ec_tk_free(term);
ec_tk_free(post);
+ ec_tk_free(final);
+
return -1;
}
-static int ec_tk_expr_testcase(void)
+static int ec_tk_expr_testcase_manual(void)
{
- struct ec_tk *term, *factor, *expr, *tk, *val,
- *pre_op, *post_op, *post, *final;
+ struct ec_tk *term = NULL, *factor = NULL, *expr = NULL, *val = NULL,
+ *pre_op = NULL, *post_op = NULL, *post = NULL, *final = NULL;
int ret = 0;
// XXX check all APIs: pointers are "moved", they are freed by
/* create the object, we will initialize it later: this is
* needed because we have a circular dependency */
- expr = ec_tk_seq_new("expr");
-
- val = ec_tk_int("val", 0, UCHAR_MAX, 0);
+ ec_log(EC_LOG_INFO, "%d\n", __LINE__);
+ expr = ec_tk_bypass_new("expr");
+ ec_log(EC_LOG_INFO, "%d\n", __LINE__);
/* reverse bits */
pre_op = ec_tk_or("pre-op",
ec_tk_str(NULL, "~"),
EC_TK_ENDLIST
);
+ ec_log(EC_LOG_INFO, "%d\n", __LINE__);
/* factorial */
post_op = ec_tk_or("post-op",
ec_tk_str(NULL, "!"),
EC_TK_ENDLIST
);
+ ec_log(EC_LOG_INFO, "%d\n", __LINE__);
+ val = ec_tk_int("val", 0, UCHAR_MAX, 0);
+ ec_log(EC_LOG_INFO, "%d\n", __LINE__);
term = ec_tk_or("term",
- ec_tk_clone(val),
+ val,
ec_tk_seq(NULL,
ec_tk_str(NULL, "("),
ec_tk_clone(expr),
),
EC_TK_ENDLIST
);
+ val = NULL;
+
+ ec_log(EC_LOG_INFO, "%d\n", __LINE__);
factor = ec_tk_seq("factor",
ec_tk_clone(term),
EC_TK_ENDLIST
);
+ ec_log(EC_LOG_INFO, "%d\n", __LINE__);
final = ec_tk_seq("final",
ec_tk_clone(post),
ec_tk_many_new(NULL, ec_tk_clone(post_op), 0, 0),
EC_TK_ENDLIST
);
+ ec_log(EC_LOG_INFO, "%d\n", __LINE__);
/* free the initial references */
- ec_tk_free(val);
ec_tk_free(pre_op);
+ pre_op = NULL;
ec_tk_free(post_op);
+ post_op = NULL;
ec_tk_free(term);
+ term = NULL;
ec_tk_free(factor);
+ factor = NULL;
ec_tk_free(post);
+ post = NULL;
- if (ec_tk_seq_add(expr, ec_tk_clone(final)) < 0) {
- ec_tk_free(final);
- ec_tk_free(expr);
- return -1;
- }
+ ec_log(EC_LOG_INFO, "%d\n", __LINE__);
+ if (ec_tk_bypass_set(expr, ec_tk_clone(final)) < 0)
+ goto fail;
+ ec_log(EC_LOG_INFO, "%d\n", __LINE__);
ec_tk_free(final);
+ final = NULL;
+
+ ec_log(EC_LOG_INFO, "%d\n", __LINE__);
ret |= EC_TEST_CHECK_TK_PARSE(expr, 1, "1", EC_TK_ENDLIST);
ret |= EC_TEST_CHECK_TK_PARSE(expr, 1, "1", "*", EC_TK_ENDLIST);
ret |= EC_TEST_CHECK_TK_PARSE(expr, 4, "1", "+", "~", "1",
EC_TK_ENDLIST);
+ final = ec_tk_bypass_pop(expr);
ec_tk_free(expr);
+ ec_tk_free(final);
+
+ return ret;
+
+fail:
+ ec_log(EC_LOG_INFO, "%d term %p\n", __LINE__, term);
+ ec_tk_free(term);
+ ec_log(EC_LOG_INFO, "%d factor %p\n", __LINE__, factor);
+ ec_tk_free(factor);
+ ec_log(EC_LOG_INFO, "%d expr %p\n", __LINE__, expr);
+ ec_tk_free(expr);
+ ec_log(EC_LOG_INFO, "%d val %p\n", __LINE__, val);
+ ec_tk_free(val);
+ ec_log(EC_LOG_INFO, "%d pre_op %p\n", __LINE__, pre_op);
+ ec_tk_free(pre_op);
+ ec_log(EC_LOG_INFO, "%d post_op %p\n", __LINE__, post_op);
+ ec_tk_free(post_op);
+ ec_log(EC_LOG_INFO, "%d post %p\n", __LINE__, post);
+ ec_tk_free(post);
+ ec_log(EC_LOG_INFO, "%d final %p\n", __LINE__, final);
+ ec_tk_free(final);
+ ec_log(EC_LOG_INFO, "%d\n", __LINE__);
+ return 0;
+}
+
+static int ec_tk_expr_testcase(void)
+{
+ struct ec_tk *tk;
+ int ret;
+
+ ret = ec_tk_expr_testcase_manual();
+ if (ret < 0)
+ return ret;
tk = ec_tk_expr_new(NULL);
ec_tk_expr_set_val_tk(tk, ec_tk_int(NULL, 0, UCHAR_MAX, 0));
ec_tk_expr_add_pre_op(tk, ec_tk_str(NULL, "!"));
ec_tk_expr_add_parenthesis(tk, ec_tk_str(NULL, "("),
ec_tk_str(NULL, ")"));
- ec_tk_expr_start(tk);
+ ec_tk_expr_start(tk); // XXX start -> commit ?
- ret |= EC_TEST_CHECK_TK_PARSE(expr, 1, "1", EC_TK_ENDLIST);
- ret |= EC_TEST_CHECK_TK_PARSE(expr, 1, "1", "*", EC_TK_ENDLIST);
- ret |= EC_TEST_CHECK_TK_PARSE(expr, 3, "1", "*", "1", EC_TK_ENDLIST);
- ret |= EC_TEST_CHECK_TK_PARSE(expr, 3, "1", "+", "1", EC_TK_ENDLIST);
- ret |= EC_TEST_CHECK_TK_PARSE(expr, 5, "1", "*", "1", "+", "1",
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "1", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "1", "*", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 3, "1", "*", "1", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 3, "1", "+", "1", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 5, "1", "*", "1", "+", "1",
EC_TK_ENDLIST);
ret |= EC_TEST_CHECK_TK_PARSE(
- expr, 10, "~", "(", "1", "*", "(", "1", "+", "1", ")", ")",
+ tk, 10, "~", "(", "1", "*", "(", "1", "+", "1", ")", ")",
EC_TK_ENDLIST);
- ret |= EC_TEST_CHECK_TK_PARSE(expr, 4, "1", "+", "~", "1",
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 4, "1", "+", "~", "1",
EC_TK_ENDLIST);
ec_tk_free(tk);