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