pure-data/src/x_vexp.c

/* 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