***************/
#define KEO_NULL 0
-#define KEO_POS 1
-#define KEO_NEG 2
+#define KEO_POS 1
+#define KEO_NEG 2
#define KEO_BNOT 3
#define KEO_LNOT 4
#define KEO_POW 5
#define KEV_INT 2
#define KEV_STR 3
+#define KEF_NULL 0
+#define KEF_REAL 1
+
struct ke1_s;
typedef struct ke1_s {
- uint32_t ttype:16, vtype:16; // ttype: token type; vtype: value type
- int32_t op:8, n_args:23, assigned:1; // op: operator, n_args: number of arguments, assigned: if a variable is assigned
+ uint32_t ttype:16, vtype:10, assigned:1, user_func:5; // ttype: token type; vtype: value type
+ int32_t op:8, n_args:24; // op: operator, n_args: number of arguments
char *name; // variable name or function name
- void (*func)(struct ke1_s *a, struct ke1_s *b); // execution function
+ union {
+ void (*builtin)(struct ke1_s *a, struct ke1_s *b); // execution function
+ double (*real_func1)(double);
+ double (*real_func2)(double, double);
+ } f;
double r;
int64_t i;
char *s;
static void ke_op_KEO_POS(ke1_t *p, ke1_t *q) { } // do nothing
static void ke_op_KEO_NEG(ke1_t *p, ke1_t *q) { p->i = -p->i, p->r = -p->r; }
-#define KE_GEN_FUNC1(_func) \
- void ke_func1_##_func(ke1_t *p, ke1_t *q) { \
- p->r = _func(p->r); p->i = (int64_t)(p->r + .5); \
- p->vtype = KEV_REAL; \
- }
-
-KE_GEN_FUNC1(log)
-KE_GEN_FUNC1(exp)
-KE_GEN_FUNC1(log2)
-KE_GEN_FUNC1(log10)
-KE_GEN_FUNC1(exp2)
-KE_GEN_FUNC1(sqrt)
-
static void ke_func1_abs(ke1_t *p, ke1_t *q) { if (p->vtype == KEV_INT) p->i = abs(p->i), p->r = (double)p->i; else p->r = fabs(p->r), p->i = (int64_t)(p->r + .5); }
-static void ke_func1_pow(ke1_t *p, ke1_t *q) { ke_op_KEO_POW(p, q); }
/**********
* Parser *
} else *err |= KEE_UNQU, *r = p;
} else { // an operator
e.ttype = KET_OP;
- if (*p == '*' && p[1] == '*') e.op = KEO_POW, e.func = ke_op_KEO_POW, e.n_args = 2, *r = q + 2;
- else if (*p == '*') e.op = KEO_MUL, e.func = ke_op_KEO_MUL, e.n_args = 2, *r = q + 1; // FIXME: NOT working for unary operators
- else if (*p == '/' && p[1] == '/') e.op = KEO_IDIV, e.func = ke_op_KEO_IDIV, e.n_args = 2, *r = q + 2;
- else if (*p == '/') e.op = KEO_DIV, e.func = ke_op_KEO_DIV, e.n_args = 2, *r = q + 1;
- else if (*p == '%') e.op = KEO_MOD, e.func = ke_op_KEO_MOD, e.n_args = 2, *r = q + 1;
+ if (*p == '*' && p[1] == '*') e.op = KEO_POW, e.f.builtin = ke_op_KEO_POW, e.n_args = 2, *r = q + 2;
+ else if (*p == '*') e.op = KEO_MUL, e.f.builtin = ke_op_KEO_MUL, e.n_args = 2, *r = q + 1; // FIXME: NOT working for unary operators
+ else if (*p == '/' && p[1] == '/') e.op = KEO_IDIV, e.f.builtin = ke_op_KEO_IDIV, e.n_args = 2, *r = q + 2;
+ else if (*p == '/') e.op = KEO_DIV, e.f.builtin = ke_op_KEO_DIV, e.n_args = 2, *r = q + 1;
+ else if (*p == '%') e.op = KEO_MOD, e.f.builtin = ke_op_KEO_MOD, e.n_args = 2, *r = q + 1;
else if (*p == '+') {
- if (last_is_val) e.op = KEO_ADD, e.func = ke_op_KEO_ADD, e.n_args = 2;
- else e.op = KEO_POS, e.func = ke_op_KEO_POS, e.n_args = 1;
+ if (last_is_val) e.op = KEO_ADD, e.f.builtin = ke_op_KEO_ADD, e.n_args = 2;
+ else e.op = KEO_POS, e.f.builtin = ke_op_KEO_POS, e.n_args = 1;
*r = q + 1;
} else if (*p == '-') {
- if (last_is_val) e.op = KEO_SUB, e.func = ke_op_KEO_SUB, e.n_args = 2;
- else e.op = KEO_NEG, e.func = ke_op_KEO_NEG, e.n_args = 1;
+ if (last_is_val) e.op = KEO_SUB, e.f.builtin = ke_op_KEO_SUB, e.n_args = 2;
+ else e.op = KEO_NEG, e.f.builtin = ke_op_KEO_NEG, e.n_args = 1;
*r = q + 1;
- } else if (*p == '=' && p[1] == '=')e.op = KEO_EQ,e.func = ke_op_KEO_EQ, e.n_args = 2, *r = q + 2;
- else if (*p == '!' && p[1] == '=') e.op = KEO_NE, e.func = ke_op_KEO_NE, e.n_args = 2, *r = q + 2;
- else if (*p == '>' && p[1] == '=') e.op = KEO_GE, e.func = ke_op_KEO_GE, e.n_args = 2, *r = q + 2;
- else if (*p == '<' && p[1] == '=') e.op = KEO_LE, e.func = ke_op_KEO_LE, e.n_args = 2, *r = q + 2;
- else if (*p == '>' && p[1] == '>') e.op = KEO_RSH, e.func = ke_op_KEO_RSH, e.n_args = 2, *r = q + 2;
- else if (*p == '<' && p[1] == '<') e.op = KEO_LSH, e.func = ke_op_KEO_LSH, e.n_args = 2, *r = q + 2;
- else if (*p == '>') e.op = KEO_GT, e.func = ke_op_KEO_GT, e.n_args = 2, *r = q + 1;
- else if (*p == '<') e.op = KEO_LT, e.func = ke_op_KEO_LT, e.n_args = 2, *r = q + 1;
- else if (*p == '|' && p[1] == '|') e.op = KEO_LOR, e.func = ke_op_KEO_LOR, e.n_args = 2, *r = q + 2;
- else if (*p == '&' && p[1] == '&') e.op = KEO_LAND, e.func = ke_op_KEO_LAND, e.n_args = 2, *r = q + 2;
- else if (*p == '|') e.op = KEO_BOR, e.func = ke_op_KEO_BOR, e.n_args = 2, *r = q + 1;
- else if (*p == '&') e.op = KEO_BAND, e.func = ke_op_KEO_BAND, e.n_args = 2, *r = q + 1;
- else if (*p == '^') e.op = KEO_BXOR, e.func = ke_op_KEO_BXOR, e.n_args = 2, *r = q + 1;
- else if (*p == '~') e.op = KEO_BNOT, e.func = ke_op_KEO_BNOT, e.n_args = 1, *r = q + 1;
- else if (*p == '!') e.op = KEO_LNOT, e.func = ke_op_KEO_LNOT, e.n_args = 1, *r = q + 1;
+ } else if (*p == '=' && p[1] == '=')e.op = KEO_EQ,e.f.builtin = ke_op_KEO_EQ, e.n_args = 2, *r = q + 2;
+ else if (*p == '!' && p[1] == '=') e.op = KEO_NE, e.f.builtin = ke_op_KEO_NE, e.n_args = 2, *r = q + 2;
+ else if (*p == '>' && p[1] == '=') e.op = KEO_GE, e.f.builtin = ke_op_KEO_GE, e.n_args = 2, *r = q + 2;
+ else if (*p == '<' && p[1] == '=') e.op = KEO_LE, e.f.builtin = ke_op_KEO_LE, e.n_args = 2, *r = q + 2;
+ else if (*p == '>' && p[1] == '>') e.op = KEO_RSH, e.f.builtin = ke_op_KEO_RSH, e.n_args = 2, *r = q + 2;
+ else if (*p == '<' && p[1] == '<') e.op = KEO_LSH, e.f.builtin = ke_op_KEO_LSH, e.n_args = 2, *r = q + 2;
+ else if (*p == '>') e.op = KEO_GT, e.f.builtin = ke_op_KEO_GT, e.n_args = 2, *r = q + 1;
+ else if (*p == '<') e.op = KEO_LT, e.f.builtin = ke_op_KEO_LT, e.n_args = 2, *r = q + 1;
+ else if (*p == '|' && p[1] == '|') e.op = KEO_LOR, e.f.builtin = ke_op_KEO_LOR, e.n_args = 2, *r = q + 2;
+ else if (*p == '&' && p[1] == '&') e.op = KEO_LAND, e.f.builtin = ke_op_KEO_LAND, e.n_args = 2, *r = q + 2;
+ else if (*p == '|') e.op = KEO_BOR, e.f.builtin = ke_op_KEO_BOR, e.n_args = 2, *r = q + 1;
+ else if (*p == '&') e.op = KEO_BAND, e.f.builtin = ke_op_KEO_BAND, e.n_args = 2, *r = q + 1;
+ else if (*p == '^') e.op = KEO_BXOR, e.f.builtin = ke_op_KEO_BXOR, e.n_args = 2, *r = q + 1;
+ else if (*p == '~') e.op = KEO_BNOT, e.f.builtin = ke_op_KEO_BNOT, e.n_args = 1, *r = q + 1;
+ else if (*p == '!') e.op = KEO_LNOT, e.f.builtin = ke_op_KEO_LNOT, e.n_args = 1, *r = q + 1;
else e.ttype = KET_NULL, *err |= KEE_UNOP;
}
return e;
if (n_op > 0 && op[n_op-1].ttype == KET_FUNC) { // the top of the operator stack is a function
u = push_back(&out, &n_out, &m_out); // move it to the output
*u = op[--n_op];
- if (u->n_args == 1) {
- if (strcmp(u->name, "abs") == 0) u->func = ke_func1_abs;
- else if (strcmp(u->name, "log") == 0) u->func = ke_func1_log;
- else if (strcmp(u->name, "exp") == 0) u->func = ke_func1_exp;
- else if (strcmp(u->name, "log2") == 0) u->func = ke_func1_log2;
- else if (strcmp(u->name, "exp2") == 0) u->func = ke_func1_exp2;
- else if (strcmp(u->name, "log10") == 0) u->func = ke_func1_log10;
- else if (strcmp(u->name, "sqrt") == 0) u->func = ke_func1_sqrt;
- } else if (u->n_args == 2) {
- if (strcmp(u->name, "pow") == 0) u->func = ke_func1_pow; // for now, this to test functions with multiple arguments
- }
+ if (u->n_args == 1 && strcmp(u->name, "abs") == 0) u->f.builtin = ke_func1_abs;
}
++p;
} else if (*p == ',') { // function arguments separator
*_i = 0, *_r = 0., *int_ret = 0;
for (i = 0; i < ke->n; ++i) {
ke1_t *e = &ke->e[i];
- if ((e->ttype == KET_OP || e->ttype == KET_FUNC) && e->func == 0) err |= KEE_UNFUNC;
+ if ((e->ttype == KET_OP || e->ttype == KET_FUNC) && e->f.builtin == 0) err |= KEE_UNFUNC;
else if (e->ttype == KET_VAL && e->name && e->assigned == 0) err |= KEE_UNVAR;
}
if (err & KEE_UNFUNC) return err;
if (e->ttype == KET_OP || e->ttype == KET_FUNC) {
if (e->n_args == 2) {
q = &stack[--top], p = &stack[top-1];
- e->func(p, q);
+ if (e->user_func) {
+ if (e->user_func == KEF_REAL)
+ p->r = e->f.real_func2(p->r, q->r), p->i = (int64_t)(p->r + .5), p->vtype = KEV_REAL;
+ } else e->f.builtin(p, q);
} else if (e->n_args == 1) {
- e->func(&stack[top-1], 0);
- } else abort(); // TODO: so far, no functions have three or more arguments; may happen in future
+ p = &stack[top-1];
+ if (e->user_func) {
+ if (e->user_func == KEF_REAL)
+ p->r = e->f.real_func1(p->r), p->i = (int64_t)(p->r + .5), p->vtype = KEV_REAL;
+ } else e->f.builtin(&stack[top-1], 0);
+ } else top -= e->n_args - 1;
} else stack[top++] = *e;
}
*_i = stack->i, *_r = stack->r, *int_ret = (stack->vtype == KEV_INT);
return n;
}
+int ke_set_real_func1(kexpr_t *ke, const char *name, double (*func)(double))
+{
+ int i, n = 0;
+ for (i = 0; i < ke->n; ++i) {
+ ke1_t *e = &ke->e[i];
+ if (e->ttype == KET_FUNC && e->n_args == 1 && strcmp(e->name, name) == 0)
+ e->f.real_func1 = func, e->user_func = KEF_REAL, ++n;
+ }
+ return n;
+}
+
+int ke_set_real_func2(kexpr_t *ke, const char *name, double (*func)(double, double))
+{
+ int i, n = 0;
+ for (i = 0; i < ke->n; ++i) {
+ ke1_t *e = &ke->e[i];
+ if (e->ttype == KET_FUNC && e->n_args == 2 && strcmp(e->name, name) == 0)
+ e->f.real_func2 = func, e->user_func = KEF_REAL, ++n;
+ }
+ return n;
+}
+
+int ke_set_default_func(kexpr_t *ke)
+{
+ int n = 0;
+ n += ke_set_real_func1(ke, "exp", exp);
+ n += ke_set_real_func1(ke, "exp2", exp2);
+ n += ke_set_real_func1(ke, "log", log);
+ n += ke_set_real_func1(ke, "log2", log2);
+ n += ke_set_real_func1(ke, "log10", log10);
+ n += ke_set_real_func1(ke, "sqrt", sqrt);
+ n += ke_set_real_func1(ke, "sin", sin);
+ n += ke_set_real_func1(ke, "cos", cos);
+ n += ke_set_real_func1(ke, "tan", tan);
+ return n;
+}
+
void ke_unset(kexpr_t *ke)
{
int i;
return 1;
}
ke = ke_parse(argv[optind], &err);
+ ke_set_default_func(ke);
if (err) {
fprintf(stderr, "Parse error: 0x%x\n", err);
return 1;
struct kexpr_s;
typedef struct kexpr_s kexpr_t;
+// Parse errors
#define KEE_UNQU 0x01 // unmatched quotation marks
#define KEE_UNLP 0x02 // unmatched left parentheses
#define KEE_UNRP 0x04 // unmatched right parentheses
#define KEE_UNOP 0x08 // unknown operators
-#define KEE_FUNC 0x10 // wrong function
+#define KEE_FUNC 0x10 // wrong function syntax
#define KEE_ARG 0x20
+// Evaluation errors
#define KEE_UNFUNC 0x40 // undefined function
#define KEE_UNVAR 0x80 // unassigned variable
extern "C" {
#endif
-kexpr_t *ke_parse(const char *_s, int *err);
-void ke_destroy(kexpr_t *ke);
-void ke_print(const kexpr_t *ke);
-int ke_set_int(kexpr_t *ke, const char *var, int64_t x);
-int ke_set_real(kexpr_t *ke, const char *var, double x);
-int ke_set_str(kexpr_t *ke, const char *var, const char *x);
-int ke_eval(const kexpr_t *ke, int64_t *_i, double *_r, int *int_ret);
-void ke_unset(kexpr_t *e);
+ // parse an expression and return errors in $err
+ kexpr_t *ke_parse(const char *_s, int *err);
+
+ // free memory allocated during parsing
+ void ke_destroy(kexpr_t *ke);
+
+ // set a variable to integer value and return the occurrence of the variable
+ int ke_set_int(kexpr_t *ke, const char *var, int64_t x);
+
+ // set a variable to real value and return the occurrence of the variable
+ int ke_set_real(kexpr_t *ke, const char *var, double x);
+
+ // set a variable to string value and return the occurrence of the variable
+ int ke_set_str(kexpr_t *ke, const char *var, const char *x);
+
+ // set a user-defined function
+ int ke_set_real_func1(kexpr_t *ke, const char *name, double (*func)(double));
+ int ke_set_real_func2(kexpr_t *ke, const char *name, double (*func)(double, double));
+
+ // set default math functions
+ int ke_set_default_func(kexpr_t *ke);
+
+ // mark all variable as unset
+ void ke_unset(kexpr_t *e);
+
+ // evaluate expression; return error code; final value is returned via pointers
+ int ke_eval(const kexpr_t *ke, int64_t *_i, double *_r, int *int_ret);
+
+ // print the expression in Reverse Polish notation (RPN)
+ void ke_print(const kexpr_t *ke);
#ifdef __cplusplus
}