tunits.y - plan9port - [fork] Plan 9 from user space
HTML git clone git://src.adamsgaard.dk/plan9port
DIR Log
DIR Files
DIR Refs
DIR README
DIR LICENSE
---
tunits.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 }