1 /*- 2 * Copyright (c) 2010 The NetBSD Foundation, Inc. 3 * All rights reserved. 4 * 5 * This code is derived from software contributed to The NetBSD Foundation 6 * by David A. Holland. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 * POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 #include <stdlib.h> 31 #include <string.h> 32 #include <limits.h> 33 #include <errno.h> 34 35 //#define DEBUG 36 #ifdef DEBUG 37 #include <stdio.h> 38 #endif 39 40 #include "utils.h" 41 #include "array.h" 42 #include "mode.h" 43 #include "place.h" 44 #include "eval.h" 45 46 /* 47 * e ::= 48 * e1 ? e2 : e3 49 * e1 || e2 50 * e1 && e2 51 * e1 | e2 52 * e1 ^ e2 53 * e1 & e2 54 * e1 == e2 | e1 != e2 55 * e1 < e2 | e1 <= e2 | e1 > e2 | e1 >= e2 56 * e1 << e2 | e1 >> e2 57 * e1 + e2 | e1 - e2 58 * e1 * e2 | e1 / e2 | e1 % e2 59 * !e | ~e | -e | +e 60 * ( e ) | ident 61 */ 62 63 enum tokens { 64 T_EOF, /* end of input */ 65 T_VAL, /* value */ 66 T_LPAREN, /* parens */ 67 T_RPAREN, 68 T_PIPEPIPE, /* operators */ 69 T_AMPAMP, 70 T_EQEQ, 71 T_BANGEQ, 72 T_LTEQ, 73 T_GTEQ, 74 T_LTLT, 75 T_GTGT, 76 T_QUES, 77 T_COLON, 78 T_PIPE, 79 T_CARET, 80 T_AMP, 81 T_LT, 82 T_GT, 83 T_PLUS, 84 T_MINUS, 85 T_STAR, 86 T_SLASH, 87 T_PCT, 88 T_BANG, 89 T_TILDE, 90 }; 91 92 static const struct { 93 char c1, c2; 94 enum tokens tok; 95 } tokens_2[] = { 96 { '|', '|', T_PIPEPIPE }, 97 { '&', '&', T_AMPAMP }, 98 { '=', '=', T_EQEQ }, 99 { '!', '=', T_BANGEQ }, 100 { '<', '=', T_LTEQ }, 101 { '>', '=', T_GTEQ }, 102 { '<', '<', T_LTLT }, 103 { '>', '>', T_GTGT }, 104 }; 105 static const unsigned num_tokens_2 = HOWMANY(tokens_2); 106 107 static const struct { 108 char c1; 109 enum tokens tok; 110 } tokens_1[] = { 111 { '?', T_QUES }, 112 { ':', T_COLON }, 113 { '|', T_PIPE }, 114 { '^', T_CARET }, 115 { '&', T_AMP }, 116 { '<', T_LT }, 117 { '>', T_GT }, 118 { '+', T_PLUS }, 119 { '-', T_MINUS }, 120 { '*', T_STAR }, 121 { '/', T_SLASH }, 122 { '%', T_PCT }, 123 { '!', T_BANG }, 124 { '~', T_TILDE }, 125 { '(', T_LPAREN }, 126 { ')', T_RPAREN }, 127 }; 128 static const unsigned num_tokens_1 = HOWMANY(tokens_1); 129 130 struct token { 131 struct place place; 132 enum tokens tok; 133 int val; 134 }; 135 DECLARRAY(token, static UNUSED); 136 DEFARRAY(token, static); 137 138 static struct tokenarray tokens; 139 140 static 141 struct token * 142 token_create(const struct place *p, enum tokens tok, int val) 143 { 144 struct token *t; 145 146 t = domalloc(sizeof(*t)); 147 t->place = *p; 148 t->tok = tok; 149 t->val = val; 150 return t; 151 } 152 153 static 154 void 155 token_destroy(struct token *t) 156 { 157 dofree(t, sizeof(*t)); 158 } 159 160 DESTROYALL_ARRAY(token, ); 161 162 #ifdef DEBUG 163 static 164 void 165 printtokens(void) 166 { 167 unsigned i, num; 168 struct token *t; 169 170 fprintf(stderr, "tokens:"); 171 num = tokenarray_num(&tokens); 172 for (i=0; i<num; i++) { 173 t = tokenarray_get(&tokens, i); 174 switch (t->tok) { 175 case T_EOF: fprintf(stderr, " <eof>"); break; 176 case T_VAL: fprintf(stderr, " %d", t->val); break; 177 case T_LPAREN: fprintf(stderr, " ("); break; 178 case T_RPAREN: fprintf(stderr, " )"); break; 179 case T_PIPEPIPE: fprintf(stderr, " ||"); break; 180 case T_AMPAMP: fprintf(stderr, " &&"); break; 181 case T_EQEQ: fprintf(stderr, " =="); break; 182 case T_BANGEQ: fprintf(stderr, " !="); break; 183 case T_LTEQ: fprintf(stderr, " <="); break; 184 case T_GTEQ: fprintf(stderr, " >="); break; 185 case T_LTLT: fprintf(stderr, " <<"); break; 186 case T_GTGT: fprintf(stderr, " >>"); break; 187 case T_QUES: fprintf(stderr, " ?"); break; 188 case T_COLON: fprintf(stderr, " :"); break; 189 case T_PIPE: fprintf(stderr, " |"); break; 190 case T_CARET: fprintf(stderr, " ^"); break; 191 case T_AMP: fprintf(stderr, " &"); break; 192 case T_LT: fprintf(stderr, " <"); break; 193 case T_GT: fprintf(stderr, " >"); break; 194 case T_PLUS: fprintf(stderr, " +"); break; 195 case T_MINUS: fprintf(stderr, " -"); break; 196 case T_STAR: fprintf(stderr, " *"); break; 197 case T_SLASH: fprintf(stderr, " /"); break; 198 case T_PCT: fprintf(stderr, " %%"); break; 199 case T_BANG: fprintf(stderr, " !"); break; 200 case T_TILDE: fprintf(stderr, " ~"); break; 201 } 202 } 203 fprintf(stderr, "\n"); 204 } 205 #endif 206 207 static 208 bool 209 isuop(enum tokens tok) 210 { 211 switch (tok) { 212 case T_BANG: 213 case T_TILDE: 214 case T_MINUS: 215 case T_PLUS: 216 return true; 217 default: 218 break; 219 } 220 return false; 221 } 222 223 static 224 bool 225 isbop(enum tokens tok) 226 { 227 switch (tok) { 228 case T_EOF: 229 case T_VAL: 230 case T_LPAREN: 231 case T_RPAREN: 232 case T_COLON: 233 case T_QUES: 234 case T_BANG: 235 case T_TILDE: 236 return false; 237 default: 238 break; 239 } 240 return true; 241 } 242 243 static 244 bool 245 isop(enum tokens tok) 246 { 247 switch (tok) { 248 case T_EOF: 249 case T_VAL: 250 case T_LPAREN: 251 case T_RPAREN: 252 return false; 253 default: 254 break; 255 } 256 return true; 257 } 258 259 static 260 int 261 getprec(enum tokens tok) 262 { 263 switch (tok) { 264 case T_BANG: case T_TILDE: return -1; 265 case T_STAR: case T_SLASH: case T_PCT: return 0; 266 case T_PLUS: case T_MINUS: return 1; 267 case T_LTLT: case T_GTGT: return 2; 268 case T_LT: case T_LTEQ: case T_GT: case T_GTEQ: return 3; 269 case T_EQEQ: case T_BANGEQ: return 4; 270 case T_AMP: return 5; 271 case T_CARET: return 6; 272 case T_PIPE: return 7; 273 case T_AMPAMP: return 8; 274 case T_PIPEPIPE: return 9; 275 default: break; 276 } 277 return 10; 278 } 279 280 static 281 bool 282 looser(enum tokens t1, enum tokens t2) 283 { 284 return getprec(t1) >= getprec(t2); 285 } 286 287 static 288 int 289 eval_uop(enum tokens op, int val) 290 { 291 switch (op) { 292 case T_BANG: val = !val; break; 293 case T_TILDE: val = (int)~(unsigned)val; break; 294 case T_MINUS: val = -val; break; 295 case T_PLUS: break; 296 default: assert(0); break; 297 } 298 return val; 299 } 300 301 static 302 int 303 eval_bop(struct place *p, int lv, enum tokens op, int rv) 304 { 305 unsigned mask; 306 307 switch (op) { 308 case T_PIPEPIPE: return lv || rv; 309 case T_AMPAMP: return lv && rv; 310 case T_PIPE: return (int)((unsigned)lv | (unsigned)rv); 311 case T_CARET: return (int)((unsigned)lv ^ (unsigned)rv); 312 case T_AMP: return (int)((unsigned)lv & (unsigned)rv); 313 case T_EQEQ: return lv == rv; 314 case T_BANGEQ: return lv != rv; 315 case T_LT: return lv < rv; 316 case T_GT: return lv > rv; 317 case T_LTEQ: return lv <= rv; 318 case T_GTEQ: return lv >= rv; 319 320 case T_LTLT: 321 case T_GTGT: 322 if (rv < 0) { 323 complain(p, "Negative bit-shift"); 324 complain_fail(); 325 rv = 0; 326 } 327 if ((unsigned)rv >= CHAR_BIT * sizeof(unsigned)) { 328 complain(p, "Bit-shift farther than type width"); 329 complain_fail(); 330 rv = 0; 331 } 332 if (op == T_LTLT) { 333 return (int)((unsigned)lv << (unsigned)rv); 334 } 335 mask = ((unsigned)-1) << (CHAR_BIT * sizeof(unsigned) - rv); 336 lv = (int)(((unsigned)lv >> (unsigned)rv) | mask); 337 return lv; 338 339 case T_MINUS: 340 if (rv == INT_MIN) { 341 if (lv == INT_MIN) { 342 return 0; 343 } 344 lv--; 345 rv++; 346 } 347 rv = -rv; 348 /* FALLTHROUGH */ 349 case T_PLUS: 350 if (rv > 0 && lv > (INT_MAX - rv)) { 351 complain(p, "Integer overflow"); 352 complain_fail(); 353 return INT_MAX; 354 } 355 if (rv < 0 && lv < (INT_MIN - rv)) { 356 complain(p, "Integer underflow"); 357 complain_fail(); 358 return INT_MIN; 359 } 360 return lv + rv; 361 362 case T_STAR: 363 if (rv == 0) { 364 return 0; 365 } 366 if (rv == 1) { 367 return lv; 368 } 369 if (rv == -1 && lv == INT_MIN) { 370 lv++; 371 lv = -lv; 372 if (lv == INT_MAX) { 373 complain(p, "Integer overflow"); 374 complain_fail(); 375 return INT_MAX; 376 } 377 lv++; 378 return lv; 379 } 380 if (lv == INT_MIN && rv < 0) { 381 complain(p, "Integer overflow"); 382 complain_fail(); 383 return INT_MAX; 384 } 385 if (lv == INT_MIN && rv > 0) { 386 complain(p, "Integer underflow"); 387 complain_fail(); 388 return INT_MIN; 389 } 390 if (rv < 0) { 391 rv = -rv; 392 lv = -lv; 393 } 394 if (lv > 0 && lv > INT_MAX / rv) { 395 complain(p, "Integer overflow"); 396 complain_fail(); 397 return INT_MAX; 398 } 399 if (lv < 0 && lv < INT_MIN / rv) { 400 complain(p, "Integer underflow"); 401 complain_fail(); 402 return INT_MIN; 403 } 404 return lv * rv; 405 406 case T_SLASH: 407 if (rv == 0) { 408 complain(p, "Division by zero"); 409 complain_fail(); 410 return 0; 411 } 412 return lv / rv; 413 414 case T_PCT: 415 if (rv == 0) { 416 complain(p, "Modulus by zero"); 417 complain_fail(); 418 return 0; 419 } 420 return lv % rv; 421 422 default: assert(0); break; 423 } 424 return 0; 425 } 426 427 static 428 void 429 tryreduce(void) 430 { 431 unsigned num; 432 struct token *t1, *t2, *t3, *t4, *t5, *t6; 433 434 while (1) { 435 #ifdef DEBUG 436 printtokens(); 437 #endif 438 num = tokenarray_num(&tokens); 439 t1 = (num >= 1) ? tokenarray_get(&tokens, num-1) : NULL; 440 t2 = (num >= 2) ? tokenarray_get(&tokens, num-2) : NULL; 441 t3 = (num >= 3) ? tokenarray_get(&tokens, num-3) : NULL; 442 443 if (num >= 3 && 444 t3->tok == T_LPAREN && 445 t2->tok == T_VAL && 446 t1->tok == T_RPAREN) { 447 /* (x) -> x */ 448 t2->place = t3->place; 449 token_destroy(t1); 450 token_destroy(t3); 451 tokenarray_remove(&tokens, num-1); 452 tokenarray_remove(&tokens, num-3); 453 continue; 454 } 455 456 if (num >= 2 && 457 (num == 2 || isop(t3->tok) || t3->tok == T_LPAREN) && 458 isuop(t2->tok) && 459 t1->tok == T_VAL) { 460 /* unary operator */ 461 t1->val = eval_uop(t2->tok, t1->val); 462 t1->place = t2->place; 463 token_destroy(t2); 464 tokenarray_remove(&tokens, num-2); 465 continue; 466 } 467 if (num >= 2 && 468 (num == 2 || isop(t3->tok) || t3->tok == T_LPAREN) && 469 t2->tok != T_LPAREN && t2->tok != T_VAL && 470 t1->tok == T_VAL) { 471 complain(&t2->place, "Invalid unary operator"); 472 complain_fail(); 473 token_destroy(t2); 474 tokenarray_remove(&tokens, num-2); 475 continue; 476 } 477 478 479 t4 = (num >= 4) ? tokenarray_get(&tokens, num-4) : NULL; 480 481 if (num >= 4 && 482 t4->tok == T_VAL && 483 isbop(t3->tok) && 484 t2->tok == T_VAL) { 485 /* binary operator */ 486 if (looser(t1->tok, t3->tok)) { 487 t4->val = eval_bop(&t3->place, 488 t4->val, t3->tok, t2->val); 489 token_destroy(t2); 490 token_destroy(t3); 491 tokenarray_remove(&tokens, num-2); 492 tokenarray_remove(&tokens, num-3); 493 continue; 494 } 495 break; 496 } 497 498 t5 = (num >= 5) ? tokenarray_get(&tokens, num-5) : NULL; 499 t6 = (num >= 6) ? tokenarray_get(&tokens, num-6) : NULL; 500 501 if (num >= 6 && 502 t6->tok == T_VAL && 503 t5->tok == T_QUES && 504 t4->tok == T_VAL && 505 t3->tok == T_COLON && 506 t2->tok == T_VAL && 507 !isop(t1->tok)) { 508 /* conditional expression */ 509 t6->val = t6->val ? t4->val : t2->val; 510 token_destroy(t2); 511 token_destroy(t3); 512 token_destroy(t4); 513 token_destroy(t5); 514 tokenarray_remove(&tokens, num-2); 515 tokenarray_remove(&tokens, num-3); 516 tokenarray_remove(&tokens, num-4); 517 tokenarray_remove(&tokens, num-5); 518 continue; 519 } 520 521 if (num >= 2 && 522 t2->tok == T_LPAREN && 523 t1->tok == T_RPAREN) { 524 complain(&t1->place, "Value expected within ()"); 525 complain_fail(); 526 t1->tok = T_VAL; 527 t1->val = 0; 528 token_destroy(t1); 529 tokenarray_remove(&tokens, num-1); 530 continue; 531 } 532 533 if (num >= 2 && 534 t2->tok == T_VAL && 535 t1->tok == T_VAL) { 536 complain(&t1->place, "Operator expected"); 537 complain_fail(); 538 token_destroy(t1); 539 tokenarray_remove(&tokens, num-1); 540 continue; 541 } 542 543 if (num >= 2 && 544 isop(t2->tok) && 545 t1->tok == T_EOF) { 546 complain(&t1->place, "Value expected after operator"); 547 complain_fail(); 548 token_destroy(t2); 549 tokenarray_remove(&tokens, num-2); 550 continue; 551 } 552 553 if (num == 2 && 554 t2->tok == T_VAL && 555 t1->tok == T_RPAREN) { 556 complain(&t1->place, "Excess right parenthesis"); 557 complain_fail(); 558 token_destroy(t1); 559 tokenarray_remove(&tokens, num-1); 560 continue; 561 } 562 563 if (num == 3 && 564 t3->tok == T_LPAREN && 565 t2->tok == T_VAL && 566 t1->tok == T_EOF) { 567 complain(&t1->place, "Unclosed left parenthesis"); 568 complain_fail(); 569 token_destroy(t3); 570 tokenarray_remove(&tokens, num-3); 571 continue; 572 } 573 574 if (num == 2 && 575 t2->tok == T_VAL && 576 t1->tok == T_EOF) { 577 /* accepting state */ 578 break; 579 } 580 581 if (num >= 1 && 582 t1->tok == T_EOF) { 583 /* any other configuration at eof is an error */ 584 complain(&t1->place, "Parse error"); 585 complain_fail(); 586 break; 587 } 588 589 /* otherwise, wait for more input */ 590 break; 591 } 592 } 593 594 static 595 void 596 token(struct place *p, enum tokens tok, int val) 597 { 598 struct token *t; 599 600 t = token_create(p, tok, val); 601 602 tokenarray_add(&tokens, t, NULL); 603 tryreduce(); 604 } 605 606 static 607 int 608 wordval(struct place *p, char *word) 609 { 610 unsigned long val; 611 char *t; 612 613 if (word[0] >= '0' && word[0] <= '9') { 614 errno = 0; 615 val = strtoul(word, &t, 0); 616 if (errno) { 617 complain(p, "Invalid integer constant"); 618 complain_fail(); 619 return 0; 620 } 621 while (*t == 'U' || *t == 'L') { 622 t++; 623 } 624 if (*t != '\0') { 625 complain(p, "Trailing garbage after integer constant"); 626 complain_fail(); 627 return 0; 628 } 629 if (val > INT_MAX) { 630 complain(p, "Integer constant too large"); 631 complain_fail(); 632 return INT_MAX; 633 } 634 return val; 635 } 636 637 /* if it's a symbol, warn and substitute 0. */ 638 if (warns.undef) { 639 complain(p, "Warning: value of undefined symbol %s is 0", 640 word); 641 if (mode.werror) { 642 complain_fail(); 643 } 644 } 645 return 0; 646 } 647 648 static 649 bool 650 check_word(struct place *p, char *expr, size_t pos, size_t *len_ret) 651 { 652 size_t len; 653 int val; 654 char tmp; 655 656 if (!strchr(alnum, expr[pos])) { 657 return false; 658 } 659 len = strspn(expr + pos, alnum); 660 tmp = expr[pos + len]; 661 expr[pos + len] = '\0'; 662 val = wordval(p, expr + pos); 663 expr[pos + len] = tmp; 664 token(p, T_VAL, val); 665 *len_ret = len; 666 return true; 667 } 668 669 static 670 bool 671 check_tokens_2(struct place *p, char *expr, size_t pos) 672 { 673 unsigned i; 674 675 for (i=0; i<num_tokens_2; i++) { 676 if (expr[pos] == tokens_2[i].c1 && 677 expr[pos+1] == tokens_2[i].c2) { 678 token(p, tokens_2[i].tok, 0); 679 return true; 680 } 681 } 682 return false; 683 } 684 685 static 686 bool 687 check_tokens_1(struct place *p, char *expr, size_t pos) 688 { 689 unsigned i; 690 691 for (i=0; i<num_tokens_1; i++) { 692 if (expr[pos] == tokens_1[i].c1) { 693 token(p, tokens_1[i].tok, 0); 694 return true; 695 } 696 } 697 return false; 698 } 699 700 static 701 void 702 tokenize(struct place *p, char *expr) 703 { 704 size_t pos, len; 705 706 pos = 0; 707 while (expr[pos] != '\0') { 708 len = strspn(expr+pos, ws); 709 pos += len; 710 p->column += len; 711 /* trailing whitespace is supposed to have been pruned */ 712 assert(expr[pos] != '\0'); 713 if (check_word(p, expr, pos, &len)) { 714 pos += len; 715 p->column += len; 716 continue; 717 } 718 if (check_tokens_2(p, expr, pos)) { 719 pos += 2; 720 p->column += 2; 721 continue; 722 } 723 if (check_tokens_1(p, expr, pos)) { 724 pos++; 725 p->column++; 726 continue; 727 } 728 complain(p, "Invalid character %u in #if-expression", 729 (unsigned char)expr[pos]); 730 complain_fail(); 731 pos++; 732 p->column++; 733 } 734 token(p, T_EOF, 0); 735 } 736 737 bool 738 eval(struct place *p, char *expr) 739 { 740 struct token *t1, *t2; 741 unsigned num; 742 bool result; 743 744 #ifdef DEBUG 745 fprintf(stderr, "eval: %s\n", expr); 746 #endif 747 748 tokenarray_init(&tokens); 749 tokenize(p, expr); 750 751 result = false; 752 num = tokenarray_num(&tokens); 753 if (num == 2) { 754 t1 = tokenarray_get(&tokens, num-1); 755 t2 = tokenarray_get(&tokens, num-2); 756 if (t2->tok == T_VAL && 757 t1->tok == T_EOF) { 758 result = t2->val != 0; 759 } 760 } 761 762 tokenarray_destroyall(&tokens); 763 tokenarray_cleanup(&tokens); 764 return result; 765 } 766