00001 /* 00002 File: Expr.cc 00003 00004 Function: 00005 00006 Authors: Paul Heckbert, Andrew Willmott 00007 00008 Notes: 00009 */ 00010 00011 #include "cl/Expr.h" 00012 00013 /* 00014 * expr_eval: expression evaluator - converts ascii string to floating point 00015 * Works by top-down predictive parsing. 00016 * Most of the routines gobble characters and advance global string pointer s. 00017 * Sets global expr_err if an error occurs. 00018 * 00019 * supports: parentheses, % for mod, ^ for pow, elementary functions, 00020 * constants pi and e, variable base constants 00021 * 00022 * Paul Heckbert 18 April 1988 00023 * converted to ANSI C, ajw 1999 00024 */ 00025 00026 00027 #include <ctype.h> 00028 #include <math.h> 00029 #include <limits.h> 00030 00031 inline Double DegsToRads(Double degrees) 00032 { return(degrees * (M_PI / 180.0)); } 00033 inline Double RadsToDegs(Double rads) 00034 { return(rads * (180.0 / M_PI)); } 00035 00036 static Double expr(); 00037 static Double term(); 00038 static Double factor(); 00039 static Double signednumber(); 00040 static Double number(); 00041 static Double paren(); 00042 static Double posconst(); 00043 static Double expt(Int a, Int n); 00044 static void paren2(Double *x, Double *y); 00045 static Int eq(Int n, const Char *a, const Char *b); 00046 static void error(const Char *s, Int len, const Char *err); 00047 static void prints(Int n, const Char *s); 00048 static Int digit(Int c); 00049 00050 static const Char *s0, *s; 00051 Int expr_error; 00052 00053 inline space() 00054 { for (; isspace(*s); s++); }; 00055 00056 #ifdef MAIN 00057 main(Int ac, Char **av) 00058 { 00059 Double x; 00060 00061 if (ac != 2) exit(1); 00062 x = expr_eval(av[1]); 00063 printf("%g\n", x); 00064 } 00065 #endif 00066 00067 Int expr_eval_int(StrConst str) 00068 { 00069 Double x; 00070 00071 x = expr_eval(str); 00072 /* do unsigned Double to signed Int conversion: */ 00073 00074 if (x > INT_MAX) 00075 return(Int(x + 2.0 * INT_MIN)); 00076 else 00077 return(Int(x)); 00078 } 00079 00080 Long expr_eval_long(StrConst str) 00081 { 00082 Double x; 00083 00084 x = expr_eval(str); 00085 /* do unsigned Double to signed Long conversion: */ 00086 if (x > LONG_MAX) 00087 return(Long(x + 2.0 * LONG_MIN)); 00088 else 00089 return(Long(x)); 00090 } 00091 00092 Double expr_eval(StrConst str) 00093 { 00094 Double x; 00095 00096 s0 = s = str; 00097 expr_error = EXPR_GOOD; 00098 x = expr(); 00099 if (*s) 00100 { 00101 error(s, 1, "garbage in expression"); 00102 expr_error = s == s0 ? EXPR_BAD : EXPR_SOSO; 00103 } 00104 return x; 00105 } 00106 00107 static Double expr() 00108 { 00109 Double x; 00110 00111 for (x = term(); ; ) 00112 { 00113 space(); 00114 switch (*s) 00115 { 00116 case '+': 00117 s++; 00118 x += term(); break; 00119 case '-': s++; x -= term(); break; 00120 default: return x; 00121 } 00122 } 00123 } 00124 00125 static Double term() 00126 { 00127 Double x, y; 00128 00129 for (x = factor(); ; ) 00130 { 00131 space(); 00132 switch (*s) 00133 { 00134 case '*': 00135 s++; 00136 x *= factor(); break; 00137 case '/': s++; x /= factor(); break; 00138 case '%': s++; y = factor(); x = x - floor(x / y) * y; break; 00139 default: return x; 00140 } 00141 } 00142 } 00143 00144 static Double factor() 00145 { 00146 Double x; 00147 00148 for (x = signednumber(); ; ) 00149 { 00150 space(); 00151 switch (*s) 00152 { 00153 case '^': 00154 s++; 00155 return pow(x, factor()); /* right-associative */ 00156 default: return x; 00157 } 00158 } 00159 } 00160 00161 static Double signednumber() 00162 { 00163 space(); 00164 switch (*s) 00165 { 00166 case '-': 00167 s++; 00168 return - signednumber(); 00169 case '+': s++; return signednumber(); 00170 default: return number(); 00171 } 00172 } 00173 00174 static Double number() 00175 { 00176 const Char *func; 00177 Int n; 00178 Double x, y; 00179 00180 space(); 00181 if (isdigit(*s) || *s == '.') return posconst(); 00182 if (*s == '(') return paren(); 00183 00184 if (isalpha(*s)) 00185 { 00186 func = s; 00187 for (s++; isalpha(*s) || isdigit(*s); s++); 00188 n = s - func; /* length of funcname */ 00189 00190 if (eq(n, func, "pi")) return M_PI; 00191 if (eq(n, func, "e")) return exp(1.); 00192 00193 if (eq(n, func, "sqrt")) return sqrt(paren()); 00194 if (eq(n, func, "exp")) return exp(paren()); 00195 if (eq(n, func, "log")) return log(paren()); 00196 if (eq(n, func, "pow")) 00197 { 00198 paren2(&x, &y); 00199 return pow(x, y); 00200 } 00201 00202 if (eq(n, func, "sin")) return sin(paren()); 00203 if (eq(n, func, "cos")) return cos(paren()); 00204 if (eq(n, func, "tan")) return tan(paren()); 00205 if (eq(n, func, "asin")) return asin(paren()); 00206 if (eq(n, func, "acos")) return acos(paren()); 00207 if (eq(n, func, "atan")) return atan(paren()); 00208 if (eq(n, func, "atan2")) 00209 { 00210 paren2(&x, &y); 00211 return atan2(x, y); 00212 } 00213 00214 if (eq(n, func, "sind")) return sin(DegsToRads(paren())); 00215 if (eq(n, func, "cosd")) return cos(DegsToRads(paren())); 00216 if (eq(n, func, "tand")) return tan(DegsToRads(paren())); 00217 if (eq(n, func, "dasin")) return RadsToDegs(asin(paren())); 00218 if (eq(n, func, "dacos")) return RadsToDegs(acos(paren())); 00219 if (eq(n, func, "datan")) return RadsToDegs(atan(paren())); 00220 if (eq(n, func, "datan2")) 00221 { 00222 paren2(&x, &y); 00223 return RadsToDegs(atan2(x, y)); 00224 } 00225 00226 if (eq(n, func, "floor")) return floor(paren()); 00227 if (eq(n, func, "ceil")) return ceil(paren()); 00228 00229 error(func, n, "bad numerical expression"); 00230 return 0.; 00231 } 00232 00233 error(s, 1, "syntax error"); 00234 return 0.; 00235 } 00236 00237 /* paren: '(' expr ')' */ 00238 00239 static Double paren() 00240 { 00241 Double x; 00242 00243 space(); 00244 if (*s != '(') error(s, 1, "expected '('"); 00245 s++; 00246 x = expr(); 00247 space(); 00248 if (*s != ')') error(s, 1, "expected ')'"); 00249 s++; 00250 return x; 00251 } 00252 00253 /* paren2: '(' expr ',' expr ')' */ 00254 00255 static void paren2(Double *x, Double *y) 00256 { 00257 space(); 00258 if (*s != '(') error(s, 1, "expected '('"); 00259 s++; 00260 *x = expr(); 00261 space(); 00262 if (*s != ',') error(s, 1, "expected ','"); 00263 s++; 00264 *y = expr(); 00265 space(); 00266 if (*s != ')') error(s, 1, "expected ')'"); 00267 s++; 00268 } 00269 00270 /* 00271 * posconst: given a string beginning at s, return floating point value. 00272 * like atof but it uses and modifies the global ptr s 00273 */ 00274 00275 static Double posconst() 00276 { 00277 Int base, exp, pos, d; 00278 Double x, y; 00279 00280 space(); 00281 if (*s == '0') 00282 { /* change base: 10 = 012 = 0xa = 0b2:1010 */ 00283 s++; 00284 switch (*s) 00285 { 00286 case 'b': 00287 s++; 00288 for (base = 0; isdigit(*s); s++) 00289 base = base * 10 + *s - '0'; /* base is in base 10! */ 00290 if (*s != ':') error(s, 1, "expecting ':'"); 00291 s++; 00292 break; 00293 case 'x': 00294 s++; 00295 base = 16; 00296 break; 00297 case 't': 00298 s++; 00299 base = 10; 00300 break; 00301 case '.': 00302 base = 10; 00303 break; /* a float, e.g.: 0.123 */ 00304 default: 00305 base = 8; 00306 break; 00307 } 00308 } 00309 else base = 10; 00310 00311 x = 0.; 00312 for (; d = digit(*s), d >= 0 && d < base; s++) 00313 x = x * base + d; 00314 if (*s == '.') 00315 { 00316 s++; 00317 for (y = 1.; d = digit(*s), d >= 0 && d < base; s++) 00318 { 00319 x = x * base + d; /* fraction is in variable base */ 00320 y *= base; 00321 } 00322 x /= y; 00323 } 00324 if (*s == 'e' || *s == 'E') 00325 { 00326 s++; 00327 if (*s == '-') 00328 { 00329 s++; 00330 pos = 0; 00331 } 00332 else if (*s == '+') 00333 { 00334 s++; 00335 pos = 1; 00336 } 00337 else pos = 1; 00338 for (exp = 0; isdigit(*s); s++) 00339 exp = exp * 10 + *s - '0'; /* exponent is in base 10 */ 00340 y = expt(base, exp); 00341 if (pos) x *= y; 00342 else x /= y; 00343 } 00344 return x; 00345 } 00346 00347 static Int digit(Int c) 00348 { 00349 return isdigit(c) ? c - '0' : 00350 c >= 'a' && c <= 'z' ? c - 'a' + 10 : c >= 'A' && c <= 'Z' ? c - 'A' + 10 : -1; 00351 } 00352 00353 /* expt: a^n for n>=0 */ 00354 00355 static Double expt(Int a, Int n) 00356 { 00357 Double t, x; 00358 00359 if (n < 0) 00360 { 00361 fprintf(stderr, "expt: can't do negative exponents\n"); 00362 return 1.; 00363 } 00364 if (n == 0) return 1.; 00365 for (t = a, x = 1.; n > 0; n >>= 1) 00366 { 00367 if (n&1) x *= t; 00368 t *= t; 00369 } 00370 return x; 00371 } 00372 00373 /* eq: test equality of string a, of length n, with null-terminated string b */ 00374 00375 static Int eq(Int n, const Char *a, const Char *b) 00376 { 00377 Char c, *nca = (Char *) a; 00378 Int ret; 00379 00380 c = a[n]; 00381 nca[n] = 0; 00382 ret = (strcmp(a, b) == 0); 00383 nca[n] = c; 00384 return ret; 00385 } 00386 00387 static void error(const Char *s, Int len, const Char *err) 00388 { 00389 // if (*s == 0) s[len] = 0; /* just in case */ 00390 printf("expr: %s: ", err); 00391 prints(s - s0, s0); 00392 printf("["); 00393 prints(len, s); 00394 printf("]"); 00395 prints(s + strlen(s) - s0 - len, s + len); 00396 printf("\n"); 00397 if (expr_error != EXPR_BAD) 00398 expr_error = s == s0 ? EXPR_BAD : EXPR_SOSO; 00399 } 00400 00401 /* prints: print string s of length n */ 00402 00403 static void prints(Int n, const Char *s) 00404 { 00405 Char c, *ncs = (Char *) s; 00406 00407 c = s[n]; 00408 ncs[n] = 0; 00409 printf("%s", s); 00410 ncs[n] = c; 00411 }