1 /* $NetBSD: expr.c,v 1.14 2002/01/31 19:36:47 tv Exp $ */ 2 /* $OpenBSD: expr.c,v 1.11 2000/01/11 14:00:57 espie 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 #include <sys/cdefs.h> 41 #if defined(__RCSID) && !defined(lint) 42 #if 0 43 static char sccsid[] = "@(#)expr.c 8.2 (Berkeley) 4/29/95"; 44 #else 45 __RCSID("$NetBSD: expr.c,v 1.14 2002/01/31 19:36:47 tv Exp $"); 46 #endif 47 #endif /* not lint */ 48 49 #include <sys/types.h> 50 #include <ctype.h> 51 #include <stddef.h> 52 #include <stdio.h> 53 #include "mdef.h" 54 #include "extern.h" 55 56 /* 57 * expression evaluator: performs a standard recursive 58 * descent parse to evaluate any expression permissible 59 * within the following grammar: 60 * 61 * expr : query EOS 62 * query : lor 63 * | lor "?" query ":" query 64 * lor : land { "||" land } 65 * land : not { "&&" not } 66 * not : eqrel 67 * | '!' not 68 * eqrel : shift { eqrelop shift } 69 * shift : primary { shop primary } 70 * primary : term { addop term } 71 * term : exp { mulop exp } 72 * exp : unary { expop unary } 73 * unary : factor 74 * | unop unary 75 * factor : constant 76 * | "(" query ")" 77 * constant: num 78 * | "'" CHAR "'" 79 * num : DIGIT 80 * | DIGIT num 81 * shop : "<<" 82 * | ">>" 83 * eqrel : "=" 84 * | "==" 85 * | "!=" 86 * | "<" 87 * | ">" 88 * | "<=" 89 * | ">=" 90 * 91 * 92 * This expression evaluator is lifted from a public-domain 93 * C Pre-Processor included with the DECUS C Compiler distribution. 94 * It is hacked somewhat to be suitable for m4. 95 * 96 * Originally by: Mike Lutz 97 * Bob Harper 98 */ 99 100 #define EQL 0 101 #define NEQ 1 102 #define LSS 2 103 #define LEQ 3 104 #define GTR 4 105 #define GEQ 5 106 #define OCTAL 8 107 #define DECIMAL 10 108 #define HEX 16 109 110 static const char *nxtch; /* Parser scan pointer */ 111 static const char *where; 112 113 static int query __P((void)); 114 static int lor __P((void)); 115 static int land __P((void)); 116 static int not __P((void)); 117 static int eqrel __P((void)); 118 static int shift __P((void)); 119 static int primary __P((void)); 120 static int term __P((void)); 121 static int exp __P((void)); 122 static int unary __P((void)); 123 static int factor __P((void)); 124 static int constant __P((void)); 125 static int num __P((void)); 126 static int geteqrel __P((void)); 127 static int skipws __P((void)); 128 static void experr __P((const char *)); 129 130 /* 131 * For longjmp 132 */ 133 #include <setjmp.h> 134 static jmp_buf expjump; 135 136 /* 137 * macros: 138 * ungetch - Put back the last character examined. 139 * getch - return the next character from expr string. 140 */ 141 #define ungetch() nxtch-- 142 #define getch() *nxtch++ 143 144 int 145 expr(expbuf) 146 const char *expbuf; 147 { 148 int rval; 149 150 nxtch = expbuf; 151 where = expbuf; 152 if (setjmp(expjump) != 0) 153 return FALSE; 154 155 rval = query(); 156 if (skipws() == EOS) 157 return rval; 158 159 printf("m4: ill-formed expression.\n"); 160 return FALSE; 161 } 162 163 /* 164 * query : lor | lor '?' query ':' query 165 */ 166 static int 167 query() 168 { 169 int bool, true_val, false_val; 170 171 bool = lor(); 172 if (skipws() != '?') { 173 ungetch(); 174 return bool; 175 } 176 177 true_val = query(); 178 if (skipws() != ':') 179 experr("bad query"); 180 181 false_val = query(); 182 return bool ? true_val : false_val; 183 } 184 185 /* 186 * lor : land { '||' land } 187 */ 188 static int 189 lor() 190 { 191 int c, vl, vr; 192 193 vl = land(); 194 while ((c = skipws()) == '|') { 195 if (getch() != '|') 196 ungetch(); 197 vr = land(); 198 vl = vl || vr; 199 } 200 201 ungetch(); 202 return vl; 203 } 204 205 /* 206 * land : not { '&&' not } 207 */ 208 static int 209 land() 210 { 211 int c, vl, vr; 212 213 vl = not(); 214 while ((c = skipws()) == '&') { 215 if (getch() != '&') 216 ungetch(); 217 vr = not(); 218 vl = vl && vr; 219 } 220 221 ungetch(); 222 return vl; 223 } 224 225 /* 226 * not : eqrel | '!' not 227 */ 228 static int 229 not() 230 { 231 int val, c; 232 233 if ((c = skipws()) == '!' && getch() != '=') { 234 ungetch(); 235 val = not(); 236 return !val; 237 } 238 239 if (c == '!') 240 ungetch(); 241 ungetch(); 242 return eqrel(); 243 } 244 245 /* 246 * eqrel : shift { eqrelop shift } 247 */ 248 static int 249 eqrel() 250 { 251 int vl, vr, eqrelvar; 252 253 vl = shift(); 254 while ((eqrelvar = geteqrel()) != -1) { 255 vr = shift(); 256 257 switch (eqrelvar) { 258 259 case EQL: 260 vl = (vl == vr); 261 break; 262 case NEQ: 263 vl = (vl != vr); 264 break; 265 266 case LEQ: 267 vl = (vl <= vr); 268 break; 269 case LSS: 270 vl = (vl < vr); 271 break; 272 case GTR: 273 vl = (vl > vr); 274 break; 275 case GEQ: 276 vl = (vl >= vr); 277 break; 278 } 279 } 280 return vl; 281 } 282 283 /* 284 * shift : primary { shop primary } 285 */ 286 static int 287 shift() 288 { 289 int vl, vr, c; 290 291 vl = primary(); 292 while (((c = skipws()) == '<' || c == '>') && getch() == c) { 293 vr = primary(); 294 295 if (c == '<') 296 vl <<= vr; 297 else 298 vl >>= vr; 299 } 300 301 if (c == '<' || c == '>') 302 ungetch(); 303 ungetch(); 304 return vl; 305 } 306 307 /* 308 * primary : term { addop term } 309 */ 310 static int 311 primary() 312 { 313 int c, vl, vr; 314 315 vl = term(); 316 while ((c = skipws()) == '+' || c == '-') { 317 vr = term(); 318 319 if (c == '+') 320 vl += vr; 321 else 322 vl -= vr; 323 } 324 325 ungetch(); 326 return vl; 327 } 328 329 /* 330 * <term> := <exp> { <mulop> <exp> } 331 */ 332 static int 333 term() 334 { 335 int c, vl, vr; 336 337 vl = exp(); 338 while ((c = skipws()) == '*' || c == '/' || c == '%') { 339 vr = exp(); 340 341 switch (c) { 342 case '*': 343 vl *= vr; 344 break; 345 case '/': 346 if (vr == 0) 347 errx(1, "division by zero in eval."); 348 else 349 vl /= vr; 350 break; 351 case '%': 352 if (vr == 0) 353 errx(1, "modulo zero in eval."); 354 else 355 vl %= vr; 356 break; 357 } 358 } 359 ungetch(); 360 return vl; 361 } 362 363 /* 364 * <term> := <unary> { <expop> <unary> } 365 */ 366 static int 367 exp() 368 { 369 int c, vl, vr, n; 370 371 vl = unary(); 372 switch (c = skipws()) { 373 374 case '*': 375 if (getch() != '*') { 376 ungetch(); 377 break; 378 } 379 380 case '^': 381 vr = exp(); 382 n = 1; 383 while (vr-- > 0) 384 n *= vl; 385 return n; 386 } 387 388 ungetch(); 389 return vl; 390 } 391 392 /* 393 * unary : factor | unop unary 394 */ 395 static int 396 unary() 397 { 398 int val, c; 399 400 if ((c = skipws()) == '+' || c == '-' || c == '~') { 401 val = unary(); 402 403 switch (c) { 404 case '+': 405 return val; 406 case '-': 407 return -val; 408 case '~': 409 return ~val; 410 } 411 } 412 413 ungetch(); 414 return factor(); 415 } 416 417 /* 418 * factor : constant | '(' query ')' 419 */ 420 static int 421 factor() 422 { 423 int val; 424 425 if (skipws() == '(') { 426 val = query(); 427 if (skipws() != ')') 428 experr("bad factor"); 429 return val; 430 } 431 432 ungetch(); 433 return constant(); 434 } 435 436 /* 437 * constant: num | 'char' 438 * Note: constant() handles multi-byte constants 439 */ 440 static int 441 constant() 442 { 443 int i; 444 int value; 445 char c; 446 int v[sizeof(int)]; 447 448 if (skipws() != '\'') { 449 ungetch(); 450 return num(); 451 } 452 for (i = 0; i < sizeof(int); i++) { 453 if ((c = getch()) == '\'') { 454 ungetch(); 455 break; 456 } 457 if (c == '\\') { 458 switch (c = getch()) { 459 case '0': 460 case '1': 461 case '2': 462 case '3': 463 case '4': 464 case '5': 465 case '6': 466 case '7': 467 ungetch(); 468 c = num(); 469 break; 470 case 'n': 471 c = 012; 472 break; 473 case 'r': 474 c = 015; 475 break; 476 case 't': 477 c = 011; 478 break; 479 case 'b': 480 c = 010; 481 break; 482 case 'f': 483 c = 014; 484 break; 485 } 486 } 487 v[i] = c; 488 } 489 if (i == 0 || getch() != '\'') 490 experr("illegal character constant"); 491 for (value = 0; --i >= 0;) { 492 value <<= 8; 493 value += v[i]; 494 } 495 return value; 496 } 497 498 /* 499 * num : digit | num digit 500 */ 501 static int 502 num() 503 { 504 int rval, c, base; 505 int ndig; 506 507 base = ((c = skipws()) == '0') ? OCTAL : DECIMAL; 508 rval = 0; 509 ndig = 0; 510 if (base == OCTAL) { 511 c = skipws(); 512 if (c == 'x' || c == 'X') { 513 base = HEX; 514 c = skipws(); 515 } else 516 ndig++; 517 } 518 while ((base == HEX && isxdigit(c)) || 519 (c >= '0' && c <= (base == OCTAL ? '7' : '9'))) { 520 rval *= base; 521 if (isalpha(c)) 522 rval += (tolower(c) - 'a' + 10); 523 else 524 rval += (c - '0'); 525 c = getch(); 526 ndig++; 527 } 528 ungetch(); 529 530 if (ndig == 0) 531 experr("bad constant"); 532 533 return rval; 534 } 535 536 /* 537 * eqrel : '=' | '==' | '!=' | '<' | '>' | '<=' | '>=' 538 */ 539 static int 540 geteqrel() 541 { 542 int c1, c2; 543 544 c1 = skipws(); 545 c2 = getch(); 546 547 switch (c1) { 548 549 case '=': 550 if (c2 != '=') 551 ungetch(); 552 return EQL; 553 554 case '!': 555 if (c2 == '=') 556 return NEQ; 557 ungetch(); 558 ungetch(); 559 return -1; 560 561 case '<': 562 if (c2 == '=') 563 return LEQ; 564 ungetch(); 565 return LSS; 566 567 case '>': 568 if (c2 == '=') 569 return GEQ; 570 ungetch(); 571 return GTR; 572 573 default: 574 ungetch(); 575 ungetch(); 576 return -1; 577 } 578 } 579 580 /* 581 * Skip over any white space and return terminating char. 582 */ 583 static int 584 skipws() 585 { 586 char c; 587 588 while ((c = getch()) <= ' ' && c > EOS) 589 ; 590 return c; 591 } 592 593 /* 594 * resets environment to eval(), prints an error 595 * and forces eval to return FALSE. 596 */ 597 static void 598 experr(msg) 599 const char *msg; 600 { 601 printf("m4: %s in expr %s.\n", msg, where); 602 longjmp(expjump, -1); 603 } 604