plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

units.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 }