home *** CD-ROM | disk | FTP | other *** search
- /*
-
- INTRODUCTION
-
- This is a generic expression evaluator which is intended to be used
- by any program which requires one.
-
- It is intended to be expandable and allows the use of variables by user
- written routines which can be modified according to the specific program
- being used. An error routine may also be specified.
-
- Users are expected to call two routines externally, the first is the
- initialisation routine which reads a data file (which may form part of
- a larger data file from the external program) and which should only be
- called once, the second is the expression evaluator which takes a string
- and returns an integer, it will also update the string so that the first
- character not forming part of the expression is returned.
-
- Strings may be passed in non-tokenised form which is slow, or may be
- tokenised before being passed which results in faster evaluation. There
- is a tokeniser routine which may be called externally to tokenise
- expressions in loaders or compilers etc.
-
- The evaluator allows the use of functions, constants, and unary and binary
- operators.
-
- ROUTINES to be used are as follows:
-
- exeinit initialises the evaluator, reads operator data file,
- grabs memory as required.
-
- exetoken Takes an ascii string and produces a tokenised output,
- it is not essential to tokenise strings as the evaluator can
- do it for you.
-
- exeval Evaluate an expression. Pass a string pointer to a pointer
- containing the expression and the result is returned as
- an int. The string pointer is updated to point to the first
- character after the expression whether it is tokenised or
- not.
-
- The routine returns a pointer to an operand structure which
- indicates the type of return (string or integer), and contains
- the return value, which in the case of a string is the pointer
- to it.
-
- ROUTINES to be provided by the user
-
- void errfatal(char *)
-
- fatal error handler must be written by the user, and must
- exit the program printing the supplied string.
-
- void err(char *)
-
- error handler, non fatal, must be written by the user, and
- return to the calling routine.
-
- struct op *getvar(char *)
-
- Get a variable pointed to by the string and return its value,
- getvar should contain a local static op structure whose
- address is returned.
-
- EXPANDING the program
-
- The program may be expanded or modified to handle different functions or
- existing functions in a different way.
-
- Constants may be added without changing the program at all, simply changing
- EXOP.DAT
-
- EXOP.DAT which may be incorporated in a user data file must be modified,
- the information contained in it is the string, priority, type and number
- of each operator. The number of the operator may be substituted below.
-
- Operators and functions are dealt with in the body of the evalstr routine.
-
-
- */
-
- #include "datapriv.hpp"
-
- /*
-
- This file is the data required for the expression evaluator.
- */
-
- /*
-
- This file contains data in the form
-
- LITERAL,TYPE,PRIORITY,TYPE of 1st OP,TYPE of 2nd OP,Type Result,No.
-
- Type is : 0, Constant (pri=value)
- 1, Unary operator
- 2, Binary Operator
- 3, Both types (e.g. -)
- >=4, Function, PRI=no. of parameters
-
- Note for a function the type represents the number of optional
- parameters, so 1 optional parameter has a type of 5, optional
- parameters with no value supplied are inserted as integer -1.
-
- Type-1 & Type-2 are the types of operand, 1=num, 2=string, 4=Date, 8=Logical
-
- LIT TYPE PRI Type-1 Type-2 Type-Result
-
- */
-
- struct opinf oppt[]=
- {
- "(",4,9,0,0,0,0,
- ")",4,9,0,0,0,1,
- "+",3,5,7,7,1,2,
- "-",3,5,7,7,1,3,
- "**",2,7,1,1,1,4,
- "*",2,6,1,1,1,5,
- "/",2,6,1,1,1,6,
- ".AND.",2,2,8,8,8,7,
- ".OR.",2,1,8,8,8,8,
- ".NOT.",1,3,8,8,8,9,
- "=",2,4,15,15,8,10,
- "<>",2,4,15,15,8,11,
- ">=",2,4,7,7,8,12,
- "<=",2,4,7,7,8,13,
- ">",2,4,7,7,8,14,
- "<",2,4,7,7,8,15,
- "$",2,4,2,2,8,16,
- "ABS",4,1,1,0,1,17,
- "ASC",4,1,2,0,1,18,
- "AT",4,2,2,2,1,19,
- "CDOW",4,1,4,0,2,20,
- "CHR",4,1,1,0,2,21,
- "CMONTH",4,1,4,0,2,22,
- "CTOD",4,1,2,0,4,23,
- "DATE",4,0,0,0,4,24,
- "DAY",4,1,4,0,1,25,
- "DOW",4,1,4,0,1,26,
- "DTOC",4,1,4,0,2,27,
- "DTOS",4,1,4,0,2,28,
- "IIF",4,3,8,15,0,29,
- "INT",4,1,1,0,1,30,
- "ISALPHA",4,1,2,0,8,31,
- "ISDIGIT",4,1,2,0,8,32,
- "ISLOWER",4,1,2,0,8,33,
- "ISUPPER",4,1,2,0,8,34,
- "LEFT",4,2,2,1,2,35,
- "LEN",5,1,2,0,1,36,
- "LOWER",4,1,2,0,2,37,
- "LTRIM",4,1,2,0,2,38,
- "MAX",4,2,5,5,0,39,
- "MIN",4,2,5,5,0,40,
- "MOD",4,2,1,1,1,41,
- "MONTH",4,1,4,0,1,42,
- "RECCOUNT",4,0,0,0,1,43,
- "RECNO",4,0,0,0,1,44,
- "RECSIZE",4,0,0,0,1,45,
- "REPLICATE",4,2,2,1,2,46,
- "RIGHT",4,2,2,1,2,47,
- "ROUND",4,2,1,1,1,48,
- "RTRIM",4,1,2,0,2,49,
- "SOUNDEX",4,1,2,0,2,50,
- "SPACE",4,1,1,0,2,51,
- "STR",6,1,1,1,2,52,
- "STUFF",4,4,2,1,2,53,
- "SUBSTR",5,2,2,1,2,54,
- "SWAPDATA",4,1,2,0,2,55,
- "TIME",4,0,0,0,2,56,
- "TRIM",4,1,2,0,2,57,
- "TYPE",4,1,15,0,0,58,
- "UPPER",4,1,2,0,2,59,
- "VAL",4,1,2,0,1,60,
- "YEAR",4,1,4,0,1,61,
- "^",2,7,1,1,1,62,
- "#",2,4,15,15,8,63
- };
-
-
- // Number of operators
-
- int nop=64;
-
- // Lookup table for end of array and spaces
- // bit 0=space, bit 1=end of expr.
-
- // End chars are 0 \n , : ; ) '
-
- // Space chars are 32 and 9
-
- char sparray[]=
- {
- // 0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f
-
- 2,0,0,0,0,0,0,0,0,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00-1f
- 1,0,0,0,0,0,0,2,0,2,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,0,0,0,0, // 20-3f
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40-5f
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 // 60-7f
- };
-
-
- /**************************************************************************
-
- Initialisation routine, this takes a file pointer and loads the file of
- operator definitions. This file is by default included in EXOP.DAT.
- The format of the file is as follows:
-
-
- #
- STRING,TYPE,PRIORITY,TYPE1,TYPE2,NUMBER
- STRING,TYPE,PRIORITY,TYPE1,TYPE2,NUMBER
- .
- .
- .
- #
-
- Characters up to the first '#' are ignored, there should be no spurious
- spaces or tabs within the data.
-
- STRING is the literal for the operator, e.g. +,-,AND.
-
- TYPE is the type, 0=constant, 1=Unary op, 2=Binary op.,
- 3=binary or unary op, 4=function.
-
- PRIORITY for a BINARY operator is the priority, higher number=higher priority
- (the priority must be greater than 0).
- for a CONSTANT this number is the value of the constant.
- for a FUNCTION this number is the number of arguments.
-
- TYPE1 is the type of the single operand for functions and unary operators
- and the type of first operand (the left operand) for binary. Type
- is 1 for integer, 2 for string or 3 for either.
-
- TYPE2 only has meaning for binary operators where it is the type of the
- second operand (after the operator).
-
- NUMBER is the index of the operator and should be used by the tokeniser.
-
- This also initialises SPARRAY, which contains special character information
- as follows:
-
- If a character (n) is not special then sparry[n]=0;
-
- Bit 0 of sparray[n]=space character.
- Bit 1 of sparray[n]=end of expression character.
-
- ***************************************************************************/
-
-
- expval::expval(database *dbi)
- {
- erflag=0;
- *(dbi->ers)=0;
- db=dbi;
-
- if (!(exwork=new char[EXWORKSIZE]))
- errfatal("No memory for expression evaluator information");
- }
-
- // Destructor
-
- expval::~expval()
- {
- if (exwork) delete exwork;
- }
-
-
- /**************************************************************************
-
- Tokeniser
-
- This routine tokenises an input string and passes the result to an output
- string, it is useful for input scanners, but is also used by the expression
- evaluator when an untokenised expression is passed.
-
- The tokenised expression consists of a number of fields each of which starts
- with a byte:
-
- EXSTART Indicates the 1st byte of the expression
- EXINT Indicates an integer follows in 2 bytes.
- EXVAR Indicates a variable, followed by the variable name which is
- terminated by a 0 byte.
- EXSTR A zero terminated string.
- EXEND Last byte of expression.
- >=0 Indicates an operator, consists of the operator index.
-
- It speeds up the evaluator by pre-evaluating constants and integers.
-
- It should be passed two strings : s1 and s2
-
- s1 is a pointer to the input string
- s2 is a pointer to the output string which the tokenised result is written to
-
- The routine returns a pointer to the 1st character which is not in the
- expression. The expression is taken to end with one of the four EXEND
- characters defined in the header file.
-
-
- **************************************************************************/
-
- char *expval::exetoken(char *s1,char **s2)
- {
- char *ls1=s1,*ls2=*s2; // Pointer to next free byte in s2
- char ws[128],*wsp; // Useful space
- int c,i;
-
- *ls2++=EXSTART; /* Write in the expression flag */
-
- c=exescan(&ls1); // Get next character
-
- do
- {
- if (isdigit(c) || c==34 || (c=='.' && isdigit(*ls1))) // Number/String ?
- {
- if (c==34) /* A string */
- {
- *ls2++=EXSTR; c=exescan(&ls1,1);
- while(c!=34 && c) {*ls2++=c; c=exescan(&ls1,1);} *ls2++=0;
- if (c==34) c=exescan(&ls1);
- }
- else /* A number */
- {
- wsp=ws;
- while(isdigit(c) || (c=='.'&& isdigit(*ls1))) {*wsp++=c; c=exescan(&ls1);}
- *wsp=0; *ls2++=EXINT; (*(double *)ls2)=atof(ws); ls2+=sizeof(double);
- }
- }
- else /* expression or a variable */
- {
- if ((i=exegetop(c,&ls1))!=-1) /* Have we found an expression */
- {
- if (i==OBRAC)
- {
- ls1=exetoken(ls1,&ls2);
- if (*ls1!=')') {err("Unmatched Bracket !"); c=0;}
- else {ls1++; c=exescan(&ls1);}
- }
- else // Operator or function, not an open bracket
- {
- c=exescan(&ls1);
- if (oppt[i].type==CONSTANT)
- {*ls2++=EXINT; (*(double *)ls2)=oppt[i].pri; ls2+=sizeof(double);}
- else
- {
- *ls2++=i; // Some kind of operator
- if (oppt[i].type>=FUNCTION) // A function
- {
- if (c!='(') {err("Need a \'(\' for a function"); c=0;}
- else
- {
- for(int j=oppt[i].pri; j>0; j--) // Get n-1 parameters, ending in ,
- {
- ls1=exetoken(ls1,&ls2);
- if (j>1) // expect , between parameters
- {
- exescan(&ls1);
- if (*ls1!=',') {err("Expected a \',\'"); c=0; j=0;}
- else ls1++;
- }
- }
- for(j=0; j<oppt[i].type-4; j++) // Optional parameters
- {
- if (*ls1==',') {ls1++; ls1=exetoken(ls1,&ls2);}
- else {*ls2++=EXSTART; *ls2++=EXINT; (*(double *)ls2)=-1;
- ls2+=sizeof(double); *ls2++=EXEND;}
- }
- exescan(&ls1);
- if (*ls1!=')') {err("Needs a \')\' !"); c=0;}
- else {ls1++; c=exescan(&ls1);}
- }
- }
- }
- }
- }
- else /* No expression, field ? */
- {
- if (isalpha(c))
- {
- field *f;
- wsp=ws;
- while(isalpha(c) || c=='_') {*wsp++=c; c=exescan(&ls1);} *wsp++=0; /* field */
- f=db->getfield(ws);
- if (!f) {err("Invalid field"); c=0;}
- else {*ls2++=EXVAR; *(int *)ls2=f->number; ls2+=sizeof(int);}
- }
- else
- {err("Unrecognised Expression"); c=0;}
- }
- }
- }
- while(c);
-
- *ls2++=EXEND;
- *s2=ls2;
-
- return(ls1);
- }
-
- // Scan input line up to next space, or end of line
- // sflag is 1 if we are reading within inverted commas ("")
-
- int expval::exescan(char **in,int sflag)
- {
- int c;
-
- if (sflag) {if (**in=='\n' || !(**in)) return 0;}
- else {if (sparray[**in] & 2) return 0;}
-
- if (sflag) c=**in;
- else
- {
- while(sparray[**in] & 1) (*in)++; /* Eliminate leading spaces */
-
- c=(islower(**in)) ? **in-('a'-'A'): **in;
- }
-
- (*in)++;
- return c;
- }
-
- /* Check supplied string to see if it is an operator */
- // c is the first character
-
- int expval::exegetop(int c,char **s)
- {
- char ws[128],*wsp;
- char *ls=*s;
-
- wsp=ws;
- *wsp++=c;
- for(int i=1; i<MAXOPLEN; i++) ws[i]=exescan(&ls);
-
- struct opinf *lop; /* Local pointer */
-
- lop=oppt;
-
- for(i=0; i<nop; i++)
- {
- if (!strncmp(lop->lit,ws,strlen(lop->lit)))
- {*s+=strlen(lop->lit)-1; return(lop->num);}
- lop++;
- }
-
- return(-1);
- }
-
-
-
- /*
- This routine grabs size characters from the expression evaluator
- work space.
- */
-
- char *expval::exealloc(int size)
- {
- char *temp;
-
- temp=exworkp; exworkp+=size;
- if (exworkp>exwork+EXWORKSIZE-40) errfatal("Out of expression evaluator space");
-
- return(temp);
- }
-
- /*
- This is a properly implemented input routine
- s is the input buffer, n is the maximum number of characters.
-
- It returns the number of characters read.
- */
-
- int expval::exeinput(char *s,int n)
- {
- char *wsp,c;
-
- wsp=s;
-
- c=getch();
-
- while(c!=13)
- {
- if (!c) getch();
- else
- {
- if ((c==8) && (wsp!=s)) {wsp--; printf("\x08 \x08");}
- else if (c!=8 && (wsp-s<n)) {*wsp++=c; printf("%c",c);}
- }
- c=getch();
- }
- *wsp=0;
-
- return(wsp-s);
- }
-
- /***********************************************************************
-
- Comparison Routines
-
- ************************************************************************/
-
- void expval::execomp(op *fo,op *so,int type)
- {
- if (fo->optype!=so->optype) {err("Data types should match"); return;}
-
- float x;
-
- if (fo->optype==OPDATE)
- {
- x=atof(fo->date); fo->num=x; x=atof(so->date); so->num=x;
- }
-
- if (fo->optype==OPSTR) x=strcmpdb(fo->str,so->str);
- else x=fo->num-so->num;
-
- switch(type)
- {
- case NE2 :
- case NE : fo->num=x; break;
- case EQUAL : fo->num=!x; break;
- case GT : fo->num=(x>0); break;
- case LT : fo->num=(x<0); break;
- case GTE : fo->num=(x>=0); break;
- case LTE : fo->num=(x<=0); break;
- }
-
- if (fo->num) fo->num=-1;
- fo->optype=OPLOG;
- }
-
- /*************************************************************************
-
- DATE Functions
-
- **************************************************************************/
-
- // Get day of week (character)
-
- char *explitday[]={"Sunday","Monday","Tuesday","Wednesday",
- "Thursday","Friday","Saturday"};
-
- char *expval::cdow(char *date) {return explitday[dow(date)-1];}
-
- // Get day of week (numeric)
-
- int expval::dow(char *date) {return((long)(days(date)+5-DATEOFF)%7);}
-
- // Get month (character)
-
- char *explitmon[]={"January","February","March","April","May","June","July",
- "August","September","October","November","December"};
-
- char *expval::cmonth(char *date)
- {
- int x,y,z;
-
- gnums(date,x,y,z);
- return(explitmon[y-1]);
- }
-
- // Days since year 0 + dBase offset
-
- int expvaldm[]={0,31,59,90,120,151,182,213,243,274,304,334};
-
- long expval::days(char *date)
- {
- int d,m,y;
-
- gnums(date,d,m,y);
-
- int ly=(!(y%4) && (y%400)); // Leap year flag
-
- return((long)d+long(expvaldm[m-1])-(long)(ly && m<3)+
- (long)((long)y*1461L)/4L+DATEOFF);
- }
-
- // Convert days since year 0 into a date
-
- void expval::daystodate(char *date,long days)
- {
- int d,m,y;
- int dintoy;
-
- days-=DATEOFF; // dBase offset
-
- y=(long)((days*4L)/1461L);
- int ly=(!(y%4) && (y%400)); // Leap year flag
-
- dintoy=days-(long)((y*1461L)/4L);
-
- m=0; while(dintoy>expvaldm[m]-(ly && m<2)) m++;
-
- d=dintoy-expvaldm[m-1]+(ly && m<3);
- sprintf(date,"%04d%02d%02d",y,m,d);
- }
-
- // Convert character string to date (eg "19/11/62" in UKDATE format)
-
- void expval::ctod(char *date,char *s)
- {
- char *sp=s;
- int d,m,y;
-
- if (db->dateformat==UKDATE)
- {
- d=atoi(sp); while(isdigit(*sp++));
- m=atoi(sp); while(isdigit(*sp++));
- y=atoi(sp);
- }
- else
- {
- m=atoi(sp); while(isdigit(*sp++));
- d=atoi(sp); while(isdigit(*sp++));
- y=atoi(sp);
- }
-
- if (!y || !m || !d) {strcpy(date," / / "); return;}
-
- if (!(y/100)) y+=1900;
-
- sprintf(date,"%04d%02d%02d",y,m,d);
- }
-
- /******************** SOUNDEX *******************************************/
-
- // Provide soundex of character
-
- char sndx[]={0,1,2,3,0,1,2,0,0,2,2,4,5,5,0,1,2,6,2,3,0,1,0,2,0,2};
-
- char* FD soundex(char *d,char *s)
- {
- char sl[128];
- char *sp;
-
- sp=strupr(strncpy(sl,s,126)); sl[127]=0;
-
- strcpy(d,"0000");
- while(isspace(*sp)) sp++;
- if (!isalpha(*sp)) return d;
-
- *d=*sp++;
- for(int i=1; i<4; i++)
- {
- while(*sp && isalpha(*sp) && (!sndx[*sp-'A'] || *(sp+1)==*sp)) sp++;
- if (!*sp || !isalpha(*sp)) break;
- d[i]=sndx[(*sp++)-'A']+'0';
- }
- return d;
- }
-
- /******************** Error Handling ************************************/
-
- void expval::err(char *s)
- {
- strcpy(db->ers,s); erflag=1;
- }
-
- void expval::errfatal(char *s)
- {
- fprintf(stderr,"Expression evaluator fatal error - %s",s);
- erflag=2;
- }
-
-