From 2066f27b80c3bb319dfe2439c275d917988ea39f Mon Sep 17 00:00:00 2001 From: Olivier Matz Date: Sat, 8 Apr 2017 13:41:40 +0200 Subject: [PATCH] save --- lib/Makefile | 28 +++- lib/ecoli_strvec.h | 3 + lib/ecoli_tk.c | 20 ++- lib/ecoli_tk_expr.c | 349 +++++++++++++++++++++++---------------- lib/ecoli_tk_expr.h | 39 +++++ lib/ecoli_tk_expr_test.c | 318 +++++++++++++++++++++++++++++++++++ lib/ecoli_tk_re_lex.c | 8 +- lib/ecoli_tk_re_lex.h | 4 +- lib/main.c | 1 + lib/todo.txt | 9 + 10 files changed, 622 insertions(+), 157 deletions(-) create mode 100644 lib/ecoli_tk_expr_test.c create mode 100644 lib/todo.txt diff --git a/lib/Makefile b/lib/Makefile index ca072a5..ecb030b 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -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 diff --git a/lib/ecoli_strvec.h b/lib/ecoli_strvec.h index 6178777..d94937b 100644 --- a/lib/ecoli_strvec.h +++ b/lib/ecoli_strvec.h @@ -28,6 +28,9 @@ #ifndef ECOLI_STRVEC_ #define ECOLI_STRVEC_ +#include +#include + 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); diff --git a/lib/ecoli_tk.c b/lib/ecoli_tk.c index 624a5b9..9d877c0 100644 --- a/lib/ecoli_tk.c +++ b/lib/ecoli_tk.c @@ -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; diff --git a/lib/ecoli_tk_expr.c b/lib/ecoli_tk_expr.c index fe28b20..7f1c3cb 100644 --- a/lib/ecoli_tk_expr.c +++ b/lib/ecoli_tk_expr.c @@ -27,10 +27,11 @@ #include #include +#include +#include #include #include #include -#include #include #include @@ -38,13 +39,11 @@ #include #include #include -#include -#include #include #include #include -#include #include +#include 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 */ diff --git a/lib/ecoli_tk_expr.h b/lib/ecoli_tk_expr.h index ca28e68..d24e0a2 100644 --- a/lib/ecoli_tk_expr.h +++ b/lib/ecoli_tk_expr.h @@ -32,13 +32,52 @@ /* 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 index 0000000..858ffcf --- /dev/null +++ b/lib/ecoli_tk_expr_test.c @@ -0,0 +1,318 @@ +/* + * Copyright (c) 2016-2017, Olivier MATZ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +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); diff --git a/lib/ecoli_tk_re_lex.c b/lib/ecoli_tk_re_lex.c index 8d31543..60ba145 100644 --- a/lib/ecoli_tk_re_lex.c +++ b/lib/ecoli_tk_re_lex.c @@ -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); diff --git a/lib/ecoli_tk_re_lex.h b/lib/ecoli_tk_re_lex.h index 877a41a..b90ab1b 100644 --- a/lib/ecoli_tk_re_lex.h +++ b/lib/ecoli_tk_re_lex.h @@ -30,6 +30,8 @@ #include -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 diff --git a/lib/main.c b/lib/main.c index d716fec..d856955 100644 --- a/lib/main.c +++ b/lib/main.c @@ -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 index 0000000..befa74c --- /dev/null +++ b/lib/todo.txt @@ -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) -- 2.39.5