]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/units.y
9bootfat: rename open() to fileinit and make it static as its really a internal funct...
[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         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
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
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
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
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
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
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
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
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
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
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
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
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
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*
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
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
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
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
783 fadd(double a, double b)
784 {
785         return a + b;
786 }