diff --git a/src/libCom/Makefile.Host b/src/libCom/Makefile.Host index 6d87a06b4..d671c8a3f 100644 --- a/src/libCom/Makefile.Host +++ b/src/libCom/Makefile.Host @@ -36,6 +36,7 @@ INC += epicsString.h INC += truncateFile.h INC += adjustment.h INC += postfix.h +INC += sCalcPostfix.h # For WIN32 we supply getopt as part of libCom: INC_WIN32 := getopt.h @@ -47,7 +48,6 @@ INC_WIN32 := getopt.h # 2) where to find the sources: ., ./os/, ./os/generic, ... # LIBSRCS += bucketLib.c -LIBSRCS += calcPerform.c LIBSRCS += cvtFast.c LIBSRCS += ellLib.c LIBSRCS += envSubr.c @@ -60,6 +60,10 @@ LIBSRCS += freeListLib.c LIBSRCS += gpHashLib.c LIBSRCS += memDebugLib.c LIBSRCS += postfix.c +LIBSRCS += calcPerform.c +LIBSRCS += sCalcPostfix.c +LIBSRCS += sCalcPerform.c +LIBSRCS += sCalcPostfix.c LIBSRCS += realpath.c LIBSRCS += tsSubr.c LIBSRCS += assertUNIX.c diff --git a/src/libCom/Makefile.Vx b/src/libCom/Makefile.Vx index 37638525c..0ddb1d07d 100644 --- a/src/libCom/Makefile.Vx +++ b/src/libCom/Makefile.Vx @@ -5,7 +5,6 @@ USR_INCLUDES = -I$(TOP)/src/include/os/vxWorks VPATH = ../os/vxWorks -SRCS.c += ../calcPerform.c SRCS.c += ../cvtFast.c SRCS.c += ../gpHashLib.c SRCS.c += ../freeListLib.c @@ -14,6 +13,9 @@ SRCS.c += ../envSubr.c SRCS.c += envData.c SRCS.c += ../errSymLib.c SRCS.c += ../postfix.c +SRCS.c += ../calcPerform.c +SRCS.c += ../sCalcPostfix.c +SRCS.c += ../sCalcPerform.c SRCS.c += ../bucketLib.c SRCS.c += ../memDebugLib.c SRCS.c += ../tsSubr.c @@ -33,7 +35,6 @@ SRCS.c += ../epicsString.c SRCS.c += ../aToIPAddr.c SRCS.c += ../adjustment.c -LIBOBJS += calcPerform.o LIBOBJS += cvtFast.o LIBOBJS += ellLib.o LIBOBJS += envData.o @@ -41,6 +42,9 @@ LIBOBJS += envSubr.o LIBOBJS += errSymLib.o LIBOBJS += errSymTbl.o LIBOBJS += postfix.o +LIBOBJS += calcPerform.o +LIBOBJS += sCalcPostfix.o +LIBOBJS += sCalcPerform.o LIBOBJS += bucketLib.o LIBOBJS += tsSubr.o LIBOBJS += gpHashLib.o diff --git a/src/libCom/calc/sCalcPerform.c b/src/libCom/calc/sCalcPerform.c new file mode 100644 index 000000000..52efc76a9 --- /dev/null +++ b/src/libCom/calc/sCalcPerform.c @@ -0,0 +1,1458 @@ +/* $Id$ */ +/* + * Author: Julie Sander and Bob Dalesio + * Date: 07-27-87 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * 03-18-98 tmm Essentially rewritten to support string operators + */ + +/* This module contains the code for processing the arithmetic + * expressions defined in calculation records. postfix must be called + * to convert a valid infix expression to postfix. sCalcPerform + * calculates the postfix expression. + * + * Subroutines + * + * Public + * + * sCalcPerform perform the calculation + * args + * double *parg address of arguments + * int numArgs number of arguments in pargs array + * double *psarg address of string arguments + * int numSArgs number of string arguments in psargs array + * double *presult address of double result + * char *psresult address of string-result buffer + * int lenSresult length of string-result buffer + * char *rpcl address of postfix buffer + * returns + * 0 fetched successfully + * -1 fetch failed + * + * Private routine for sCalcPerform + * local_random random number generator + * returns + * double value between 0.00 and 1.00 + */ + +#ifdef vxWorks +#include +#endif + +#include +#include +#include +#include + +#include "dbDefs.h" +#include "cvtFast.h" +#define epicsExportSharedSymbols +#include "sCalcPostfix.h" +#include "sCalcPostfixPvt.h" + +static double local_random(); + +static int isnan(double d) +{ + union { long l[2]; double d; } u; + u.d = d; + if ((u.l[0] & 0x7ff00000) != 0x7ff00000) return(0); + if (u.l[1] || (u.l[0] & 0x000fffff)) return(1); + return(0); +} +#define myNINT(a) ((int)((a) >= 0 ? (a)+0.5 : (a)-0.5)) +#ifndef PI +#define PI 3.141592654 +#endif +#define MAX(a,b) (a)>(b)?(a):(b) +#define MIN(a,b) (a)<(b)?(a):(b) + + +#define STACKSIZE 30 /* <-------------------------<<<<<<< */ + +struct stackElement { + double d; + char *s; +}; + +#define DEBUG 0 +volatile int sCalcPerformDebug = 0; +#if DEBUG +int sCalcStackHW = 0; +int sCalcStackLW = 0; +#define INC(ps) {if ((int)(++(ps)-top) > sCalcStackHW) sCalcStackHW = (int)((ps)-top);} +#define DEC(ps) {if ((int)(--(ps)-top) < sCalcStackLW) sCalcStackLW = (int)((ps)-top);} +#else +#define INC(ps) ++ps +#define DEC(ps) ps-- +#endif + + +#define isDouble(ps) ((ps)->s==NULL) +#define isString(ps) ((ps)->s) + +#define cleanStringElement(ps) {free((ps)->s); (ps)->s=NULL;} + +static void cleanStackElement(struct stackElement *ps) +{ + if (isString(ps)) { + free(ps->s); + ps->s = NULL; + } +} + +static void cleanup(struct stackElement *ps, struct stackElement *pe) +{ + for (; ps <= pe; ps++) { + if (isString(ps)) { + free(ps->s); + ps->s = NULL; + } + } +} + +#define toDouble(ps) {if (isString(ps)) to_double(ps);} + +/* convert stack element to double */ +static void to_double(struct stackElement *ps) +{ + ps->d = atof(ps->s); + free(ps->s); + ps->s = NULL; +} + +#define toString(ps) {if (isDouble(ps)) to_string(ps);} + +/* convert stack element to string */ +static void to_string(struct stackElement *ps) +{ + ps->s = calloc(20, 1); + /* any precision greater than 8 results in (slow) sprintf call */ + if (isnan(ps->d)) + strcpy(ps->s,"NaN"); + else + (void)cvtDoubleToString(ps->d, ps->s, 8); +} + +static char *findConversionIndicator(char *s) +{ + char *cc=NULL, *s1, *retval; + + while (s && *s) { + if ((s1 = strstr(s, "%%")) != NULL) { + /* not a conversion/assignment indicator; skip over */ + s = s1+2; continue; + } + if ((s = strchr(s, (int)'%')) == NULL) { + return(NULL); + } + if ((cc = strpbrk(s, "pwn$c[deEfgGiousxX")) == NULL) { + return(NULL); + } + /* + * (*cc) is a conversion character; look for suppressed assignment + * ('*' occurring after '%' and before conversion character) + */ + s1 = strchr(s, (int)'*'); + if (s1 && (s1 < cc)) { + /* suppressed assignment; skip past conversion character */ + s = cc+1; + if (*cc == '[') { + /* skip character set ([..], []..], or [^]..]) */ + if (cc[1] == ']') { + s = &(cc[2]); + } else if ((cc[1] == '^') && (cc[2] == ']')) { + s = &(cc[3]); + } + s = strchr(s, (int)']'); + if (s == NULL) { + /* bad character-set syntax */ + return(NULL); + } + s++; /* skip past ']' */ + } + /* keep looking for conversion/assignment character */ + + continue; + } else { + /* (*cc) is a conversion/assignment character */ + break; + } + } + if (cc == NULL) return(NULL); + retval = cc; + /* + * (*cc) is a conversion/assignment indicator. Make sure there + * aren't any more in the format string. + */ + s = cc+1; + while (s && *s) { + if ((s1 = strstr(s, "%%")) != NULL) { + /* not a conversion/assignment indicator; skip over */ + s = s1+2; continue; + } + if ((s = strchr(s, (int)'%')) == NULL) return(retval); + if ((cc = strpbrk(s, "pwn$c[deEfgGiousxX")) == NULL) return(retval); + /* + * (*cc) is a conversion character; look for suppressed assignment + * ('*' occurring after '%' and before conversion character) + */ + s1 = strchr(s, (int)'*'); + if (s1 && (s1 < cc)) { + /* suppressed assignment; skip past conversion character */ + s = cc+1; + if (*cc == '[') { + /* skip character set ([..], []..], or [^]..]) */ + if (cc[1] == ']') { + s = &(cc[2]); + } else if ((cc[1] == '^') && (cc[2] == ']')) { + s = &(cc[3]); + } + s = strchr(s, (int)']'); + if (s == NULL) return(NULL); /* bad character-set syntax */ + s++; /* skip past ']' */ + } + continue; + } else { + /* (*cc) assignment is not suppressed */ + return(NULL); + } + } + return(retval); +} + +#if DEBUG +/* Override standard EPICS expression evaluator (if we're loaded after it) */ +epicsShareFunc long epicsShareAPI + calcPerform(double *parg, double *presult, char *post) +{ + return(sCalcPerform(parg, 12, NULL, 0, presult, NULL, 0, post)); +} +#endif + +epicsShareFunc long epicsShareAPI + sCalcPerform(double *parg, int numArgs, char **psarg, int numSArgs, double *presult, char *psresult, int lenSresult, char *post) +{ + struct stackElement stack[STACKSIZE], *top; + register struct stackElement *ps, *ps1, *ps2; + char *s2, tmpstr[1000]; + register char *s, *s1; + register int i, j, k; + long l; + unsigned short ui; + unsigned long ul; + float f; + double d; + register double *topd, *pd; + short h, got_if; + +#if DEBUG + if (sCalcPerformDebug>=10) { + int more; + printf("sCalcPerform: postfix:"); + for (s=post, more=1; more;) { + printf("%2d ", *s); + switch (*s) { + case END_STACK: + more = 0; + break; + case LITERAL: + printf("(0x"); + for (i=0, s++; i<8; i++, s++) printf("%2x ", (unsigned int)(unsigned char)*s); + printf(") "); + break; + case SLITERAL: + s++; /* point past code */ + printf("'"); + while (*s) printf("%c", *s++); + printf("' "); + s++; + break; + case FETCH: + s++; /* point past code */ + printf("@%d ", *s++); + break; + case SFETCH: + s++; /* point past code */ + printf("$%d ", *s++); + break; + default: + if (*s == BAD_EXPRESSION) more=0; + s++; + break; + } + } + printf("\n"); + } +#endif + + /* Make sure postfix expression exists and is nontrivial */ + /* if ((*post == END_STACK) || (*post == BAD_EXPRESSION)) return(-1);*/ + if (*post == BAD_EXPRESSION) return(-1); + + + if (*post++ != USES_STRING) { + + topd = pd = (double *)&stack[10]; + pd--; + + /* No string expressions */ + while (*post != END_STACK) { + + switch (*post){ + + case FETCH: + ++pd; + ++post; + *pd = (*post < numArgs) ? parg[*post] : 0; + break; + + case STORE: + /* not implemented */ + return(-1); + + case CONST_PI: + ++pd; + *pd = PI; + break; + + case CONST_D2R: + ++pd; + *pd = PI/180.; + break; + + case CONST_R2D: + ++pd; + *pd = 180./PI; + break; + + case CONST_S2R: + ++pd; + *pd = PI/(180.*3600); + break; + + case CONST_R2S: + ++pd; + *pd = (180.*3600)/PI; + break; + + case ADD: + --pd; + *pd = *pd + pd[1]; + break; + + case SUB: + --pd; + *pd = *pd - pd[1]; + break; + + case MULT: + --pd; + *pd = *pd * pd[1]; + break; + + case DIV: + --pd; + if (pd[1] == 0) /* can't divide by zero */ + return(-1); + *pd = *pd / pd[1]; + break; + + case COND_IF: + /* if false condition then skip true expression */ + if (*pd == 0.0) { + /* skip to matching COND_ELSE */ + for (got_if=1; got_if>0 && *(post+1) != END_STACK; ++post) { + switch(post[1]) { + case LITERAL: post+=8; break; + case COND_IF: got_if++; break; + case COND_ELSE: got_if--; break; + case FETCH: case SFETCH: post++; break; + } + } + } + /* remove condition from stack top */ + --pd; + break; + + case COND_ELSE: + /* result, true condition is on stack so skip false condition */ + /* skip to matching COND_END */ + for (got_if=1; got_if>0 && *(post+1) != END_STACK; ++post) { + switch(post[1]) { + case LITERAL: post+=8; break; + case COND_IF: got_if++; break; + case COND_ELSE: got_if--; break; + case FETCH: post++; break; + } + } + break; + + case COND_END: + break; + + case ABS_VAL: + if (*pd < 0 ) *pd *= -1; + break; + + case UNARY_NEG: + *pd *= -1; + break; + + case SQU_RT: + /* check for neg number */ + if (*pd < 0) return(-1); + *pd = sqrt(*pd); + break; + + case EXP: + *pd = exp(*pd); + break; + + case LOG_10: + /* check for neg number */ + if (*pd < 0) return(-1); + *pd = log10(*pd); + break; + + case LOG_E: + /* check for neg number */ + if (*pd < 0) return(-1); + *pd = log(*pd); + break; + + case RANDOM: + ++pd; + *pd = local_random(); + break; + + case EXPON: + --pd; + if (*pd == 0) break; + if (*pd < 0) { + i = (int) pd[1]; + /* is exponent an integer? */ + if ((pd[1] - (double)i) != 0) return (-1); + *pd = exp(pd[1] * log(-(*pd))); + /* is value negative */ + if ((i % 2) > 0) *pd = -(*pd); + } else { + *pd = exp(pd[1] * log(*pd)); + } + break; + + case MODULO: + --pd; + if ((int)(pd[1]) == 0) + return(-1); + *pd = (double)((int)(*pd) % (int)(pd[1])); + break; + + case REL_OR: + --pd; + *pd = *pd || pd[1]; + break; + + case REL_AND: + --pd; + *pd = *pd && pd[1]; + break; + + case BIT_OR: + /* force double values into integers and or them */ + --pd; + *pd = (int)(pd[1]) | (int)(*pd); + break; + + case BIT_AND: + /* force double values into integers and and them */ + --pd; + *pd = (int)(pd[1]) & (int)(*pd); + break; + + case BIT_EXCL_OR: + /* force double values to integers to exclusive or them */ + --pd; + *pd = (int)(pd[1]) ^ (int)(*pd); + break; + + case GR_OR_EQ: + --pd; + *pd = *pd >= pd[1]; + break; + + case GR_THAN: + --pd; + *pd = *pd > pd[1]; + break; + + case LESS_OR_EQ: + --pd; + *pd = *pd <= pd[1]; + break; + + case LESS_THAN: + --pd; + *pd = *pd < pd[1]; + break; + + case NOT_EQ: + --pd; + *pd = *pd != pd[1]; + break; + + case EQUAL: + --pd; + *pd = *pd == pd[1]; + break; + + case RIGHT_SHIFT: + --pd; + *pd = (int)(*pd) >> (int)(pd[1]); + break; + + case LEFT_SHIFT: + --pd; + *pd = (int)(*pd) << (int)(pd[1]); + break; + + case MAX_VAL: + --pd; + if (*pd < pd[1]) *pd = pd[1]; + break; + + case MIN_VAL: + --pd; + if (*pd > pd[1]) *pd = pd[1]; + break; + + case ACOS: + *pd = acos(*pd); + break; + + case ASIN: + *pd = asin(*pd); + break; + + case ATAN: + *pd = atan(*pd); + break; + + case ATAN2: + --pd; + *pd = atan2(pd[1], *pd); + break; + + case COS: + *pd = cos(*pd); + break; + + case SIN: + *pd = sin(*pd); + break; + + case TAN: + *pd = tan(*pd); + break; + + case COSH: + *pd = cosh(*pd); + break; + + case SINH: + *pd = sinh(*pd); + break; + + case TANH: + *pd = tanh(*pd); + break; + + case CEIL: + *pd = ceil(*pd); + break; + + case FLOOR: + *pd = floor(*pd); + break; + + case NINT: + d = *pd; + *pd = (double)(long)(d >= 0 ? d+0.5 : d-0.5); + break; + + case REL_NOT: + *pd = (*pd ? 0 : 1); + break; + + case BIT_NOT: + *pd = ~(int)(*pd); + break; + + case LITERAL: + ++pd; + ++post; + if (post == NULL) { + ++post; + printf("%.7s bad constant in expression\n",post); + *pd = 0.; + break; + } + memcpy((void *)&(*pd),post,8); + post += 7; + break; + + default: + break; + } + + /* move ahead in postfix expression */ + ++post; + } + + /* if everything is peachy,the stack should end at its first position */ + if (pd != topd) return(-1); +#if DEBUG > 1 +/* check out floating rep of numbers like NaN, Inf */ +{ +union { unsigned char s[8]; double d; } u; +u.d = *pd; +printf("sCalcPerform: result: %g = (0x", *pd); +for (i=0; i<8; i++) printf("%2x ", u.s[i]); +printf(") \n"); +} +#endif + *presult = *pd; + if (psresult && (lenSresult > 15)) { + if (isnan(*pd)) + strcpy(psresult,"NaN"); + else + (void)cvtDoubleToString(*pd, psresult, 8); + } + } else { + + /*** expression requires string operations ***/ + + top = ps = &stack[10]; + ps--; /* Expression handler assumes ps is pointing to a filled element */ + + /* string expressions and values handled */ + while (*post != END_STACK) { + + switch (*post){ + + case FETCH: + INC(ps); + ++post; + ps->s = NULL; + ps->d = (*post < numArgs) ? parg[*post] : 0; + break; + + case SFETCH: + INC(ps); + ++post; + if (*post < numSArgs) { + /* fetch from string variable */ + ps->s = calloc(strlen(psarg[*post])+1, 1); + strcpy(ps->s, psarg[*post]); + } else { + /* fetch from variable that caller did not supply */ + ps->s = calloc(1, 1); + *(ps->s) = 0; + } + break; + + case STORE: + /* not implemented */ + cleanup(top, ps); + return(-1); + + case CONST_PI: + INC(ps); + ps->s = NULL; + ps->d = PI; + break; + + case CONST_D2R: + INC(ps); + ps->s = NULL; + ps->d = PI/180.; + break; + + case CONST_R2D: + INC(ps); + ps->s = NULL; + ps->d = 180./PI; + break; + + case CONST_S2R: + INC(ps); + ps->s = NULL; + ps->d = PI/(180.*3600); + break; + + case CONST_R2S: + INC(ps); + ps->s = NULL; + ps->d = (180.*3600)/PI; + break; + + case ADD: + ps1 = ps; + DEC(ps); + if (isDouble(ps)) { + toDouble(ps1); + ps->d = ps->d + ps1->d; + } else if (isDouble(ps1)) { + to_double(ps); + ps->d = ps->d + ps1->d; + } else { + /* concatenate two strings */ + s = ps->s; + ps->s = calloc(strlen(ps->s) + strlen(ps1->s) + 1, 1); + strcpy(ps->s, s); + strcat(ps->s, ps1->s); + free(s); + cleanStringElement(ps1); + } + break; + + case SUB: + ps1 = ps; + DEC(ps); + if (isDouble(ps)) { + toDouble(ps1); + ps->d = ps->d - ps1->d; + } else if (isDouble(ps1)) { + to_double(ps); + ps->d = ps->d - ps1->d; + } else { + /* subtract ps1->s from ps->s */ + s = strstr(ps->s, ps1->s); + s1 = ps->s; + s2 = ps1->s; + if (s && (strlen(s2) <= (strlen(s1) - (s - s1)))) { + for (s1=s+strlen(s2); *s1; s++, s1++) *s = *s1; + *s = '\0'; + } + cleanStringElement(ps1); + } + break; + + case MULT: + ps1 = ps; + DEC(ps); + toDouble(ps1); + toDouble(ps); + ps->d = ps->d * ps1->d; + break; + + case DIV: + ps1 = ps; + DEC(ps); + toDouble(ps1); + toDouble(ps); + if (ps1->d == 0) /* can't divide by zero */ + return(-1); + ps->d = ps->d / ps1->d; + break; + + case COND_IF: + /* if false condition then skip true expression */ + toDouble(ps); + if (ps->d == 0.0) { + /* skip to matching COND_ELSE */ + for (got_if=1; got_if>0 && *(post+1) != END_STACK; ++post) { + switch(post[1]) { + case LITERAL: post+=8; break; + case SLITERAL: while (*(++post)); break; + case COND_IF: got_if++; break; + case COND_ELSE: got_if--; break; + case FETCH: case SFETCH: post++; break; + } + } + } + /* remove condition from stack top */ + DEC(ps); + break; + + case COND_ELSE: + /* result, true condition is on stack so skip false condition */ + /* skip to matching COND_END */ + for (got_if=1; got_if>0 && *(post+1) != END_STACK; ++post) { + switch(post[1]) { + case LITERAL: post+=8; break; + case SLITERAL: while (*(++post)); break; + case COND_IF: got_if++; break; + case COND_ELSE: got_if--; break; + case FETCH: case SFETCH: post++; break; + } + } + break; + + case COND_END: + break; + + case ABS_VAL: + toDouble(ps); + if (ps->d < 0 ) ps->d *= -1; + break; + + case UNARY_NEG: + toDouble(ps); + ps->d *= -1; + break; + + case SQU_RT: + toDouble(ps); + /* check for neg number */ + if (ps->d < 0) {cleanup(top, ps); return(-1);} + ps->d = sqrt(ps->d); + break; + + case EXP: + toDouble(ps); + ps->d = exp(ps->d); + break; + + case LOG_10: + toDouble(ps); + /* check for neg number */ + if (ps->d < 0) {cleanup(top, ps); return(-1);} + ps->d = log10(ps->d); + break; + + case LOG_E: + toDouble(ps); + /* check for neg number */ + if (ps->d < 0) {cleanup(top, ps); return(-1);} + ps->d = log(ps->d); + break; + + case RANDOM: + INC(ps); + ps->d = local_random(); + ps->s = NULL; + break; + + case EXPON: + ps1 = ps; + DEC(ps); + toDouble(ps1); + toDouble(ps); + if (ps->d == 0) break; + if (ps->d < 0) { + i = (int) ps1->d; + /* is exponent an integer? */ + if ((ps1->d - (double)i) != 0) return (-1); + ps->d = exp(ps1->d * log(-(ps->d))); + /* is value negative */ + if ((i % 2) > 0) ps->d = -ps->d; + } else { + ps->d = exp(ps1->d * log(ps->d)); + } + break; + + case MODULO: + ps1 = ps; + DEC(ps); + toDouble(ps1); + toDouble(ps); + if ((int)ps1->d == 0) + return(-1); + ps->d = (double)((int)ps->d % (int)ps1->d); + break; + + case REL_OR: + ps1 = ps; + DEC(ps); + toDouble(ps1); + toDouble(ps); + ps->d = ps->d || ps1->d; + break; + + case REL_AND: + ps1 = ps; + DEC(ps); + toDouble(ps1); + toDouble(ps); + ps->d = ps->d && ps1->d; + break; + + case BIT_OR: + /* force double values into integers and or them */ + ps1 = ps; + DEC(ps); + toDouble(ps1); + toDouble(ps); + ps->d = (int)(ps1->d) | (int)(ps->d); + break; + + case BIT_AND: + /* force double values into integers and and them */ + ps1 = ps; + DEC(ps); + toDouble(ps1); + toDouble(ps); + ps->d = (int)(ps1->d) & (int)(ps->d); + break; + + case BIT_EXCL_OR: + /* force double values to integers to exclusive or them */ + ps1 = ps; + DEC(ps); + toDouble(ps1); + toDouble(ps); + ps->d = (int)(ps1->d) ^ (int)(ps->d); + break; + + case GR_OR_EQ: + ps1 = ps; + DEC(ps); + if (isDouble(ps)) { + toDouble(ps1); + ps->d = ps->d >= ps1->d; + } else if (isDouble(ps1)) { + to_double(ps); + ps->d = ps->d >= ps1->d; + } else { + /* compare ps->s to ps1->s */ + ps->d = (double)(strcmp(ps->s, ps1->s) >= 0); + free(ps->s); + ps->s = NULL; + cleanStringElement(ps1); + } + break; + + case GR_THAN: + ps1 = ps; + DEC(ps); + if (isDouble(ps)) { + toDouble(ps1); + ps->d = ps->d > ps1->d; + } else if (isDouble(ps1)) { + to_double(ps); + ps->d = ps->d > ps1->d; + } else { + /* compare ps->s to ps1->s */ + ps->d = (double)(strcmp(ps->s, ps1->s) > 0); + free(ps->s); + ps->s = NULL; + cleanStringElement(ps1); + } + break; + + case LESS_OR_EQ: + ps1 = ps; + DEC(ps); + if (isDouble(ps)) { + toDouble(ps1); + ps->d = ps->d <= ps1->d; + } else if (isDouble(ps1)) { + to_double(ps); + ps->d = ps->d <= ps1->d; + } else { + /* compare ps->s to ps1->s */ + ps->d = (double)(strcmp(ps->s, ps1->s) <= 0); + free(ps->s); + ps->s = NULL; + cleanStringElement(ps1); + } + break; + + case LESS_THAN: + ps1 = ps; + DEC(ps); + if (isDouble(ps)) { + toDouble(ps1); + ps->d = ps->d < ps1->d; + } else if (isDouble(ps1)) { + to_double(ps); + ps->d = ps->d < ps1->d; + } else { + /* compare ps->s to ps1->s */ + ps->d = (double)(strcmp(ps->s, ps1->s) < 0); + free(ps->s); + ps->s = NULL; + cleanStringElement(ps1); + } + break; + + case NOT_EQ: + ps1 = ps; + DEC(ps); + if (isDouble(ps)) { + toDouble(ps1); + ps->d = ps->d != ps1->d; + } else if (isDouble(ps1)) { + to_double(ps); + ps->d = ps->d != ps1->d; + } else { + /* compare ps->s to ps1->s */ + ps->d = (double)(strcmp(ps->s, ps1->s) != 0); + free(ps->s); + ps->s = NULL; + cleanStringElement(ps1); + } + break; + + case EQUAL: + ps1 = ps; + DEC(ps); + if (isDouble(ps)) { + toDouble(ps1); + ps->d = ps->d == ps1->d; + } else if (isDouble(ps1)) { + to_double(ps); + ps->d = ps->d == ps1->d; + } else if ((isString(ps)) && (isString(ps1))) { + /* compare ps->s to ps1->s */ + ps->d = (double)(strcmp(ps->s, ps1->s) == 0); + free(ps->s); + ps->s = NULL; + cleanStringElement(ps1); + } + break; + + case RIGHT_SHIFT: + ps1 = ps; + DEC(ps); + toDouble(ps1); + toDouble(ps); + ps->d = (int)(ps->d) >> (int)(ps1->d); + break; + + case LEFT_SHIFT: + ps1 = ps; + DEC(ps); + toDouble(ps1); + toDouble(ps); + ps->d = (int)(ps->d) << (int)(ps1->d); + break; + + case MAX_VAL: + ps1 = ps; + DEC(ps); + if (isDouble(ps)) { + toDouble(ps1); + if (ps->d < ps1->d) ps->d = ps1->d; + } else if (isDouble(ps1)) { + to_double(ps); + if (ps->d < ps1->d) ps->d = ps1->d; + } else { + /* compare ps->s to ps1->s */ + if (strcmp(ps->s, ps1->s) < 0) { + s = ps->s; + ps->s = ps1->s; + ps1->s = s; + } + cleanStringElement(ps1); + } + break; + + case MIN_VAL: + ps1 = ps; + DEC(ps); + if (isDouble(ps)) { + toDouble(ps1); + if (ps->d > ps1->d) ps->d = ps1->d; + } else if (isDouble(ps1)) { + to_double(ps); + if (ps->d > ps1->d) ps->d = ps1->d; + } else { + /* compare ps->s to ps1->s */ + if (strcmp(ps->s, ps1->s) > 0) { + s = ps->s; + ps->s = ps1->s; + ps1->s = s; + } + cleanStringElement(ps1); + } + break; + + case ACOS: + toDouble(ps); + ps->d = acos(ps->d); + break; + + case ASIN: + toDouble(ps); + ps->d = asin(ps->d); + break; + + case ATAN: + toDouble(ps); + ps->d = atan(ps->d); + break; + + case ATAN2: + ps1 = ps; + DEC(ps); + toDouble(ps1); + toDouble(ps); + ps->d = atan2(ps1->d, ps->d); + break; + + case COS: + toDouble(ps); + ps->d = cos(ps->d); + break; + + case SIN: + toDouble(ps); + ps->d = sin(ps->d); + break; + + case TAN: + toDouble(ps); + ps->d = tan(ps->d); + break; + + case COSH: + toDouble(ps); + ps->d = cosh(ps->d); + break; + + case SINH: + toDouble(ps); + ps->d = sinh(ps->d); + break; + + case TANH: + toDouble(ps); + ps->d = tanh(ps->d); + break; + + case CEIL: + toDouble(ps); + ps->d = ceil(ps->d); + break; + + case FLOOR: + toDouble(ps); + ps->d = floor(ps->d); + break; + + case NINT: + if (isDouble(ps)) { + d = ps->d; + ps->d = (double)(long)(d >= 0 ? d+0.5 : d-0.5); + } else { + /* hunt down integer and convert */ + s = strpbrk(ps->s,"0123456789"); + if ((s > ps->s) && (s[-1] == '.')) s--; + if ((s > ps->s) && (s[-1] == '-')) s--; + d = s ? atof(s) : 0.0; + free(ps->s); ps->s = NULL; + ps->d = (double)(long)(d >= 0 ? d+0.5 : d-0.5); + } + break; + + case REL_NOT: + toDouble(ps); + ps->d = (ps->d ? 0 : 1); + break; + + case BIT_NOT: + toDouble(ps); + ps->d = ~(int)(ps->d); + break; + + case LITERAL: + INC(ps); + ++post; + if (post == NULL) { + ++post; + printf("%.7s bad constant in expression\n",post); + ps->s = NULL; + ps->d = 0.; + break; + } + memcpy((void *)&(ps->d),post,8); + ps->s = NULL; + post += 7; + break; + + case SLITERAL: + INC(ps); + ++post; + if (post == NULL) { + ++post; + printf("%.7s bad constant in expression\n",post); + ps->s = NULL; + ps->d = 0.; + break; + } + ps->s = calloc(strlen(post)+1, 1); + strcpy(ps->s, post); + /* skip to end of string */ + while (*post) post++; + break; + + case TO_DOUBLE: + if (isString(ps)) { + /* hunt down number and convert */ + s = strpbrk(ps->s,"0123456789"); + if ((s > ps->s) && (s[-1] == '.')) s--; + if ((s > ps->s) && (s[-1] == '-')) s--; + ps->d = s ? atof(s) : 0.0; + free(ps->s); ps->s = NULL; + } + break; + + case TO_STRING: + toString(ps); + break; + + case PRINTF: + ps1 = ps; + DEC(ps); + if (isDouble(ps)) + {cleanup(top, ps1); return(-1);} + s = ps->s; + while ((s1 = strstr(s, "%%"))) {s = s1+2;} + if (((s = strpbrk(s, "%")) == NULL) || + ((s = strpbrk(s+1, "*cdeEfgGiousxX")) == NULL)) { + /* no printf arguments needed */ + sprintf(tmpstr, ps->s); + } else { + switch (*s) { + default: case '*': + cleanup(top, ps1); + return(-1); + break; + case 'c': case 'd': case 'i': case 'o': + case 'u': case 'x': case 'X': + toDouble(ps1); + l = myNINT(ps1->d); + sprintf(tmpstr, ps->s, l); + break; + case 'e': case 'E': case 'f': case 'g': case 'G': + toDouble(ps1); + sprintf(tmpstr, ps->s, ps1->d); + break; + case 's': + toString(ps1); + sprintf(tmpstr, ps->s, ps1->s); + break; + } + } + if (strlen(ps->s) < strlen(tmpstr)) { + free(ps->s); + ps->s = calloc(strlen(tmpstr)+1, 1); + } + strcpy(ps->s, tmpstr); + cleanStackElement(ps1); + break; + + case SSCANF: + ps1 = ps; + DEC(ps); + if (isDouble(ps) || isDouble(ps1)) + {cleanup(top, ps1); return(-1);} + s = findConversionIndicator(ps1->s); + if (s == NULL) {cleanup(top, ps1); return(-1);} + switch (*s) { + default: case 'p': case 'w': case 'n': case '$': + cleanup(top, ps1); + return(-1); + break; + case 'd': case 'i': + if (s[-1] == 'h') { + sscanf(ps->s, ps1->s, &h); + ps->d = (double)h; + } else { + sscanf(ps->s, ps1->s, &l); + ps->d = (double)l; + } + cleanStringElement(ps); + ps->s = NULL; + break; + case 'o': case 'u': case 'x': case 'X': + if (s[-1] == 'h') { + sscanf(ps->s, ps1->s, &ui); + ps->d = (double)ui; + } else { + sscanf(ps->s, ps1->s, &ul); + ps->d = (double)ul; + } + cleanStringElement(ps); + ps->s = NULL; + break; + case 'e': case 'E': case 'f': case 'g': case 'G': + if (s[-1] == 'l') { + sscanf(ps->s, ps1->s, &(ps->d)); + } else { + sscanf(ps->s, ps1->s, &f); + ps->d = (double)f; + } + cleanStringElement(ps); + ps->s = NULL; + break; + case 'c': case '[': case 's': + sscanf(ps->s, ps1->s, tmpstr); + if (strlen(ps->s) < strlen(tmpstr)) { + free(ps->s); + ps->s = calloc(strlen(tmpstr)+1, 1); + } + strcpy(ps->s, tmpstr); + break; + } + cleanStringElement(ps1); + break; + + case SUBRANGE: + ps2 = ps; + DEC(ps); + ps1 = ps; + DEC(ps); + toString(ps); + k = strlen(ps->s); + if (isDouble(ps1)) { + i = ps1->d; + if (i < 0) i += k; + } else { + s = strstr(ps->s, ps1->s); + i = s ? (s - ps->s) + strlen(ps1->s) : 0; + } + if (isDouble(ps2)) { + j = ps2->d; + if (j < 0) j += k; + } else { + if (*(ps2->s)) { + s = strstr(ps->s, ps2->s); + j = s ? (s - ps->s) - 1 : k; + } else { + j = k; + } + } + i = MAX(MIN(i,k),0); + j = MIN(j,k); + for (s=ps->s, s1=s+i, s2=s+j ; *s1 && s1 <= s2; ) + {*s++ = *s1++;} + *s = 0; + break; + + case REPLACE: + ps2 = ps; + DEC(ps); + ps1 = ps; + DEC(ps); + toString(ps); + toString(ps1); + toString(ps2); + i = strlen(ps->s); + j = strlen(ps1->s); + k = strlen(ps2->s); + s1 = strstr(ps->s, ps1->s); + s2 = ps2->s; + if (s1 >= ps->s) { + char *s_old, *s_dest; + s_old = s = ps->s; + if (k > j) ps->s = malloc(i - j + k + 1); + s_dest = ps->s; + while (s < s1) *s_dest++ = *s++; + s += j; + while (*s2) *s_dest++ = *s2++; + while (*s) *s_dest++ = *s++; + *s_dest = '\0'; + if (k > j) free(s_old); + } + cleanStringElement(ps1); + cleanStringElement(ps2); + break; + + default: + break; + } + + /* move ahead in postfix expression */ + ++post; + } + + /* if everything is peachy,the stack should end at its first position */ + if (ps != top) + return(-1); + + if (isDouble(ps)) { + if (presult) *presult = ps->d; + if (psresult) { + toString(ps); + for (i=0, s=ps->s, s1=psresult; *s && is, lenSresult); */ + psresult[lenSresult-1] = 0; + cleanStringElement(ps); + } + } else { + if (psresult) { + for (i=0, s=ps->s, s1=psresult; *s && id; + } else { + cleanStringElement(ps); + } + } + + } /* if (*post++ != USES_STRING) {} else */ + + return(0); +} + + +/* + * RAND + * + * generates a random number between 0 and 1 using the + * seed = (multy * seed) + addy Random Number Generator by Knuth + * SemiNumerical Algorithms + * Chapter 1 + * randy = seed / 65535.0 To normalize the number between 0 - 1 + */ +static unsigned short seed = 0xa3bf; +static unsigned short multy = 191 * 8 + 5; /* 191 % 8 == 5 */ +static unsigned short addy = 0x3141; +static double local_random() +{ + double randy; + + /* random number */ + seed = (seed * multy) + addy; + randy = (float) seed / 65535.0; + + /* between 0 - 1 */ + return(randy); +} diff --git a/src/libCom/calc/sCalcPostfix.c b/src/libCom/calc/sCalcPostfix.c new file mode 100644 index 000000000..385c6f04d --- /dev/null +++ b/src/libCom/calc/sCalcPostfix.c @@ -0,0 +1,720 @@ +/* $Id$ + * Subroutines used to convert an infix expression to a postfix expression + * + * Author: Bob Dalesio + * Date: 12-12-86 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * .01 01-11-89 lrd added right shift and left shift operations + * .02 01-13-89 lrd modified to load into IOCs + * .03 02-01-89 lrd added trigonometric functions + * .04 04-05-89 lrd fixed the order of some operations in the + * element table and added a warning label + * .05 11-26-90 lrd fix SINH, COSH, TANH + * .06 02-20-92 rcz fixed for vxWorks build + * .07 02-24-92 jba add EXP and fixed trailing blanks in expression + * .08 03-03-92 jba added MAX_VAL and MIN_VAL and comma(like close paren) + * .09 03-06-92 jba added multiple conditional expressions ? + * .10 04-01-92 jba allowed floating pt constants in expression + * .11 05-01-92 jba flt pt constant string replaced with double in postfix + * .12 08-21-92 jba ANSI c changes + * .13 08-21-92 jba initialized *ppostfix: needed when calc expr not defined + * .14 12-11-92 mrk Removed include for stdioLib.h + * .15 11-03-93 jba Added test for extra close paren at end of expression + * .16 01-24-94 jba Changed seperator test to catch invalid commas + * .17 05-11-94 jba Added support for CONST_PI, CONST_R2D, and CONST_D2R + * and conversion of infix expression to uppercase + * .18 01-22-98 tmm Changed name postfix() to calcPostfix(). Changed arg list + * from pointer to postfix expression to address of pointer to + * postfix expression. calcPostfix() frees old expression, allocates + * space sufficient to hold new expression, and returns pointer to it. + * Added S2R, R2S (conversions between arc-seconds and radians). Changed + * CONSTANT to LITERAL to avoid conflict with link.h. Support 26 vars (A-Z). + * .19 03-18-98 tmm Added string operators + */ + +/* + * Subroutines + * + * Public + * + * sCalcPostfix convert an algebraic expression to symbolic postfix + * args + * pinfix the algebraic expression + * pp_postfix address of the symbolic postfix expression + * perror error information + * returns + * 0 successful + * -1 not successful + * Private routines for calcPostfix + * + * find_element finds a symbolic element in the expression element tbl + * args + * pbuffer pointer to the infix expression element + * pelement pointer to the expression element table entry + * pno_bytes pointer to the size of this element + * parg pointer to arg (used for fetch) + * returns + * TRUE element found + * FALSE element not found + * + * get_element finds the next expression element in the infix expr + * args + * pinfix pointer into the infix expression + * pelement pointer to the expression element table + * pno_bytes size of the element in the infix expression + * parg pointer to argument (used for fetch) + * returns + * FINE found an expression element + * VARIABLE found a database reference + * UNKNOWN_ELEMENT unknown element found in the infix expression + */ + +#ifdef vxWorks +#include +#endif + +#include +#include +#include +#include + +#include "dbDefs.h" +#define epicsExportSharedSymbols +#include "sCalcPostfix.h" +#include "sCalcPostfixPvt.h" + + +#define DEBUG 0 +volatile int sCalcPostfixDebug=0; + +/* declarations for postfix */ +/* element types */ +#define OPERAND 0 +#define UNARY_OPERATOR 1 +#define BINARY_OPERATOR 2 +#define EXPR_TERM 3 +#define COND 4 +#define CLOSE_PAREN 5 +#define CONDITIONAL 6 +#define ELSE 7 +#define SEPARATOR 8 +#define TRASH 9 +#define FLOAT_PT_CONST 10 +#define MINUS_OPERATOR 11 +#define STRING_CONST 12 +#define CLOSE_BRACKET 13 +#define CLOSE_CURLY 14 + +#define UNARY_MINUS_I_S_P 7 +#define UNARY_MINUS_I_C_P 8 +#define UNARY_MINUS_CODE UNARY_NEG +#define BINARY_MINUS_I_S_P 4 +#define BINARY_MINUS_I_C_P 4 +#define BINARY_MINUS_CODE SUB + +/* parsing return values */ +#define FINE 0 +#define UNKNOWN_ELEMENT -1 +#define END -2 + +/* + * element table + * + * structure of an element + */ +struct expression_element{ + char element[10]; /* character representation of an element */ + char in_stack_pri; /* priority in translation stack */ + char in_coming_pri; /* priority when first checking */ + char type; /* element type */ + char code; /* postfix representation */ +}; + +/* + * NOTE: DO NOT CHANGE WITHOUT READING THIS NOTICE !!!!!!!!!!!!!!!!!!!! + * Because the routine that looks for a match in this table takes the first + * match it finds, elements whose designations are contained in other elements + * MUST come first in this list. (e.g. ABS will match A if A preceeds ABS and + * then try to find BS therefore ABS must be first in this list + */ +static struct expression_element elements[] = { +/* +element i_s_p i_c_p type_element internal_rep */ +"ABS", 7, 8, UNARY_OPERATOR, ABS_VAL, /* absolute value */ +"NOT", 7, 8, UNARY_OPERATOR, UNARY_NEG, /* unary negate */ +"-", 7, 8, MINUS_OPERATOR, UNARY_NEG, /* unary negate (or binary op) */ +"SQRT", 7, 8, UNARY_OPERATOR, SQU_RT, /* square root */ +"SQR", 7, 8, UNARY_OPERATOR, SQU_RT, /* square root */ +"EXP", 7, 8, UNARY_OPERATOR, EXP, /* exponential function */ +"LOGE", 7, 8, UNARY_OPERATOR, LOG_E, /* log E */ +"LN", 7, 8, UNARY_OPERATOR, LOG_E, /* log E */ +"LOG", 7, 8, UNARY_OPERATOR, LOG_10, /* log 10 */ +"ACOS", 7, 8, UNARY_OPERATOR, ACOS, /* arc cosine */ +"ASIN", 7, 8, UNARY_OPERATOR, ASIN, /* arc sine */ +"ATAN2", 7, 8, UNARY_OPERATOR, ATAN2, /* arc tangent */ +"ATAN", 7, 8, UNARY_OPERATOR, ATAN, /* arc tangent */ +"MAX", 7, 8, UNARY_OPERATOR, MAX_VAL, /* maximum of 2 args */ +"MIN", 7, 8, UNARY_OPERATOR, MIN_VAL, /* minimum of 2 args */ +"CEIL", 7, 8, UNARY_OPERATOR, CEIL, /* smallest integer >= */ +"FLOOR", 7, 8, UNARY_OPERATOR, FLOOR, /* largest integer <= */ +"NINT", 7, 8, UNARY_OPERATOR, NINT, /* nearest integer */ +"INT", 7, 8, UNARY_OPERATOR, NINT, /* nearest integer */ +"COSH", 7, 8, UNARY_OPERATOR, COSH, /* hyperbolic cosine */ +"COS", 7, 8, UNARY_OPERATOR, COS, /* cosine */ +"SINH", 7, 8, UNARY_OPERATOR, SINH, /* hyperbolic sine */ +"SIN", 7, 8, UNARY_OPERATOR, SIN, /* sine */ +"TANH", 7, 8, UNARY_OPERATOR, TANH, /* hyperbolic tangent*/ +"TAN", 7, 8, UNARY_OPERATOR, TAN, /* tangent */ +"!=", 3, 3, BINARY_OPERATOR,NOT_EQ, /* not equal */ +"!", 7, 8, UNARY_OPERATOR, REL_NOT, /* not */ +"~", 7, 8, UNARY_OPERATOR, BIT_NOT, /* bitwise not */ +"DBL", 7, 8, UNARY_OPERATOR, TO_DOUBLE, /* convert to double */ +"STR", 7, 8, UNARY_OPERATOR, TO_STRING, /* convert to string */ +"$P", 7, 8, UNARY_OPERATOR, PRINTF, /* formatted print to string */ +"PRINTF", 7, 8, UNARY_OPERATOR, PRINTF, /* formatted print to string */ +"$S", 7, 8, UNARY_OPERATOR, SSCANF, /* scan string argument */ +"SSCANF", 7, 8, UNARY_OPERATOR, SSCANF, /* scan string argument */ +"RNDM", 0, 0, OPERAND, RANDOM, /* Random Number */ +"OR", 1, 1, BINARY_OPERATOR,BIT_OR, /* or */ +"AND", 2, 2, BINARY_OPERATOR,BIT_AND, /* and */ +"XOR", 1, 1, BINARY_OPERATOR,BIT_EXCL_OR, /* exclusive or */ +"PI", 0, 0, OPERAND, CONST_PI, /* pi */ +"D2R", 0, 0, OPERAND, CONST_D2R, /* pi/180 */ +"R2D", 0, 0, OPERAND, CONST_R2D, /* 180/pi */ +"S2R", 0, 0, OPERAND, CONST_S2R, /* arc-sec to radians: pi/(180*3600) */ +"R2S", 0, 0, OPERAND, CONST_R2S, /* radians to arc-sec: (180*3600)/pi */ +"0", 0, 0, FLOAT_PT_CONST, LITERAL, /* flt pt constant */ +"1", 0, 0, FLOAT_PT_CONST, LITERAL, /* flt pt constant */ +"2", 0, 0, FLOAT_PT_CONST, LITERAL, /* flt pt constant */ +"3", 0, 0, FLOAT_PT_CONST, LITERAL, /* flt pt constant */ +"4", 0, 0, FLOAT_PT_CONST, LITERAL, /* flt pt constant */ +"5", 0, 0, FLOAT_PT_CONST, LITERAL, /* flt pt constant */ +"6", 0, 0, FLOAT_PT_CONST, LITERAL, /* flt pt constant */ +"7", 0, 0, FLOAT_PT_CONST, LITERAL, /* flt pt constant */ +"8", 0, 0, FLOAT_PT_CONST, LITERAL, /* flt pt constant */ +"9", 0, 0, FLOAT_PT_CONST, LITERAL, /* flt pt constant */ +".", 0, 0, FLOAT_PT_CONST, LITERAL, /* flt pt constant */ +"\"", 0, 0, STRING_CONST, SLITERAL, /* string constant */ +"'", 0, 0, STRING_CONST, SLITERAL, /* string constant */ +"?", 0, 0, CONDITIONAL, COND_IF, /* conditional */ +":", 0, 0, CONDITIONAL, COND_ELSE, /* else */ +"(", 0, 8, UNARY_OPERATOR, PAREN, /* open paren */ +"[", 0, 8, BINARY_OPERATOR,SUBRANGE, /* string subrange */ +"{", 0, 8, BINARY_OPERATOR,REPLACE, /* string replace */ +"^", 6, 6, BINARY_OPERATOR,EXPON, /* exponentiation */ +"**", 6, 6, BINARY_OPERATOR,EXPON, /* exponentiation */ +"+", 4, 4, BINARY_OPERATOR,ADD, /* addition */ +#if 0 /* "-" operator is overloaded; may be unary or binary */ +"-", 4, 4, BINARY_OPERATOR,SUB, /* subtraction */ +#endif +"*", 5, 5, BINARY_OPERATOR,MULT, /* multiplication */ +"/", 5, 5, BINARY_OPERATOR,DIV, /* division */ +"%", 5, 5, BINARY_OPERATOR,MODULO, /* modulo */ +",", 0, 0, SEPARATOR, COMMA, /* comma */ +")", 0, 0, CLOSE_PAREN, PAREN, /* close paren */ +"]", 0, 0, CLOSE_BRACKET, SUBRANGE, /* close bracket */ +"}", 0, 0, CLOSE_CURLY, REPLACE, /* close curly bracket */ +"||", 1, 1, BINARY_OPERATOR,REL_OR, /* logical or */ +"|", 1, 1, BINARY_OPERATOR,BIT_OR, /* bitwise or */ +"&&", 2, 2, BINARY_OPERATOR,REL_AND, /* logical and */ +"&", 2, 2, BINARY_OPERATOR,BIT_AND, /* bitwise and */ +">>", 2, 2, BINARY_OPERATOR,RIGHT_SHIFT, /* right shift */ +">=", 3, 3, BINARY_OPERATOR,GR_OR_EQ, /* greater or equal*/ +">", 3, 3, BINARY_OPERATOR,GR_THAN, /* greater than */ +"<<", 2, 2, BINARY_OPERATOR,LEFT_SHIFT, /* left shift */ +"<=", 3, 3, BINARY_OPERATOR,LESS_OR_EQ,/* less or equal to*/ +"<", 3, 3, BINARY_OPERATOR,LESS_THAN, /* less than */ +"#", 3, 3, BINARY_OPERATOR,NOT_EQ, /* not equal */ +"==", 3, 3, BINARY_OPERATOR,EQUAL, /* equal */ +"=", 3, 3, BINARY_OPERATOR,EQUAL, /* equal */ +"" +}; + +/* + * Element-table entry for "fetch" operation. This element is used for all + * named variables. Currently, letters A-Z (double) and AA-ZZ (string) are + * allowed. Lower and upper case letters mean the same thing. + */ +static struct expression_element fetch_element = { +"A", 0, 0, OPERAND, FETCH, /* fetch var */ +}; + +static struct expression_element fetch_string_element = { +"AA", 0, 0, OPERAND, SFETCH, /* fetch var */ +}; + +static int strncasecmp(char *s1, char *s2, size_t n) +{ + short i; + for (i=0; i toupper((int)*s2)) return(1); + if (toupper((int)*s1) < toupper((int)*s2)) return(-1); + } + return(0); +} +/* + * FIND_ELEMENT + * + * find the pointer to an entry in the element table + */ +static int find_element(pbuffer, pelement, pno_bytes, parg) + register char *pbuffer; + register struct expression_element **pelement; + register short *pno_bytes, *parg; + { + *parg = 0; + + /* compare the string to each element in the element table */ + *pelement = &elements[0]; + while ((*pelement)->element[0] != NULL){ + if (strncasecmp(pbuffer,(*pelement)->element, strlen((*pelement)->element)) == 0){ + *pno_bytes += strlen((*pelement)->element); + return(TRUE); + } + *pelement += 1; + } + + /* look for a variable reference */ + /* double variables: ["a" - "z"], numbered 1-26 */ + if (isalpha((int)*pbuffer)) { + *pelement = &fetch_element; /* fetch means "variable reference" (fetch or store) */ + *parg = *pbuffer - (isupper((int)*pbuffer) ? 'A' : 'a'); + *pno_bytes += 1; + /* string variables: ["aa" - "zz"], numbered 1-26 */ + if (pbuffer[1] == pbuffer[0]) { + *pelement = &fetch_string_element; + *pno_bytes += 1; + } + return(TRUE); + } + if (sCalcPostfixDebug) printf("find_element: can't find '%s'\n", pbuffer); + return(FALSE); + } + +/* + * GET_ELEMENT + * + * get an expression element + */ +static int get_element(pinfix, pelement, pno_bytes, parg) +register char *pinfix; +register struct expression_element **pelement; +register short *pno_bytes, *parg; +{ + + /* get the next expression element from the infix expression */ + if (*pinfix == NULL) return(END); + *pno_bytes = 0; + while (*pinfix == 0x20){ + *pno_bytes += 1; + pinfix++; + } + if (*pinfix == NULL) return(END); + if (!find_element(pinfix, pelement, pno_bytes, parg)) + return(UNKNOWN_ELEMENT); + if (sCalcPostfixDebug > 5) printf("get_element: found element '%s', arg=%d\n", (*pelement)->element, *parg); + + return(FINE); + + +} + +#if DEBUG +/* Override standard EPICS expression evaluator (if we're loaded after it). */ +long epicsShareAPI postfix(char *pinfix,char *ppostfix,short *perror) +{ + char *my_ppostfix = NULL, *s, *d; + long retval; + + retval = sCalcPostfix(pinfix, &my_ppostfix, perror); + if (*my_ppostfix == BAD_EXPRESSION) { + *ppostfix = BAD_EXPRESSION; + } else { + for (s = my_ppostfix, d = ppostfix; *s != END_STACK; ) { + *d++=*s++; + } + *d = *s; + } + free(my_ppostfix); + return(retval); +} +#endif + +/* + * sCalcPostFix + * + * convert an infix expression to a postfix expression + */ +long epicsShareAPI sCalcPostfix(char *pinfix, char **pp_postfix, short *perror) +{ + short no_bytes; + register short operand_needed; + register short new_expression; + struct expression_element stack[80]; + struct expression_element *pelement; + register struct expression_element *pstacktop; + double constant; + register char c, *pposthold; + char in_stack_pri, in_coming_pri, code; + char *ppostfix, *ppostfixStart; + short arg; + + if (sCalcPostfixDebug) printf("sCalcPostfix: entry\n"); + + /* Allocate a buffer for the postfix expression. */ + if (*pp_postfix) free(*pp_postfix); /* Free old buffer. */ + ppostfix = calloc(5*strlen(pinfix)+7, 1); + *pp_postfix = ppostfix; + ppostfixStart = ppostfix++; + *ppostfixStart = BAD_EXPRESSION; + *ppostfix = END_STACK; + + /* place the expression elements into postfix */ + operand_needed = TRUE; + new_expression = TRUE; + *perror = 0; + if (*pinfix == 0) { + return(0); + } + pstacktop = &stack[0]; + while (get_element(pinfix, &pelement, &no_bytes, &arg) != END){ + pinfix += no_bytes; + code = pelement->code; + if ((*ppostfixStart != USES_STRING) && ((code == TO_STRING) || + (code == PRINTF) || (code == SSCANF) || (code == SLITERAL) || + (code == SUBRANGE) || (code == SFETCH))) { + *ppostfixStart = USES_STRING; + } + + switch (pelement->type){ + + case OPERAND: + if (!operand_needed){ + *perror = 5; + *ppostfixStart = BAD_EXPRESSION; return(-1); + } + + /* add operand to the expression */ + *ppostfix++ = pelement->code; + + /* if this is a variable reference, append variable number */ + if ((pelement->code == FETCH) || (pelement->code == SFETCH)) { + *ppostfix++ = arg; + } + + operand_needed = FALSE; + new_expression = FALSE; + break; + + case FLOAT_PT_CONST: + if (!operand_needed){ + *perror = 5; + *ppostfixStart = BAD_EXPRESSION; return(-1); + } + + /* add constant to postfix expression */ + *ppostfix++ = pelement->code; + pposthold = ppostfix; + + pinfix-=no_bytes; + while (*pinfix == ' ') *ppostfix++ = *pinfix++; + while (TRUE) { + if ( ( *pinfix >= '0' && *pinfix <= '9' ) || *pinfix == '.' ) { + *ppostfix++ = *pinfix; + pinfix++; + } else if ( *pinfix == 'E' || *pinfix == 'e' ) { + *ppostfix++ = *pinfix; + pinfix++; + if (*pinfix == '+' || *pinfix == '-' ) { + *ppostfix++ = *pinfix; + pinfix++; + } + } else break; + } + *ppostfix++ = '\0'; + + ppostfix = pposthold; + if ( sscanf(ppostfix,"%lg",&constant) != 1) { + *ppostfix = '\0'; + } else { + memcpy(ppostfix,(void *)&constant,8); + } + ppostfix+=8; + + operand_needed = FALSE; + new_expression = FALSE; + break; + + case STRING_CONST: + if (!operand_needed){ + *perror = 5; + *ppostfixStart = BAD_EXPRESSION; return(-1); + } + + /* add string literal to the postfix expression */ + *ppostfix++ = pelement->code; + c = pinfix[-1]; + while (*pinfix != c && *pinfix) *ppostfix++ = *pinfix++; + *ppostfix++ = '\0'; + if (*pinfix) pinfix++; + + operand_needed = FALSE; + new_expression = FALSE; + break; + + case BINARY_OPERATOR: + if (operand_needed){ + *perror = 4; + *ppostfixStart = BAD_EXPRESSION; return(-1); + } + + /* add operators of higher or equal priority to postfix expression */ + while ((pstacktop->in_stack_pri >= pelement->in_coming_pri) + && (pstacktop >= &stack[1])){ + *ppostfix++ = pstacktop->code; + pstacktop--; + } + + /* add new operator to stack */ + pstacktop++; + *pstacktop = *pelement; + + operand_needed = TRUE; + break; + + case UNARY_OPERATOR: + if (!operand_needed){ + *perror = 5; + *ppostfixStart = BAD_EXPRESSION; return(-1); + } + + /* add operators of higher or equal priority to postfix expression */ + while ((pstacktop->in_stack_pri >= pelement->in_coming_pri) + && (pstacktop >= &stack[1])){ + *ppostfix++ = pstacktop->code; + pstacktop--; + } + + /* add new operator to stack */ + pstacktop++; + *pstacktop = *pelement; + + new_expression = FALSE; + break; + + case MINUS_OPERATOR: + if (operand_needed) { + /* then assume minus was intended as a unary operator */ + in_coming_pri = UNARY_MINUS_I_C_P; + in_stack_pri = UNARY_MINUS_I_S_P; + code = UNARY_MINUS_CODE; + new_expression = FALSE; + } else { + /* then assume minus was intended as a binary operator */ + in_coming_pri = BINARY_MINUS_I_C_P; + in_stack_pri = BINARY_MINUS_I_S_P; + code = BINARY_MINUS_CODE; + operand_needed = TRUE; + } + + /* add operators of higher or equal priority to postfix expression */ + while ((pstacktop->in_stack_pri >= in_coming_pri) + && (pstacktop >= &stack[1])){ + *ppostfix++ = pstacktop->code; + pstacktop--; + } + + /* add new operator to stack */ + pstacktop++; + *pstacktop = *pelement; + pstacktop->in_stack_pri = in_stack_pri; + pstacktop->code = code; + + break; + + case SEPARATOR: + if (operand_needed){ + *perror = 4; + *ppostfixStart = BAD_EXPRESSION; return(-1); + } + + /* add operators to postfix until open paren */ + while ((pstacktop->element[0] != '(') && (pstacktop->element[0] != '[') + && (pstacktop->element[0] != '{')) { + if (pstacktop == &stack[1] || pstacktop == &stack[0]){ + *perror = 6; + *ppostfixStart = BAD_EXPRESSION; return(-1); + } + *ppostfix++ = pstacktop->code; + pstacktop--; + } + operand_needed = TRUE; + break; + + case CLOSE_PAREN: + if (operand_needed){ + *perror = 4; + *ppostfixStart = BAD_EXPRESSION; return(-1); + } + + /* add operators to postfix until matching paren */ + while (pstacktop->element[0] != '(') { + if (pstacktop == &stack[1] || pstacktop == &stack[0]) { + *perror = 6; + *ppostfixStart = BAD_EXPRESSION; return(-1); + } + *ppostfix++ = pstacktop->code; + pstacktop--; + } + pstacktop--; /* remove ( from stack */ + break; + + case CLOSE_BRACKET: + if (operand_needed){ + *perror = 4; + *ppostfixStart = BAD_EXPRESSION; return(-1); + } + + /* add operators to postfix until matching bracket */ + while (pstacktop->element[0] != '[') { + if (pstacktop == &stack[1] || pstacktop == &stack[0]) { + *perror = 6; + *ppostfixStart = BAD_EXPRESSION; return(-1); + } + *ppostfix++ = pstacktop->code; + pstacktop--; + } + /* add SUBRANGE operator to postfix */ + if (pstacktop == &stack[0]) { + *perror = 6; + *ppostfixStart = BAD_EXPRESSION; return(-1); + } + *ppostfix++ = pstacktop->code; + pstacktop--; + break; + + case CLOSE_CURLY: + if (operand_needed){ + *perror = 4; + *ppostfixStart = BAD_EXPRESSION; return(-1); + } + + /* add operators to postfix until matching bracket */ + while (pstacktop->element[0] != '{') { + if (pstacktop == &stack[1] || pstacktop == &stack[0]) { + *perror = 6; + *ppostfixStart = BAD_EXPRESSION; return(-1); + } + *ppostfix++ = pstacktop->code; + pstacktop--; + } + /* add REPLACE operator to postfix */ + if (pstacktop == &stack[0]) { + *perror = 6; + *ppostfixStart = BAD_EXPRESSION; return(-1); + } + *ppostfix++ = pstacktop->code; + pstacktop--; + break; + + case CONDITIONAL: + if (operand_needed){ + *perror = 4; + *ppostfixStart = BAD_EXPRESSION; return(-1); + } + + /* add operators of higher priority to postfix expression */ + while ((pstacktop->in_stack_pri > pelement->in_coming_pri) + && (pstacktop >= &stack[1])){ + *ppostfix++ = pstacktop->code; + pstacktop--; + } + + /* add new element to the postfix expression */ + *ppostfix++ = pelement->code; + + /* add : operator with COND_END code to stack */ + if (pelement->element[0] == ':'){ + pstacktop++; + *pstacktop = *pelement; + pstacktop->code = COND_END; + } + + operand_needed = TRUE; + break; + + case EXPR_TERM: + if (operand_needed && !new_expression){ + *perror = 4; + *ppostfixStart = BAD_EXPRESSION; return(-1); + } + + /* add all operators on stack to postfix */ + while (pstacktop >= &stack[1]){ + if (pstacktop->element[0] == '('){ + *perror = 6; + *ppostfixStart = BAD_EXPRESSION; return(-1); + } + *ppostfix++ = pstacktop->code; + pstacktop--; + } + + /* add new element to the postfix expression */ + *ppostfix++ = pelement->code; + + operand_needed = TRUE; + new_expression = TRUE; + break; + + default: + *perror = 8; + *ppostfixStart = BAD_EXPRESSION; return(-1); + } + } + if (operand_needed){ + *perror = 4; + *ppostfixStart = BAD_EXPRESSION; return(-1); + } + + /* add all operators on stack to postfix */ + while (pstacktop >= &stack[1]){ + if (pstacktop->element[0] == '('){ + *perror = 6; + *ppostfixStart = BAD_EXPRESSION; return(-1); + } + *ppostfix++ = pstacktop->code; + pstacktop--; + } + *ppostfix++ = END_STACK; + *ppostfix = '\0'; + + if (ppostfixStart[1] == END_STACK) + *ppostfixStart = BAD_EXPRESSION; + else if (*ppostfixStart != USES_STRING) + *ppostfixStart = NO_STRING; + + return(0); +} diff --git a/src/libCom/calc/sCalcPostfix.h b/src/libCom/calc/sCalcPostfix.h new file mode 100644 index 000000000..d6b73c9a0 --- /dev/null +++ b/src/libCom/calc/sCalcPostfix.h @@ -0,0 +1,39 @@ +/* sCalcPostfix.h + * Author: Bob Dalesio + * Date: 9-21-88 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * .01 03-18-98 tmm derived from postfix.h + * + */ + +#ifndef INCpostfixh +#define INCpostfixh + +long epicsShareAPI sCalcPostfix(char *pinfix, char **pp_postfix, short *perror); +epicsShareFunc long epicsShareAPI + sCalcPerform(double *parg, int numArgs, char **psarg, int numSArgs, double *presult, char *psresult, int lenSresult, char *post); + +#endif /* INCpostfixh */ diff --git a/src/libCom/calc/sCalcPostfixPvt.h b/src/libCom/calc/sCalcPostfixPvt.h new file mode 100644 index 000000000..b66ef9330 --- /dev/null +++ b/src/libCom/calc/sCalcPostfixPvt.h @@ -0,0 +1,111 @@ +/* sCalcPostfixPvt.h + * Author: Bob Dalesio + * Date: 9-21-88 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * .01 03-18-98 tmm derived from postfix.h + * + */ + +#ifndef INCpostfixPvth +#define INCpostfixPvth + +/* defines for element table */ +#define BAD_EXPRESSION 0 +/* elements that define a value */ +#define FETCH 1 +#define SFETCH 2 +#define CONST_PI 3 +#define CONST_D2R 4 +#define CONST_R2D 5 +#define CONST_S2R 6 +#define CONST_R2S 7 +#define RANDOM 8 +#define LITERAL 9 +#define SLITERAL 10 +#define SSCANF 11 + +#define VALUE_ELEMENT 11 + +/* elements that operate on a value */ +#define ACOS 12 +#define ASIN 13 +#define ATAN 14 +#define COS 15 +#define COSH 16 +#define SIN 17 +#define RIGHT_SHIFT 18 +#define LEFT_SHIFT 19 +#define SINH 20 +#define TAN 21 +#define TANH 22 +#define LOG_2 23 +#define COND_ELSE 24 +#define ABS_VAL 25 +#define UNARY_NEG 26 +#define SQU_RT 27 +#define EXP 28 +#define CEIL 29 +#define FLOOR 30 +#define LOG_10 31 +#define LOG_E 32 +#define ADD 33 +#define SUB 34 +#define MULT 35 +#define DIV 36 +#define EXPON 37 +#define MODULO 38 +#define BIT_OR 39 +#define BIT_AND 40 +#define BIT_EXCL_OR 41 +#define GR_OR_EQ 42 +#define GR_THAN 43 +#define LESS_OR_EQ 44 +#define LESS_THAN 45 +#define NOT_EQ 46 +#define EQUAL 47 +#define REL_OR 48 +#define REL_AND 49 +#define REL_NOT 50 +#define BIT_NOT 51 +#define PAREN 52 +#define MAX_VAL 53 +#define MIN_VAL 54 +#define COMMA 55 +#define COND_IF 56 +#define COND_END 57 +#define NINT 58 +#define ATAN2 59 +#define STORE 60 +#define TO_DOUBLE 61 +#define PRINTF 62 +#define SUBRANGE 63 +#define TO_STRING 64 +#define REPLACE 65 +#define END_STACK 127 + +#define USES_STRING 126 +#define NO_STRING 125 +#endif /* INCpostfixPvth */ diff --git a/src/libCom/sCalcPerform.c b/src/libCom/sCalcPerform.c new file mode 100644 index 000000000..52efc76a9 --- /dev/null +++ b/src/libCom/sCalcPerform.c @@ -0,0 +1,1458 @@ +/* $Id$ */ +/* + * Author: Julie Sander and Bob Dalesio + * Date: 07-27-87 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * 03-18-98 tmm Essentially rewritten to support string operators + */ + +/* This module contains the code for processing the arithmetic + * expressions defined in calculation records. postfix must be called + * to convert a valid infix expression to postfix. sCalcPerform + * calculates the postfix expression. + * + * Subroutines + * + * Public + * + * sCalcPerform perform the calculation + * args + * double *parg address of arguments + * int numArgs number of arguments in pargs array + * double *psarg address of string arguments + * int numSArgs number of string arguments in psargs array + * double *presult address of double result + * char *psresult address of string-result buffer + * int lenSresult length of string-result buffer + * char *rpcl address of postfix buffer + * returns + * 0 fetched successfully + * -1 fetch failed + * + * Private routine for sCalcPerform + * local_random random number generator + * returns + * double value between 0.00 and 1.00 + */ + +#ifdef vxWorks +#include +#endif + +#include +#include +#include +#include + +#include "dbDefs.h" +#include "cvtFast.h" +#define epicsExportSharedSymbols +#include "sCalcPostfix.h" +#include "sCalcPostfixPvt.h" + +static double local_random(); + +static int isnan(double d) +{ + union { long l[2]; double d; } u; + u.d = d; + if ((u.l[0] & 0x7ff00000) != 0x7ff00000) return(0); + if (u.l[1] || (u.l[0] & 0x000fffff)) return(1); + return(0); +} +#define myNINT(a) ((int)((a) >= 0 ? (a)+0.5 : (a)-0.5)) +#ifndef PI +#define PI 3.141592654 +#endif +#define MAX(a,b) (a)>(b)?(a):(b) +#define MIN(a,b) (a)<(b)?(a):(b) + + +#define STACKSIZE 30 /* <-------------------------<<<<<<< */ + +struct stackElement { + double d; + char *s; +}; + +#define DEBUG 0 +volatile int sCalcPerformDebug = 0; +#if DEBUG +int sCalcStackHW = 0; +int sCalcStackLW = 0; +#define INC(ps) {if ((int)(++(ps)-top) > sCalcStackHW) sCalcStackHW = (int)((ps)-top);} +#define DEC(ps) {if ((int)(--(ps)-top) < sCalcStackLW) sCalcStackLW = (int)((ps)-top);} +#else +#define INC(ps) ++ps +#define DEC(ps) ps-- +#endif + + +#define isDouble(ps) ((ps)->s==NULL) +#define isString(ps) ((ps)->s) + +#define cleanStringElement(ps) {free((ps)->s); (ps)->s=NULL;} + +static void cleanStackElement(struct stackElement *ps) +{ + if (isString(ps)) { + free(ps->s); + ps->s = NULL; + } +} + +static void cleanup(struct stackElement *ps, struct stackElement *pe) +{ + for (; ps <= pe; ps++) { + if (isString(ps)) { + free(ps->s); + ps->s = NULL; + } + } +} + +#define toDouble(ps) {if (isString(ps)) to_double(ps);} + +/* convert stack element to double */ +static void to_double(struct stackElement *ps) +{ + ps->d = atof(ps->s); + free(ps->s); + ps->s = NULL; +} + +#define toString(ps) {if (isDouble(ps)) to_string(ps);} + +/* convert stack element to string */ +static void to_string(struct stackElement *ps) +{ + ps->s = calloc(20, 1); + /* any precision greater than 8 results in (slow) sprintf call */ + if (isnan(ps->d)) + strcpy(ps->s,"NaN"); + else + (void)cvtDoubleToString(ps->d, ps->s, 8); +} + +static char *findConversionIndicator(char *s) +{ + char *cc=NULL, *s1, *retval; + + while (s && *s) { + if ((s1 = strstr(s, "%%")) != NULL) { + /* not a conversion/assignment indicator; skip over */ + s = s1+2; continue; + } + if ((s = strchr(s, (int)'%')) == NULL) { + return(NULL); + } + if ((cc = strpbrk(s, "pwn$c[deEfgGiousxX")) == NULL) { + return(NULL); + } + /* + * (*cc) is a conversion character; look for suppressed assignment + * ('*' occurring after '%' and before conversion character) + */ + s1 = strchr(s, (int)'*'); + if (s1 && (s1 < cc)) { + /* suppressed assignment; skip past conversion character */ + s = cc+1; + if (*cc == '[') { + /* skip character set ([..], []..], or [^]..]) */ + if (cc[1] == ']') { + s = &(cc[2]); + } else if ((cc[1] == '^') && (cc[2] == ']')) { + s = &(cc[3]); + } + s = strchr(s, (int)']'); + if (s == NULL) { + /* bad character-set syntax */ + return(NULL); + } + s++; /* skip past ']' */ + } + /* keep looking for conversion/assignment character */ + + continue; + } else { + /* (*cc) is a conversion/assignment character */ + break; + } + } + if (cc == NULL) return(NULL); + retval = cc; + /* + * (*cc) is a conversion/assignment indicator. Make sure there + * aren't any more in the format string. + */ + s = cc+1; + while (s && *s) { + if ((s1 = strstr(s, "%%")) != NULL) { + /* not a conversion/assignment indicator; skip over */ + s = s1+2; continue; + } + if ((s = strchr(s, (int)'%')) == NULL) return(retval); + if ((cc = strpbrk(s, "pwn$c[deEfgGiousxX")) == NULL) return(retval); + /* + * (*cc) is a conversion character; look for suppressed assignment + * ('*' occurring after '%' and before conversion character) + */ + s1 = strchr(s, (int)'*'); + if (s1 && (s1 < cc)) { + /* suppressed assignment; skip past conversion character */ + s = cc+1; + if (*cc == '[') { + /* skip character set ([..], []..], or [^]..]) */ + if (cc[1] == ']') { + s = &(cc[2]); + } else if ((cc[1] == '^') && (cc[2] == ']')) { + s = &(cc[3]); + } + s = strchr(s, (int)']'); + if (s == NULL) return(NULL); /* bad character-set syntax */ + s++; /* skip past ']' */ + } + continue; + } else { + /* (*cc) assignment is not suppressed */ + return(NULL); + } + } + return(retval); +} + +#if DEBUG +/* Override standard EPICS expression evaluator (if we're loaded after it) */ +epicsShareFunc long epicsShareAPI + calcPerform(double *parg, double *presult, char *post) +{ + return(sCalcPerform(parg, 12, NULL, 0, presult, NULL, 0, post)); +} +#endif + +epicsShareFunc long epicsShareAPI + sCalcPerform(double *parg, int numArgs, char **psarg, int numSArgs, double *presult, char *psresult, int lenSresult, char *post) +{ + struct stackElement stack[STACKSIZE], *top; + register struct stackElement *ps, *ps1, *ps2; + char *s2, tmpstr[1000]; + register char *s, *s1; + register int i, j, k; + long l; + unsigned short ui; + unsigned long ul; + float f; + double d; + register double *topd, *pd; + short h, got_if; + +#if DEBUG + if (sCalcPerformDebug>=10) { + int more; + printf("sCalcPerform: postfix:"); + for (s=post, more=1; more;) { + printf("%2d ", *s); + switch (*s) { + case END_STACK: + more = 0; + break; + case LITERAL: + printf("(0x"); + for (i=0, s++; i<8; i++, s++) printf("%2x ", (unsigned int)(unsigned char)*s); + printf(") "); + break; + case SLITERAL: + s++; /* point past code */ + printf("'"); + while (*s) printf("%c", *s++); + printf("' "); + s++; + break; + case FETCH: + s++; /* point past code */ + printf("@%d ", *s++); + break; + case SFETCH: + s++; /* point past code */ + printf("$%d ", *s++); + break; + default: + if (*s == BAD_EXPRESSION) more=0; + s++; + break; + } + } + printf("\n"); + } +#endif + + /* Make sure postfix expression exists and is nontrivial */ + /* if ((*post == END_STACK) || (*post == BAD_EXPRESSION)) return(-1);*/ + if (*post == BAD_EXPRESSION) return(-1); + + + if (*post++ != USES_STRING) { + + topd = pd = (double *)&stack[10]; + pd--; + + /* No string expressions */ + while (*post != END_STACK) { + + switch (*post){ + + case FETCH: + ++pd; + ++post; + *pd = (*post < numArgs) ? parg[*post] : 0; + break; + + case STORE: + /* not implemented */ + return(-1); + + case CONST_PI: + ++pd; + *pd = PI; + break; + + case CONST_D2R: + ++pd; + *pd = PI/180.; + break; + + case CONST_R2D: + ++pd; + *pd = 180./PI; + break; + + case CONST_S2R: + ++pd; + *pd = PI/(180.*3600); + break; + + case CONST_R2S: + ++pd; + *pd = (180.*3600)/PI; + break; + + case ADD: + --pd; + *pd = *pd + pd[1]; + break; + + case SUB: + --pd; + *pd = *pd - pd[1]; + break; + + case MULT: + --pd; + *pd = *pd * pd[1]; + break; + + case DIV: + --pd; + if (pd[1] == 0) /* can't divide by zero */ + return(-1); + *pd = *pd / pd[1]; + break; + + case COND_IF: + /* if false condition then skip true expression */ + if (*pd == 0.0) { + /* skip to matching COND_ELSE */ + for (got_if=1; got_if>0 && *(post+1) != END_STACK; ++post) { + switch(post[1]) { + case LITERAL: post+=8; break; + case COND_IF: got_if++; break; + case COND_ELSE: got_if--; break; + case FETCH: case SFETCH: post++; break; + } + } + } + /* remove condition from stack top */ + --pd; + break; + + case COND_ELSE: + /* result, true condition is on stack so skip false condition */ + /* skip to matching COND_END */ + for (got_if=1; got_if>0 && *(post+1) != END_STACK; ++post) { + switch(post[1]) { + case LITERAL: post+=8; break; + case COND_IF: got_if++; break; + case COND_ELSE: got_if--; break; + case FETCH: post++; break; + } + } + break; + + case COND_END: + break; + + case ABS_VAL: + if (*pd < 0 ) *pd *= -1; + break; + + case UNARY_NEG: + *pd *= -1; + break; + + case SQU_RT: + /* check for neg number */ + if (*pd < 0) return(-1); + *pd = sqrt(*pd); + break; + + case EXP: + *pd = exp(*pd); + break; + + case LOG_10: + /* check for neg number */ + if (*pd < 0) return(-1); + *pd = log10(*pd); + break; + + case LOG_E: + /* check for neg number */ + if (*pd < 0) return(-1); + *pd = log(*pd); + break; + + case RANDOM: + ++pd; + *pd = local_random(); + break; + + case EXPON: + --pd; + if (*pd == 0) break; + if (*pd < 0) { + i = (int) pd[1]; + /* is exponent an integer? */ + if ((pd[1] - (double)i) != 0) return (-1); + *pd = exp(pd[1] * log(-(*pd))); + /* is value negative */ + if ((i % 2) > 0) *pd = -(*pd); + } else { + *pd = exp(pd[1] * log(*pd)); + } + break; + + case MODULO: + --pd; + if ((int)(pd[1]) == 0) + return(-1); + *pd = (double)((int)(*pd) % (int)(pd[1])); + break; + + case REL_OR: + --pd; + *pd = *pd || pd[1]; + break; + + case REL_AND: + --pd; + *pd = *pd && pd[1]; + break; + + case BIT_OR: + /* force double values into integers and or them */ + --pd; + *pd = (int)(pd[1]) | (int)(*pd); + break; + + case BIT_AND: + /* force double values into integers and and them */ + --pd; + *pd = (int)(pd[1]) & (int)(*pd); + break; + + case BIT_EXCL_OR: + /* force double values to integers to exclusive or them */ + --pd; + *pd = (int)(pd[1]) ^ (int)(*pd); + break; + + case GR_OR_EQ: + --pd; + *pd = *pd >= pd[1]; + break; + + case GR_THAN: + --pd; + *pd = *pd > pd[1]; + break; + + case LESS_OR_EQ: + --pd; + *pd = *pd <= pd[1]; + break; + + case LESS_THAN: + --pd; + *pd = *pd < pd[1]; + break; + + case NOT_EQ: + --pd; + *pd = *pd != pd[1]; + break; + + case EQUAL: + --pd; + *pd = *pd == pd[1]; + break; + + case RIGHT_SHIFT: + --pd; + *pd = (int)(*pd) >> (int)(pd[1]); + break; + + case LEFT_SHIFT: + --pd; + *pd = (int)(*pd) << (int)(pd[1]); + break; + + case MAX_VAL: + --pd; + if (*pd < pd[1]) *pd = pd[1]; + break; + + case MIN_VAL: + --pd; + if (*pd > pd[1]) *pd = pd[1]; + break; + + case ACOS: + *pd = acos(*pd); + break; + + case ASIN: + *pd = asin(*pd); + break; + + case ATAN: + *pd = atan(*pd); + break; + + case ATAN2: + --pd; + *pd = atan2(pd[1], *pd); + break; + + case COS: + *pd = cos(*pd); + break; + + case SIN: + *pd = sin(*pd); + break; + + case TAN: + *pd = tan(*pd); + break; + + case COSH: + *pd = cosh(*pd); + break; + + case SINH: + *pd = sinh(*pd); + break; + + case TANH: + *pd = tanh(*pd); + break; + + case CEIL: + *pd = ceil(*pd); + break; + + case FLOOR: + *pd = floor(*pd); + break; + + case NINT: + d = *pd; + *pd = (double)(long)(d >= 0 ? d+0.5 : d-0.5); + break; + + case REL_NOT: + *pd = (*pd ? 0 : 1); + break; + + case BIT_NOT: + *pd = ~(int)(*pd); + break; + + case LITERAL: + ++pd; + ++post; + if (post == NULL) { + ++post; + printf("%.7s bad constant in expression\n",post); + *pd = 0.; + break; + } + memcpy((void *)&(*pd),post,8); + post += 7; + break; + + default: + break; + } + + /* move ahead in postfix expression */ + ++post; + } + + /* if everything is peachy,the stack should end at its first position */ + if (pd != topd) return(-1); +#if DEBUG > 1 +/* check out floating rep of numbers like NaN, Inf */ +{ +union { unsigned char s[8]; double d; } u; +u.d = *pd; +printf("sCalcPerform: result: %g = (0x", *pd); +for (i=0; i<8; i++) printf("%2x ", u.s[i]); +printf(") \n"); +} +#endif + *presult = *pd; + if (psresult && (lenSresult > 15)) { + if (isnan(*pd)) + strcpy(psresult,"NaN"); + else + (void)cvtDoubleToString(*pd, psresult, 8); + } + } else { + + /*** expression requires string operations ***/ + + top = ps = &stack[10]; + ps--; /* Expression handler assumes ps is pointing to a filled element */ + + /* string expressions and values handled */ + while (*post != END_STACK) { + + switch (*post){ + + case FETCH: + INC(ps); + ++post; + ps->s = NULL; + ps->d = (*post < numArgs) ? parg[*post] : 0; + break; + + case SFETCH: + INC(ps); + ++post; + if (*post < numSArgs) { + /* fetch from string variable */ + ps->s = calloc(strlen(psarg[*post])+1, 1); + strcpy(ps->s, psarg[*post]); + } else { + /* fetch from variable that caller did not supply */ + ps->s = calloc(1, 1); + *(ps->s) = 0; + } + break; + + case STORE: + /* not implemented */ + cleanup(top, ps); + return(-1); + + case CONST_PI: + INC(ps); + ps->s = NULL; + ps->d = PI; + break; + + case CONST_D2R: + INC(ps); + ps->s = NULL; + ps->d = PI/180.; + break; + + case CONST_R2D: + INC(ps); + ps->s = NULL; + ps->d = 180./PI; + break; + + case CONST_S2R: + INC(ps); + ps->s = NULL; + ps->d = PI/(180.*3600); + break; + + case CONST_R2S: + INC(ps); + ps->s = NULL; + ps->d = (180.*3600)/PI; + break; + + case ADD: + ps1 = ps; + DEC(ps); + if (isDouble(ps)) { + toDouble(ps1); + ps->d = ps->d + ps1->d; + } else if (isDouble(ps1)) { + to_double(ps); + ps->d = ps->d + ps1->d; + } else { + /* concatenate two strings */ + s = ps->s; + ps->s = calloc(strlen(ps->s) + strlen(ps1->s) + 1, 1); + strcpy(ps->s, s); + strcat(ps->s, ps1->s); + free(s); + cleanStringElement(ps1); + } + break; + + case SUB: + ps1 = ps; + DEC(ps); + if (isDouble(ps)) { + toDouble(ps1); + ps->d = ps->d - ps1->d; + } else if (isDouble(ps1)) { + to_double(ps); + ps->d = ps->d - ps1->d; + } else { + /* subtract ps1->s from ps->s */ + s = strstr(ps->s, ps1->s); + s1 = ps->s; + s2 = ps1->s; + if (s && (strlen(s2) <= (strlen(s1) - (s - s1)))) { + for (s1=s+strlen(s2); *s1; s++, s1++) *s = *s1; + *s = '\0'; + } + cleanStringElement(ps1); + } + break; + + case MULT: + ps1 = ps; + DEC(ps); + toDouble(ps1); + toDouble(ps); + ps->d = ps->d * ps1->d; + break; + + case DIV: + ps1 = ps; + DEC(ps); + toDouble(ps1); + toDouble(ps); + if (ps1->d == 0) /* can't divide by zero */ + return(-1); + ps->d = ps->d / ps1->d; + break; + + case COND_IF: + /* if false condition then skip true expression */ + toDouble(ps); + if (ps->d == 0.0) { + /* skip to matching COND_ELSE */ + for (got_if=1; got_if>0 && *(post+1) != END_STACK; ++post) { + switch(post[1]) { + case LITERAL: post+=8; break; + case SLITERAL: while (*(++post)); break; + case COND_IF: got_if++; break; + case COND_ELSE: got_if--; break; + case FETCH: case SFETCH: post++; break; + } + } + } + /* remove condition from stack top */ + DEC(ps); + break; + + case COND_ELSE: + /* result, true condition is on stack so skip false condition */ + /* skip to matching COND_END */ + for (got_if=1; got_if>0 && *(post+1) != END_STACK; ++post) { + switch(post[1]) { + case LITERAL: post+=8; break; + case SLITERAL: while (*(++post)); break; + case COND_IF: got_if++; break; + case COND_ELSE: got_if--; break; + case FETCH: case SFETCH: post++; break; + } + } + break; + + case COND_END: + break; + + case ABS_VAL: + toDouble(ps); + if (ps->d < 0 ) ps->d *= -1; + break; + + case UNARY_NEG: + toDouble(ps); + ps->d *= -1; + break; + + case SQU_RT: + toDouble(ps); + /* check for neg number */ + if (ps->d < 0) {cleanup(top, ps); return(-1);} + ps->d = sqrt(ps->d); + break; + + case EXP: + toDouble(ps); + ps->d = exp(ps->d); + break; + + case LOG_10: + toDouble(ps); + /* check for neg number */ + if (ps->d < 0) {cleanup(top, ps); return(-1);} + ps->d = log10(ps->d); + break; + + case LOG_E: + toDouble(ps); + /* check for neg number */ + if (ps->d < 0) {cleanup(top, ps); return(-1);} + ps->d = log(ps->d); + break; + + case RANDOM: + INC(ps); + ps->d = local_random(); + ps->s = NULL; + break; + + case EXPON: + ps1 = ps; + DEC(ps); + toDouble(ps1); + toDouble(ps); + if (ps->d == 0) break; + if (ps->d < 0) { + i = (int) ps1->d; + /* is exponent an integer? */ + if ((ps1->d - (double)i) != 0) return (-1); + ps->d = exp(ps1->d * log(-(ps->d))); + /* is value negative */ + if ((i % 2) > 0) ps->d = -ps->d; + } else { + ps->d = exp(ps1->d * log(ps->d)); + } + break; + + case MODULO: + ps1 = ps; + DEC(ps); + toDouble(ps1); + toDouble(ps); + if ((int)ps1->d == 0) + return(-1); + ps->d = (double)((int)ps->d % (int)ps1->d); + break; + + case REL_OR: + ps1 = ps; + DEC(ps); + toDouble(ps1); + toDouble(ps); + ps->d = ps->d || ps1->d; + break; + + case REL_AND: + ps1 = ps; + DEC(ps); + toDouble(ps1); + toDouble(ps); + ps->d = ps->d && ps1->d; + break; + + case BIT_OR: + /* force double values into integers and or them */ + ps1 = ps; + DEC(ps); + toDouble(ps1); + toDouble(ps); + ps->d = (int)(ps1->d) | (int)(ps->d); + break; + + case BIT_AND: + /* force double values into integers and and them */ + ps1 = ps; + DEC(ps); + toDouble(ps1); + toDouble(ps); + ps->d = (int)(ps1->d) & (int)(ps->d); + break; + + case BIT_EXCL_OR: + /* force double values to integers to exclusive or them */ + ps1 = ps; + DEC(ps); + toDouble(ps1); + toDouble(ps); + ps->d = (int)(ps1->d) ^ (int)(ps->d); + break; + + case GR_OR_EQ: + ps1 = ps; + DEC(ps); + if (isDouble(ps)) { + toDouble(ps1); + ps->d = ps->d >= ps1->d; + } else if (isDouble(ps1)) { + to_double(ps); + ps->d = ps->d >= ps1->d; + } else { + /* compare ps->s to ps1->s */ + ps->d = (double)(strcmp(ps->s, ps1->s) >= 0); + free(ps->s); + ps->s = NULL; + cleanStringElement(ps1); + } + break; + + case GR_THAN: + ps1 = ps; + DEC(ps); + if (isDouble(ps)) { + toDouble(ps1); + ps->d = ps->d > ps1->d; + } else if (isDouble(ps1)) { + to_double(ps); + ps->d = ps->d > ps1->d; + } else { + /* compare ps->s to ps1->s */ + ps->d = (double)(strcmp(ps->s, ps1->s) > 0); + free(ps->s); + ps->s = NULL; + cleanStringElement(ps1); + } + break; + + case LESS_OR_EQ: + ps1 = ps; + DEC(ps); + if (isDouble(ps)) { + toDouble(ps1); + ps->d = ps->d <= ps1->d; + } else if (isDouble(ps1)) { + to_double(ps); + ps->d = ps->d <= ps1->d; + } else { + /* compare ps->s to ps1->s */ + ps->d = (double)(strcmp(ps->s, ps1->s) <= 0); + free(ps->s); + ps->s = NULL; + cleanStringElement(ps1); + } + break; + + case LESS_THAN: + ps1 = ps; + DEC(ps); + if (isDouble(ps)) { + toDouble(ps1); + ps->d = ps->d < ps1->d; + } else if (isDouble(ps1)) { + to_double(ps); + ps->d = ps->d < ps1->d; + } else { + /* compare ps->s to ps1->s */ + ps->d = (double)(strcmp(ps->s, ps1->s) < 0); + free(ps->s); + ps->s = NULL; + cleanStringElement(ps1); + } + break; + + case NOT_EQ: + ps1 = ps; + DEC(ps); + if (isDouble(ps)) { + toDouble(ps1); + ps->d = ps->d != ps1->d; + } else if (isDouble(ps1)) { + to_double(ps); + ps->d = ps->d != ps1->d; + } else { + /* compare ps->s to ps1->s */ + ps->d = (double)(strcmp(ps->s, ps1->s) != 0); + free(ps->s); + ps->s = NULL; + cleanStringElement(ps1); + } + break; + + case EQUAL: + ps1 = ps; + DEC(ps); + if (isDouble(ps)) { + toDouble(ps1); + ps->d = ps->d == ps1->d; + } else if (isDouble(ps1)) { + to_double(ps); + ps->d = ps->d == ps1->d; + } else if ((isString(ps)) && (isString(ps1))) { + /* compare ps->s to ps1->s */ + ps->d = (double)(strcmp(ps->s, ps1->s) == 0); + free(ps->s); + ps->s = NULL; + cleanStringElement(ps1); + } + break; + + case RIGHT_SHIFT: + ps1 = ps; + DEC(ps); + toDouble(ps1); + toDouble(ps); + ps->d = (int)(ps->d) >> (int)(ps1->d); + break; + + case LEFT_SHIFT: + ps1 = ps; + DEC(ps); + toDouble(ps1); + toDouble(ps); + ps->d = (int)(ps->d) << (int)(ps1->d); + break; + + case MAX_VAL: + ps1 = ps; + DEC(ps); + if (isDouble(ps)) { + toDouble(ps1); + if (ps->d < ps1->d) ps->d = ps1->d; + } else if (isDouble(ps1)) { + to_double(ps); + if (ps->d < ps1->d) ps->d = ps1->d; + } else { + /* compare ps->s to ps1->s */ + if (strcmp(ps->s, ps1->s) < 0) { + s = ps->s; + ps->s = ps1->s; + ps1->s = s; + } + cleanStringElement(ps1); + } + break; + + case MIN_VAL: + ps1 = ps; + DEC(ps); + if (isDouble(ps)) { + toDouble(ps1); + if (ps->d > ps1->d) ps->d = ps1->d; + } else if (isDouble(ps1)) { + to_double(ps); + if (ps->d > ps1->d) ps->d = ps1->d; + } else { + /* compare ps->s to ps1->s */ + if (strcmp(ps->s, ps1->s) > 0) { + s = ps->s; + ps->s = ps1->s; + ps1->s = s; + } + cleanStringElement(ps1); + } + break; + + case ACOS: + toDouble(ps); + ps->d = acos(ps->d); + break; + + case ASIN: + toDouble(ps); + ps->d = asin(ps->d); + break; + + case ATAN: + toDouble(ps); + ps->d = atan(ps->d); + break; + + case ATAN2: + ps1 = ps; + DEC(ps); + toDouble(ps1); + toDouble(ps); + ps->d = atan2(ps1->d, ps->d); + break; + + case COS: + toDouble(ps); + ps->d = cos(ps->d); + break; + + case SIN: + toDouble(ps); + ps->d = sin(ps->d); + break; + + case TAN: + toDouble(ps); + ps->d = tan(ps->d); + break; + + case COSH: + toDouble(ps); + ps->d = cosh(ps->d); + break; + + case SINH: + toDouble(ps); + ps->d = sinh(ps->d); + break; + + case TANH: + toDouble(ps); + ps->d = tanh(ps->d); + break; + + case CEIL: + toDouble(ps); + ps->d = ceil(ps->d); + break; + + case FLOOR: + toDouble(ps); + ps->d = floor(ps->d); + break; + + case NINT: + if (isDouble(ps)) { + d = ps->d; + ps->d = (double)(long)(d >= 0 ? d+0.5 : d-0.5); + } else { + /* hunt down integer and convert */ + s = strpbrk(ps->s,"0123456789"); + if ((s > ps->s) && (s[-1] == '.')) s--; + if ((s > ps->s) && (s[-1] == '-')) s--; + d = s ? atof(s) : 0.0; + free(ps->s); ps->s = NULL; + ps->d = (double)(long)(d >= 0 ? d+0.5 : d-0.5); + } + break; + + case REL_NOT: + toDouble(ps); + ps->d = (ps->d ? 0 : 1); + break; + + case BIT_NOT: + toDouble(ps); + ps->d = ~(int)(ps->d); + break; + + case LITERAL: + INC(ps); + ++post; + if (post == NULL) { + ++post; + printf("%.7s bad constant in expression\n",post); + ps->s = NULL; + ps->d = 0.; + break; + } + memcpy((void *)&(ps->d),post,8); + ps->s = NULL; + post += 7; + break; + + case SLITERAL: + INC(ps); + ++post; + if (post == NULL) { + ++post; + printf("%.7s bad constant in expression\n",post); + ps->s = NULL; + ps->d = 0.; + break; + } + ps->s = calloc(strlen(post)+1, 1); + strcpy(ps->s, post); + /* skip to end of string */ + while (*post) post++; + break; + + case TO_DOUBLE: + if (isString(ps)) { + /* hunt down number and convert */ + s = strpbrk(ps->s,"0123456789"); + if ((s > ps->s) && (s[-1] == '.')) s--; + if ((s > ps->s) && (s[-1] == '-')) s--; + ps->d = s ? atof(s) : 0.0; + free(ps->s); ps->s = NULL; + } + break; + + case TO_STRING: + toString(ps); + break; + + case PRINTF: + ps1 = ps; + DEC(ps); + if (isDouble(ps)) + {cleanup(top, ps1); return(-1);} + s = ps->s; + while ((s1 = strstr(s, "%%"))) {s = s1+2;} + if (((s = strpbrk(s, "%")) == NULL) || + ((s = strpbrk(s+1, "*cdeEfgGiousxX")) == NULL)) { + /* no printf arguments needed */ + sprintf(tmpstr, ps->s); + } else { + switch (*s) { + default: case '*': + cleanup(top, ps1); + return(-1); + break; + case 'c': case 'd': case 'i': case 'o': + case 'u': case 'x': case 'X': + toDouble(ps1); + l = myNINT(ps1->d); + sprintf(tmpstr, ps->s, l); + break; + case 'e': case 'E': case 'f': case 'g': case 'G': + toDouble(ps1); + sprintf(tmpstr, ps->s, ps1->d); + break; + case 's': + toString(ps1); + sprintf(tmpstr, ps->s, ps1->s); + break; + } + } + if (strlen(ps->s) < strlen(tmpstr)) { + free(ps->s); + ps->s = calloc(strlen(tmpstr)+1, 1); + } + strcpy(ps->s, tmpstr); + cleanStackElement(ps1); + break; + + case SSCANF: + ps1 = ps; + DEC(ps); + if (isDouble(ps) || isDouble(ps1)) + {cleanup(top, ps1); return(-1);} + s = findConversionIndicator(ps1->s); + if (s == NULL) {cleanup(top, ps1); return(-1);} + switch (*s) { + default: case 'p': case 'w': case 'n': case '$': + cleanup(top, ps1); + return(-1); + break; + case 'd': case 'i': + if (s[-1] == 'h') { + sscanf(ps->s, ps1->s, &h); + ps->d = (double)h; + } else { + sscanf(ps->s, ps1->s, &l); + ps->d = (double)l; + } + cleanStringElement(ps); + ps->s = NULL; + break; + case 'o': case 'u': case 'x': case 'X': + if (s[-1] == 'h') { + sscanf(ps->s, ps1->s, &ui); + ps->d = (double)ui; + } else { + sscanf(ps->s, ps1->s, &ul); + ps->d = (double)ul; + } + cleanStringElement(ps); + ps->s = NULL; + break; + case 'e': case 'E': case 'f': case 'g': case 'G': + if (s[-1] == 'l') { + sscanf(ps->s, ps1->s, &(ps->d)); + } else { + sscanf(ps->s, ps1->s, &f); + ps->d = (double)f; + } + cleanStringElement(ps); + ps->s = NULL; + break; + case 'c': case '[': case 's': + sscanf(ps->s, ps1->s, tmpstr); + if (strlen(ps->s) < strlen(tmpstr)) { + free(ps->s); + ps->s = calloc(strlen(tmpstr)+1, 1); + } + strcpy(ps->s, tmpstr); + break; + } + cleanStringElement(ps1); + break; + + case SUBRANGE: + ps2 = ps; + DEC(ps); + ps1 = ps; + DEC(ps); + toString(ps); + k = strlen(ps->s); + if (isDouble(ps1)) { + i = ps1->d; + if (i < 0) i += k; + } else { + s = strstr(ps->s, ps1->s); + i = s ? (s - ps->s) + strlen(ps1->s) : 0; + } + if (isDouble(ps2)) { + j = ps2->d; + if (j < 0) j += k; + } else { + if (*(ps2->s)) { + s = strstr(ps->s, ps2->s); + j = s ? (s - ps->s) - 1 : k; + } else { + j = k; + } + } + i = MAX(MIN(i,k),0); + j = MIN(j,k); + for (s=ps->s, s1=s+i, s2=s+j ; *s1 && s1 <= s2; ) + {*s++ = *s1++;} + *s = 0; + break; + + case REPLACE: + ps2 = ps; + DEC(ps); + ps1 = ps; + DEC(ps); + toString(ps); + toString(ps1); + toString(ps2); + i = strlen(ps->s); + j = strlen(ps1->s); + k = strlen(ps2->s); + s1 = strstr(ps->s, ps1->s); + s2 = ps2->s; + if (s1 >= ps->s) { + char *s_old, *s_dest; + s_old = s = ps->s; + if (k > j) ps->s = malloc(i - j + k + 1); + s_dest = ps->s; + while (s < s1) *s_dest++ = *s++; + s += j; + while (*s2) *s_dest++ = *s2++; + while (*s) *s_dest++ = *s++; + *s_dest = '\0'; + if (k > j) free(s_old); + } + cleanStringElement(ps1); + cleanStringElement(ps2); + break; + + default: + break; + } + + /* move ahead in postfix expression */ + ++post; + } + + /* if everything is peachy,the stack should end at its first position */ + if (ps != top) + return(-1); + + if (isDouble(ps)) { + if (presult) *presult = ps->d; + if (psresult) { + toString(ps); + for (i=0, s=ps->s, s1=psresult; *s && is, lenSresult); */ + psresult[lenSresult-1] = 0; + cleanStringElement(ps); + } + } else { + if (psresult) { + for (i=0, s=ps->s, s1=psresult; *s && id; + } else { + cleanStringElement(ps); + } + } + + } /* if (*post++ != USES_STRING) {} else */ + + return(0); +} + + +/* + * RAND + * + * generates a random number between 0 and 1 using the + * seed = (multy * seed) + addy Random Number Generator by Knuth + * SemiNumerical Algorithms + * Chapter 1 + * randy = seed / 65535.0 To normalize the number between 0 - 1 + */ +static unsigned short seed = 0xa3bf; +static unsigned short multy = 191 * 8 + 5; /* 191 % 8 == 5 */ +static unsigned short addy = 0x3141; +static double local_random() +{ + double randy; + + /* random number */ + seed = (seed * multy) + addy; + randy = (float) seed / 65535.0; + + /* between 0 - 1 */ + return(randy); +} diff --git a/src/libCom/sCalcPostfix.c b/src/libCom/sCalcPostfix.c new file mode 100644 index 000000000..385c6f04d --- /dev/null +++ b/src/libCom/sCalcPostfix.c @@ -0,0 +1,720 @@ +/* $Id$ + * Subroutines used to convert an infix expression to a postfix expression + * + * Author: Bob Dalesio + * Date: 12-12-86 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * .01 01-11-89 lrd added right shift and left shift operations + * .02 01-13-89 lrd modified to load into IOCs + * .03 02-01-89 lrd added trigonometric functions + * .04 04-05-89 lrd fixed the order of some operations in the + * element table and added a warning label + * .05 11-26-90 lrd fix SINH, COSH, TANH + * .06 02-20-92 rcz fixed for vxWorks build + * .07 02-24-92 jba add EXP and fixed trailing blanks in expression + * .08 03-03-92 jba added MAX_VAL and MIN_VAL and comma(like close paren) + * .09 03-06-92 jba added multiple conditional expressions ? + * .10 04-01-92 jba allowed floating pt constants in expression + * .11 05-01-92 jba flt pt constant string replaced with double in postfix + * .12 08-21-92 jba ANSI c changes + * .13 08-21-92 jba initialized *ppostfix: needed when calc expr not defined + * .14 12-11-92 mrk Removed include for stdioLib.h + * .15 11-03-93 jba Added test for extra close paren at end of expression + * .16 01-24-94 jba Changed seperator test to catch invalid commas + * .17 05-11-94 jba Added support for CONST_PI, CONST_R2D, and CONST_D2R + * and conversion of infix expression to uppercase + * .18 01-22-98 tmm Changed name postfix() to calcPostfix(). Changed arg list + * from pointer to postfix expression to address of pointer to + * postfix expression. calcPostfix() frees old expression, allocates + * space sufficient to hold new expression, and returns pointer to it. + * Added S2R, R2S (conversions between arc-seconds and radians). Changed + * CONSTANT to LITERAL to avoid conflict with link.h. Support 26 vars (A-Z). + * .19 03-18-98 tmm Added string operators + */ + +/* + * Subroutines + * + * Public + * + * sCalcPostfix convert an algebraic expression to symbolic postfix + * args + * pinfix the algebraic expression + * pp_postfix address of the symbolic postfix expression + * perror error information + * returns + * 0 successful + * -1 not successful + * Private routines for calcPostfix + * + * find_element finds a symbolic element in the expression element tbl + * args + * pbuffer pointer to the infix expression element + * pelement pointer to the expression element table entry + * pno_bytes pointer to the size of this element + * parg pointer to arg (used for fetch) + * returns + * TRUE element found + * FALSE element not found + * + * get_element finds the next expression element in the infix expr + * args + * pinfix pointer into the infix expression + * pelement pointer to the expression element table + * pno_bytes size of the element in the infix expression + * parg pointer to argument (used for fetch) + * returns + * FINE found an expression element + * VARIABLE found a database reference + * UNKNOWN_ELEMENT unknown element found in the infix expression + */ + +#ifdef vxWorks +#include +#endif + +#include +#include +#include +#include + +#include "dbDefs.h" +#define epicsExportSharedSymbols +#include "sCalcPostfix.h" +#include "sCalcPostfixPvt.h" + + +#define DEBUG 0 +volatile int sCalcPostfixDebug=0; + +/* declarations for postfix */ +/* element types */ +#define OPERAND 0 +#define UNARY_OPERATOR 1 +#define BINARY_OPERATOR 2 +#define EXPR_TERM 3 +#define COND 4 +#define CLOSE_PAREN 5 +#define CONDITIONAL 6 +#define ELSE 7 +#define SEPARATOR 8 +#define TRASH 9 +#define FLOAT_PT_CONST 10 +#define MINUS_OPERATOR 11 +#define STRING_CONST 12 +#define CLOSE_BRACKET 13 +#define CLOSE_CURLY 14 + +#define UNARY_MINUS_I_S_P 7 +#define UNARY_MINUS_I_C_P 8 +#define UNARY_MINUS_CODE UNARY_NEG +#define BINARY_MINUS_I_S_P 4 +#define BINARY_MINUS_I_C_P 4 +#define BINARY_MINUS_CODE SUB + +/* parsing return values */ +#define FINE 0 +#define UNKNOWN_ELEMENT -1 +#define END -2 + +/* + * element table + * + * structure of an element + */ +struct expression_element{ + char element[10]; /* character representation of an element */ + char in_stack_pri; /* priority in translation stack */ + char in_coming_pri; /* priority when first checking */ + char type; /* element type */ + char code; /* postfix representation */ +}; + +/* + * NOTE: DO NOT CHANGE WITHOUT READING THIS NOTICE !!!!!!!!!!!!!!!!!!!! + * Because the routine that looks for a match in this table takes the first + * match it finds, elements whose designations are contained in other elements + * MUST come first in this list. (e.g. ABS will match A if A preceeds ABS and + * then try to find BS therefore ABS must be first in this list + */ +static struct expression_element elements[] = { +/* +element i_s_p i_c_p type_element internal_rep */ +"ABS", 7, 8, UNARY_OPERATOR, ABS_VAL, /* absolute value */ +"NOT", 7, 8, UNARY_OPERATOR, UNARY_NEG, /* unary negate */ +"-", 7, 8, MINUS_OPERATOR, UNARY_NEG, /* unary negate (or binary op) */ +"SQRT", 7, 8, UNARY_OPERATOR, SQU_RT, /* square root */ +"SQR", 7, 8, UNARY_OPERATOR, SQU_RT, /* square root */ +"EXP", 7, 8, UNARY_OPERATOR, EXP, /* exponential function */ +"LOGE", 7, 8, UNARY_OPERATOR, LOG_E, /* log E */ +"LN", 7, 8, UNARY_OPERATOR, LOG_E, /* log E */ +"LOG", 7, 8, UNARY_OPERATOR, LOG_10, /* log 10 */ +"ACOS", 7, 8, UNARY_OPERATOR, ACOS, /* arc cosine */ +"ASIN", 7, 8, UNARY_OPERATOR, ASIN, /* arc sine */ +"ATAN2", 7, 8, UNARY_OPERATOR, ATAN2, /* arc tangent */ +"ATAN", 7, 8, UNARY_OPERATOR, ATAN, /* arc tangent */ +"MAX", 7, 8, UNARY_OPERATOR, MAX_VAL, /* maximum of 2 args */ +"MIN", 7, 8, UNARY_OPERATOR, MIN_VAL, /* minimum of 2 args */ +"CEIL", 7, 8, UNARY_OPERATOR, CEIL, /* smallest integer >= */ +"FLOOR", 7, 8, UNARY_OPERATOR, FLOOR, /* largest integer <= */ +"NINT", 7, 8, UNARY_OPERATOR, NINT, /* nearest integer */ +"INT", 7, 8, UNARY_OPERATOR, NINT, /* nearest integer */ +"COSH", 7, 8, UNARY_OPERATOR, COSH, /* hyperbolic cosine */ +"COS", 7, 8, UNARY_OPERATOR, COS, /* cosine */ +"SINH", 7, 8, UNARY_OPERATOR, SINH, /* hyperbolic sine */ +"SIN", 7, 8, UNARY_OPERATOR, SIN, /* sine */ +"TANH", 7, 8, UNARY_OPERATOR, TANH, /* hyperbolic tangent*/ +"TAN", 7, 8, UNARY_OPERATOR, TAN, /* tangent */ +"!=", 3, 3, BINARY_OPERATOR,NOT_EQ, /* not equal */ +"!", 7, 8, UNARY_OPERATOR, REL_NOT, /* not */ +"~", 7, 8, UNARY_OPERATOR, BIT_NOT, /* bitwise not */ +"DBL", 7, 8, UNARY_OPERATOR, TO_DOUBLE, /* convert to double */ +"STR", 7, 8, UNARY_OPERATOR, TO_STRING, /* convert to string */ +"$P", 7, 8, UNARY_OPERATOR, PRINTF, /* formatted print to string */ +"PRINTF", 7, 8, UNARY_OPERATOR, PRINTF, /* formatted print to string */ +"$S", 7, 8, UNARY_OPERATOR, SSCANF, /* scan string argument */ +"SSCANF", 7, 8, UNARY_OPERATOR, SSCANF, /* scan string argument */ +"RNDM", 0, 0, OPERAND, RANDOM, /* Random Number */ +"OR", 1, 1, BINARY_OPERATOR,BIT_OR, /* or */ +"AND", 2, 2, BINARY_OPERATOR,BIT_AND, /* and */ +"XOR", 1, 1, BINARY_OPERATOR,BIT_EXCL_OR, /* exclusive or */ +"PI", 0, 0, OPERAND, CONST_PI, /* pi */ +"D2R", 0, 0, OPERAND, CONST_D2R, /* pi/180 */ +"R2D", 0, 0, OPERAND, CONST_R2D, /* 180/pi */ +"S2R", 0, 0, OPERAND, CONST_S2R, /* arc-sec to radians: pi/(180*3600) */ +"R2S", 0, 0, OPERAND, CONST_R2S, /* radians to arc-sec: (180*3600)/pi */ +"0", 0, 0, FLOAT_PT_CONST, LITERAL, /* flt pt constant */ +"1", 0, 0, FLOAT_PT_CONST, LITERAL, /* flt pt constant */ +"2", 0, 0, FLOAT_PT_CONST, LITERAL, /* flt pt constant */ +"3", 0, 0, FLOAT_PT_CONST, LITERAL, /* flt pt constant */ +"4", 0, 0, FLOAT_PT_CONST, LITERAL, /* flt pt constant */ +"5", 0, 0, FLOAT_PT_CONST, LITERAL, /* flt pt constant */ +"6", 0, 0, FLOAT_PT_CONST, LITERAL, /* flt pt constant */ +"7", 0, 0, FLOAT_PT_CONST, LITERAL, /* flt pt constant */ +"8", 0, 0, FLOAT_PT_CONST, LITERAL, /* flt pt constant */ +"9", 0, 0, FLOAT_PT_CONST, LITERAL, /* flt pt constant */ +".", 0, 0, FLOAT_PT_CONST, LITERAL, /* flt pt constant */ +"\"", 0, 0, STRING_CONST, SLITERAL, /* string constant */ +"'", 0, 0, STRING_CONST, SLITERAL, /* string constant */ +"?", 0, 0, CONDITIONAL, COND_IF, /* conditional */ +":", 0, 0, CONDITIONAL, COND_ELSE, /* else */ +"(", 0, 8, UNARY_OPERATOR, PAREN, /* open paren */ +"[", 0, 8, BINARY_OPERATOR,SUBRANGE, /* string subrange */ +"{", 0, 8, BINARY_OPERATOR,REPLACE, /* string replace */ +"^", 6, 6, BINARY_OPERATOR,EXPON, /* exponentiation */ +"**", 6, 6, BINARY_OPERATOR,EXPON, /* exponentiation */ +"+", 4, 4, BINARY_OPERATOR,ADD, /* addition */ +#if 0 /* "-" operator is overloaded; may be unary or binary */ +"-", 4, 4, BINARY_OPERATOR,SUB, /* subtraction */ +#endif +"*", 5, 5, BINARY_OPERATOR,MULT, /* multiplication */ +"/", 5, 5, BINARY_OPERATOR,DIV, /* division */ +"%", 5, 5, BINARY_OPERATOR,MODULO, /* modulo */ +",", 0, 0, SEPARATOR, COMMA, /* comma */ +")", 0, 0, CLOSE_PAREN, PAREN, /* close paren */ +"]", 0, 0, CLOSE_BRACKET, SUBRANGE, /* close bracket */ +"}", 0, 0, CLOSE_CURLY, REPLACE, /* close curly bracket */ +"||", 1, 1, BINARY_OPERATOR,REL_OR, /* logical or */ +"|", 1, 1, BINARY_OPERATOR,BIT_OR, /* bitwise or */ +"&&", 2, 2, BINARY_OPERATOR,REL_AND, /* logical and */ +"&", 2, 2, BINARY_OPERATOR,BIT_AND, /* bitwise and */ +">>", 2, 2, BINARY_OPERATOR,RIGHT_SHIFT, /* right shift */ +">=", 3, 3, BINARY_OPERATOR,GR_OR_EQ, /* greater or equal*/ +">", 3, 3, BINARY_OPERATOR,GR_THAN, /* greater than */ +"<<", 2, 2, BINARY_OPERATOR,LEFT_SHIFT, /* left shift */ +"<=", 3, 3, BINARY_OPERATOR,LESS_OR_EQ,/* less or equal to*/ +"<", 3, 3, BINARY_OPERATOR,LESS_THAN, /* less than */ +"#", 3, 3, BINARY_OPERATOR,NOT_EQ, /* not equal */ +"==", 3, 3, BINARY_OPERATOR,EQUAL, /* equal */ +"=", 3, 3, BINARY_OPERATOR,EQUAL, /* equal */ +"" +}; + +/* + * Element-table entry for "fetch" operation. This element is used for all + * named variables. Currently, letters A-Z (double) and AA-ZZ (string) are + * allowed. Lower and upper case letters mean the same thing. + */ +static struct expression_element fetch_element = { +"A", 0, 0, OPERAND, FETCH, /* fetch var */ +}; + +static struct expression_element fetch_string_element = { +"AA", 0, 0, OPERAND, SFETCH, /* fetch var */ +}; + +static int strncasecmp(char *s1, char *s2, size_t n) +{ + short i; + for (i=0; i toupper((int)*s2)) return(1); + if (toupper((int)*s1) < toupper((int)*s2)) return(-1); + } + return(0); +} +/* + * FIND_ELEMENT + * + * find the pointer to an entry in the element table + */ +static int find_element(pbuffer, pelement, pno_bytes, parg) + register char *pbuffer; + register struct expression_element **pelement; + register short *pno_bytes, *parg; + { + *parg = 0; + + /* compare the string to each element in the element table */ + *pelement = &elements[0]; + while ((*pelement)->element[0] != NULL){ + if (strncasecmp(pbuffer,(*pelement)->element, strlen((*pelement)->element)) == 0){ + *pno_bytes += strlen((*pelement)->element); + return(TRUE); + } + *pelement += 1; + } + + /* look for a variable reference */ + /* double variables: ["a" - "z"], numbered 1-26 */ + if (isalpha((int)*pbuffer)) { + *pelement = &fetch_element; /* fetch means "variable reference" (fetch or store) */ + *parg = *pbuffer - (isupper((int)*pbuffer) ? 'A' : 'a'); + *pno_bytes += 1; + /* string variables: ["aa" - "zz"], numbered 1-26 */ + if (pbuffer[1] == pbuffer[0]) { + *pelement = &fetch_string_element; + *pno_bytes += 1; + } + return(TRUE); + } + if (sCalcPostfixDebug) printf("find_element: can't find '%s'\n", pbuffer); + return(FALSE); + } + +/* + * GET_ELEMENT + * + * get an expression element + */ +static int get_element(pinfix, pelement, pno_bytes, parg) +register char *pinfix; +register struct expression_element **pelement; +register short *pno_bytes, *parg; +{ + + /* get the next expression element from the infix expression */ + if (*pinfix == NULL) return(END); + *pno_bytes = 0; + while (*pinfix == 0x20){ + *pno_bytes += 1; + pinfix++; + } + if (*pinfix == NULL) return(END); + if (!find_element(pinfix, pelement, pno_bytes, parg)) + return(UNKNOWN_ELEMENT); + if (sCalcPostfixDebug > 5) printf("get_element: found element '%s', arg=%d\n", (*pelement)->element, *parg); + + return(FINE); + + +} + +#if DEBUG +/* Override standard EPICS expression evaluator (if we're loaded after it). */ +long epicsShareAPI postfix(char *pinfix,char *ppostfix,short *perror) +{ + char *my_ppostfix = NULL, *s, *d; + long retval; + + retval = sCalcPostfix(pinfix, &my_ppostfix, perror); + if (*my_ppostfix == BAD_EXPRESSION) { + *ppostfix = BAD_EXPRESSION; + } else { + for (s = my_ppostfix, d = ppostfix; *s != END_STACK; ) { + *d++=*s++; + } + *d = *s; + } + free(my_ppostfix); + return(retval); +} +#endif + +/* + * sCalcPostFix + * + * convert an infix expression to a postfix expression + */ +long epicsShareAPI sCalcPostfix(char *pinfix, char **pp_postfix, short *perror) +{ + short no_bytes; + register short operand_needed; + register short new_expression; + struct expression_element stack[80]; + struct expression_element *pelement; + register struct expression_element *pstacktop; + double constant; + register char c, *pposthold; + char in_stack_pri, in_coming_pri, code; + char *ppostfix, *ppostfixStart; + short arg; + + if (sCalcPostfixDebug) printf("sCalcPostfix: entry\n"); + + /* Allocate a buffer for the postfix expression. */ + if (*pp_postfix) free(*pp_postfix); /* Free old buffer. */ + ppostfix = calloc(5*strlen(pinfix)+7, 1); + *pp_postfix = ppostfix; + ppostfixStart = ppostfix++; + *ppostfixStart = BAD_EXPRESSION; + *ppostfix = END_STACK; + + /* place the expression elements into postfix */ + operand_needed = TRUE; + new_expression = TRUE; + *perror = 0; + if (*pinfix == 0) { + return(0); + } + pstacktop = &stack[0]; + while (get_element(pinfix, &pelement, &no_bytes, &arg) != END){ + pinfix += no_bytes; + code = pelement->code; + if ((*ppostfixStart != USES_STRING) && ((code == TO_STRING) || + (code == PRINTF) || (code == SSCANF) || (code == SLITERAL) || + (code == SUBRANGE) || (code == SFETCH))) { + *ppostfixStart = USES_STRING; + } + + switch (pelement->type){ + + case OPERAND: + if (!operand_needed){ + *perror = 5; + *ppostfixStart = BAD_EXPRESSION; return(-1); + } + + /* add operand to the expression */ + *ppostfix++ = pelement->code; + + /* if this is a variable reference, append variable number */ + if ((pelement->code == FETCH) || (pelement->code == SFETCH)) { + *ppostfix++ = arg; + } + + operand_needed = FALSE; + new_expression = FALSE; + break; + + case FLOAT_PT_CONST: + if (!operand_needed){ + *perror = 5; + *ppostfixStart = BAD_EXPRESSION; return(-1); + } + + /* add constant to postfix expression */ + *ppostfix++ = pelement->code; + pposthold = ppostfix; + + pinfix-=no_bytes; + while (*pinfix == ' ') *ppostfix++ = *pinfix++; + while (TRUE) { + if ( ( *pinfix >= '0' && *pinfix <= '9' ) || *pinfix == '.' ) { + *ppostfix++ = *pinfix; + pinfix++; + } else if ( *pinfix == 'E' || *pinfix == 'e' ) { + *ppostfix++ = *pinfix; + pinfix++; + if (*pinfix == '+' || *pinfix == '-' ) { + *ppostfix++ = *pinfix; + pinfix++; + } + } else break; + } + *ppostfix++ = '\0'; + + ppostfix = pposthold; + if ( sscanf(ppostfix,"%lg",&constant) != 1) { + *ppostfix = '\0'; + } else { + memcpy(ppostfix,(void *)&constant,8); + } + ppostfix+=8; + + operand_needed = FALSE; + new_expression = FALSE; + break; + + case STRING_CONST: + if (!operand_needed){ + *perror = 5; + *ppostfixStart = BAD_EXPRESSION; return(-1); + } + + /* add string literal to the postfix expression */ + *ppostfix++ = pelement->code; + c = pinfix[-1]; + while (*pinfix != c && *pinfix) *ppostfix++ = *pinfix++; + *ppostfix++ = '\0'; + if (*pinfix) pinfix++; + + operand_needed = FALSE; + new_expression = FALSE; + break; + + case BINARY_OPERATOR: + if (operand_needed){ + *perror = 4; + *ppostfixStart = BAD_EXPRESSION; return(-1); + } + + /* add operators of higher or equal priority to postfix expression */ + while ((pstacktop->in_stack_pri >= pelement->in_coming_pri) + && (pstacktop >= &stack[1])){ + *ppostfix++ = pstacktop->code; + pstacktop--; + } + + /* add new operator to stack */ + pstacktop++; + *pstacktop = *pelement; + + operand_needed = TRUE; + break; + + case UNARY_OPERATOR: + if (!operand_needed){ + *perror = 5; + *ppostfixStart = BAD_EXPRESSION; return(-1); + } + + /* add operators of higher or equal priority to postfix expression */ + while ((pstacktop->in_stack_pri >= pelement->in_coming_pri) + && (pstacktop >= &stack[1])){ + *ppostfix++ = pstacktop->code; + pstacktop--; + } + + /* add new operator to stack */ + pstacktop++; + *pstacktop = *pelement; + + new_expression = FALSE; + break; + + case MINUS_OPERATOR: + if (operand_needed) { + /* then assume minus was intended as a unary operator */ + in_coming_pri = UNARY_MINUS_I_C_P; + in_stack_pri = UNARY_MINUS_I_S_P; + code = UNARY_MINUS_CODE; + new_expression = FALSE; + } else { + /* then assume minus was intended as a binary operator */ + in_coming_pri = BINARY_MINUS_I_C_P; + in_stack_pri = BINARY_MINUS_I_S_P; + code = BINARY_MINUS_CODE; + operand_needed = TRUE; + } + + /* add operators of higher or equal priority to postfix expression */ + while ((pstacktop->in_stack_pri >= in_coming_pri) + && (pstacktop >= &stack[1])){ + *ppostfix++ = pstacktop->code; + pstacktop--; + } + + /* add new operator to stack */ + pstacktop++; + *pstacktop = *pelement; + pstacktop->in_stack_pri = in_stack_pri; + pstacktop->code = code; + + break; + + case SEPARATOR: + if (operand_needed){ + *perror = 4; + *ppostfixStart = BAD_EXPRESSION; return(-1); + } + + /* add operators to postfix until open paren */ + while ((pstacktop->element[0] != '(') && (pstacktop->element[0] != '[') + && (pstacktop->element[0] != '{')) { + if (pstacktop == &stack[1] || pstacktop == &stack[0]){ + *perror = 6; + *ppostfixStart = BAD_EXPRESSION; return(-1); + } + *ppostfix++ = pstacktop->code; + pstacktop--; + } + operand_needed = TRUE; + break; + + case CLOSE_PAREN: + if (operand_needed){ + *perror = 4; + *ppostfixStart = BAD_EXPRESSION; return(-1); + } + + /* add operators to postfix until matching paren */ + while (pstacktop->element[0] != '(') { + if (pstacktop == &stack[1] || pstacktop == &stack[0]) { + *perror = 6; + *ppostfixStart = BAD_EXPRESSION; return(-1); + } + *ppostfix++ = pstacktop->code; + pstacktop--; + } + pstacktop--; /* remove ( from stack */ + break; + + case CLOSE_BRACKET: + if (operand_needed){ + *perror = 4; + *ppostfixStart = BAD_EXPRESSION; return(-1); + } + + /* add operators to postfix until matching bracket */ + while (pstacktop->element[0] != '[') { + if (pstacktop == &stack[1] || pstacktop == &stack[0]) { + *perror = 6; + *ppostfixStart = BAD_EXPRESSION; return(-1); + } + *ppostfix++ = pstacktop->code; + pstacktop--; + } + /* add SUBRANGE operator to postfix */ + if (pstacktop == &stack[0]) { + *perror = 6; + *ppostfixStart = BAD_EXPRESSION; return(-1); + } + *ppostfix++ = pstacktop->code; + pstacktop--; + break; + + case CLOSE_CURLY: + if (operand_needed){ + *perror = 4; + *ppostfixStart = BAD_EXPRESSION; return(-1); + } + + /* add operators to postfix until matching bracket */ + while (pstacktop->element[0] != '{') { + if (pstacktop == &stack[1] || pstacktop == &stack[0]) { + *perror = 6; + *ppostfixStart = BAD_EXPRESSION; return(-1); + } + *ppostfix++ = pstacktop->code; + pstacktop--; + } + /* add REPLACE operator to postfix */ + if (pstacktop == &stack[0]) { + *perror = 6; + *ppostfixStart = BAD_EXPRESSION; return(-1); + } + *ppostfix++ = pstacktop->code; + pstacktop--; + break; + + case CONDITIONAL: + if (operand_needed){ + *perror = 4; + *ppostfixStart = BAD_EXPRESSION; return(-1); + } + + /* add operators of higher priority to postfix expression */ + while ((pstacktop->in_stack_pri > pelement->in_coming_pri) + && (pstacktop >= &stack[1])){ + *ppostfix++ = pstacktop->code; + pstacktop--; + } + + /* add new element to the postfix expression */ + *ppostfix++ = pelement->code; + + /* add : operator with COND_END code to stack */ + if (pelement->element[0] == ':'){ + pstacktop++; + *pstacktop = *pelement; + pstacktop->code = COND_END; + } + + operand_needed = TRUE; + break; + + case EXPR_TERM: + if (operand_needed && !new_expression){ + *perror = 4; + *ppostfixStart = BAD_EXPRESSION; return(-1); + } + + /* add all operators on stack to postfix */ + while (pstacktop >= &stack[1]){ + if (pstacktop->element[0] == '('){ + *perror = 6; + *ppostfixStart = BAD_EXPRESSION; return(-1); + } + *ppostfix++ = pstacktop->code; + pstacktop--; + } + + /* add new element to the postfix expression */ + *ppostfix++ = pelement->code; + + operand_needed = TRUE; + new_expression = TRUE; + break; + + default: + *perror = 8; + *ppostfixStart = BAD_EXPRESSION; return(-1); + } + } + if (operand_needed){ + *perror = 4; + *ppostfixStart = BAD_EXPRESSION; return(-1); + } + + /* add all operators on stack to postfix */ + while (pstacktop >= &stack[1]){ + if (pstacktop->element[0] == '('){ + *perror = 6; + *ppostfixStart = BAD_EXPRESSION; return(-1); + } + *ppostfix++ = pstacktop->code; + pstacktop--; + } + *ppostfix++ = END_STACK; + *ppostfix = '\0'; + + if (ppostfixStart[1] == END_STACK) + *ppostfixStart = BAD_EXPRESSION; + else if (*ppostfixStart != USES_STRING) + *ppostfixStart = NO_STRING; + + return(0); +} diff --git a/src/libCom/sCalcPostfix.h b/src/libCom/sCalcPostfix.h new file mode 100644 index 000000000..d6b73c9a0 --- /dev/null +++ b/src/libCom/sCalcPostfix.h @@ -0,0 +1,39 @@ +/* sCalcPostfix.h + * Author: Bob Dalesio + * Date: 9-21-88 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * .01 03-18-98 tmm derived from postfix.h + * + */ + +#ifndef INCpostfixh +#define INCpostfixh + +long epicsShareAPI sCalcPostfix(char *pinfix, char **pp_postfix, short *perror); +epicsShareFunc long epicsShareAPI + sCalcPerform(double *parg, int numArgs, char **psarg, int numSArgs, double *presult, char *psresult, int lenSresult, char *post); + +#endif /* INCpostfixh */ diff --git a/src/libCom/sCalcPostfixPvt.h b/src/libCom/sCalcPostfixPvt.h new file mode 100644 index 000000000..b66ef9330 --- /dev/null +++ b/src/libCom/sCalcPostfixPvt.h @@ -0,0 +1,111 @@ +/* sCalcPostfixPvt.h + * Author: Bob Dalesio + * Date: 9-21-88 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * .01 03-18-98 tmm derived from postfix.h + * + */ + +#ifndef INCpostfixPvth +#define INCpostfixPvth + +/* defines for element table */ +#define BAD_EXPRESSION 0 +/* elements that define a value */ +#define FETCH 1 +#define SFETCH 2 +#define CONST_PI 3 +#define CONST_D2R 4 +#define CONST_R2D 5 +#define CONST_S2R 6 +#define CONST_R2S 7 +#define RANDOM 8 +#define LITERAL 9 +#define SLITERAL 10 +#define SSCANF 11 + +#define VALUE_ELEMENT 11 + +/* elements that operate on a value */ +#define ACOS 12 +#define ASIN 13 +#define ATAN 14 +#define COS 15 +#define COSH 16 +#define SIN 17 +#define RIGHT_SHIFT 18 +#define LEFT_SHIFT 19 +#define SINH 20 +#define TAN 21 +#define TANH 22 +#define LOG_2 23 +#define COND_ELSE 24 +#define ABS_VAL 25 +#define UNARY_NEG 26 +#define SQU_RT 27 +#define EXP 28 +#define CEIL 29 +#define FLOOR 30 +#define LOG_10 31 +#define LOG_E 32 +#define ADD 33 +#define SUB 34 +#define MULT 35 +#define DIV 36 +#define EXPON 37 +#define MODULO 38 +#define BIT_OR 39 +#define BIT_AND 40 +#define BIT_EXCL_OR 41 +#define GR_OR_EQ 42 +#define GR_THAN 43 +#define LESS_OR_EQ 44 +#define LESS_THAN 45 +#define NOT_EQ 46 +#define EQUAL 47 +#define REL_OR 48 +#define REL_AND 49 +#define REL_NOT 50 +#define BIT_NOT 51 +#define PAREN 52 +#define MAX_VAL 53 +#define MIN_VAL 54 +#define COMMA 55 +#define COND_IF 56 +#define COND_END 57 +#define NINT 58 +#define ATAN2 59 +#define STORE 60 +#define TO_DOUBLE 61 +#define PRINTF 62 +#define SUBRANGE 63 +#define TO_STRING 64 +#define REPLACE 65 +#define END_STACK 127 + +#define USES_STRING 126 +#define NO_STRING 125 +#endif /* INCpostfixPvth */