ref: babf901b4a508c3ec5d1f89655f10377bbdf9637
dir: /appl/cmd/units.b/
implement Units;
#line 2 "units.y"
#
# subject to the Lucent Public License 1.02
#
include "sys.m";
sys: Sys;
include "draw.m";
include "bufio.m";
bufio: Bufio;
Iobuf: import bufio;
include "math.m";
math: Math;
include "arg.m";
Ndim: con 15; # number of dimensions
Nvar: con 203; # hash table size
Maxe: con 695.0; # log of largest number
Node: adt
{
val: real;
dim: array of int; # [Ndim] schar
mk: fn(v: real): Node;
text: fn(n: self Node): string;
add: fn(a: self Node, b: Node): Node;
sub: fn(a: self Node, b: Node): Node;
mul: fn(a: self Node, b: Node): Node;
div: fn(a: self Node, b: Node): Node;
xpn: fn(a: self Node, b: int): Node;
copy: fn(a: self Node): Node;
};
Var: adt
{
name: string;
node: Node;
};
Prefix: adt
{
val: real;
pname: string;
};
digval := 0;
fi: ref Iobuf;
fund := array[Ndim] of ref Var;
line: string;
lineno := 0;
linep := 0;
nerrors := 0;
peekrune := 0;
retnode1: Node;
retnode2: Node;
retnode: Node;
sym: string;
vars := array[Nvar] of list of ref Var;
vflag := 0;
YYSTYPE: adt {
node: Node;
var: ref Var;
numb: int;
val: real;
};
YYLEX: adt {
lval: YYSTYPE;
lex: fn(l: self ref YYLEX): int;
error: fn(l: self ref YYLEX, msg: string);
};
Units: module {
init: fn(nil: ref Draw->Context, args: list of string);
VAL: con 57346;
VAR: con 57347;
SUP: con 57348;
};
YYEOFCODE: con 1;
YYERRCODE: con 2;
YYMAXDEPTH: con 200;
#line 203 "units.y"
init(nil: ref Draw->Context, args: list of string)
{
sys = load Sys Sys->PATH;
bufio = load Bufio Bufio->PATH;
math = load Math Math->PATH;
arg := load Arg Arg->PATH;
arg->init(args);
arg->setusage("units [-v] [file]");
while((o := arg->opt()) != 0)
case o {
'v' => vflag = 1;
* => arg->usage();
}
args = arg->argv();
arg = nil;
file := "/lib/units";
if(args != nil)
file = hd args;
fi = bufio->open(file, Sys->OREAD);
if(fi == nil) {
sys->fprint(sys->fildes(2), "units: cannot open %s: %r\n", file);
raise "fail:open";
}
lex := ref YYLEX;
#
# read the 'units' file to
# develop a database
#
lineno = 0;
for(;;) {
lineno++;
if(readline())
break;
if(len line == 0 || line[0] == '/')
continue;
peekrune = ':';
yyparse(lex);
}
#
# read the console to
# print ratio of pairs
#
fi = bufio->fopen(sys->fildes(0), Sys->OREAD);
lineno = 0;
for(;;) {
if(lineno & 1)
sys->print("you want: ");
else
sys->print("you have: ");
if(readline())
break;
peekrune = '?';
nerrors = 0;
yyparse(lex);
if(nerrors)
continue;
if(lineno & 1) {
isspcl: int;
(isspcl, retnode) = specialcase(retnode2, retnode1);
if(isspcl)
sys->print("\tis %s\n", retnode.text());
else {
retnode = retnode2.div(retnode1);
sys->print("\t* %s\n", retnode.text());
retnode = retnode1.div(retnode2);
sys->print("\t/ %s\n", retnode.text());
}
} else
retnode2 = retnode1.copy();
lineno++;
}
sys->print("\n");
}
YYLEX.lex(lex: self ref YYLEX): int
{
c := peekrune;
peekrune = ' ';
while(c == ' ' || c == '\t'){
if(linep >= len line)
return 0; # -1?
c = line[linep++];
}
case c {
'0' to '9' or '.' =>
digval = c;
(lex.lval.val, peekrune) = readreal(gdigit, lex);
return VAL;
'×' =>
return '*';
'÷' =>
return '/';
'¹' or
'ⁱ' =>
lex.lval.numb = 1;
return SUP;
'²' or
'' =>
lex.lval.numb = 2;
return SUP;
'³' or
'' =>
lex.lval.numb = 3;
return SUP;
* =>
if(ralpha(c)){
sym = "";
for(i:=0;; i++) {
sym[i] = c;
if(linep >= len line){
c = ' ';
break;
}
c = line[linep++];
if(!ralpha(c))
break;
}
peekrune = c;
lex.lval.var = lookup(0);
return VAR;
}
}
return c;
}
#
# all characters that have some
# meaning. rest are usable as names
#
ralpha(c: int): int
{
case c {
0 or
'+' or
'-' or
'*' or
'/' or
'[' or
']' or
'(' or
')' or
'^' or
':' or
'?' or
' ' or
'\t' or
'.' or
'|' or
'#' or
'¹' or
'ⁱ' or
'²' or
'' or
'³' or
'' or
'×' or
'÷' =>
return 0;
}
return 1;
}
gdigit(nil: ref YYLEX): int
{
c := digval;
if(c) {
digval = 0;
return c;
}
if(linep >= len line)
return 0;
return line[linep++];
}
YYLEX.error(lex: self ref YYLEX, s: string)
{
#
# hack to intercept message from yaccpar
#
if(s == "syntax error") {
lex.error(sys->sprint("syntax error, last name: %s", sym));
return;
}
sys->print("%d: %s\n\t%s\n", lineno, line, s);
nerrors++;
if(nerrors > 5) {
sys->print("too many errors\n");
raise "fail:errors";
}
}
yyerror(s: string)
{
l := ref YYLEX;
l.error(s);
}
Node.mk(v: real): Node
{
return (v, array[Ndim] of {* => 0});
}
Node.add(a: self Node, b: Node): Node
{
c := Node.mk(fadd(a.val, b.val));
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");
}
return c;
}
Node.sub(a: self Node, b: Node): Node
{
c := Node.mk(fadd(a.val, -b.val));
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");
}
return c;
}
Node.mul(a: self Node, b: Node): Node
{
c := Node.mk(fmul(a.val, b.val));
for(i:=0; i<Ndim; i++)
c.dim[i] = a.dim[i] + b.dim[i];
return c;
}
Node.div(a: self Node, b: Node): Node
{
c := Node.mk(fdiv(a.val, b.val));
for(i:=0; i<Ndim; i++)
c.dim[i] = a.dim[i] - b.dim[i];
return c;
}
Node.xpn(a: self Node, b: int): Node
{
c := Node.mk(1.0);
if(b < 0) {
b = -b;
for(i:=0; i<b; i++)
c = c.div(a);
} else
for(i:=0; i<b; i++)
c = c.mul(a);
return c;
}
Node.copy(a: self Node): Node
{
c := Node.mk(a.val);
c.dim[0:] = a.dim;
return c;
}
specialcase(a, b: Node): (int, Node)
{
c := Node.mk(0.0);
d1 := 0;
d2 := 0;
for(i:=1; i<Ndim; i++) {
d := a.dim[i];
if(d) {
if(d != 1 || d1)
return (0, c);
d1 = i;
}
d = b.dim[i];
if(d) {
if(d != 1 || d2)
return (0, c);
d2 = i;
}
}
if(d1 == 0 || d2 == 0)
return (0, c);
if(fund[d1].name == "°C" &&
fund[d2].name == "°F" &&
b.val == 1.0) {
c = b.copy();
c.val = a.val * 9. / 5. + 32.;
return (1, c);
}
if(fund[d1].name == "°F" &&
fund[d2].name == "°C" &&
b.val == 1.0) {
c = b.copy();
c.val = (a.val - 32.) * 5. / 9.;
return (1, c);
}
return (0, c);
}
printdim(d: int, n: int): string
{
s := "";
if(n) {
v := fund[d];
if(v != nil)
s += " "+v.name;
else
s += sys->sprint(" [%d]", d);
case n {
1 =>
;
2 =>
s += "²";
3 =>
s += "³";
4 =>
s += "⁴";
* =>
s += sys->sprint("^%d", n);
}
}
return s;
}
Node.text(n: self Node): string
{
str := sys->sprint("%.7g", n.val);
f := 0;
for(i:=1; i<len n.dim; i++) {
d := n.dim[i];
if(d > 0)
str += printdim(i, d);
else if(d < 0)
f = 1;
}
if(f) {
str += " /";
for(i=1; i<len n.dim; i++) {
d := n.dim[i];
if(d < 0)
str += printdim(i, -d);
}
}
return str;
}
readline(): int
{
linep = 0;
line = "";
for(i:=0;; i++) {
c := fi.getc();
if(c < 0)
return 1;
if(c == '\n')
return 0;
line[i] = c;
}
}
lookup(f: int): ref Var
{
h := 0;
for(i:=0; i < len sym; i++)
h = h*13 + sym[i];
if(h < 0)
h ^= int 16r80000000;
h %= len vars;
for(vl:=vars[h]; vl != nil; vl = tl vl)
if((hd vl).name == sym)
return hd vl;
if(f)
return nil;
v := ref Var(sym, Node.mk(0.0));
vars[h] = v :: vars[h];
p := 1.0;
for(;;) {
p = fmul(p, pname());
if(p == 0.0)
break;
w := lookup(1);
if(w != nil) {
v.node = w.node.copy();
v.node.val = fmul(v.node.val, p);
break;
}
}
return v;
}
prefix: array of Prefix = array[] of {
(1e-24, "yocto"),
(1e-21, "zepto"),
(1e-18, "atto"),
(1e-15, "femto"),
(1e-12, "pico"),
(1e-9, "nano"),
(1e-6, "micro"),
(1e-6, "μ"),
(1e-3, "milli"),
(1e-2, "centi"),
(1e-1, "deci"),
(1e1, "deka"),
(1e2, "hecta"),
(1e2, "hecto"),
(1e3, "kilo"),
(1e6, "mega"),
(1e6, "meg"),
(1e9, "giga"),
(1e12, "tera"),
(1e15, "peta"),
(1e18, "exa"),
(1e21, "zetta"),
(1e24, "yotta")
};
pname(): real
{
#
# rip off normal prefices
#
Pref:
for(i:=0; i < len prefix; i++) {
p := prefix[i].pname;
for(j:=0; j < len p; j++)
if(j >= len sym || p[j] != sym[j])
continue Pref;
sym = sym[j:];
return prefix[i].val;
}
#
# rip off 's' suffixes
#
for(j:=0; j < len sym; j++)
;
j--;
# j>1 is special hack to disallow ms finding m
if(j > 1 && sym[j] == 's') {
sym = sym[0:j];
return 1.0;
}
return 0.0;
}
#
# reads a floating-point number
#
readreal[T](f: ref fn(t: T): int, vp: T): (real, int)
{
s := "";
c := f(vp);
while(c == ' ' || c == '\t')
c = f(vp);
if(c == '-' || c == '+'){
s[len s] = c;
c = f(vp);
}
start := len s;
while(c >= '0' && c <= '9'){
s[len s] = c;
c = f(vp);
}
if(c == '.'){
s[len s] = c;
c = f(vp);
while(c >= '0' && c <= '9'){
s[len s] = c;
c = f(vp);
}
}
if(len s > start && (c == 'e' || c == 'E')){
s[len s] = c;
c = f(vp);
if(c == '-' || c == '+'){
s[len s] = c;
c = f(vp);
}
while(c >= '0' && c <= '9'){
s[len s] = c;
c = f(vp);
}
}
return (real s, c);
}
#
# careful floating point
#
fmul(a, b: real): real
{
l: real;
if(a <= 0.0) {
if(a == 0.0)
return 0.0;
l = math->log(-a);
} else
l = math->log(a);
if(b <= 0.0) {
if(b == 0.0)
return 0.0;
l += math->log(-b);
} else
l += math->log(b);
if(l > Maxe) {
yyerror("overflow in multiply");
return 1.0;
}
if(l < -Maxe) {
yyerror("underflow in multiply");
return 0.0;
}
return a*b;
}
fdiv(a, b: real): real
{
l: real;
if(a <= 0.0) {
if(a == 0.0)
return 0.0;
l = math->log(-a);
} else
l = math->log(a);
if(b <= 0.0) {
if(b == 0.0) {
yyerror("division by zero");
return 1.0;
}
l -= math->log(-b);
} else
l -= math->log(b);
if(l > Maxe) {
yyerror("overflow in divide");
return 1.0;
}
if(l < -Maxe) {
yyerror("underflow in divide");
return 0.0;
}
return a/b;
}
fadd(a, b: real): real
{
return a + b;
}
yyexca := array[] of {-1, 1,
1, -1,
-2, 0,
};
YYNPROD: con 21;
YYPRIVATE: con 57344;
yytoknames: array of string;
yystates: array of string;
yydebug: con 0;
YYLAST: con 41;
yyact := array[] of {
8, 10, 7, 9, 16, 17, 12, 11, 20, 21,
15, 31, 23, 6, 4, 12, 11, 22, 13, 5,
1, 27, 28, 0, 14, 30, 29, 13, 20, 20,
25, 26, 0, 24, 18, 19, 16, 17, 2, 0,
3,
};
yypact := array[] of {
31,-1000, 9, 11, 2, 26, 22, 11, 3, -3,
-1000,-1000,-1000, 11, 26,-1000, 11, 11, 11, 11,
3,-1000, 11, 11, -6, 22, 22, 11, 11, -3,
-1000,-1000,
};
yypgo := array[] of {
0, 20, 19, 1, 3, 0, 2, 13,
};
yyr1 := array[] of {
0, 1, 1, 1, 1, 2, 2, 2, 7, 7,
7, 6, 6, 5, 5, 5, 4, 4, 3, 3,
3,
};
yyr2 := array[] of {
0, 3, 3, 2, 1, 1, 3, 3, 1, 3,
3, 1, 2, 1, 2, 3, 1, 3, 1, 1,
3,
};
yychk := array[] of {
-1000, -1, 7, 9, 5, -2, -7, -6, -5, -4,
-3, 5, 4, 16, -2, 8, 10, 11, 12, 13,
-5, 6, 14, 15, -2, -7, -7, -6, -6, -4,
-3, 17,
};
yydef := array[] of {
0, -2, 0, 4, 0, 3, 5, 8, 11, 13,
16, 18, 19, 0, 1, 2, 0, 0, 0, 0,
12, 14, 0, 0, 0, 6, 7, 9, 10, 15,
17, 20,
};
yytok1 := array[] of {
1, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 8, 3, 3, 3, 3,
16, 17, 12, 10, 3, 11, 3, 13, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 7, 3,
3, 3, 3, 9, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 14, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 15,
};
yytok2 := array[] of {
2, 3, 4, 5, 6,
};
yytok3 := array[] of {
0
};
YYSys: module
{
FD: adt
{
fd: int;
};
fildes: fn(fd: int): ref FD;
fprint: fn(fd: ref FD, s: string, *): int;
};
yysys: YYSys;
yystderr: ref YYSys->FD;
YYFLAG: con -1000;
# parser for yacc output
yytokname(yyc: int): string
{
if(yyc > 0 && yyc <= len yytoknames && yytoknames[yyc-1] != nil)
return yytoknames[yyc-1];
return "<"+string yyc+">";
}
yystatname(yys: int): string
{
if(yys >= 0 && yys < len yystates && yystates[yys] != nil)
return yystates[yys];
return "<"+string yys+">\n";
}
yylex1(yylex: ref YYLEX): int
{
c : int;
yychar := yylex.lex();
if(yychar <= 0)
c = yytok1[0];
else if(yychar < len yytok1)
c = yytok1[yychar];
else if(yychar >= YYPRIVATE && yychar < YYPRIVATE+len yytok2)
c = yytok2[yychar-YYPRIVATE];
else{
n := len yytok3;
c = 0;
for(i := 0; i < n; i+=2) {
if(yytok3[i+0] == yychar) {
c = yytok3[i+1];
break;
}
}
if(c == 0)
c = yytok2[1]; # unknown char
}
if(yydebug >= 3)
yysys->fprint(yystderr, "lex %.4ux %s\n", yychar, yytokname(c));
return c;
}
YYS: adt
{
yyv: YYSTYPE;
yys: int;
};
yyparse(yylex: ref YYLEX): int
{
if(yydebug >= 1 && yysys == nil) {
yysys = load YYSys "$Sys";
yystderr = yysys->fildes(2);
}
yys := array[YYMAXDEPTH] of YYS;
yyval: YYSTYPE;
yystate := 0;
yychar := -1;
yynerrs := 0; # number of errors
yyerrflag := 0; # error recovery flag
yyp := -1;
yyn := 0;
yystack:
for(;;){
# put a state and value onto the stack
if(yydebug >= 4)
yysys->fprint(yystderr, "char %s in %s", yytokname(yychar), yystatname(yystate));
yyp++;
if(yyp >= len yys)
yys = (array[len yys * 2] of YYS)[0:] = yys;
yys[yyp].yys = yystate;
yys[yyp].yyv = yyval;
for(;;){
yyn = yypact[yystate];
if(yyn > YYFLAG) { # simple state
if(yychar < 0)
yychar = yylex1(yylex);
yyn += yychar;
if(yyn >= 0 && yyn < YYLAST) {
yyn = yyact[yyn];
if(yychk[yyn] == yychar) { # valid shift
yychar = -1;
yyp++;
if(yyp >= len yys)
yys = (array[len yys * 2] of YYS)[0:] = yys;
yystate = yyn;
yys[yyp].yys = yystate;
yys[yyp].yyv = yylex.lval;
if(yyerrflag > 0)
yyerrflag--;
if(yydebug >= 4)
yysys->fprint(yystderr, "char %s in %s", yytokname(yychar), yystatname(yystate));
continue;
}
}
}
# default state action
yyn = yydef[yystate];
if(yyn == -2) {
if(yychar < 0)
yychar = yylex1(yylex);
# look through exception table
for(yyxi:=0;; yyxi+=2)
if(yyexca[yyxi] == -1 && yyexca[yyxi+1] == yystate)
break;
for(yyxi += 2;; yyxi += 2) {
yyn = yyexca[yyxi];
if(yyn < 0 || yyn == yychar)
break;
}
yyn = yyexca[yyxi+1];
if(yyn < 0){
yyn = 0;
break yystack;
}
}
if(yyn != 0)
break;
# error ... attempt to resume parsing
if(yyerrflag == 0) { # brand new error
yylex.error("syntax error");
yynerrs++;
if(yydebug >= 1) {
yysys->fprint(yystderr, "%s", yystatname(yystate));
yysys->fprint(yystderr, "saw %s\n", yytokname(yychar));
}
}
if(yyerrflag != 3) { # incompletely recovered error ... try again
yyerrflag = 3;
# find a state where "error" is a legal shift action
while(yyp >= 0) {
yyn = yypact[yys[yyp].yys] + YYERRCODE;
if(yyn >= 0 && yyn < YYLAST) {
yystate = yyact[yyn]; # simulate a shift of "error"
if(yychk[yystate] == YYERRCODE)
continue yystack;
}
# the current yyp has no shift onn "error", pop stack
if(yydebug >= 2)
yysys->fprint(yystderr, "error recovery pops state %d, uncovers %d\n",
yys[yyp].yys, yys[yyp-1].yys );
yyp--;
}
# there is no state on the stack with an error shift ... abort
yyn = 1;
break yystack;
}
# no shift yet; clobber input char
if(yydebug >= 2)
yysys->fprint(yystderr, "error recovery discards %s\n", yytokname(yychar));
if(yychar == YYEOFCODE) {
yyn = 1;
break yystack;
}
yychar = -1;
# try again in the same state
}
# reduction by production yyn
if(yydebug >= 2)
yysys->fprint(yystderr, "reduce %d in:\n\t%s", yyn, yystatname(yystate));
yypt := yyp;
yyp -= yyr2[yyn];
# yyval = yys[yyp+1].yyv;
yym := yyn;
# consult goto table to find next state
yyn = yyr1[yyn];
yyg := yypgo[yyn];
yyj := yyg + yys[yyp].yys + 1;
if(yyj >= YYLAST || yychk[yystate=yyact[yyj]] != -yyn)
yystate = yyact[yyg];
case yym {
1=>
#line 90 "units.y"
{
f := yys[yypt-1].yyv.var.node.dim[0];
yys[yypt-1].yyv.var.node = yys[yypt-0].yyv.node.copy();
yys[yypt-1].yyv.var.node.dim[0] = 1;
if(f)
yyerror(sys->sprint("redefinition of %s", yys[yypt-1].yyv.var.name));
else if(vflag)
sys->print("%s\t%s\n", yys[yypt-1].yyv.var.name, yys[yypt-1].yyv.var.node.text());
}
2=>
#line 100 "units.y"
{
for(i:=1; i<Ndim; i++)
if(fund[i] == nil)
break;
if(i >= Ndim) {
yyerror("too many dimensions");
i = Ndim-1;
}
fund[i] = yys[yypt-1].yyv.var;
f := yys[yypt-1].yyv.var.node.dim[0];
yys[yypt-1].yyv.var.node = Node.mk(1.0);
yys[yypt-1].yyv.var.node.dim[0] = 1;
yys[yypt-1].yyv.var.node.dim[i] = 1;
if(f)
yyerror(sys->sprint("redefinition of %s", yys[yypt-1].yyv.var.name));
else if(vflag)
sys->print("%s\t#\n", yys[yypt-1].yyv.var.name);
}
3=>
#line 120 "units.y"
{
retnode1 = yys[yypt-0].yyv.node.copy();
}
4=>
#line 124 "units.y"
{
retnode1 = Node.mk(1.0);
}
5=>
yyval.node = yys[yyp+1].yyv.node;
6=>
#line 131 "units.y"
{
yyval.node = yys[yypt-2].yyv.node.add(yys[yypt-0].yyv.node);
}
7=>
#line 135 "units.y"
{
yyval.node = yys[yypt-2].yyv.node.sub(yys[yypt-0].yyv.node);
}
8=>
yyval.node = yys[yyp+1].yyv.node;
9=>
#line 142 "units.y"
{
yyval.node = yys[yypt-2].yyv.node.mul(yys[yypt-0].yyv.node);
}
10=>
#line 146 "units.y"
{
yyval.node = yys[yypt-2].yyv.node.div(yys[yypt-0].yyv.node);
}
11=>
yyval.node = yys[yyp+1].yyv.node;
12=>
#line 153 "units.y"
{
yyval.node = yys[yypt-1].yyv.node.mul(yys[yypt-0].yyv.node);
}
13=>
yyval.node = yys[yyp+1].yyv.node;
14=>
#line 160 "units.y"
{
yyval.node = yys[yypt-1].yyv.node.xpn(yys[yypt-0].yyv.numb);
}
15=>
#line 164 "units.y"
{
for(i:=1; i<Ndim; i++)
if(yys[yypt-0].yyv.node.dim[i]) {
yyerror("exponent has units");
yyval.node = yys[yypt-2].yyv.node;
break;
}
if(i >= Ndim) {
i = int yys[yypt-0].yyv.node.val;
if(real i != yys[yypt-0].yyv.node.val)
yyerror("exponent not integral");
yyval.node = yys[yypt-2].yyv.node.xpn(i);
}
}
16=>
yyval.node = yys[yyp+1].yyv.node;
17=>
#line 182 "units.y"
{
yyval.node = yys[yypt-2].yyv.node.div(yys[yypt-0].yyv.node);
}
18=>
#line 188 "units.y"
{
if(yys[yypt-0].yyv.var.node.dim[0] == 0) {
yyerror(sys->sprint("undefined %s", yys[yypt-0].yyv.var.name));
yyval.node = Node.mk(1.0);
} else
yyval.node = yys[yypt-0].yyv.var.node.copy();
}
19=>
#line 196 "units.y"
{
yyval.node = Node.mk(yys[yypt-0].yyv.val);
}
20=>
#line 200 "units.y"
{
yyval.node = yys[yypt-1].yyv.node;
}
}
}
return yyn;
}