Blob


1 %{
2 #include <u.h>
3 #include <libc.h>
4 #include <bio.h>
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 };
14 typedef struct Var Var;
15 typedef struct Node Node;
16 typedef struct Prefix Prefix;
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 };
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;
54 #define div unitsdiv
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);
78 typedef Node* indnode;
79 /* #pragma varargck type "U" indnode */
81 %}
82 %union
83 {
84 Node node;
85 Var* var;
86 int numb;
87 double val;
88 }
90 %type <node> prog expr expr0 expr1 expr2 expr3 expr4
92 %token <val> VAL
93 %token <var> VAR
94 %token <numb> SUP
95 %%
96 prog:
97 ':' VAR expr
98 {
99 int f;
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);
110 | ':' VAR '#'
112 int f, i;
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;
121 fund[i] = $2;
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);
133 | '?' expr
135 retnode1 = $2;
137 | '?'
139 retnode1 = one;
142 expr:
143 expr4
144 | expr '+' expr4
146 add(&$$, &$1, &$3);
148 | expr '-' expr4
150 sub(&$$, &$1, &$3);
153 expr4:
154 expr3
155 | expr4 '*' expr3
157 mul(&$$, &$1, &$3);
159 | expr4 '/' expr3
161 div(&$$, &$1, &$3);
164 expr3:
165 expr2
166 | expr3 expr2
168 mul(&$$, &$1, &$2);
171 expr2:
172 expr1
173 | expr2 SUP
175 xpn(&$$, &$1, $2);
177 | expr2 '^' expr1
179 int i;
181 for(i=1; i<Ndim; i++)
182 if($3.dim[i]) {
183 yyerror("exponent has units");
184 $$ = $1;
185 break;
187 if(i >= Ndim) {
188 i = $3.val;
189 if(i != $3.val)
190 yyerror("exponent not integral");
191 xpn(&$$, &$1, i);
195 expr1:
196 expr0
197 | expr1 '|' expr0
199 div(&$$, &$1, &$3);
202 expr0:
203 VAR
205 if($1->node.dim[0] == 0) {
206 yyerror("undefined %S", $1->name);
207 $$ = one;
208 } else
209 $$ = $1->node;
211 | VAL
213 $$ = one;
214 $$.val = $1;
216 | '(' expr ')'
218 $$ = $2;
220 %%
222 int
223 yylex(void)
225 int c, i;
227 c = peekrune;
228 peekrune = ' ';
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;
257 return c;
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;
268 sym[nelem(sym)-1] = 0;
269 peekrune = c;
270 yylval.var = lookup(0);
271 return VAR;
273 numb:
274 digval = c;
275 yylval.val = fmtcharstod(gdigit, 0);
276 return VAL;
279 void
280 main(int argc, char *argv[])
282 char *file;
284 ARGBEGIN {
285 default:
286 print("usage: units [-v] [file]\n");
287 exits("usage");
288 case 'v':
289 vflag = 1;
290 break;
291 } ARGEND
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");
301 fmtinstall('U', Ufmt);
302 one.val = 1;
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();
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);
348 } else
349 retnode2 = retnode1;
350 lineno++;
352 print("\n");
353 exits(0);
356 /*
357 * all characters that have some
358 * meaning. rest are usable as names
359 */
360 int
361 ralpha(int c)
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;
391 return 1;
394 int
395 gdigit(void *v)
397 int c;
399 USED(v);
400 c = digval;
401 if(c) {
402 digval = 0;
403 return c;
405 c = line[linep++];
406 peekrune = c;
407 return c;
410 void
411 yyerror(char *fmt, ...)
413 va_list arg;
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;
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");
433 void
434 add(Node *c, Node *a, Node *b)
436 int i, d;
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");
444 c->val = fadd(a->val, b->val);
447 void
448 sub(Node *c, Node *a, Node *b)
450 int i, d;
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");
458 c->val = fadd(a->val, -b->val);
461 void
462 mul(Node *c, Node *a, Node *b)
464 int i;
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);
471 void
472 div(Node *c, Node *a, Node *b)
474 int i;
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);
481 void
482 xpn(Node *c, Node *a, int b)
484 int i;
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);
496 int
497 specialcase(Node *c, Node *a, Node *b)
499 int i, d, d1, d2;
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;
510 d = b->dim[i];
511 if(d) {
512 if(d != 1 || d2)
513 return 0;
514 d2 = i;
517 if(d1 == 0 || d2 == 0)
518 return 0;
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;
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;
535 return 0;
538 void
539 printdim(char *str, int d, int n)
541 Var *v;
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);
564 int
565 Ufmt(Fmt *fp)
567 char str[200];
568 Node *n;
569 int f, i, d;
571 n = va_arg(fp->args, Node*);
572 sprint(str, "%g", n->val);
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;
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);
593 return fmtstrcpy(fp, str);
596 int
597 readline(void)
599 int i, c;
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;
611 if(i >= nelem(line))
612 i = nelem(line)-1;
613 line[i] = 0;
614 return 0;
617 Var*
618 lookup(int f)
620 int i;
621 Var *v, *w;
622 double p;
623 ulong h;
625 h = 0;
626 for(i=0; sym[i]; i++)
627 h = h*13 + sym[i];
628 h %= nelem(vars);
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");
640 memset(v, 0, sizeof(*v));
641 memcpy(v->name, sym, sizeof(sym));
642 v->link = vars[h];
643 vars[h] = v;
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;
657 return v;
660 Prefix prefix[] =
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 };
688 double
689 pname(void)
691 Rune *p;
692 int i, j, c;
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);
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:;
712 /*
713 * rip off 's' suffixes
714 */
715 for(j=0; sym[j]; j++)
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;
723 return 0;
726 /*
727 * careful floating point
728 */
729 double
730 fmul(double a, double b)
732 double l;
734 if(a <= 0) {
735 if(a == 0)
736 return 0;
737 l = log(-a);
738 } else
739 l = log(a);
741 if(b <= 0) {
742 if(b == 0)
743 return 0;
744 l += log(-b);
745 } else
746 l += log(b);
748 if(l > Maxe) {
749 yyerror("overflow in multiply");
750 return 1;
752 if(l < -Maxe) {
753 yyerror("underflow in multiply");
754 return 0;
756 return a*b;
759 double
760 fdiv(double a, double b)
762 double l;
764 if(a <= 0) {
765 if(a == 0)
766 return 0;
767 l = log(-a);
768 } else
769 l = log(a);
771 if(b <= 0) {
772 if(b == 0) {
773 yyerror("division by zero");
774 return 1;
776 l -= log(-b);
777 } else
778 l -= log(b);
780 if(l > Maxe) {
781 yyerror("overflow in divide");
782 return 1;
784 if(l < -Maxe) {
785 yyerror("underflow in divide");
786 return 0;
788 return a/b;
791 double
792 fadd(double a, double b)
794 return a + b;