set() function + userdata storage + Expr instance access for functions
- functions can take string arguments (i.e. variable names that may be undefined) - set(<varname>, <value>) sets a variable to a user-defined value - any userdata can be stored via expr_set_userdata() and retrieved via expr_get_userdata(); also accessible to functions - functions gain access to the expr instance and consequently all of its variables including userdata
This commit is contained in:
parent
824153a9df
commit
dd918e6208
44
expr.c
44
expr.c
@ -35,14 +35,15 @@ typedef struct {
|
|||||||
};
|
};
|
||||||
} Tok;
|
} Tok;
|
||||||
|
|
||||||
typedef struct Var {
|
typedef struct {
|
||||||
char *name;
|
char *name;
|
||||||
double val;
|
double val;
|
||||||
} Var;
|
} Var;
|
||||||
|
|
||||||
typedef struct Func {
|
typedef struct {
|
||||||
char *name;
|
char *name;
|
||||||
double (*func)(double *args);
|
double (*func)(Expr *e, ExprArg *args);
|
||||||
|
ExprArgType *arg_types;
|
||||||
size_t n_args;
|
size_t n_args;
|
||||||
} Func;
|
} Func;
|
||||||
|
|
||||||
@ -60,6 +61,8 @@ struct _Expr {
|
|||||||
Func *funcs;
|
Func *funcs;
|
||||||
size_t funcs_len;
|
size_t funcs_len;
|
||||||
size_t funcs_cap;
|
size_t funcs_cap;
|
||||||
|
|
||||||
|
void *userdata;
|
||||||
};
|
};
|
||||||
|
|
||||||
static size_t smap_get_idx(void *smap, const char *key, size_t type_size, size_t cap);
|
static size_t smap_get_idx(void *smap, const char *key, size_t type_size, size_t cap);
|
||||||
@ -106,7 +109,7 @@ Expr *expr_new() {
|
|||||||
Expr *res = malloc(sizeof(Expr));
|
Expr *res = malloc(sizeof(Expr));
|
||||||
*res = (Expr){0};
|
*res = (Expr){0};
|
||||||
for (size_t i = 0; i < expr_n_builtin_funcs; i++) {
|
for (size_t i = 0; i < expr_n_builtin_funcs; i++) {
|
||||||
expr_set_func(res, expr_builtin_funcs[i].name, expr_builtin_funcs[i].func, expr_builtin_funcs[i].n_args);
|
expr_set_func(res, expr_builtin_funcs[i].name, expr_builtin_funcs[i].func, expr_builtin_funcs[i].arg_types, expr_builtin_funcs[i].n_args);
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < expr_n_builtin_vars; i++) {
|
for (size_t i = 0; i < expr_n_builtin_vars; i++) {
|
||||||
expr_set_var(res, expr_builtin_vars[i].name, expr_builtin_vars[i].val);
|
expr_set_var(res, expr_builtin_vars[i].name, expr_builtin_vars[i].val);
|
||||||
@ -206,12 +209,21 @@ bool expr_get_var(Expr *e, const char *name, double *out) {
|
|||||||
return v.name != NULL;
|
return v.name != NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void expr_set_func(Expr *e, const char *name, double (*func)(double *args), size_t n_args) {
|
void expr_set_func(Expr *e, const char *name, double (*func)(Expr *e, ExprArg *args), ExprArgType *arg_types, size_t n_args) {
|
||||||
Func *v = smap_get_for_setting((void**)&e->funcs, name, sizeof(Func), &e->funcs_len, &e->funcs_cap);
|
Func *v = smap_get_for_setting((void**)&e->funcs, name, sizeof(Func), &e->funcs_len, &e->funcs_cap);
|
||||||
v->func = func;
|
v->func = func;
|
||||||
|
v->arg_types = arg_types;
|
||||||
v->n_args = n_args;
|
v->n_args = n_args;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void expr_set_userdata(Expr *e, void *userdata) {
|
||||||
|
e->userdata = userdata;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *expr_get_userdata(Expr *e) {
|
||||||
|
return e->userdata;
|
||||||
|
}
|
||||||
|
|
||||||
static void del_toks(Expr *e, Tok *start, Tok *end) {
|
static void del_toks(Expr *e, Tok *start, Tok *end) {
|
||||||
memmove(start, end, (e->toks_working_len - (end - e->toks_working)) * sizeof(Tok));
|
memmove(start, end, (e->toks_working_len - (end - e->toks_working)) * sizeof(Tok));
|
||||||
e->toks_working_len -= end - start;
|
e->toks_working_len -= end - start;
|
||||||
@ -244,15 +256,26 @@ static ExprError collapse(Expr *e, Tok *t) {
|
|||||||
if (t[1].kind == TokIdent) {
|
if (t[1].kind == TokIdent) {
|
||||||
if (t + 2 < e->toks_working + e->toks_working_len && (t[2].kind == TokOp && t[2].Char == '(')) {
|
if (t + 2 < e->toks_working + e->toks_working_len && (t[2].kind == TokOp && t[2].Char == '(')) {
|
||||||
/* Collapse function. */
|
/* Collapse function. */
|
||||||
double arg_results[16];
|
ExprArg arg_results[16];
|
||||||
size_t arg_results_size = 0;
|
size_t arg_results_size = 0;
|
||||||
|
|
||||||
|
Func func = get_func(e, t[1].Str);
|
||||||
|
if (func.name == NULL)
|
||||||
|
return (ExprError){.start = t[1].start, .end = t[1].end, .err = "unknown function"};
|
||||||
|
|
||||||
t += 2;
|
t += 2;
|
||||||
while (1) {
|
while (1) {
|
||||||
if (arg_results_size < 16) {
|
if (arg_results_size < 16) {
|
||||||
|
if (func.arg_types[arg_results_size] == ExprArgTypeNum) {
|
||||||
double res;
|
double res;
|
||||||
TRY(eval(e, t, &res));
|
TRY(eval(e, t, &res));
|
||||||
arg_results[arg_results_size++] = res;
|
arg_results[arg_results_size++].Num = res;
|
||||||
|
} else if (func.arg_types[arg_results_size] == ExprArgTypeStr) {
|
||||||
|
if (t[1].kind != TokIdent)
|
||||||
|
return (ExprError){.start = t[1].start, .end = t[1].end, .err = "expected string argument"};
|
||||||
|
|
||||||
|
arg_results[arg_results_size++].Str = t[1].Str;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
size_t i = 1;
|
size_t i = 1;
|
||||||
for (; !(t[i].kind == TokOp && OP_PREC(t[i].Char) == 0); i++);
|
for (; !(t[i].kind == TokOp && OP_PREC(t[i].Char) == 0); i++);
|
||||||
@ -266,14 +289,11 @@ static ExprError collapse(Expr *e, Tok *t) {
|
|||||||
}
|
}
|
||||||
t -= 2;
|
t -= 2;
|
||||||
|
|
||||||
Func func = get_func(e, t[1].Str);
|
|
||||||
if (func.name == NULL)
|
|
||||||
return (ExprError){.start = t[1].start, .end = t[1].end, .err = "unknown function"};
|
|
||||||
if (arg_results_size != func.n_args)
|
if (arg_results_size != func.n_args)
|
||||||
return (ExprError){.start = t[1].start, .end = t[1].end, .err = "invalid number of arguments to function"};
|
return (ExprError){.start = t[1].start, .end = t[1].end, .err = "invalid number of arguments to function"};
|
||||||
|
|
||||||
t[1].kind = TokNum;
|
t[1].kind = TokNum;
|
||||||
t[1].Num = func.func(arg_results);
|
t[1].Num = func.func(e, arg_results);
|
||||||
} else {
|
} else {
|
||||||
/* Collapse variable. */
|
/* Collapse variable. */
|
||||||
t[1].kind = TokNum;
|
t[1].kind = TokNum;
|
||||||
@ -293,7 +313,7 @@ static ExprError eval(Expr *e, Tok *t, double *out_res) {
|
|||||||
TRY(collapse(e, t));
|
TRY(collapse(e, t));
|
||||||
|
|
||||||
if (!(t[0].kind == TokOp && t[1].kind == TokNum && t[2].kind == TokOp)) {
|
if (!(t[0].kind == TokOp && t[1].kind == TokNum && t[2].kind == TokOp)) {
|
||||||
return (ExprError){.start = t[0].start, .end = t[1].end, .err = "invalid token order"};
|
return (ExprError){.start = t[0].start, .end = t[1].end, .err = "unexpected token"};
|
||||||
}
|
}
|
||||||
|
|
||||||
const char curr_op = t[0].Char;
|
const char curr_op = t[0].Char;
|
||||||
|
21
expr.h
21
expr.h
@ -5,16 +5,29 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef struct _Expr Expr;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
size_t start, end;
|
size_t start, end;
|
||||||
const char *err;
|
const char *err;
|
||||||
} ExprError;
|
} ExprError;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
ExprArgTypeStr,
|
||||||
|
ExprArgTypeNum,
|
||||||
|
} ExprArgType;
|
||||||
|
|
||||||
|
typedef union {
|
||||||
|
char *Str;
|
||||||
|
double Num;
|
||||||
|
} ExprArg;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const char *name;
|
const char *name;
|
||||||
const char *description;
|
const char *description;
|
||||||
double (*func)(double *args);
|
double (*func)(Expr *e, ExprArg *args);
|
||||||
const char **arg_names;
|
const char **arg_names;
|
||||||
|
ExprArgType *arg_types;
|
||||||
size_t n_args;
|
size_t n_args;
|
||||||
} ExprBuiltinFunc;
|
} ExprBuiltinFunc;
|
||||||
|
|
||||||
@ -24,8 +37,6 @@ typedef struct {
|
|||||||
double val;
|
double val;
|
||||||
} ExprBuiltinVar;
|
} ExprBuiltinVar;
|
||||||
|
|
||||||
typedef struct _Expr Expr;
|
|
||||||
|
|
||||||
extern ExprBuiltinFunc *expr_builtin_funcs;
|
extern ExprBuiltinFunc *expr_builtin_funcs;
|
||||||
extern const size_t expr_n_builtin_funcs;
|
extern const size_t expr_n_builtin_funcs;
|
||||||
extern ExprBuiltinVar *expr_builtin_vars;
|
extern ExprBuiltinVar *expr_builtin_vars;
|
||||||
@ -37,6 +48,8 @@ ExprError expr_set(Expr *e, const char *expr) __attribute__((warn_unused_result)
|
|||||||
ExprError expr_eval(Expr *e, double *out_res) __attribute__((warn_unused_result));
|
ExprError expr_eval(Expr *e, double *out_res) __attribute__((warn_unused_result));
|
||||||
void expr_set_var(Expr *e, const char *name, double val);
|
void expr_set_var(Expr *e, const char *name, double val);
|
||||||
bool expr_get_var(Expr *e, const char *name, double *out); /* Returns false if not present */
|
bool expr_get_var(Expr *e, const char *name, double *out); /* Returns false if not present */
|
||||||
void expr_set_func(Expr *e, const char *name, double (*func)(double *args), size_t n_args);
|
void expr_set_func(Expr *e, const char *name, double (*func)(Expr *e, ExprArg *args), ExprArgType *arg_types, size_t n_args);
|
||||||
|
void expr_set_userdata(Expr *e, void *userdata);
|
||||||
|
void *expr_get_userdata(Expr *e);
|
||||||
|
|
||||||
#endif /* __EXPR_H__ */
|
#endif /* __EXPR_H__ */
|
||||||
|
128
expr_config.h
128
expr_config.h
@ -2,70 +2,82 @@
|
|||||||
#error expr_config.h should not be imported by any files other than expr.c
|
#error expr_config.h should not be imported by any files other than expr.c
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static double fn_sqrt(double *args) {return sqrt(args[0]); }
|
static double fn_sqrt(Expr *e, ExprArg *args) {return sqrt(args[0].Num); }
|
||||||
static double fn_cbrt(double *args) {return cbrt(args[0]); }
|
static double fn_cbrt(Expr *e, ExprArg *args) {return cbrt(args[0].Num); }
|
||||||
static double fn_pow(double *args) {return pow(args[0], args[1]); }
|
static double fn_pow(Expr *e, ExprArg *args) {return pow(args[0].Num, args[1].Num); }
|
||||||
static double fn_exp(double *args) {return exp(args[0]); }
|
static double fn_exp(Expr *e, ExprArg *args) {return exp(args[0].Num); }
|
||||||
static double fn_ln(double *args) {return log(args[0]); }
|
static double fn_ln(Expr *e, ExprArg *args) {return log(args[0].Num); }
|
||||||
static double fn_log(double *args) {return log(args[1]) / log(args[0]);}
|
static double fn_log(Expr *e, ExprArg *args) {return log(args[1].Num) / log(args[0].Num);}
|
||||||
static double fn_mod(double *args) {return fmod(args[0], args[1]); }
|
static double fn_mod(Expr *e, ExprArg *args) {return fmod(args[0].Num, args[1].Num); }
|
||||||
static double fn_round(double *args) {return round(args[0]); }
|
static double fn_round(Expr *e, ExprArg *args) {return round(args[0].Num); }
|
||||||
static double fn_floor(double *args) {return floor(args[0]); }
|
static double fn_floor(Expr *e, ExprArg *args) {return floor(args[0].Num); }
|
||||||
static double fn_ceil(double *args) {return ceil(args[0]); }
|
static double fn_ceil(Expr *e, ExprArg *args) {return ceil(args[0].Num); }
|
||||||
static double fn_sin(double *args) {return sin(args[0]); }
|
static double fn_sin(Expr *e, ExprArg *args) {return sin(args[0].Num); }
|
||||||
static double fn_cos(double *args) {return cos(args[0]); }
|
static double fn_cos(Expr *e, ExprArg *args) {return cos(args[0].Num); }
|
||||||
static double fn_tan(double *args) {return tan(args[0]); }
|
static double fn_tan(Expr *e, ExprArg *args) {return tan(args[0].Num); }
|
||||||
static double fn_asin(double *args) {return asin(args[0]); }
|
static double fn_asin(Expr *e, ExprArg *args) {return asin(args[0].Num); }
|
||||||
static double fn_acos(double *args) {return acos(args[0]); }
|
static double fn_acos(Expr *e, ExprArg *args) {return acos(args[0].Num); }
|
||||||
static double fn_atan(double *args) {return atan(args[0]); }
|
static double fn_atan(Expr *e, ExprArg *args) {return atan(args[0].Num); }
|
||||||
static double fn_sinh(double *args) {return sinh(args[0]); }
|
static double fn_sinh(Expr *e, ExprArg *args) {return sinh(args[0].Num); }
|
||||||
static double fn_cosh(double *args) {return cosh(args[0]); }
|
static double fn_cosh(Expr *e, ExprArg *args) {return cosh(args[0].Num); }
|
||||||
static double fn_tanh(double *args) {return tanh(args[0]); }
|
static double fn_tanh(Expr *e, ExprArg *args) {return tanh(args[0].Num); }
|
||||||
static double fn_asinh(double *args) {return asinh(args[0]); }
|
static double fn_asinh(Expr *e, ExprArg *args) {return asinh(args[0].Num); }
|
||||||
static double fn_acosh(double *args) {return acosh(args[0]); }
|
static double fn_acosh(Expr *e, ExprArg *args) {return acosh(args[0].Num); }
|
||||||
static double fn_atanh(double *args) {return atanh(args[0]); }
|
static double fn_atanh(Expr *e, ExprArg *args) {return atanh(args[0].Num); }
|
||||||
static double fn_abs(double *args) {return fabs(args[0]); }
|
static double fn_abs(Expr *e, ExprArg *args) {return fabs(args[0].Num); }
|
||||||
static double fn_hypot(double *args) {return hypot(args[0], args[1]); }
|
static double fn_hypot(Expr *e, ExprArg *args) {return hypot(args[0].Num, args[1].Num); }
|
||||||
static double fn_polar(double *args) {return atan2(args[1], args[0]); }
|
static double fn_polar(Expr *e, ExprArg *args) {return atan2(args[1].Num, args[0].Num); }
|
||||||
static double fn_max(double *args) {return fmax(args[0], args[1]); }
|
static double fn_max(Expr *e, ExprArg *args) {return fmax(args[0].Num, args[1].Num); }
|
||||||
static double fn_min(double *args) {return fmin(args[0], args[1]); }
|
static double fn_min(Expr *e, ExprArg *args) {return fmin(args[0].Num, args[1].Num); }
|
||||||
static double fn_rad(double *args) {return args[0] / M_PI * 180.0; }
|
static double fn_rad(Expr *e, ExprArg *args) {return args[0].Num / M_PI * 180.0; }
|
||||||
static double fn_deg(double *args) {return args[0] / 180.0 * M_PI; }
|
static double fn_deg(Expr *e, ExprArg *args) {return args[0].Num / 180.0 * M_PI; }
|
||||||
|
|
||||||
|
static double fn_set(Expr *e, ExprArg *args) {
|
||||||
|
expr_set_var(e, args[0].Str, args[1].Num);
|
||||||
|
return args[1].Num;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ExprArgType arg_types_n[] = {ExprArgTypeNum };
|
||||||
|
static ExprArgType arg_types_nn[] = {ExprArgTypeNum, ExprArgTypeNum};
|
||||||
|
static ExprArgType arg_types_sn[] = {ExprArgTypeStr, ExprArgTypeNum};
|
||||||
|
|
||||||
static const char *arg_names_x[] = {"x" };
|
static const char *arg_names_x[] = {"x" };
|
||||||
static const char *arg_names_xy[] = {"x", "y" };
|
static const char *arg_names_xy[] = {"x", "y" };
|
||||||
static const char *arg_names_nx[] = {"n", "x" };
|
static const char *arg_names_nx[] = {"n", "x" };
|
||||||
|
static const char *arg_names_name_val[] = {"name", "value"};
|
||||||
|
|
||||||
static ExprBuiltinFunc _builtin_funcs[] = {
|
static ExprBuiltinFunc _builtin_funcs[] = {
|
||||||
{"sqrt", "square root of x", fn_sqrt, arg_names_x, 1},
|
{"sqrt", "square root of x", fn_sqrt, arg_names_x, arg_types_n, 1},
|
||||||
{"cbrt", "cube root of x", fn_cbrt, arg_names_x, 1},
|
{"cbrt", "cube root of x", fn_cbrt, arg_names_x, arg_types_n, 1},
|
||||||
{"pow", "x^y", fn_pow, arg_names_xy, 2},
|
{"pow", "x^y", fn_pow, arg_names_xy, arg_types_nn, 2},
|
||||||
{"exp", "e^x", fn_exp, arg_names_x, 1},
|
{"exp", "e^x", fn_exp, arg_names_x, arg_types_n, 1},
|
||||||
{"ln", "natural log (base e) of x", fn_ln, arg_names_x, 1},
|
{"ln", "natural log (base e) of x", fn_ln, arg_names_x, arg_types_n, 1},
|
||||||
{"log", "log (base n) of x", fn_log, arg_names_nx, 2},
|
{"log", "log (base n) of x", fn_log, arg_names_nx, arg_types_nn, 2},
|
||||||
{"mod", "x%y", fn_mod, arg_names_xy, 2},
|
{"mod", "x%y", fn_mod, arg_names_xy, arg_types_nn, 2},
|
||||||
{"round", "closest integer to x", fn_round, arg_names_x, 1},
|
{"round", "closest integer to x", fn_round, arg_names_x, arg_types_n, 1},
|
||||||
{"floor", "greatest integer less than x", fn_floor, arg_names_x, 1},
|
{"floor", "greatest integer less than x", fn_floor, arg_names_x, arg_types_n, 1},
|
||||||
{"ceil", "smallest integer grater than x", fn_ceil, arg_names_x, 1},
|
{"ceil", "smallest integer grater than x", fn_ceil, arg_names_x, arg_types_n, 1},
|
||||||
{"sin", "sine of x", fn_sin, arg_names_x, 1},
|
{"sin", "sine of x", fn_sin, arg_names_x, arg_types_n, 1},
|
||||||
{"cos", "cosine of x", fn_cos, arg_names_x, 1},
|
{"cos", "cosine of x", fn_cos, arg_names_x, arg_types_n, 1},
|
||||||
{"tan", "tangent of x", fn_tan, arg_names_x, 1},
|
{"tan", "tangent of x", fn_tan, arg_names_x, arg_types_n, 1},
|
||||||
{"asin", "inverse sine of x", fn_asin, arg_names_x, 1},
|
{"asin", "inverse sine of x", fn_asin, arg_names_x, arg_types_n, 1},
|
||||||
{"acos", "inverse cosine of x", fn_acos, arg_names_x, 1},
|
{"acos", "inverse cosine of x", fn_acos, arg_names_x, arg_types_n, 1},
|
||||||
{"atan", "inverse tangent of x", fn_atan, arg_names_x, 1},
|
{"atan", "inverse tangent of x", fn_atan, arg_names_x, arg_types_n, 1},
|
||||||
{"sinh", "hyperbolic sine of x", fn_sinh, arg_names_x, 1},
|
{"sinh", "hyperbolic sine of x", fn_sinh, arg_names_x, arg_types_n, 1},
|
||||||
{"cosh", "hyperbolic cosine of x", fn_cosh, arg_names_x, 1},
|
{"cosh", "hyperbolic cosine of x", fn_cosh, arg_names_x, arg_types_n, 1},
|
||||||
{"tanh", "hyperbolic tangent of x", fn_tanh, arg_names_x, 1},
|
{"tanh", "hyperbolic tangent of x", fn_tanh, arg_names_x, arg_types_n, 1},
|
||||||
{"asinh", "inverse hyperbolic sine of x", fn_asinh, arg_names_x, 1},
|
{"asinh", "inverse hyperbolic sine of x", fn_asinh, arg_names_x, arg_types_n, 1},
|
||||||
{"acosh", "inverse hyperbolic cosine of x", fn_acosh, arg_names_x, 1},
|
{"acosh", "inverse hyperbolic cosine of x", fn_acosh, arg_names_x, arg_types_n, 1},
|
||||||
{"atanh", "inverse hyperbolic tangent of x", fn_atanh, arg_names_x, 1},
|
{"atanh", "inverse hyperbolic tangent of x", fn_atanh, arg_names_x, arg_types_n, 1},
|
||||||
{"abs", "absolute value of x", fn_abs, arg_names_x, 1},
|
{"abs", "absolute value of x", fn_abs, arg_names_x, arg_types_n, 1},
|
||||||
{"hypot", "sqrt(x^2+y^2)", fn_hypot, arg_names_xy, 2},
|
{"hypot", "sqrt(x^2+y^2)", fn_hypot, arg_names_xy, arg_types_nn, 2},
|
||||||
{"polar", "polar coordinates to radians", fn_polar, arg_names_xy, 2},
|
{"polar", "polar coordinates to radians", fn_polar, arg_names_xy, arg_types_nn, 2},
|
||||||
{"max", "the greater value of x and y", fn_max, arg_names_xy, 2},
|
{"max", "the greater value of x and y", fn_max, arg_names_xy, arg_types_nn, 2},
|
||||||
{"min", "the smaller value of x and y", fn_min, arg_names_xy, 2},
|
{"min", "the smaller value of x and y", fn_min, arg_names_xy, arg_types_nn, 2},
|
||||||
{"rad", "x (radians) to degrees", fn_rad, arg_names_x, 1},
|
{"rad", "x (radians) to degrees", fn_rad, arg_names_x, arg_types_n, 1},
|
||||||
{"deg", "x (degrees) to radians", fn_deg, arg_names_x, 1},
|
{"deg", "x (degrees) to radians", fn_deg, arg_names_x, arg_types_n, 1},
|
||||||
|
|
||||||
|
{"set", "(re-)set the value of a variable", fn_set, arg_names_name_val, arg_types_sn, 2},
|
||||||
};
|
};
|
||||||
|
|
||||||
static ExprBuiltinVar _builtin_vars[] = {
|
static ExprBuiltinVar _builtin_vars[] = {
|
||||||
|
Loading…
Reference in New Issue
Block a user