xref: /inferno-os/appl/cmd/units.b (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
1implement Units;
2
3#line	2	"units.y"
4#
5# subject to the Lucent Public License 1.02
6#
7include "sys.m";
8	sys: Sys;
9
10include "draw.m";
11
12include "bufio.m";
13	bufio: Bufio;
14	Iobuf: import bufio;
15
16include "math.m";
17	math: Math;
18
19include "arg.m";
20
21Ndim: con 15;	# number of dimensions
22Nvar: con 203;	# hash table size
23Maxe: con 695.0;	# log of largest number
24
25Node: adt
26{
27	val:	real;
28	dim:	array of int;	# [Ndim] schar
29
30	mk:	fn(v: real): Node;
31	text:	fn(n: self Node): string;
32	add:	fn(a: self Node, b: Node): Node;
33	sub:	fn(a: self Node, b: Node): Node;
34	mul:	fn(a: self Node, b: Node): Node;
35	div:	fn(a: self Node, b: Node): Node;
36	xpn:	fn(a: self Node, b: int): Node;
37	copy: fn(a: self Node): Node;
38};
39Var: adt
40{
41	name:	string;
42	node:	Node;
43};
44Prefix: adt
45{
46	val:	real;
47	pname:	string;
48};
49
50digval := 0;
51fi: ref Iobuf;
52fund := array[Ndim] of ref Var;
53line: string;
54lineno := 0;
55linep := 0;
56nerrors := 0;
57peekrune := 0;
58retnode1: Node;
59retnode2: Node;
60retnode: Node;
61sym: string;
62vars := array[Nvar] of list of ref Var;
63vflag := 0;
64
65YYSTYPE: adt {
66	node:	Node;
67	var:	ref Var;
68	numb:	int;
69	val:	real;
70};
71
72YYLEX: adt {
73	lval: YYSTYPE;
74	lex: fn(l: self ref YYLEX): int;
75	error: fn(l: self ref YYLEX, msg: string);
76};
77
78Units: module {
79
80	init:	fn(nil: ref Draw->Context, args: list of string);
81VAL: con	57346;
82VAR: con	57347;
83SUP: con	57348;
84
85};
86YYEOFCODE: con 1;
87YYERRCODE: con 2;
88YYMAXDEPTH: con 200;
89
90#line	203	"units.y"
91
92
93init(nil: ref Draw->Context, args: list of string)
94{
95	sys = load Sys Sys->PATH;
96	bufio = load Bufio Bufio->PATH;
97	math = load Math Math->PATH;
98
99	arg := load Arg Arg->PATH;
100	arg->init(args);
101	arg->setusage("units [-v] [file]");
102	while((o := arg->opt()) != 0)
103		case o {
104		'v' => vflag = 1;
105		* => arg->usage();
106	}
107	args = arg->argv();
108	arg = nil;
109
110	file := "/lib/units";
111	if(args != nil)
112		file = hd args;
113	fi = bufio->open(file, Sys->OREAD);
114	if(fi == nil) {
115		sys->fprint(sys->fildes(2), "units: cannot open %s: %r\n", file);
116		raise "fail:open";
117	}
118	lex := ref YYLEX;
119
120	#
121	# read the 'units' file to
122	# develop a database
123	#
124	lineno = 0;
125	for(;;) {
126		lineno++;
127		if(readline())
128			break;
129		if(len line == 0 || line[0] == '/')
130			continue;
131		peekrune = ':';
132		yyparse(lex);
133	}
134
135	#
136	# read the console to
137	# print ratio of pairs
138	#
139	fi = bufio->fopen(sys->fildes(0), Sys->OREAD);
140	lineno = 0;
141	for(;;) {
142		if(lineno & 1)
143			sys->print("you want: ");
144		else
145			sys->print("you have: ");
146		if(readline())
147			break;
148		peekrune = '?';
149		nerrors = 0;
150		yyparse(lex);
151		if(nerrors)
152			continue;
153		if(lineno & 1) {
154			isspcl: int;
155			(isspcl, retnode) = specialcase(retnode2, retnode1);
156			if(isspcl)
157				sys->print("\tis %s\n", retnode.text());
158			else {
159				retnode = retnode2.div(retnode1);
160				sys->print("\t* %s\n", retnode.text());
161				retnode = retnode1.div(retnode2);
162				sys->print("\t/ %s\n", retnode.text());
163			}
164		} else
165			retnode2 = retnode1.copy();
166		lineno++;
167	}
168	sys->print("\n");
169}
170
171YYLEX.lex(lex: self ref YYLEX): int
172{
173	c := peekrune;
174	peekrune = ' ';
175
176	while(c == ' ' || c == '\t'){
177		if(linep >= len line)
178			return 0;	# -1?
179		c = line[linep++];
180	}
181	case c {
182	'0' to '9' or '.' =>
183		digval = c;
184		(lex.lval.val, peekrune) = readreal(gdigit, lex);
185		return VAL;
186	'×' =>
187		return '*';
188	'÷' =>
189		return '/';
190	'¹' or
191	'ⁱ' =>
192		lex.lval.numb = 1;
193		return SUP;
194	'²' or
195	'⁲' =>
196		lex.lval.numb = 2;
197		return SUP;
198	'³' or
199	'⁳' =>
200		lex.lval.numb = 3;
201		return SUP;
202	* =>
203		if(ralpha(c)){
204			sym = "";
205			for(i:=0;; i++) {
206				sym[i] = c;
207				if(linep >= len line){
208					c = ' ';
209					break;
210				}
211				c = line[linep++];
212				if(!ralpha(c))
213					break;
214			}
215			peekrune = c;
216			lex.lval.var = lookup(0);
217			return VAR;
218		}
219	}
220	return c;
221}
222
223#
224# all characters that have some
225# meaning. rest are usable as names
226#
227ralpha(c: int): int
228{
229	case c {
230	0 or
231	'+'  or
232	'-'  or
233	'*'  or
234	'/'  or
235	'['  or
236	']'  or
237	'('  or
238	')'  or
239	'^'  or
240	':'  or
241	'?'  or
242	' '  or
243	'\t'  or
244	'.'  or
245	'|'  or
246	'#'  or
247	'¹'  or
248	'ⁱ'  or
249	'²'  or
250	'⁲'  or
251	'³'  or
252	'⁳'  or
253	'×'  or
254	'÷'  =>
255		return 0;
256	}
257	return 1;
258}
259
260gdigit(nil: ref YYLEX): int
261{
262	c := digval;
263	if(c) {
264		digval = 0;
265		return c;
266	}
267	if(linep >= len line)
268		return 0;
269	return line[linep++];
270}
271
272YYLEX.error(lex: self ref YYLEX, s: string)
273{
274	#
275	# hack to intercept message from yaccpar
276	#
277	if(s == "syntax error") {
278		lex.error(sys->sprint("syntax error, last name: %s", sym));
279		return;
280	}
281	sys->print("%d: %s\n\t%s\n", lineno, line, s);
282	nerrors++;
283	if(nerrors > 5) {
284		sys->print("too many errors\n");
285		raise "fail:errors";
286	}
287}
288
289yyerror(s: string)
290{
291	l := ref YYLEX;
292	l.error(s);
293}
294
295Node.mk(v: real): Node
296{
297	return (v, array[Ndim] of {* => 0});
298}
299
300Node.add(a: self Node, b: Node): Node
301{
302	c := Node.mk(fadd(a.val, b.val));
303	for(i:=0; i<Ndim; i++) {
304		d := a.dim[i];
305		c.dim[i] = d;
306		if(d != b.dim[i])
307			yyerror("add must be like units");
308	}
309	return c;
310}
311
312Node.sub(a: self Node, b: Node): Node
313{
314	c := Node.mk(fadd(a.val, -b.val));
315	for(i:=0; i<Ndim; i++) {
316		d := a.dim[i];
317		c.dim[i] = d;
318		if(d != b.dim[i])
319			yyerror("sub must be like units");
320	}
321	return c;
322}
323
324Node.mul(a: self Node, b: Node): Node
325{
326	c := Node.mk(fmul(a.val, b.val));
327	for(i:=0; i<Ndim; i++)
328		c.dim[i] = a.dim[i] + b.dim[i];
329	return c;
330}
331
332Node.div(a: self Node, b: Node): Node
333{
334	c := Node.mk(fdiv(a.val, b.val));
335	for(i:=0; i<Ndim; i++)
336		c.dim[i] = a.dim[i] - b.dim[i];
337	return c;
338}
339
340Node.xpn(a: self Node, b: int): Node
341{
342	c := Node.mk(1.0);
343	if(b < 0) {
344		b = -b;
345		for(i:=0; i<b; i++)
346			c = c.div(a);
347	} else
348		for(i:=0; i<b; i++)
349			c = c.mul(a);
350	return c;
351}
352
353Node.copy(a: self Node): Node
354{
355	c := Node.mk(a.val);
356	c.dim[0:] = a.dim;
357	return c;
358}
359
360specialcase(a, b: Node): (int, Node)
361{
362	c := Node.mk(0.0);
363	d1 := 0;
364	d2 := 0;
365	for(i:=1; i<Ndim; i++) {
366		d := a.dim[i];
367		if(d) {
368			if(d != 1 || d1)
369				return (0, c);
370			d1 = i;
371		}
372		d = b.dim[i];
373		if(d) {
374			if(d != 1 || d2)
375				return (0, c);
376			d2 = i;
377		}
378	}
379	if(d1 == 0 || d2 == 0)
380		return (0, c);
381
382	if(fund[d1].name == "°C" &&
383	   fund[d2].name == "°F" &&
384	   b.val == 1.0) {
385		c = b.copy();
386		c.val = a.val * 9. / 5. + 32.;
387		return (1, c);
388	}
389
390	if(fund[d1].name == "°F" &&
391	   fund[d2].name == "°C" &&
392	   b.val == 1.0) {
393		c = b.copy();
394		c.val = (a.val - 32.) * 5. / 9.;
395		return (1, c);
396	}
397	return (0, c);
398}
399
400printdim(d: int, n: int): string
401{
402	s := "";
403	if(n) {
404		v := fund[d];
405		if(v != nil)
406			s += " "+v.name;
407		else
408			s += sys->sprint(" [%d]", d);
409		case n {
410		1 =>
411			;
412		2 =>
413			s += "²";
414		3 =>
415			s += "³";
416		4 =>
417			s += "⁴";
418		* =>
419			s += sys->sprint("^%d", n);
420		}
421	}
422	return s;
423}
424
425Node.text(n: self Node): string
426{
427	str := sys->sprint("%.7g", n.val);
428	f := 0;
429	for(i:=1; i<len n.dim; i++) {
430		d := n.dim[i];
431		if(d > 0)
432			str += printdim(i, d);
433		else if(d < 0)
434			f = 1;
435	}
436
437	if(f) {
438		str += " /";
439		for(i=1; i<len n.dim; i++) {
440			d := n.dim[i];
441			if(d < 0)
442				str += printdim(i, -d);
443		}
444	}
445
446	return str;
447}
448
449readline(): int
450{
451	linep = 0;
452	line = "";
453	for(i:=0;; i++) {
454		c := fi.getc();
455		if(c < 0)
456			return 1;
457		if(c == '\n')
458			return 0;
459		line[i] = c;
460	}
461}
462
463lookup(f: int): ref Var
464{
465	h := 0;
466	for(i:=0; i < len sym; i++)
467		h = h*13 + sym[i];
468	if(h < 0)
469		h ^= int 16r80000000;
470	h %= len vars;
471
472	for(vl:=vars[h]; vl != nil; vl = tl vl)
473		if((hd vl).name == sym)
474			return hd vl;
475	if(f)
476		return nil;
477	v := ref Var(sym, Node.mk(0.0));
478	vars[h] = v :: vars[h];
479
480	p := 1.0;
481	for(;;) {
482		p = fmul(p, pname());
483		if(p == 0.0)
484			break;
485		w := lookup(1);
486		if(w != nil) {
487			v.node = w.node.copy();
488			v.node.val = fmul(v.node.val, p);
489			break;
490		}
491	}
492	return v;
493}
494
495prefix: array of Prefix = array[] of {
496	(1e-24,	"yocto"),
497	(1e-21,	"zepto"),
498	(1e-18,	"atto"),
499	(1e-15,	"femto"),
500	(1e-12,	"pico"),
501	(1e-9,	"nano"),
502	(1e-6,	"micro"),
503	(1e-6,	"μ"),
504	(1e-3,	"milli"),
505	(1e-2,	"centi"),
506	(1e-1,	"deci"),
507	(1e1,	"deka"),
508	(1e2,	"hecta"),
509	(1e2,	"hecto"),
510	(1e3,	"kilo"),
511	(1e6,	"mega"),
512	(1e6,	"meg"),
513	(1e9,	"giga"),
514	(1e12,	"tera"),
515	(1e15,	"peta"),
516	(1e18,	"exa"),
517	(1e21,	"zetta"),
518	(1e24,	"yotta")
519};
520
521pname(): real
522{
523	#
524	# rip off normal prefices
525	#
526Pref:
527	for(i:=0; i < len prefix; i++) {
528		p := prefix[i].pname;
529		for(j:=0; j < len p; j++)
530			if(j >= len sym || p[j] != sym[j])
531				continue Pref;
532		sym = sym[j:];
533		return prefix[i].val;
534	}
535
536	#
537	# rip off 's' suffixes
538	#
539	for(j:=0; j < len sym; j++)
540		;
541	j--;
542	# j>1 is special hack to disallow ms finding m
543	if(j > 1 && sym[j] == 's') {
544		sym = sym[0:j];
545		return 1.0;
546	}
547	return 0.0;
548}
549
550#
551# reads a floating-point number
552#
553
554readreal[T](f: ref fn(t: T): int, vp: T): (real, int)
555{
556	s := "";
557	c := f(vp);
558	while(c == ' ' || c == '\t')
559		c = f(vp);
560	if(c == '-' || c == '+'){
561		s[len s] = c;
562		c = f(vp);
563	}
564	start := len s;
565	while(c >= '0' && c <= '9'){
566		s[len s] = c;
567		c = f(vp);
568	}
569	if(c == '.'){
570		s[len s] = c;
571		c = f(vp);
572		while(c >= '0' && c <= '9'){
573			s[len s] = c;
574			c = f(vp);
575		}
576	}
577	if(len s > start && (c == 'e' || c == 'E')){
578		s[len s] = c;
579		c = f(vp);
580		if(c == '-' || c == '+'){
581			s[len s] = c;
582			c = f(vp);
583		}
584		while(c >= '0' && c <= '9'){
585			s[len s] = c;
586			c = f(vp);
587		}
588	}
589	return (real s, c);
590}
591
592#
593# careful floating point
594#
595
596fmul(a, b: real): real
597{
598	l: real;
599
600	if(a <= 0.0) {
601		if(a == 0.0)
602			return 0.0;
603		l = math->log(-a);
604	} else
605		l = math->log(a);
606
607	if(b <= 0.0) {
608		if(b == 0.0)
609			return 0.0;
610		l += math->log(-b);
611	} else
612		l += math->log(b);
613
614	if(l > Maxe) {
615		yyerror("overflow in multiply");
616		return 1.0;
617	}
618	if(l < -Maxe) {
619		yyerror("underflow in multiply");
620		return 0.0;
621	}
622	return a*b;
623}
624
625fdiv(a, b: real): real
626{
627	l: real;
628
629	if(a <= 0.0) {
630		if(a == 0.0)
631			return 0.0;
632		l = math->log(-a);
633	} else
634		l = math->log(a);
635
636	if(b <= 0.0) {
637		if(b == 0.0) {
638			yyerror("division by zero");
639			return 1.0;
640		}
641		l -= math->log(-b);
642	} else
643		l -= math->log(b);
644
645	if(l > Maxe) {
646		yyerror("overflow in divide");
647		return 1.0;
648	}
649	if(l < -Maxe) {
650		yyerror("underflow in divide");
651		return 0.0;
652	}
653	return a/b;
654}
655
656fadd(a, b: real): real
657{
658	return a + b;
659}
660yyexca := array[] of {-1, 1,
661	1, -1,
662	-2, 0,
663};
664YYNPROD: con 21;
665YYPRIVATE: con 57344;
666yytoknames: array of string;
667yystates: array of string;
668yydebug: con 0;
669YYLAST:	con 41;
670yyact := array[] of {
671   8,  10,   7,   9,  16,  17,  12,  11,  20,  21,
672  15,  31,  23,   6,   4,  12,  11,  22,  13,   5,
673   1,  27,  28,   0,  14,  30,  29,  13,  20,  20,
674  25,  26,   0,  24,  18,  19,  16,  17,   2,   0,
675   3,
676};
677yypact := array[] of {
678  31,-1000,   9,  11,   2,  26,  22,  11,   3,  -3,
679-1000,-1000,-1000,  11,  26,-1000,  11,  11,  11,  11,
680   3,-1000,  11,  11,  -6,  22,  22,  11,  11,  -3,
681-1000,-1000,
682};
683yypgo := array[] of {
684   0,  20,  19,   1,   3,   0,   2,  13,
685};
686yyr1 := array[] of {
687   0,   1,   1,   1,   1,   2,   2,   2,   7,   7,
688   7,   6,   6,   5,   5,   5,   4,   4,   3,   3,
689   3,
690};
691yyr2 := array[] of {
692   0,   3,   3,   2,   1,   1,   3,   3,   1,   3,
693   3,   1,   2,   1,   2,   3,   1,   3,   1,   1,
694   3,
695};
696yychk := array[] of {
697-1000,  -1,   7,   9,   5,  -2,  -7,  -6,  -5,  -4,
698  -3,   5,   4,  16,  -2,   8,  10,  11,  12,  13,
699  -5,   6,  14,  15,  -2,  -7,  -7,  -6,  -6,  -4,
700  -3,  17,
701};
702yydef := array[] of {
703   0,  -2,   0,   4,   0,   3,   5,   8,  11,  13,
704  16,  18,  19,   0,   1,   2,   0,   0,   0,   0,
705  12,  14,   0,   0,   0,   6,   7,   9,  10,  15,
706  17,  20,
707};
708yytok1 := array[] of {
709   1,   3,   3,   3,   3,   3,   3,   3,   3,   3,
710   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,
711   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,
712   3,   3,   3,   3,   3,   8,   3,   3,   3,   3,
713  16,  17,  12,  10,   3,  11,   3,  13,   3,   3,
714   3,   3,   3,   3,   3,   3,   3,   3,   7,   3,
715   3,   3,   3,   9,   3,   3,   3,   3,   3,   3,
716   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,
717   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,
718   3,   3,   3,   3,  14,   3,   3,   3,   3,   3,
719   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,
720   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,
721   3,   3,   3,   3,  15,
722};
723yytok2 := array[] of {
724   2,   3,   4,   5,   6,
725};
726yytok3 := array[] of {
727   0
728};
729
730YYSys: module
731{
732	FD: adt
733	{
734		fd:	int;
735	};
736	fildes:		fn(fd: int): ref FD;
737	fprint:		fn(fd: ref FD, s: string, *): int;
738};
739
740yysys: YYSys;
741yystderr: ref YYSys->FD;
742
743YYFLAG: con -1000;
744
745# parser for yacc output
746
747yytokname(yyc: int): string
748{
749	if(yyc > 0 && yyc <= len yytoknames && yytoknames[yyc-1] != nil)
750		return yytoknames[yyc-1];
751	return "<"+string yyc+">";
752}
753
754yystatname(yys: int): string
755{
756	if(yys >= 0 && yys < len yystates && yystates[yys] != nil)
757		return yystates[yys];
758	return "<"+string yys+">\n";
759}
760
761yylex1(yylex: ref YYLEX): int
762{
763	c : int;
764	yychar := yylex.lex();
765	if(yychar <= 0)
766		c = yytok1[0];
767	else if(yychar < len yytok1)
768		c = yytok1[yychar];
769	else if(yychar >= YYPRIVATE && yychar < YYPRIVATE+len yytok2)
770		c = yytok2[yychar-YYPRIVATE];
771	else{
772		n := len yytok3;
773		c = 0;
774		for(i := 0; i < n; i+=2) {
775			if(yytok3[i+0] == yychar) {
776				c = yytok3[i+1];
777				break;
778			}
779		}
780		if(c == 0)
781			c = yytok2[1];	# unknown char
782	}
783	if(yydebug >= 3)
784		yysys->fprint(yystderr, "lex %.4ux %s\n", yychar, yytokname(c));
785	return c;
786}
787
788YYS: adt
789{
790	yyv: YYSTYPE;
791	yys: int;
792};
793
794yyparse(yylex: ref YYLEX): int
795{
796	if(yydebug >= 1 && yysys == nil) {
797		yysys = load YYSys "$Sys";
798		yystderr = yysys->fildes(2);
799	}
800
801	yys := array[YYMAXDEPTH] of YYS;
802
803	yyval: YYSTYPE;
804	yystate := 0;
805	yychar := -1;
806	yynerrs := 0;		# number of errors
807	yyerrflag := 0;		# error recovery flag
808	yyp := -1;
809	yyn := 0;
810
811yystack:
812	for(;;){
813		# put a state and value onto the stack
814		if(yydebug >= 4)
815			yysys->fprint(yystderr, "char %s in %s", yytokname(yychar), yystatname(yystate));
816
817		yyp++;
818		if(yyp >= len yys)
819			yys = (array[len yys * 2] of YYS)[0:] = yys;
820		yys[yyp].yys = yystate;
821		yys[yyp].yyv = yyval;
822
823		for(;;){
824			yyn = yypact[yystate];
825			if(yyn > YYFLAG) {	# simple state
826				if(yychar < 0)
827					yychar = yylex1(yylex);
828				yyn += yychar;
829				if(yyn >= 0 && yyn < YYLAST) {
830					yyn = yyact[yyn];
831					if(yychk[yyn] == yychar) { # valid shift
832						yychar = -1;
833						yyp++;
834						if(yyp >= len yys)
835							yys = (array[len yys * 2] of YYS)[0:] = yys;
836						yystate = yyn;
837						yys[yyp].yys = yystate;
838						yys[yyp].yyv = yylex.lval;
839						if(yyerrflag > 0)
840							yyerrflag--;
841						if(yydebug >= 4)
842							yysys->fprint(yystderr, "char %s in %s", yytokname(yychar), yystatname(yystate));
843						continue;
844					}
845				}
846			}
847
848			# default state action
849			yyn = yydef[yystate];
850			if(yyn == -2) {
851				if(yychar < 0)
852					yychar = yylex1(yylex);
853
854				# look through exception table
855				for(yyxi:=0;; yyxi+=2)
856					if(yyexca[yyxi] == -1 && yyexca[yyxi+1] == yystate)
857						break;
858				for(yyxi += 2;; yyxi += 2) {
859					yyn = yyexca[yyxi];
860					if(yyn < 0 || yyn == yychar)
861						break;
862				}
863				yyn = yyexca[yyxi+1];
864				if(yyn < 0){
865					yyn = 0;
866					break yystack;
867				}
868			}
869
870			if(yyn != 0)
871				break;
872
873			# error ... attempt to resume parsing
874			if(yyerrflag == 0) { # brand new error
875				yylex.error("syntax error");
876				yynerrs++;
877				if(yydebug >= 1) {
878					yysys->fprint(yystderr, "%s", yystatname(yystate));
879					yysys->fprint(yystderr, "saw %s\n", yytokname(yychar));
880				}
881			}
882
883			if(yyerrflag != 3) { # incompletely recovered error ... try again
884				yyerrflag = 3;
885
886				# find a state where "error" is a legal shift action
887				while(yyp >= 0) {
888					yyn = yypact[yys[yyp].yys] + YYERRCODE;
889					if(yyn >= 0 && yyn < YYLAST) {
890						yystate = yyact[yyn];  # simulate a shift of "error"
891						if(yychk[yystate] == YYERRCODE)
892							continue yystack;
893					}
894
895					# the current yyp has no shift onn "error", pop stack
896					if(yydebug >= 2)
897						yysys->fprint(yystderr, "error recovery pops state %d, uncovers %d\n",
898							yys[yyp].yys, yys[yyp-1].yys );
899					yyp--;
900				}
901				# there is no state on the stack with an error shift ... abort
902				yyn = 1;
903				break yystack;
904			}
905
906			# no shift yet; clobber input char
907			if(yydebug >= 2)
908				yysys->fprint(yystderr, "error recovery discards %s\n", yytokname(yychar));
909			if(yychar == YYEOFCODE) {
910				yyn = 1;
911				break yystack;
912			}
913			yychar = -1;
914			# try again in the same state
915		}
916
917		# reduction by production yyn
918		if(yydebug >= 2)
919			yysys->fprint(yystderr, "reduce %d in:\n\t%s", yyn, yystatname(yystate));
920
921		yypt := yyp;
922		yyp -= yyr2[yyn];
923#		yyval = yys[yyp+1].yyv;
924		yym := yyn;
925
926		# consult goto table to find next state
927		yyn = yyr1[yyn];
928		yyg := yypgo[yyn];
929		yyj := yyg + yys[yyp].yys + 1;
930
931		if(yyj >= YYLAST || yychk[yystate=yyact[yyj]] != -yyn)
932			yystate = yyact[yyg];
933		case yym {
934
9351=>
936#line	90	"units.y"
937{
938		f := yys[yypt-1].yyv.var.node.dim[0];
939		yys[yypt-1].yyv.var.node = yys[yypt-0].yyv.node.copy();
940		yys[yypt-1].yyv.var.node.dim[0] = 1;
941		if(f)
942			yyerror(sys->sprint("redefinition of %s", yys[yypt-1].yyv.var.name));
943		else if(vflag)
944			sys->print("%s\t%s\n", yys[yypt-1].yyv.var.name, yys[yypt-1].yyv.var.node.text());
945	}
9462=>
947#line	100	"units.y"
948{
949		for(i:=1; i<Ndim; i++)
950			if(fund[i] == nil)
951				break;
952		if(i >= Ndim) {
953			yyerror("too many dimensions");
954			i = Ndim-1;
955		}
956		fund[i] = yys[yypt-1].yyv.var;
957
958		f := yys[yypt-1].yyv.var.node.dim[0];
959		yys[yypt-1].yyv.var.node = Node.mk(1.0);
960		yys[yypt-1].yyv.var.node.dim[0] = 1;
961		yys[yypt-1].yyv.var.node.dim[i] = 1;
962		if(f)
963			yyerror(sys->sprint("redefinition of %s", yys[yypt-1].yyv.var.name));
964		else if(vflag)
965			sys->print("%s\t#\n", yys[yypt-1].yyv.var.name);
966	}
9673=>
968#line	120	"units.y"
969{
970		retnode1 = yys[yypt-0].yyv.node.copy();
971	}
9724=>
973#line	124	"units.y"
974{
975		retnode1 = Node.mk(1.0);
976	}
9775=>
978yyval.node = yys[yyp+1].yyv.node;
9796=>
980#line	131	"units.y"
981{
982		yyval.node = yys[yypt-2].yyv.node.add(yys[yypt-0].yyv.node);
983	}
9847=>
985#line	135	"units.y"
986{
987		yyval.node = yys[yypt-2].yyv.node.sub(yys[yypt-0].yyv.node);
988	}
9898=>
990yyval.node = yys[yyp+1].yyv.node;
9919=>
992#line	142	"units.y"
993{
994		yyval.node = yys[yypt-2].yyv.node.mul(yys[yypt-0].yyv.node);
995	}
99610=>
997#line	146	"units.y"
998{
999		yyval.node = yys[yypt-2].yyv.node.div(yys[yypt-0].yyv.node);
1000	}
100111=>
1002yyval.node = yys[yyp+1].yyv.node;
100312=>
1004#line	153	"units.y"
1005{
1006		yyval.node = yys[yypt-1].yyv.node.mul(yys[yypt-0].yyv.node);
1007	}
100813=>
1009yyval.node = yys[yyp+1].yyv.node;
101014=>
1011#line	160	"units.y"
1012{
1013		yyval.node = yys[yypt-1].yyv.node.xpn(yys[yypt-0].yyv.numb);
1014	}
101515=>
1016#line	164	"units.y"
1017{
1018		for(i:=1; i<Ndim; i++)
1019			if(yys[yypt-0].yyv.node.dim[i]) {
1020				yyerror("exponent has units");
1021				yyval.node = yys[yypt-2].yyv.node;
1022				break;
1023			}
1024		if(i >= Ndim) {
1025			i = int yys[yypt-0].yyv.node.val;
1026			if(real i != yys[yypt-0].yyv.node.val)
1027				yyerror("exponent not integral");
1028			yyval.node = yys[yypt-2].yyv.node.xpn(i);
1029		}
1030	}
103116=>
1032yyval.node = yys[yyp+1].yyv.node;
103317=>
1034#line	182	"units.y"
1035{
1036		yyval.node = yys[yypt-2].yyv.node.div(yys[yypt-0].yyv.node);
1037	}
103818=>
1039#line	188	"units.y"
1040{
1041		if(yys[yypt-0].yyv.var.node.dim[0] == 0) {
1042			yyerror(sys->sprint("undefined %s", yys[yypt-0].yyv.var.name));
1043			yyval.node = Node.mk(1.0);
1044		} else
1045			yyval.node = yys[yypt-0].yyv.var.node.copy();
1046	}
104719=>
1048#line	196	"units.y"
1049{
1050		yyval.node = Node.mk(yys[yypt-0].yyv.val);
1051	}
105220=>
1053#line	200	"units.y"
1054{
1055		yyval.node = yys[yypt-1].yyv.node;
1056	}
1057		}
1058	}
1059
1060	return yyn;
1061}
1062