From 17e5fb8973d9e48ef53a88eb78f845f8a7b41a5b Mon Sep 17 00:00:00 2001 From: rsc Date: Wed, 21 Apr 2004 23:22:06 +0000 Subject: add new guys --- src/cmd/units.y | 795 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 795 insertions(+) create mode 100644 src/cmd/units.y (limited to 'src/cmd/units.y') diff --git a/src/cmd/units.y b/src/cmd/units.y new file mode 100644 index 00000000..372295fd --- /dev/null +++ b/src/cmd/units.y @@ -0,0 +1,795 @@ +%{ +#include +#include +#include + +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 prog expr expr0 expr1 expr2 expr3 expr4 + +%token VAL +%token VAR +%token 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) { + 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 = $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; idim[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; idim[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; idim[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; idim[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; idim[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; idim[i]; + if(d > 0) + printdim(str, i, d); + else + if(d < 0) + f = 1; + } + + if(f) { + strcat(str, " /"); + for(i=1; idim[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; +} -- cgit v1.2.3