diff --git a/src/libCom/calc/calcPerform.c b/src/libCom/calc/calcPerform.c index f8cb182ba..8f0520280 100644 --- a/src/libCom/calc/calcPerform.c +++ b/src/libCom/calc/calcPerform.c @@ -12,450 +12,391 @@ * Author: Julie Sander and Bob Dalesio * Date: 07-27-87 */ - -/* 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. CalcPerform - * calculates the postfix expression. - * - * Subroutines - * - * Public - * - * calcPerform perform the calculation - * args - * double *pargs address of arguments (12) - * double *presult address of result - * char *rpcl address of reverse polish buffer - * returns - * 0 fetched successfully - * -1 fetch failed - * - * Private routine for calcPerform - * local_random random number generator - * returns - * double value between 0.00 and 1.00 - */ - + #include #include #include #include -#define epicsExportSharedSymbols +#include "osiUnistd.h" #include "dbDefs.h" #include "epicsMath.h" +#define epicsExportSharedSymbols #include "postfix.h" #include "postfixPvt.h" -#include "osiUnistd.h" -static double local_random(void); - -#define NOT_SET 0 -#define TRUE_COND 1 -#define FALSE_COND 2 +static double calcRandom(void); +static int cond_search(const char **ppinst, int match); #ifndef PI -#define PI 3.141592654 +#define PI 3.14159265358979323 #endif -epicsShareFunc long epicsShareAPI - calcPerform(double *parg, double *presult, char *post) + +/* calcPerform + * + * Evalutate the postfix expression + */ +epicsShareFunc long + calcPerform(double *parg, double *presult, const char *pinst) { - double *pstacktop; /* stack of values */ - double stack[80]; - short temp1; - double *top; - int itop; /* integer top value */ - int inexttop; /* ineteger next to top value */ - short cond_flag; /* conditional else flag */ - short got_if; - - /* initialize flag */ - cond_flag = NOT_SET; - pstacktop = &stack[0]; - -/* DEBUG print statements -for (short i=0;i<184;i++){ -printf ("%d_",post[i]); -if ( post[i] == END_STACK ) break; -if ( post[i] == 71 ) i=i+8; -} -printf ("*FINISHED*\n"); -*/ - if(*post == BAD_EXPRESSION) return(-1); - - /* set post to postfix expression in calc structure */ - top = pstacktop; - - /* polish calculator loop */ - while (*post != END_STACK){ - - switch (*post){ - case FETCH_A: - ++pstacktop; - *pstacktop = parg[0]; - break; - - case FETCH_B: - ++pstacktop; - *pstacktop = parg[1]; - break; - - case FETCH_C: - ++pstacktop; - *pstacktop = parg[2]; - break; - - case FETCH_D: - ++pstacktop; - *pstacktop = parg[3]; - break; - - case FETCH_E: - ++pstacktop; - *pstacktop = parg[4]; - break; - - case FETCH_F: - ++pstacktop; - *pstacktop = parg[5]; - break; - - case FETCH_G: - ++pstacktop; - *pstacktop = parg[6]; - break; - - case FETCH_H: - ++pstacktop; - *pstacktop = parg[7]; - break; - - case FETCH_I: - ++pstacktop; - *pstacktop = parg[8]; - break; - - case FETCH_J: - ++pstacktop; - *pstacktop = parg[9]; - break; - - case FETCH_K: - ++pstacktop; - *pstacktop = parg[10]; - break; - - case FETCH_L: - ++pstacktop; - *pstacktop = parg[11]; - break; - - case CONST_PI: - ++pstacktop; - *pstacktop = PI; - break; - - case CONST_D2R: - ++pstacktop; - *pstacktop = PI/180.; - break; - - case CONST_R2D: - ++pstacktop; - *pstacktop = 180./PI; - break; - - case ADD: - --pstacktop; - *pstacktop = *pstacktop + *(pstacktop+1); - break; - - case SUB: - --pstacktop; - *pstacktop = *pstacktop - *(pstacktop+1); - break; - - case MULT: - --pstacktop; - *pstacktop = *pstacktop * *(pstacktop+1); - break; - - case DIV: - --pstacktop; - if (*(pstacktop+1) == 0) /* can't divide by zero */ - return(-1); - *pstacktop = *pstacktop / *(pstacktop+1); - break; - - case COND_IF: - /* if false condition then skip true expression */ - if (*pstacktop == 0.0) { - /* skip to matching COND_ELSE */ - for (got_if=1; got_if>0 && *(post+1) != END_STACK; ++post) { - if (*(post+1) == CONSTANT ) post+=8; - else if (*(post+1) == COND_IF ) got_if++; - else if (*(post+1) == COND_ELSE) got_if--; - } - } - /* remove condition from stack top */ - --pstacktop; - 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) { - if (*(post+1) == CONSTANT ) post+=8; - else if (*(post+1) == COND_IF ) got_if++; - else if (*(post+1) == COND_END) got_if--; - } - break; - - case COND_END: - break; - - case ABS_VAL: - if (*pstacktop < 0) *pstacktop = -*pstacktop; - break; - - case UNARY_NEG: - *pstacktop = -1* (*pstacktop); - break; - - case SQU_RT: - if (*pstacktop < 0) return(-1); /* undefined */ - *pstacktop = sqrt(*pstacktop); - break; - - case EXP: - *pstacktop = exp(*pstacktop); - break; - - case LOG_10: - *pstacktop = log10(*pstacktop); - break; - - case LOG_E: - *pstacktop = log(*pstacktop); - break; - - case RANDOM: - ++pstacktop; - *pstacktop = local_random(); - break; - - case EXPON: - --pstacktop; - if (*pstacktop == 0) break; - if (*pstacktop < 0){ - temp1 = (int) *(pstacktop+1); - /* is exponent an integer */ - if ((*(pstacktop+1) - (double)temp1) != 0) return (-1); - *pstacktop = exp(*(pstacktop+1) * log(-*pstacktop)); - /* is value negative */ - if ((temp1 % 2) > 0) *pstacktop = -*pstacktop; - }else{ - *pstacktop = exp(*(pstacktop+1) * log(*pstacktop)); - } - break; - - case MODULO: - --pstacktop; - itop = (int)*pstacktop; - inexttop = (int)*(pstacktop+1); - if (inexttop == 0) - return(-1); - *pstacktop = itop % inexttop; - break; - - case REL_OR: - --pstacktop; - *pstacktop = (*pstacktop || *(pstacktop+1)); - break; - - case REL_AND: - --pstacktop; - *pstacktop = (*pstacktop && *(pstacktop+1)); - break; - - case BIT_OR: - /* force double values into integers and or them */ - itop = (int)*pstacktop; - inexttop = (int)*(pstacktop-1); - --pstacktop; - *pstacktop = (inexttop | itop); - break; - - case BIT_AND: - /* force double values into integers and and them */ - itop = (int)*pstacktop; - inexttop = (int)*(pstacktop-1); - --pstacktop; - *pstacktop = (inexttop & itop); - break; - - case BIT_EXCL_OR: - /*force double values to integers to exclusive or them*/ - itop = (int)*pstacktop; - inexttop = (int)*(pstacktop-1); - --pstacktop; - *pstacktop = (inexttop ^ itop); - break; - - case GR_OR_EQ: - --pstacktop; - *pstacktop = *pstacktop >= *(pstacktop+1); - break; - - case GR_THAN: - --pstacktop; - *pstacktop = *pstacktop > *(pstacktop+1); - break; - - case LESS_OR_EQ: - --pstacktop; - *pstacktop = *pstacktop <= *(pstacktop+1); - break; - - case LESS_THAN: - --pstacktop; - *pstacktop = *pstacktop < *(pstacktop+1); - break; - - case NOT_EQ: - --pstacktop; - *pstacktop = *pstacktop != *(pstacktop+1); - break; - - case EQUAL: - --pstacktop; - *pstacktop = (*pstacktop == *(pstacktop+1)); - break; - - case RIGHT_SHIFT: - itop = (int)*pstacktop; - inexttop = (int)*(pstacktop-1); - --pstacktop; - *pstacktop = (inexttop >> itop); - break; - - case LEFT_SHIFT: - itop = (int)*pstacktop; - inexttop = (int)*(pstacktop-1); - --pstacktop; - *pstacktop = (inexttop << itop); - break; - - case MAX: - --pstacktop; - if (*pstacktop < *(pstacktop+1)) - *pstacktop = *(pstacktop+1); - break; - - case MIN: - --pstacktop; - if (*pstacktop > *(pstacktop+1)) - *pstacktop = *(pstacktop+1); - break; - - - case ACOS: - *pstacktop = acos(*pstacktop); - break; - - case ASIN: - *pstacktop = asin(*pstacktop); - break; - - case ATAN: - *pstacktop = atan(*pstacktop); - break; - - case ATAN2: - --pstacktop; - *pstacktop = atan2(*(pstacktop+1), *pstacktop); - break; - - case COS: - *pstacktop = cos(*pstacktop); - break; - - case SIN: - *pstacktop = sin(*pstacktop); - break; - - case TAN: - *pstacktop = tan(*pstacktop); - break; - - case COSH: - *pstacktop = cosh(*pstacktop); - break; - - case SINH: - *pstacktop = sinh(*pstacktop); - break; - - case TANH: - *pstacktop = tanh(*pstacktop); - break; - - case CEIL: - *pstacktop = ceil(*pstacktop); - break; - - case FLOOR: - *pstacktop = floor(*pstacktop); - break; - - case NINT: - *pstacktop = (double)(long)((*pstacktop) >= 0 ? (*pstacktop)+0.5 : (*pstacktop)-0.5); - break; - - case REL_NOT: - *pstacktop = ((*pstacktop)?0:1); - break; - - case BIT_NOT: - itop = (int)*pstacktop; - *pstacktop = ~itop; - break; - - case CONSTANT: - ++pstacktop; - ++post; - if ( post == NULL ) { - ++post; - printf("%.7s bad constant in expression\n",post); - break; - } - memcpy((void *)pstacktop,post,8); - post+=7; - break; - default: - printf("%d bad expression element\n",*post); - break; - } - - /* move ahead in postfix expression */ - ++post; + double stack[CALCPERFORM_STACK]; /* zero'th entry not used */ + double *ptop; /* stack pointer */ + double top; /* value from top of stack */ + int itop; /* integer from top of stack */ + + /* initialize */ + ptop = stack; + + if(*pinst == END_EXPRESSION) return -1; + + /* RPN evaluation loop */ + while (*pinst != END_EXPRESSION){ + switch (*pinst){ + + case LITERAL: + ++ptop; + ++pinst; + memcpy((void *)ptop, pinst, sizeof(double)); + pinst += sizeof(double) - 1; + break; + + case FETCH_A: + case FETCH_B: + case FETCH_C: + case FETCH_D: + case FETCH_E: + case FETCH_F: + case FETCH_G: + case FETCH_H: + case FETCH_I: + case FETCH_J: + case FETCH_K: + case FETCH_L: + *++ptop = parg[*pinst - FETCH_A]; + break; + + case STORE_A: + case STORE_B: + case STORE_C: + case STORE_D: + case STORE_E: + case STORE_F: + case STORE_G: + case STORE_H: + case STORE_I: + case STORE_J: + case STORE_K: + case STORE_L: + parg[*pinst - STORE_A] = *ptop--; + break; + + case CONST_PI: + *++ptop = PI; + break; + + case CONST_D2R: + *++ptop = PI/180.; + break; + + case CONST_R2D: + *++ptop = 180./PI; + break; + + case UNARY_NEG: + *ptop = - *ptop; + break; + + case ADD: + top = *ptop--; + *ptop += top; + break; + + case SUB: + top = *ptop--; + *ptop -= top; + break; + + case MULT: + top = *ptop--; + *ptop *= top; + break; + + case DIV: + top = *ptop--; + *ptop /= top; + break; + + case MODULO: + top = *ptop--; + *ptop = fmod(*ptop, top); + break; + + case POWER: + top = *ptop--; + *ptop = pow(*ptop, top); + break; + + case ABS_VAL: + if (*ptop < 0.0) *ptop = - *ptop; + break; + + case EXP: + *ptop = exp(*ptop); + break; + + case LOG_10: + *ptop = log10(*ptop); + break; + + case LOG_E: + *ptop = log(*ptop); + break; + + case MAX: + top = *ptop--; + if (*ptop < top || isnan(top)) + *ptop = top; + break; + + case MIN: + top = *ptop--; + if (*ptop > top || isnan(top)) + *ptop = top; + break; + + case SQU_RT: + *ptop = sqrt(*ptop); + break; + + case ACOS: + *ptop = acos(*ptop); + break; + + case ASIN: + *ptop = asin(*ptop); + break; + + case ATAN: + *ptop = atan(*ptop); + break; + + case ATAN2: + top = *ptop--; + *ptop = atan2(top, *ptop); /* Ouch!: Args backwards! */ + break; + + case COS: + *ptop = cos(*ptop); + break; + + case SIN: + *ptop = sin(*ptop); + break; + + case TAN: + *ptop = tan(*ptop); + break; + + case COSH: + *ptop = cosh(*ptop); + break; + + case SINH: + *ptop = sinh(*ptop); + break; + + case TANH: + *ptop = tanh(*ptop); + break; + + case CEIL: + *ptop = ceil(*ptop); + break; + + case FLOOR: + *ptop = floor(*ptop); + break; + + case FINITE: + *ptop = finite(*ptop); + break; + + case ISINF: + *ptop = isinf(*ptop); + break; + + case ISNAN: + *ptop = isnan(*ptop); + break; + + case NINT: + top = *ptop; + *ptop = (double)(long)(top >= 0 ? top + 0.5 : top - 0.5); + break; + + case RANDOM: + *++ptop = calcRandom(); + break; + + case REL_OR: + top = *ptop--; + *ptop = *ptop || top; + break; + + case REL_AND: + top = *ptop--; + *ptop = *ptop && top; + break; + + case REL_NOT: + *ptop = ! *ptop; + break; + + case BIT_OR: + itop = (int) *ptop--; + *ptop = (int) *ptop | itop; + break; + + case BIT_AND: + itop = (int) *ptop--; + *ptop = (int) *ptop & itop; + break; + + case BIT_EXCL_OR: + itop = (int) *ptop--; + *ptop = (int) *ptop ^ itop; + break; + + case BIT_NOT: + itop = (int) *ptop; + *ptop = ~itop; + break; + + case RIGHT_SHIFT: + itop = (int) *ptop--; + *ptop = (int) *ptop >> itop; + break; + + case LEFT_SHIFT: + itop = (int) *ptop--; + *ptop = (int) *ptop << itop; + break; + + case NOT_EQ: + top = *ptop--; + *ptop = *ptop != top; + break; + + case LESS_THAN: + top = *ptop--; + *ptop = *ptop < top; + break; + + case LESS_OR_EQ: + top = *ptop--; + *ptop = *ptop <= top; + break; + + case EQUAL: + top = *ptop--; + *ptop = *ptop == top; + break; + + case GR_OR_EQ: + top = *ptop--; + *ptop = *ptop >= top; + break; + + case GR_THAN: + top = *ptop--; + *ptop = *ptop > top; + break; + + case COND_IF: + if (*ptop-- == 0.0 && + cond_search(&pinst, COND_ELSE)) return -1; + break; + + case COND_ELSE: + if (cond_search(&pinst, COND_END)) return -1; + break; + + case COND_END: + break; + + default: + errlogPrintf("calcPerform: Bad Opcode %d at %p\n",*pinst, pinst); + return -1; } - /* if everything is peachy,the stack should end at its first position */ - if (++top == pstacktop) - *presult = *pstacktop; - else - return(-1); - return(((isnan(*presult)||isinf(*presult)) ? -1 : 0)); + /* Advance to next opcode */ + ++pinst; + } + + /* The stack should now have one item on it, the expression value */ + if (ptop != stack + 1) + return -1; + *presult = *ptop; + return (isnan(*presult) || isinf(*presult)) ? -1 : 0; /* FIXME */ } -/* - * RAND - * - * generates a random number between 0 and 1 using the +epicsShareFunc long +calcArgUsage(const char *pinst, unsigned long *pinputs, unsigned long *pstores) +{ + unsigned long inputs = 0; + unsigned long stores = 0; + char op; + while ((op = *pinst) != END_EXPRESSION) { + switch (op) { + + case LITERAL: + pinst += sizeof(double); + break; + + case FETCH_A: + case FETCH_B: + case FETCH_C: + case FETCH_D: + case FETCH_E: + case FETCH_F: + case FETCH_G: + case FETCH_H: + case FETCH_I: + case FETCH_J: + case FETCH_K: + case FETCH_L: + /* Don't claim to use an arg we already stored to */ + inputs |= (1 << (op - FETCH_A)) & ~stores; + break; + + case STORE_A: + case STORE_B: + case STORE_C: + case STORE_D: + case STORE_E: + case STORE_F: + case STORE_G: + case STORE_H: + case STORE_I: + case STORE_J: + case STORE_K: + case STORE_L: + stores |= (1 << (op - STORE_A)); + break; + + default: + break; + } + pinst++; + } + if (pinputs) *pinputs = inputs; + if (pstores) *pstores = stores; + return 0; +} + +/* Generate a random number between 0 and 1 using the algorithm * seed = (multy * seed) + addy Random Number Generator by Knuth * SemiNumerical Algorithms * Chapter 1 @@ -464,14 +405,34 @@ printf ("*FINISHED*\n"); static unsigned short seed = 0xa3bf; static unsigned short multy = 191 * 8 + 5; /* 191 % 8 == 5 */ static unsigned short addy = 0x3141; -static double local_random() + +static double calcRandom() { - double randy; + seed = (seed * multy) + addy; - /* random number */ - seed = (seed * multy) + addy; - randy = (float) seed / 65535.0; - - /* between 0 - 1 */ - return(randy); + /* between 0 - 1 */ + return (double) seed / 65535.0; } + +/* Search the instruction stream for a matching operator, skipping any + * other conditional instructions found + */ +static int cond_search(const char **ppinst, int match) +{ + const char *pinst = *ppinst + 1; + int count = 1; + int op; + + while ((op = *pinst) != END_EXPRESSION) { + if (op == match && --count == 0) { + *ppinst = pinst; + return 0; + } else if (op == COND_IF) + count++; + else if (op == LITERAL) + pinst += sizeof(double); + pinst++; + } + return 1; +} + diff --git a/src/libCom/calc/postfix.c b/src/libCom/calc/postfix.c index 1121985e5..c0b0b101d 100644 --- a/src/libCom/calc/postfix.c +++ b/src/libCom/calc/postfix.c @@ -8,526 +8,541 @@ * in file LICENSE that is included with this distribution. \*************************************************************************/ /* $Id$ + * * Subroutines used to convert an infix expression to a postfix expression * * Author: Bob Dalesio * Date: 12-12-86 */ - -/* - * Subroutines - * - * Public - * - * postfix convert an algebraic expression to symbolic postfix - * args - * pinfix the algebraic expression - * ppostfix the symbolic postfix expression - * returns - * 0 successful - * -1 not successful - * Private routines for postfix - * - * find_element finds a symbolic element in the expression element tbl - * args - * pbuffer pointer to the infox expression element - * pelement pointer to the expression element table entry - * pno_bytes pointer to the size of this element - * 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 - * plink pointer to a resolved database reference (N/A) - * returns - * FINE found an expression element - * VARIABLE found a database reference - * UNKNOWN_ELEMENT unknown element found in the infix expression - * match_element finds an alpha element in the expression table - * args - * pbuffer pointer to an alpha expression element - * pelement pointer to the expression element table - * returns - * TRUE found the element in the element table - * FLASE expression element not found - */ - -#include -#include -#include + +#include +#include +#include + +#include "dbDefs.h" +#include "epicsStdlib.h" +#include "epicsString.h" #define epicsExportSharedSymbols -#include "dbDefs.h" -#include "epicsStdlib.h" -#include "postfix.h" -#include "postfixPvt.h" +#include "postfix.h" +#include "postfixPvt.h" +#include "shareLib.h" + - /* 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 SEPERATOR 8 -#define TRASH 9 -#define FLOAT_PT_CONST 10 -#define MINUS_OPERATOR 11 +typedef enum { + OPERAND, + LITERAL_OPERAND, + STORE_OPERATOR, + UNARY_OPERATOR, + BINARY_OPERATOR, + SEPERATOR, + CLOSE_PAREN, + CONDITIONAL, + EXPR_TERMINATOR, +} element_type; -#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 +/* 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 */ -}; +typedef struct expression_element{ + char *name; /* character representation of an element */ + char in_stack_pri; /* priority on translation stack */ + char in_coming_pri; /* priority in input string */ + signed char runtime_effect; /* stack change, positive means push */ + element_type type; /* element type */ + rpn_opcode code; /* postfix opcode */ +} ELEMENT; /* - * 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 + * NOTE: Keep these lists sorted. Elements are searched in reverse order, + * and where two names start with the same substring we must pick out the + * longest name first (hence the sort requirement). */ -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}, /* maximum of 2 args */ -{"MIN", 7, 8, UNARY_OPERATOR, MIN}, /* 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 */ -{"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 */ -{"!", 7, 8, UNARY_OPERATOR, REL_NOT}, /* not */ -{"~", 7, 8, UNARY_OPERATOR, BIT_NOT}, /* and */ -{"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 */ -{"A", 0, 0, OPERAND, FETCH_A}, /* fetch var A */ -{"B", 0, 0, OPERAND, FETCH_B}, /* fetch var B */ -{"C", 0, 0, OPERAND, FETCH_C}, /* fetch var C */ -{"D", 0, 0, OPERAND, FETCH_D}, /* fetch var D */ -{"E", 0, 0, OPERAND, FETCH_E}, /* fetch var E */ -{"F", 0, 0, OPERAND, FETCH_F}, /* fetch var F */ -{"G", 0, 0, OPERAND, FETCH_G}, /* fetch var G */ -{"H", 0, 0, OPERAND, FETCH_H}, /* fetch var H */ -{"I", 0, 0, OPERAND, FETCH_I}, /* fetch var I */ -{"J", 0, 0, OPERAND, FETCH_J}, /* fetch var J */ -{"K", 0, 0, OPERAND, FETCH_K}, /* fetch var K */ -{"L", 0, 0, OPERAND, FETCH_L}, /* fetch var L */ -{"0", 0, 0, FLOAT_PT_CONST, CONSTANT}, /* flt pt constant */ -{"1", 0, 0, FLOAT_PT_CONST, CONSTANT}, /* flt pt constant */ -{"2", 0, 0, FLOAT_PT_CONST, CONSTANT}, /* flt pt constant */ -{"3", 0, 0, FLOAT_PT_CONST, CONSTANT}, /* flt pt constant */ -{"4", 0, 0, FLOAT_PT_CONST, CONSTANT}, /* flt pt constant */ -{"5", 0, 0, FLOAT_PT_CONST, CONSTANT}, /* flt pt constant */ -{"6", 0, 0, FLOAT_PT_CONST, CONSTANT}, /* flt pt constant */ -{"7", 0, 0, FLOAT_PT_CONST, CONSTANT}, /* flt pt constant */ -{"8", 0, 0, FLOAT_PT_CONST, CONSTANT}, /* flt pt constant */ -{"9", 0, 0, FLOAT_PT_CONST, CONSTANT}, /* flt pt constant */ -{".", 0, 0, FLOAT_PT_CONST, CONSTANT}, /* flt pt constant */ -{"?", 0, 0, CONDITIONAL, COND_IF}, /* conditional */ -{":", 0, 0, CONDITIONAL, COND_ELSE}, /* else */ -{"(", 0, 8, UNARY_OPERATOR, PAREN}, /* open paren */ -{"^", 6, 6, BINARY_OPERATOR,EXPON}, /* exponentiation */ -{"**", 6, 6, BINARY_OPERATOR,EXPON}, /* exponentiation */ -{"+", 4, 4, BINARY_OPERATOR,ADD}, /* addition */ -#if 0 -{"-", 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, SEPERATOR, COMMA}, /* comma */ -{")", 0, 0, CLOSE_PAREN, PAREN}, /* close paren */ -{"||", 1, 1, BINARY_OPERATOR,REL_OR}, /* or */ -{"|", 1, 1, BINARY_OPERATOR,BIT_OR}, /* or */ -{"&&", 2, 2, BINARY_OPERATOR,REL_AND}, /* and */ -{"&", 2, 2, BINARY_OPERATOR,BIT_AND}, /* 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 */ -{""} +static const ELEMENT operands[] = { +/* name prio's stack element type opcode */ +{"!", 7, 8, 0, UNARY_OPERATOR, REL_NOT}, +{"(", 0, 8, 0, UNARY_OPERATOR, NOT_GENERATED}, +{"-", 7, 8, 0, UNARY_OPERATOR, UNARY_NEG}, +{".", 0, 0, 1, LITERAL_OPERAND,LITERAL}, +{"0", 0, 0, 1, LITERAL_OPERAND,LITERAL}, +{"1", 0, 0, 1, LITERAL_OPERAND,LITERAL}, +{"2", 0, 0, 1, LITERAL_OPERAND,LITERAL}, +{"3", 0, 0, 1, LITERAL_OPERAND,LITERAL}, +{"4", 0, 0, 1, LITERAL_OPERAND,LITERAL}, +{"5", 0, 0, 1, LITERAL_OPERAND,LITERAL}, +{"6", 0, 0, 1, LITERAL_OPERAND,LITERAL}, +{"7", 0, 0, 1, LITERAL_OPERAND,LITERAL}, +{"8", 0, 0, 1, LITERAL_OPERAND,LITERAL}, +{"9", 0, 0, 1, LITERAL_OPERAND,LITERAL}, +{"A", 0, 0, 1, OPERAND, FETCH_A}, +{"ABS", 7, 8, 0, UNARY_OPERATOR, ABS_VAL}, +{"ACOS", 7, 8, 0, UNARY_OPERATOR, ACOS}, +{"ASIN", 7, 8, 0, UNARY_OPERATOR, ASIN}, +{"ATAN", 7, 8, 0, UNARY_OPERATOR, ATAN}, +{"ATAN2", 7, 8, -1, UNARY_OPERATOR, ATAN2}, +{"B", 0, 0, 1, OPERAND, FETCH_B}, +{"C", 0, 0, 1, OPERAND, FETCH_C}, +{"CEIL", 7, 8, 0, UNARY_OPERATOR, CEIL}, +{"COS", 7, 8, 0, UNARY_OPERATOR, COS}, +{"COSH", 7, 8, 0, UNARY_OPERATOR, COSH}, +{"D", 0, 0, 1, OPERAND, FETCH_D}, +{"D2R", 0, 0, 1, OPERAND, CONST_D2R}, +{"E", 0, 0, 1, OPERAND, FETCH_E}, +{"EXP", 7, 8, 0, UNARY_OPERATOR, EXP}, +{"F", 0, 0, 1, OPERAND, FETCH_F}, +{"FINITE", 7, 8, 0, UNARY_OPERATOR, FINITE}, +{"FLOOR", 7, 8, 0, UNARY_OPERATOR, FLOOR}, +{"G", 0, 0, 1, OPERAND, FETCH_G}, +{"H", 0, 0, 1, OPERAND, FETCH_H}, +{"I", 0, 0, 1, OPERAND, FETCH_I}, +{"INF", 0, 0, 1, LITERAL_OPERAND,LITERAL}, +{"ISINF", 7, 8, 0, UNARY_OPERATOR, ISINF}, +{"ISNAN", 7, 8, 0, UNARY_OPERATOR, ISNAN}, +{"J", 0, 0, 1, OPERAND, FETCH_J}, +{"K", 0, 0, 1, OPERAND, FETCH_K}, +{"L", 0, 0, 1, OPERAND, FETCH_L}, +{"LN", 7, 8, 0, UNARY_OPERATOR, LOG_E}, +{"LOG", 7, 8, 0, UNARY_OPERATOR, LOG_10}, +{"LOGE", 7, 8, 0, UNARY_OPERATOR, LOG_E}, +{"MAX", 7, 8, -1, UNARY_OPERATOR, MAX}, +{"MIN", 7, 8, -1, UNARY_OPERATOR, MIN}, +{"NINT", 7, 8, 0, UNARY_OPERATOR, NINT}, +{"NAN", 0, 0, 1, LITERAL_OPERAND,LITERAL}, +{"NOT", 7, 8, 0, UNARY_OPERATOR, BIT_NOT}, +{"PI", 0, 0, 1, OPERAND, CONST_PI}, +{"R2D", 0, 0, 1, OPERAND, CONST_R2D}, +{"RNDM", 0, 0, 1, OPERAND, RANDOM}, +{"SIN", 7, 8, 0, UNARY_OPERATOR, SIN}, +{"SINH", 7, 8, 0, UNARY_OPERATOR, SINH}, +{"SQR", 7, 8, 0, UNARY_OPERATOR, SQU_RT}, +{"SQRT", 7, 8, 0, UNARY_OPERATOR, SQU_RT}, +{"TAN", 7, 8, 0, UNARY_OPERATOR, TAN}, +{"TANH", 7, 8, 0, UNARY_OPERATOR, TANH}, +{"~", 7, 8, 0, UNARY_OPERATOR, BIT_NOT}, }; - -/* - * FIND_ELEMENT - * - * find the pointer to an entry in the element table - */ -static int find_element(pbuffer,pelement,pno_bytes) - char *pbuffer; - struct expression_element **pelement; - short *pno_bytes; - { - /* compare the string to each element in the element table */ - *pelement = &elements[0]; - while ((*pelement)->element[0] != '\0'){ - if (strncmp(pbuffer,(*pelement)->element, - strlen((*pelement)->element)) == 0){ - *pno_bytes += strlen((*pelement)->element); - return(TRUE); - } - *pelement += 1; - } - return(FALSE); - } - -/* - * GET_ELEMENT +static const ELEMENT operators[] = { +/* name prio's stack element type opcode */ +{"!=", 3, 3, -1, BINARY_OPERATOR,NOT_EQ}, +{"#", 3, 3, -1, BINARY_OPERATOR,NOT_EQ}, +{"%", 5, 5, -1, BINARY_OPERATOR,MODULO}, +{"&", 2, 2, -1, BINARY_OPERATOR,BIT_AND}, +{"&&", 2, 2, -1, BINARY_OPERATOR,REL_AND}, +{")", 0, 0, 0, CLOSE_PAREN, NOT_GENERATED}, +{"*", 5, 5, -1, BINARY_OPERATOR,MULT}, +{"**", 6, 6, -1, BINARY_OPERATOR,POWER}, +{"+", 4, 4, -1, BINARY_OPERATOR,ADD}, +{",", 0, 0, 0, SEPERATOR, NOT_GENERATED}, +{"-", 4, 4, -1, BINARY_OPERATOR,SUB}, +{"/", 5, 5, -1, BINARY_OPERATOR,DIV}, +{":", 0, 0, -1, CONDITIONAL, COND_ELSE}, +{":=", 0, 0, -1, STORE_OPERATOR, STORE_A}, +{";", 0, 0, 0, EXPR_TERMINATOR,NOT_GENERATED}, +{"<", 3, 3, -1, BINARY_OPERATOR,LESS_THAN}, +{"<<", 2, 2, -1, BINARY_OPERATOR,LEFT_SHIFT}, +{"<=", 3, 3, -1, BINARY_OPERATOR,LESS_OR_EQ}, +{"=", 3, 3, -1, BINARY_OPERATOR,EQUAL}, +{"==", 3, 3, -1, BINARY_OPERATOR,EQUAL}, +{">", 3, 3, -1, BINARY_OPERATOR,GR_THAN}, +{">=", 3, 3, -1, BINARY_OPERATOR,GR_OR_EQ}, +{">>", 2, 2, -1, BINARY_OPERATOR,RIGHT_SHIFT}, +{"?", 0, 0, -1, CONDITIONAL, COND_IF}, +{"AND", 2, 2, -1, BINARY_OPERATOR,BIT_AND}, +{"OR", 1, 1, -1, BINARY_OPERATOR,BIT_OR}, +{"XOR", 1, 1, -1, BINARY_OPERATOR,BIT_EXCL_OR}, +{"^", 6, 6, -1, BINARY_OPERATOR,POWER}, +{"|", 1, 1, -1, BINARY_OPERATOR,BIT_OR}, +{"||", 1, 1, -1, BINARY_OPERATOR,REL_OR}, +}; + + +/* get_element * - * get an expression element + * find the next expression element in the infix expression */ -static int get_element(pinfix,pelement,pno_bytes) -char *pinfix; -struct expression_element **pelement; -short *pno_bytes; +static int + get_element(int opnd, const char **ppsrc, const ELEMENT **ppel) { + const ELEMENT *ptable, *pel; - /* get the next expression element from the infix expression */ - if (*pinfix == '\0') return(END); - *pno_bytes = 0; - while (*pinfix == 0x20){ - *pno_bytes += 1; - pinfix++; + *ppel = NULL; + + while (isspace(**ppsrc)) ++*ppsrc; + if (**ppsrc == '\0') return FALSE; + + if (opnd) { + ptable = operands; + pel = ptable + NELEMENTS(operands) - 1; + } else { + ptable = operators; + pel = ptable + NELEMENTS(operators) - 1; + } + + while (pel >= ptable) { + int len = strlen(pel->name); + if (epicsStrnCaseCmp(*ppsrc, pel->name, len) == 0) { + *ppel = pel; + *ppsrc += len; + return TRUE; } - if (*pinfix == '\0') return(END); - if (!find_element(pinfix,pelement,pno_bytes)) - return(UNKNOWN_ELEMENT); - return(FINE); - - + --pel; + } + return FALSE; } - -/* - * POSTFIX + + +/* postfix * * convert an infix expression to a postfix expression */ -#define MAX_POSTFIX_SIZE 100 -long epicsShareAPI postfix(const char *pin,char *ppostfix,short *perror) +epicsShareFunc long + postfix(const char *psrc, char * const ppostfix, short *perror) { - short no_bytes; - short operand_needed; - short new_expression; - struct expression_element stack[80]; - struct expression_element *pelement; - struct expression_element *pstacktop; - double constant; - char *pposthold, *pc; - char in_stack_pri, in_coming_pri, code; - char *ppostfixStart = ppostfix; - char infix[MAX_POSTFIX_SIZE]; - char *pinfix = &infix[0]; - int len; + ELEMENT stack[80]; + ELEMENT *pstacktop = stack; + const ELEMENT *pel; + int operand_needed = TRUE; + int runtime_depth = 0; + int cond_count = 0; + char *pout = ppostfix; + char *pnext; + double constant; - len = strlen(pin); - if(len>=MAX_POSTFIX_SIZE) return(-1); - strcpy(infix,pin); - /* convert infix expression to upper case */ - for (pc=pinfix; *pc; pc++) { - if (islower((int)*pc)) *pc = toupper((int)*pc); - } + if (psrc == NULL || *psrc == '\0' || + pout == NULL || perror == NULL) { + if (perror) *perror = CALC_ERR_NULL_ARG; + if (pout) *pout = END_EXPRESSION; + return -1; + } - /* place the expression elements into postfix */ - operand_needed = TRUE; - new_expression = TRUE; - *ppostfix = END_STACK; - *perror = 0; - if (* pinfix == 0 ) - return(0); - pstacktop = stack; - while (get_element(pinfix,&pelement,&no_bytes) != END){ - pinfix += no_bytes; - switch (pelement->type){ + /* place the expression elements into postfix */ + *pout = END_EXPRESSION; + *perror = CALC_ERR_NONE; - case OPERAND: - if (!operand_needed){ - *perror = 5; - *ppostfixStart = BAD_EXPRESSION; return(-1); - } + while (get_element(operand_needed, &psrc, &pel)) { + switch (pel->type) { - /* add operand to the expression */ - *ppostfix++ = pelement->code; + case OPERAND: + *pout++ = pel->code; + runtime_depth += pel->runtime_effect; + operand_needed = FALSE; + break; - operand_needed = FALSE; - new_expression = FALSE; - break; + case LITERAL_OPERAND: + *pout++ = pel->code; + runtime_depth += pel->runtime_effect; - case FLOAT_PT_CONST: - if (!operand_needed){ - *perror = 5; - *ppostfixStart = BAD_EXPRESSION; return(-1); - } - - /* add constant to the 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 ( epicsScanDouble(ppostfix, &constant) != 1) { - *ppostfix = '\0'; - } else { - memcpy(ppostfix,(void *)&constant,8); - } - ppostfix+=8; - - 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 notation */ - while ((pstacktop >= stack+1) && - (pstacktop->in_stack_pri >= pelement->in_coming_pri)) { - *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 notation */ - while ((pstacktop >= stack+1) && - (pstacktop->in_stack_pri >= pelement->in_coming_pri)) { - *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 notation */ - while ((pstacktop >= stack+1) && - (pstacktop->in_stack_pri >= in_coming_pri)) { - *ppostfix++ = pstacktop->code; - pstacktop--; - } - - /* add new operator to stack */ - pstacktop++; - *pstacktop = *pelement; - pstacktop->in_stack_pri = in_stack_pri; - pstacktop->code = code; - - break; - - case SEPERATOR: - if (operand_needed){ - *perror = 4; - *ppostfixStart = BAD_EXPRESSION; return(-1); - } - - /* add operators to postfix until open paren */ - while (pstacktop->element[0] != '('){ - if (pstacktop == stack+1 || - pstacktop == stack){ - *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){ - *perror = 6; - *ppostfixStart = BAD_EXPRESSION; return(-1); - } - *ppostfix++ = pstacktop->code; - pstacktop--; - } - pstacktop--; /* remove ( from stack */ - break; - - case CONDITIONAL: - if (operand_needed){ - *perror = 4; - *ppostfixStart = BAD_EXPRESSION; return(-1); - } - - /* add operators of higher priority to */ - /* postfix notation */ - while ((pstacktop >= stack+1) && - (pstacktop->in_stack_pri > pelement->in_coming_pri)) { - *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); + psrc -= strlen(pel->name); + constant = epicsStrtod(psrc, &pnext); + if (pnext == psrc) { + *perror = CALC_ERR_BAD_LITERAL; + goto bad; } - } - if (operand_needed){ - *perror = 4; - *ppostfixStart = BAD_EXPRESSION; return(-1); - } + psrc = pnext; + memcpy(pout, (void *)&constant, sizeof(double)); + pout += sizeof(double); - /* add all operators on stack to postfix */ - while (pstacktop >= stack+1){ - if (pstacktop->element[0] == '('){ - *perror = 6; - *ppostfixStart = BAD_EXPRESSION; return(-1); + operand_needed = FALSE; + break; + + case STORE_OPERATOR: + if (pout == ppostfix || pstacktop > stack || + *--pout < FETCH_A || *pout > FETCH_L) { + *perror = CALC_ERR_BAD_ASSIGNMENT; + goto bad; } - *ppostfix++ = pstacktop->code; - pstacktop--; - } - *ppostfix = END_STACK; + /* Convert fetch into a store on the stack */ + *++pstacktop = *pel; + pstacktop->code = STORE_A + *pout - FETCH_A; + runtime_depth -= 1; + operand_needed = TRUE; + break; - return(0); + case UNARY_OPERATOR: + /* Move operators of >= priority to the output */ + while ((pstacktop > stack) && + (pstacktop->in_stack_pri >= pel->in_coming_pri)) { + *pout++ = pstacktop->code; + runtime_depth += pstacktop->runtime_effect; + pstacktop--; + } + + /* Push new operator onto stack */ + pstacktop++; + *pstacktop = *pel; + break; + + case BINARY_OPERATOR: + /* Move operators of >= priority to the output */ + while ((pstacktop > stack) && + (pstacktop->in_stack_pri >= pel->in_coming_pri)) { + *pout++ = pstacktop->code; + runtime_depth += pstacktop->runtime_effect; + pstacktop--; + } + + /* Push new operator onto stack */ + pstacktop++; + *pstacktop = *pel; + + operand_needed = TRUE; + break; + + case SEPERATOR: + /* Move operators to the output until open paren */ + while (pstacktop->name[0] != '(') { + if (pstacktop <= stack+1) { + *perror = CALC_ERR_BAD_SEPERATOR; + goto bad; + } + *pout++ = pstacktop->code; + runtime_depth += pstacktop->runtime_effect; + pstacktop--; + } + operand_needed = TRUE; + break; + + case CLOSE_PAREN: + /* Move operators to the output until matching paren */ + while (pstacktop->name[0] != '(') { + if (pstacktop <= stack+1) { + *perror = CALC_ERR_PAREN_NOT_OPEN; + goto bad; + } + *pout++ = pstacktop->code; + runtime_depth += pstacktop->runtime_effect; + pstacktop--; + } + pstacktop--; /* remove ( from stack */ + break; + + case CONDITIONAL: + /* Move operators of > priority to the output */ + while ((pstacktop > stack) && + (pstacktop->in_stack_pri > pel->in_coming_pri)) { + *pout++ = pstacktop->code; + runtime_depth += pstacktop->runtime_effect; + pstacktop--; + } + + /* Add new element to the output */ + *pout++ = pel->code; + runtime_depth += pel->runtime_effect; + + /* For : operator, also push COND_END code to stack */ + if (pel->name[0] == ':') { + if (--cond_count < 0) { + *perror = CALC_ERR_CONDITIONAL; + goto bad; + } + pstacktop++; + *pstacktop = *pel; + pstacktop->code = COND_END; + pstacktop->runtime_effect = 0; + } else { + cond_count++; + } + + operand_needed = TRUE; + break; + + case EXPR_TERMINATOR: + /* Move everything from stack to the output */ + while (pstacktop > stack) { + if (pstacktop->name[0] == '(') { + *perror = CALC_ERR_PAREN_OPEN; + goto bad; + } + *pout++ = pstacktop->code; + runtime_depth += pstacktop->runtime_effect; + pstacktop--; + } + + if (cond_count != 0) { + *perror = CALC_ERR_CONDITIONAL; + goto bad; + } + if (runtime_depth > 1) { + *perror = CALC_ERR_TOOMANY; + goto bad; + } + + operand_needed = TRUE; + break; + + default: + *perror = CALC_ERR_INTERNAL; + goto bad; + } + + if (runtime_depth < 0) { + *perror = CALC_ERR_UNDERFLOW; + goto bad; + } + if (runtime_depth >= CALCPERFORM_STACK) { + *perror = CALC_ERR_OVERFLOW; + goto bad; + } + } + + if (*psrc != '\0') { + *perror = CALC_ERR_SYNTAX; + goto bad; + } + + /* Move everything from stack to the output */ + while (pstacktop > stack) { + if (pstacktop->name[0] == '(') { + *perror = CALC_ERR_PAREN_OPEN; + goto bad; + } + *pout++ = pstacktop->code; + runtime_depth += pstacktop->runtime_effect; + pstacktop--; + } + *pout = END_EXPRESSION; + + if (cond_count != 0) { + *perror = CALC_ERR_CONDITIONAL; + goto bad; + } + if (operand_needed || runtime_depth != 1) { + *perror = CALC_ERR_INCOMPLETE; + goto bad; + } + return 0; + +bad: + *ppostfix = END_EXPRESSION; + return -1; +} + + +/* calcErrorStr + * + * Return a message string appropriate for the given error code + */ +epicsShareFunc const char * + calcErrorStr(short error) +{ + static const char *errStrs[] = { + "No error", + "Too many results returned", + "Badly formed numeric literal", + "Bad assignment target", + "Comma without enclosing parentheses", + "Close parenthesis found without open", + "Parenthesis still open at end of expression", + "Unbalanced conditional ?: operators", + "Incomplete expression, operand missing", + "Not enough operands provided", + "Runtime stack overflow", + "Syntax error, unknown operator/operand", + "NULL or empty input argument to postfix()", + "Internal error, unknown element type", + }; + + if (error < CALC_ERR_NONE || error > CALC_ERR_INTERNAL) + return NULL; + return errStrs[error]; +} + + +/* calcExprDump + * + * Disassemble the given postfix instructions to stdout + */ +epicsShareFunc void + calcExprDump(const char *pinst) +{ + static const char *opcodes[] = { + "End Expression", + /* Operands */ + "LITERAL", + "FETCH_A", "FETCH_B", "FETCH_C", "FETCH_D", "FETCH_E", "FETCH_F", + "FETCH_G", "FETCH_H", "FETCH_I", "FETCH_J", "FETCH_K", "FETCH_L", + /* Assignment */ + "STORE_A", "STORE_B", "STORE_C", "STORE_D", "STORE_E", "STORE_F", + "STORE_G", "STORE_H", "STORE_I", "STORE_J", "STORE_K", "STORE_L", + /* Trigonometry Constants */ + "CONST_PI", + "CONST_D2R", + "CONST_R2D", + /* Arithmetic */ + "UNARY_NEG", + "ADD", + "SUB", + "MULT", + "DIV", + "MODULO", + "POWER", + /* Algebraic */ + "ABS_VAL", + "EXP", + "LOG_10", + "LOG_E", + "MAX", + "MIN", + "SQU_RT", + /* Trigonometric */ + "ACOS", + "ASIN", + "ATAN", + "ATAN2", + "COS", + "COSH", + "SIN", + "SINH", + "TAN", + "TANH", + /* Numeric */ + "CEIL", + "FLOOR", + "FINITE", + "ISINF", + "ISNAN", + "NINT", + "RANDOM", + /* Boolean */ + "REL_OR", + "REL_AND", + "REL_NOT", + /* Bitwise */ + "BIT_OR", + "BIT_AND", + "BIT_EXCL_OR", + "BIT_NOT", + "RIGHT_SHIFT", + "LEFT_SHIFT", + /* Relationals */ + "NOT_EQ", + "LESS_THAN", + "LESS_OR_EQ", + "EQUAL", + "GR_OR_EQ", + "GR_THAN", + /* Conditional */ + "COND_IF", + "COND_ELSE", + "COND_END", + /* Misc */ + "NOT_GENERATED" + }; + char op; + while ((op = *pinst) != END_EXPRESSION) { + if (op == LITERAL) { + double lit; + memcpy((void *)&lit, ++pinst, sizeof(double)); + printf("\tLiteral: %g\n", lit); + pinst += sizeof(double); + } else { + printf("\t%s\n", opcodes[(int) op]); + pinst++; + } + } } diff --git a/src/libCom/calc/postfix.h b/src/libCom/calc/postfix.h index 7ac043b25..2e378d07c 100644 --- a/src/libCom/calc/postfix.h +++ b/src/libCom/calc/postfix.h @@ -16,15 +16,61 @@ #define INCpostfixh #include "shareLib.h" + +#define CALCPERFORM_NARGS 12 +#define CALCPERFORM_STACK 80 + +#define INFIX_TO_POSTFIX_SIZE(n) (((n-2)>>2)*21 + ((n-2)&2)*5 + 10) +/* The above is calculated from the following expression fragments: + * 1?1: 4 chars expand to 21 chars + * 1+ 2 chars expand to 10 chars + * 1 1 char expands to 9 chars + * All other infix operators convert by a factor of 1:1 or less. + * Allow 1 char each for the infix and postfix terminators, + * and the infix must be a complete expression + */ + +#define MAX_INFIX_SIZE 100 +#define MAX_POSTFIX_SIZE INFIX_TO_POSTFIX_SIZE(MAX_INFIX_SIZE) + + +/* Error numbers from postfix */ + +#define CALC_ERR_NONE 0 /* No error */ +#define CALC_ERR_TOOMANY 1 /* Too many results returned */ +#define CALC_ERR_BAD_LITERAL 2 /* Bad numeric literal */ +#define CALC_ERR_BAD_ASSIGNMENT 3 /* Bad assignment target */ +#define CALC_ERR_BAD_SEPERATOR 4 /* Comma without parentheses */ +#define CALC_ERR_PAREN_NOT_OPEN 5 /* Close parenthesis without open */ +#define CALC_ERR_PAREN_OPEN 6 /* Open parenthesis at end of expression */ +#define CALC_ERR_CONDITIONAL 7 /* Unbalanced conditional ?: operators */ +#define CALC_ERR_INCOMPLETE 8 /* Incomplete expression, operand missing */ +#define CALC_ERR_UNDERFLOW 9 /* Runtime stack would underflow */ +#define CALC_ERR_OVERFLOW 10 /* Runtime stack would overflow */ +#define CALC_ERR_SYNTAX 11 /* Syntax error */ +#define CALC_ERR_NULL_ARG 12 /* NULL or empty input argument */ +#define CALC_ERR_INTERNAL 13 /* Internal error, bad element type */ +/* Changes in the above errors must also be made in calcErrorStr() */ + + #ifdef __cplusplus extern "C" { #endif -epicsShareFunc long epicsShareAPI - postfix (const char *pinfix, char *ppostfix, short *perror); +epicsShareFunc long + postfix(const char *pinfix, char *ppostfix, short *perror); -epicsShareFunc long epicsShareAPI - calcPerform(double *parg, double *presult, char *post); +epicsShareFunc long + calcPerform(double *parg, double *presult, const char *ppostfix); + +epicsShareFunc long + calcArgUsage(const char *ppostfix, unsigned long *pinputs, unsigned long *pstores); + +epicsShareFunc const char * + calcErrorStr(short error); + +epicsShareFunc void + calcExprDump(const char *pinst); #ifdef __cplusplus } diff --git a/src/libCom/calc/postfixPvt.h b/src/libCom/calc/postfixPvt.h index fc7c64c31..bfac987fb 100644 --- a/src/libCom/calc/postfixPvt.h +++ b/src/libCom/calc/postfixPvt.h @@ -12,90 +12,91 @@ * Date: 9-21-88 */ +/* Notes: + * 1. The FETCH_A through FETCH_L and STORE_A through STORE_L opcodes must + * be contiguous. + * 2. The LITERAL opcode is followed by its double value. + * 3. You can't quite use strlen() on an RPN buffer, since the literal + * double value can contain nil bytes. + */ + #ifndef INCpostfixPvth #define INCpostfixPvth -#include "shareLib.h" -/* defines for element table */ -#define BAD_EXPRESSION 0 -#define FETCH_A 1 -#define FETCH_B 2 -#define FETCH_C 3 -#define FETCH_D 4 -#define FETCH_E 5 -#define FETCH_F 6 -#define FETCH_G 7 -#define FETCH_H 8 -#define FETCH_I 9 -#define FETCH_J 10 -#define FETCH_K 11 -#define FETCH_L 12 -#define ACOS 13 -#define ASIN 14 -#define ATAN 15 -#define COS 16 -#define COSH 17 -#define SIN 18 -#define STORE_A 19 -#define STORE_B 20 -#define STORE_C 21 -#define STORE_D 22 -#define STORE_E 23 -#define STORE_F 24 -#define STORE_G 25 -#define STORE_H 26 -#define STORE_I 27 -#define STORE_J 28 -#define STORE_K 29 -#define STORE_L 30 -#define RIGHT_SHIFT 31 -#define LEFT_SHIFT 32 -#define SINH 33 -#define TAN 34 -#define TANH 35 -#define LOG_2 36 -#define COND_ELSE 37 -#define ABS_VAL 38 -#define UNARY_NEG 39 -#define SQU_RT 40 -#define EXP 41 -#define CEIL 42 -#define FLOOR 43 -#define LOG_10 44 -#define LOG_E 45 -#define RANDOM 46 -#define ADD 47 -#define SUB 48 -#define MULT 49 -#define DIV 50 -#define EXPON 51 -#define MODULO 52 -#define BIT_OR 53 -#define BIT_AND 54 -#define BIT_EXCL_OR 55 -#define GR_OR_EQ 56 -#define GR_THAN 57 -#define LESS_OR_EQ 58 -#define LESS_THAN 59 -#define NOT_EQ 60 -#define EQUAL 61 -#define REL_OR 62 -#define REL_AND 63 -#define REL_NOT 64 -#define BIT_NOT 65 -#define PAREN 66 -#define MAX 67 -#define MIN 68 -#define COMMA 69 -#define COND_IF 70 -#define COND_END 71 -#define CONSTANT 72 -#define CONST_PI 73 -#define CONST_D2R 74 -#define CONST_R2D 75 -#define NINT 76 -#define ATAN2 77 -#define END_STACK 127 +/* RPN opcodes */ +typedef enum { + END_EXPRESSION = 0, + /* Operands */ + LITERAL, + FETCH_A, FETCH_B, FETCH_C, FETCH_D, FETCH_E, FETCH_F, + FETCH_G, FETCH_H, FETCH_I, FETCH_J, FETCH_K, FETCH_L, + /* Assignment */ + STORE_A, STORE_B, STORE_C, STORE_D, STORE_E, STORE_F, + STORE_G, STORE_H, STORE_I, STORE_J, STORE_K, STORE_L, + /* Trigonometry Constants */ + CONST_PI, + CONST_D2R, + CONST_R2D, + /* Arithmetic */ + UNARY_NEG, + ADD, + SUB, + MULT, + DIV, + MODULO, + POWER, + /* Algebraic */ + ABS_VAL, + EXP, + LOG_10, + LOG_E, + MAX, + MIN, + SQU_RT, + /* Trigonometric */ + ACOS, + ASIN, + ATAN, + ATAN2, + COS, + COSH, + SIN, + SINH, + TAN, + TANH, + /* Numeric */ + CEIL, + FLOOR, + FINITE, + ISINF, + ISNAN, + NINT, + RANDOM, + /* Boolean */ + REL_OR, + REL_AND, + REL_NOT, + /* Bitwise */ + BIT_OR, + BIT_AND, + BIT_EXCL_OR, + BIT_NOT, + RIGHT_SHIFT, + LEFT_SHIFT, + /* Relationals */ + NOT_EQ, + LESS_THAN, + LESS_OR_EQ, + EQUAL, + GR_OR_EQ, + GR_THAN, + /* Conditional */ + COND_IF, + COND_ELSE, + COND_END, + /* Misc */ + NOT_GENERATED +} rpn_opcode; #endif /* INCpostfixPvth */