bc.y (14560B)
1 %{ 2 #include <libgen.h> 3 #include <unistd.h> 4 5 #include <ctype.h> 6 #include <errno.h> 7 #include <setjmp.h> 8 #include <stdarg.h> 9 #include <stdio.h> 10 #include <stdlib.h> 11 #include <string.h> 12 13 #include "arg.h" 14 #include "util.h" 15 16 #define DIGITS "0123456789ABCDEF" 17 18 int yydebug; 19 20 typedef struct macro Macro; 21 22 struct macro { 23 char *init; 24 char *cmp; 25 char *inc; 26 char *vars; 27 char *body; 28 int n; 29 }; 30 31 static int yyerror(char *); 32 static int yylex(void); 33 34 static void quit(void); 35 static char *code(char *, ...); 36 static char *forcode(Macro, char *); 37 static char *whilecode(Macro, char *); 38 static char *ifcode(Macro, char *); 39 static char *funcode(Macro, char *, char *); 40 static Macro macro(char *, char *, char *); 41 static Macro function(char *, char *); 42 43 static char *ftn(char *); 44 static char *var(char *); 45 static char *ary(char *); 46 static void writeout(char *); 47 48 static size_t used; 49 static char *yytext, *buff; 50 static char *filename = "<stdin>"; 51 static int lineno, nerr; 52 static jmp_buf recover; 53 static int nested; 54 int cflag, dflag, lflag, sflag; 55 56 %} 57 58 %union { 59 char *str; 60 char id[2]; 61 Macro macro; 62 } 63 64 %token <id> ID 65 %token <str> STRING NUMBER 66 %token <str> EQOP '+' '-' '*' '/' '%' '^' INCDEC 67 %token EQ 68 %token LE 69 %token GE 70 %token NE 71 %token DEF 72 %token BREAK 73 %token QUIT 74 %token LENGTH 75 %token RETURN 76 %token FOR 77 %token IF 78 %token WHILE 79 %token SQRT 80 %token SCALE 81 %token IBASE 82 %token OBASE 83 %token AUTO 84 85 %type <str> assign nexpr expr exprstat rel stat ary statlst 86 %type <str> autolst arglst 87 %type <str> parlst locals local 88 %type <str> function 89 %type <macro> fordef cond funbody 90 91 %right '=' EQOP 92 %left '+' '-' 93 %left '*' '/' '%' 94 %right '^' 95 96 %start program 97 98 %% 99 100 program : 101 | item program 102 ; 103 104 item : scolonlst '\n' {used = 0;} 105 | function {writeout($1); used = 0;} 106 ; 107 108 scolonlst: 109 | stat {writeout($1);} 110 | scolonlst ';' stat {writeout($3);} 111 | scolonlst ';' 112 ; 113 114 statlst : {$$ = code("");} 115 | stat 116 | statlst '\n' stat {$$ = code("%s%s", $1, $3);} 117 | statlst ';' stat {$$ = code("%s%s", $1, $3);} 118 | statlst '\n' 119 | statlst ';' 120 ; 121 122 stat : exprstat 123 | STRING {$$ = code("[%s]P", $1);} 124 | BREAK {$$ = code(" %dQ", nested);} /* FIXME */ 125 | QUIT {quit();} 126 | RETURN {$$ = code(" %dQ", nested);} 127 | RETURN '(' expr ')' {$$ = code(" %s %dQ", $3, nested);} 128 | RETURN '(' ')' {$$ = code(" %dQ", nested);} 129 | FOR fordef stat {$$ = forcode($2, $3);} 130 | IF cond stat {$$ = ifcode($2, $3);} 131 | WHILE cond stat {$$ = whilecode($2, $3);} 132 | '{' statlst '}' {$$ = $2;} 133 ; 134 135 fordef : '(' expr ';' rel ';' expr ')' {$$ = macro($2, $4, $6);} 136 ; 137 138 cond : '(' rel ')' {$$ = macro(NULL, $2, NULL);} 139 ; 140 141 function: DEF ID parlst funbody {$$ = funcode($4, $2, $3);} 142 ; 143 144 funbody : '{' '\n' autolst statlst '}' {$$ = function($3, $4);} 145 ; 146 147 parlst : '(' ')' {$$ = "%s";} 148 | '(' locals ')' {$$ = $2;} 149 ; 150 151 autolst : {$$ = "%s";} 152 | AUTO locals '\n' {$$ = $2;} 153 | AUTO locals ';' {$$ = $2;} 154 ; 155 156 locals : local 157 | locals ',' local {$$ = code("%s%s", $1, $3);} 158 ; 159 160 local : ID {$$ = code("S%s%%sL%ss.", var($1), var($1));} 161 | ID '[' ']' {$$ = code("S%s%%sL%ss.", ary($1), ary($1));} 162 ; 163 164 arglst : expr 165 | ID '[' ']' {$$ = code("%s", ary($1));} 166 | expr ',' arglst {$$ = code("%s%s", $1, $3);} 167 | ID '[' ']' ',' arglst {$$ = code("%s%s", ary($1), $5);} 168 ; 169 170 rel : expr 171 | expr EQ expr {$$ = code("%s%s=", $3, $1);} 172 | expr LE expr {$$ = code("%s%s!>", $3, $1);} 173 | expr GE expr {$$ = code("%s%s!<", $3, $1);} 174 | expr NE expr {$$ = code("%s%s!=", $3, $1);} 175 | expr '<' expr {$$ = code("%s%s<", $3, $1);} 176 | expr '>' expr {$$ = code("%s%s>", $3, $1);} 177 ; 178 179 exprstat: nexpr {$$ = code("%s%s", $1, sflag ? "s." : "p");} 180 | assign {$$ = code("%ss.", $1);} 181 ; 182 183 expr : nexpr 184 | assign 185 ; 186 187 nexpr : NUMBER {$$ = code(" %s", $1);} 188 | ID {$$ = code("l%s", var($1));} 189 | SCALE {$$ = code("K");} 190 | IBASE {$$ = code("I");} 191 | OBASE {$$ = code("O");} 192 | ID ary {$$ = code("%s;%s", $2, ary($1));} 193 | '(' expr ')' {$$ = $2;} 194 | ID '(' arglst ')' {$$ = code("%sl%sx", $3, ftn($1));} 195 | ID '(' ')' {$$ = code("l%sx", ftn($1));} 196 | '-' expr {$$ = code("0%s-", $2);} 197 | expr '+' expr {$$ = code("%s%s+", $1, $3);} 198 | expr '-' expr {$$ = code("%s%s-", $1, $3);} 199 | expr '*' expr {$$ = code("%s%s*", $1, $3);} 200 | expr '/' expr {$$ = code("%s%s/", $1, $3);} 201 | expr '%' expr {$$ = code("%s%s%", $1, $3);} 202 | expr '^' expr {$$ = code("%s%s^", $1, $3);} 203 | LENGTH '(' expr ')' {$$ = code("%sZ", $3);} 204 | SQRT '(' expr ')' {$$ = code("%sv", $3);} 205 | SCALE '(' expr ')' {$$ = code("%sX", $3);} 206 | INCDEC ID {$$ = code("l%s1%sds%s", var($2), $1, var($2));} 207 | INCDEC SCALE {$$ = code("K1%sk", $1);} 208 | INCDEC IBASE {$$ = code("I1%sdi", $1);} 209 | INCDEC OBASE {$$ = code("O1%sdo", $1);} 210 | INCDEC ID ary {$$ = code("%sdS_;%s1%sdL_:%s", $3, ary($2), $1, ary($2));} 211 | ID INCDEC {$$ = code("l%sd1%ss%s", var($1), $2, var($1));} 212 | SCALE INCDEC {$$ = code("Kd1%sk", $2);} 213 | IBASE INCDEC {$$ = code("Id1%si", $2);} 214 | OBASE INCDEC {$$ = code("Od1%so", $2);} 215 | ID ary INCDEC {$$ = code("%sds.;%sd1%sl.:%s", $2, ary($1), $3, ary($1));} 216 ; 217 218 assign : ID '=' expr {$$ = code("%sds%s", $3, var($1));} 219 | SCALE '=' expr {$$ = code("%sdk", $3);} 220 | IBASE '=' expr {$$ = code("%sdi", $3);} 221 | OBASE '=' expr {$$ = code("%sdo", $3);} 222 | ID ary '=' expr {$$ = code("%sd%s:%s", $4, $2, ary($1));} 223 | ID EQOP expr {$$ = code("%sl%s%sds%s", $3, var($1), $2, var($1));} 224 | SCALE EQOP expr {$$ = code("%sK%sdk", $3, $2);} 225 | IBASE EQOP expr {$$ = code("%sI%sdi", $3, $2);} 226 | OBASE EQOP expr {$$ = code("%sO%sdo", $3, $2);} 227 | ID ary EQOP expr {$$ = code("%s%sds.;%s%sdl.:s", $4, $2, ary($1), $3, ary($1));} 228 ; 229 230 ary : '[' expr ']' {$$ = $2;} 231 ; 232 233 %% 234 static int 235 yyerror(char *s) 236 { 237 fprintf(stderr, "bc: %s:%d:%s\n", filename, lineno, s); 238 nerr++; 239 longjmp(recover, 1); 240 } 241 242 static void 243 writeout(char *s) 244 { 245 if (write(1, s, strlen(s)) < 0) 246 goto err; 247 if (write(1, (char[]){'\n'}, 1) < 0) 248 goto err; 249 return; 250 251 err: 252 eprintf("writing to dc:"); 253 } 254 255 static char * 256 code(char *fmt, ...) 257 { 258 char *s; 259 va_list va; 260 size_t n, room; 261 262 va_start(va, fmt); 263 room = BUFSIZ - used; 264 n = vsnprintf(buff+used, room, fmt, va); 265 va_end(va); 266 267 if (n < 0 || n >= room) 268 eprintf("unable to code requested operation\n"); 269 270 s = buff + used; 271 used += n + 1; 272 273 return s; 274 } 275 276 static char * 277 funcode(Macro d, char *id, char *params) 278 { 279 char *s; 280 281 s = code(d.vars, d.body); 282 s = code(params, s); 283 s = code("[%s]s%s", s, id); 284 return s; 285 } 286 287 static char * 288 forcode(Macro d, char *body) 289 { 290 char *s; 291 292 s = code("[%s%ss.%s%d]s%d", 293 body, 294 d.inc, 295 d.cmp, 296 d.n, d.n); 297 writeout(s); 298 299 s = code("%ss.l%dx", d.init, d.n); 300 nested--; 301 302 return s; 303 } 304 305 static char * 306 whilecode(Macro d, char *body) 307 { 308 char *s; 309 310 s = code("[%ss.%s%d]s%d", body, d.cmp, d.n, d.n); 311 writeout(s); 312 313 s = code("l%dx", d.n); 314 nested--; 315 316 return s; 317 } 318 319 static char * 320 ifcode(Macro d, char *body) 321 { 322 char *s; 323 324 s = code("[%s]s%d", body, d.n); 325 writeout(s); 326 327 s = code("%s%d", d.cmp, d.n); 328 nested--; 329 330 return s; 331 } 332 333 static Macro 334 macro(char *init, char *cmp, char *inc) 335 { 336 Macro d; 337 338 if (nested == 10) 339 yyerror("bc:too much nesting"); 340 341 d.init = init; 342 d.cmp = cmp; 343 d.inc = inc; 344 d.n = nested++; 345 346 return d; 347 } 348 349 static Macro 350 function(char *vars, char *body) 351 { 352 Macro d; 353 354 if (nested == 10) 355 yyerror("bc:too much nesting"); 356 357 d.vars = vars; 358 d.body = body; 359 d.n = nested++; 360 361 return d; 362 } 363 364 static char * 365 ary(char *s) 366 { 367 return code("%c", toupper(s[0])); 368 } 369 370 static char * 371 ftn(char *s) 372 { 373 return code("%c", s[0] | 0x80); 374 } 375 376 static char * 377 var(char *s) 378 { 379 return code("%s", s); 380 } 381 382 static void 383 quit(void) 384 { 385 exit(nerr > 0 ? 1 : 0); 386 } 387 388 static void 389 skipspaces(void) 390 { 391 int ch; 392 393 while (isspace(ch = getchar())) { 394 if (ch == '\n') { 395 lineno++; 396 break; 397 } 398 } 399 ungetc(ch, stdin); 400 } 401 402 static int 403 iden(int ch) 404 { 405 static struct keyword { 406 char *str; 407 int token; 408 } keywords[] = { 409 {"define", DEF}, 410 {"break", BREAK}, 411 {"quit", QUIT}, 412 {"length", LENGTH}, 413 {"return", RETURN}, 414 {"for", FOR}, 415 {"if", IF}, 416 {"while", WHILE}, 417 {"sqrt", SQRT}, 418 {"scale", SCALE}, 419 {"ibase", IBASE}, 420 {"obase", OBASE}, 421 {"auto", AUTO}, 422 {NULL} 423 }; 424 struct keyword *p; 425 char *bp; 426 427 ungetc(ch, stdin); 428 for (bp = yytext; bp < &yytext[BUFSIZ]; ++bp) { 429 ch = getchar(); 430 if (!islower(ch)) 431 break; 432 *bp = ch; 433 } 434 435 if (bp == &yytext[BUFSIZ]) 436 yyerror("too long token"); 437 *bp = '\0'; 438 ungetc(ch, stdin); 439 440 if (strlen(yytext) == 1) { 441 strcpy(yylval.id, yytext); 442 return ID; 443 } 444 445 for (p = keywords; p->str && strcmp(p->str, yytext); ++p) 446 ; 447 448 if (!p->str) 449 yyerror("invalid keyword"); 450 451 return p->token; 452 } 453 454 static char * 455 digits(char *bp) 456 { 457 int ch; 458 char *digits = DIGITS, *p; 459 460 while (bp < &yytext[BUFSIZ]) { 461 ch = getchar(); 462 p = strchr(digits, ch); 463 if (!p) 464 break; 465 *bp++ = ch; 466 } 467 468 if (bp == &yytext[BUFSIZ]) 469 return NULL; 470 ungetc(ch, stdin); 471 472 return bp; 473 } 474 475 static int 476 number(int ch) 477 { 478 int d; 479 char *bp; 480 481 ungetc(ch, stdin); 482 if ((bp = digits(yytext)) == NULL) 483 goto toolong; 484 485 if ((ch = getchar()) != '.') { 486 ungetc(ch, stdin); 487 goto end; 488 } 489 *bp++ = '.'; 490 491 if ((bp = digits(bp)) == NULL) 492 goto toolong; 493 494 end: 495 if (bp == &yytext[BUFSIZ]) 496 goto toolong; 497 *bp = '\0'; 498 yylval.str = yytext; 499 500 return NUMBER; 501 502 toolong: 503 yyerror("bc:too long number"); 504 } 505 506 static int 507 string(int ch) 508 { 509 char *bp; 510 511 for (bp = yytext; bp < &yytext[BUFSIZ]; ++bp) { 512 if ((ch = getchar()) == '"') 513 break; 514 *bp = ch; 515 } 516 517 if (bp == &yytext[BUFSIZ]) 518 yyerror("bc:too long string"); 519 *bp = '\0'; 520 yylval.str = yytext; 521 522 return STRING; 523 } 524 525 static int 526 follow(int next, int yes, int no) 527 { 528 int ch; 529 530 ch = getchar(); 531 if (ch == next) 532 return yes; 533 ungetc(ch, stdin); 534 return no; 535 } 536 537 static int 538 operand(int ch) 539 { 540 switch (ch) { 541 case '\n': 542 case '{': 543 case '}': 544 case '[': 545 case ']': 546 case '(': 547 case ')': 548 case ',': 549 case ';': 550 return ch; 551 case '"': 552 return string(ch); 553 case '*': 554 yylval.str = "*"; 555 return follow('=', EQOP, '*'); 556 case '/': 557 yylval.str = "/"; 558 return follow('=', EQOP, '/'); 559 case '%': 560 yylval.str = "%"; 561 return follow('=', EQOP, '%'); 562 case '=': 563 return follow('=', EQ, '='); 564 case '+': 565 case '-': 566 yylval.str = (ch == '+') ? "+" : "-"; 567 if (follow('=', EQOP, ch) != ch) 568 return EQOP; 569 return follow(ch, INCDEC, ch); 570 case '^': 571 yylval.str = "^"; 572 return follow('=', EQOP, '^'); 573 case '<': 574 return follow('=', LE, '<'); 575 case '>': 576 return follow('=', GE, '>'); 577 case '!': 578 if (getchar() == '=') 579 return NE; 580 default: 581 yyerror("invalid operand"); 582 } 583 } 584 585 static void 586 comment(void) 587 { 588 int c; 589 590 for (;;) { 591 while ((c = getchar()) != '*') { 592 if (c == '\n') 593 lineno++; 594 } 595 if ((c = getchar()) == '/') 596 break; 597 ungetc(c, stdin); 598 } 599 } 600 601 static int 602 yylex(void) 603 { 604 int peekc, ch; 605 606 repeat: 607 skipspaces(); 608 609 ch = getchar(); 610 if (ch == EOF) { 611 return EOF; 612 } else if (!isascii(ch)) { 613 yyerror("invalid input character"); 614 } else if (islower(ch)) { 615 return iden(ch); 616 } else if (ch == '.' || strchr(DIGITS, ch)) { 617 return number(ch); 618 } else { 619 if (ch == '/') { 620 peekc = getchar(); 621 if (peekc == '*') { 622 comment(); 623 goto repeat; 624 } 625 ungetc(peekc, stdin); 626 } 627 return operand(ch); 628 } 629 } 630 631 static void 632 spawn(void) 633 { 634 int fds[2]; 635 char errmsg[] = "bc:error execing dc\n"; 636 637 if (pipe(fds) < 0) 638 eprintf("creating pipe:"); 639 640 switch (fork()) { 641 case -1: 642 eprintf("forking dc:"); 643 case 0: 644 close(1); 645 dup(fds[1]); 646 close(fds[0]); 647 close(fds[1]); 648 break; 649 default: 650 close(0); 651 dup(fds[0]); 652 close(fds[0]); 653 close(fds[1]); 654 execlp("dc", "dc", (char *) NULL); 655 656 /* it shouldn't happen */ 657 write(3, errmsg, sizeof(errmsg)-1); 658 _Exit(2); 659 } 660 } 661 662 static void 663 init(void) 664 { 665 used = 0; 666 667 if (!yytext) 668 yytext = malloc(BUFSIZ); 669 if (!buff) 670 buff = malloc(BUFSIZ); 671 if (!yytext || !buff) 672 eprintf("out of memory\n"); 673 } 674 675 static int 676 run(void) 677 { 678 if (feof(stdin)) 679 return 0; 680 681 if (setjmp(recover)) 682 return 1; 683 684 yyparse(); 685 return 1; 686 } 687 688 static void 689 bc(char *fname) 690 { 691 lineno = 1; 692 nested = 0; 693 694 if (fname) { 695 filename = fname; 696 if (!freopen(fname, "r", stdin)) 697 eprintf("%s:", fname); 698 } 699 700 for (init(); run(); init()) 701 ; 702 nested = used = 0; 703 } 704 static void 705 loadlib(void) 706 { 707 int r; 708 size_t len; 709 char bin[FILENAME_MAX], fname[FILENAME_MAX]; 710 static char bclib[] = "bc.library"; 711 712 /* 713 * try first to load the library from the same directory than 714 * the executable, because that can makes easier the tests 715 * because it does not require to install to test the last version 716 */ 717 len = strlen(argv0); 718 if (len >= FILENAME_MAX) 719 goto share; 720 memcpy(bin, argv0, len + 1); 721 722 r = snprintf(fname, sizeof(fname), "%s/%s", dirname(bin), bclib); 723 if (r < 0 || r >= sizeof(fname)) 724 goto share; 725 726 if (access(fname, F_OK) < 0) 727 goto share; 728 729 bc(fname); 730 return; 731 732 share: 733 r = snprintf(fname, sizeof(fname), "%s/share/misc/%s", PREFIX, bclib); 734 if (r < 0 || r >= sizeof(fname)) 735 eprintf("invalid path name for bc.library\n"); 736 bc(fname); 737 } 738 739 static void 740 usage(void) 741 { 742 eprintf("usage: %s [-cdls]\n", argv0); 743 } 744 745 int 746 main(int argc, char *argv[]) 747 { 748 ARGBEGIN { 749 case 'c': 750 cflag = 1; 751 break; 752 case 'd': 753 dflag = 1; 754 yydebug = 3; 755 break; 756 case 'l': 757 lflag = 1; 758 break; 759 case 's': 760 sflag = 1; 761 break; 762 default: 763 usage(); 764 } ARGEND 765 766 if (!cflag) 767 spawn(); 768 if (lflag) 769 loadlib(); 770 771 if (*argv == NULL) { 772 bc(NULL); 773 } else { 774 while (*argv) 775 bc(*argv++); 776 } 777 778 quit(); 779 }