| %{ |
| #include <u.h> |
| #include <libc.h> |
| #include <bio.h> |
| |
| enum |
| { |
| Ndim = 15, /* number of dimensions */ |
| Nsym = 40, /* size of a name */ |
| Nvar = 203, /* hash table size */ |
| Maxe = 695 /* log of largest number */ |
| }; |
| |
| typedef struct Var Var; |
| typedef struct Node Node; |
| typedef struct Prefix Prefix; |
| |
| struct Node |
| { |
| double val; |
| schar dim[Ndim]; |
| }; |
| struct Var |
| { |
| Rune name[Nsym]; |
| Node node; |
| Var* link; |
| }; |
| struct Prefix |
| { |
| double val; |
| char* name; |
| Rune* pname; |
| }; |
| |
| char buf[100]; |
| int digval; |
| Biobuf* fi; |
| Biobuf linebuf; |
| Var* fund[Ndim]; |
| Rune line[1000]; |
| ulong lineno; |
| int linep; |
| int nerrors; |
| Node one; |
| int peekrune; |
| Node retnode1; |
| Node retnode2; |
| Node retnode; |
| Rune sym[Nsym]; |
| Var* vars[Nvar]; |
| int vflag; |
| |
| #define div unitsdiv |
| |
| extern void add(Node*, Node*, Node*); |
| extern void div(Node*, Node*, Node*); |
| extern int specialcase(Node*, Node*, Node*); |
| extern double fadd(double, double); |
| extern double fdiv(double, double); |
| extern double fmul(double, double); |
| extern int gdigit(void*); |
| extern Var* lookup(int); |
| extern void main(int, char*[]); |
| extern void mul(Node*, Node*, Node*); |
| extern void ofile(void); |
| extern double pname(void); |
| extern void printdim(char*, int, int); |
| extern int ralpha(int); |
| extern int readline(void); |
| extern void sub(Node*, Node*, Node*); |
| extern int Ufmt(Fmt*); |
| extern void xpn(Node*, Node*, int); |
| extern void yyerror(char*, ...); |
| extern int yylex(void); |
| extern int yyparse(void); |
| |
| typedef Node* indnode; |
| /* #pragma varargck type "U" indnode */ |
| |
| %} |
| %union |
| { |
| Node node; |
| Var* var; |
| int numb; |
| double val; |
| } |
| |
| %type <node> prog expr expr0 expr1 expr2 expr3 expr4 |
| |
| %token <val> VAL |
| %token <var> VAR |
| %token <numb> SUP |
| %% |
| prog: |
| ':' VAR expr |
| { |
| int f; |
| |
| f = $2->node.dim[0]; |
| $2->node = $3; |
| $2->node.dim[0] = 1; |
| if(f) |
| yyerror("redefinition of %S", $2->name); |
| else |
| if(vflag) |
| print("%S\t%U\n", $2->name, &$2->node); |
| } |
| | ':' VAR '#' |
| { |
| int f, i; |
| |
| for(i=1; i<Ndim; i++) |
| if(fund[i] == 0) |
| break; |
| if(i >= Ndim) { |
| yyerror("too many dimensions"); |
| i = Ndim-1; |
| } |
| fund[i] = $2; |
| |
| f = $2->node.dim[0]; |
| $2->node = one; |
| $2->node.dim[0] = 1; |
| $2->node.dim[i] = 1; |
| if(f) |
| yyerror("redefinition of %S", $2->name); |
| else |
| if(vflag) |
| print("%S\t#\n", $2->name); |
| } |
| | '?' expr |
| { |
| retnode1 = $2; |
| } |
| | '?' |
| { |
| retnode1 = one; |
| } |
| |
| expr: |
| expr4 |
| | expr '+' expr4 |
| { |
| add(&$$, &$1, &$3); |
| } |
| | expr '-' expr4 |
| { |
| sub(&$$, &$1, &$3); |
| } |
| |
| expr4: |
| expr3 |
| | expr4 '*' expr3 |
| { |
| mul(&$$, &$1, &$3); |
| } |
| | expr4 '/' expr3 |
| { |
| div(&$$, &$1, &$3); |
| } |
| |
| expr3: |
| expr2 |
| | expr3 expr2 |
| { |
| mul(&$$, &$1, &$2); |
| } |
| |
| expr2: |
| expr1 |
| | expr2 SUP |
| { |
| xpn(&$$, &$1, $2); |
| } |
| | expr2 '^' expr1 |
| { |
| int i; |
| |
| for(i=1; i<Ndim; i++) |
| if($3.dim[i]) { |
| yyerror("exponent has units"); |
| $$ = $1; |
| break; |
| } |
| if(i >= Ndim) { |
| i = $3.val; |
| if(i != $3.val) |
| yyerror("exponent not integral"); |
| xpn(&$$, &$1, i); |
| } |
| } |
| |
| expr1: |
| expr0 |
| | expr1 '|' expr0 |
| { |
| div(&$$, &$1, &$3); |
| } |
| |
| expr0: |
| VAR |
| { |
| if($1->node.dim[0] == 0) { |
| yyerror("undefined %S", $1->name); |
| $$ = one; |
| } else |
| $$ = $1->node; |
| } |
| | VAL |
| { |
| $$ = one; |
| $$.val = $1; |
| } |
| | '(' expr ')' |
| { |
| $$ = $2; |
| } |
| %% |
| |
| int |
| yylex(void) |
| { |
| int c, i; |
| |
| c = peekrune; |
| peekrune = ' '; |
| |
| loop: |
| if((c >= '0' && c <= '9') || c == '.') |
| goto numb; |
| if(ralpha(c)) |
| goto alpha; |
| switch(c) { |
| case ' ': |
| case '\t': |
| c = line[linep++]; |
| goto loop; |
| case 0xd7: |
| return 0x2a; |
| case 0xf7: |
| return 0x2f; |
| case 0xb9: |
| case 0x2071: |
| yylval.numb = 1; |
| return SUP; |
| case 0xb2: |
| case 0x2072: |
| yylval.numb = 2; |
| return SUP; |
| case 0xb3: |
| case 0x2073: |
| yylval.numb = 3; |
| return SUP; |
| } |
| return c; |
| |
| alpha: |
| memset(sym, 0, sizeof(sym)); |
| for(i=0;; i++) { |
| if(i < nelem(sym)) |
| sym[i] = c; |
| c = line[linep++]; |
| if(!ralpha(c)) |
| break; |
| } |
| sym[nelem(sym)-1] = 0; |
| peekrune = c; |
| yylval.var = lookup(0); |
| return VAR; |
| |
| numb: |
| digval = c; |
| yylval.val = fmtcharstod(gdigit, 0); |
| return VAL; |
| } |
| |
| void |
| main(int argc, char *argv[]) |
| { |
| char *file; |
| |
| ARGBEGIN { |
| default: |
| print("usage: units [-v] [file]\n"); |
| exits("usage"); |
| case 'v': |
| vflag = 1; |
| break; |
| } ARGEND |
| |
| file = unsharp("#9/lib/units"); |
| if(argc > 0) |
| file = argv[0]; |
| fi = Bopen(file, OREAD); |
| if(fi == 0) { |
| print("cant open: %s\n", file); |
| exits("open"); |
| } |
| fmtinstall('U', Ufmt); |
| one.val = 1; |
| |
| /* |
| * read the 'units' file to |
| * develope a database |
| */ |
| lineno = 0; |
| for(;;) { |
| lineno++; |
| if(readline()) |
| break; |
| if(line[0] == 0 || line[0] == '/') |
| continue; |
| peekrune = ':'; |
| yyparse(); |
| } |
| |
| /* |
| * read the console to |
| * print ratio of pairs |
| */ |
| Bterm(fi); |
| fi = &linebuf; |
| Binit(fi, 0, OREAD); |
| lineno = 0; |
| for(;;) { |
| if(lineno & 1) |
| print("you want: "); |
| else |
| print("you have: "); |
| if(readline()) |
| break; |
| peekrune = '?'; |
| nerrors = 0; |
| yyparse(); |
| if(nerrors) |
| continue; |
| if(lineno & 1) { |
| if(specialcase(&retnode, &retnode2, &retnode1)) |
| print("\tis %U\n", &retnode); |
| else { |
| div(&retnode, &retnode2, &retnode1); |
| print("\t* %U\n", &retnode); |
| div(&retnode, &retnode1, &retnode2); |
| print("\t/ %U\n", &retnode); |
| } |
| } else |
| retnode2 = retnode1; |
| lineno++; |
| } |
| print("\n"); |
| exits(0); |
| } |
| |
| /* |
| * all characters that have some |
| * meaning. rest are usable as names |
| */ |
| int |
| ralpha(int c) |
| { |
| switch(c) { |
| case 0: |
| case '+': |
| case '-': |
| case '*': |
| case '/': |
| case '[': |
| case ']': |
| case '(': |
| case ')': |
| case '^': |
| case ':': |
| case '?': |
| case ' ': |
| case '\t': |
| case '.': |
| case '|': |
| case '#': |
| case 0xb9: |
| case 0x2071: |
| case 0xb2: |
| case 0x2072: |
| case 0xb3: |
| case 0x2073: |
| case 0xd7: |
| case 0xf7: |
| return 0; |
| } |
| return 1; |
| } |
| |
| int |
| gdigit(void *v) |
| { |
| int c; |
| |
| USED(v); |
| c = digval; |
| if(c) { |
| digval = 0; |
| return c; |
| } |
| c = line[linep++]; |
| peekrune = c; |
| return c; |
| } |
| |
| void |
| yyerror(char *fmt, ...) |
| { |
| va_list arg; |
| |
| /* |
| * hack to intercept message from yaccpar |
| */ |
| if(strcmp(fmt, "syntax error") == 0) { |
| yyerror("syntax error, last name: %S", sym); |
| return; |
| } |
| va_start(arg, fmt); |
| vseprint(buf, buf+sizeof(buf), fmt, arg); |
| va_end(arg); |
| print("%ld: %S\n\t%s\n", lineno, line, buf); |
| nerrors++; |
| if(nerrors > 5) { |
| print("too many errors\n"); |
| exits("errors"); |
| } |
| } |
| |
| void |
| add(Node *c, Node *a, Node *b) |
| { |
| int i, d; |
| |
| for(i=0; i<Ndim; i++) { |
| d = a->dim[i]; |
| c->dim[i] = d; |
| if(d != b->dim[i]) |
| yyerror("add must be like units"); |
| } |
| c->val = fadd(a->val, b->val); |
| } |
| |
| void |
| sub(Node *c, Node *a, Node *b) |
| { |
| int i, d; |
| |
| for(i=0; i<Ndim; i++) { |
| d = a->dim[i]; |
| c->dim[i] = d; |
| if(d != b->dim[i]) |
| yyerror("sub must be like units"); |
| } |
| c->val = fadd(a->val, -b->val); |
| } |
| |
| void |
| mul(Node *c, Node *a, Node *b) |
| { |
| int i; |
| |
| for(i=0; i<Ndim; i++) |
| c->dim[i] = a->dim[i] + b->dim[i]; |
| c->val = fmul(a->val, b->val); |
| } |
| |
| void |
| div(Node *c, Node *a, Node *b) |
| { |
| int i; |
| |
| for(i=0; i<Ndim; i++) |
| c->dim[i] = a->dim[i] - b->dim[i]; |
| c->val = fdiv(a->val, b->val); |
| } |
| |
| void |
| xpn(Node *c, Node *a, int b) |
| { |
| int i; |
| |
| *c = one; |
| if(b < 0) { |
| b = -b; |
| for(i=0; i<b; i++) |
| div(c, c, a); |
| } else |
| for(i=0; i<b; i++) |
| mul(c, c, a); |
| } |
| |
| int |
| specialcase(Node *c, Node *a, Node *b) |
| { |
| int i, d, d1, d2; |
| |
| d1 = 0; |
| d2 = 0; |
| for(i=1; i<Ndim; i++) { |
| d = a->dim[i]; |
| if(d) { |
| if(d != 1 || d1) |
| return 0; |
| d1 = i; |
| } |
| d = b->dim[i]; |
| if(d) { |
| if(d != 1 || d2) |
| return 0; |
| d2 = i; |
| } |
| } |
| if(d1 == 0 || d2 == 0) |
| return 0; |
| |
| if(memcmp(fund[d1]->name, L"°C", 3*sizeof(Rune)) == 0 && |
| memcmp(fund[d2]->name, L"°F", 3*sizeof(Rune)) == 0 && |
| b->val == 1) { |
| memcpy(c->dim, b->dim, sizeof(c->dim)); |
| c->val = a->val * 9. / 5. + 32.; |
| return 1; |
| } |
| |
| if(memcmp(fund[d1]->name, L"°F", 3*sizeof(Rune)) == 0 && |
| memcmp(fund[d2]->name, L"°C", 3*sizeof(Rune)) == 0 && |
| b->val == 1) { |
| memcpy(c->dim, b->dim, sizeof(c->dim)); |
| c->val = (a->val - 32.) * 5. / 9.; |
| return 1; |
| } |
| return 0; |
| } |
| |
| void |
| printdim(char *str, int d, int n) |
| { |
| Var *v; |
| |
| if(n) { |
| v = fund[d]; |
| if(v) |
| sprint(strchr(str, 0), " %S", v->name); |
| else |
| sprint(strchr(str, 0), " [%d]", d); |
| switch(n) { |
| case 1: |
| break; |
| case 2: |
| strcat(str, "²"); |
| break; |
| case 3: |
| strcat(str, "³"); |
| break; |
| default: |
| sprint(strchr(str, 0), "^%d", n); |
| } |
| } |
| } |
| |
| int |
| Ufmt(Fmt *fp) |
| { |
| char str[200]; |
| Node *n; |
| int f, i, d; |
| |
| n = va_arg(fp->args, Node*); |
| sprint(str, "%g", n->val); |
| |
| f = 0; |
| for(i=1; i<Ndim; i++) { |
| d = n->dim[i]; |
| if(d > 0) |
| printdim(str, i, d); |
| else |
| if(d < 0) |
| f = 1; |
| } |
| |
| if(f) { |
| strcat(str, " /"); |
| for(i=1; i<Ndim; i++) { |
| d = n->dim[i]; |
| if(d < 0) |
| printdim(str, i, -d); |
| } |
| } |
| |
| return fmtstrcpy(fp, str); |
| } |
| |
| int |
| readline(void) |
| { |
| int i, c; |
| |
| linep = 0; |
| for(i=0;; i++) { |
| c = Bgetrune(fi); |
| if(c < 0) |
| return 1; |
| if(c == '\n') |
| break; |
| if(i < nelem(line)) |
| line[i] = c; |
| } |
| if(i >= nelem(line)) |
| i = nelem(line)-1; |
| line[i] = 0; |
| return 0; |
| } |
| |
| Var* |
| lookup(int f) |
| { |
| int i; |
| Var *v, *w; |
| double p; |
| ulong h; |
| |
| h = 0; |
| for(i=0; sym[i]; i++) |
| h = h*13 + sym[i]; |
| h %= nelem(vars); |
| |
| for(v=vars[h]; v; v=v->link) |
| if(memcmp(sym, v->name, sizeof(sym)) == 0) |
| return v; |
| if(f) |
| return 0; |
| v = malloc(sizeof(*v)); |
| if(v == nil) { |
| fprint(2, "out of memory\n"); |
| exits("mem"); |
| } |
| memset(v, 0, sizeof(*v)); |
| memcpy(v->name, sym, sizeof(sym)); |
| v->link = vars[h]; |
| vars[h] = v; |
| |
| p = 1; |
| for(;;) { |
| p = fmul(p, pname()); |
| if(p == 0) |
| break; |
| w = lookup(1); |
| if(w) { |
| v->node = w->node; |
| v->node.val = fmul(v->node.val, p); |
| break; |
| } |
| } |
| return v; |
| } |
| |
| Prefix prefix[] = |
| { |
| 1e-24, "yocto", 0, |
| 1e-21, "zepto", 0, |
| 1e-18, "atto", 0, |
| 1e-15, "femto", 0, |
| 1e-12, "pico", 0, |
| 1e-9, "nano", 0, |
| 1e-6, "micro", 0, |
| 1e-6, "μ", 0, |
| 1e-3, "milli", 0, |
| 1e-2, "centi", 0, |
| 1e-1, "deci", 0, |
| 1e1, "deka", 0, |
| 1e2, "hecta", 0, |
| 1e2, "hecto", 0, |
| 1e3, "kilo", 0, |
| 1e6, "mega", 0, |
| 1e6, "meg", 0, |
| 1e9, "giga", 0, |
| 1e12, "tera", 0, |
| 1e15, "peta", 0, |
| 1e18, "exa", 0, |
| 1e21, "zetta", 0, |
| 1e24, "yotta", 0, |
| 0, 0, 0, |
| }; |
| |
| double |
| pname(void) |
| { |
| Rune *p; |
| int i, j, c; |
| |
| /* |
| * rip off normal prefixs |
| */ |
| if(prefix[0].pname == nil){ |
| for(i=0; prefix[i].name; i++) |
| prefix[i].pname = runesmprint("%s", prefix[i].name); |
| } |
| |
| for(i=0; p=prefix[i].pname; i++) { |
| for(j=0; c=p[j]; j++) |
| if(c != sym[j]) |
| goto no; |
| memmove(sym, sym+j, (Nsym-j)*sizeof(*sym)); |
| memset(sym+(Nsym-j), 0, j*sizeof(*sym)); |
| return prefix[i].val; |
| no:; |
| } |
| |
| /* |
| * rip off 's' suffixes |
| */ |
| for(j=0; sym[j]; j++) |
| ; |
| j--; |
| /* j>1 is special hack to disallow ms finding m */ |
| if(j > 1 && sym[j] == 's') { |
| sym[j] = 0; |
| return 1; |
| } |
| return 0; |
| } |
| |
| /* |
| * careful floating point |
| */ |
| double |
| fmul(double a, double b) |
| { |
| double l; |
| |
| if(a <= 0) { |
| if(a == 0) |
| return 0; |
| l = log(-a); |
| } else |
| l = log(a); |
| |
| if(b <= 0) { |
| if(b == 0) |
| return 0; |
| l += log(-b); |
| } else |
| l += log(b); |
| |
| if(l > Maxe) { |
| yyerror("overflow in multiply"); |
| return 1; |
| } |
| if(l < -Maxe) { |
| yyerror("underflow in multiply"); |
| return 0; |
| } |
| return a*b; |
| } |
| |
| double |
| fdiv(double a, double b) |
| { |
| double l; |
| |
| if(a <= 0) { |
| if(a == 0) |
| return 0; |
| l = log(-a); |
| } else |
| l = log(a); |
| |
| if(b <= 0) { |
| if(b == 0) { |
| yyerror("division by zero"); |
| return 1; |
| } |
| l -= log(-b); |
| } else |
| l -= log(b); |
| |
| if(l > Maxe) { |
| yyerror("overflow in divide"); |
| return 1; |
| } |
| if(l < -Maxe) { |
| yyerror("underflow in divide"); |
| return 0; |
| } |
| return a/b; |
| } |
| |
| double |
| fadd(double a, double b) |
| { |
| return a + b; |
| } |