/* * This file is part of the Vars library, copyright (C) Glenn Hutchings * 1996-2006. * * The Vars library comes with ABSOLUTELY NO WARRANTY. This is free * software, and you are welcome to redistribute it under certain * conditions; see the file COPYING for details. */ /*! @defgroup parser Parsers @ingroup types A parser is an object which is used to evaluate mathematical expressions. It stores variables, constants and functions which are used to perform the evaluations. Parsers get created with a set of builtin functions and constants, and you can define your own or override the builtin ones. */ /*! @defgroup parser_create Creating and destroying parsers @ingroup parser */ /*! @defgroup parser_exp Parsing expressions @ingroup parser */ /*! @defgroup parser_custom Customizing parsers @ingroup parser */ /*! @defgroup parser_access Accessing parser internals @ingroup parser */ /*! @defgroup parser_syntax Parser input syntax @ingroup parser Input to a parser consists of a number of expressions. Each expression is terminated by a newline. Whitespace is ignored. Comments may also be included, starting with \c # and continuing to the end of the line. Expressions may have a numeric or string value. Conversion between numbers and strings is done automatically whenever required. The following types of expression are allowed (where \c exp represents another expression): - number -- A number, with optional decimal part and exponent. - string -- A string, in double-quotes. A backslash in a string quotes the next character, in the same way as a C string. - exp + exp, exp - exp, exp * exp, exp / exp -- The four standard arithmetic operations. - exp ^ exp -- Exponentiation. This function uses the \c pow(3) function internally, but cannot be overridden, as can the parser builtin "pow" function. - exp \% exp -- The modulo function. This function uses the \c fmod(3) function internally, but cannot be overridden, as can the parser builtin "fmod" function. - exp == exp, exp != exp, exp < exp, exp > exp, exp <= exp, exp >= exp -- Various tests. The return value is 1 if the expression is true, 0 if it is false. If both expressions are strings, a string comparison is done. Equivalent operators: \c eq, \c ne, \c lt, \c gt, \c le, \c ge. - exp && exp, exp || exp -- Boolean AND and OR. The return value is as above. Equivalent operators: \c and, \c or. - exp =~ exp, exp !~ exp -- Pattern matching. Return value is 1 if the string matches (or doesn't match) the regexp string. See \ref regex_syntax. Equivalent operators: \c mat, \c nmat. - exp ? exp1 : exp2 -- The C ternary operator. If \c exp is nonzero the return value is \c exp1, otherwise \c exp2. - exp ; exp -- Concatenation of expressions. This allows more than one expression per line. The return value is the rightmost expression. - (exp) -- Parentheses. - + exp, - exp -- Unary plus and minus. - variable -- The name of a variable. The return value is the value of the variable. If the variable is undefined, either 0 is returned or an error is raised (see vp_error_undef()). - variable = exp -- Variable assignment. The return value is the value of the expression. - func([exp [, exp...]]) -- Function call, where "func" is the name of a parser function. . */ /*! @defgroup parser_builtin Builtin functions and constants @ingroup parser The following functions are builtin, and are described in \c math(3). - Trigonometric functions -- sin, cos, tan, asin, acos, atan, atan2 - Exponent and log functions -- exp, log, log10, pow, sqrt, hypot - Rounding functions -- ceil, floor, trunc, abs, fmod . The following functions are also defined: - min(val1, val2 [, val3...]) -- Return the minimum of a list of values. - max(val1, val2 [, val3...]) -- Return the maximum of a list of values. - randint([arg1 [, arg2]]) -- Return random integer twixt two values. If no arguments specified, between 0 and 1. If one argument specified, between 0 and that value. If two arguments specified, between those values. - randreal([arg1 [, arg2]]) -- Like randint(), except for real values. . The following constants are predefined: - \c pi -- 3.14159265... - \c e -- 2.7182818... - \c dtor -- Degrees to radians conversion factor. - \c rtod -- Radians to degrees conversion factor. . */ #define CALL_MATH_1(func) \ vs_dcreate(func(vl_dget(args, 0))) #define CALL_MATH_2(func) \ vs_dcreate(func(vl_dget(args, 0), vl_dget(args, 1))) #define BUILTIN(func, macro) \ static vscalar *builtin_ ## func(vlist *args) { return macro(func); } #define BUILTIN_1(func) \ BUILTIN(func, CALL_MATH_1) #define BUILTIN_2(func) \ BUILTIN(func, CALL_MATH_2) #define __NO_MATH_INLINES #include #include #include #include #include #include #include #include "vars-config.h" #include "vars-buffer.h" #include "vars-hash.h" #include "vars-macros.h" #include "vars-memory.h" #include "vars-parser.h" #include "vars-parser-lex.h" #include "vars-random.h" /* Type definition */ struct v_parser { struct v_header id; /* Type marker */ vhash *constants; /* Constants */ vhash *variables; /* Variables */ vhash *functions; /* Functions */ vscalar *value; /* Last parse value */ vlist *errors; /* List of parse errors */ int undef_ok; /* OK to refer to undefined variables? */ }; /* Function attributes */ struct v_pfunc { vscalar *(*func)(vlist *args); int minargs, maxargs; }; /* Internal type abbreviations */ typedef struct v_pfunc vpfunc; /* Type variable */ vtype *vparser_type = NULL; /* Whether to report line numbers */ static int report_line = 0; /* Builtin math functions */ BUILTIN_1(sin); BUILTIN_1(cos); BUILTIN_1(tan); BUILTIN_1(asin); BUILTIN_1(acos); BUILTIN_1(atan); BUILTIN_2(atan2); BUILTIN_2(pow); BUILTIN_1(exp); BUILTIN_1(log); BUILTIN_1(log10); BUILTIN_1(sqrt); BUILTIN_2(hypot); BUILTIN_1(ceil); BUILTIN_1(floor); BUILTIN_1(fabs); BUILTIN_2(fmod); /* Other internal functions */ static void vp_add_builtins(vparser *p); static vparser *vp_create_empty(void); static vscalar *builtin_max(vlist *args); static vscalar *builtin_min(vlist *args); static vscalar *builtin_randint(vlist *args); static vscalar *builtin_randreal(vlist *args); /* List of builtin functions */ static struct builtin_func { char *name; vscalar *(*func)(vlist *args); int minargs, maxargs; } builtin_functions[] = { { "sin", builtin_sin, 1, 1 }, { "cos", builtin_cos, 1, 1 }, { "tan", builtin_tan, 1, 1 }, { "asin", builtin_asin, 1, 1 }, { "acos", builtin_acos, 1, 1 }, { "atan", builtin_atan, 1, 1 }, { "atan2", builtin_atan2, 2, 2 }, { "pow", builtin_pow, 2, 2 }, { "exp", builtin_exp, 1, 1 }, { "log", builtin_log, 1, 1 }, { "log10", builtin_log10, 1, 1 }, { "sqrt", builtin_sqrt, 1, 1 }, { "hypot", builtin_hypot, 2, 2 }, { "ceil", builtin_ceil, 1, 1 }, { "floor", builtin_floor, 1, 1 }, { "abs", builtin_fabs, 1, 1 }, { "fmod", builtin_fmod, 2, 2 }, { "min", builtin_min, 2, VP_FUNC_NOLIMIT }, { "max", builtin_max, 2, VP_FUNC_NOLIMIT }, { "randint", builtin_randint, 0, 2 }, { "randreal", builtin_randreal, 0, 2 }, /* Terminator */ { NULL, NULL, 0, 0 } }; /* List of builtin constants */ static struct builtin_const { char *name; double value; } builtin_constants[] = { { "pi", M_PI }, /* Pi */ { "e", M_E }, /* E */ { "dtor", M_PI / 180 }, /* Degrees-to-radians */ { "rtod", 180 / M_PI }, /* Radians-to-degrees */ { NULL, 0.0 } }; /* Add builtins to a parser */ static void vp_add_builtins(vparser *p) { int i; /* Install builtin constants */ for (i = 0; builtin_constants[i].name != NULL; i++) vp_dconst(p, builtin_constants[i].name, builtin_constants[i].value); /* Install builtin functions */ for (i = 0; builtin_functions[i].name != NULL; i++) vp_func(p, builtin_functions[i].name, builtin_functions[i].func, builtin_functions[i].minargs, builtin_functions[i].maxargs); } /*! @brief Call a parser function. @ingroup parser_access @param p Parser. @param name Function name. @param args List of arguments (or \c NULL, meaning no args). @return Result. @retval NULL if the call failed. */ vscalar * vp_call(vparser *p, char *name, vlist *args) { vscalar *result; int nargs = 0; vpfunc *f; VP_CHECK(p); if (args != NULL) { VL_CHECK(args); nargs = vl_length(args); } /* Get function info */ if ((f = vh_pget(p->functions, name)) == NULL) { vp_err("function \"%s\" is undefined", name); return NULL; } /* Check no. of arguments */ if (nargs < f->minargs || nargs > f->maxargs) { if (f->minargs == f->maxargs) vp_err("function \"%s\" requires %d argument%s", name, f->minargs, (f->minargs == 1 ? "" : "s")); else if (f->maxargs == VP_FUNC_NOLIMIT) vp_err("function \"%s\" requires at least %d argument%s", name, f->minargs, (f->minargs == 1 ? "" : "s")); else if (f->minargs == 0) vp_err("function \"%s\" requires at most %d argument%s", name, f->maxargs, (f->maxargs == 1 ? "" : "s")); else vp_err("function \"%s\" requires between %d and %d arguments", name, f->minargs, f->maxargs); return NULL; } /* Call function, checking for errors */ errno = 0; result = f->func(args); if (errno && vp_errno(name, errno)) { if (result != NULL) vs_destroy(result); return NULL; } return result; } /*! @brief Declare a parser constant. @ingroup parser_custom @param p Parser. @param name Constant name. @param val Its value. */ void vp_const(vparser *p, char *name, vscalar *val) { VP_CHECK(p); vh_istore(p->constants, name, 1); vh_store(p->variables, name, val); } /*! @brief Return a copy of a parser. @ingroup parser_create @param p Parser to copy. @return Copy. Builtin functions and constants, and also current values of variables, are copied. */ vparser * vp_copy(vparser *p) { vpfunc *cf, *f; vparser *copy; viter iter; char *name; VP_CHECK(p); copy = vp_create_empty(); vh_destroy(copy->constants); copy->constants = vh_copy(p->constants); vh_destroy(copy->variables); copy->variables = vh_copy(p->variables); v_iterate(p->functions, iter) { name = vh_iter_key(iter); f = vh_iter_pval(iter); cf = V_ALLOC(vpfunc, 1); cf->func = f->func; cf->minargs = f->minargs; cf->maxargs = f->maxargs; vh_pstore(copy->functions, name, cf); } return copy; } /*! @brief Return a newly-created parser with builtins. @ingroup parser_create @return New parser. */ vparser * vp_create(void) { vparser *p; p = vp_create_empty(); vp_add_builtins(p); return p; } /* Return a newly-created empty parser */ static vparser * vp_create_empty(void) { static vheader *id = NULL; vparser *p; if (id == NULL) { vp_declare(); id = v_header(vparser_type); } p = V_ALLOC(vparser, 1); p->id = *id; p->constants = vh_create(); p->variables = vh_create(); p->functions = vh_create(); p->value = NULL; p->errors = NULL; p->undef_ok = 0; return p; } /* Declare parser type */ vtype * vp_declare(void) { if (vparser_type == NULL) { vparser_type = v_create("PARSER", "P"); v_create_func(vparser_type, (void *(*)()) vp_create); v_copy_func(vparser_type, (void *(*)()) vp_copy); v_read_func(vparser_type, (void *(*)()) vp_read); v_write_func(vparser_type, vp_write); v_print_func(vparser_type, vp_print); v_destroy_func(vparser_type, vp_destroy); } return vparser_type; } /*! @brief Delete a builtin constant. @ingroup parser_custom @param p Parser. @param name Constant to delete. Does nothing if the constant doesn't exist. */ void vp_delconst(vparser *p, char *name) { VP_CHECK(p); vh_delete(p->constants, name); vh_delete(p->variables, name); } /*! @brief Delete a builtin function. @ingroup parser_custom @param p Parser. @param name Function to delete. Does nothing if the function doesn't exist. */ void vp_delfunc(vparser *p, char *name) { vpfunc *f; VP_CHECK(p); if ((f = vh_pget(p->functions, name)) != NULL) { vh_delete(p->functions, name); V_DEALLOC(f); } } /*! @brief Deallocate a parser. @ingroup parser_create @param p Parser. */ void vp_destroy(vparser *p) { char *name; viter iter; vpfunc *f; VP_CHECK(p); /* Destroy builtin functions */ v_iterate(p->functions, iter) { name = vh_iter_key(iter); f = vh_iter_pval(iter); V_DEALLOC(f); } /* Destroy parser */ vh_destroy(p->constants); vh_destroy(p->variables); vh_destroy(p->functions); if (p->value != NULL) vs_destroy(p->value); if (p->errors != NULL) vl_destroy(p->errors); V_DEALLOC(p); } /*! @brief Flag a parse error. @ingroup parser_custom @param fmt Format string. This function is for use inside a user-defined parser function to indicate that an error has occurred. */ void vp_err(char *fmt, ...) { extern vparser *vp_parser; V_NBUF_DECL(err); V_BUF_DECL; char *msg; if (vp_parser->errors == NULL) vp_parser->errors = vl_create(); V_NBUF_FMT(err, fmt, msg); if (report_line) V_BUF_SET2("line %d: %s", scanner_line + 1, V_NBUF_VAL(err)); else V_BUF_SET(V_NBUF_VAL(err)); vl_spush(vp_parser->errors, V_BUF_VAL); v_exception("%s", V_BUF_VAL); } /* Flag an errno error */ int vp_errno(char *name, int num) { vp_err("function \"%s\": %s", name, strerror(num)); return 1; } /*! @brief Return the last parser error. @ingroup parser_exp @param p Parser. @return Error string. @retval NULL if no errors occurred. */ char * vp_error(vparser *p) { VP_CHECK(p); return (p->errors != NULL ? vl_shead(p->errors) : NULL); } /*! @brief Set whether undefined variable references cause an error. @ingroup parser_custom @param p Parser. @param flag Yes (the default) or no. */ void vp_error_undef(vparser *p, int flag) { VP_CHECK(p); p->undef_ok = !flag; } /*! @brief Return the list of parser errors. @ingroup parser_exp @param p Parser. @return List of error strings. @retval NULL if no errors occurred. If an evaluation failed, then one or more errors occurred. This function returns the list of errors encountered during the last evaluation. */ vlist * vp_errors(vparser *p) { VP_CHECK(p); return p->errors; } /*! @brief Evaluate an expression. @ingroup parser_exp @param p Parser. @param expr Expression. @return Whether successful. Use vp_value() to get the value of the expression. */ int vp_eval(vparser *p, char *expr) { extern vscalar *vp_parse(vparser *p, char *expr); VP_CHECK(p); /* Initialise */ report_line = (strchr(expr, '\n') != NULL); if (p->value != NULL) vs_destroy(p->value); if (p->errors != NULL) vl_destroy(p->errors); p->errors = NULL; /* Parse input */ p->value = vp_parse(p, expr); /* Clean up */ report_line = 0; return (p->errors == NULL); } /*! @brief Return whether a variable exists. @ingroup parser_access @param p Parser. @param name Variable name. @return Yes or no. */ int vp_exists(vparser *p, char *name) { VP_CHECK(p); return vh_exists(p->variables, name); } /*! @brief Declare a parser function. @ingroup parser_custom @param p Parser. @param name Function name. @param func Function. @param minargs Minimum no. of arguments. @param maxargs Maximum no. of arguments. @see vp_func_min(), vp_func_max(), vp_func_args(), vp_func_any() Declare a parser function with the given name, function and range of arguments. Overrides any previous definition. A parser function is one that accepts a list of scalars as arguments and returns a single scalar, which should be of type V_INT, V_DOUBLE or V_STRING. In vp_eval(), the number of arguments supplied to the function must be between \c minargs and \c maxargs or a parse error is given. If \c maxargs is VP_FUNC_NOLIMIT, then there is no upper limit to the number of arguments. If the function fails for any reason, it should call vp_err() and return NULL. The function should check for the case where \c args is \c NULL, and treat it as if an empty list of arguments was passed. */ void vp_func(vparser *p, char *name, vscalar *(*func)(vlist *args), int minargs, int maxargs) { vpfunc *f, *oldf; VP_CHECK(p); f = V_ALLOC(vpfunc, 1); f->func = func; f->minargs = V_MIN(minargs, maxargs); f->maxargs = V_MAX(minargs, maxargs); if ((oldf = vh_pget(p->functions, name)) != NULL) V_DEALLOC(oldf); vh_pstore(p->functions, name, f); } /*! @brief Return the value of a parser variable. @ingroup parser_access @param p Parser. @param name Variable name. @return Its value. */ vscalar * vp_get(vparser *p, char *name) { extern vparser *vp_parser; vscalar *val; VP_CHECK(p); if ((val = vh_get(p->variables, name)) == NULL && !p->undef_ok && vp_parser != NULL) vp_err("undefined variable \"%s\"", name); return val; } /*! @brief Return whether a name exists as a constant. @ingroup parser_access @param p Parser. @param name Name. @return Yes or no. */ int vp_isconst(vparser *p, char *name) { VP_CHECK(p); return vh_exists(p->constants, name); } /*! @brief Return whether a name exists as a function. @ingroup parser_access @param p Parser. @param name Name. @return Yes or no. */ int vp_isfunc(vparser *p, char *name) { VP_CHECK(p); return vh_exists(p->functions, name); } /* Print contents of a parser */ void vp_print(vparser *p, FILE *fp) { vlist *list; char *name; viter iter; vpfunc *f; VP_CHECK(p); v_print_start(); v_push_indent(); v_print_type(vparser_type, p, fp); /* Print variables */ v_indent(fp); fprintf(fp, "VARIABLES => LIST\n"); v_push_indent(); list = vh_keys(p->variables); v_iterate(list, iter) { name = vl_iter_svalref(iter); v_indent(fp); fprintf(fp, "%s = %s", name, vh_sget(p->variables, name)); if (vh_exists(p->constants, name)) fprintf(fp, " (constant)"); fprintf(fp, "\n"); } v_pop_indent(); /* Print functions */ v_indent(fp); fprintf(fp, "FUNCTIONS => LIST\n"); v_push_indent(); list = vh_keys(p->functions); v_iterate(list, iter) { name = vl_iter_svalref(iter); f = vh_pget(p->functions, name); v_indent(fp); fprintf(fp, "%s", name); if (f->minargs == f->maxargs) fprintf(fp, " (%d arg%s)", f->minargs, (f->minargs == 1 ? "" : "s")); else if (f->maxargs == VP_FUNC_NOLIMIT) fprintf(fp, " (>= %d arg%s)", f->minargs, (f->minargs == 1 ? "" : "s")); else if (f->minargs == 0) fprintf(fp, " (<= %d arg%s)", f->maxargs, (f->maxargs == 1 ? "" : "s")); else fprintf(fp, " (%d-%d args)", f->minargs, f->maxargs); fprintf(fp, "\n"); } v_pop_indent(); /* That's it */ v_pop_indent(); v_print_finish(); } /* Read parser from a stream */ vparser * vp_read(FILE *fp) { vparser *p; /* Initialise */ p = vp_create_empty(); vp_add_builtins(p); /* Read variables */ vh_destroy(p->variables); if ((p->variables = vh_read(fp)) == NULL) return NULL; /* Read constant flags */ vh_destroy(p->constants); if ((p->constants = vh_read(fp)) == NULL) return NULL; return p; } /*! @brief Set the value of a parser variable. @ingroup parser_access @param p Parser. @param name Variable name. @param val Value to set. */ void vp_store(vparser *p, char *name, vscalar *val) { VP_CHECK(p); if (!vh_exists(p->constants, name)) vh_store(p->variables, name, val); else vp_err("attempt to modify constant \"%s\"", name); } /*! @brief Return the value of the last expression evaluated. @ingroup parser_exp @param p Parser. @return Expression value. */ vscalar * vp_value(vparser *p) { VP_CHECK(p); return p->value; } /* Write parser to a stream */ int vp_write(vparser *p, FILE *fp) { VP_CHECK(p); /* Write variables */ if (!vh_write(p->variables, fp)) return 0; /* Write constant flags */ if (!vh_write(p->constants, fp)) return 0; return 1; } /* Builtin functions */ static vscalar * builtin_max(vlist *args) { double max = vl_dget(args, 0); viter iter; v_iterate(args, iter) max = V_MAX(max, vl_iter_dval(iter)); return vs_dcreate(max); } static vscalar * builtin_min(vlist *args) { double min = vl_dget(args, 0); viter iter; v_iterate(args, iter) min = V_MIN(min, vl_iter_dval(iter)); return vs_dcreate(min); } static vscalar * builtin_randint(vlist *args) { int min = 0, max; switch (vl_length(args)) { case 0: max = 1; break; case 1: max = vl_iget(args, 0); break; default: min = vl_iget(args, 0); max = vl_iget(args, 1); break; } return vs_icreate(v_randint(min, max)); } static vscalar * builtin_randreal(vlist *args) { double min = 0.0, max; switch (vl_length(args)) { case 0: max = 1.0; break; case 1: max = vl_dget(args, 0); break; default: min = vl_dget(args, 0); max = vl_dget(args, 1); break; } return vs_dcreate(v_randreal(min, max)); }