1 /* $OpenBSD: expr.c,v 1.4 1998/04/25 18:47:19 millert Exp $ */ 2 /* $NetBSD: expr.c,v 1.7 1995/09/28 05:37:31 tls Exp $ */ 3 4 /* 5 * Copyright (c) 1989, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * This code is derived from software contributed to Berkeley by 9 * Ozan Yigit at York University. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgement: 21 * This product includes software developed by the University of 22 * California, Berkeley and its contributors. 23 * 4. Neither the name of the University nor the names of its contributors 24 * may be used to endorse or promote products derived from this software 25 * without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 37 * SUCH DAMAGE. 38 */ 39 40 #ifndef lint 41 #if 0 42 static char sccsid[] = "@(#)expr.c 8.2 (Berkeley) 4/29/95"; 43 #else 44 static char rcsid[] = "$OpenBSD: expr.c,v 1.4 1998/04/25 18:47:19 millert Exp $"; 45 #endif 46 #endif /* not lint */ 47 48 #include <sys/cdefs.h> 49 #include <stdio.h> 50 51 /* 52 * expression evaluator: performs a standard recursive 53 * descent parse to evaluate any expression permissible 54 * within the following grammar: 55 * 56 * expr : query EOS 57 * query : lor 58 * | lor "?" query ":" query 59 * lor : land { "||" land } 60 * land : not { "&&" not } 61 * not : eqrel 62 * | '!' not 63 * eqrel : shift { eqrelop shift } 64 * shift : primary { shop primary } 65 * primary : term { addop term } 66 * term : exp { mulop exp } 67 * exp : unary { expop unary } 68 * unary : factor 69 * | unop unary 70 * factor : constant 71 * | "(" query ")" 72 * constant: num 73 * | "'" CHAR "'" 74 * num : DIGIT 75 * | DIGIT num 76 * shop : "<<" 77 * | ">>" 78 * eqrel : "=" 79 * | "==" 80 * | "!=" 81 * | "<" 82 * | ">" 83 * | "<=" 84 * | ">=" 85 * 86 * 87 * This expression evaluator is lifted from a public-domain 88 * C Pre-Processor included with the DECUS C Compiler distribution. 89 * It is hacked somewhat to be suitable for m4. 90 * 91 * Originally by: Mike Lutz 92 * Bob Harper 93 */ 94 95 #define TRUE 1 96 #define FALSE 0 97 #define EOS (char) 0 98 #define EQL 0 99 #define NEQ 1 100 #define LSS 2 101 #define LEQ 3 102 #define GTR 4 103 #define GEQ 5 104 #define OCTAL 8 105 #define DECIMAL 10 106 107 static char *nxtch; /* Parser scan pointer */ 108 109 static int query __P((void)); 110 static int lor __P((void)); 111 static int land __P((void)); 112 static int not __P((void)); 113 static int eqrel __P((void)); 114 static int shift __P((void)); 115 static int primary __P((void)); 116 static int term __P((void)); 117 static int exp __P((void)); 118 static int unary __P((void)); 119 static int factor __P((void)); 120 static int constant __P((void)); 121 static int num __P((void)); 122 static int geteqrel __P((void)); 123 static int skipws __P((void)); 124 static void experr __P((char *)); 125 126 /* 127 * For longjmp 128 */ 129 #include <setjmp.h> 130 static jmp_buf expjump; 131 132 /* 133 * macros: 134 * ungetch - Put back the last character examined. 135 * getch - return the next character from expr string. 136 */ 137 #define ungetch() nxtch-- 138 #define getch() *nxtch++ 139 140 int 141 expr(expbuf) 142 char *expbuf; 143 { 144 register int rval; 145 146 nxtch = expbuf; 147 if (setjmp(expjump) != 0) 148 return FALSE; 149 150 rval = query(); 151 if (skipws() == EOS) 152 return rval; 153 154 printf("m4: ill-formed expression.\n"); 155 return FALSE; 156 } 157 158 /* 159 * query : lor | lor '?' query ':' query 160 */ 161 static int 162 query() 163 { 164 register int bool, true_val, false_val; 165 166 bool = lor(); 167 if (skipws() != '?') { 168 ungetch(); 169 return bool; 170 } 171 172 true_val = query(); 173 if (skipws() != ':') 174 experr("bad query"); 175 176 false_val = query(); 177 return bool ? true_val : false_val; 178 } 179 180 /* 181 * lor : land { '||' land } 182 */ 183 static int 184 lor() 185 { 186 register int c, vl, vr; 187 188 vl = land(); 189 while ((c = skipws()) == '|') { 190 if (getch() != '|') 191 ungetch(); 192 vr = land(); 193 vl = vl || vr; 194 } 195 196 ungetch(); 197 return vl; 198 } 199 200 /* 201 * land : not { '&&' not } 202 */ 203 static int 204 land() 205 { 206 register int c, vl, vr; 207 208 vl = not(); 209 while ((c = skipws()) == '&') { 210 if (getch() != '&') 211 ungetch(); 212 vr = not(); 213 vl = vl && vr; 214 } 215 216 ungetch(); 217 return vl; 218 } 219 220 /* 221 * not : eqrel | '!' not 222 */ 223 static int 224 not() 225 { 226 register int val, c; 227 228 if ((c = skipws()) == '!' && getch() != '=') { 229 ungetch(); 230 val = not(); 231 return !val; 232 } 233 234 if (c == '!') 235 ungetch(); 236 ungetch(); 237 return eqrel(); 238 } 239 240 /* 241 * eqrel : shift { eqrelop shift } 242 */ 243 static int 244 eqrel() 245 { 246 register int vl, vr, eqrel; 247 248 vl = shift(); 249 while ((eqrel = geteqrel()) != -1) { 250 vr = shift(); 251 252 switch (eqrel) { 253 254 case EQL: 255 vl = (vl == vr); 256 break; 257 case NEQ: 258 vl = (vl != vr); 259 break; 260 261 case LEQ: 262 vl = (vl <= vr); 263 break; 264 case LSS: 265 vl = (vl < vr); 266 break; 267 case GTR: 268 vl = (vl > vr); 269 break; 270 case GEQ: 271 vl = (vl >= vr); 272 break; 273 } 274 } 275 return vl; 276 } 277 278 /* 279 * shift : primary { shop primary } 280 */ 281 static int 282 shift() 283 { 284 register int vl, vr, c; 285 286 vl = primary(); 287 while (((c = skipws()) == '<' || c == '>') && getch() == c) { 288 vr = primary(); 289 290 if (c == '<') 291 vl <<= vr; 292 else 293 vl >>= vr; 294 } 295 296 if (c == '<' || c == '>') 297 ungetch(); 298 ungetch(); 299 return vl; 300 } 301 302 /* 303 * primary : term { addop term } 304 */ 305 static int 306 primary() 307 { 308 register int c, vl, vr; 309 310 vl = term(); 311 while ((c = skipws()) == '+' || c == '-') { 312 vr = term(); 313 314 if (c == '+') 315 vl += vr; 316 else 317 vl -= vr; 318 } 319 320 ungetch(); 321 return vl; 322 } 323 324 /* 325 * <term> := <exp> { <mulop> <exp> } 326 */ 327 static int 328 term() 329 { 330 register int c, vl, vr; 331 332 vl = exp(); 333 while ((c = skipws()) == '*' || c == '/' || c == '%') { 334 vr = exp(); 335 336 switch (c) { 337 case '*': 338 vl *= vr; 339 break; 340 case '/': 341 vl /= vr; 342 break; 343 case '%': 344 vl %= vr; 345 break; 346 } 347 } 348 ungetch(); 349 return vl; 350 } 351 352 /* 353 * <term> := <unary> { <expop> <unary> } 354 */ 355 static int 356 exp() 357 { 358 register int c, vl, vr, n; 359 360 vl = unary(); 361 switch (c = skipws()) { 362 363 case '*': 364 if (getch() != '*') { 365 ungetch(); 366 break; 367 } 368 369 case '^': 370 vr = exp(); 371 n = 1; 372 while (vr-- > 0) 373 n *= vl; 374 return n; 375 } 376 377 ungetch(); 378 return vl; 379 } 380 381 /* 382 * unary : factor | unop unary 383 */ 384 static int 385 unary() 386 { 387 register int val, c; 388 389 if ((c = skipws()) == '+' || c == '-' || c == '~') { 390 val = unary(); 391 392 switch (c) { 393 case '+': 394 return val; 395 case '-': 396 return -val; 397 case '~': 398 return ~val; 399 } 400 } 401 402 ungetch(); 403 return factor(); 404 } 405 406 /* 407 * factor : constant | '(' query ')' 408 */ 409 static int 410 factor() 411 { 412 register int val; 413 414 if (skipws() == '(') { 415 val = query(); 416 if (skipws() != ')') 417 experr("bad factor"); 418 return val; 419 } 420 421 ungetch(); 422 return constant(); 423 } 424 425 /* 426 * constant: num | 'char' 427 * Note: constant() handles multi-byte constants 428 */ 429 static int 430 constant() 431 { 432 register int i; 433 register int value; 434 register int c; 435 int v[sizeof(int)]; 436 437 if (skipws() != '\'') { 438 ungetch(); 439 return num(); 440 } 441 for (i = 0; i < sizeof(int); i++) { 442 if ((c = getch()) == '\'') { 443 ungetch(); 444 break; 445 } 446 if (c == '\\') { 447 switch (c = getch()) { 448 case '0': 449 case '1': 450 case '2': 451 case '3': 452 case '4': 453 case '5': 454 case '6': 455 case '7': 456 ungetch(); 457 c = num(); 458 break; 459 case 'n': 460 c = 012; 461 break; 462 case 'r': 463 c = 015; 464 break; 465 case 't': 466 c = 011; 467 break; 468 case 'b': 469 c = 010; 470 break; 471 case 'f': 472 c = 014; 473 break; 474 } 475 } 476 v[i] = c; 477 } 478 if (i == 0 || getch() != '\'') 479 experr("illegal character constant"); 480 for (value = 0; --i >= 0;) { 481 value <<= 8; 482 value += v[i]; 483 } 484 return value; 485 } 486 487 /* 488 * num : digit | num digit 489 */ 490 static int 491 num() 492 { 493 register int rval, c, base; 494 int ndig; 495 496 base = ((c = skipws()) == '0') ? OCTAL : DECIMAL; 497 rval = 0; 498 ndig = 0; 499 while (c >= '0' && c <= (base == OCTAL ? '7' : '9')) { 500 rval *= base; 501 rval += (c - '0'); 502 c = getch(); 503 ndig++; 504 } 505 ungetch(); 506 507 if (ndig == 0) 508 experr("bad constant"); 509 510 return rval; 511 512 } 513 514 /* 515 * eqrel : '=' | '==' | '!=' | '<' | '>' | '<=' | '>=' 516 */ 517 static int 518 geteqrel() 519 { 520 register int c1, c2; 521 522 c1 = skipws(); 523 c2 = getch(); 524 525 switch (c1) { 526 527 case '=': 528 if (c2 != '=') 529 ungetch(); 530 return EQL; 531 532 case '!': 533 if (c2 == '=') 534 return NEQ; 535 ungetch(); 536 ungetch(); 537 return -1; 538 539 case '<': 540 if (c2 == '=') 541 return LEQ; 542 ungetch(); 543 return LSS; 544 545 case '>': 546 if (c2 == '=') 547 return GEQ; 548 ungetch(); 549 return GTR; 550 551 default: 552 ungetch(); 553 ungetch(); 554 return -1; 555 } 556 } 557 558 /* 559 * Skip over any white space and return terminating char. 560 */ 561 static int 562 skipws() 563 { 564 register int c; 565 566 while ((c = getch()) <= ' ' && c > EOS) 567 ; 568 return c; 569 } 570 571 /* 572 * resets environment to eval(), prints an error 573 * and forces eval to return FALSE. 574 */ 575 static void 576 experr(msg) 577 char *msg; 578 { 579 printf("m4: %s in expr.\n", msg); 580 longjmp(expjump, -1); 581 } 582