srcs += ecoli_strvec.c
srcs += ecoli_test.c
srcs += ecoli_tk.c
+srcs += ecoli_tk_cmd.c
srcs += ecoli_tk_empty.c
srcs += ecoli_tk_expr.c
srcs += ecoli_tk_expr_test.c
srcs += ecoli_tk_many.c
srcs += ecoli_tk_option.c
srcs += ecoli_tk_or.c
+srcs += ecoli_tk_re.c
srcs += ecoli_tk_re_lex.c
srcs += ecoli_tk_seq.c
srcs += ecoli_tk_sh_lex.c
srcs += ecoli_tk_space.c
srcs += ecoli_tk_str.c
+srcs += ecoli_tk_subset.c
srcs += ecoli_tk_weakref.c
shlib-y-$(O)libecoli.so := $(srcs)
.test = ec_keyval_testcase,
};
-EC_REGISTER_TEST(ec_keyval_test);
+EC_TEST_REGISTER(ec_keyval_test);
void ec_test_register(struct ec_test *test)
{
TAILQ_INSERT_TAIL(&test_list, test, next);
+ // XXX check if already exist, like for type
}
int ec_test_check_tk_parse(struct ec_tk *tk, int expected, ...)
return ret;
}
-int ec_test_check_tk_complete(const struct ec_tk *tk, ...)
+int ec_test_check_tk_complete(struct ec_tk *tk, ...)
{
struct ec_completed_tk *c = NULL;
struct ec_completed_tk_elt *elt;
return ret;
}
-int ec_test_all(void)
+static int launch_test(const char *name)
{
struct ec_test *test;
int ret = 0;
+ unsigned int count = 0;
TAILQ_FOREACH(test, &test_list, next) {
+ if (name != NULL && strcmp(name, test->name))
+ continue;
+
ec_log(EC_LOG_INFO, "== starting test %-20s\n", test->name);
+ count++;
if (test->test() == 0) {
ec_log(EC_LOG_INFO, "== test %-20s success\n",
test->name);
}
}
+ if (name != NULL && count == 0)
+ ec_log(EC_LOG_WARNING, "== test %s not found\n", name);
+
return ret;
}
+
+int ec_test_all(void)
+{
+ return launch_test(NULL);
+}
+
+int ec_test_one(const char *name)
+{
+ return launch_test(name);
+}
#include <ecoli_log.h>
#include <ecoli_tk.h>
-#define EC_REGISTER_TEST(t) \
- static void ec_init_##t(void); \
- static void __attribute__((constructor, used)) ec_init_##t(void) \
+// XXX check if already exists?
+#define EC_TEST_REGISTER(t) \
+ static void ec_test_init_##t(void); \
+ static void __attribute__((constructor, used)) \
+ ec_test_init_##t(void) \
{ \
ec_test_register(&t); \
}
void ec_test_register(struct ec_test *test);
int ec_test_all(void);
+int ec_test_one(const char *name);
/* expected == -1 means no match */
int ec_test_check_tk_parse(struct ec_tk *tk, int expected, ...);
ret_; \
})
-int ec_test_check_tk_complete(const struct ec_tk *tk, ...);
+int ec_test_check_tk_complete(struct ec_tk *tk, ...);
#define EC_TEST_CHECK_TK_COMPLETE(tk, args...) ({ \
int ret_ = ec_test_check_tk_complete(tk, args); \
#include <ecoli_log.h>
#include <ecoli_tk.h>
-struct ec_tk *ec_tk_new(const char *id, const struct ec_tk_ops *ops,
+static struct ec_tk_type_list tk_type_list =
+ TAILQ_HEAD_INITIALIZER(tk_type_list);
+
+struct ec_tk_type *ec_tk_type_lookup(const char *name)
+{
+ struct ec_tk_type *type;
+
+ TAILQ_FOREACH(type, &tk_type_list, next) {
+ if (!strcmp(name, type->name))
+ return type;
+ }
+
+ return NULL;
+}
+
+int ec_tk_type_register(struct ec_tk_type *type)
+{
+ if (ec_tk_type_lookup(type->name) != NULL)
+ return -EEXIST;
+ TAILQ_INSERT_TAIL(&tk_type_list, type, next);
+
+ return 0;
+}
+
+void ec_tk_type_dump(FILE *out)
+{
+ struct ec_tk_type *type;
+
+ TAILQ_FOREACH(type, &tk_type_list, next)
+ fprintf(out, "%s\n", type->name);
+}
+
+struct ec_tk *ec_tk_new(const char *id, const struct ec_tk_type *type,
size_t size)
{
struct ec_tk *tk = NULL;
assert(size >= sizeof(*tk));
- ec_log(EC_LOG_DEBUG, "create node type=%s id=%s\n", ops->typename, id);
+ ec_log(EC_LOG_DEBUG, "create node type=%s id=%s\n", type->name, id);
tk = ec_calloc(1, size);
if (tk == NULL)
goto fail;
TAILQ_INIT(&tk->children);
- tk->ops = ops;
+ tk->type = type;
tk->refcnt = 1;
if (id != NULL) {
goto fail;
}
- snprintf(buf, sizeof(buf), "<%s>", ops->typename);
+ snprintf(buf, sizeof(buf), "<%s>", type->name);
tk->desc = ec_strdup(buf); // XXX ec_asprintf ?
if (tk->desc == NULL)
goto fail;
if (--tk->refcnt > 0)
return;
- if (tk->ops != NULL && tk->ops->free_priv != NULL)
- tk->ops->free_priv(tk);
+ if (tk->type != NULL && tk->type->free_priv != NULL)
+ tk->type->free_priv(tk);
ec_free(tk->id);
ec_free(tk->desc);
ec_free(tk->attrs);
return tk->parent;
}
+static void __ec_tk_dump(FILE *out,
+ const struct ec_tk *tk, size_t indent)
+{
+ struct ec_tk *child;
+ size_t i;
+ const char *id = "None", *typename = "None";
+
+ if (tk->id != NULL)
+ id = tk->id;
+ typename = tk->type->name;
+
+ /* XXX enhance */
+ for (i = 0; i < indent; i++) {
+ if (i % 2)
+ fprintf(out, " ");
+ else
+ fprintf(out, "|");
+ }
+
+ fprintf(out, "tk_type=%s id=%s\n", typename, id);
+ TAILQ_FOREACH(child, &tk->children, next)
+ __ec_tk_dump(out, child, indent + 2);
+}
+
+void ec_tk_dump(FILE *out, const struct ec_tk *tk)
+{
+ fprintf(out, "------------------- tk dump:\n"); //XXX
+
+ if (tk == NULL) {
+ fprintf(out, "tk is NULL\n");
+ return;
+ }
+
+ __ec_tk_dump(out, tk, 0);
+}
+
struct ec_parsed_tk *ec_tk_parse(struct ec_tk *tk, const char *str)
{
struct ec_strvec *strvec = NULL;
int ret;
/* build the node if required */
- if (tk->ops->build != NULL) {
+ if (tk->type->build != NULL) {
if ((tk->flags & EC_TK_F_BUILT) == 0) {
- ret = tk->ops->build(tk);
+ ret = tk->type->build(tk);
if (ret < 0) {
errno = -ret;
return NULL;
}
tk->flags |= EC_TK_F_BUILT;
- if (tk->ops->parse == NULL) {
+ if (tk->type->parse == NULL) {
errno = ENOTSUP;
return NULL;
}
- parsed_tk = tk->ops->parse(tk, strvec);
+ parsed_tk = tk->type->parse(tk, strvec);
return parsed_tk;
}
if (parsed_tk->tk != NULL) {
if (parsed_tk->tk->id != NULL)
id = parsed_tk->tk->id;
- typename = parsed_tk->tk->ops->typename;
+ typename = parsed_tk->tk->type->name;
}
- /* XXX remove this debug hack */
- if (!strcmp(typename, "str") || !strcmp(typename, "int"))
- fprintf(out, ">>> ");
- else
- fprintf(out, " ");
-
/* XXX enhance */
for (i = 0; i < indent; i++) {
if (i % 2)
void ec_parsed_tk_dump(FILE *out, const struct ec_parsed_tk *parsed_tk)
{
- fprintf(out, "------------------- dump:\n");
+ fprintf(out, "------------------- parsed_tk dump:\n"); //XXX
if (parsed_tk == NULL) {
fprintf(out, "parsed_tk is NULL, error in parse\n");
TAILQ_INSERT_TAIL(&parsed_tk->children, child, next);
}
+void ec_parsed_tk_del_child(struct ec_parsed_tk *parsed_tk,
+ struct ec_parsed_tk *child)
+{
+ TAILQ_REMOVE(&parsed_tk->children, child, next);
+}
+
struct ec_parsed_tk *ec_parsed_tk_find_first(struct ec_parsed_tk *parsed_tk,
const char *id)
{
* suggestion: tk->op() is internal, user calls the function
* other idea: have 2 functions
*/
-struct ec_completed_tk *ec_tk_complete(const struct ec_tk *tk,
+struct ec_completed_tk *ec_tk_complete(struct ec_tk *tk,
const char *str)
{
struct ec_strvec *strvec = NULL;
return completed_tk;
}
-struct ec_completed_tk *ec_tk_complete_tokens(const struct ec_tk *tk,
+struct ec_completed_tk *ec_tk_complete_tokens(struct ec_tk *tk,
const struct ec_strvec *strvec)
{
- return tk->ops->complete(tk, strvec);
+ int ret;
+
+ /* build the node if required */
+ if (tk->type->build != NULL) {
+ if ((tk->flags & EC_TK_F_BUILT) == 0) {
+ ret = tk->type->build(tk);
+ if (ret < 0) {
+ errno = -ret;
+ return NULL;
+ }
+ }
+ }
+ tk->flags |= EC_TK_F_BUILT;
+
+ if (tk->type->complete == NULL) {
+ errno = ENOTSUP;
+ return NULL;
+ }
+
+ return tk->type->complete(tk, strvec);
}
/* count the number of identical chars at the beginning of 2 strings */
TAILQ_FOREACH(elt, &completed_tk->elts, next) {
fprintf(out, "add=<%s>, tk=%p, tk_type=%s\n",
- elt->add, elt->tk, elt->tk->ops->typename);
+ elt->add, elt->tk, elt->tk->type->name);
}
}
const char *ec_tk_desc(const struct ec_tk *tk)
{
- if (tk->ops->desc != NULL)
- return tk->ops->desc(tk);
+ if (tk->type->desc != NULL)
+ return tk->type->desc(tk);
return tk->desc;
}
typedef const char * (*ec_tk_desc_t)(const struct ec_tk *);
typedef void (*ec_tk_free_priv_t)(struct ec_tk *);
-struct ec_tk_ops {
- const char *typename;
+#define EC_TK_TYPE_REGISTER(t) \
+ static void ec_tk_init_##t(void); \
+ static void __attribute__((constructor, used)) \
+ ec_tk_init_##t(void) \
+ { \
+ if (ec_tk_type_register(&t) < 0) \
+ fprintf(stderr, "cannot register %s\n", t.name); \
+ }
+
+TAILQ_HEAD(ec_tk_type_list, ec_tk_type);
+
+/**
+ * A structure describing a tk type.
+ */
+struct ec_tk_type {
+ TAILQ_ENTRY(ec_tk_type) next; /**< Next in list. */
+ const char *name; /**< Tk type name. */
ec_tk_build_t build; /* (re)build the node, called by generic parse */
ec_tk_parse_t parse;
ec_tk_complete_t complete;
ec_tk_free_priv_t free_priv;
};
+/**
+ * Register a token type.
+ *
+ * @param type
+ * A pointer to a ec_test structure describing the test
+ * to be registered.
+ * @return
+ * 0 on success, negative value on error.
+ */
+int ec_tk_type_register(struct ec_tk_type *type);
+
+/**
+ * Lookup token type by name
+ *
+ * @param name
+ * The name of the token type to search.
+ * @return
+ * The token type if found, or NULL on error.
+ */
+struct ec_tk_type *ec_tk_type_lookup(const char *name);
+
+/**
+ * Dump registered log types
+ */
+void ec_tk_type_dump(FILE *out);
+
TAILQ_HEAD(ec_tk_list, ec_tk);
struct ec_tk {
- const struct ec_tk_ops *ops;
+ const struct ec_tk_type *type;
char *id;
char *desc;
struct ec_keyval *attrs;
struct ec_tk_list children;
};
-struct ec_tk *ec_tk_new(const char *id, const struct ec_tk_ops *ops,
+struct ec_tk *ec_tk_new(const char *id, const struct ec_tk_type *type,
size_t priv_size);
void ec_tk_free(struct ec_tk *tk);
struct ec_keyval *ec_tk_attrs(const struct ec_tk *tk);
struct ec_tk *ec_tk_parent(const struct ec_tk *tk);
const char *ec_tk_id(const struct ec_tk *tk);
+const char *ec_tk_desc(const struct ec_tk *tk);
+void ec_tk_dump(FILE *out, const struct ec_tk *tk);
struct ec_tk *ec_tk_find(struct ec_tk *tk, const char *id);
/* XXX split this file ? */
void ec_parsed_tk_add_child(struct ec_parsed_tk *parsed_tk,
struct ec_parsed_tk *child);
+void ec_parsed_tk_del_child(struct ec_parsed_tk *parsed_tk,
+ struct ec_parsed_tk *child);
void ec_parsed_tk_dump(FILE *out, const struct ec_parsed_tk *parsed_tk);
struct ec_parsed_tk *ec_parsed_tk_find_first(struct ec_parsed_tk *parsed_tk,
* return a completed_tk object filled with elts
* return NULL on error (nomem?)
*/
-struct ec_completed_tk *ec_tk_complete(const struct ec_tk *tk,
+struct ec_completed_tk *ec_tk_complete(struct ec_tk *tk,
const char *str);
-struct ec_completed_tk *ec_tk_complete_tokens(const struct ec_tk *tk,
+struct ec_completed_tk *ec_tk_complete_tokens(struct ec_tk *tk,
const struct ec_strvec *strvec);
struct ec_completed_tk *ec_completed_tk_new(void);
struct ec_completed_tk_elt *ec_completed_tk_elt_new(const struct ec_tk *tk,
void ec_completed_tk_iter_free(struct ec_completed_tk_iter *iter);
-const char *ec_tk_desc(const struct ec_tk *tk);
-
#endif
--- /dev/null
+/*
+ * Copyright (c) 2016-2017, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the University of California, Berkeley nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/queue.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <limits.h>
+
+#include <ecoli_malloc.h>
+#include <ecoli_log.h>
+#include <ecoli_test.h>
+#include <ecoli_strvec.h>
+#include <ecoli_tk_expr.h>
+#include <ecoli_tk_str.h>
+#include <ecoli_tk_or.h>
+#include <ecoli_tk_int.h>
+#include <ecoli_tk_many.h>
+#include <ecoli_tk_seq.h>
+#include <ecoli_tk_option.h>
+#include <ecoli_tk_re.h>
+#include <ecoli_tk_re_lex.h>
+#include <ecoli_tk_cmd.h>
+
+struct ec_tk_cmd {
+ struct ec_tk gen;
+ char *cmd_str; /* the command string. */
+ struct ec_tk *cmd; /* the command token. */
+ struct ec_tk *lex; /* the lexer token. */
+ struct ec_tk *expr; /* the expression parser. */
+ struct ec_tk **table; /* table of tk referenced in command. */
+ unsigned int len; /* len of the table. */
+};
+
+static int
+ec_tk_cmd_eval_var(void **result, void *userctx,
+ const struct ec_parsed_tk *var)
+{
+ const struct ec_strvec *vec;
+ struct ec_tk_cmd *tk = userctx;
+ struct ec_tk *eval = NULL;
+ const char *str, *id;
+ unsigned int i;
+
+ (void)userctx;
+
+ /* get parsed string vector, it should contain only one str */
+ vec = ec_parsed_tk_strvec(var);
+ if (ec_strvec_len(vec) != 1)
+ return -EINVAL;
+ str = ec_strvec_val(vec, 0);
+
+ for (i = 0; i < tk->len; i++) {
+ id = ec_tk_id(tk->table[i]);
+ printf("i=%d id=%s\n", i, id);
+ if (id == NULL)
+ continue;
+ if (strcmp(str, id))
+ continue;
+ /* if id matches, use a tk provided by the user... */
+ eval = ec_tk_clone(tk->table[i]);
+ if (eval == NULL)
+ return -ENOMEM;
+ break;
+ }
+
+ /* ...or create a string token */
+ if (eval == NULL) {
+ eval = ec_tk_str(NULL, str);
+ if (eval == NULL)
+ return -ENOMEM;
+ }
+
+ printf("eval var %s %p\n", str, eval);
+ *result = eval;
+
+ return 0;
+}
+
+static int
+ec_tk_cmd_eval_pre_op(void **result, void *userctx, void *operand,
+ const struct ec_parsed_tk *operator)
+{
+ (void)result;
+ (void)userctx;
+ (void)operand;
+ (void)operator;
+
+ return -EINVAL;
+}
+
+static int
+ec_tk_cmd_eval_post_op(void **result, void *userctx, void *operand,
+ const struct ec_parsed_tk *operator)
+{
+ const struct ec_strvec *vec;
+ struct ec_tk *eval = operand;;
+
+ (void)userctx;
+
+ /* get parsed string vector, it should contain only one str */
+ vec = ec_parsed_tk_strvec(operator);
+ if (ec_strvec_len(vec) != 1)
+ return -EINVAL;
+
+ if (!strcmp(ec_strvec_val(vec, 0), "*"))
+ eval = NULL; //XXX
+ else
+ return -EINVAL;
+
+ printf("eval post_op %p\n", eval);
+ *result = eval;
+
+ return 0;
+}
+
+static int
+ec_tk_cmd_eval_bin_op(void **result, void *userctx, void *operand1,
+ const struct ec_parsed_tk *operator, void *operand2)
+
+{
+ const struct ec_strvec *vec;
+ struct ec_tk *out = NULL;
+ struct ec_tk *in1 = operand1;
+ struct ec_tk *in2 = operand2;
+
+ (void)userctx;
+
+ printf("eval bin_op %p %p\n", in1, in2);
+
+ /* get parsed string vector, it should contain only one str */
+ vec = ec_parsed_tk_strvec(operator);
+ if (ec_strvec_len(vec) != 1)
+ return -EINVAL;
+
+ if (!strcmp(ec_strvec_val(vec, 0), "|")) {
+ out = EC_TK_OR(NULL, ec_tk_clone(in1), ec_tk_clone(in2));
+ if (out == NULL)
+ return -EINVAL;
+ ec_tk_free(in1);
+ ec_tk_free(in2);
+ *result = out;
+ } else if (!strcmp(ec_strvec_val(vec, 0), ",")) {
+ ec_tk_free(in1);
+ ec_tk_free(in2);
+ *result = NULL; //XXX
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int
+ec_tk_cmd_eval_parenthesis(void **result, void *userctx,
+ const struct ec_parsed_tk *open_paren,
+ const struct ec_parsed_tk *close_paren,
+ void *value)
+{
+ const struct ec_strvec *vec;
+ struct ec_tk *in = value;;
+ struct ec_tk *out = NULL;;
+
+ (void)userctx;
+ (void)close_paren;
+
+ /* get parsed string vector, it should contain only one str */
+ vec = ec_parsed_tk_strvec(open_paren);
+ if (ec_strvec_len(vec) != 1)
+ return -EINVAL;
+
+ if (!strcmp(ec_strvec_val(vec, 0), "[")) {
+ out = ec_tk_option_new(NULL, ec_tk_clone(in));
+ if (out == NULL)
+ return -EINVAL;
+ ec_tk_free(in);
+ } else {
+ return -EINVAL;
+ }
+
+ printf("eval paren\n");
+ *result = out;
+
+ return 0;
+}
+
+static void
+ec_tk_cmd_eval_free(void *result, void *userctx)
+{
+ (void)userctx;
+ ec_free(result);
+}
+
+static const struct ec_tk_expr_eval_ops test_ops = {
+ .eval_var = ec_tk_cmd_eval_var,
+ .eval_pre_op = ec_tk_cmd_eval_pre_op,
+ .eval_post_op = ec_tk_cmd_eval_post_op,
+ .eval_bin_op = ec_tk_cmd_eval_bin_op,
+ .eval_parenthesis = ec_tk_cmd_eval_parenthesis,
+ .eval_free = ec_tk_cmd_eval_free,
+};
+
+static struct ec_parsed_tk *ec_tk_cmd_parse(const struct ec_tk *gen_tk,
+ const struct ec_strvec *strvec)
+{
+ struct ec_tk_cmd *tk = (struct ec_tk_cmd *)gen_tk;
+
+ return ec_tk_parse_tokens(tk->cmd, strvec);
+}
+
+static struct ec_completed_tk *ec_tk_cmd_complete(const struct ec_tk *gen_tk,
+ const struct ec_strvec *strvec)
+{
+ struct ec_tk_cmd *tk = (struct ec_tk_cmd *)gen_tk;
+
+ return ec_tk_complete_tokens(tk->cmd, strvec);
+}
+
+static void ec_tk_cmd_free_priv(struct ec_tk *gen_tk)
+{
+ struct ec_tk_cmd *tk = (struct ec_tk_cmd *)gen_tk;
+ unsigned int i;
+
+ ec_free(tk->cmd_str);
+ ec_tk_free(tk->cmd);
+ ec_tk_free(tk->expr);
+ ec_tk_free(tk->lex);
+ for (i = 0; i < tk->len; i++)
+ ec_tk_free(tk->table[i]);
+ ec_free(tk->table);
+}
+
+static int ec_tk_cmd_build(struct ec_tk *gen_tk)
+{
+ struct ec_tk *expr = NULL, *lex = NULL, *cmd = NULL;
+ struct ec_parsed_tk *p, *child;
+ struct ec_tk_cmd *tk = (struct ec_tk_cmd *)gen_tk;
+ void *result;
+ int ret;
+
+ /* build the expression parser */
+ ret = -ENOMEM;
+ expr = ec_tk_expr("expr");
+ if (expr == NULL)
+ goto fail;
+ ret = ec_tk_expr_set_val_tk(expr, ec_tk_re(NULL, "[a-zA-Z0-9]+"));
+ if (ret < 0)
+ goto fail;
+ ret = ec_tk_expr_add_bin_op(expr, ec_tk_str(NULL, ","));
+ if (ret < 0)
+ goto fail;
+ ret = ec_tk_expr_add_bin_op(expr, ec_tk_str(NULL, "|"));
+ if (ret < 0)
+ goto fail;
+ ret = ec_tk_expr_add_post_op(expr, ec_tk_str(NULL, "+"));
+ if (ret < 0)
+ goto fail;
+ ret = ec_tk_expr_add_post_op(expr, ec_tk_str(NULL, "*"));
+ if (ret < 0)
+ goto fail;
+ ret = ec_tk_expr_add_parenthesis(expr, ec_tk_str(NULL, "["),
+ ec_tk_str(NULL, "]"));
+ if (ret < 0)
+ goto fail;
+ ec_tk_expr_add_parenthesis(expr, ec_tk_str(NULL, "("),
+ ec_tk_str(NULL, ")"));
+ if (ret < 0)
+ goto fail;
+
+ /* prepend a lexer and a "many" to the expression token */
+ ret = -ENOMEM;
+ lex = ec_tk_re_lex(NULL,
+ ec_tk_many(NULL, ec_tk_clone(expr), 1, 0));
+ if (lex == NULL)
+ goto fail;
+
+ ret = ec_tk_re_lex_add(lex, "[a-zA-Z0-9]+", 1);
+ if (ret < 0)
+ goto fail;
+ ret = ec_tk_re_lex_add(lex, "[*|,()]", 1);
+ if (ret < 0)
+ goto fail;
+ ret = ec_tk_re_lex_add(lex, "\\[", 1);
+ if (ret < 0)
+ goto fail;
+ ret = ec_tk_re_lex_add(lex, "\\]", 1);
+ if (ret < 0)
+ goto fail;
+ ret = ec_tk_re_lex_add(lex, "[ ]+", 0);
+ if (ret < 0)
+ goto fail;
+
+ /* parse the command expression */
+ ret = -ENOMEM;
+ p = ec_tk_parse(lex, tk->cmd_str);
+ if (p == NULL)
+ goto fail;
+
+ ret = -EINVAL;
+ if (!ec_parsed_tk_matches(p))
+ goto fail;
+ if (TAILQ_EMPTY(&p->children))
+ goto fail;
+ if (TAILQ_EMPTY(&TAILQ_FIRST(&p->children)->children))
+ goto fail;
+
+ ret = -ENOMEM;
+ cmd = ec_tk_seq(NULL);
+ if (cmd == NULL)
+ goto fail;
+
+ TAILQ_FOREACH(child, &TAILQ_FIRST(&p->children)->children, next) {
+ ret = ec_tk_expr_eval(&result, expr, child,
+ &test_ops, tk);
+ if (ret < 0)
+ goto fail;
+ ret = ec_tk_seq_add(cmd, result);
+ if (ret < 0)
+ goto fail;
+ }
+ ec_parsed_tk_free(p);
+ ec_tk_dump(stdout, cmd);
+
+ ec_tk_free(tk->expr);
+ tk->expr = expr;
+ ec_tk_free(tk->lex);
+ tk->lex = lex;
+ ec_tk_free(tk->cmd);
+ tk->cmd = cmd;
+
+ return 0;
+
+fail:
+ ec_tk_free(expr);
+ ec_tk_free(lex);
+ ec_tk_free(cmd);
+ return ret;
+}
+
+static struct ec_tk_type ec_tk_cmd_type = {
+ .name = "cmd",
+ .build = ec_tk_cmd_build,
+ .parse = ec_tk_cmd_parse,
+ .complete = ec_tk_cmd_complete,
+ .free_priv = ec_tk_cmd_free_priv,
+};
+
+EC_TK_TYPE_REGISTER(ec_tk_cmd_type);
+
+int ec_tk_cmd_add_child(struct ec_tk *gen_tk, struct ec_tk *child)
+{
+ struct ec_tk_cmd *tk = (struct ec_tk_cmd *)gen_tk;
+ struct ec_tk **table;
+
+ // XXX check tk type
+
+ assert(tk != NULL);
+
+ printf("add child %s\n", child->id);
+ if (child == NULL)
+ return -EINVAL;
+
+ gen_tk->flags &= ~EC_TK_F_BUILT;
+
+ table = ec_realloc(tk->table, (tk->len + 1) * sizeof(*tk->table));
+ if (table == NULL) {
+ ec_tk_free(child);
+ return -ENOMEM;
+ }
+
+ tk->table = table;
+ table[tk->len] = child;
+ tk->len++;
+
+ child->parent = gen_tk;
+ TAILQ_INSERT_TAIL(&gen_tk->children, child, next); // XXX really needed?
+
+ return 0;
+}
+
+struct ec_tk *ec_tk_cmd(const char *id, const char *cmd_str)
+{
+ struct ec_tk *gen_tk = NULL;
+ struct ec_tk_cmd *tk = NULL;
+
+ gen_tk = ec_tk_new(id, &ec_tk_cmd_type, sizeof(*tk));
+ if (gen_tk == NULL)
+ goto fail;
+
+ tk = (struct ec_tk_cmd *)gen_tk;
+ tk->cmd_str = ec_strdup(cmd_str);
+ if (tk->cmd_str == NULL)
+ goto fail;
+
+ return gen_tk;
+
+fail:
+ ec_tk_free(gen_tk);
+ return NULL;
+}
+
+struct ec_tk *__ec_tk_cmd(const char *id, const char *cmd, ...)
+{
+ struct ec_tk *gen_tk = NULL;
+ struct ec_tk_cmd *tk = NULL;
+ struct ec_tk *child;
+ va_list ap;
+ int fail = 0;
+
+ va_start(ap, cmd);
+
+ gen_tk = ec_tk_cmd(id, cmd);
+ tk = (struct ec_tk_cmd *)gen_tk;
+ if (tk == NULL)
+ fail = 1;;
+
+ for (child = va_arg(ap, struct ec_tk *);
+ child != EC_TK_ENDLIST;
+ child = va_arg(ap, struct ec_tk *)) {
+
+ /* on error, don't quit the loop to avoid leaks */
+ if (fail == 1 || child == NULL ||
+ ec_tk_cmd_add_child(&tk->gen, child) < 0) {
+ fail = 1;
+ ec_tk_free(child);
+ }
+ }
+
+ if (fail == 1)
+ goto fail;
+
+ va_end(ap);
+ return gen_tk;
+
+fail:
+ ec_tk_free(gen_tk); /* will also free children */
+ va_end(ap);
+ return NULL;
+}
+
+static int ec_tk_cmd_testcase(void)
+{
+ struct ec_tk *tk;
+ int ret = 0;
+
+ tk = EC_TK_CMD(NULL,
+ "add [toto] x | y",
+ ec_tk_int("x", 0, 10, 10),
+ ec_tk_int("y", 20, 30, 10)
+ );
+ if (tk == NULL) {
+ ec_log(EC_LOG_ERR, "cannot create tk\n");
+ return -1;
+ }
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 2, "add", "1");
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 2, "add", "23");
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 3, "add", "toto", "23");
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "add", "15");
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "foo");
+ ec_tk_free(tk);
+
+ // XXX completion
+
+ return ret;
+}
+
+static struct ec_test ec_tk_cmd_test = {
+ .name = "tk_cmd",
+ .test = ec_tk_cmd_testcase,
+};
+
+EC_TEST_REGISTER(ec_tk_cmd_test);
--- /dev/null
+/*
+ * Copyright (c) 2016-2017, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the University of California, Berkeley nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ECOLI_TK_CMD_
+#define ECOLI_TK_CMD_
+
+#include <ecoli_tk.h>
+
+#define EC_TK_CMD(args...) __ec_tk_cmd(args, EC_TK_ENDLIST)
+
+struct ec_tk *__ec_tk_cmd(const char *id, const char *cmd_str, ...);
+
+struct ec_tk *ec_tk_cmd(const char *id, const char *cmd_str);
+
+/* child is consumed */
+int ec_tk_cmd_add_child(struct ec_tk *tk, struct ec_tk *child);
+
+#endif
return NULL;
}
-static struct ec_tk_ops ec_tk_empty_ops = {
- .typename = "empty",
+static struct ec_tk_type ec_tk_empty_type = {
+ .name = "empty",
.parse = ec_tk_empty_parse,
.complete = ec_tk_default_complete,
};
+EC_TK_TYPE_REGISTER(ec_tk_empty_type);
+
struct ec_tk *ec_tk_empty_new(const char *id)
{
- return ec_tk_new(id, &ec_tk_empty_ops,
+ return ec_tk_new(id, &ec_tk_empty_type,
sizeof(struct ec_tk_empty));
}
.test = ec_tk_empty_testcase,
};
-EC_REGISTER_TEST(ec_tk_empty_test);
+EC_TEST_REGISTER(ec_tk_empty_test);
return ret;
}
-static struct ec_tk_ops ec_tk_expr_ops = {
- .typename = "expr",
+static struct ec_tk_type ec_tk_expr_type = {
+ .name = "expr",
.build = ec_tk_expr_build,
.parse = ec_tk_expr_parse,
.complete = ec_tk_expr_complete,
.free_priv = ec_tk_expr_free_priv,
};
+EC_TK_TYPE_REGISTER(ec_tk_expr_type);
+
struct ec_tk *ec_tk_expr(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));
+ gen_tk = ec_tk_new(id, &ec_tk_expr_type, sizeof(*tk));
if (gen_tk == NULL)
return NULL;
tk = (struct ec_tk_expr *)gen_tk;
BIN_OP,
PRE_OP,
POST_OP,
- PAREN,
+ PAREN_OPEN,
+ PAREN_CLOSE,
};
static enum expr_tk_type get_tk_type(const struct ec_tk *expr_gen_tk,
const struct ec_tk *check_tk)
return POST_OP;
}
- /* for (i = 0; i < expr_tk->paren_len; i++) { */
- /* if (check_tk == expr_tk->open_ops[i]) */
- /* return PAREN; */
- /* } */
+ for (i = 0; i < expr_tk->paren_len; i++) {
+ if (check_tk == expr_tk->open_ops[i])
+ return PAREN_OPEN;
+ }
+ for (i = 0; i < expr_tk->paren_len; i++) {
+ if (check_tk == expr_tk->close_ops[i])
+ return PAREN_CLOSE;
+ }
return NONE;
}
-struct eval_state {
- void *userctx;
- bool has_lval;
- struct ec_tk_expr_eval_result lval;
- const struct ec_parsed_tk **op_stack;
- size_t op_stack_size;
- size_t op_stack_len;
+struct result {
+ bool has_val;
+ void *val;
+ const struct ec_parsed_tk *op;
+ enum expr_tk_type op_type;
};
-static struct eval_state *eval_state_new(void *userctx)
-{
- struct eval_state *state;
-
- state = ec_calloc(1, sizeof(*state));
- state->userctx = userctx;
- return state;
-}
-
-static void eval_state_free(struct eval_state *state)
-{
- if (state == NULL)
- return;
- ec_free(state->op_stack);
- ec_free(state);
-}
-
-static int eval_state_add_val(struct eval_state *state,
+/* merge x and y results in x */
+static int merge_results(void *userctx,
const struct ec_tk_expr_eval_ops *ops,
- const struct ec_tk *expr_gen_tk,
- const struct ec_parsed_tk *parsed_tk)
+ struct result *x, const struct result *y)
{
- struct ec_tk_expr_eval_result val;
- const struct ec_parsed_tk *op_parsed_tk;
- enum expr_tk_type type;
+ int ret;
- (void)ops;
- (void)state;
- printf("push val: ");
- ec_parsed_tk_dump(stdout, parsed_tk);
+ if (y->has_val == 0 && y->op == NULL)
+ return 0;
+ if (x->has_val == 0 && x->op == NULL) {
+ *x = *y;
+ return 0;
+ }
- val = ops->eval_var(state->userctx, parsed_tk);
- if (val.code != 0)
- return val.code; // XXX free val?
+ if (x->has_val && x->op == NULL && y->has_val && y->op != NULL) {
+ ret = ops->eval_bin_op(&x->val, userctx, x->val, y->op, y->val);
+ if (ret < 0)
+ return ret;
- /* first value, it is the left operand */
- if (state->has_lval == 0) {
- state->lval = val;
- state->has_lval = 1;
return 0;
}
- /* if we have a right operand without operator... fail */
- if (state->op_stack_len == 0)
- return -EINVAL; // XXX free val?
-
- while (state->op_stack_len > 0) {
- state->op_stack_len--;
- op_parsed_tk = state->op_stack[state->op_stack_len];
- type = get_tk_type(expr_gen_tk, op_parsed_tk->tk);
- if (type == PRE_OP) {
- state->lval = ops->eval_pre_op(state->userctx,
- val, op_parsed_tk);
- if (val.code != 0)
- return val.code; // XXX free val?
- } else if (type == BIN_OP) {
- state->lval = ops->eval_bin_op(state->userctx,
- state->lval, op_parsed_tk, val);
- if (state->lval.code != 0)
- return state->lval.code; // XXX free val?
- break;
- } else {
- // ?
+ if (x->has_val == 0 && x->op != NULL && y->has_val && y->op == NULL) {
+ if (x->op_type == PRE_OP) {
+ ret = ops->eval_pre_op(&x->val, userctx, y->val, x->op);
+ if (ret < 0)
+ return ret;
+ x->has_val = true;
+ x->op_type = NONE;
+ x->op = NULL;
+ return 0;
+ } else if (x->op_type == BIN_OP) {
+ x->val = y->val;
+ x->has_val = true;
+ return 0;
}
}
- return 0;
-}
-
-static int eval_state_add_op(struct eval_state *state,
- const struct ec_tk_expr_eval_ops *ops,
- const struct ec_tk *expr_gen_tk,
- const struct ec_parsed_tk *parsed_tk)
-{
- enum expr_tk_type type;
-
- (void)ops;
- (void)state;
-
- printf("push op: ");
- ec_parsed_tk_dump(stdout, parsed_tk);
+ if (x->has_val && x->op == NULL && y->has_val == 0 && y->op != NULL) {
+ ret = ops->eval_post_op(&x->val, userctx, x->val, y->op);
+ if (ret < 0)
+ return ret;
- type = get_tk_type(expr_gen_tk, parsed_tk->tk);
- if (type == POST_OP) {
- if (state->has_lval == 0)
- return -EINVAL;
- state->lval = ops->eval_post_op(state->userctx,
- state->lval, parsed_tk);
- if (state->lval.code != 0)
- return state->lval.code;
return 0;
}
- if (state->op_stack_len >= state->op_stack_size) {
- const struct ec_parsed_tk **op_stack;
-
- op_stack = ec_realloc(state->op_stack,
- (state->op_stack_len + 1) * sizeof(*op_stack));
- if (op_stack == NULL)
- return -ENOMEM;
- state->op_stack = op_stack;
- state->op_stack_size = state->op_stack_len + 1;
- }
-
- state->op_stack[state->op_stack_len] = parsed_tk;
- state->op_stack_len++;
-
- return 0;
+ assert(true); /* we should not get here */
+ return -EINVAL;
}
-static int eval_expression(struct eval_state *state,
+static int eval_expression(struct result *result,
+ void *userctx,
const struct ec_tk_expr_eval_ops *ops,
const struct ec_tk *expr_gen_tk,
const struct ec_parsed_tk *parsed_tk)
{
+ struct ec_parsed_tk *open = NULL, *close = NULL;
+ struct result child_result;
struct ec_parsed_tk *child;
enum expr_tk_type type;
int ret;
+ memset(result, 0, sizeof(*result));
+ memset(&child_result, 0, sizeof(child_result));
+
type = get_tk_type(expr_gen_tk, parsed_tk->tk);
if (type == VAL) {
- ret = eval_state_add_val(state, ops, expr_gen_tk, parsed_tk);
- if (ret < 0)
- return ret;
- } else if (type != NONE) {
- ret = eval_state_add_op(state, ops, expr_gen_tk, parsed_tk);
+ ret = ops->eval_var(&result->val, userctx, parsed_tk);
if (ret < 0)
- return ret;
+ goto fail;
+ result->has_val = 1;
+ } else if (type == PRE_OP || type == POST_OP || type == BIN_OP) {
+ result->op = parsed_tk;
+ result->op_type = type;
}
TAILQ_FOREACH(child, &parsed_tk->children, next) {
- ret = eval_expression(state, ops, expr_gen_tk, child);
+
+ type = get_tk_type(expr_gen_tk, child->tk);
+ if (type == PAREN_OPEN) {
+ open = child;
+ continue;
+ } else if (type == PAREN_CLOSE) {
+ close = child;
+ continue;
+ }
+
+ ret = eval_expression(&child_result, userctx, ops,
+ expr_gen_tk, child);
if (ret < 0)
- return ret;
+ goto fail;
+
+ ret = merge_results(userctx, ops, result, &child_result);
+ if (ret < 0)
+ goto fail;
+
+ memset(&child_result, 0, sizeof(child_result));
+ }
+
+ if (open != NULL && close != NULL) {
+ ret = ops->eval_parenthesis(&result->val, userctx, open, close,
+ result->val);
+ if (ret < 0)
+ goto fail;
}
return 0;
+
+fail:
+ if (result->has_val)
+ ops->eval_free(result->val, userctx);
+ if (child_result.has_val)
+ ops->eval_free(child_result.val, userctx);
+ memset(result, 0, sizeof(*result));
+
+ return ret;
}
-struct ec_tk_expr_eval_result ec_tk_expr_eval(const struct ec_tk *tk,
+int ec_tk_expr_eval(void **user_result, const struct ec_tk *tk,
struct ec_parsed_tk *parsed, const struct ec_tk_expr_eval_ops *ops,
void *userctx)
{
- struct ec_tk_expr_eval_result res = { -EINVAL, NULL };
- struct eval_state *state;
+ struct result result;
int ret;
- if (ops == NULL)
- return res;
-
- ec_parsed_tk_dump(stdout, parsed);
+ if (ops == NULL || ops->eval_var == NULL || ops->eval_pre_op == NULL ||
+ ops->eval_post_op == NULL || ops->eval_bin_op == NULL ||
+ ops->eval_parenthesis == NULL || ops->eval_free == NULL)
+ return -EINVAL;
- res.code = -ENOMEM;
- state = eval_state_new(&userctx);
- if (state == NULL)
- return res;
+ if (!ec_parsed_tk_matches(parsed))
+ return -EINVAL;
- ret = eval_expression(state, ops, tk, parsed);
-// XXX check if op stack len is 0 ?
- res = state->lval;
- eval_state_free(state);
+ ec_parsed_tk_dump(stdout, parsed); //XXX
+ ret = eval_expression(&result, userctx, ops, tk, parsed);
+ if (ret < 0)
+ return ret;
- if (ret < 0 && res.code == 0)
- res.code = ret;
+ assert(result.has_val);
+ assert(result.op == NULL);
+ *user_result = result.val;
- return res;
+ return 0;
}
/* the test case is in a separate file ecoli_tk_expr_test.c */
/* XXX remove the _new for all other tokens */
-/* XXX explain the free policy in case of error or success */
-struct ec_tk_expr_eval_result {
- int code; /* 0 on success, negative on error */
- void *parsed;
-};
-
-typedef struct ec_tk_expr_eval_result (*ec_tk_expr_eval_var_t)(
- void *userctx, const struct ec_parsed_tk *var);
+/**
+ * Callback function type for evaluating a variable
+ *
+ * @param result
+ * On success, this pointer must be set by the user to point
+ * to a user structure describing the evaluated result.
+ * @param userctx
+ * A user-defined context passed to all callback functions, which
+ * can be used to maintain a state or store global information.
+ * @param var
+ * The parsed token referencing the variable.
+ * @return
+ * 0 on success (*result must be set), or -errno on error (*result
+ * is undefined).
+ */
+typedef int (*ec_tk_expr_eval_var_t)(
+ void **result, void *userctx,
+ const struct ec_parsed_tk *var);
-typedef struct ec_tk_expr_eval_result (*ec_tk_expr_eval_pre_op_t)(
- void *userctx, struct ec_tk_expr_eval_result operand,
+/**
+ * Callback function type for evaluating a prefix-operator
+ *
+ * @param result
+ * On success, this pointer must be set by the user to point
+ * to a user structure describing the evaluated result.
+ * @param userctx
+ * A user-defined context passed to all callback functions, which
+ * can be used to maintain a state or store global information.
+ * @param operand
+ * The evaluated expression on which the operation should be applied.
+ * @param var
+ * The parsed token referencing the operator.
+ * @return
+ * 0 on success (*result must be set, operand is freed),
+ * or -errno on error (*result is undefined, operand is not freed).
+ */
+typedef int (*ec_tk_expr_eval_pre_op_t)(
+ void **result, void *userctx,
+ void *operand,
const struct ec_parsed_tk *operator);
-typedef struct ec_tk_expr_eval_result (*ec_tk_expr_eval_post_op_t)(
- void *userctx, struct ec_tk_expr_eval_result operand,
+typedef int (*ec_tk_expr_eval_post_op_t)(
+ void **result, void *userctx,
+ void *operand,
const struct ec_parsed_tk *operator);
-typedef struct ec_tk_expr_eval_result (*ec_tk_expr_eval_bin_op_t)(
- void *userctx, struct ec_tk_expr_eval_result operand1,
+typedef int (*ec_tk_expr_eval_bin_op_t)(
+ void **result, void *userctx,
+ void *operand1,
const struct ec_parsed_tk *operator,
- struct ec_tk_expr_eval_result operand2);
+ void *operand2);
+
+typedef int (*ec_tk_expr_eval_parenthesis_t)(
+ void **result, void *userctx,
+ const struct ec_parsed_tk *open_paren,
+ const struct ec_parsed_tk *close_paren,
+ void * value);
-typedef struct ec_tk_expr_eval_result (*ec_tk_expr_eval_parenthesis_t)(
- void *userctx, const struct ec_parsed_tk *operator_str,
- const struct ec_parsed_tk *operand_str,
- void *operand);
+typedef void (*ec_tk_expr_eval_free_t)(
+ void *result, void *userctx);
struct ec_tk *ec_tk_expr(const char *id);
ec_tk_expr_eval_post_op_t eval_post_op;
ec_tk_expr_eval_bin_op_t eval_bin_op;
ec_tk_expr_eval_parenthesis_t eval_parenthesis;
+ ec_tk_expr_eval_free_t eval_free;
};
-struct ec_tk_expr_eval_result ec_tk_expr_eval(const struct ec_tk *tk,
+int ec_tk_expr_eval(void **result, const struct ec_tk *tk,
struct ec_parsed_tk *parsed, const struct ec_tk_expr_eval_ops *ops,
void *userctx);
#include <errno.h>
#include <limits.h>
#include <stdint.h>
+#include <assert.h>
#include <ecoli_malloc.h>
#include <ecoli_strvec.h>
int val;
};
-static struct ec_tk_expr_eval_result
-ec_tk_expr_test_eval_var(void *userctx, const struct ec_parsed_tk *var)
+static int
+ec_tk_expr_test_eval_var(void **result, void *userctx,
+ const struct ec_parsed_tk *var)
{
const struct ec_strvec *vec;
- struct ec_tk_expr_eval_result res = { .code = -EINVAL, .parsed = NULL };
struct my_eval_result *eval;
(void)userctx;
/* get parsed string vector, it should contain only one str */
vec = ec_parsed_tk_strvec(var);
if (ec_strvec_len(vec) != 1)
- return res;
+ return -EINVAL;
- res.code = -ENOMEM;
eval = ec_malloc(sizeof(*eval));
if (eval == NULL)
- return res;
+ return -ENOMEM;
eval->val = atoi(ec_strvec_val(vec, 0)); // XXX use strtol
printf("eval var %d\n", eval->val);
- res.code = 0;
- res.parsed = eval;
+ *result = eval;
- return res;
+ return 0;
}
-static struct ec_tk_expr_eval_result
-ec_tk_expr_test_eval_pre_op(void *userctx,
- struct ec_tk_expr_eval_result operand,
+static int
+ec_tk_expr_test_eval_pre_op(void **result, void *userctx, void *operand,
const struct ec_parsed_tk *operator)
{
const struct ec_strvec *vec;
- struct ec_tk_expr_eval_result res = { .code = -EINVAL, .parsed = NULL };
- struct my_eval_result *eval = operand.parsed;;
+ struct my_eval_result *eval = operand;;
(void)userctx;
/* get parsed string vector, it should contain only one str */
vec = ec_parsed_tk_strvec(operator);
- if (ec_strvec_len(vec) != 1) {
- ec_free(eval);
- return res;
- }
+ if (ec_strvec_len(vec) != 1)
+ return -EINVAL;
- if (!strcmp(ec_strvec_val(vec, 0), "~")) {
+ if (!strcmp(ec_strvec_val(vec, 0), "!"))
eval->val = !eval->val;
- } else {
- ec_free(eval);
- return res;
- }
+ else
+ return -EINVAL;
printf("eval pre_op %d\n", eval->val);
- res.code = 0;
- res.parsed = eval;
+ *result = eval;
- return res;
+ return 0;
}
-static struct ec_tk_expr_eval_result
-ec_tk_expr_test_eval_post_op(void *userctx,
- struct ec_tk_expr_eval_result operand,
+static int
+ec_tk_expr_test_eval_post_op(void **result, void *userctx, void *operand,
const struct ec_parsed_tk *operator)
{
const struct ec_strvec *vec;
- struct ec_tk_expr_eval_result res = { .code = -EINVAL, .parsed = NULL };
- struct my_eval_result *eval = operand.parsed;;
+ struct my_eval_result *eval = operand;;
(void)userctx;
/* get parsed string vector, it should contain only one str */
vec = ec_parsed_tk_strvec(operator);
- if (ec_strvec_len(vec) != 1) {
- ec_free(eval);
- return res;
- }
+ if (ec_strvec_len(vec) != 1)
+ return -EINVAL;
- if (!strcmp(ec_strvec_val(vec, 0), "!")) {
+ if (!strcmp(ec_strvec_val(vec, 0), "^"))
eval->val = eval->val * eval->val;
- } else {
- ec_free(eval);
- return res;
- }
+ else
+ return -EINVAL;
printf("eval post_op %d\n", eval->val);
- res.code = 0;
- res.parsed = eval;
+ *result = eval;
- return res;
+ return 0;
}
-static struct ec_tk_expr_eval_result
-ec_tk_expr_test_eval_bin_op(void *userctx,
- struct ec_tk_expr_eval_result operand1,
- const struct ec_parsed_tk *operator,
- struct ec_tk_expr_eval_result operand2)
+static int
+ec_tk_expr_test_eval_bin_op(void **result, void *userctx, void *operand1,
+ const struct ec_parsed_tk *operator, void *operand2)
{
const struct ec_strvec *vec;
- struct ec_tk_expr_eval_result res = { .code = -EINVAL, .parsed = NULL };
- struct my_eval_result *eval1 = operand1.parsed;;
- struct my_eval_result *eval2 = operand2.parsed;;
+ struct my_eval_result *eval1 = operand1;;
+ struct my_eval_result *eval2 = operand2;;
(void)userctx;
/* get parsed string vector, it should contain only one str */
vec = ec_parsed_tk_strvec(operator);
- if (ec_strvec_len(vec) != 1) {
- ec_free(eval1);
- ec_free(eval2);
- return res;
- }
+ if (ec_strvec_len(vec) != 1)
+ return -EINVAL;
- if (!strcmp(ec_strvec_val(vec, 0), "+")) {
+ if (!strcmp(ec_strvec_val(vec, 0), "+"))
eval1->val = eval1->val + eval2->val;
- } else if (!strcmp(ec_strvec_val(vec, 0), "*")) {
+ else if (!strcmp(ec_strvec_val(vec, 0), "*"))
eval1->val = eval1->val * eval2->val;
- } else {
- ec_free(eval1);
- ec_free(eval2);
- return res;
- }
+ else
+ return -EINVAL;
printf("eval bin_op %d\n", eval1->val);
- res.code = 0;
- res.parsed = eval1;
+ ec_free(eval2);
+ *result = eval1;
+
+ return 0;
+}
+
+static int
+ec_tk_expr_test_eval_parenthesis(void **result, void *userctx,
+ const struct ec_parsed_tk *open_paren,
+ const struct ec_parsed_tk *close_paren,
+ void *value)
+{
+ (void)userctx;
+ (void)open_paren;
+ (void)close_paren;
+
+ printf("eval paren\n");
+ *result = value;
+
+ return 0;
+}
- return res;
+static void
+ec_tk_expr_test_eval_free(void *result, void *userctx)
+{
+ (void)userctx;
+ ec_free(result);
}
static const struct ec_tk_expr_eval_ops test_ops = {
.eval_pre_op = ec_tk_expr_test_eval_pre_op,
.eval_post_op = ec_tk_expr_test_eval_post_op,
.eval_bin_op = ec_tk_expr_test_eval_bin_op,
+ .eval_parenthesis = ec_tk_expr_test_eval_parenthesis,
+ .eval_free = ec_tk_expr_test_eval_free,
};
static int ec_tk_expr_test_eval(struct ec_tk *lex_tk,
const struct ec_tk *expr_tk,
const char *str, int val)
{
- struct ec_tk_expr_eval_result res;
struct ec_parsed_tk *p;
+ void *result;
struct my_eval_result *eval;
int ret;
/* XXX check tk type (again and again) */
-
p = ec_tk_parse(lex_tk, str);
if (p == NULL)
return -1;
- res = ec_tk_expr_eval(expr_tk, p, &test_ops, NULL);
+ ret = ec_tk_expr_eval(&result, expr_tk, p, &test_ops, NULL);
ec_parsed_tk_free(p);
- if (res.code < 0)
- return res.code;
+ if (ret < 0)
+ return -1;
/* the parsed value is an integer */
- eval = res.parsed;
- if (eval == NULL) {
+ eval = result;
+ assert(eval != NULL);
+
+ printf("result: %d (expected %d)\n", eval->val, val);
+ if (eval->val == val)
+ ret = 0;
+ else
ret = -1;
- } else {
- printf("result: %d (expected %d)\n", eval->val, val);
- if (eval->val == val)
- ret = 0;
- else
- ret = -1;
- }
+
ec_free(eval);
return ret;
ec_tk_expr_set_val_tk(tk, ec_tk_int(NULL, 0, UCHAR_MAX, 0));
ec_tk_expr_add_bin_op(tk, ec_tk_str(NULL, "+"));
ec_tk_expr_add_bin_op(tk, ec_tk_str(NULL, "*"));
- ec_tk_expr_add_pre_op(tk, ec_tk_str(NULL, "~"));
- ec_tk_expr_add_post_op(tk, ec_tk_str(NULL, "!"));
+ ec_tk_expr_add_pre_op(tk, ec_tk_str(NULL, "!")); /* not */
+ ec_tk_expr_add_post_op(tk, ec_tk_str(NULL, "^")); /* square */
ec_tk_expr_add_parenthesis(tk, ec_tk_str(NULL, "("),
ec_tk_str(NULL, ")"));
ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "1");
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "1", "1");
ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "1", "*");
ret |= EC_TEST_CHECK_TK_PARSE(tk, 3, "1", "*", "1");
- ret |= EC_TEST_CHECK_TK_PARSE(tk, 4, "1", "+", "~", "1");
- ret |= EC_TEST_CHECK_TK_PARSE(tk, 4, "1", "!", "+", "1");
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 3, "1", "*", "1", "*");
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 4, "1", "+", "!", "1");
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 4, "1", "^", "+", "1");
ret |= EC_TEST_CHECK_TK_PARSE(tk, 5, "1", "*", "1", "*", "1");
- ret |= EC_TEST_CHECK_TK_PARSE(tk, 5, "1", "*", "1", "*", "1", "*", "1");
ret |= EC_TEST_CHECK_TK_PARSE(tk, 5, "1", "*", "1", "+", "1");
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 7, "1", "*", "1", "*", "1", "*", "1");
ret |= EC_TEST_CHECK_TK_PARSE(
- tk, 10, "~", "(", "1", "*", "(", "1", "+", "1", ")", ")");
- ret |= EC_TEST_CHECK_TK_PARSE(tk, 5, "1", "+", "~", "1", "!");
+ tk, 10, "!", "(", "1", "*", "(", "1", "+", "1", ")", ")");
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 5, "1", "+", "!", "1", "^");
/* prepend a lexer to the expression token */
lex_tk = ec_tk_re_lex(NULL, ec_tk_clone(tk));
if (lex_tk == NULL)
goto fail;
- ret |= ec_tk_re_lex_add(lex_tk, "^[0-9]+", 1); /* vars */
- ret |= ec_tk_re_lex_add(lex_tk, "^[+*~!()]", 1); /* operators */
- ret |= ec_tk_re_lex_add(lex_tk, "^[ ]+", 0); /* spaces */
- if (ret != 0)
- goto fail;
+ ret |= ec_tk_re_lex_add(lex_tk, "[0-9]+", 1); /* vars */
+ ret |= ec_tk_re_lex_add(lex_tk, "[+*!^()]", 1); /* operators */
+ ret |= ec_tk_re_lex_add(lex_tk, "[ ]+", 0); /* spaces */
/* valid expressions */
- ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "~1");
- ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "1!");
- ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "1! + 1");
- ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "1 + 4 * (2 + 3!)!");
+ ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "!1");
+ ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "1^");
+ ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "1^ + 1");
+ ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "1 + 4 * (2 + 3^)^");
ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "(1)");
- ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "3*~3+~3*(2+ 2)");
- ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "~~(~1)! + ~(4 + (2*3))");
- ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "(1 + 1)! * 1!");
+ ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "3*!3+!3*(2+ 2)");
+ ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "!!(!1)^ + !(4 + (2*3))");
+ ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "(1 + 1)^ * 1^");
/* invalid expressions */
ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, -1, "");
ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, -1, "1+");
ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, -1, "1+*1");
ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, -1, "1+(1*1");
- ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, -1, "1+~1~1)");
- if (ret)
- exit(ret);
+ ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, -1, "1+!1!1)");
+
+ ret |= ec_tk_expr_test_eval(lex_tk, tk, "1^", 1);
+ ret |= ec_tk_expr_test_eval(lex_tk, tk, "2^", 4);
+ ret |= ec_tk_expr_test_eval(lex_tk, tk, "!1", 0);
+ ret |= ec_tk_expr_test_eval(lex_tk, tk, "!0", 1);
- ret |= ec_tk_expr_test_eval(lex_tk, tk, "2!", 4);
ret |= ec_tk_expr_test_eval(lex_tk, tk, "1+1", 2);
ret |= ec_tk_expr_test_eval(lex_tk, tk, "1+1*2", 4);
-
- // XXX marche pas:
- // le "*" s'applique avant le "!" final
- ret |= ec_tk_expr_test_eval(lex_tk, tk, "2 * 2!", 8);
- /* ret |= ec_tk_expr_test_eval(lex_tk, tk, "(1 + ~0)! * ~0!", 4); */
- /* ret |= ec_tk_expr_test_eval(lex_tk, tk, "(1 + ~1) * 3", 3); */
- /* ret |= ec_tk_expr_test_eval(lex_tk, tk, "~1", 0); */
- printf("exit\n");
- exit(ret);
-
- /*
- idee:
- eval_expression() retourne soit:
- - NONE
- - *_OP, parsed_tk
- - VAL, eval(parsed_tk)
- */
-
+ ret |= ec_tk_expr_test_eval(lex_tk, tk, "2 * 2^", 8);
+ ret |= ec_tk_expr_test_eval(lex_tk, tk, "(1 + !0)^ * !0^", 4);
+ ret |= ec_tk_expr_test_eval(lex_tk, tk, "(1 + !1) * 3", 3);
ec_tk_free(tk);
ec_tk_free(lex_tk);
.test = ec_tk_expr_testcase,
};
-EC_REGISTER_TEST(ec_tk_expr_test);
+EC_TEST_REGISTER(ec_tk_expr_test);
return NULL;
}
-static struct ec_tk_ops ec_tk_int_ops = {
- .typename = "int",
+static struct ec_tk_type ec_tk_int_type = {
+ .name = "int",
.parse = ec_tk_int_parse,
.complete = ec_tk_default_complete,
};
+EC_TK_TYPE_REGISTER(ec_tk_int_type);
+
struct ec_tk *ec_tk_int(const char *id, long long int min,
long long int max, unsigned int base)
{
struct ec_tk *gen_tk = NULL;
struct ec_tk_int *tk = NULL;
- gen_tk = ec_tk_new(id, &ec_tk_int_ops, sizeof(*tk));
+ gen_tk = ec_tk_new(id, &ec_tk_int_type, sizeof(*tk));
if (gen_tk == NULL)
return NULL;
tk = (struct ec_tk_int *)gen_tk;
.test = ec_tk_int_testcase,
};
-EC_REGISTER_TEST(ec_tk_int_test);
+EC_TEST_REGISTER(ec_tk_int_test);
#include <ecoli_tk.h>
+// XXX remove min, max, base from new(), and add ec_tk_int_set_limits() +
+// XXX ec_tk_int_set_base() ?
struct ec_tk *ec_tk_int(const char *id, long long int min,
long long int max, unsigned int base);
long long ec_tk_int_getval(struct ec_tk *tk, const char *str);
return NULL;
}
-#if 0
+#if 0 //XXX missing tk_many_complete
static struct ec_completed_tk *ec_tk_many_complete(const struct ec_tk *gen_tk,
const struct ec_strvec *strvec)
{
ec_tk_free(tk->child);
}
-static struct ec_tk_ops ec_tk_many_ops = {
- .typename = "many",
+static struct ec_tk_type ec_tk_many_type = {
+ .name = "many",
.parse = ec_tk_many_parse,
.complete = ec_tk_default_complete,
//XXX .complete = ec_tk_many_complete,
.free_priv = ec_tk_many_free_priv,
};
+EC_TK_TYPE_REGISTER(ec_tk_many_type);
+
struct ec_tk *ec_tk_many(const char *id, struct ec_tk *child,
unsigned int min, unsigned int max)
{
if (child == NULL)
return NULL;
- tk = (struct ec_tk_many *)ec_tk_new(id, &ec_tk_many_ops,
+ tk = (struct ec_tk_many *)ec_tk_new(id, &ec_tk_many_type,
sizeof(*tk));
if (tk == NULL) {
ec_tk_free(child);
.test = ec_tk_many_testcase,
};
-EC_REGISTER_TEST(ec_tk_many_test);
+EC_TEST_REGISTER(ec_tk_many_test);
ec_tk_free(tk->child);
}
-static struct ec_tk_ops ec_tk_option_ops = {
- .typename = "option",
+static struct ec_tk_type ec_tk_option_type = {
+ .name = "option",
.parse = ec_tk_option_parse,
.complete = ec_tk_option_complete,
.free_priv = ec_tk_option_free_priv,
};
+EC_TK_TYPE_REGISTER(ec_tk_option_type);
+
struct ec_tk *ec_tk_option_new(const char *id, struct ec_tk *child)
{
+ struct ec_tk *gen_tk = NULL;
struct ec_tk_option *tk = NULL;
if (child == NULL)
return NULL;
- tk = (struct ec_tk_option *)ec_tk_new(id, &ec_tk_option_ops,
- sizeof(*tk));
- if (tk == NULL) {
+ gen_tk = ec_tk_new(id, &ec_tk_option_type, sizeof(*tk));
+ if (gen_tk == NULL) {
ec_tk_free(child);
return NULL;
}
+ tk = (struct ec_tk_option *)gen_tk;
tk->child = child;
+ child->parent = gen_tk;
+ TAILQ_INSERT_TAIL(&gen_tk->children, child, next);
+
return &tk->gen;
}
.test = ec_tk_option_testcase,
};
-EC_REGISTER_TEST(ec_tk_option_test);
+EC_TEST_REGISTER(ec_tk_option_test);
ec_free(tk->table);
}
-static struct ec_tk_ops ec_tk_or_ops = {
- .typename = "or",
- .parse = ec_tk_or_parse,
- .complete = ec_tk_or_complete,
- .free_priv = ec_tk_or_free_priv,
-};
-
int ec_tk_or_add(struct ec_tk *gen_tk, struct ec_tk *child)
{
struct ec_tk_or *tk = (struct ec_tk_or *)gen_tk;
return 0;
}
+static struct ec_tk_type ec_tk_or_type = {
+ .name = "tk_or",
+ .parse = ec_tk_or_parse,
+ .complete = ec_tk_or_complete,
+ .free_priv = ec_tk_or_free_priv,
+};
+
+EC_TK_TYPE_REGISTER(ec_tk_or_type);
+
struct ec_tk *ec_tk_or(const char *id)
{
struct ec_tk *gen_tk = NULL;
struct ec_tk_or *tk = NULL;
- gen_tk = ec_tk_new(id, &ec_tk_or_ops, sizeof(*tk));
+ gen_tk = ec_tk_new(id, &ec_tk_or_type, sizeof(*tk));
if (gen_tk == NULL)
return NULL;
.test = ec_tk_or_testcase,
};
-EC_REGISTER_TEST(ec_tk_or_test);
+EC_TEST_REGISTER(ec_tk_or_test);
struct ec_tk *ec_tk_or(const char *id);
/* child is consumed */
-/* all token given in the list will be freed when freeing this one */
int ec_tk_or_add(struct ec_tk *tk, struct ec_tk *child);
--- /dev/null
+/*
+ * Copyright (c) 2016, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the University of California, Berkeley nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <regex.h>
+
+#include <ecoli_log.h>
+#include <ecoli_malloc.h>
+#include <ecoli_test.h>
+#include <ecoli_strvec.h>
+#include <ecoli_tk.h>
+#include <ecoli_tk_re.h>
+
+struct ec_tk_re {
+ struct ec_tk gen;
+ char *re_str;
+ regex_t re;
+};
+
+static struct ec_parsed_tk *ec_tk_re_parse(const struct ec_tk *gen_tk,
+ const struct ec_strvec *strvec)
+{
+ struct ec_tk_re *tk = (struct ec_tk_re *)gen_tk;
+ struct ec_strvec *match_strvec;
+ struct ec_parsed_tk *parsed_tk = NULL;
+ const char *str;
+ regmatch_t pos;
+
+ parsed_tk = ec_parsed_tk_new();
+ if (parsed_tk == NULL)
+ goto fail;
+
+ if (ec_strvec_len(strvec) == 0)
+ return parsed_tk;
+
+ str = ec_strvec_val(strvec, 0);
+ if (regexec(&tk->re, str, 1, &pos, 0) != 0)
+ return parsed_tk;
+ if (pos.rm_so != 0 || pos.rm_eo != (int)strlen(str))
+ return parsed_tk;
+
+ match_strvec = ec_strvec_ndup(strvec, 0, 1);
+ if (match_strvec == NULL)
+ goto fail;
+
+ ec_parsed_tk_set_match(parsed_tk, gen_tk, match_strvec);
+
+ return parsed_tk;
+
+ fail:
+ ec_parsed_tk_free(parsed_tk);
+ return NULL;
+}
+
+static void ec_tk_re_free_priv(struct ec_tk *gen_tk)
+{
+ struct ec_tk_re *tk = (struct ec_tk_re *)gen_tk;
+
+ ec_free(tk->re_str);
+ regfree(&tk->re);
+}
+
+static struct ec_tk_type ec_tk_re_type = {
+ .name = "re",
+ .parse = ec_tk_re_parse,
+ .complete = ec_tk_default_complete,
+ .free_priv = ec_tk_re_free_priv,
+};
+
+EC_TK_TYPE_REGISTER(ec_tk_re_type);
+
+struct ec_tk *ec_tk_re_new(const char *id)
+{
+ struct ec_tk *gen_tk = NULL;
+
+ gen_tk = ec_tk_new(id, &ec_tk_re_type, sizeof(struct ec_tk_re));
+ if (gen_tk == NULL)
+ return NULL;
+
+ return gen_tk;
+}
+
+int ec_tk_re_set_regexp(struct ec_tk *gen_tk, const char *str)
+{
+ struct ec_tk_re *tk = (struct ec_tk_re *)gen_tk;
+ int ret;
+
+ if (str == NULL)
+ return -EINVAL;
+ if (tk->re_str != NULL) // XXX allow to replace
+ return -EEXIST;
+
+ ret = -ENOMEM;
+ tk->re_str = ec_strdup(str);
+ if (tk->re_str == NULL)
+ goto fail;
+
+ ret = -EINVAL;
+ if (regcomp(&tk->re, tk->re_str, REG_EXTENDED) != 0)
+ goto fail;
+
+ return 0;
+
+fail:
+ ec_free(tk->re_str);
+ return ret;
+}
+
+struct ec_tk *ec_tk_re(const char *id, const char *re_str)
+{
+ struct ec_tk *gen_tk = NULL;
+
+ gen_tk = ec_tk_re_new(id);
+ if (gen_tk == NULL)
+ goto fail;
+
+ if (ec_tk_re_set_regexp(gen_tk, re_str) < 0)
+ goto fail;
+
+ return gen_tk;
+
+fail:
+ ec_tk_free(gen_tk);
+ return NULL;
+}
+
+static int ec_tk_re_testcase(void)
+{
+ struct ec_tk *tk;
+ int ret = 0;
+
+ tk = ec_tk_re(NULL, "fo+|bar");
+ if (tk == NULL) {
+ ec_log(EC_LOG_ERR, "cannot create tk\n");
+ return -1;
+ }
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo");
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo", "bar");
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "bar");
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "foobar");
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, " foo");
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "");
+ ec_tk_free(tk);
+
+ return ret;
+}
+
+static struct ec_test ec_tk_re_test = {
+ .name = "tk_re",
+ .test = ec_tk_re_testcase,
+};
+
+EC_TEST_REGISTER(ec_tk_re_test);
--- /dev/null
+/*
+ * Copyright (c) 2016, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the University of California, Berkeley nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ECOLI_TK_RE_
+#define ECOLI_TK_RE_
+
+#include <ecoli_tk.h>
+
+struct ec_tk *ec_tk_re(const char *id, const char *str);
+
+struct ec_tk *ec_tk_re_new(const char *id);
+
+/* re is duplicated */
+int ec_tk_re_set_regexp(struct ec_tk *tk, const char *re);
+
+#endif
if (new_vec == NULL)
goto fail;
+ printf("--------\n");
+ ec_strvec_dump(stdout, new_vec);
child_parsed_tk = ec_tk_parse_tokens(tk->child, new_vec);
if (child_parsed_tk == NULL)
goto fail;
ec_tk_free(tk->child);
}
-static struct ec_tk_ops ec_tk_re_lex_ops = {
- .typename = "re_lex",
+static struct ec_tk_type ec_tk_re_lex_type = {
+ .name = "re_lex",
.parse = ec_tk_re_lex_parse,
//.complete = ec_tk_re_lex_complete, //XXX
.free_priv = ec_tk_re_lex_free_priv,
};
+EC_TK_TYPE_REGISTER(ec_tk_re_lex_type);
+
int ec_tk_re_lex_add(struct ec_tk *gen_tk, const char *pattern, int keep)
{
struct ec_tk_re_lex *tk = (struct ec_tk_re_lex *)gen_tk;
if (child == NULL)
return NULL;
- tk = (struct ec_tk_re_lex *)ec_tk_new(id, &ec_tk_re_lex_ops,
+ tk = (struct ec_tk_re_lex *)ec_tk_new(id, &ec_tk_re_lex_type,
sizeof(*tk));
if (tk == NULL) {
ec_tk_free(child);
}
/* XXX add ^ automatically ? */
- ret |= ec_tk_re_lex_add(tk, "^[a-zA-Z]+", 1);
- ret |= ec_tk_re_lex_add(tk, "^[0-9]+", 1);
- ret |= ec_tk_re_lex_add(tk, "^=", 1);
- ret |= ec_tk_re_lex_add(tk, "^-", 1);
- ret |= ec_tk_re_lex_add(tk, "^\\+", 1);
- ret |= ec_tk_re_lex_add(tk, "^[ ]+", 0);
+ ret |= ec_tk_re_lex_add(tk, "[a-zA-Z]+", 1);
+ ret |= ec_tk_re_lex_add(tk, "[0-9]+", 1);
+ ret |= ec_tk_re_lex_add(tk, "=", 1);
+ ret |= ec_tk_re_lex_add(tk, "-", 1);
+ ret |= ec_tk_re_lex_add(tk, "\\+", 1);
+ ret |= ec_tk_re_lex_add(tk, "[ ]+", 0);
if (ret != 0) {
ec_log(EC_LOG_ERR, "cannot add regexp to token\n");
ec_tk_free(tk);
.test = ec_tk_re_lex_testcase,
};
-EC_REGISTER_TEST(ec_tk_re_lex_test);
+EC_TEST_REGISTER(ec_tk_re_lex_test);
if (!ec_parsed_tk_matches(child_parsed_tk)) {
ec_parsed_tk_free(child_parsed_tk);
+ // XXX ec_parsed_tk_free_children needed? see subset.c
ec_parsed_tk_free_children(parsed_tk);
return parsed_tk;
}
ec_free(tk->table);
}
-static struct ec_tk_ops ec_tk_seq_ops = {
- .typename = "seq",
+static struct ec_tk_type ec_tk_seq_type = {
+ .name = "seq",
.parse = ec_tk_seq_parse,
.complete = ec_tk_seq_complete,
.free_priv = ec_tk_seq_free_priv,
};
+EC_TK_TYPE_REGISTER(ec_tk_seq_type);
+
int ec_tk_seq_add(struct ec_tk *gen_tk, struct ec_tk *child)
{
struct ec_tk_seq *tk = (struct ec_tk_seq *)gen_tk;
struct ec_tk *gen_tk = NULL;
struct ec_tk_seq *tk = NULL;
- gen_tk = ec_tk_new(id, &ec_tk_seq_ops, sizeof(*tk));
+ gen_tk = ec_tk_new(id, &ec_tk_seq_type, sizeof(*tk));
if (gen_tk == NULL)
return NULL;
.test = ec_tk_seq_testcase,
};
-EC_REGISTER_TEST(ec_tk_seq_test);
+EC_TEST_REGISTER(ec_tk_seq_test);
struct ec_tk *ec_tk_seq(const char *id);
/* child is consumed */
-/* all token given in the list will be freed when freeing this one */
+// XXX add_child?
int ec_tk_seq_add(struct ec_tk *tk, struct ec_tk *child);
#endif
ec_tk_free(tk->child);
}
-static struct ec_tk_ops ec_tk_sh_lex_ops = {
- .typename = "sh_lex",
+static struct ec_tk_type ec_tk_sh_lex_type = {
+ .name = "sh_lex",
.parse = ec_tk_sh_lex_parse,
.complete = ec_tk_sh_lex_complete,
.free_priv = ec_tk_sh_lex_free_priv,
};
+EC_TK_TYPE_REGISTER(ec_tk_sh_lex_type);
+
struct ec_tk *ec_tk_sh_lex_new(const char *id, struct ec_tk *child)
{
struct ec_tk_sh_lex *tk = NULL;
if (child == NULL)
return NULL;
- tk = (struct ec_tk_sh_lex *)ec_tk_new(id, &ec_tk_sh_lex_ops,
+ tk = (struct ec_tk_sh_lex *)ec_tk_new(id, &ec_tk_sh_lex_type,
sizeof(*tk));
if (tk == NULL) {
ec_tk_free(child);
.test = ec_tk_sh_lex_testcase,
};
-EC_REGISTER_TEST(ec_tk_sh_lex_test);
+EC_TEST_REGISTER(ec_tk_sh_lex_test);
return NULL;
}
-static struct ec_tk_ops ec_tk_space_ops = {
- .typename = "space",
+static struct ec_tk_type ec_tk_space_type = {
+ .name = "space",
.parse = ec_tk_space_parse,
.complete = ec_tk_default_complete,
};
+EC_TK_TYPE_REGISTER(ec_tk_space_type);
+
struct ec_tk *ec_tk_space_new(const char *id)
{
- return ec_tk_new(id, &ec_tk_space_ops, sizeof(struct ec_tk_space));
+ return ec_tk_new(id, &ec_tk_space_type, sizeof(struct ec_tk_space));
}
static int ec_tk_space_testcase(void)
.test = ec_tk_space_testcase,
};
-EC_REGISTER_TEST(ec_tk_space_test);
+EC_TEST_REGISTER(ec_tk_space_test);
ec_free(tk->string);
}
-static const struct ec_tk_ops ec_tk_str_ops = {
- .typename = "str",
+static struct ec_tk_type ec_tk_str_type = {
+ .name = "str",
.parse = ec_tk_str_parse,
.complete = ec_tk_str_complete,
.desc = ec_tk_str_desc,
.free_priv = ec_tk_str_free_priv,
};
+EC_TK_TYPE_REGISTER(ec_tk_str_type);
+
struct ec_tk *ec_tk_str_new(const char *id)
{
struct ec_tk *gen_tk = NULL;
- gen_tk = ec_tk_new(id, &ec_tk_str_ops, sizeof(struct ec_tk_str));
+ gen_tk = ec_tk_new(id, &ec_tk_str_type, sizeof(struct ec_tk_str));
if (gen_tk == NULL)
return NULL;
if (str == NULL)
return -EINVAL;
if (tk->string != NULL)
- return -EEXIST;
+ return -EEXIST; // XXX allow to replace
tk->string = ec_strdup(str);
if (tk->string == NULL)
struct ec_tk *tk;
int ret = 0;
+ /* XXX use EC_NO_ID instead of NULL */
tk = ec_tk_str(NULL, "foo");
if (tk == NULL) {
ec_log(EC_LOG_ERR, "cannot create tk\n");
.test = ec_tk_str_testcase,
};
-EC_REGISTER_TEST(ec_tk_str_test);
+EC_TEST_REGISTER(ec_tk_str_test);
--- /dev/null
+/*
+ * Copyright (c) 2016-2017, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the University of California, Berkeley nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <stdbool.h>
+
+#include <ecoli_malloc.h>
+#include <ecoli_log.h>
+#include <ecoli_strvec.h>
+#include <ecoli_tk.h>
+#include <ecoli_tk_subset.h>
+#include <ecoli_tk_str.h>
+#include <ecoli_tk_or.h>
+#include <ecoli_test.h>
+
+struct ec_tk_subset {
+ struct ec_tk gen;
+ struct ec_tk **table;
+ unsigned int len;
+};
+
+struct parse_result {
+ struct ec_parsed_tk **parsed_table; /* list of parsed tk */
+ size_t parsed_table_len; /* number of parsed tk */
+ size_t len; /* consumed strings */
+};
+
+static int __ec_tk_subset_parse(struct ec_tk **table, size_t table_len,
+ const struct ec_strvec *strvec,
+ struct parse_result *out)
+{
+ struct ec_tk **child_table;
+ struct ec_parsed_tk *child_parsed_tk = NULL;
+ struct ec_strvec *childvec = NULL;
+ size_t i, j, len = 0;
+ struct parse_result best_result, result;
+ int ret;
+
+ if (table_len == 0)
+ return 0;
+
+ memset(&best_result, 0, sizeof(best_result));
+ best_result.parsed_table =
+ ec_calloc(table_len, sizeof(*best_result.parsed_table[0]));
+ if (best_result.parsed_table == NULL)
+ goto fail;
+
+ child_table = ec_calloc(table_len - 1, sizeof(*child_table));
+ if (child_table == NULL)
+ goto fail;
+
+ for (i = 0; i < table_len; i++) {
+ /* try to parse elt i */
+ child_parsed_tk = ec_tk_parse_tokens(table[i], strvec);
+ if (child_parsed_tk == NULL)
+ goto fail;
+
+ if (!ec_parsed_tk_matches(child_parsed_tk)) {
+ ec_parsed_tk_free(child_parsed_tk);
+ child_parsed_tk = NULL;
+ continue;
+ }
+
+ /* build a new table without elt i */
+ for (j = 0; j < table_len; j++) {
+ if (j < i)
+ child_table[j] = table[j];
+ else if (j > i)
+ child_table[j - 1] = table[j];
+ }
+
+ /* build a new strvec */
+ len = ec_parsed_tk_len(child_parsed_tk);
+ childvec = ec_strvec_ndup(strvec, len,
+ ec_strvec_len(strvec) - len);
+ if (childvec == NULL)
+ goto fail;
+
+ memset(&result, 0, sizeof(result));
+
+ ret = __ec_tk_subset_parse(child_table, table_len - 1,
+ childvec, &result);
+ ec_strvec_free(childvec);
+ childvec = NULL;
+ if (ret < 0)
+ goto fail;
+
+ /* if result is not the best, ignore */
+ if (result.parsed_table_len + 1 <=
+ best_result.parsed_table_len) {
+ ec_parsed_tk_free(child_parsed_tk);
+ child_parsed_tk = NULL;
+ for (j = 0; j < result.parsed_table_len; j++)
+ ec_parsed_tk_free(result.parsed_table[j]);
+ ec_free(result.parsed_table);
+ memset(&result, 0, sizeof(result));
+ continue;
+ }
+
+ /* replace the previous best result */
+ for (j = 0; j < best_result.parsed_table_len; j++)
+ ec_parsed_tk_free(best_result.parsed_table[j]);
+ best_result.parsed_table[0] = child_parsed_tk;
+ child_parsed_tk = NULL;
+ for (j = 0; j < result.parsed_table_len; j++)
+ best_result.parsed_table[j+1] = result.parsed_table[j];
+ best_result.parsed_table_len = result.parsed_table_len + 1;
+ best_result.len = len + result.len;
+ ec_free(result.parsed_table);
+ }
+
+ *out = best_result;
+ ec_free(child_table);
+
+ return 0;
+
+ fail:
+ ec_parsed_tk_free(child_parsed_tk);
+ ec_strvec_free(childvec);
+ for (j = 0; j < best_result.parsed_table_len; j++)
+ ec_parsed_tk_free(best_result.parsed_table[j]);
+ ec_free(best_result.parsed_table);
+ ec_free(child_table);
+ return -1;
+}
+
+static struct ec_parsed_tk *ec_tk_subset_parse(const struct ec_tk *gen_tk,
+ const struct ec_strvec *strvec)
+{
+ struct ec_tk_subset *tk = (struct ec_tk_subset *)gen_tk;
+ struct ec_parsed_tk *parsed_tk = NULL;
+ struct parse_result result;
+ struct ec_strvec *match_strvec;
+ size_t i;
+ int ret;
+
+ printf("---------parse\n");
+ ec_strvec_dump(stdout, strvec);
+
+ memset(&result, 0, sizeof(result));
+
+ parsed_tk = ec_parsed_tk_new();
+ if (parsed_tk == NULL)
+ goto fail;
+
+ ret = __ec_tk_subset_parse(tk->table, tk->len, strvec, &result);
+ if (ret < 0)
+ goto fail;
+
+ if (result.parsed_table_len == 0) {
+ ec_free(result.parsed_table);
+ return parsed_tk;
+ }
+
+ for (i = 0; i < result.parsed_table_len; i++) {
+ ec_parsed_tk_add_child(parsed_tk, result.parsed_table[i]);
+ result.parsed_table[i] = NULL;
+ }
+ ec_free(result.parsed_table);
+ result.parsed_table = NULL;
+
+ match_strvec = ec_strvec_ndup(strvec, 0, result.len);
+ if (match_strvec == NULL)
+ goto fail;
+
+ ec_parsed_tk_set_match(parsed_tk, gen_tk, match_strvec);
+
+ return parsed_tk;
+
+ fail:
+ for (i = 0; i < result.parsed_table_len; i++)
+ ec_parsed_tk_free(result.parsed_table[i]);
+ ec_free(result.parsed_table);
+ ec_parsed_tk_free(parsed_tk);
+
+ return NULL;
+}
+
+static struct ec_completed_tk *ec_tk_subset_complete(const struct ec_tk *gen_tk,
+ const struct ec_strvec *strvec)
+{
+ (void)gen_tk;
+ (void)strvec;
+#if 0
+ struct ec_tk_subset *tk = (struct ec_tk_subset *)gen_tk;
+ struct ec_completed_tk *completed_tk = NULL, *child_completed_tk;
+ struct ec_parsed_tk **child_parsed_tab = NULL;
+ struct ec_strvec *childvec = NULL;
+ size_t i, len;
+
+ completed_tk = ec_completed_tk_new();
+ if (completed_tk == NULL)
+ goto fail;
+
+ child_parsed_tab = __ec_tk_subset_parse(gen_tk, strvec, &len);
+ if (child_parsed_tab == NULL)
+ goto fail;
+
+ childvec = ec_strvec_ndup(strvec, len, ec_strvec_len(strvec) - len);
+ for (i = 0; i < tk->len; i++) {
+ if (child_parsed_tab[i] != NULL)
+ continue;
+
+ child_completed_tk = ec_tk_complete_tokens(tk->table[i],
+ childvec);
+
+ if (child_completed_tk == NULL) // XXX fail instead?
+ continue;
+
+ ec_completed_tk_merge(completed_tk, child_completed_tk);
+ }
+ ec_strvec_free(childvec);
+ if (len > 0) {
+ childvec = ec_strvec_ndup(strvec, len - 1,
+ ec_strvec_len(strvec) - len + 1);
+ for (i = 0; i < tk->len; i++) {
+ if (child_parsed_tab[i] != NULL)
+ continue;
+
+ child_completed_tk = ec_tk_complete_tokens(tk->table[i],
+ childvec);
+
+ if (child_completed_tk == NULL) // XXX fail instead?
+ continue;
+
+ ec_completed_tk_merge(completed_tk, child_completed_tk);
+ }
+ ec_strvec_free(childvec);
+ }
+
+ for (i = 0; i < tk->len; i++)
+ ec_parsed_tk_free(child_parsed_tab[i]);
+ ec_free(child_parsed_tab);
+
+ return completed_tk;
+
+fail:
+ ec_strvec_free(childvec);
+ for (i = 0; i < tk->len; i++)
+ ec_parsed_tk_free(child_parsed_tab[i]);
+ ec_free(child_parsed_tab);
+ ec_completed_tk_free(completed_tk);
+#endif
+ return NULL;
+}
+
+static void ec_tk_subset_free_priv(struct ec_tk *gen_tk)
+{
+ struct ec_tk_subset *tk = (struct ec_tk_subset *)gen_tk;
+ unsigned int i;
+
+ for (i = 0; i < tk->len; i++)
+ ec_tk_free(tk->table[i]);
+ ec_free(tk->table);
+}
+
+int ec_tk_subset_add(struct ec_tk *gen_tk, struct ec_tk *child)
+{
+ struct ec_tk_subset *tk = (struct ec_tk_subset *)gen_tk;
+ struct ec_tk **table;
+
+ assert(tk != NULL);
+
+ if (child == NULL)
+ return -EINVAL;
+
+ gen_tk->flags &= ~EC_TK_F_BUILT;
+
+ table = ec_realloc(tk->table, (tk->len + 1) * sizeof(*tk->table));
+ if (table == NULL) {
+ ec_tk_free(child);
+ return -1;
+ }
+
+ tk->table = table;
+ table[tk->len] = child;
+ tk->len++;
+
+ child->parent = gen_tk;
+ TAILQ_INSERT_TAIL(&gen_tk->children, child, next);
+
+ return 0;
+}
+
+static struct ec_tk_type ec_tk_subset_type = {
+ .name = "tk_subset",
+ .parse = ec_tk_subset_parse,
+ .complete = ec_tk_subset_complete,
+ .free_priv = ec_tk_subset_free_priv,
+};
+
+EC_TK_TYPE_REGISTER(ec_tk_subset_type);
+
+struct ec_tk *ec_tk_subset(const char *id)
+{
+ struct ec_tk *gen_tk = NULL;
+ struct ec_tk_subset *tk = NULL;
+
+ gen_tk = ec_tk_new(id, &ec_tk_subset_type, sizeof(*tk));
+ if (gen_tk == NULL)
+ return NULL;
+
+ tk = (struct ec_tk_subset *)gen_tk;
+ tk->table = NULL;
+ tk->len = 0;
+
+ return gen_tk;
+}
+
+struct ec_tk *__ec_tk_subset(const char *id, ...)
+{
+ struct ec_tk *gen_tk = NULL;
+ struct ec_tk_subset *tk = NULL;
+ struct ec_tk *child;
+ va_list ap;
+ int fail = 0;
+
+ va_start(ap, id);
+
+ gen_tk = ec_tk_subset(id);
+ tk = (struct ec_tk_subset *)gen_tk;
+ if (tk == NULL)
+ fail = 1;;
+
+ for (child = va_arg(ap, struct ec_tk *);
+ child != EC_TK_ENDLIST;
+ child = va_arg(ap, struct ec_tk *)) {
+
+ /* on error, don't quit the loop to avoid leaks */
+ if (fail == 1 || child == NULL ||
+ ec_tk_subset_add(gen_tk, child) < 0) {
+ fail = 1;
+ ec_tk_free(child);
+ }
+ }
+
+ if (fail == 1)
+ goto fail;
+
+ va_end(ap);
+ return gen_tk;
+
+fail:
+ ec_tk_free(gen_tk); /* will also free children */
+ va_end(ap);
+ return NULL;
+}
+
+static int ec_tk_subset_testcase(void)
+{
+ struct ec_tk *tk;
+ int ret = 0;
+
+ tk = EC_TK_SUBSET(NULL,
+ EC_TK_OR(NULL,
+ ec_tk_str(NULL, "foo"),
+ ec_tk_str(NULL, "bar")),
+ ec_tk_str(NULL, "bar"),
+ ec_tk_str(NULL, "toto")
+ );
+ if (tk == NULL) {
+ ec_log(EC_LOG_ERR, "cannot create tk\n");
+ return -1;
+ }
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo");
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "bar");
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 2, "foo", "bar", "titi");
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 3, "bar", "foo", "toto");
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo", "foo");
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 2, "bar", "bar");
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 2, "bar", "foo");
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, " ");
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "foox");
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "titi");
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "");
+ ec_tk_free(tk);
+
+ /* test completion */
+ tk = EC_TK_SUBSET(NULL,
+ ec_tk_str(NULL, "foo"),
+ ec_tk_str(NULL, "bar"),
+ ec_tk_str(NULL, "bar2"),
+ ec_tk_str(NULL, "toto"),
+ ec_tk_str(NULL, "titi")
+ );
+ if (tk == NULL) {
+ ec_log(EC_LOG_ERR, "cannot create tk\n");
+ return -1;
+ }
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "", EC_TK_ENDLIST,
+ "foo", "bar", "bar2", "toto", "titi", EC_TK_ENDLIST,
+ "");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "bar", "bar2", "", EC_TK_ENDLIST,
+ "foo", "toto", "titi", EC_TK_ENDLIST,
+ "");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "f", EC_TK_ENDLIST,
+ "oo", EC_TK_ENDLIST,
+ "oo");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "b", EC_TK_ENDLIST,
+ "ar", "ar2", EC_TK_ENDLIST,
+ "ar");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "bar", EC_TK_ENDLIST,
+ "", "2", EC_TK_ENDLIST,
+ "");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "bar", "b", EC_TK_ENDLIST,
+ "bar2", EC_TK_ENDLIST,
+ "ar2");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "t", EC_TK_ENDLIST,
+ "oto", "iti", EC_TK_ENDLIST,
+ "");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "to", EC_TK_ENDLIST,
+ "to", EC_TK_ENDLIST,
+ "to");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "x", EC_TK_ENDLIST,
+ EC_TK_ENDLIST,
+ "");
+ ec_tk_free(tk);
+
+ return ret;
+}
+
+static struct ec_test ec_tk_subset_test = {
+ .name = "tk_subset",
+ .test = ec_tk_subset_testcase,
+};
+
+EC_TEST_REGISTER(ec_tk_subset_test);
--- /dev/null
+/*
+ * Copyright (c) 2016-2017, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the University of California, Berkeley nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ECOLI_TK_SUBSET_
+#define ECOLI_TK_SUBSET_
+
+#include <ecoli_tk.h>
+
+#define EC_TK_SUBSET(args...) __ec_tk_subset(args, EC_TK_ENDLIST)
+
+/* list must be terminated with EC_TK_ENDLIST */
+/* all token given in the list will be freed when freeing this one */
+/* avoid using this function directly, prefer the macro EC_TK_SUBSET() or
+ * ec_tk_subset() + ec_tk_subset_add() */
+struct ec_tk *__ec_tk_subset(const char *id, ...);
+
+struct ec_tk *ec_tk_subset(const char *id);
+
+/* child is consumed */
+int ec_tk_subset_add(struct ec_tk *tk, struct ec_tk *child);
+
+#endif
return ec_tk_complete_tokens(tk->child, strvec);
}
-static struct ec_tk_ops ec_tk_weakref_ops = {
- .typename = "weakref",
+static struct ec_tk_type ec_tk_weakref_type = {
+ .name = "weakref",
.parse = ec_tk_weakref_parse,
.complete = ec_tk_weakref_complete,
};
+EC_TK_TYPE_REGISTER(ec_tk_weakref_type);
+
struct ec_tk *ec_tk_weakref_empty(const char *id)
{
struct ec_tk_weakref *tk = NULL;
tk = (struct ec_tk_weakref *)ec_tk_new(id,
- &ec_tk_weakref_ops, sizeof(*tk));
+ &ec_tk_weakref_type, sizeof(*tk));
if (tk == NULL)
return NULL;
.test = ec_tk_weakref_testcase,
};
-EC_REGISTER_TEST(ec_tk_weakref_test);
+EC_TEST_REGISTER(ec_tk_weakref_test);
#include <ecoli_tk_or.h>
#include <ecoli_tk_sh_lex.h>
#include <ecoli_tk_int.h>
+#include <ecoli_tk_option.h>
+#include <ecoli_tk_cmd.h>
static struct ec_tk *commands;
cmd = EC_TK_SEQ(NULL,
ec_tk_str(NULL, "hello"),
- EC_TK_OR(NULL,
- EC_TK_OR("name",
- ec_tk_str(NULL, "john"),
- ec_tk_str(NULL, "johnny"),
- ec_tk_str(NULL, "mike")
- ),
- ec_tk_int("int", 0, 10, 10)
- )
+ EC_TK_OR("name",
+ ec_tk_str(NULL, "john"),
+ ec_tk_str(NULL, "johnny"),
+ ec_tk_str(NULL, "mike")
+ ),
+ ec_tk_option_new(NULL, ec_tk_int("int", 0, 10, 10))
);
- ec_keyval_set(ec_tk_attrs(cmd), "help", "say hello to someone", NULL);
+ if (cmd == NULL)
+ goto fail;
+ ec_keyval_set(ec_tk_attrs(cmd), "help",
+ "say hello to someone several times", NULL);
ec_keyval_set(ec_tk_attrs(ec_tk_find(cmd, "name")),
"help", "the name of the person", NULL);
ec_keyval_set(ec_tk_attrs(ec_tk_find(cmd, "int")),
if (ec_tk_or_add(cmdlist, cmd) < 0)
goto fail;
+#if 0
+ cmd = EC_TK_CMD(NULL, "good morning john|johnny|mike [count]",
+ ec_tk_int("count", 0, 10, 10));
+ if (cmd == NULL)
+ goto fail;
+ ec_keyval_set(ec_tk_attrs(cmd), "help",
+ "say good morning to someone several times", NULL);
+ if (ec_tk_or_add(cmdlist, cmd) < 0)
+ goto fail;
+#endif
+
cmd = EC_TK_SEQ(NULL,
ec_tk_str(NULL, "bye")
);
return 0;
}
-static void parse_args(int argc, char **argv)
+static int parse_args(int argc, char **argv)
{
- int opt;
+ int ret, opt;
while ((opt = getopt_long(argc, argv, ec_short_options,
ec_long_options, NULL)) != EOF) {
default:
usage(argv[0]);
- exit(1);
+ return -1;
}
}
+
+ ret = optind - 1;
+ optind = 1;
+
+ return ret;
}
TAILQ_HEAD(debug_alloc_hdr_list, debug_alloc_hdr);
int main(int argc, char **argv)
{
- int ret, leaks;
+ int i, ret = 0, leaks;
+
+ ret = parse_args(argc, argv);
+ if (ret < 0)
+ return 1;
+
+ argc -= ret;
+ argv += ret;
- parse_args(argc, argv);
srandom(seed);
ec_log_register(debug_log, NULL);
return -1;
}
- ret = ec_test_all();
+ ret = 0;
+ if (argc <= 1) {
+ ret = ec_test_all();
+ } else {
+ for (i = 1; i < argc; i++)
+ ret |= ec_test_one(argv[i]);
+ }
ec_malloc_unregister();
leaks = debug_alloc_dump_leaks();
SEED=100
while [ ${SEED} -gt 0 ]; do
- CMD="./build/test --random-alloc-fail=10 --seed=${SEED}"
+ CMD="./build/test --random-alloc-fail=1 --seed=${SEED}"
${CMD} --log-level=0 || (
echo "=== test failed, replay seed=${SEED} with logs ===" &&
${CMD} --log-level=6 ||
-- evaluate expression tree in ec_tk_expr
+tk_cmd
+======
+
+X evaluate expression tree in ec_tk_expr
- cmd token
-- node regexp
-- node which always matches
-- yaml interface to create nodes
+- example
+- tk_re
+
+cleanup
+=======
+
- check XXX in code
+- remove the _new() functions
+- add a tk vector type: will be used in several nodes (ex: or, seq, ...)
+- check allocation model everywhere
+- checkpatch?
+- use linux style (update .emacs)
+- better logs
+- return values
+- missing static / const
+- license: "s/neither the name...may/the names of its contributors may not/"
+- check all completion nodes
+- split ecoli_tk.h
+- cache results when appropriate?
+- size_t or unsigned int?
+
+logs
+====
+
+- register log types
+
+yaml
+====
+
+X register nodes by name
+- yaml interface to create nodes
+- example
+
+examples
+========
+
- example which parses arguments (argc/argv)
- example that acts as bash completion (ip link ?)
- calculator example (var assignation, expression evaluation)
+- example with libedit
+- mini script language
+- configuration file
+
+doc
+===
+
+- overview
+- add api doc in .h
+- generate automatic api doc
+- architecture
+- coding rules, process
+- each node
+- allocation model
+- say that it stops at first match (no ambigous support)
+
+build framework
+===============
+
+- .map files for API
+- split libs, tests and examples
+- add make help
+- add make config
+- -fvisibility=
+
+tests
+=====
+
+- complete automatic tests with "make test"
+
+new nodes
+=========
+
+- regexp
+- node which always matches
+- file + partial completion
+- ether, ip, network
+- fusion node: need to match several children, same for completion
+- float
+
+encoding
+========
+
+- support utf-8 and other encodings
+- example
+- documentation
+
+netconf example
+===============
+
+- demonstration example that parses yang file and generate cli