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