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:
		
							
								
								
									
										44
									
								
								expr.c
									
									
									
									
									
								
							
							
						
						
									
										44
									
								
								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) { | ||||
| 					if (func.arg_types[arg_results_size] == ExprArgTypeNum) { | ||||
| 						double 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; | ||||
| 				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; | ||||
|   | ||||
							
								
								
									
										21
									
								
								expr.h
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								expr.h
									
									
									
									
									
								
							| @@ -5,16 +5,29 @@ | ||||
| #include <stddef.h> | ||||
| #include <stdint.h> | ||||
|  | ||||
| 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__ */ | ||||
|   | ||||
							
								
								
									
										134
									
								
								expr_config.h
									
									
									
									
									
								
							
							
						
						
									
										134
									
								
								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[] = { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user