URI:
       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 }