save
authorOlivier Matz <zer0@droids-corp.org>
Sat, 8 Apr 2017 11:41:40 +0000 (13:41 +0200)
committerOlivier Matz <zer0@droids-corp.org>
Sat, 8 Apr 2017 11:41:40 +0000 (13:41 +0200)
lib/Makefile
lib/ecoli_strvec.h
lib/ecoli_tk.c
lib/ecoli_tk_expr.c
lib/ecoli_tk_expr.h
lib/ecoli_tk_expr_test.c [new file with mode: 0644]
lib/ecoli_tk_re_lex.c
lib/ecoli_tk_re_lex.h
lib/main.c
lib/todo.txt [new file with mode: 0644]

index ca072a5..ecb030b 100644 (file)
@@ -30,14 +30,30 @@ include $(ECOLI)/mk/ecoli-pre.mk
 # output path with trailing slash
 O ?= build/
 
-CFLAGS  = -g -O0 -Wall -Werror -W -fPIC
+CFLAGS  = -g -O0 -Wall -Werror -W -Wextra -fPIC -Wmissing-prototypes
 CFLAGS += -I.
 
-srcs := ecoli_tk.c ecoli_malloc.c ecoli_log.c ecoli_strvec.c
-srcs += ecoli_keyval.c ecoli_test.c
-srcs += ecoli_tk_str.c ecoli_tk_seq.c ecoli_tk_space.c ecoli_tk_or.c
-srcs += ecoli_tk_empty.c ecoli_tk_int.c ecoli_tk_option.c ecoli_tk_many.c
-srcs += ecoli_tk_sh_lex.c ecoli_tk_expr.c ecoli_tk_weakref.c ecoli_tk_re_lex.c
+srcs :=
+srcs += ecoli_keyval.c
+srcs += ecoli_log.c
+srcs += ecoli_malloc.c
+srcs += ecoli_strvec.c
+srcs += ecoli_test.c
+srcs += ecoli_tk.c
+srcs += ecoli_tk_empty.c
+srcs += ecoli_tk_expr.c
+srcs += ecoli_tk_expr_test.c
+srcs += ecoli_tk_int.c
+srcs += ecoli_tk_many.c
+srcs += ecoli_tk_option.c
+srcs += ecoli_tk_or.c
+srcs += ecoli_tk_re_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_weakref.c
+
 shlib-y-$(O)libecoli.so := $(srcs)
 
 ldflags-$(O)test = -rdynamic
index 6178777..d94937b 100644 (file)
@@ -28,6 +28,9 @@
 #ifndef ECOLI_STRVEC_
 #define ECOLI_STRVEC_
 
+#include <sys/types.h>
+#include <stdio.h>
+
 struct ec_strvec *ec_strvec_new(void);
 int ec_strvec_add(struct ec_strvec *strvec, const char *s);
 struct ec_strvec *ec_strvec_dup(const struct ec_strvec *strvec);
index 624a5b9..9d877c0 100644 (file)
@@ -243,16 +243,26 @@ static void __ec_parsed_tk_dump(FILE *out,
        size_t i;
        const char *id = "None", *typename = "None";
 
-       /* XXX enhance */
-       for (i = 0; i < indent; i++)
-               fprintf(out, " ");
-
        if (parsed_tk->tk != NULL) {
                if (parsed_tk->tk->id != NULL)
                        id = parsed_tk->tk->id;
                typename = parsed_tk->tk->ops->typename;
        }
 
+       /* 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)
+                       fprintf(out, " ");
+               else
+                       fprintf(out, "|");
+       }
+
        fprintf(out, "tk_type=%s id=%s vec=[", typename, id);
        vec = ec_parsed_tk_strvec(parsed_tk);
        for (i = 0; i < ec_strvec_len(vec); i++)
@@ -267,6 +277,8 @@ static void __ec_parsed_tk_dump(FILE *out,
 
 void ec_parsed_tk_dump(FILE *out, const struct ec_parsed_tk *parsed_tk)
 {
+       fprintf(out, "------------------- dump:\n");
+
        if (parsed_tk == NULL) {
                fprintf(out, "parsed_tk is NULL, error in parse\n");
                return;
index fe28b20..7f1c3cb 100644 (file)
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
 #include <string.h>
 #include <assert.h>
 #include <stdarg.h>
-#include <limits.h>
 #include <errno.h>
 
 #include <ecoli_malloc.h>
 #include <ecoli_test.h>
 #include <ecoli_strvec.h>
 #include <ecoli_tk.h>
-#include <ecoli_tk_str.h>
-#include <ecoli_tk_int.h>
 #include <ecoli_tk_seq.h>
 #include <ecoli_tk_many.h>
 #include <ecoli_tk_or.h>
-#include <ecoli_tk_expr.h>
 #include <ecoli_tk_weakref.h>
+#include <ecoli_tk_expr.h>
 
 struct ec_tk_expr {
        struct ec_tk gen;
@@ -148,23 +147,29 @@ static int ec_tk_expr_build(struct ec_tk *gen_tk)
                        goto fail;
        }
 
-       term = ec_tk_or("term");
-       if (term == NULL)
+       post = ec_tk_or("post");
+       if (post == NULL)
                goto fail;
-       if (ec_tk_or_add(term, ec_tk_clone(tk->val_tk)) < 0)
+       if (ec_tk_or_add(post, ec_tk_clone(tk->val_tk)) < 0)
                goto fail;
-       if (ec_tk_or_add(term,
+       if (ec_tk_or_add(post,
                EC_TK_SEQ(NULL,
                        ec_tk_clone(pre_op),
                        ec_tk_clone(weak))) < 0)
                goto fail;
        for (i = 0; i < tk->paren_len; i++) {
-               if (ec_tk_or_add(term, EC_TK_SEQ(NULL,
+               if (ec_tk_or_add(post, EC_TK_SEQ(NULL,
                                        ec_tk_clone(tk->open_ops[i]),
                                        ec_tk_clone(weak),
                                        ec_tk_clone(tk->close_ops[i]))) < 0)
                        goto fail;
        }
+       term = EC_TK_SEQ("term",
+               ec_tk_clone(post),
+               ec_tk_many(NULL, ec_tk_clone(post_op), 0, 0)
+       );
+       if (term == NULL)
+               goto fail;
 
        for (i = 0; i < tk->bin_ops_len; i++) {
                next = EC_TK_SEQ("next",
@@ -182,21 +187,14 @@ static int ec_tk_expr_build(struct ec_tk *gen_tk)
                if (term == NULL)
                        goto fail;
        }
-
-       expr = EC_TK_SEQ("expr",
-               ec_tk_clone(term),
-               ec_tk_many(NULL, ec_tk_clone(post_op), 0, 0)
-       );
-       if (expr == NULL)
-               goto fail;
+       expr = term;
+       term = NULL;
 
        /* free the initial references */
        ec_tk_free(pre_op);
        pre_op = NULL;
        ec_tk_free(post_op);
        post_op = NULL;
-       ec_tk_free(term);
-       term = NULL;
        ec_tk_free(post);
        post = NULL;
 
@@ -411,153 +409,220 @@ fail:
        return ret;
 }
 
-static int ec_tk_expr_testcase_manual(void)
+enum expr_tk_type {
+       NONE,
+       VAL,
+       BIN_OP,
+       PRE_OP,
+       POST_OP,
+       PAREN,
+};
+static enum expr_tk_type get_tk_type(const struct ec_tk *expr_gen_tk,
+       const struct ec_tk *check_tk)
 {
-       struct ec_tk *term = NULL, *factor = NULL, *expr = NULL, *val = NULL,
-               *pre_op = NULL, *post_op = NULL, *post = NULL, *weak = NULL;
-       int ret = 0;
+       struct ec_tk_expr *expr_tk = (struct ec_tk_expr *)expr_gen_tk;
+       size_t i;
 
-       // XXX check all APIs: pointers are "moved", they are freed by
-       // the callee
+       if (check_tk == expr_tk->val_tk)
+               return VAL;
 
-       /* Example that generates an expression "manually". We keep it
-        * here for reference. */
+       for (i = 0; i < expr_tk->bin_ops_len; i++) {
+               if (check_tk == expr_tk->bin_ops[i])
+                       return BIN_OP;
+       }
+       for (i = 0; i < expr_tk->pre_ops_len; i++) {
+               if (check_tk == expr_tk->pre_ops[i])
+                       return PRE_OP;
+       }
+       for (i = 0; i < expr_tk->post_ops_len; i++) {
+               if (check_tk == expr_tk->post_ops[i])
+                       return POST_OP;
+       }
 
-       weak = ec_tk_weakref_empty("weak");
-       if (weak == NULL)
-               return -1;
+       /* for (i = 0; i < expr_tk->paren_len; i++) { */
+       /*      if (check_tk == expr_tk->open_ops[i]) */
+       /*              return PAREN; */
+       /* } */
 
-       /* reverse bits */
-       pre_op = EC_TK_OR("pre-op",
-               ec_tk_str(NULL, "~")
-       );
+       return NONE;
+}
 
-       /* factorial */
-       post_op = EC_TK_OR("post-op",
-               ec_tk_str(NULL, "!")
-       );
+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;
+};
 
-       val = ec_tk_int("val", 0, UCHAR_MAX, 0);
-       term = EC_TK_OR("term",
-               val,
-               EC_TK_SEQ(NULL,
-                       ec_tk_str(NULL, "("),
-                       ec_tk_clone(weak),
-                       ec_tk_str(NULL, ")")
-               ),
-               EC_TK_SEQ(NULL,
-                       ec_tk_clone(pre_op),
-                       ec_tk_clone(weak)
-               )
-       );
-       val = NULL;
-
-       factor = EC_TK_SEQ("factor",
-               ec_tk_clone(term),
-               ec_tk_many(NULL,
-                       EC_TK_SEQ(NULL,
-                               ec_tk_str(NULL, "+"),
-                               ec_tk_clone(term)
-                       ),
-                       0, 0
-               )
-       );
+static struct eval_state *eval_state_new(void *userctx)
+{
+       struct eval_state *state;
 
-       post = EC_TK_SEQ("post",
-               ec_tk_clone(factor),
-               ec_tk_many(NULL,
-                       EC_TK_SEQ(NULL,
-                               ec_tk_str(NULL, "*"),
-                               ec_tk_clone(factor)
-                       ),
-                       0, 0
-               )
-       );
+       state = ec_calloc(1, sizeof(*state));
+       state->userctx = userctx;
+       return state;
+}
 
-       expr = EC_TK_SEQ("expr",
-               ec_tk_clone(post),
-               ec_tk_many(NULL, ec_tk_clone(post_op), 0, 0)
-       );
+static void eval_state_free(struct eval_state *state)
+{
+       if (state == NULL)
+               return;
+       ec_free(state->op_stack);
+       ec_free(state);
+}
 
-       /* 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(factor);
-       factor = NULL;
-       ec_tk_free(post);
-       post = NULL;
+static int eval_state_add_val(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)
+{
+       struct ec_tk_expr_eval_result val;
+       const struct ec_parsed_tk *op_parsed_tk;
+       enum expr_tk_type type;
+
+       (void)ops;
+       (void)state;
+       printf("push val: ");
+       ec_parsed_tk_dump(stdout, parsed_tk);
+
+       val = ops->eval_var(state->userctx, parsed_tk);
+       if (val.code != 0)
+               return val.code; // XXX free val?
+
+       /* first value, it is the left operand */
+       if (state->has_lval == 0) {
+               state->lval = val;
+               state->has_lval = 1;
+               return 0;
+       }
 
-       /* no need to clone here, the node is not consumed */
-       if (ec_tk_weakref_set(weak, expr) < 0)
-               goto fail;
-       ec_tk_free(weak);
-       weak = NULL;
+       /* 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 {
+                       // ?
+               }
+       }
 
-       ret |= EC_TEST_CHECK_TK_PARSE(expr, 1, "1");
-       ret |= EC_TEST_CHECK_TK_PARSE(expr, 1, "1", "*");
-       ret |= EC_TEST_CHECK_TK_PARSE(expr, 3, "1", "*", "1");
-       ret |= EC_TEST_CHECK_TK_PARSE(expr, 3, "1", "+", "1");
-       ret |= EC_TEST_CHECK_TK_PARSE(expr, 5, "1", "*", "1", "+", "1");
-       ret |= EC_TEST_CHECK_TK_PARSE(
-               expr, 10, "~", "(", "1", "*", "(", "1", "+", "1", ")", ")");
-       ret |= EC_TEST_CHECK_TK_PARSE(expr, 4, "1", "+", "~", "1");
+       return 0;
+}
 
-       ec_tk_free(expr);
+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);
+
+       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;
+       }
 
-       return ret;
+       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++;
 
-fail:
-       ec_tk_free(term);
-       ec_tk_free(factor);
-       ec_tk_free(expr);
-       ec_tk_free(val);
-       ec_tk_free(pre_op);
-       ec_tk_free(post_op);
-       ec_tk_free(post);
-       ec_tk_free(weak);
        return 0;
 }
 
-static int ec_tk_expr_testcase(void)
+static int eval_expression(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)
+
 {
-       struct ec_tk *tk;
+       struct ec_parsed_tk *child;
+       enum expr_tk_type type;
        int ret;
 
-       ret = ec_tk_expr_testcase_manual();
-       if (ret < 0)
-               return ret;
-
-       tk = ec_tk_expr(NULL);
-       if (tk == NULL)
-               return -1;
+       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);
+               if (ret < 0)
+                       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_pre_op(tk, ec_tk_str(NULL, "!"));
-       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", "*");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, 3, "1", "*", "1");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, 3, "1", "+", "1");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, 5, "1", "*", "1", "+", "1");
-       ret |= EC_TEST_CHECK_TK_PARSE(
-               tk, 10, "~", "(", "1", "*", "(", "1", "+", "1", ")", ")");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, 4, "1", "+", "~", "1");
-       ec_tk_free(tk);
+       TAILQ_FOREACH(child, &parsed_tk->children, next) {
+               ret = eval_expression(state, ops, expr_gen_tk, child);
+               if (ret < 0)
+                       return ret;
+       }
 
-       return ret;
+       return 0;
 }
 
-static struct ec_test ec_tk_expr_test = {
-       .name = "tk_expr",
-       .test = ec_tk_expr_testcase,
-};
+struct ec_tk_expr_eval_result ec_tk_expr_eval(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;
+       int ret;
+
+       if (ops == NULL)
+               return res;
+
+       ec_parsed_tk_dump(stdout, parsed);
+
+       res.code = -ENOMEM;
+       state = eval_state_new(&userctx);
+       if (state == NULL)
+               return res;
+
+       ret = eval_expression(state, ops, tk, parsed);
+// XXX check if op stack len is 0 ?
+       res = state->lval;
+       eval_state_free(state);
+
+       if (ret < 0 && res.code == 0)
+               res.code = ret;
+
+       return res;
+}
 
-EC_REGISTER_TEST(ec_tk_expr_test);
+/* the test case is in a separate file ecoli_tk_expr_test.c */
index ca28e68..d24e0a2 100644 (file)
 
 /* 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);
+
+typedef struct ec_tk_expr_eval_result (*ec_tk_expr_eval_pre_op_t)(
+       void *userctx, struct ec_tk_expr_eval_result 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,
+       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,
+       const struct ec_parsed_tk *operator,
+       struct ec_tk_expr_eval_result operand2);
+
+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);
+
 
 struct ec_tk *ec_tk_expr(const char *id);
+int ec_tk_expr_set_val_tk(struct ec_tk *gen_tk, struct ec_tk *val_tk);
 int ec_tk_expr_add_bin_op(struct ec_tk *gen_tk, struct ec_tk *op);
 int ec_tk_expr_add_pre_op(struct ec_tk *gen_tk, struct ec_tk *op);
 int ec_tk_expr_add_post_op(struct ec_tk *gen_tk, struct ec_tk *op);
 int ec_tk_expr_add_parenthesis(struct ec_tk *gen_tk,
        struct ec_tk *open, struct ec_tk *close);
 
+struct ec_tk_expr_eval_ops {
+       ec_tk_expr_eval_var_t eval_var;
+       ec_tk_expr_eval_pre_op_t eval_pre_op;
+       ec_tk_expr_eval_post_op_t eval_post_op;
+       ec_tk_expr_eval_bin_op_t eval_bin_op;
+       ec_tk_expr_eval_parenthesis_t eval_parenthesis;
+};
+
+struct ec_tk_expr_eval_result ec_tk_expr_eval(const struct ec_tk *tk,
+       struct ec_parsed_tk *parsed, const struct ec_tk_expr_eval_ops *ops,
+       void *userctx);
 
 #endif
diff --git a/lib/ecoli_tk_expr_test.c b/lib/ecoli_tk_expr_test.c
new file mode 100644 (file)
index 0000000..858ffcf
--- /dev/null
@@ -0,0 +1,318 @@
+/*
+ * 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 <errno.h>
+#include <limits.h>
+#include <stdint.h>
+
+#include <ecoli_malloc.h>
+#include <ecoli_strvec.h>
+#include <ecoli_test.h>
+#include <ecoli_tk_int.h>
+#include <ecoli_tk_str.h>
+#include <ecoli_tk_re_lex.h>
+#include <ecoli_tk_expr.h>
+
+struct my_eval_result {
+       int val;
+};
+
+static struct ec_tk_expr_eval_result
+ec_tk_expr_test_eval_var(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;
+
+       res.code = -ENOMEM;
+       eval = ec_malloc(sizeof(*eval));
+       if (eval == NULL)
+               return res;
+
+       eval->val = atoi(ec_strvec_val(vec, 0)); // XXX use strtol
+       printf("eval var %d\n", eval->val);
+       res.code = 0;
+       res.parsed = eval;
+
+       return res;
+}
+
+static struct ec_tk_expr_eval_result
+ec_tk_expr_test_eval_pre_op(void *userctx,
+       struct ec_tk_expr_eval_result 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;;
+
+       (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 (!strcmp(ec_strvec_val(vec, 0), "~")) {
+               eval->val = !eval->val;
+       } else {
+               ec_free(eval);
+               return res;
+       }
+
+       printf("eval pre_op %d\n", eval->val);
+       res.code = 0;
+       res.parsed = eval;
+
+       return res;
+}
+
+static struct ec_tk_expr_eval_result
+ec_tk_expr_test_eval_post_op(void *userctx,
+       struct ec_tk_expr_eval_result 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;;
+
+       (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 (!strcmp(ec_strvec_val(vec, 0), "!")) {
+               eval->val = eval->val * eval->val;
+       } else {
+               ec_free(eval);
+               return res;
+       }
+
+       printf("eval post_op %d\n", eval->val);
+       res.code = 0;
+       res.parsed = eval;
+
+       return res;
+}
+
+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)
+
+{
+       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;;
+
+       (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 (!strcmp(ec_strvec_val(vec, 0), "+")) {
+               eval1->val = eval1->val + eval2->val;
+       } else if (!strcmp(ec_strvec_val(vec, 0), "*")) {
+               eval1->val = eval1->val * eval2->val;
+       } else {
+               ec_free(eval1);
+               ec_free(eval2);
+               return res;
+       }
+
+       printf("eval bin_op %d\n", eval1->val);
+       res.code = 0;
+       res.parsed = eval1;
+
+       return res;
+}
+
+static const struct ec_tk_expr_eval_ops test_ops = {
+       .eval_var = ec_tk_expr_test_eval_var,
+       .eval_pre_op = ec_tk_expr_test_eval_pre_op,
+       .eval_post_op = ec_tk_expr_test_eval_post_op,
+       .eval_bin_op = ec_tk_expr_test_eval_bin_op,
+};
+
+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;
+       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);
+       ec_parsed_tk_free(p);
+       if (res.code < 0)
+               return res.code;
+
+       /* the parsed value is an integer */
+       eval = res.parsed;
+       if (eval == NULL) {
+               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;
+}
+
+static int ec_tk_expr_testcase(void)
+{
+       struct ec_tk *tk = NULL, *lex_tk = NULL;
+       int ret = 0;
+
+       tk = ec_tk_expr("expr");
+       if (tk == NULL)
+               return -1;
+
+       ec_tk_expr_set_val_tk(tk, ec_tk_int(NULL, 0, UCHAR_MAX, 0));
+       ec_tk_expr_add_bin_op(tk, ec_tk_str(NULL, "+"));
+       ec_tk_expr_add_bin_op(tk, ec_tk_str(NULL, "*"));
+       ec_tk_expr_add_pre_op(tk, ec_tk_str(NULL, "~"));
+       ec_tk_expr_add_post_op(tk, ec_tk_str(NULL, "!"));
+       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", "*");
+       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, 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;
+
+       /* valid expressions */
+       ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "~1");
+       ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "1!");
+       ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "1! + 1");
+       ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "1 + 4 * (2 + 3!)!");
+       ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "(1)");
+       ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "3*~3+~3*(2+ 2)");
+       ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "~~(~1)! + ~(4 + (2*3))");
+       ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "(1 + 1)! * 1!");
+
+       /* invalid expressions */
+       ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, -1, "");
+       ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, -1, "()");
+       ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, -1, "(");
+       ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, -1, ")");
+       ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, -1, "+1");
+       ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, -1, "1+");
+       ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, -1, "1+*1");
+       ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, -1, "1+(1*1");
+       ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, -1, "1+~1~1)");
+       if (ret)
+               exit(ret);
+
+       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)
+         */
+
+
+       ec_tk_free(tk);
+       ec_tk_free(lex_tk);
+
+       return ret;
+
+fail:
+       ec_tk_free(lex_tk);
+       ec_tk_free(tk);
+       return -1;
+}
+
+static struct ec_test ec_tk_expr_test = {
+       .name = "tk_expr",
+       .test = ec_tk_expr_testcase,
+};
+
+EC_REGISTER_TEST(ec_tk_expr_test);
index 8d31543..60ba145 100644 (file)
@@ -153,7 +153,7 @@ static void ec_tk_re_lex_free_priv(struct ec_tk *gen_tk)
 static struct ec_tk_ops ec_tk_re_lex_ops = {
        .typename = "re_lex",
        .parse = ec_tk_re_lex_parse,
-       //.complete = ec_tk_re_lex_complete,
+       //.complete = ec_tk_re_lex_complete, //XXX
        .free_priv = ec_tk_re_lex_free_priv,
 };
 
@@ -177,8 +177,8 @@ int ec_tk_re_lex_add(struct ec_tk *gen_tk, const char *pattern, int keep)
        ret = regcomp(&table[tk->len].r, pattern, REG_EXTENDED);
        if (ret != 0) {
                ec_log(EC_LOG_ERR,
-                       "Regular expression compilation failed: %d\n",
-                       ret);
+                       "Regular expression <%s> compilation failed: %d\n",
+                       pattern, ret);
                if (ret == REG_ESPACE)
                        ret = -ENOMEM;
                else
@@ -238,7 +238,7 @@ static int ec_tk_re_lex_testcase(void)
                return -1;
        }
 
-       /* XXX add ^ automatically */
+       /* 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);
index 877a41a..b90ab1b 100644 (file)
@@ -30,6 +30,8 @@
 
 #include <ecoli_tk.h>
 
-struct ec_tk *ec_tk_re_lex_new(const char *id, struct ec_tk *child);
+struct ec_tk *ec_tk_re_lex(const char *id, struct ec_tk *child);
+
+int ec_tk_re_lex_add(struct ec_tk *gen_tk, const char *pattern, int keep);
 
 #endif
index d716fec..d856955 100644 (file)
@@ -65,6 +65,7 @@ static const struct option ec_long_options[] = {
 
 static void usage(const char *prgname)
 {
+       /* XXX add a parameter to test only one testcase */
        printf("%s [options]\n"
                "  -h\n"
                "  --"EC_OPT_HELP"\n"
diff --git a/lib/todo.txt b/lib/todo.txt
new file mode 100644 (file)
index 0000000..befa74c
--- /dev/null
@@ -0,0 +1,9 @@
+- evaluate expression tree in ec_tk_expr
+- cmd token
+- node regexp
+- node which always matches
+- yaml interface to create nodes
+- check XXX in code
+- example which parses arguments (argc/argv)
+- example that acts as bash completion (ip link ?)
+- calculator example (var assignation, expression evaluation)