# 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
#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);
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++)
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;
#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;
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",
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;
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 */
/* 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
--- /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 <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);
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,
};
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
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);
#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
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"
--- /dev/null
+- 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)