/*** *eval.c - if expression evaluator * * Copyright (c) 1992-2001, Microsoft Corporation. All rights reserved. * *Purpose: * produce truth values and simplified conditions from compound if * statements. * *Revision History: * 09-30-92 MAL Original version * 10-13-93 SKS Recognize comments of the form /-*IFSTRIP=IGN*-/ to * override ifstrip behavior. * 09-01-94 SKS Add support for more operators: == != < > <= >= * Add terseflag (-t) to suppress mesgs about directives * 10-04-94 SKS Add support for more operators: EQ NE LT GT LE NE * @ is an identifier character (e.g., MASM's @Version) * 01-04-00 GB Add support for internal crt builds * *******************************************************************************/ #include #include #include #include #include "eval.h" /* Header for this module */ #include "symtab.h" /* Symbol table access */ #include "constant.h" /* Constants for tokens etc */ #include "errormes.h" /* Errors and Warning messages */ /* Types */ typedef struct condrec { int truth; char *condition; } condrec, *cond; /* Global variables */ extern int terseFlag; /* controls display of forced directives */ extern char **comments; /* Language dependent comment strings */ extern int *commlen; /* Lengths of above strings */ extern int nonumbers; /* allow numeric expressions */ extern enum {NON_CRT = 0, CRT = 1} progtype; char *operators[] = { "!", "(", ")", "||", "&&", "defined" , "==" , "!=" , "<" , ">" , "<=" , ">=" , "EQ" , "NE" , "LT" , "GT" , "LE" , "GE" }; int oplengths[] = { 1 , 1 , 1 , 2 , 2 , 7 , 2 , 2 , 1 , 1 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 }; /* # significant chars */ #define numoperators 18 /* These tokens must be in the same order as 'operators' */ #define NOT 0 #define OPENPARENTHESIS 1 #define CLOSEPARENTHESIS 2 #define OR 3 #define AND 4 #define DEFINEDFN 5 #define EQUALS 6 #define NOTEQUALS 7 #define LESSTHAN 8 #define LESSOREQ 9 #define GREATERTHAN 10 #define GREATEROREQ 11 #define EQUALS_ASM 12 #define NOTEQUALS_ASM 13 #define LESSTHAN_ASM 14 #define LESSOREQ_ASM 15 #define GREATERTHAN_ASM 16 #define GREATEROREQ_ASM 17 #define UINT 100 #define ID 101 #define ENDOFLINE 102 #define UNKNOWN 103 /* Global state */ /* String holding input, the current pointer into it, and the current token */ char conditions[MAXLINELEN], *tokenptr, currenttoken[MAXCONDLEN]; int token = -1; /* Function prototypes */ cond evaluateexpression(void); cond orexpression(void); cond andexpression(void); cond unaryexpression(void); cond parenthesesexpression(void); cond atomicexpression(void); cond createcondition(void); void destroycondition(cond); char *createstring(int); void destroystring(char *); void gettoken(void); int issymbolchar(char); /* CFW - added complex expression warning */ void evalwarn() { warning("cannot parse expression - ignoring", conditions); } /* * Comments may be in the input source which contain IFSTRIP Directives: * * For C source they should look like: * * #if foo>bar !*IFSTRIP=DEF*! '!' here stands for '/' * * and for Assembler source they should look like: * * if foo ;;IFSTRIP=UND;; * * Note that the directive and an optional preceding blank or tab * are deleted from the input source. The directive can thus be * followed by a backslash to continue the line, another comment, etc. */ static char IfStripCStr[] = "/*IFSTRIP="; /* -*IFSTRIP=IGN*- */ static char IfStripAStr[] = /*0123456789*- -* 0123456789012345 */ ";;IFSTRIP="; /* ;;IFSTRIP=IGN;; */ /* IGN may also be DEF or UND */ #define IFSTRIPVALOFF 10 #define IFSTRIPVALEND 13 #define IFSTRIPVALLEN 15 void evaluate(char *outputstring, int *value, char *inputstring) { int forcevalue = IGNORE; cond result; strcpy(conditions, inputstring); /* Prepare the string for tokenising */ tokenptr = conditions; gettoken(); /* Read in the first token of input */ result = evaluateexpression(); /* check for bad/complex expression */ if (token != ENDOFLINE) { char *adir = NULL; char *cdir = NULL; if(((cdir = strstr(inputstring, IfStripCStr)) && cdir[IFSTRIPVALEND] == '*' && cdir[IFSTRIPVALEND+1] == '/') || ((adir = strstr(inputstring, IfStripAStr)) && adir[IFSTRIPVALEND] == ';' && adir[IFSTRIPVALEND+1] == ';')) { char *pstr; char *ifstr; /* fprintf(stderr,"<< evaluate(): (%s)\n", inputstring); */ pstr = ifstr = ( adir ? adir : cdir ) ; /* * Have recognized the /-*-IFSTRIP= directive, interpret its argument * and remove the directive comment from the input/output text. * Back up exactly one white space character (blank or tab) if possible. */ if(pstr > inputstring && (pstr[-1] == '\t' || pstr[-1] == ' ')) -- pstr; if(!memcmp(ifstr+IFSTRIPVALOFF, "DEF", 3)) /* DEFINED */ forcevalue = DEFINED; else if(!memcmp(ifstr+IFSTRIPVALOFF, "UND", 3)) /* UNDEFINED */ forcevalue = UNDEFINED; else if(memcmp(ifstr+IFSTRIPVALOFF, "IGN", 3)) /* IGNORE */ warning("cannot recognize IFSTRIP: directive - ignoring", conditions); /* else "IGNORE" -- forcevalue is already set by default to IGNORE */ if(!terseFlag) warning("ifstrip directive forced evaluation", conditions); /* remove the directive comment (and preceding blank or tab) from the input line */ strcpy(pstr, ifstr + IFSTRIPVALLEN); /* "C" comments have closing -*-/- */ /* fprintf(stderr,">> evaluate(): (%s)\n", inputstring); */ } else evalwarn(); if (result) { destroycondition(result); result = NULL; } } /* bad/complex expression, return IGNORE and entire expression */ if (!result) { *value = forcevalue; strcpy(outputstring, inputstring); return; } *value = result -> truth; if(!result -> condition) * outputstring = '\0'; else strcpy(outputstring, result -> condition); /* Convert from internal to external representation */ destroycondition(result); } cond evaluateexpression() { return orexpression(); } cond orexpression() { cond condition1, condition2; char *output; condition1 = andexpression(); if (!condition1) return NULL; while (token == OR) { gettoken(); condition2 = andexpression(); if (!condition2) { destroycondition(condition1); return NULL; } switch (condition1 -> truth) { case DEFINED: /* DEFINED || x == DEFINED */ /* condition1 set up correctly for next pass */ destroycondition(condition2); break; case UNDEFINED: switch (condition2 -> truth) { case DEFINED: /* UNDEFINED || DEFINED == DEFINED */ destroycondition(condition1); condition1 = condition2; break; case UNDEFINED: /* UNDEFINED || UNDEFINED == UNDEFINED */ destroycondition(condition2); /* condition1 set up correctly for next pass */ break; case IGNORE: /* UNDEFINED || IGNORE == IGNORE */ destroycondition(condition1); condition1 = condition2; break; } break; case IGNORE: switch (condition2 -> truth) { case DEFINED: /* IGNORE || DEFINED == DEFINED */ destroycondition(condition1); condition1 = condition2; break; case UNDEFINED: /* IGNORE || UNDEFINED == IGNORE */ /* condition1 set up correctly for next pass */ destroycondition(condition2); break; case IGNORE: /* IGNORE || IGNORE == IGNORE */ output = createstring(strlen(condition1 -> condition) + strlen (condition2 -> condition) + (sizeof(" || ") - 1)); strcpy(output, condition1 -> condition); strcat(output, " || "); strcat(output, condition2 -> condition); /* Build up the condition string */ destroystring(condition1 -> condition); condition1 -> condition = output; /* Place the new string in condition1 */ destroycondition(condition2); break; } break; } } return condition1; } cond andexpression() { cond condition1, condition2; char *output; condition1 = unaryexpression(); if (!condition1) return NULL; while (token == AND) { gettoken(); condition2 = unaryexpression(); if (!condition2) { destroycondition(condition1); return NULL; } switch (condition1 -> truth) { case DEFINED: switch (condition2 -> truth) { case DEFINED: /* DEFINED && DEFINED == DEFINED */ destroycondition(condition2); /* condition1 set up correctly for next pass */ break; case UNDEFINED: /* DEFINED && UNDEFINED == UNDEFINED */ destroycondition(condition1); condition1 = condition2; break; case IGNORE: /* DEFINED && IGNORE == IGNORE */ destroycondition(condition1); condition1 = condition2; break; } break; case UNDEFINED: /* UNDEFINED && x == UNDEFINED */ /* condition1 set up correctly for next pass */ destroycondition(condition2); break; case IGNORE: switch (condition2 -> truth) { case DEFINED: /* IGNORE && DEFINED == IGNORE */ /* condition1 set up correctly for next pass */ destroycondition(condition2); break; case UNDEFINED: /* IGNORE && UNDEFINED == UNDEFINED */ destroycondition(condition1); condition1 = condition2; break; case IGNORE: /* IGNORE && IGNORE == IGNORE */ output = createstring(strlen(condition1 -> condition) + strlen (condition2 -> condition) + (sizeof(" && ") - 1)); strcpy(output, condition1 -> condition); strcat(output, " && "); strcat(output, condition2 -> condition); /* Build up the condition string */ destroystring(condition1 -> condition); condition1 -> condition = output; /* Place the new string in condition1 */ destroycondition(condition2); break; } break; } } return condition1; } cond unaryexpression() { cond condition1; char *output; switch (token) { case NOT: gettoken(); condition1 = unaryexpression(); if (!condition1) return NULL; if ((condition1 -> truth) == IGNORE) { output = createstring(strlen(condition1 -> condition) + 1); *output = '!'; strcpy(output + 1, condition1 -> condition); destroystring(condition1 -> condition); condition1 -> condition = output; } else condition1 -> truth = negatecondition(condition1 -> truth); break; case DEFINEDFN: gettoken(); condition1 = parenthesesexpression(); if (!condition1) return NULL; if ((condition1 -> truth) == IGNORE) { output = createstring(strlen(condition1 -> condition) + (sizeof("defined ") - 1)); strcpy(output, "defined "); strcat(output, condition1 -> condition); destroystring(condition1 -> condition); condition1 -> condition = output; } break; default: condition1 = parenthesesexpression(); if (!condition1) return NULL; break; } return condition1; } cond parenthesesexpression() { cond condition1; char *output; if (token == OPENPARENTHESIS) { gettoken(); condition1 = evaluateexpression(); if (!condition1) return NULL; if (token != CLOSEPARENTHESIS) { /* check for bad/complex expression */ evalwarn(); destroycondition(condition1); return NULL; } gettoken(); if ((condition1 -> truth) == IGNORE) { output = createstring(strlen(condition1 -> condition) + 2); *output = '('; strcpy(output + 1, condition1 -> condition); strcat(output, ")"); destroystring(condition1 -> condition); condition1 -> condition = output; } } else condition1 = atomicexpression(); return condition1; } cond atomicexpression() { cond condition1 = createcondition(); switch (token) { case UINT: if ( progtype == 1) condition1 -> truth = DEFINED; else condition1 -> truth = (atoi(currenttoken) == 0) ? UNDEFINED : DEFINED; break; case ID: condition1 -> truth = lookupsym(currenttoken); if ((condition1 -> truth) == NOTPRESENT) { warning("Switch unlisted - ignoring", currenttoken); condition1 -> truth = IGNORE; } if ((condition1 -> truth) == IGNORE) { condition1 -> condition = createstring(strlen(currenttoken)); strcpy(condition1 -> condition, currenttoken); } break; default: /* bad/complex expression */ evalwarn(); destroycondition(condition1); return NULL; break; } gettoken(); return condition1; } /* Negate condition (MAL) */ __inline int negatecondition(int condvalue) /* inline for speed */ { switch (condvalue) { case DEFINED: return UNDEFINED; case UNDEFINED: return DEFINED; default: return condvalue; }; } /* Allocate the memory for an empty condition structure and return a pointer to it */ __inline cond createcondition() { cond retvalue; retvalue = (cond) malloc(sizeof(condrec)); if (retvalue == NULL) error("Memory overflow",""); retvalue -> condition = NULL; return retvalue; } /* Destroy a condition structure */ __inline void destroycondition(cond condition1) { if (condition1 -> condition) free(condition1 -> condition); free(condition1); } /* Allocate the memory for a string of given length (not including terminator) and return the pointer */ __inline char *createstring(int length) { char *retvalue; retvalue = (char *) malloc(length + 1); if (retvalue == NULL) error("Memory overflow",""); return retvalue; } /* Destroy a string */ __inline void destroystring(char *string) { free(string); } int iscomment(char *tokenptr) { int cindex; for (cindex = 0; cindex < maxcomment; cindex++) { if (commlen[cindex] && !_strnicmp(tokenptr, comments[cindex], commlen[cindex])) return TRUE; } return FALSE; } void gettoken() { int numofwhitespace, comparetoken = 0, found = FALSE, isnumber = TRUE; char *digitcheck; numofwhitespace = strspn(tokenptr, " \t"); /* CFW - skips comments, assumes comment is last thing on line */ if (numofwhitespace == (int) strlen(tokenptr)) token = ENDOFLINE; else { tokenptr += numofwhitespace; if (iscomment(tokenptr)) { token = ENDOFLINE; } else { do { if (!_strnicmp(tokenptr, operators[comparetoken], oplengths[comparetoken])) found = TRUE; else comparetoken++; } while ( (!found) && (comparetoken < numoperators) ); if (found) { tokenptr += oplengths[comparetoken]; token = comparetoken; /* currenttoken is left blank for all but UINTs and IDs */ } else { digitcheck = tokenptr; if (!nonumbers && isdigit(*digitcheck)) { while (isdigit(*digitcheck)) digitcheck++; strncpy(currenttoken, tokenptr, digitcheck - tokenptr); tokenptr = digitcheck; token = UINT; } else if (issymbolchar(*digitcheck)) { while (issymbolchar(*digitcheck)) digitcheck++; strncpy(currenttoken, tokenptr, digitcheck - tokenptr); *(currenttoken + (digitcheck - tokenptr)) = '\0'; tokenptr = digitcheck; token = ID; } else token = UNKNOWN; } } } } __inline int issymbolchar(char c) { return (iscsym(c) || (c == '$') || (c == '?') || (c == '@')); }