/* Copyright (c) IRCAM. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* "expr" was written by Shahrokh Yadegari c. 1989. -msp */ /* "expr~" and "fexpr~" conversion by Shahrokh Yadegari c. 1999,2000 */ /* * * July 2024, Version 0.58 (major changes) * - Added string functions in expr; now expr can output symbols * - fixed the expr~ array[0] bug * - Provide better error messages; * now expr prints the expr string when reporting errors * - fixed a memory issue were extra unused vector output was * allocated for each inlet even though the were not used * - cleaned up some indentation issues * * Oct 2020, Version 0.57 * - fixed a bug in fact() * - fixed the bad lvalue bug - "4 + 5 = 3" was not caught before * - fact() (factorial) now calculates and returns its value in double * - Added mtof(), mtof(), dbtorms(), rmstodb(), powtodb(), dbtopow() * * July 2017, Version 0.55 * - The arrays now redraw after a store into one of their members * - ex_if() (the "if()" function is reworked to only evaluate * either the left or the right args depending on the truth * value of the condition. However, if the condition is a * vector, both the left and the right are evaluated regardless. * - priority of ',' and '=' was switched to fix the bug of using * store "=" in functions with multiple arguments, which caused * an error during execution. * - The number of inlet and outlets (EX_MAX_INLETS) is now set at 100 * * Oct 2015 * $x[-1] was not equal $x1[-1], not accessing the previous block * (bug fix by Dan Ellis) * * July 2002 * fixed bugs introduced in last changes in store and ET_EQ * --sdy * * Jan 2018, Version 0.56 * -fexpr~ now accepts a float in its first input * -Added avg() and Avg() back to the list of functions * * Feb 2002 * added access to variables * multiple expression support * new short hand forms for fexpr~ * now $y or $y1 = $y1[-1] and $y2 = $y2[-1] * --sdy * */ /* * vexp.c -- a variable expression evaluator * * This modules implements an expression evaluator using the * operator-precedence parsing. It transforms an infix expression * to a prefix stack ready to be evaluated. The expression sysntax * is close to that of C. There are a few operators that are not * supported and functions are also recognized. Strings can be * passed to functions when they are quoted in '"'s. "[]" are implemented * as an easy way of accessing the content of tables, and the syntax * table_name[index]. * Variables (inlets) are specified with the following syntax: $x#, * where x is either i(integers), f(floats), and s(strings); and # * is a digit that corresponds to the inlet number. The string variables * can be used as strings when they are quoted and can also be used as * table names when they are followed by "[]". * * signal vectors have been added to this implementation: * $v# denotes a signal vector * $x#[index] is the value of a sample at the index of a the signal vector * $x# is the shorthand for $x#[0] * $y[index] is the value of the sample output at the index of a the * signal output * "index" for $x#[index] has to have this range (0 <= index < vectorsize) * "index" for $y[index] has to have this range (0 < index < vectorsize) */ #include <string.h> #include <stdlib.h> #include <stdarg.h> #include <ctype.h> #include "x_vexp.h" #include <errno.h> #ifdef MSP #undef isdigit #define isdigit … #endif #if defined _MSC_VER && (_MSC_VER < 1800) #define strtof … #endif char *atoif(char *s, long int *value, long int *type); static struct ex_ex *ex_lex(struct expr *expr, long int *n); struct ex_ex *ex_match(struct ex_ex *eptr, long int op); struct ex_ex *ex_parse(struct expr *expr, struct ex_ex *iptr, struct ex_ex *optr, long int *argc); static int ex_checklval (struct expr *e, struct ex_ex *eptr); struct ex_ex *ex_eval(struct expr *expr, struct ex_ex *eptr, struct ex_ex *optr, int i); int expr_donew(struct expr *exprr, int ac, t_atom *av); struct ex_ex *eval_func(struct expr *expr,struct ex_ex *eptr, struct ex_ex *optr, int i); struct ex_ex *eval_tab(struct expr *expr, struct ex_ex *eptr, struct ex_ex *optr, int i); struct ex_ex *eval_var(struct expr *expr, struct ex_ex *eptr, struct ex_ex *optr, int i); struct ex_ex *eval_store(struct expr *expr, struct ex_ex *eptr, struct ex_ex *optr, int i); struct ex_ex *eval_sigidx(struct expr *expr, struct ex_ex *eptr, struct ex_ex *optr, int i); static int cal_sigidx(struct ex_ex *optr, /* The output value */ int i, t_float rem_i, /* integer and fractinal part of index */ int idx, /* index of current fexpr~ processing */ int vsize, /* vector size */ t_float *curvec, t_float *prevec); /* current and previous table */ t_ex_func *find_func(char *s); void ex_dzdetect(struct expr *expr); #define MAX_ARGS … extern t_ex_func ex_funcs[]; struct ex_ex nullex = …; void set_tokens (char *s); int getoken (struct expr *expr, struct ex_ex *eptr); void ex_print (struct ex_ex *eptr); #ifdef MSP void atom_string(t_atom *a, char *buf, unsigned int bufsize); void atom_string(t_atom *a, char *buf, unsigned int bufsize) { char tbuf[30]; switch(a->a_type) { case A_SEMI: strcpy(buf, ";"); break; case A_COMMA: strcpy(buf, ","); break; #ifdef PD case A_POINTER: strcpy(buf, "(pointer)"); break; #endif case A_FLOAT: sprintf(tbuf, "%g", a->a_w.w_float); if (strlen(tbuf) < bufsize-1) strcpy(buf, tbuf); else if (a->a_w.w_float < 0) strcpy(buf, "-"); else strcat(buf, "+"); break; case A_LONG: sprintf(tbuf, "%d", a->a_w.w_long); if (strlen(tbuf) < bufsize-1) strcpy(buf, tbuf); else if (a->a_w.w_float < 0) strcpy(buf, "-"); else strcat(buf, "+"); break; case A_SYMBOL: { char *sp; unsigned int len; int quote; for (sp = a->a_w.w_symbol->s_name, len = 0, quote = 0; *sp; sp++, len++) if (*sp == ';' || *sp == ',' || *sp == '\\' || (*sp == '$' && sp == a->a_w.w_symbol->s_name && sp[1] >= '0' && sp[1] <= '9')) quote = 1; if (quote) { char *bp = buf, *ep = buf + (bufsize-2); sp = a->a_w.w_symbol->s_name; while (bp < ep && *sp) { if (*sp == ';' || *sp == ',' || *sp == '\\' || (*sp == '$' && bp == buf && sp[1] >= '0' && sp[1] <= '9')) *bp++ = '\\'; *bp++ = *sp++; } if (*sp) *bp++ = '*'; *bp = 0; /* post("quote %s -> %s", a->a_w.w_symbol->s_name, buf); */ } else { if (len < bufsize-1) strcpy(buf, a->a_w.w_symbol->s_name); else { strncpy(buf, a->a_w.w_symbol->s_name, bufsize - 2); strcpy(buf + (bufsize - 2), "*"); } } } break; #ifdef PD case A_DOLLAR: sprintf(buf, "$%d", a->a_w.w_index); break; case A_DOLLSYM: sprintf(buf, "$%s", a->a_w.w_symbol->s_name); break; #else /* MAX */ case A_DOLLAR: sprintf(buf, "$%s", a->a_w.w_symbol->s_name); break; #endif default: post("atom_string bug"); } } #endif /* MSP */ /* * expr_donew -- create a new "expr" object. * returns 1 on failure, 0 on success. */ int expr_donew(struct expr *expr, int ac, t_atom *av) { … } void * ex_calloc(size_t count, size_t size) { … } void ex_free(void *ptr) { … } void * ex_malloc(size_t size) { … } void * ex_realloc(void *ptr, size_t size) { … } /* * ex_lex -- This routine is a bit more than a lexical parser since it will * also do some syntax checking. It reads the string s and will * return a linked list of struct ex_ex. * It will also put the number of the nodes in *n. */ struct ex_ex * ex_lex(struct expr *expr, long int *n) { … } /* * ex_match -- this routine walks through the eptr and matches the * parentheses and brackets, it also converts the function * names to a pointer to the describing structure of the * specified function */ /* operator to match */ struct ex_ex * ex_match(struct ex_ex *eptr, long int op) { … } /* * ex_parse -- This function is called when we have already done some * parsing on the expression, and we have already matched * our brackets and parenthesis. The main job of this * function is to convert the infix expression to the * prefix form. * First we find the operator with the lowest precedence and * put it on the stack ('optr', it is really just an array), then * we call ourself (ex_parse()), on its arguments (unary operators * only have one operator.) * When "argc" is set it means that we are parsing the arguments * of a function and we will increment *argc anytime we find * a segment that can qualify as an argument (counting commas). * * returns 0 on syntax error */ /* number of argument separated by comma */ struct ex_ex * ex_parse(struct expr *x, struct ex_ex *iptr, struct ex_ex *optr, long int *argc) { … } /* * ex_checklval -- check the left value for all stores ('=') * all left values should either be a variable or a table * return 1 if syntax error * return 0 on success */ static int ex_checklval(struct expr *e, struct ex_ex *eptr) { … } /* * this is the divide zero check for a non divide operator */ #define DZC(ARG1,OPR,ARG2) … #define EVAL(OPR) … /* * evaluate a unary operator, TYPE is applied to float operands */ #define EVAL_UNARY(OPR, TYPE) … void ex_mkvector(t_float *fp, t_float x, int size) { … } /* * ex_dzdetect -- divide by zero detected */ void ex_dzdetect(struct expr *expr) { … } /* * ex_eval -- evaluate the array of prefix expression * ex_eval returns the pointer to the first unevaluated node * in the array. This is a recursive routine. */ /* SDY - look into the variable nullret */ struct ex_ex * ex_eval(struct expr *expr, struct ex_ex *eptr, struct ex_ex *optr, int idx) /* the expr object data pointer */ /* the operation stack */ /* the result pointer */ /* the sample number processed for fexpr~ */ { … } extern struct ex_ex * ex_if(t_expr *expr, struct ex_ex *eptr, struct ex_ex *optr,struct ex_ex *argv, int idx); /* * eval_func -- evaluate a function, call ex_eval() on all the arguments * so that all of them are terminal nodes. The call the * appropriate function */ struct ex_ex * eval_func(struct expr *expr, struct ex_ex *eptr, struct ex_ex *optr, int idx) /* the expr object data pointer */ /* the operation stack */ /* the result pointer */ { … } /* * eval_store -- evaluate the '=' operator, * make sure the first operator is a legal left operator * and call ex_eval on the right operator */ struct ex_ex * eval_store(struct expr *expr, struct ex_ex *eptr, struct ex_ex *optr, int idx) /* the expr object data pointer */ /* the operation stack */ /* the result pointer */ { … } /* * eval_tab -- evaluate a table operation */ struct ex_ex * eval_tab(struct expr *expr, struct ex_ex *eptr, struct ex_ex *optr, int idx) /* the expr object data pointer */ /* the operation stack */ /* the result pointer */ { … } /* * eval_var -- evaluate a variable */ struct ex_ex * eval_var(struct expr *expr, struct ex_ex *eptr, struct ex_ex *optr, int idx) /* the expr object data pointer */ /* the operation stack */ /* the result pointer */ { … } /* * eval_sigidx -- evaluate the value of an indexed signal for fexpr~ */ struct ex_ex * eval_sigidx(struct expr *expr, struct ex_ex *eptr, struct ex_ex *optr, int idx) /* the expr object data pointer */ /* the operation stack */ /* the result pointer */ /* the index */ { … } /* * cal_sigidx -- given two tables (one current one previous) calculate an * evaluation of a float index into the vectors by linear * interpolation * return 0 on success, 1 on failure (index out of bound) */ static int cal_sigidx(struct ex_ex *optr, /* The output value */ int i, t_float rem_i,/* integer and fractinal part of index */ int idx, /* index of current fexpr~ processing */ int vsize, /* vector size */ t_float *curvec, t_float *prevec) /* current and previous table */ { … } /* * getoken -- return 1 on syntax error otherwise 0 */ int getoken(struct expr *expr, struct ex_ex *eptr) { … } /* * atoif -- ascii to float or integer (strtof() understand exponential notations ad strtod() understand hex numbers) */ char * atoif(char *s, long int *value, long int *type) { … } /* * find_func -- returns a pointer to the found function structure * otherwise it returns 0 */ t_ex_func * find_func(char *s) { … } /* * ex_error - print an error message */ void ex_error(t_expr *e, const char *fmt, ...) { … } /* * ex_print -- print an expression array */ void ex_print(struct ex_ex *eptr) { … } #ifdef _WIN32 void ABORT(void) {bug("expr");} #endif