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