]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/units.y
usb lib: add maxpkt and ntds to Altc struct
[plan9front.git] / sys / src / cmd / units.y
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                 yylval.numb = 1;
243                 return SUP;
244         case L'²':
245                 yylval.numb = 2;
246                 return SUP;
247         case L'³':
248                 yylval.numb = 3;
249                 return SUP;
250         case L'⁴':
251                 yylval.numb = 4;
252                 return SUP;
253         case L'⁵':
254                 yylval.numb = 5;
255                 return SUP;
256         case L'⁶':
257                 yylval.numb = 6;
258                 return SUP;
259         case L'⁷':
260                 yylval.numb = 7;
261                 return SUP;
262         case L'⁸':
263                 yylval.numb = 8;
264                 return SUP;
265         case L'⁹':
266                 yylval.numb = 9;
267                 return SUP;
268         }
269         return c;
270
271 alpha:
272         memset(sym, 0, sizeof(sym));
273         for(i=0;; i++) {
274                 if(i < nelem(sym))
275                         sym[i] = c;
276                 c = line[linep++];
277                 if(!ralpha(c))
278                         break;
279         }
280         sym[nelem(sym)-1] = 0;
281         peekrune = c;
282         yylval.var = lookup(0);
283         return VAR;
284
285 numb:
286         digval = c;
287         yylval.val = charstod(gdigit, 0);
288         return VAL;
289 }
290
291 void
292 main(int argc, char *argv[])
293 {
294         char *file;
295
296         ARGBEGIN {
297         default:
298                 print("usage: units [-v] [file]\n");
299                 exits("usage");
300         case 'v':
301                 vflag = 1;
302                 break;
303         } ARGEND
304
305         file = "/lib/units";
306         if(argc > 0)
307                 file = argv[0];
308         fi = Bopen(file, OREAD);
309         if(fi == 0) {
310                 print("cant open: %s\n", file);
311                 exits("open");
312         }
313         fmtinstall('U', Ufmt);
314         one.val = 1;
315
316         /*
317          * read the 'units' file to
318          * develope a database
319          */
320         lineno = 0;
321         for(;;) {
322                 lineno++;
323                 if(readline())
324                         break;
325                 if(line[0] == 0 || line[0] == '/')
326                         continue;
327                 peekrune = ':';
328                 yyparse();
329         }
330
331         /*
332          * read the console to
333          * print ratio of pairs
334          */
335         Bterm(fi);
336         fi = &linebuf;
337         Binit(fi, 0, OREAD);
338         lineno = 0;
339         for(;;) {
340                 if(lineno & 1)
341                         print("you want: ");
342                 else
343                         print("you have: ");
344                 if(readline())
345                         break;
346                 peekrune = '?';
347                 nerrors = 0;
348                 yyparse();
349                 if(nerrors)
350                         continue;
351                 if(lineno & 1) {
352                         if(specialcase(&retnode, &retnode2, &retnode1))
353                                 print("\tis %U\n", &retnode);
354                         else {
355                                 div(&retnode, &retnode2, &retnode1);
356                                 print("\t* %U\n", &retnode);
357                                 div(&retnode, &retnode1, &retnode2);
358                                 print("\t/ %U\n", &retnode);
359                         }
360                 } else
361                         retnode2 = retnode1;
362                 lineno++;
363         }
364         print("\n");
365         exits(0);
366 }
367
368 /*
369  * all characters that have some
370  * meaning. rest are usable as names
371  */
372 int
373 ralpha(int c)
374 {
375         switch(c) {
376         case 0:
377         case '+':
378         case '-':
379         case '*':
380         case '/':
381         case '[':
382         case ']':
383         case '(':
384         case ')':
385         case '^':
386         case ':':
387         case '?':
388         case ' ':
389         case '\t':
390         case '.':
391         case '|':
392         case '#':
393         case L'¹':
394         case L'²':
395         case L'³':
396         case L'⁴':
397         case L'⁵':
398         case L'⁶':
399         case L'⁷':
400         case L'⁸':
401         case L'⁹':
402         case L'×':
403         case L'÷':
404                 return 0;
405         }
406         return 1;
407 }
408
409 int
410 gdigit(void*)
411 {
412         int c;
413
414         c = digval;
415         if(c) {
416                 digval = 0;
417                 return c;
418         }
419         c = line[linep++];
420         peekrune = c;
421         return c;
422 }
423
424 void
425 yyerror(char *fmt, ...)
426 {
427         va_list arg;
428
429         /*
430          * hack to intercept message from yaccpar
431          */
432         if(strcmp(fmt, "syntax error") == 0) {
433                 yyerror("syntax error, last name: %S", sym);
434                 return;
435         }
436         va_start(arg, fmt);
437         vseprint(buf, buf+sizeof(buf), fmt, arg);
438         va_end(arg);
439         print("%ld: %S\n\t%s\n", lineno, line, buf);
440         nerrors++;
441         if(nerrors > 5) {
442                 print("too many errors\n");
443                 exits("errors");
444         }
445 }
446
447 void
448 add(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("add must be like units");
457         }
458         c->val = fadd(a->val, b->val);
459 }
460
461 void
462 sub(Node *c, Node *a, Node *b)
463 {
464         int i, d;
465
466         for(i=0; i<Ndim; i++) {
467                 d = a->dim[i];
468                 c->dim[i] = d;
469                 if(d != b->dim[i])
470                         yyerror("sub must be like units");
471         }
472         c->val = fadd(a->val, -b->val);
473 }
474
475 void
476 mul(Node *c, Node *a, Node *b)
477 {
478         int i;
479
480         for(i=0; i<Ndim; i++)
481                 c->dim[i] = a->dim[i] + b->dim[i];
482         c->val = fmul(a->val, b->val);
483 }
484
485 void
486 div(Node *c, Node *a, Node *b)
487 {
488         int i;
489
490         for(i=0; i<Ndim; i++)
491                 c->dim[i] = a->dim[i] - b->dim[i];
492         c->val = fdiv(a->val, b->val);
493 }
494
495 void
496 xpn(Node *c, Node *a, int b)
497 {
498         int i;
499
500         *c = one;
501         if(b < 0) {
502                 b = -b;
503                 for(i=0; i<b; i++)
504                         div(c, c, a);
505         } else
506         for(i=0; i<b; i++)
507                 mul(c, c, a);
508 }
509
510 int
511 specialcase(Node *c, Node *a, Node *b)
512 {
513         int i, d, d1, d2;
514
515         d1 = 0;
516         d2 = 0;
517         for(i=1; i<Ndim; i++) {
518                 d = a->dim[i];
519                 if(d) {
520                         if(d != 1 || d1)
521                                 return 0;
522                         d1 = i;
523                 }
524                 d = b->dim[i];
525                 if(d) {
526                         if(d != 1 || d2)
527                                 return 0;
528                         d2 = i;
529                 }
530         }
531         if(d1 == 0 || d2 == 0)
532                 return 0;
533
534         if(memcmp(fund[d1]->name, L"°C", 3*sizeof(Rune)) == 0 &&
535            memcmp(fund[d2]->name, L"°F", 3*sizeof(Rune)) == 0 &&
536            b->val == 1) {
537                 memcpy(c->dim, b->dim, sizeof(c->dim));
538                 c->val = a->val * 9. / 5. + 32.;
539                 return 1;
540         }
541
542         if(memcmp(fund[d1]->name, L"°F", 3*sizeof(Rune)) == 0 &&
543            memcmp(fund[d2]->name, L"°C", 3*sizeof(Rune)) == 0 &&
544            b->val == 1) {
545                 memcpy(c->dim, b->dim, sizeof(c->dim));
546                 c->val = (a->val - 32.) * 5. / 9.;
547                 return 1;
548         }
549         return 0;
550 }
551
552 void
553 printdim(char *str, int d, int n)
554 {
555         Var *v;
556
557         if(n) {
558                 v = fund[d];
559                 if(v)
560                         sprint(strchr(str, 0), " %S", v->name);
561                 else
562                         sprint(strchr(str, 0), " [%d]", d);
563                 switch(n) {
564                 case 1:
565                         break;
566                 case 2:
567                         strcat(str, "²");
568                         break;
569                 case 3:
570                         strcat(str, "³");
571                         break;
572                 case 4:
573                         strcat(str, "⁴");
574                         break;
575                 case 5:
576                         strcat(str, "⁵");
577                         break;
578                 case 6:
579                         strcat(str, "⁶");
580                         break;
581                 case 7:
582                         strcat(str, "⁷");
583                         break;
584                 case 8:
585                         strcat(str, "⁸");
586                         break;
587                 case 9:
588                         strcat(str, "⁹");
589                         break;
590                 default:
591                         sprint(strchr(str, 0), "^%d", n);
592                 }
593         }
594 }
595
596 int
597 Ufmt(Fmt *fp)
598 {
599         char str[200];
600         Node *n;
601         int f, i, d;
602
603         n = va_arg(fp->args, Node*);
604         sprint(str, "%g", n->val);
605
606         f = 0;
607         for(i=1; i<Ndim; i++) {
608                 d = n->dim[i];
609                 if(d > 0)
610                         printdim(str, i, d);
611                 else
612                 if(d < 0)
613                         f = 1;
614         }
615
616         if(f) {
617                 strcat(str, " /");
618                 for(i=1; i<Ndim; i++) {
619                         d = n->dim[i];
620                         if(d < 0)
621                                 printdim(str, i, -d);
622                 }
623         }
624
625         return fmtstrcpy(fp, str);
626 }
627
628 int
629 readline(void)
630 {
631         int i, c;
632
633         linep = 0;
634         for(i=0;; i++) {
635                 c = Bgetrune(fi);
636                 if(c < 0)
637                         return 1;
638                 if(c == '\n')
639                         break;
640                 if(i < nelem(line))
641                         line[i] = c;
642         }
643         if(i >= nelem(line))
644                 i = nelem(line)-1;
645         line[i] = 0;
646         return 0;
647 }
648
649 Var*
650 lookup(int f)
651 {
652         int i;
653         Var *v, *w;
654         double p;
655         ulong h;
656
657         h = 0;
658         for(i=0; sym[i]; i++)
659                 h = h*13 + sym[i];
660         h %= nelem(vars);
661
662         for(v=vars[h]; v; v=v->link)
663                 if(memcmp(sym, v->name, sizeof(sym)) == 0)
664                         return v;
665         if(f)
666                 return 0;
667         v = malloc(sizeof(*v));
668         if(v == nil) {
669                 fprint(2, "out of memory\n");
670                 exits("mem");
671         }
672         memset(v, 0, sizeof(*v));
673         memcpy(v->name, sym, sizeof(sym));
674         v->link = vars[h];
675         vars[h] = v;
676
677         p = 1;
678         for(;;) {
679                 p = fmul(p, pname());
680                 if(p == 0)
681                         break;
682                 w = lookup(1);
683                 if(w) {
684                         v->node = w->node;
685                         v->node.val = fmul(v->node.val, p);
686                         break;
687                 }
688         }
689         return v;
690 }
691
692 Prefix  prefix[] =
693 {
694         1e-24,  L"yocto",
695         1e-21,  L"zepto",
696         1e-18,  L"atto",
697         1e-15,  L"femto",
698         1e-12,  L"pico",
699         1e-9,   L"nano",
700         1e-6,   L"micro",
701         1e-6,   L"μ",
702         1e-3,   L"milli",
703         1e-2,   L"centi",
704         1e-1,   L"deci",
705         1e1,    L"deka",
706         1e2,    L"hecta",
707         1e2,    L"hecto",
708         1e3,    L"kilo",
709         1e6,    L"mega",
710         1e6,    L"meg",
711         1e9,    L"giga",
712         1e12,   L"tera",
713         1e15,   L"peta",
714         1e18,   L"exa",
715         1e21,   L"zetta",
716         1e24,   L"yotta",
717         0,      0
718 };
719
720 double
721 pname(void)
722 {
723         Rune *p;
724         int i, j, c;
725
726         /*
727          * rip off normal prefixs
728          */
729         for(i=0; p=prefix[i].pname; i++) {
730                 for(j=0; c=p[j]; j++)
731                         if(c != sym[j])
732                                 goto no;
733                 memmove(sym, sym+j, (Nsym-j)*sizeof(*sym));
734                 memset(sym+(Nsym-j), 0, j*sizeof(*sym));
735                 return prefix[i].val;
736         no:;
737         }
738
739         /*
740          * rip off 's' suffixes
741          */
742         for(j=0; sym[j]; j++)
743                 ;
744         j--;
745         /* j>1 is special hack to disallow ms finding m */
746         if(j > 1 && sym[j] == 's') {
747                 sym[j] = 0;
748                 return 1;
749         }
750         return 0;
751 }
752
753 /*
754  * careful floating point
755  */
756 double
757 fmul(double a, double b)
758 {
759         double l;
760
761         if(a <= 0) {
762                 if(a == 0)
763                         return 0;
764                 l = log(-a);
765         } else
766                 l = log(a);
767
768         if(b <= 0) {
769                 if(b == 0)
770                         return 0;
771                 l += log(-b);
772         } else
773                 l += log(b);
774
775         if(l > Maxe) {
776                 yyerror("overflow in multiply");
777                 return 1;
778         }
779         if(l < -Maxe) {
780                 yyerror("underflow in multiply");
781                 return 0;
782         }
783         return a*b;
784 }
785
786 double
787 fdiv(double a, double b)
788 {
789         double l;
790
791         if(a <= 0) {
792                 if(a == 0)
793                         return 0;
794                 l = log(-a);
795         } else
796                 l = log(a);
797
798         if(b <= 0) {
799                 if(b == 0) {
800                         yyerror("division by zero");
801                         return 1;
802                 }
803                 l -= log(-b);
804         } else
805                 l -= log(b);
806
807         if(l > Maxe) {
808                 yyerror("overflow in divide");
809                 return 1;
810         }
811         if(l < -Maxe) {
812                 yyerror("underflow in divide");
813                 return 0;
814         }
815         return a/b;
816 }
817
818 double
819 fadd(double a, double b)
820 {
821         return a + b;
822 }