diff --git a/expr.c b/expr.c index 065d6a1..abaf58c 100644 --- a/expr.c +++ b/expr.c @@ -35,14 +35,15 @@ typedef struct { }; } Tok; -typedef struct Var { +typedef struct { char *name; double val; } Var; -typedef struct Func { +typedef struct { char *name; - double (*func)(double *args); + double (*func)(Expr *e, ExprArg *args); + ExprArgType *arg_types; size_t n_args; } Func; @@ -60,6 +61,8 @@ struct _Expr { Func *funcs; size_t funcs_len; size_t funcs_cap; + + void *userdata; }; 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)); *res = (Expr){0}; 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++) { 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; } -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); v->func = func; + v->arg_types = arg_types; 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) { memmove(start, end, (e->toks_working_len - (end - e->toks_working)) * sizeof(Tok)); e->toks_working_len -= end - start; @@ -244,15 +256,26 @@ static ExprError collapse(Expr *e, Tok *t) { if (t[1].kind == TokIdent) { if (t + 2 < e->toks_working + e->toks_working_len && (t[2].kind == TokOp && t[2].Char == '(')) { /* Collapse function. */ - double arg_results[16]; + ExprArg arg_results[16]; 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; while (1) { if (arg_results_size < 16) { - double res; - TRY(eval(e, t, &res)); - arg_results[arg_results_size++] = res; + if (func.arg_types[arg_results_size] == ExprArgTypeNum) { + double res; + TRY(eval(e, t, &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; 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; - 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) return (ExprError){.start = t[1].start, .end = t[1].end, .err = "invalid number of arguments to function"}; t[1].kind = TokNum; - t[1].Num = func.func(arg_results); + t[1].Num = func.func(e, arg_results); } else { /* Collapse variable. */ t[1].kind = TokNum; @@ -293,7 +313,7 @@ static ExprError eval(Expr *e, Tok *t, double *out_res) { TRY(collapse(e, t)); 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; diff --git a/expr.h b/expr.h index 9a9a7e5..ab72ff2 100644 --- a/expr.h +++ b/expr.h @@ -5,16 +5,29 @@ #include #include +typedef struct _Expr Expr; + typedef struct { size_t start, end; const char *err; } ExprError; +typedef enum { + ExprArgTypeStr, + ExprArgTypeNum, +} ExprArgType; + +typedef union { + char *Str; + double Num; +} ExprArg; + typedef struct { const char *name; const char *description; - double (*func)(double *args); + double (*func)(Expr *e, ExprArg *args); const char **arg_names; + ExprArgType *arg_types; size_t n_args; } ExprBuiltinFunc; @@ -24,8 +37,6 @@ typedef struct { double val; } ExprBuiltinVar; -typedef struct _Expr Expr; - extern ExprBuiltinFunc *expr_builtin_funcs; extern const size_t expr_n_builtin_funcs; 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)); 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 */ -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__ */ diff --git a/expr_config.h b/expr_config.h index 340b816..f6ce4a7 100644 --- a/expr_config.h +++ b/expr_config.h @@ -2,70 +2,82 @@ #error expr_config.h should not be imported by any files other than expr.c #endif -static double fn_sqrt(double *args) {return sqrt(args[0]); } -static double fn_cbrt(double *args) {return cbrt(args[0]); } -static double fn_pow(double *args) {return pow(args[0], args[1]); } -static double fn_exp(double *args) {return exp(args[0]); } -static double fn_ln(double *args) {return log(args[0]); } -static double fn_log(double *args) {return log(args[1]) / log(args[0]);} -static double fn_mod(double *args) {return fmod(args[0], args[1]); } -static double fn_round(double *args) {return round(args[0]); } -static double fn_floor(double *args) {return floor(args[0]); } -static double fn_ceil(double *args) {return ceil(args[0]); } -static double fn_sin(double *args) {return sin(args[0]); } -static double fn_cos(double *args) {return cos(args[0]); } -static double fn_tan(double *args) {return tan(args[0]); } -static double fn_asin(double *args) {return asin(args[0]); } -static double fn_acos(double *args) {return acos(args[0]); } -static double fn_atan(double *args) {return atan(args[0]); } -static double fn_sinh(double *args) {return sinh(args[0]); } -static double fn_cosh(double *args) {return cosh(args[0]); } -static double fn_tanh(double *args) {return tanh(args[0]); } -static double fn_asinh(double *args) {return asinh(args[0]); } -static double fn_acosh(double *args) {return acosh(args[0]); } -static double fn_atanh(double *args) {return atanh(args[0]); } -static double fn_abs(double *args) {return fabs(args[0]); } -static double fn_hypot(double *args) {return hypot(args[0], args[1]); } -static double fn_polar(double *args) {return atan2(args[1], args[0]); } -static double fn_max(double *args) {return fmax(args[0], args[1]); } -static double fn_min(double *args) {return fmin(args[0], args[1]); } -static double fn_rad(double *args) {return args[0] / M_PI * 180.0; } -static double fn_deg(double *args) {return args[0] / 180.0 * M_PI; } +static double fn_sqrt(Expr *e, ExprArg *args) {return sqrt(args[0].Num); } +static double fn_cbrt(Expr *e, ExprArg *args) {return cbrt(args[0].Num); } +static double fn_pow(Expr *e, ExprArg *args) {return pow(args[0].Num, args[1].Num); } +static double fn_exp(Expr *e, ExprArg *args) {return exp(args[0].Num); } +static double fn_ln(Expr *e, ExprArg *args) {return log(args[0].Num); } +static double fn_log(Expr *e, ExprArg *args) {return log(args[1].Num) / log(args[0].Num);} +static double fn_mod(Expr *e, ExprArg *args) {return fmod(args[0].Num, args[1].Num); } +static double fn_round(Expr *e, ExprArg *args) {return round(args[0].Num); } +static double fn_floor(Expr *e, ExprArg *args) {return floor(args[0].Num); } +static double fn_ceil(Expr *e, ExprArg *args) {return ceil(args[0].Num); } +static double fn_sin(Expr *e, ExprArg *args) {return sin(args[0].Num); } +static double fn_cos(Expr *e, ExprArg *args) {return cos(args[0].Num); } +static double fn_tan(Expr *e, ExprArg *args) {return tan(args[0].Num); } +static double fn_asin(Expr *e, ExprArg *args) {return asin(args[0].Num); } +static double fn_acos(Expr *e, ExprArg *args) {return acos(args[0].Num); } +static double fn_atan(Expr *e, ExprArg *args) {return atan(args[0].Num); } +static double fn_sinh(Expr *e, ExprArg *args) {return sinh(args[0].Num); } +static double fn_cosh(Expr *e, ExprArg *args) {return cosh(args[0].Num); } +static double fn_tanh(Expr *e, ExprArg *args) {return tanh(args[0].Num); } +static double fn_asinh(Expr *e, ExprArg *args) {return asinh(args[0].Num); } +static double fn_acosh(Expr *e, ExprArg *args) {return acosh(args[0].Num); } +static double fn_atanh(Expr *e, ExprArg *args) {return atanh(args[0].Num); } +static double fn_abs(Expr *e, ExprArg *args) {return fabs(args[0].Num); } +static double fn_hypot(Expr *e, ExprArg *args) {return hypot(args[0].Num, args[1].Num); } +static double fn_polar(Expr *e, ExprArg *args) {return atan2(args[1].Num, args[0].Num); } +static double fn_max(Expr *e, ExprArg *args) {return fmax(args[0].Num, args[1].Num); } +static double fn_min(Expr *e, ExprArg *args) {return fmin(args[0].Num, args[1].Num); } +static double fn_rad(Expr *e, ExprArg *args) {return args[0].Num / M_PI * 180.0; } +static double fn_deg(Expr *e, ExprArg *args) {return args[0].Num / 180.0 * M_PI; } -static const char *arg_names_x[] = {"x"}; -static const char *arg_names_xy[] = {"x", "y"}; -static const char *arg_names_nx[] = {"n", "x"}; +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_xy[] = {"x", "y" }; +static const char *arg_names_nx[] = {"n", "x" }; +static const char *arg_names_name_val[] = {"name", "value"}; static ExprBuiltinFunc _builtin_funcs[] = { - {"sqrt", "square root of x", fn_sqrt, arg_names_x, 1}, - {"cbrt", "cube root of x", fn_cbrt, arg_names_x, 1}, - {"pow", "x^y", fn_pow, arg_names_xy, 2}, - {"exp", "e^x", fn_exp, arg_names_x, 1}, - {"ln", "natural log (base e) of x", fn_ln, arg_names_x, 1}, - {"log", "log (base n) of x", fn_log, arg_names_nx, 2}, - {"mod", "x%y", fn_mod, arg_names_xy, 2}, - {"round", "closest integer to x", fn_round, arg_names_x, 1}, - {"floor", "greatest integer less than x", fn_floor, arg_names_x, 1}, - {"ceil", "smallest integer grater than x", fn_ceil, arg_names_x, 1}, - {"sin", "sine of x", fn_sin, arg_names_x, 1}, - {"cos", "cosine of x", fn_cos, arg_names_x, 1}, - {"tan", "tangent of x", fn_tan, arg_names_x, 1}, - {"asin", "inverse sine of x", fn_asin, arg_names_x, 1}, - {"acos", "inverse cosine of x", fn_acos, arg_names_x, 1}, - {"atan", "inverse tangent of x", fn_atan, arg_names_x, 1}, - {"sinh", "hyperbolic sine of x", fn_sinh, arg_names_x, 1}, - {"cosh", "hyperbolic cosine of x", fn_cosh, arg_names_x, 1}, - {"tanh", "hyperbolic tangent of x", fn_tanh, arg_names_x, 1}, - {"asinh", "inverse hyperbolic sine of x", fn_asinh, arg_names_x, 1}, - {"acosh", "inverse hyperbolic cosine of x", fn_acosh, arg_names_x, 1}, - {"atanh", "inverse hyperbolic tangent of x", fn_atanh, arg_names_x, 1}, - {"abs", "absolute value of x", fn_abs, arg_names_x, 1}, - {"hypot", "sqrt(x^2+y^2)", fn_hypot, arg_names_xy, 2}, - {"polar", "polar coordinates to radians", fn_polar, arg_names_xy, 2}, - {"max", "the greater value of x and y", fn_max, arg_names_xy, 2}, - {"min", "the smaller value of x and y", fn_min, arg_names_xy, 2}, - {"rad", "x (radians) to degrees", fn_rad, arg_names_x, 1}, - {"deg", "x (degrees) to radians", fn_deg, 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, arg_types_n, 1}, + {"pow", "x^y", fn_pow, arg_names_xy, arg_types_nn, 2}, + {"exp", "e^x", fn_exp, arg_names_x, arg_types_n, 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, arg_types_nn, 2}, + {"mod", "x%y", fn_mod, arg_names_xy, arg_types_nn, 2}, + {"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, arg_types_n, 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, arg_types_n, 1}, + {"cos", "cosine of x", fn_cos, arg_names_x, arg_types_n, 1}, + {"tan", "tangent of x", fn_tan, arg_names_x, arg_types_n, 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, arg_types_n, 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, arg_types_n, 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, arg_types_n, 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, arg_types_n, 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, arg_types_n, 1}, + {"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, arg_types_nn, 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, arg_types_nn, 2}, + {"rad", "x (radians) to degrees", fn_rad, arg_names_x, arg_types_n, 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[] = {