summaryrefslogtreecommitdiff
path: root/appl/cmd/units.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/cmd/units.b')
-rw-r--r--appl/cmd/units.b1061
1 files changed, 1061 insertions, 0 deletions
diff --git a/appl/cmd/units.b b/appl/cmd/units.b
new file mode 100644
index 00000000..b1dffc60
--- /dev/null
+++ b/appl/cmd/units.b
@@ -0,0 +1,1061 @@
+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;
+}