units.y (11208B)
1 %{ 2 #include <u.h> 3 #include <libc.h> 4 #include <bio.h> 5 6 enum 7 { 8 Ndim = 15, /* number of dimensions */ 9 Nsym = 40, /* size of a name */ 10 Nvar = 203, /* hash table size */ 11 Maxe = 695 /* log of largest number */ 12 }; 13 14 typedef struct Var Var; 15 typedef struct Node Node; 16 typedef struct Prefix Prefix; 17 18 struct Node 19 { 20 double val; 21 schar dim[Ndim]; 22 }; 23 struct Var 24 { 25 Rune name[Nsym]; 26 Node node; 27 Var* link; 28 }; 29 struct Prefix 30 { 31 double val; 32 char* name; 33 Rune* pname; 34 }; 35 36 char buf[100]; 37 int digval; 38 Biobuf* fi; 39 Biobuf linebuf; 40 Var* fund[Ndim]; 41 Rune line[1000]; 42 ulong lineno; 43 int linep; 44 int nerrors; 45 Node one; 46 int peekrune; 47 Node retnode1; 48 Node retnode2; 49 Node retnode; 50 Rune sym[Nsym]; 51 Var* vars[Nvar]; 52 int vflag; 53 54 #define div unitsdiv 55 56 extern void add(Node*, Node*, Node*); 57 extern void div(Node*, Node*, Node*); 58 extern int specialcase(Node*, Node*, Node*); 59 extern double fadd(double, double); 60 extern double fdiv(double, double); 61 extern double fmul(double, double); 62 extern int gdigit(void*); 63 extern Var* lookup(int); 64 extern void main(int, char*[]); 65 extern void mul(Node*, Node*, Node*); 66 extern void ofile(void); 67 extern double pname(void); 68 extern void printdim(char*, int, int); 69 extern int ralpha(int); 70 extern int readline(void); 71 extern void sub(Node*, Node*, Node*); 72 extern int Ufmt(Fmt*); 73 extern void xpn(Node*, Node*, int); 74 extern void yyerror(char*, ...); 75 extern int yylex(void); 76 extern int yyparse(void); 77 78 typedef Node* indnode; 79 /* #pragma varargck type "U" indnode */ 80 81 %} 82 %union 83 { 84 Node node; 85 Var* var; 86 int numb; 87 double val; 88 } 89 90 %type <node> prog expr expr0 expr1 expr2 expr3 expr4 91 92 %token <val> VAL 93 %token <var> VAR 94 %token <numb> SUP 95 %% 96 prog: 97 ':' VAR expr 98 { 99 int f; 100 101 f = $2->node.dim[0]; 102 $2->node = $3; 103 $2->node.dim[0] = 1; 104 if(f) 105 yyerror("redefinition of %S", $2->name); 106 else 107 if(vflag) 108 print("%S\t%U\n", $2->name, &$2->node); 109 } 110 | ':' VAR '#' 111 { 112 int f, i; 113 114 for(i=1; i<Ndim; i++) 115 if(fund[i] == 0) 116 break; 117 if(i >= Ndim) { 118 yyerror("too many dimensions"); 119 i = Ndim-1; 120 } 121 fund[i] = $2; 122 123 f = $2->node.dim[0]; 124 $2->node = one; 125 $2->node.dim[0] = 1; 126 $2->node.dim[i] = 1; 127 if(f) 128 yyerror("redefinition of %S", $2->name); 129 else 130 if(vflag) 131 print("%S\t#\n", $2->name); 132 } 133 | '?' expr 134 { 135 retnode1 = $2; 136 } 137 | '?' 138 { 139 retnode1 = one; 140 } 141 142 expr: 143 expr4 144 | expr '+' expr4 145 { 146 add(&$$, &$1, &$3); 147 } 148 | expr '-' expr4 149 { 150 sub(&$$, &$1, &$3); 151 } 152 153 expr4: 154 expr3 155 | expr4 '*' expr3 156 { 157 mul(&$$, &$1, &$3); 158 } 159 | expr4 '/' expr3 160 { 161 div(&$$, &$1, &$3); 162 } 163 164 expr3: 165 expr2 166 | expr3 expr2 167 { 168 mul(&$$, &$1, &$2); 169 } 170 171 expr2: 172 expr1 173 | expr2 SUP 174 { 175 xpn(&$$, &$1, $2); 176 } 177 | expr2 '^' expr1 178 { 179 int i; 180 181 for(i=1; i<Ndim; i++) 182 if($3.dim[i]) { 183 yyerror("exponent has units"); 184 $$ = $1; 185 break; 186 } 187 if(i >= Ndim) { 188 i = $3.val; 189 if(i != $3.val) 190 yyerror("exponent not integral"); 191 xpn(&$$, &$1, i); 192 } 193 } 194 195 expr1: 196 expr0 197 | expr1 '|' expr0 198 { 199 div(&$$, &$1, &$3); 200 } 201 202 expr0: 203 VAR 204 { 205 if($1->node.dim[0] == 0) { 206 yyerror("undefined %S", $1->name); 207 $$ = one; 208 } else 209 $$ = $1->node; 210 } 211 | VAL 212 { 213 $$ = one; 214 $$.val = $1; 215 } 216 | '(' expr ')' 217 { 218 $$ = $2; 219 } 220 %% 221 222 int 223 yylex(void) 224 { 225 int c, i; 226 227 c = peekrune; 228 peekrune = ' '; 229 230 loop: 231 if((c >= '0' && c <= '9') || c == '.') 232 goto numb; 233 if(ralpha(c)) 234 goto alpha; 235 switch(c) { 236 case ' ': 237 case '\t': 238 c = line[linep++]; 239 goto loop; 240 case 0xd7: 241 return 0x2a; 242 case 0xf7: 243 return 0x2f; 244 case 0xb9: 245 case 0x2071: 246 yylval.numb = 1; 247 return SUP; 248 case 0xb2: 249 case 0x2072: 250 yylval.numb = 2; 251 return SUP; 252 case 0xb3: 253 case 0x2073: 254 yylval.numb = 3; 255 return SUP; 256 } 257 return c; 258 259 alpha: 260 memset(sym, 0, sizeof(sym)); 261 for(i=0;; i++) { 262 if(i < nelem(sym)) 263 sym[i] = c; 264 c = line[linep++]; 265 if(!ralpha(c)) 266 break; 267 } 268 sym[nelem(sym)-1] = 0; 269 peekrune = c; 270 yylval.var = lookup(0); 271 return VAR; 272 273 numb: 274 digval = c; 275 yylval.val = fmtcharstod(gdigit, 0); 276 return VAL; 277 } 278 279 void 280 main(int argc, char *argv[]) 281 { 282 char *file; 283 284 ARGBEGIN { 285 default: 286 print("usage: units [-v] [file]\n"); 287 exits("usage"); 288 case 'v': 289 vflag = 1; 290 break; 291 } ARGEND 292 293 file = unsharp("#9/lib/units"); 294 if(argc > 0) 295 file = argv[0]; 296 fi = Bopen(file, OREAD); 297 if(fi == 0) { 298 print("cant open: %s\n", file); 299 exits("open"); 300 } 301 fmtinstall('U', Ufmt); 302 one.val = 1; 303 304 /* 305 * read the 'units' file to 306 * develope a database 307 */ 308 lineno = 0; 309 for(;;) { 310 lineno++; 311 if(readline()) 312 break; 313 if(line[0] == 0 || line[0] == '/') 314 continue; 315 peekrune = ':'; 316 yyparse(); 317 } 318 319 /* 320 * read the console to 321 * print ratio of pairs 322 */ 323 Bterm(fi); 324 fi = &linebuf; 325 Binit(fi, 0, OREAD); 326 lineno = 0; 327 for(;;) { 328 if(lineno & 1) 329 print("you want: "); 330 else 331 print("you have: "); 332 if(readline()) 333 break; 334 peekrune = '?'; 335 nerrors = 0; 336 yyparse(); 337 if(nerrors) 338 continue; 339 if(lineno & 1) { 340 if(specialcase(&retnode, &retnode2, &retnode1)) 341 print("\tis %U\n", &retnode); 342 else { 343 div(&retnode, &retnode2, &retnode1); 344 print("\t* %U\n", &retnode); 345 div(&retnode, &retnode1, &retnode2); 346 print("\t/ %U\n", &retnode); 347 } 348 } else 349 retnode2 = retnode1; 350 lineno++; 351 } 352 print("\n"); 353 exits(0); 354 } 355 356 /* 357 * all characters that have some 358 * meaning. rest are usable as names 359 */ 360 int 361 ralpha(int c) 362 { 363 switch(c) { 364 case 0: 365 case '+': 366 case '-': 367 case '*': 368 case '/': 369 case '[': 370 case ']': 371 case '(': 372 case ')': 373 case '^': 374 case ':': 375 case '?': 376 case ' ': 377 case '\t': 378 case '.': 379 case '|': 380 case '#': 381 case 0xb9: 382 case 0x2071: 383 case 0xb2: 384 case 0x2072: 385 case 0xb3: 386 case 0x2073: 387 case 0xd7: 388 case 0xf7: 389 return 0; 390 } 391 return 1; 392 } 393 394 int 395 gdigit(void *v) 396 { 397 int c; 398 399 USED(v); 400 c = digval; 401 if(c) { 402 digval = 0; 403 return c; 404 } 405 c = line[linep++]; 406 peekrune = c; 407 return c; 408 } 409 410 void 411 yyerror(char *fmt, ...) 412 { 413 va_list arg; 414 415 /* 416 * hack to intercept message from yaccpar 417 */ 418 if(strcmp(fmt, "syntax error") == 0) { 419 yyerror("syntax error, last name: %S", sym); 420 return; 421 } 422 va_start(arg, fmt); 423 vseprint(buf, buf+sizeof(buf), fmt, arg); 424 va_end(arg); 425 print("%ld: %S\n\t%s\n", lineno, line, buf); 426 nerrors++; 427 if(nerrors > 5) { 428 print("too many errors\n"); 429 exits("errors"); 430 } 431 } 432 433 void 434 add(Node *c, Node *a, Node *b) 435 { 436 int i, d; 437 438 for(i=0; i<Ndim; i++) { 439 d = a->dim[i]; 440 c->dim[i] = d; 441 if(d != b->dim[i]) 442 yyerror("add must be like units"); 443 } 444 c->val = fadd(a->val, b->val); 445 } 446 447 void 448 sub(Node *c, Node *a, Node *b) 449 { 450 int i, d; 451 452 for(i=0; i<Ndim; i++) { 453 d = a->dim[i]; 454 c->dim[i] = d; 455 if(d != b->dim[i]) 456 yyerror("sub must be like units"); 457 } 458 c->val = fadd(a->val, -b->val); 459 } 460 461 void 462 mul(Node *c, Node *a, Node *b) 463 { 464 int i; 465 466 for(i=0; i<Ndim; i++) 467 c->dim[i] = a->dim[i] + b->dim[i]; 468 c->val = fmul(a->val, b->val); 469 } 470 471 void 472 div(Node *c, Node *a, Node *b) 473 { 474 int i; 475 476 for(i=0; i<Ndim; i++) 477 c->dim[i] = a->dim[i] - b->dim[i]; 478 c->val = fdiv(a->val, b->val); 479 } 480 481 void 482 xpn(Node *c, Node *a, int b) 483 { 484 int i; 485 486 *c = one; 487 if(b < 0) { 488 b = -b; 489 for(i=0; i<b; i++) 490 div(c, c, a); 491 } else 492 for(i=0; i<b; i++) 493 mul(c, c, a); 494 } 495 496 int 497 specialcase(Node *c, Node *a, Node *b) 498 { 499 int i, d, d1, d2; 500 501 d1 = 0; 502 d2 = 0; 503 for(i=1; i<Ndim; i++) { 504 d = a->dim[i]; 505 if(d) { 506 if(d != 1 || d1) 507 return 0; 508 d1 = i; 509 } 510 d = b->dim[i]; 511 if(d) { 512 if(d != 1 || d2) 513 return 0; 514 d2 = i; 515 } 516 } 517 if(d1 == 0 || d2 == 0) 518 return 0; 519 520 if(memcmp(fund[d1]->name, L"°C", 3*sizeof(Rune)) == 0 && 521 memcmp(fund[d2]->name, L"°F", 3*sizeof(Rune)) == 0 && 522 b->val == 1) { 523 memcpy(c->dim, b->dim, sizeof(c->dim)); 524 c->val = a->val * 9. / 5. + 32.; 525 return 1; 526 } 527 528 if(memcmp(fund[d1]->name, L"°F", 3*sizeof(Rune)) == 0 && 529 memcmp(fund[d2]->name, L"°C", 3*sizeof(Rune)) == 0 && 530 b->val == 1) { 531 memcpy(c->dim, b->dim, sizeof(c->dim)); 532 c->val = (a->val - 32.) * 5. / 9.; 533 return 1; 534 } 535 return 0; 536 } 537 538 void 539 printdim(char *str, int d, int n) 540 { 541 Var *v; 542 543 if(n) { 544 v = fund[d]; 545 if(v) 546 sprint(strchr(str, 0), " %S", v->name); 547 else 548 sprint(strchr(str, 0), " [%d]", d); 549 switch(n) { 550 case 1: 551 break; 552 case 2: 553 strcat(str, "²"); 554 break; 555 case 3: 556 strcat(str, "³"); 557 break; 558 default: 559 sprint(strchr(str, 0), "^%d", n); 560 } 561 } 562 } 563 564 int 565 Ufmt(Fmt *fp) 566 { 567 char str[200]; 568 Node *n; 569 int f, i, d; 570 571 n = va_arg(fp->args, Node*); 572 sprint(str, "%g", n->val); 573 574 f = 0; 575 for(i=1; i<Ndim; i++) { 576 d = n->dim[i]; 577 if(d > 0) 578 printdim(str, i, d); 579 else 580 if(d < 0) 581 f = 1; 582 } 583 584 if(f) { 585 strcat(str, " /"); 586 for(i=1; i<Ndim; i++) { 587 d = n->dim[i]; 588 if(d < 0) 589 printdim(str, i, -d); 590 } 591 } 592 593 return fmtstrcpy(fp, str); 594 } 595 596 int 597 readline(void) 598 { 599 int i, c; 600 601 linep = 0; 602 for(i=0;; i++) { 603 c = Bgetrune(fi); 604 if(c < 0) 605 return 1; 606 if(c == '\n') 607 break; 608 if(i < nelem(line)) 609 line[i] = c; 610 } 611 if(i >= nelem(line)) 612 i = nelem(line)-1; 613 line[i] = 0; 614 return 0; 615 } 616 617 Var* 618 lookup(int f) 619 { 620 int i; 621 Var *v, *w; 622 double p; 623 ulong h; 624 625 h = 0; 626 for(i=0; sym[i]; i++) 627 h = h*13 + sym[i]; 628 h %= nelem(vars); 629 630 for(v=vars[h]; v; v=v->link) 631 if(memcmp(sym, v->name, sizeof(sym)) == 0) 632 return v; 633 if(f) 634 return 0; 635 v = malloc(sizeof(*v)); 636 if(v == nil) { 637 fprint(2, "out of memory\n"); 638 exits("mem"); 639 } 640 memset(v, 0, sizeof(*v)); 641 memcpy(v->name, sym, sizeof(sym)); 642 v->link = vars[h]; 643 vars[h] = v; 644 645 p = 1; 646 for(;;) { 647 p = fmul(p, pname()); 648 if(p == 0) 649 break; 650 w = lookup(1); 651 if(w) { 652 v->node = w->node; 653 v->node.val = fmul(v->node.val, p); 654 break; 655 } 656 } 657 return v; 658 } 659 660 Prefix prefix[] = 661 { 662 1e-24, "yocto", 0, 663 1e-21, "zepto", 0, 664 1e-18, "atto", 0, 665 1e-15, "femto", 0, 666 1e-12, "pico", 0, 667 1e-9, "nano", 0, 668 1e-6, "micro", 0, 669 1e-6, "μ", 0, 670 1e-3, "milli", 0, 671 1e-2, "centi", 0, 672 1e-1, "deci", 0, 673 1e1, "deka", 0, 674 1e2, "hecta", 0, 675 1e2, "hecto", 0, 676 1e3, "kilo", 0, 677 1e6, "mega", 0, 678 1e6, "meg", 0, 679 1e9, "giga", 0, 680 1e12, "tera", 0, 681 1e15, "peta", 0, 682 1e18, "exa", 0, 683 1e21, "zetta", 0, 684 1e24, "yotta", 0, 685 0, 0, 0, 686 }; 687 688 double 689 pname(void) 690 { 691 Rune *p; 692 int i, j, c; 693 694 /* 695 * rip off normal prefixs 696 */ 697 if(prefix[0].pname == nil){ 698 for(i=0; prefix[i].name; i++) 699 prefix[i].pname = runesmprint("%s", prefix[i].name); 700 } 701 702 for(i=0; p=prefix[i].pname; i++) { 703 for(j=0; c=p[j]; j++) 704 if(c != sym[j]) 705 goto no; 706 memmove(sym, sym+j, (Nsym-j)*sizeof(*sym)); 707 memset(sym+(Nsym-j), 0, j*sizeof(*sym)); 708 return prefix[i].val; 709 no:; 710 } 711 712 /* 713 * rip off 's' suffixes 714 */ 715 for(j=0; sym[j]; j++) 716 ; 717 j--; 718 /* j>1 is special hack to disallow ms finding m */ 719 if(j > 1 && sym[j] == 's') { 720 sym[j] = 0; 721 return 1; 722 } 723 return 0; 724 } 725 726 /* 727 * careful floating point 728 */ 729 double 730 fmul(double a, double b) 731 { 732 double l; 733 734 if(a <= 0) { 735 if(a == 0) 736 return 0; 737 l = log(-a); 738 } else 739 l = log(a); 740 741 if(b <= 0) { 742 if(b == 0) 743 return 0; 744 l += log(-b); 745 } else 746 l += log(b); 747 748 if(l > Maxe) { 749 yyerror("overflow in multiply"); 750 return 1; 751 } 752 if(l < -Maxe) { 753 yyerror("underflow in multiply"); 754 return 0; 755 } 756 return a*b; 757 } 758 759 double 760 fdiv(double a, double b) 761 { 762 double l; 763 764 if(a <= 0) { 765 if(a == 0) 766 return 0; 767 l = log(-a); 768 } else 769 l = log(a); 770 771 if(b <= 0) { 772 if(b == 0) { 773 yyerror("division by zero"); 774 return 1; 775 } 776 l -= log(-b); 777 } else 778 l -= log(b); 779 780 if(l > Maxe) { 781 yyerror("overflow in divide"); 782 return 1; 783 } 784 if(l < -Maxe) { 785 yyerror("underflow in divide"); 786 return 0; 787 } 788 return a/b; 789 } 790 791 double 792 fadd(double a, double b) 793 { 794 return a + b; 795 }