summaryrefslogtreecommitdiff
path: root/appl/cmd/limbo
diff options
context:
space:
mode:
Diffstat (limited to 'appl/cmd/limbo')
-rw-r--r--appl/cmd/limbo/arg.m50
-rw-r--r--appl/cmd/limbo/asm.b263
-rw-r--r--appl/cmd/limbo/com.b1387
-rw-r--r--appl/cmd/limbo/decls.b1177
-rw-r--r--appl/cmd/limbo/dis.b560
-rw-r--r--appl/cmd/limbo/disoptab.m355
-rw-r--r--appl/cmd/limbo/ecom.b2345
-rw-r--r--appl/cmd/limbo/gen.b1012
-rw-r--r--appl/cmd/limbo/isa.m247
-rw-r--r--appl/cmd/limbo/lex.b1146
-rw-r--r--appl/cmd/limbo/limbo.b3099
-rw-r--r--appl/cmd/limbo/limbo.m527
-rw-r--r--appl/cmd/limbo/limbo.y1973
-rw-r--r--appl/cmd/limbo/mkfile35
-rw-r--r--appl/cmd/limbo/nodes.b1402
-rw-r--r--appl/cmd/limbo/opname.m109
-rw-r--r--appl/cmd/limbo/optim.b3
-rw-r--r--appl/cmd/limbo/sbl.b397
-rw-r--r--appl/cmd/limbo/stubs.b575
-rw-r--r--appl/cmd/limbo/typecheck.b3223
-rw-r--r--appl/cmd/limbo/types.b4234
21 files changed, 24119 insertions, 0 deletions
diff --git a/appl/cmd/limbo/arg.m b/appl/cmd/limbo/arg.m
new file mode 100644
index 00000000..212752c1
--- /dev/null
+++ b/appl/cmd/limbo/arg.m
@@ -0,0 +1,50 @@
+Arg: adt
+{
+ argv: list of string;
+ c: int;
+ opts: string;
+
+ init: fn(argv: list of string): ref Arg;
+ opt: fn(arg: self ref Arg): int;
+ arg: fn(arg: self ref Arg): string;
+};
+
+Arg.init(argv: list of string): ref Arg
+{
+ if(argv != nil)
+ argv = tl argv;
+ return ref Arg(argv, 0, nil);
+}
+
+Arg.opt(arg: self ref Arg): int
+{
+ if(arg.opts != ""){
+ arg.c = arg.opts[0];
+ arg.opts = arg.opts[1:];
+ return arg.c;
+ }
+ if(arg.argv == nil)
+ return arg.c = 0;
+ arg.opts = hd arg.argv;
+ if(len arg.opts < 2 || arg.opts[0] != '-')
+ return arg.c = 0;
+ arg.argv = tl arg.argv;
+ if(arg.opts == "--")
+ return arg.c = 0;
+ arg.c = arg.opts[1];
+ arg.opts = arg.opts[2:];
+ return arg.c;
+}
+
+Arg.arg(arg: self ref Arg): string
+{
+ s := arg.opts;
+ arg.opts = "";
+ if(s != "")
+ return s;
+ if(arg.argv == nil)
+ return "";
+ s = hd arg.argv;
+ arg.argv = tl arg.argv;
+ return s;
+}
diff --git a/appl/cmd/limbo/asm.b b/appl/cmd/limbo/asm.b
new file mode 100644
index 00000000..3788d016
--- /dev/null
+++ b/appl/cmd/limbo/asm.b
@@ -0,0 +1,263 @@
+asmentry(e: ref Decl)
+{
+ if(e == nil)
+ return;
+ bout.puts("\tentry\t"+string e.pc.pc+", "+string e.desc.id+"\n");
+}
+
+asmmod(m: ref Decl)
+{
+ bout.puts("\tmodule\t");
+ bout.puts(m.sym.name);
+ bout.putc('\n');
+ for(m = m.ty.tof.ids; m != nil; m = m.next){
+ case m.store{
+ Dglobal =>
+ bout.puts("\tlink\t-1,-1,0x"+hex(sign(m), 0)+",\".mp\"\n");
+ Dfn =>
+ bout.puts("\tlink\t"+string m.desc.id+","+string m.pc.pc+",0x"+string hex(sign(m), 0)+",\"");
+ if(m.dot.ty.kind == Tadt)
+ bout.puts(m.dot.sym.name+".");
+ bout.puts(m.sym.name+"\"\n");
+ }
+ }
+}
+
+asmpath()
+{
+ bout.puts("\tsource\t\"" + srcpath() + "\"\n");
+}
+
+asmdesc(d: ref Desc)
+{
+ for(; d != nil; d = d.next){
+ bout.puts("\tdesc\t$"+string d.id+","+string d.size+",\"");
+ e := d.nmap;
+ m := d.map;
+ for(i := 0; i < e; i++)
+ bout.puts(hex(int m[i], 2));
+ bout.puts("\"\n");
+ }
+}
+
+asmvar(size: int, d: ref Decl)
+{
+ bout.puts("\tvar\t@mp," + string size + "\n");
+
+ for(; d != nil; d = d.next)
+ if(d.store == Dglobal && d.init != nil)
+ asminitializer(d.offset, d.init);
+}
+
+asmldt(size: int, d: ref Decl)
+{
+ bout.puts("\tldts\t@ldt," + string size + "\n");
+
+ for(; d != nil; d = d.next)
+ if(d.store == Dglobal && d.init != nil)
+ asminitializer(d.offset, d.init);
+}
+
+asminitializer(offset: int, n: ref Node)
+{
+ wild: ref Node;
+ c: ref Case;
+ lab: Label;
+ id: ref Decl;
+ i, e: int;
+
+ case n.ty.kind{
+ Tbyte =>
+ bout.puts("\tbyte\t@mp+"+string offset+","+string(int n.c.val & 16rff)+"\n");
+ Tint or
+ Tfix =>
+ bout.puts("\tword\t@mp+"+string offset+","+string(int n.c.val)+"\n");
+ Tbig =>
+ bout.puts("\tlong\t@mp+"+string offset+","+string n.c.val+" # "+string bhex(n.c.val, 16)+"\n");
+ Tstring =>
+ asmstring(offset, n.decl.sym);
+ Treal =>
+ fs := "";
+ ba := array[8] of byte;
+ export_real(ba, array[] of {n.c.rval});
+ for(i = 0; i < 8; i++)
+ fs += hex(int ba[i], 2);
+ bout.puts("\treal\t@mp+"+string offset+","+string n.c.rval+" # "+fs+"\n");
+ Tadt or
+ Tadtpick or
+ Ttuple =>
+ id = n.ty.ids;
+ for(n = n.left; n != nil; n = n.right){
+ asminitializer(offset + id.offset, n.left);
+ id = id.next;
+ }
+ Tcase =>
+ c = n.ty.cse;
+ bout.puts("\tword\t@mp+"+string offset+","+string c.nlab);
+ for(i = 0; i < c.nlab; i++){
+ lab = c.labs[i];
+ bout.puts(","+string(int lab.start.c.val)+","+string(int lab.stop.c.val+1)+","+string(lab.inst.pc));
+ }
+ if(c.iwild != nil)
+ bout.puts(","+string c.iwild.pc+"\n");
+ else
+ bout.puts(",-1\n");
+ Tcasel =>
+ c = n.ty.cse;
+ bout.puts("\tword\t@mp+"+string offset+","+string c.nlab);
+ for(i = 0; i < c.nlab; i++){
+ lab = c.labs[i];
+ bout.puts(","+string(lab.start.c.val)+","+string(lab.stop.c.val+big 1)+","+string(lab.inst.pc));
+ }
+ if(c.iwild != nil)
+ bout.puts(","+string c.iwild.pc+"\n");
+ else
+ bout.puts(",-1\n");
+ Tcasec =>
+ c = n.ty.cse;
+ bout.puts("\tword\t@mp+"+string offset+","+string c.nlab+"\n");
+ offset += IBY2WD;
+ for(i = 0; i < c.nlab; i++){
+ lab = c.labs[i];
+ asmstring(offset, lab.start.decl.sym);
+ offset += IBY2WD;
+ if(lab.stop != lab.start)
+ asmstring(offset, lab.stop.decl.sym);
+ offset += IBY2WD;
+ bout.puts("\tword\t@mp+"+string offset+","+string lab.inst.pc+"\n");
+ offset += IBY2WD;
+ }
+ if(c.iwild != nil)
+ bout.puts("\tword\t@mp+"+string offset+","+string c.iwild.pc+"\n");
+ else
+ bout.puts("\tword\t@mp+"+string offset+",-1\n");
+ Tgoto =>
+ c = n.ty.cse;
+ bout.puts("\tword\t@mp+"+string offset);
+ bout.puts(","+string(n.ty.size/IBY2WD-1));
+ for(i = 0; i < c.nlab; i++)
+ bout.puts(","+string c.labs[i].inst.pc);
+ if(c.iwild != nil)
+ bout.puts(","+string c.iwild.pc);
+ bout.puts("\n");
+ Tany =>
+ break;
+ Tarray =>
+ bout.puts("\tarray\t@mp+"+string offset+",$"+string n.ty.tof.decl.desc.id+","+string int n.left.c.val+"\n");
+ if(n.right == nil)
+ break;
+ bout.puts("\tindir\t@mp+"+string offset+",0\n");
+ c = n.right.ty.cse;
+ wild = nil;
+ if(c.wild != nil)
+ wild = c.wild.right;
+ last := 0;
+ esz := n.ty.tof.size;
+ for(i = 0; i < c.nlab; i++){
+ e = int c.labs[i].start.c.val;
+ if(wild != nil){
+ for(; last < e; last++)
+ asminitializer(esz * last, wild);
+ }
+ last = e;
+ e = int c.labs[i].stop.c.val;
+ elem := c.labs[i].node.right;
+ for(; last <= e; last++)
+ asminitializer(esz * last, elem);
+ }
+ if(wild != nil)
+ for(e = int n.left.c.val; last < e; last++)
+ asminitializer(esz * last, wild);
+ bout.puts("\tapop\n");
+ Tiface =>
+ if(LDT)
+ bout.puts("\tword\t@ldt+"+string offset+","+string int n.c.val+"\n");
+ else
+ bout.puts("\tword\t@mp+"+string offset+","+string int n.c.val+"\n");
+ offset += IBY2WD;
+ for(id = n.decl.ty.ids; id != nil; id = id.next){
+ offset = align(offset, IBY2WD);
+ if(LDT)
+ bout.puts("\text\t@ldt+"+string offset+",0x"+string hex(sign(id), 0)+",\"");
+ else
+ bout.puts("\text\t@mp+"+string offset+",0x"+string hex(sign(id), 0)+",\"");
+ dotlen := 0;
+ idlen := len array of byte id.sym.name + 1;
+ if(id.dot.ty.kind == Tadt){
+ dotlen = len array of byte id.dot.sym.name + 1;
+ bout.puts(id.dot.sym.name+".");
+ }
+ bout.puts(id.sym.name+"\"\n");
+ offset += idlen + dotlen + IBY2WD;
+ }
+ * =>
+ fatal("can't asm global "+nodeconv(n));
+ }
+}
+
+asmexc(es: ref Except)
+{
+ e: ref Except;
+
+ n := 0;
+ for(e = es; e != nil; e = e.next)
+ n++;
+ bout.puts("\texceptions\t" + string n + "\n");
+ for(e = es; e != nil; e = e.next){
+ if(!int e.p1.reach && !int e.p2.reach)
+ continue;
+ c := e.c;
+ o := e.d.offset;
+ if(e.desc != nil)
+ id := e.desc.id;
+ else
+ id = -1;
+ bout.puts("\texception\t" + string getpc(e.p1) + ", " + string getpc(e.p2) + ", " + string o + ", " + string id + ", " + string c.nlab + ", " + string e.ne + "\n");
+ for(i := 0; i < c.nlab; i++){
+ lab := c.labs[i];
+ d := lab.start.decl;
+ if(lab.start.ty.kind == Texception)
+ d = d.init.decl;
+ bout.puts("\texctab\t\"" + d.sym.name + "\", " + string lab.inst.pc + "\n");
+ }
+ if(c.iwild == nil)
+ bout.puts("\texctab\t" + "*" + ", " + string -1 + "\n");
+ else
+ bout.puts("\texctab\t" + "*" + ", " + string c.iwild.pc + "\n");
+ }
+}
+
+asmstring(offset: int, sym: ref Sym)
+{
+ bout.puts("\tstring\t@mp+"+string offset+",\"");
+ s := sym.name;
+ for(i := 0; i < len s; i++){
+ c := s[i];
+ if(c == '\n')
+ bout.puts("\\n");
+ else if(c == '\u0000')
+ bout.puts("\\z");
+ else if(c == '"')
+ bout.puts("\\\"");
+ else if(c == '\\')
+ bout.puts("\\\\");
+ else
+ bout.putc(c);
+ }
+ bout.puts("\"\n");
+}
+
+asminst(in: ref Inst)
+{
+ for(; in != nil; in = in.next){
+ if(in.op == INOOP)
+ continue;
+ if(in.pc % 10 == 0){
+ bout.putc('#');
+ bout.puts(string in.pc);
+ bout.putc('\n');
+ }
+ bout.puts(instconv(in));
+ bout.putc('\n');
+ }
+}
diff --git a/appl/cmd/limbo/com.b b/appl/cmd/limbo/com.b
new file mode 100644
index 00000000..bc977d0a
--- /dev/null
+++ b/appl/cmd/limbo/com.b
@@ -0,0 +1,1387 @@
+# back end
+
+breaks: array of ref Inst;
+conts: array of ref Inst;
+labels: array of ref Decl;
+bcscps: array of ref Node;
+labdep: int;
+nocont: ref Inst;
+nlabel: int;
+
+scp: int;
+scps:= array[MaxScope] of ref Node;
+
+curfn: ref Decl;
+
+pushscp(n : ref Node)
+{
+ if (scp >= MaxScope)
+ fatal("scope too deep");
+ scps[scp++] = n;
+}
+
+popscp()
+{
+ scp--;
+}
+
+curscp() : ref Node
+{
+ if (scp == 0)
+ return nil;
+ return scps[scp-1];
+}
+
+zeroscopes(stop : ref Node)
+{
+ i : int;
+ cs : ref Node;
+
+ for (i = scp-1; i >= 0; i--) {
+ cs = scps[i];
+ if (cs == stop)
+ break;
+ zcom(cs.left, nil);
+ }
+}
+
+zeroallscopes(n: ref Node, nn: array of ref Node)
+{
+ if(n == nil)
+ return;
+ for(; n != nil; n = n.right){
+ case(n.op){
+ Oscope =>
+ zeroallscopes(n.right, nn);
+ zcom(n.left, nn);
+ return;
+ Olabel or
+ Odo =>
+ zeroallscopes(n.right, nn);
+ return;
+ Oif or
+ Ofor =>
+ zeroallscopes(n.right.left, nn);
+ zeroallscopes(n.right.right, nn);
+ return;
+ Oalt or
+ Ocase or
+ Opick or
+ Oexcept =>
+ for(n = n.right; n != nil; n = n.right)
+ zeroallscopes(n.left.right, nn);
+ return;
+ Oseq =>
+ zeroallscopes(n.left, nn);
+ break;
+ Oexstmt =>
+ zeroallscopes(n.left, nn);
+ zeroallscopes(n.right, nn);
+ return;
+ * =>
+ return;
+ }
+ }
+}
+
+excs: ref Except;
+
+installexc(en: ref Node, p1: ref Inst, p2: ref Inst, zn: ref Node)
+{
+ e := ref Except;
+ e.p1 = p1;
+ e.p2 = p2;
+ e.c = en.ty.cse;
+ e.d = en.left.decl;
+ e.zn = zn;
+ e.next = excs;
+ excs = e;
+
+ ne := 0;
+ c := e.c;
+ for(i := 0; i < c.nlab; i++){
+ lab := c.labs[i];
+ if(lab.start.ty.kind == Texception)
+ ne++;
+ }
+ e.ne = ne;
+}
+
+inlist(d: ref Decl, dd: ref Decl): int
+{
+ for( ; dd != nil; dd = dd.next)
+ if(d == dd)
+ return 1;
+ return 0;
+}
+
+excdesc()
+{
+ dd, nd: ref Decl;
+
+ for(e := excs; e != nil; e = e.next){
+ if(e.zn != nil){
+ dd = nil;
+ maxo := 0;
+ for(n := e.zn ; n != nil; n = n.right){
+ d := n.decl;
+ d.locals = d.next;
+ if(!inlist(d, dd)){
+ d.next = dd;
+ dd = d;
+ o := d.offset+d.ty.size;
+ if(o > maxo)
+ maxo = o;
+ }
+ }
+ e.desc = gendesc(e.d, align(maxo, MaxAlign), dd);
+ for(d := dd; d != nil; d = nd){
+ nd = d.next;
+ d.next = d.locals;
+ d.locals = nil;
+ }
+ e.zn = nil;
+ }
+ }
+}
+
+reve(e: ref Except): ref Except
+{
+ l, n: ref Except;
+
+ l = nil;
+ for( ; e != nil; e = n){
+ n = e.next;
+ e.next = l;
+ l = e;
+ }
+ return l;
+}
+
+ckinline0(n: ref Node, d: ref Decl): int
+{
+ dd: ref Decl;
+
+ if(n == nil)
+ return 1;
+ if(n.op == Oname){
+ dd = n.decl;
+ if(d == dd)
+ return 0;
+ if(int dd.inline == 1)
+ return ckinline0(dd.init.right, d);
+ return 1;
+ }
+ return ckinline0(n.left, d) && ckinline0(n.right, d);
+}
+
+ckinline(d: ref Decl)
+{
+ d.inline = byte ckinline0(d.init.right, d);
+}
+
+modcom(entry: ref Decl)
+{
+ d, m: ref Decl;
+
+ if(errors)
+ return;
+
+ if(emitcode != "" || emitstub || emittab != "" || emitsbl != ""){
+ emit(curscope());
+ popscope();
+ return;
+ }
+
+ #
+ # scom introduces global variables for case statements
+ # and unaddressable constants, so it must be done before
+ # popping the global scope
+ #
+ gent = sys->millisec();
+ nlabel = 0;
+ maxstack = MaxTemp;
+ nocont = ref Inst;
+ genstart();
+
+ for(i := 0; i < nfns; i++)
+ if(int fns[i].inline == 1)
+ ckinline(fns[i]);
+
+ ok := 0;
+ for(i = 0; i < nfns; i++){
+ d = fns[i];
+ if(d.refs > 1 && !(int d.inline == 1 && local(d) && d.iface == nil)){
+ fns[ok++] = d;
+ fncom(d);
+ }
+ }
+ fns = fns[:ok];
+ nfns = ok;
+ if(blocks != -1)
+ fatal("blocks not nested correctly");
+ firstinst = firstinst.next;
+ if(errors)
+ return;
+
+ globals := popscope();
+ checkrefs(globals);
+ if(errors)
+ return;
+ globals = vars(globals);
+ moddataref();
+
+ nils := popscope();
+ m = nil;
+ for(d = nils; d != nil; d = d.next){
+ if(debug['n'])
+ print("nil '%s' ref %d\n", d.sym.name, d.refs);
+ if(d.refs && m == nil)
+ m = dupdecl(d);
+ d.offset = 0;
+ }
+ globals = appdecls(m, globals);
+ globals = namesort(globals);
+ globals = modglobals(impdecls.d, globals);
+ vcom(globals);
+ narrowmods();
+ ldts: ref Decl;
+ if(LDT)
+ (globals, ldts) = resolveldts(globals);
+ offset := idoffsets(globals, 0, IBY2WD);
+ if(LDT)
+ ldtoff := idindices(ldts); # idoffsets(ldts, 0, IBY2WD);
+ for(d = nils; d != nil; d = d.next){
+ if(debug['n'])
+ print("nil '%s' ref %d\n", d.sym.name, d.refs);
+ if(d.refs)
+ d.offset = m.offset;
+ }
+
+ if(debug['g']){
+ print("globals:\n");
+ printdecls(globals);
+ }
+
+ ndata := 0;
+ for(d = globals; d != nil; d = d.next)
+ ndata++;
+ ndesc := resolvedesc(impdecls.d, offset, globals);
+ ninst := resolvepcs(firstinst);
+ modresolve();
+ if(impdecls.next != nil)
+ for(dl := impdecls; dl != nil; dl = dl.next)
+ resolvemod(dl.d);
+ nlink := resolvemod(impdecl);
+ gent = sys->millisec() - gent;
+
+ maxstack *= 10;
+ if(fixss != 0)
+ maxstack = fixss;
+
+ if(debug['s'])
+ print("%d instructions\n%d data elements\n%d type descriptors\n%d functions exported\n%d stack size\n",
+ ninst, ndata, ndesc, nlink, maxstack);
+
+ excs = reve(excs);
+
+ writet = sys->millisec();
+ if(gendis){
+ discon(XMAGIC);
+ hints := 0;
+ if(mustcompile)
+ hints |= MUSTCOMPILE;
+ if(dontcompile)
+ hints |= DONTCOMPILE;
+ if(LDT)
+ hints |= HASLDT;
+ if(excs != nil)
+ hints |= HASEXCEPT;
+ discon(hints); # runtime hints
+ discon(maxstack); # minimum stack extent size
+ discon(ninst);
+ discon(offset);
+ discon(ndesc);
+ discon(nlink);
+ disentry(entry);
+ disinst(firstinst);
+ disdesc(descriptors);
+ disvar(offset, globals);
+ dismod(impdecl);
+ if(LDT)
+ disldt(ldtoff, ldts);
+ if(excs != nil)
+ disexc(excs);
+ dispath();
+ }else{
+ asminst(firstinst);
+ asmentry(entry);
+ asmdesc(descriptors);
+ asmvar(offset, globals);
+ asmmod(impdecl);
+ if(LDT)
+ asmldt(ldtoff, ldts);
+ if(excs != nil)
+ asmexc(excs);
+ asmpath();
+ }
+ writet = sys->millisec() - writet;
+
+ symt = sys->millisec();
+ if(bsym != nil){
+ sblmod(impdecl);
+
+ sblfiles();
+ sblinst(firstinst, ninst);
+ sblty(adts, nadts);
+ sblfn(fns, nfns);
+ sblvar(globals);
+ }
+ symt = sys->millisec() - symt;
+
+ firstinst = nil;
+ lastinst = nil;
+
+ excs = nil;
+}
+
+fncom(decl: ref Decl)
+{
+ curfn = decl;
+ if(ispoly(decl))
+ addfnptrs(decl, 1);
+
+ #
+ # pick up the function body and compile it
+ # this code tries to clean up the parse nodes as fast as possible
+ # function is Ofunc(name, body)
+ #
+ decl.pc = nextinst();
+ tinit();
+ labdep = 0;
+ scp = 0;
+ breaks = array[maxlabdep] of ref Inst;
+ conts = array[maxlabdep] of ref Inst;
+ labels = array[maxlabdep] of ref Decl;
+ bcscps = array[maxlabdep] of ref Node;
+
+ n := decl.init;
+ if(int decl.inline == 1)
+ decl.init = dupn(0, nosrc, n);
+ else
+ decl.init = n.left;
+ src := n.right.src;
+ src.start = src.stop - 1;
+ for(n = n.right; n != nil; n = n.right){
+ if(n.op != Oseq){
+ if(n.op == Ocall && trcom(n, nil, 1))
+ break;
+ scom(n);
+ break;
+ }
+ if(n.left.op == Ocall && trcom(n.left, n.right, 1)){
+ n = n.right;
+ if(n == nil || n.op != Oseq)
+ break;
+ }
+ else
+ scom(n.left);
+ }
+ pushblock();
+ in := genrawop(src, IRET, nil, nil, nil);
+ popblock();
+ reach(decl.pc);
+ if(in.reach != byte 0 && decl.ty.tof != tnone)
+ error(src.start, "no return at end of function " + dotconv(decl));
+ # decl.endpc = lastinst;
+ if(labdep != 0)
+ fatal("unbalanced label stack");
+ breaks = nil;
+ conts = nil;
+ labels = nil;
+ bcscps = nil;
+
+ loc := declsort(appdecls(vars(decl.locals), tdecls()));
+
+ decl.offset = idoffsets(loc, decl.offset, MaxAlign);
+ for(last := decl.ty.ids; last != nil && last.next != nil; last = last.next)
+ ;
+ if(last != nil)
+ last.next = loc;
+ else
+ decl.ty.ids = loc;
+
+ if(debug['f']){
+ print("fn: %s\n", decl.sym.name);
+ printdecls(decl.ty.ids);
+ }
+
+ decl.desc = gendesc(decl, decl.offset, decl.ty.ids);
+ decl.locals = loc;
+ excdesc();
+ if(decl.offset > maxstack)
+ maxstack = decl.offset;
+ if(optims)
+ optim(decl.pc, decl);
+ if(last != nil)
+ last.next = nil;
+ else
+ decl.ty.ids = nil;
+}
+
+#
+# statement compiler
+#
+scom(n: ref Node)
+{
+ b: int;
+ p, pp: ref Inst;
+ left: ref Node;
+
+ for(; n != nil; n = n.right){
+ case n.op{
+ Ocondecl or
+ Otypedecl or
+ Ovardecl or
+ Oimport or
+ Oexdecl =>
+ return;
+ Ovardecli =>
+ break;
+ Oscope =>
+ pushscp(n);
+ scom(n.right);
+ popscp();
+ zcom(n.left, nil);
+ return;
+ Olabel =>
+ scom(n.right);
+ return;
+ Oif =>
+ pushblock();
+ left = simplify(n.left);
+ if(left.op == Oconst && left.ty == tint){
+ if(left.c.val != big 0)
+ scom(n.right.left);
+ else
+ scom(n.right.right);
+ popblock();
+ return;
+ }
+ sumark(left);
+ pushblock();
+ p = bcom(left, 1, nil);
+ tfreenow();
+ popblock();
+ scom(n.right.left);
+ if(n.right.right != nil){
+ pp = p;
+ p = genrawop(lastinst.src, IJMP, nil, nil, nil);
+ patch(pp, nextinst());
+ scom(n.right.right);
+ }
+ patch(p, nextinst());
+ popblock();
+ return;
+ Ofor =>
+ n.left = left = simplify(n.left);
+ if(left.op == Oconst && left.ty == tint){
+ if(left.c.val == big 0)
+ return;
+ left.op = Onothing;
+ left.ty = tnone;
+ left.decl = nil;
+ }
+ pp = nextinst();
+ b = pushblock();
+ sumark(left);
+ p = bcom(left, 1, nil);
+ tfreenow();
+ popblock();
+
+ if(labdep >= maxlabdep)
+ fatal("label stack overflow");
+ breaks[labdep] = nil;
+ conts[labdep] = nil;
+ labels[labdep] = n.decl;
+ bcscps[labdep] = curscp();
+ labdep++;
+ scom(n.right.left);
+ labdep--;
+
+ patch(conts[labdep], nextinst());
+ if(n.right.right != nil){
+ pushblock();
+ scom(n.right.right);
+ popblock();
+ }
+ repushblock(lastinst.block); # was b
+ patch(genrawop(lastinst.src, IJMP, nil, nil, nil), pp); # for cprof: was left.src
+ popblock();
+ patch(p, nextinst());
+ patch(breaks[labdep], nextinst());
+ return;
+ Odo =>
+ pp = nextinst();
+
+ if(labdep >= maxlabdep)
+ fatal("label stack overflow");
+ breaks[labdep] = nil;
+ conts[labdep] = nil;
+ labels[labdep] = n.decl;
+ bcscps[labdep] = curscp();
+ labdep++;
+ scom(n.right);
+ labdep--;
+
+ patch(conts[labdep], nextinst());
+
+ left = simplify(n.left);
+ if(left.op == Onothing
+ || left.op == Oconst && left.ty == tint){
+ if(left.op == Onothing || left.c.val != big 0){
+ pushblock();
+ p = genrawop(left.src, IJMP, nil, nil, nil);
+ popblock();
+ }else
+ p = nil;
+ }else{
+ pushblock();
+ p = bcom(sumark(left), 0, nil);
+ tfreenow();
+ popblock();
+ }
+ patch(p, pp);
+ patch(breaks[labdep], nextinst());
+ return;
+ Ocase or
+ Opick or
+ Oalt or
+ Oexcept =>
+ pushblock();
+ if(labdep >= maxlabdep)
+ fatal("label stack overflow");
+ breaks[labdep] = nil;
+ conts[labdep] = nocont;
+ labels[labdep] = n.decl;
+ bcscps[labdep] = curscp();
+ labdep++;
+ case n.op{
+ Oalt =>
+ altcom(n);
+ Ocase or
+ Opick =>
+ casecom(n);
+ Oexcept =>
+ excom(n);
+ }
+ labdep--;
+ patch(breaks[labdep], nextinst());
+ popblock();
+ return;
+ Obreak =>
+ pushblock();
+ bccom(n, breaks);
+ popblock();
+ Ocont =>
+ pushblock();
+ bccom(n, conts);
+ popblock();
+ Oseq =>
+ if(n.left.op == Ocall && trcom(n.left, n.right, 0)){
+ n = n.right;
+ if(n == nil || n.op != Oseq)
+ return;
+ }
+ else
+ scom(n.left);
+ Oret =>
+ if(n.left != nil && n.left.op == Ocall && trcom(n.left, nil, 1))
+ return;
+ pushblock();
+ if(n.left != nil){
+ n.left = simplify(n.left);
+ sumark(n.left);
+ ecom(n.left.src, retalloc(ref Node, n.left), n.left);
+ tfreenow();
+ }
+ genrawop(n.src, IRET, nil, nil, nil);
+ popblock();
+ return;
+ Oexit =>
+ pushblock();
+ genrawop(n.src, IEXIT, nil, nil, nil);
+ popblock();
+ return;
+ Onothing =>
+ return;
+ Ofunc =>
+ fatal("Ofunc");
+ return;
+ Oexstmt =>
+ pushblock();
+ pp = genrawop(n.right.src, IEXC0, nil, nil, nil); # marker
+ p1 := nextinst();
+ scom(n.left);
+ p2 := nextinst();
+ p3 := genrawop(n.right.src, IJMP, nil, nil, nil);
+ p = genrawop(n.right.src, IEXC, nil, nil, nil); # marker
+ p.d.decl = mkdecl(n.src, 0, n.right.ty);
+ zn := array[1] of ref Node;
+ zeroallscopes(n.left, zn);
+ scom(n.right);
+ patch(p3, nextinst());
+ installexc(n.right, p1, p2, zn[0]);
+ patch(pp, p);
+ popblock();
+ return;
+ * =>
+ pushblock();
+ n = simplify(n);
+ sumark(n);
+ ecom(n.src, nil, n);
+ tfreenow();
+ popblock();
+ return;
+ }
+ }
+}
+
+#
+# compile a break, continue
+#
+bccom(n: ref Node, bs: array of ref Inst)
+{
+ s: ref Sym;
+
+ s = nil;
+ if(n.decl != nil)
+ s = n.decl.sym;
+ ok := -1;
+ for(i := 0; i < labdep; i++){
+ if(bs[i] == nocont)
+ continue;
+ if(s == nil || labels[i] != nil && labels[i].sym == s)
+ ok = i;
+ }
+ if(ok < 0)
+ fatal("didn't find break or continue");
+ zeroscopes(bcscps[ok]);
+ p := genrawop(n.src, IJMP, nil, nil, nil);
+ p.branch = bs[ok];
+ bs[ok] = p;
+}
+
+dogoto(c: ref Case): int
+{
+ i, j, k, n, r, q, v: int;
+ l, nl: array of Label;
+ src: Src;
+
+ l = c.labs;
+ n = c.nlab;
+ if(n == 0)
+ return 0;
+ r = int l[n-1].stop.c.val - int l[0].start.c.val+1;
+ if(r >= 3 && r <= 3*n){
+ if(r != n){
+ # remove ranges, fill in gaps
+ c.nlab = r;
+ nl = c.labs = array[r] of Label;
+ k = 0;
+ v = int l[0].start.c.val-1;
+ for(i = 0; i < n; i++){
+ # p = int l[i].start.c.val;
+ q = int l[i].stop.c.val;
+ src = l[i].start.src;
+ for(j = v+1; j <= q; j++){
+ nl[k] = l[i];
+ nl[k].start = nl[k].stop = mkconst(src, big j);
+ k++;
+ }
+ v = q;
+ }
+ if(k != r)
+ fatal("bad case expansion");
+ }
+ l = c.labs;
+ for(i = 0; i < r; i++)
+ l[i].inst = nil;
+ return 1;
+ }
+ return 0;
+}
+
+fillrange(c: ref Case, nn: ref Node, in: ref Inst)
+{
+ i, j, n, p, q: int;
+ l: array of Label;
+
+ l = c.labs;
+ n = c.nlab;
+ p = int nn.left.c.val;
+ q = int nn.right.c.val;
+ for(i = 0; i < n; i++)
+ if(int l[i].start.c.val == p)
+ break;
+ if(i == n)
+ fatal("fillrange fails");
+ for(j = p; j <= q; j++)
+ l[i++].inst = in;
+}
+
+casecom(cn: ref Node)
+{
+ d: ref Decl;
+ left, p, tmp, tmpc: ref Node;
+ jmps, wild, j1, j2: ref Inst;
+
+ c := cn.ty.cse;
+
+ needwild := cn.op != Opick || c.nlab != cn.left.right.ty.tof.decl.tag;
+ igoto := cn.left.ty == tint && dogoto(c);
+
+ #
+ # generate global which has case labels
+ #
+ if(igoto){
+ d = mkids(cn.src, enter(".g"+string nlabel++, 0), cn.ty, nil);
+ cn.ty.kind = Tgoto;
+ }
+ else
+ d = mkids(cn.src, enter(".c"+string nlabel++, 0), cn.ty, nil);
+ d.init = mkdeclname(cn.src, d);
+ nto := ref znode;
+ nto.addable = Rmreg;
+ nto.left = nil;
+ nto.right = nil;
+ nto.op = Oname;
+ nto.ty = d.ty;
+ nto.decl = d;
+
+ tmp = nil;
+ left = cn.left;
+ left = simplify(left);
+ cn.left = left;
+ sumark(left);
+ if(debug['c'])
+ print("case %s\n", nodeconv(left));
+ ctype := cn.left.ty;
+ if(left.addable >= Rcant){
+ if(cn.op == Opick){
+ ecom(left.src, nil, left);
+ tfreenow();
+ left = mkunary(Oind, dupn(1, left.src, left.left));
+ left.ty = tint;
+ sumark(left);
+ ctype = tint;
+ }else{
+ (left, tmp) = eacom(left, nil);
+ tfreenow();
+ }
+ }
+
+ labs := c.labs;
+ nlab := c.nlab;
+
+ if(igoto){
+ if(labs[0].start.c.val != big 0){
+ tmpc = talloc(left.ty, nil);
+ if(left.addable == Radr || left.addable == Rmadr){
+ genrawop(left.src, IMOVW, left, nil, tmpc);
+ left = tmpc;
+ }
+ genrawop(left.src, ISUBW, sumark(labs[0].start), left, tmpc);
+ left = tmpc;
+ }
+ if(needwild){
+ j1 = genrawop(left.src, IBLTW, left, sumark(mkconst(left.src, big 0)), nil);
+ j2 = genrawop(left.src, IBGTW, left, sumark(mkconst(left.src, labs[nlab-1].start.c.val-labs[0].start.c.val)), nil);
+ }
+ j := nextinst();
+ genrawop(left.src, IGOTO, left, nil, nto);
+ j.d.reg = IBY2WD;
+ }
+ else{
+ op := ICASE;
+ if(ctype == tbig)
+ op = ICASEL;
+ else if(ctype == tstring)
+ op = ICASEC;
+ genrawop(left.src, op, left, nil, nto);
+ }
+ tfree(tmp);
+ tfree(tmpc);
+
+ jmps = nil;
+ wild = nil;
+ for(n := cn.right; n != nil; n = n.right){
+ j := nextinst();
+ for(p = n.left.left; p != nil; p = p.right){
+ if(debug['c'])
+ print("case qualifier %s\n", nodeconv(p.left));
+ case p.left.op{
+ Oconst =>
+ labs[findlab(ctype, p.left, labs, nlab)].inst = j;
+ Orange =>
+ labs[findlab(ctype, p.left.left, labs, nlab)].inst = j;
+ if(igoto)
+ fillrange(c, p.left, j);
+ Owild =>
+ if(needwild)
+ wild = j;
+ # else
+ # nwarn(p.left, "default case redundant");
+ }
+ }
+
+ if(debug['c'])
+ print("case body for %s: %s\n", expconv(n.left.left), nodeconv(n.left.right));
+
+ k := nextinst();
+ scom(n.left.right);
+
+ src := lastinst.src;
+ # if(n.left.right == nil || n.left.right.op == Onothing)
+ if(k == nextinst())
+ src = n.left.left.src;
+ j = genrawop(src, IJMP, nil, nil, nil);
+ j.branch = jmps;
+ jmps = j;
+ }
+ patch(jmps, nextinst());
+ if(wild == nil && needwild)
+ wild = nextinst();
+
+ if(igoto){
+ if(needwild){
+ patch(j1, wild);
+ patch(j2, wild);
+ }
+ for(i := 0; i < nlab; i++)
+ if(labs[i].inst == nil)
+ labs[i].inst = wild;
+ }
+
+ c.iwild = wild;
+
+ d.ty.cse = c;
+ usetype(d.ty);
+ installids(Dglobal, d);
+}
+
+altcom(nalt: ref Node)
+{
+ p, op, left: ref Node;
+ jmps, wild, j: ref Inst = nil;
+
+ talt := nalt.ty;
+ c := talt.cse;
+ nlab := c.nlab;
+ nsnd := c.nsnd;
+ comm := array[nlab] of ref Node;
+ labs := array[nlab] of Label;
+ tmps := array[nlab] of ref Node;
+ c.labs = labs;
+
+ #
+ # built the type of the alt channel table
+ # note that we lie to the garbage collector
+ # if we know that another reference exists for the channel
+ #
+ is := 0;
+ ir := nsnd;
+ i := 0;
+ for(n := nalt.left; n != nil; n = n.right){
+ for(p = n.left.right.left; p != nil; p = p.right){
+ left = simplify(p.left);
+ p.left = left;
+ if(left.op == Owild)
+ continue;
+ comm[i] = hascomm(left);
+ left = comm[i].left;
+ sumark(left);
+ isptr := left.addable >= Rcant;
+ if(comm[i].op == Osnd)
+ labs[is++].isptr = isptr;
+ else
+ labs[ir++].isptr = isptr;
+ i++;
+ }
+ }
+
+ which := talloc(tint, nil);
+ tab := talloc(talt, nil);
+
+ #
+ # build the node for the address of each channel,
+ # the values to send, and the storage fro values received
+ #
+ off := ref znode;
+ adr := ref znode;
+ add := ref znode;
+ slot := ref znode;
+ off.op = Oconst;
+ off.c = ref Const(big 0, 0.0); # jrf - added initialization
+ off.ty = tint;
+ off.addable = Rconst;
+ adr.op = Oadr;
+ adr.left = tab;
+ adr.ty = tint;
+ add.op = Oadd;
+ add.left = adr;
+ add.right = off;
+ add.ty = tint;
+ slot.op = Oind;
+ slot.left = add;
+ sumark(slot);
+
+ #
+ # compile the sending and receiving channels and values
+ #
+ is = 2*IBY2WD;
+ ir = is + nsnd*2*IBY2WD;
+ i = 0;
+ for(n = nalt.left; n != nil; n = n.right){
+ for(p = n.left.right.left; p != nil; p = p.right){
+ if(p.left.op == Owild)
+ continue;
+
+ #
+ # gen channel
+ #
+ op = comm[i];
+ if(op.op == Osnd){
+ off.c.val = big is;
+ is += 2*IBY2WD;
+ }else{
+ off.c.val = big ir;
+ ir += 2*IBY2WD;
+ }
+ left = op.left;
+
+ #
+ # this sleaze is lying to the garbage collector
+ #
+ if(left.addable < Rcant)
+ genmove(left.src, Mas, tint, left, slot);
+ else{
+ slot.ty = left.ty;
+ ecom(left.src, slot, left);
+ tfreenow();
+ slot.ty = nil;
+ }
+
+ #
+ # gen value
+ #
+ off.c.val += big IBY2WD;
+ (p.left, tmps[i]) = rewritecomm(p.left, comm[i], slot);
+
+ i++;
+ }
+ }
+
+ #
+ # stuff the number of send & receive channels into the table
+ #
+ altsrc := nalt.src;
+ altsrc.stop = (altsrc.stop & ~PosMask) | ((altsrc.stop + 3) & PosMask);
+ off.c.val = big 0;
+ genmove(altsrc, Mas, tint, sumark(mkconst(altsrc, big nsnd)), slot);
+ off.c.val += big IBY2WD;
+ genmove(altsrc, Mas, tint, sumark(mkconst(altsrc, big(nlab-nsnd))), slot);
+ off.c.val += big IBY2WD;
+
+ altop := IALT;
+ if(c.wild != nil)
+ altop = INBALT;
+ pp := genrawop(altsrc, altop, tab, nil, which);
+ pp.m.offset = talt.size; # for optimizer
+
+ d := mkids(nalt.src, enter(".g"+string nlabel++, 0), mktype(nalt.src.start, nalt.src.stop, Tgoto, nil, nil), nil);
+ d.ty.cse = c;
+ d.init = mkdeclname(nalt.src, d);
+
+ nto := ref znode;
+ nto.addable = Rmreg;
+ nto.left = nil;
+ nto.right = nil;
+ nto.op = Oname;
+ nto.decl = d;
+ nto.ty = d.ty;
+
+ me := genrawop(altsrc, IGOTO, which, nil, nto);
+ me.d.reg = IBY2WD; # skip the number of cases field
+ tfree(tab);
+ tfree(which);
+
+ #
+ # compile the guard expressions and bodies
+ #
+ i = 0;
+ is = 0;
+ ir = nsnd;
+ jmps = nil;
+ wild = nil;
+ for(n = nalt.left; n != nil; n = n.right){
+ j = nil;
+ for(p = n.left.right.left; p != nil; p = p.right){
+ tj := nextinst();
+ if(p.left.op == Owild){
+ wild = nextinst();
+ }else{
+ if(comm[i].op == Osnd)
+ labs[is++].inst = tj;
+ else{
+ labs[ir++].inst = tj;
+ tacquire(tmps[i]);
+ }
+ sumark(p.left);
+ if(debug['a'])
+ print("alt guard %s\n", nodeconv(p.left));
+ ecom(p.left.src, nil, p.left);
+ tfree(tmps[i]);
+ tfreenow();
+ i++;
+ }
+ if(p.right != nil){
+ tj = genrawop(lastinst.src, IJMP, nil, nil, nil);
+ tj.branch = j;
+ j = tj;
+ }
+ }
+
+ patch(j, nextinst());
+ if(debug['a'])
+ print("alt body %s\n", nodeconv(n.left.right));
+ scom(n.left);
+
+ j = genrawop(lastinst.src, IJMP, nil, nil, nil);
+ j.branch = jmps;
+ jmps = j;
+ }
+ patch(jmps, nextinst());
+ comm = nil;
+
+ c.iwild = wild;
+
+ usetype(d.ty);
+ installids(Dglobal, d);
+}
+
+excom(en: ref Node)
+{
+ ed: ref Decl;
+ p: ref Node;
+ jmps, wild: ref Inst;
+
+ ed = en.left.decl;
+ ed.ty = rtexception;
+ c := en.ty.cse;
+ labs := c.labs;
+ nlab := c.nlab;
+ jmps = nil;
+ wild = nil;
+ for(n := en.right; n != nil; n = n.right){
+ qt: ref Type = nil;
+ j := nextinst();
+ for(p = n.left.left; p != nil; p = p.right){
+ case p.left.op{
+ Oconst =>
+ labs[findlab(texception, p.left, labs, nlab)].inst = j;
+ Owild =>
+ wild = j;
+ }
+ if(qt == nil)
+ qt = p.left.ty;
+ else if(!tequal(qt, p.left.ty))
+ qt = texception;
+ }
+ if(qt != nil)
+ ed.ty = qt;
+ k := nextinst();
+ scom(n.left.right);
+ src := lastinst.src;
+ if(k == nextinst())
+ src = n.left.left.src;
+ j = genrawop(src, IJMP, nil, nil, nil);
+ j.branch = jmps;
+ jmps = j;
+ }
+ ed.ty = rtexception;
+ patch(jmps, nextinst());
+ c.iwild = wild;
+}
+
+#
+# rewrite the communication operand
+# allocate any temps needed for holding value to send or receive
+#
+rewritecomm(n, comm, slot: ref Node): (ref Node, ref Node)
+{
+ adr, tmp: ref Node;
+
+ if(n == nil)
+ return (nil, nil);
+ adr = nil;
+ if(n == comm){
+ if(comm.op == Osnd && sumark(n.right).addable < Rcant)
+ adr = n.right;
+ else{
+ adr = tmp = talloc(n.ty, nil);
+ tmp.src = n.src;
+ if(comm.op == Osnd){
+ ecom(n.right.src, tmp, n.right);
+ tfreenow();
+ }
+ else
+ trelease(tmp);
+ }
+ }
+ if(n.right == comm && n.op == Oas && comm.op == Orcv
+ && sumark(n.left).addable < Rcant)
+ adr = n.left;
+ if(adr != nil){
+ p := genrawop(comm.left.src, ILEA, adr, nil, slot);
+ p.m.offset = adr.ty.size; # for optimizer
+ if(comm.op == Osnd)
+ p.m.reg = 1; # for optimizer
+ return (adr, tmp);
+ }
+ (n.left, tmp) = rewritecomm(n.left, comm, slot);
+ if(tmp == nil)
+ (n.right, tmp) = rewritecomm(n.right, comm, slot);
+ return (n, tmp);
+}
+
+#
+# merge together two sorted lists, yielding a sorted list
+#
+declmerge(e, f: ref Decl): ref Decl
+{
+ d := rock := ref Decl;
+ while(e != nil && f != nil){
+ fs := f.ty.size;
+ es := e.ty.size;
+ # v := 0;
+ v := (e.link == nil) - (f.link == nil);
+ if(v == 0 && (es <= IBY2WD || fs <= IBY2WD))
+ v = fs - es;
+ if(v == 0)
+ v = e.refs - f.refs;
+ if(v == 0)
+ v = fs - es;
+ if(v == 0 && e.sym.name > f.sym.name)
+ v = -1;
+ if(v >= 0){
+ d.next = e;
+ d = e;
+ e = e.next;
+ while(e != nil && e.nid == byte 0){
+ d = e;
+ e = e.next;
+ }
+ }else{
+ d.next = f;
+ d = f;
+ f = f.next;
+ while(f != nil && f.nid == byte 0){
+ d = f;
+ f = f.next;
+ }
+ }
+ # d = d.next;
+ }
+ if(e != nil)
+ d.next = e;
+ else
+ d.next = f;
+ return rock.next;
+}
+
+#
+# recursively split lists and remerge them after they are sorted
+#
+recdeclsort(d: ref Decl, n: int): ref Decl
+{
+ if(n <= 1)
+ return d;
+ m := n / 2 - 1;
+ dd := d;
+ for(i := 0; i < m; i++){
+ dd = dd.next;
+ while(dd.nid == byte 0)
+ dd = dd.next;
+ }
+ r := dd.next;
+ while(r.nid == byte 0){
+ dd = r;
+ r = r.next;
+ }
+ dd.next = nil;
+ return declmerge(recdeclsort(d, n / 2),
+ recdeclsort(r, (n + 1) / 2));
+}
+
+#
+# sort the ids by size and number of references
+#
+declsort(d: ref Decl): ref Decl
+{
+ n := 0;
+ for(dd := d; dd != nil; dd = dd.next)
+ if(dd.nid > byte 0)
+ n++;
+ return recdeclsort(d, n);
+}
+
+nilsrc : Src;
+
+zcom1(n : ref Node, nn: array of ref Node)
+{
+ ty : ref Type;
+ d : ref Decl;
+ e : ref Node;
+
+ ty = n.ty;
+ if (!tmustzero(ty))
+ return;
+ if (n.op == Oname && n.decl.refs == 0)
+ return;
+ if (nn != nil) {
+ if(n.op != Oname)
+ error(n.src.start, "fatal: bad op in zcom1 map");
+ n.right = nn[0];
+ nn[0] = n;
+ return;
+ }
+ if (ty.kind == Tadtpick)
+ ty = ty.tof;
+ if (ty.kind == Ttuple || ty.kind == Tadt) {
+ for (d = ty.ids; d != nil; d = d.next) {
+ if (tmustzero(d.ty)) {
+ dn := n;
+ if (d.next != nil)
+ dn = dupn(0, nilsrc, n);
+ e = mkbin(Odot, dn, mkname(nilsrc, d.sym));
+ e.right.decl = d;
+ e.ty = e.right.ty = d.ty;
+ zcom1(e, nn);
+ }
+ }
+ }
+ else {
+ src := n.src;
+ n.src = nilsrc;
+ e = mkbin(Oas, n, mknil(nilsrc));
+ e.ty = e.right.ty = ty;
+ if (debug['Z'])
+ print("ecom %s\n", nodeconv(e));
+ pushblock();
+ e = simplify(e);
+ sumark(e);
+ ecom(e.src, nil, e);
+ popblock();
+ n.src = src;
+ e = nil;
+ }
+}
+
+zcom0(id : ref Decl, nn: array of ref Node)
+{
+ e := mkname(nilsrc, id.sym);
+ e.decl = id;
+ e.ty = id.ty;
+ zcom1(e, nn);
+}
+
+zcom(n : ref Node, nn: array of ref Node)
+{
+ r : ref Node;
+
+ for ( ; n != nil; n = r) {
+ r = n.right;
+ n.right = nil;
+ case (n.op) {
+ Ovardecl =>
+ last := n.left.decl;
+ for (ids := n.decl; ids != last.next; ids = ids.next)
+ zcom0(ids, nn);
+ break;
+ Oname =>
+ if (n.decl != nildecl)
+ zcom1(dupn(0, nilsrc, n), nn);
+ break;
+ Otuple =>
+ for (nt := n.left; nt != nil; nt = nt.right)
+ zcom(nt.left, nn);
+ break;
+ * =>
+ fatal("bad node in zcom()");
+ break;
+ }
+ n.right = r;
+ }
+}
+
+ret(n: ref Node, nilret: int): int
+{
+ if(n == nil)
+ return nilret;
+ if(n.op == Oseq)
+ n = n.left;
+ return n.op == Oret && n.left == nil;
+}
+
+trcom(e: ref Node, ne: ref Node, nilret: int): int
+{
+ d, id: ref Decl;
+ as, a, f, n: ref Node;
+ p: ref Inst;
+
+return 0; # TBS
+ if(e.op != Ocall || e.left.op != Oname)
+ return 0;
+ d = e.left.decl;
+ if(d != curfn || int d.handler || ispoly(d))
+ return 0;
+ if(!ret(ne, nilret))
+ return 0;
+ pushblock();
+ id = d.ty.ids;
+ # evaluate args in same order as normal calls
+ for(as = e.right; as != nil; as = as.right){
+ a = as.left;
+ if(!(a.op == Oname && id == a.decl)){
+ if(occurs(id, as.right)){
+ f = talloc(id.ty, nil);
+ f.flags |= byte TEMP;
+ }
+ else
+ f = mkdeclname(as.src, id);
+ n = mkbin(Oas, f, a);
+ n.ty = id.ty;
+ scom(n);
+ if(int f.flags&TEMP)
+ as.left = f;
+ }
+ id = id.next;
+ }
+ id = d.ty.ids;
+ for(as = e.right; as != nil; as = as.right){
+ a = as.left;
+ if(int a.flags&TEMP){
+ f = mkdeclname(as.src, id);
+ n = mkbin(Oas, f, a);
+ n.ty = id.ty;
+ scom(n);
+ tfree(a);
+ }
+ id = id.next;
+ }
+ p = genrawop(e.src, IJMP, nil, nil, nil);
+ patch(p, d.pc);
+ popblock();
+ return 1;
+}
diff --git a/appl/cmd/limbo/decls.b b/appl/cmd/limbo/decls.b
new file mode 100644
index 00000000..53d9e822
--- /dev/null
+++ b/appl/cmd/limbo/decls.b
@@ -0,0 +1,1177 @@
+
+storename := array[Dend] of
+{
+ Dtype => "type",
+ Dfn => "function",
+ Dglobal => "global",
+ Darg => "argument",
+ Dlocal => "local",
+ Dconst => "con",
+ Dfield => "field",
+ Dtag => "pick tag",
+ Dimport => "import",
+ Dunbound => "unbound",
+ Dundef => "undefined",
+ Dwundef => "undefined",
+};
+
+storeart := array[Dend] of
+{
+ Dtype => "a ",
+ Dfn => "a ",
+ Dglobal => "a ",
+ Darg => "an ",
+ Dlocal => "a ",
+ Dconst => "a ",
+ Dfield => "a ",
+ Dtag => "a ",
+ Dimport => "an ",
+ Dunbound => "",
+ Dundef => "",
+ Dwundef => "",
+};
+
+storespace := array[Dend] of
+{
+ Dtype => 0,
+ Dfn => 0,
+ Dglobal => 1,
+ Darg => 1,
+ Dlocal => 1,
+ Dconst => 0,
+ Dfield => 1,
+ Dtag => 0,
+ Dimport => 0,
+ Dunbound => 0,
+ Dundef => 0,
+ Dwundef => 0,
+};
+
+impdecl: ref Decl;
+impdecls: ref Dlist;
+scopes := array[MaxScope] of ref Decl;
+tails := array[MaxScope] of ref Decl;
+scopekind := array[MaxScope] of byte;
+scopenode := array[MaxScope] of ref Node;
+iota: ref Decl;
+zdecl: Decl;
+
+popscopes()
+{
+ d: ref Decl;
+
+ #
+ # clear out any decls left in syms
+ #
+ while(scope >= ScopeBuiltin){
+ for(d = scopes[scope--]; d != nil; d = d.next){
+ if(d.sym != nil){
+ d.sym.decl = d.old;
+ d.old = nil;
+ }
+ }
+ }
+
+ for(id := impdecls; id != nil; id = id.next){
+ for(d = id.d.ty.ids; d != nil; d = d.next){
+ d.sym.decl = nil;
+ d.old = nil;
+ }
+ }
+ impdecls = nil;
+
+ scope = ScopeBuiltin;
+ scopes[ScopeBuiltin] = nil;
+ tails[ScopeBuiltin] = nil;
+}
+
+declstart()
+{
+ iota = mkids(nosrc, enter("iota", 0), tint, nil);
+ iota.init = mkconst(nosrc, big 0);
+
+ scope = ScopeNils;
+ scopes[ScopeNils] = nil;
+ tails[ScopeNils] = nil;
+
+ nildecl = mkdecl(nosrc, Dglobal, tany);
+ nildecl.sym = enter("nil", 0);
+ installids(Dglobal, nildecl);
+ d := mkdecl(nosrc, Dglobal, tstring);
+ d.sym = enterstring("");
+ installids(Dglobal, d);
+
+ scope = ScopeGlobal;
+ scopes[ScopeGlobal] = nil;
+ tails[ScopeGlobal] = nil;
+}
+
+redecl(d: ref Decl)
+{
+ old := d.sym.decl;
+ if(old.store == Dwundef)
+ return;
+ error(d.src.start, "redeclaration of "+declconv(d)+", previously declared as "+storeconv(old)+" on line "+
+ lineconv(old.src.start));
+}
+
+checkrefs(d: ref Decl)
+{
+ id, m: ref Decl;
+ refs: int;
+
+ for(; d != nil; d = d.next){
+ if(d.das != byte 0)
+ d.refs--;
+ case d.store{
+ Dtype =>
+ refs = d.refs;
+ if(d.ty.kind == Tadt){
+ for(id = d.ty.ids; id != nil; id = id.next){
+ d.refs += id.refs;
+ if(id.store != Dfn)
+ continue;
+ if(id.init == nil && id.link == nil && d.importid == nil)
+ error(d.src.start, "function "+d.sym.name+"."+id.sym.name+" not defined");
+ if(superwarn && !id.refs && d.importid == nil)
+ warn(d.src.start, "function "+d.sym.name+"."+id.sym.name+" not referenced");
+ }
+ }
+ if(d.ty.kind == Tmodule){
+ for(id = d.ty.ids; id != nil; id = id.next){
+ refs += id.refs;
+ if(id.iface != nil)
+ id.iface.refs += id.refs;
+ if(id.store == Dtype){
+ for(m = id.ty.ids; m != nil; m = m.next){
+ refs += m.refs;
+ if(m.iface != nil)
+ m.iface.refs += m.refs;
+ }
+ }
+ }
+ d.refs = refs;
+ }
+ if(superwarn && !refs && d.importid == nil)
+ warn(d.src.start, declconv(d)+" not referenced");
+ Dglobal =>
+ if(superwarn && !d.refs && d.sym != nil && d.sym.name[0] != '.')
+ warn(d.src.start, declconv(d)+" not referenced");
+ Dlocal or
+ Darg =>
+ if(!d.refs && d.sym != nil && d.sym.name != nil && d.sym.name[0] != '.')
+ warn(d.src.start, declconv(d)+" not referenced");
+ Dconst =>
+ if(superwarn && !d.refs && d.sym != nil)
+ warn(d.src.start, declconv(d)+" not referenced");
+ Dfn =>
+ if(d.init == nil && d.importid == nil)
+ error(d.src.start, declconv(d)+" not defined");
+ if(superwarn && !d.refs)
+ warn(d.src.start, declconv(d)+" not referenced");
+ Dimport =>
+ if(superwarn && !d.refs)
+ warn(d.src.start, declconv(d)+" not referenced");
+ }
+ if(d.das != byte 0)
+ d.refs++;
+ }
+}
+
+vardecl(ids: ref Decl, t: ref Type): ref Node
+{
+ n := mkn(Ovardecl, mkn(Oseq, nil, nil), nil);
+ n.decl = ids;
+ n.ty = t;
+ return n;
+}
+
+vardecled(n: ref Node)
+{
+ store := Dlocal;
+ if(scope == ScopeGlobal)
+ store = Dglobal;
+ if(n.ty.kind == Texception && n.ty.cons == byte 1){
+ store = Dconst;
+ fatal("Texception in vardecled");
+ }
+ ids := n.decl;
+ installids(store, ids);
+ t := n.ty;
+ for(last := ids; ids != nil; ids = ids.next){
+ ids.ty = t;
+ last = ids;
+ }
+ n.left.decl = last;
+}
+
+condecl(ids: ref Decl, init: ref Node): ref Node
+{
+ n := mkn(Ocondecl, mkn(Oseq, nil, nil), init);
+ n.decl = ids;
+ return n;
+}
+
+condecled(n: ref Node)
+{
+ ids := n.decl;
+ installids(Dconst, ids);
+ for(last := ids; ids != nil; ids = ids.next){
+ ids.ty = tunknown;
+ last = ids;
+ }
+ n.left.decl = last;
+}
+
+exdecl(ids: ref Decl, tids: ref Decl): ref Node
+{
+ n: ref Node;
+ t: ref Type;
+
+ t = mktype(ids.src.start, ids.src.stop, Texception, nil, tids);
+ t.cons = byte 1;
+ n = mkn(Oexdecl, mkn(Oseq, nil, nil), nil);
+ n.decl = ids;
+ n.ty = t;
+ return n;
+}
+
+exdecled(n: ref Node)
+{
+ ids, last: ref Decl;
+ t: ref Type;
+
+ ids = n.decl;
+ installids(Dconst, ids);
+ t = n.ty;
+ for(last = ids; ids != nil; ids = ids.next){
+ ids.ty = t;
+ last = ids;
+ }
+ n.left.decl = last;
+}
+
+importdecl(m: ref Node, ids: ref Decl): ref Node
+{
+ n := mkn(Oimport, mkn(Oseq, nil, nil), m);
+ n.decl = ids;
+ return n;
+}
+
+importdecled(n: ref Node)
+{
+ ids := n.decl;
+ installids(Dimport, ids);
+ for(last := ids; ids != nil; ids = ids.next){
+ ids.ty = tunknown;
+ last = ids;
+ }
+ n.left.decl = last;
+}
+
+mkscope(body: ref Node): ref Node
+{
+ n := mkn(Oscope, nil, body);
+ if(body != nil)
+ n.src = body.src;
+ return n;
+}
+
+fndecl(n: ref Node, t: ref Type, body: ref Node): ref Node
+{
+ n = mkbin(Ofunc, n, body);
+ n.ty = t;
+ return n;
+}
+
+fndecled(n: ref Node)
+{
+ left := n.left;
+ if(left.op == Oname){
+ d := left.decl.sym.decl;
+ if(d == nil || d.store == Dimport){
+ d = mkids(left.src, left.decl.sym, n.ty, nil);
+ installids(Dfn, d);
+ }
+ left.decl = d;
+ d.refs++;
+ }
+ if(left.op == Odot)
+ pushscope(nil, Sother);
+ if(n.ty.polys != nil){
+ pushscope(nil, Sother);
+ installids(Dtype, n.ty.polys);
+ }
+ pushscope(nil, Sother);
+ installids(Darg, n.ty.ids);
+ n.ty.ids = popscope();
+ if(n.ty.val != nil)
+ mergepolydecs(n.ty);
+ if(n.ty.polys != nil)
+ n.ty.polys = popscope();
+ if(left.op == Odot)
+ popscope();
+}
+
+#
+# check the function declaration only
+# the body will be type checked later by fncheck
+#
+fnchk(n: ref Node): ref Decl
+{
+ bad := 0;
+ d := n.left.decl;
+ if(n.left.op == Odot)
+ d = n.left.right.decl;
+ if(d == nil)
+ fatal("decl() fnchk nil");
+ n.left.decl = d;
+ if(d.store == Dglobal || d.store == Dfield)
+ d.store = Dfn;
+ if(d.store != Dfn || d.init != nil){
+ nerror(n, "redeclaration of function "+dotconv(d)+", previously declared as "
+ +storeconv(d)+" on line "+lineconv(d.src.start));
+ if(d.store == Dfn && d.init != nil)
+ bad = 1;
+ }
+ d.init = n;
+
+ t := n.ty;
+ inadt := d.dot;
+ if(inadt != nil && (inadt.store != Dtype || inadt.ty.kind != Tadt))
+ inadt = nil;
+ if(n.left.op == Odot){
+ pushscope(nil, Sother);
+ adtp := outerpolys(n.left);
+ if(adtp != nil)
+ installids(Dtype, adtp);
+ if(!polyequal(adtp, n.decl))
+ nerror(n, "adt polymorphic type mismatch");
+ n.decl = nil;
+ }
+ t = validtype(t, inadt);
+ if(n.left.op == Odot)
+ popscope();
+ if(debug['d'])
+ print("declare function %s ty %s newty %s\n", dotconv(d), typeconv(d.ty), typeconv(t));
+ t = usetype(t);
+
+ if(!polyequal(d.ty.polys, t.polys))
+ nerror(n, "function polymorphic type mismatch");
+ if(!tcompat(d.ty, t, 0))
+ nerror(n, "type mismatch: "+dotconv(d)+" defined as "
+ +typeconv(t)+" declared as "+typeconv(d.ty)+" on line "+lineconv(d.src.start));
+ else if(!raisescompat(d.ty.eraises, t.eraises))
+ nerror(n, "raises mismatch: " + dotconv(d));
+ if(t.varargs != byte 0)
+ nerror(n, "cannot define functions with a '*' argument, such as "+dotconv(d));
+
+ t.eraises = d.ty.eraises;
+
+ d.ty = t;
+ d.offset = idoffsets(t.ids, MaxTemp, IBY2WD);
+ d.src = n.src;
+
+ d.locals = nil;
+
+ n.ty = t;
+
+ if(bad)
+ return nil;
+ return d;
+}
+
+globalas(dst: ref Node, v: ref Node, valok: int): ref Node
+{
+ if(v == nil)
+ return nil;
+ if(v.op == Oas || v.op == Odas){
+ v = globalas(v.left, v.right, valok);
+ if(v == nil)
+ return nil;
+ }else if(valok && !initable(dst, v, 0))
+ return nil;
+ case dst.op{
+ Oname =>
+ if(dst.decl.init != nil)
+ nerror(dst, "duplicate assignment to "+expconv(dst)+", previously assigned on line "
+ +lineconv(dst.decl.init.src.start));
+ if(valok)
+ dst.decl.init = v;
+ return v;
+ Otuple =>
+ if(valok && v.op != Otuple)
+ fatal("can't deal with "+nodeconv(v)+" in tuple case of globalas");
+ tv := v.left;
+ for(dst = dst.left; dst != nil; dst = dst.right){
+ globalas(dst.left, tv.left, valok);
+ if(valok)
+ tv = tv.right;
+ }
+ return v;
+ }
+ fatal("can't deal with "+nodeconv(dst)+" in globalas");
+ return nil;
+}
+
+needsstore(d: ref Decl): int
+{
+ if(!d.refs)
+ return 0;
+ if(d.importid != nil)
+ return 0;
+ if(storespace[d.store])
+ return 1;
+ return 0;
+}
+
+#
+# return the list of all referenced storage variables
+#
+vars(d: ref Decl): ref Decl
+{
+ while(d != nil && !needsstore(d))
+ d = d.next;
+ for(v := d; v != nil; v = v.next){
+ while(v.next != nil){
+ n := v.next;
+ if(needsstore(n))
+ break;
+ v.next = n.next;
+ }
+ }
+ return d;
+}
+
+#
+# declare variables from the left side of a := statement
+#
+recdasdecl(n: ref Node, store: int, nid: int): (int, int)
+{
+ r: int;
+
+ case n.op{
+ Otuple =>
+ ok := 1;
+ for(n = n.left; n != nil; n = n.right){
+ (r, nid) = recdasdecl(n.left, store, nid);
+ ok &= r;
+ }
+ return (ok, nid);
+ Oname =>
+ if(n.decl == nildecl)
+ return (1, -1);
+ d := mkids(n.src, n.decl.sym, nil, nil);
+ installids(store, d);
+ n.decl = d;
+ old := d.old;
+ if(old != nil
+ && old.store != Dfn
+ && old.store != Dwundef
+ && old.store != Dundef)
+ warn(d.src.start, "redeclaration of "+declconv(d)+", previously declared as "
+ +storeconv(old)+" on line "+lineconv(old.src.start));
+ d.refs++;
+ d.das = byte 1;
+ if(nid >= 0)
+ nid++;
+ return (1, nid);
+ }
+ return (0, nid);
+}
+
+recmark(n: ref Node, nid: int): int
+{
+ case(n.op){
+ Otuple =>
+ for(n = n.left; n != nil; n = n.right)
+ nid = recmark(n.left, nid);
+ Oname =>
+ n.decl.nid = byte nid;
+ nid = 0;
+ }
+ return nid;
+}
+
+dasdecl(n: ref Node): int
+{
+ ok: int;
+
+ nid := 0;
+ store := Dlocal;
+ if(scope == ScopeGlobal)
+ store = Dglobal;
+
+ (ok, nid) = recdasdecl(n, store, nid);
+ if(!ok)
+ nerror(n, "illegal declaration expression "+expconv(n));
+ if(ok && store == Dlocal && nid > 1)
+ recmark(n, nid);
+ return ok;
+}
+
+#
+# declare global variables in nested := expressions
+#
+gdasdecl(n: ref Node)
+{
+ if(n == nil)
+ return;
+
+ if(n.op == Odas){
+ gdasdecl(n.right);
+ dasdecl(n.left);
+ }else{
+ gdasdecl(n.left);
+ gdasdecl(n.right);
+ }
+}
+
+undefed(src: Src, s: ref Sym): ref Decl
+{
+ d := mkids(src, s, tnone, nil);
+ error(src.start, s.name+" is not declared");
+ installids(Dwundef, d);
+ return d;
+}
+
+# inloop() : int
+# {
+# for (i := scope; i > 0; i--)
+# if (int scopekind[i] == Sloop)
+# return 1;
+# return 0;
+# }
+
+nested() : int
+{
+ for (i := scope; i > 0; i--)
+ if (int scopekind[i] == Sscope || int scopekind[i] == Sloop)
+ return 1;
+ return 0;
+}
+
+decltozero(n : ref Node)
+{
+ if ((scop := scopenode[scope]) != nil) {
+ if (n.right != nil && errors == 0)
+ fatal("Ovardecl/Oname/Otuple has right field\n");
+ n.right = scop.left;
+ scop.left = n;
+ }
+}
+
+pushscope(scp : ref Node, kind : int)
+{
+ if(scope >= MaxScope)
+ fatal("scope too deep");
+ scope++;
+ scopes[scope] = nil;
+ tails[scope] = nil;
+ scopenode[scope] = scp;
+ scopekind[scope] = byte kind;
+}
+
+curscope(): ref Decl
+{
+ return scopes[scope];
+}
+
+#
+# revert to old declarations for each symbol in the currect scope.
+# remove the effects of any imported adt types
+# whenever the adt is imported from a module,
+# we record in the type's decl the module to use
+# when calling members. the process is reversed here.
+#
+popscope(): ref Decl
+{
+ for(id := scopes[scope]; id != nil; id = id.next){
+ if(id.sym != nil){
+ id.sym.decl = id.old;
+ id.old = nil;
+ }
+ if(id.importid != nil)
+ id.importid.refs += id.refs;
+ t := id.ty;
+ if(id.store == Dtype
+ && t.decl != nil
+ && t.decl.timport == id)
+ t.decl.timport = id.timport;
+ if(id.store == Dlocal)
+ freeloc(id);
+ }
+ return scopes[scope--];
+}
+
+#
+# make a new scope,
+# preinstalled with some previously installed identifiers
+# don't add the identifiers to the scope chain,
+# so they remain separate from any newly installed ids
+#
+# these routines assume no ids are imports
+#
+repushids(ids: ref Decl)
+{
+ if(scope >= MaxScope)
+ fatal("scope too deep");
+ scope++;
+ scopes[scope] = nil;
+ tails[scope] = nil;
+ scopenode[scope] = nil;
+ scopekind[scope] = byte Sother;
+
+ for(; ids != nil; ids = ids.next){
+ if(ids.scope != scope
+ && (ids.dot == nil || !isimpmod(ids.dot.sym)
+ || ids.scope != ScopeGlobal || scope != ScopeGlobal + 1))
+ fatal("repushids scope mismatch");
+ s := ids.sym;
+ if(s != nil && ids.store != Dtag){
+ if(s.decl != nil && s.decl.scope >= scope)
+ ids.old = s.decl.old;
+ else
+ ids.old = s.decl;
+ s.decl = ids;
+ }
+ }
+}
+
+#
+# pop a scope which was started with repushids
+# return any newly installed ids
+#
+popids(ids: ref Decl): ref Decl
+{
+ for(; ids != nil; ids = ids.next){
+ if(ids.sym != nil && ids.store != Dtag){
+ ids.sym.decl = ids.old;
+ ids.old = nil;
+ }
+ }
+ return popscope();
+}
+
+installids(store: int, ids: ref Decl)
+{
+ last : ref Decl = nil;
+ for(d := ids; d != nil; d = d.next){
+ d.scope = scope;
+ if(d.store == Dundef)
+ d.store = store;
+ s := d.sym;
+ if(s != nil){
+ if(s.decl != nil && s.decl.scope >= scope){
+ redecl(d);
+ d.old = s.decl.old;
+ }else
+ d.old = s.decl;
+ s.decl = d;
+ }
+ last = d;
+ }
+ if(ids != nil){
+ d = tails[scope];
+ if(d == nil)
+ scopes[scope] = ids;
+ else
+ d.next = ids;
+ tails[scope] = last;
+ }
+}
+
+lookup(sym: ref Sym): ref Decl
+{
+ s: int;
+ d: ref Decl;
+
+ for(s = scope; s >= ScopeBuiltin; s--){
+ for(d = scopes[s]; d != nil; d = d.next){
+ if(d.sym == sym)
+ return d;
+ }
+ }
+ return nil;
+}
+
+mkids(src: Src, s: ref Sym, t: ref Type, next: ref Decl): ref Decl
+{
+ d := ref zdecl;
+ d.src = src;
+ d.store = Dundef;
+ d.ty = t;
+ d.next = next;
+ d.sym = s;
+ d.nid = byte 1;
+ return d;
+}
+
+mkdecl(src: Src, store: int, t: ref Type): ref Decl
+{
+ d := ref zdecl;
+ d.src = src;
+ d.store = store;
+ d.ty = t;
+ d.nid = byte 1;
+ return d;
+}
+
+dupdecl(old: ref Decl): ref Decl
+{
+ d := ref *old;
+ d.next = nil;
+ return d;
+}
+
+dupdecls(old: ref Decl): ref Decl
+{
+ d, nd, first, last: ref Decl;
+
+ first = last = nil;
+ for(d = old; d != nil; d = d.next){
+ nd = dupdecl(d);
+ if(first == nil)
+ first = nd;
+ else
+ last.next = nd;
+ last = nd;
+ }
+ return first;
+}
+
+appdecls(d: ref Decl, dd: ref Decl): ref Decl
+{
+ if(d == nil)
+ return dd;
+ for(t := d; t.next != nil; t = t.next)
+ ;
+ t.next = dd;
+ return d;
+}
+
+revids(id: ref Decl): ref Decl
+{
+ next : ref Decl;
+ d : ref Decl = nil;
+ for(; id != nil; id = next){
+ next = id.next;
+ id.next = d;
+ d = id;
+ }
+ return d;
+}
+
+idoffsets(id: ref Decl, offset: int, al: int): int
+{
+ algn := 1;
+ for(; id != nil; id = id.next){
+ if(storespace[id.store]){
+usedty(id.ty);
+ if(id.store == Dlocal && id.link != nil){
+ # id.nid always 1
+ id.offset = id.link.offset;
+ continue;
+ }
+ a := id.ty.align;
+ if(id.nid > byte 1){
+ for(d := id.next; d != nil && d.nid == byte 0; d = d.next)
+ if(d.ty.align > a)
+ a = d.ty.align;
+ algn = a;
+ }
+ offset = align(offset, a);
+ id.offset = offset;
+ offset += id.ty.size;
+ if(id.nid == byte 0 && (id.next == nil || id.next.nid != byte 0))
+ offset = align(offset, algn);
+ }
+ }
+ return align(offset, al);
+}
+
+idindices(id: ref Decl): int
+{
+ i := 0;
+ for(; id != nil; id = id.next){
+ if(storespace[id.store]){
+ usedty(id.ty);
+ id.offset = i++;
+ }
+ }
+ return i;
+}
+
+declconv(d: ref Decl): string
+{
+ if(d.sym == nil)
+ return storename[d.store] + " " + "<???>";
+ return storename[d.store] + " " + d.sym.name;
+}
+
+storeconv(d: ref Decl): string
+{
+ return storeart[d.store] + storename[d.store];
+}
+
+dotconv(d: ref Decl): string
+{
+ s: string;
+
+ if(d.dot != nil && !isimpmod(d.dot.sym)){
+ s = dotconv(d.dot);
+ if(d.dot.ty != nil && d.dot.ty.kind == Tmodule)
+ s += ".";
+ else
+ s += ".";
+ }
+ s += d.sym.name;
+ return s;
+}
+
+#
+# merge together two sorted lists, yielding a sorted list
+#
+namemerge(e, f: ref Decl): ref Decl
+{
+ d := rock := ref Decl;
+ while(e != nil && f != nil){
+ if(e.sym.name <= f.sym.name){
+ d.next = e;
+ e = e.next;
+ }else{
+ d.next = f;
+ f = f.next;
+ }
+ d = d.next;
+ }
+ if(e != nil)
+ d.next = e;
+ else
+ d.next = f;
+ return rock.next;
+}
+
+#
+# recursively split lists and remerge them after they are sorted
+#
+recnamesort(d: ref Decl, n: int): ref Decl
+{
+ if(n <= 1)
+ return d;
+ m := n / 2 - 1;
+ dd := d;
+ for(i := 0; i < m; i++)
+ dd = dd.next;
+ r := dd.next;
+ dd.next = nil;
+ return namemerge(recnamesort(d, n / 2),
+ recnamesort(r, (n + 1) / 2));
+}
+
+#
+# sort the ids by name
+#
+namesort(d: ref Decl): ref Decl
+{
+ n := 0;
+ for(dd := d; dd != nil; dd = dd.next)
+ n++;
+ return recnamesort(d, n);
+}
+
+printdecls(d: ref Decl)
+{
+ for(; d != nil; d = d.next)
+ print("%d: %s %s ref %d\n", d.offset, declconv(d), typeconv(d.ty), d.refs);
+}
+
+mergepolydecs(t: ref Type)
+{
+ n, nn: ref Node;
+ id, ids, ids1: ref Decl;
+
+ for(n = t.val; n != nil; n = n.right){
+ nn = n.left;
+ for(ids = nn.decl; ids != nil; ids = ids.next){
+ id = ids.sym.decl;
+ if(id == nil){
+ undefed(ids.src, ids.sym);
+ break;
+ }
+ if(id.store != Dtype){
+ error(ids.src.start, declconv(id) + " is not a type");
+ break;
+ }
+ if(id.ty.kind != Tpoly){
+ error(ids.src.start, declconv(id) + " is not a polymorphic type");
+ break;
+ }
+ if(id.ty.ids != nil)
+ error(ids.src.start, declconv(id) + " redefined");
+ pushscope(nil, Sother);
+ fielddecled(nn.left);
+ id.ty.ids = popscope();
+ for(ids1 = id.ty.ids; ids1 != nil; ids1 = ids1.next){
+ ids1.dot = id;
+ bindtypes(ids1.ty);
+ if(ids1.ty.kind != Tfn){
+ error(ids1.src.start, "only function types expected");
+ id.ty.ids = nil;
+ }
+ }
+ }
+ }
+ t.val = nil;
+}
+
+adjfnptrs(d: ref Decl, polys1: ref Decl, polys2: ref Decl)
+{
+ n: int;
+ id, idt, idf, arg: ref Decl;
+
+ n = 0;
+ for(id = d.ty.ids; id != nil; id = id.next)
+ n++;
+ for(idt = polys1; idt != nil; idt = idt.next)
+ for(idf = idt.ty.ids; idf != nil; idf = idf.next)
+ n -= 2;
+ for(idt = polys2; idt != nil; idt = idt.next)
+ for(idf = idt.ty.ids; idf != nil; idf = idf.next)
+ n -= 2;
+ for(arg = d.ty.ids; --n >= 0; arg = arg.next)
+ ;
+ for(idt = polys1; idt != nil; idt = idt.next){
+ for(idf = idt.ty.ids; idf != nil; idf = idf.next){
+ idf.link = arg;
+ arg = arg.next.next;
+ }
+ }
+ for(idt = polys2; idt != nil; idt = idt.next){
+ for(idf = idt.ty.ids; idf != nil; idf = idf.next){
+ idf.link = arg;
+ arg = arg.next.next;
+ }
+ }
+}
+
+addptrs(polys: ref Decl, fps: ref Decl, last: ref Decl, link: int, src: Src): (ref Decl, ref Decl)
+{
+ for(idt := polys; idt != nil; idt = idt.next){
+ for(idf := idt.ty.ids; idf != nil; idf = idf.next){
+ fp := mkdecl(src, Darg, tany);
+ fp.sym = idf.sym;
+ if(link)
+ idf.link = fp;
+ if(fps == nil)
+ fps = fp;
+ else
+ last.next = fp;
+ last = fp;
+ fp = mkdecl(src, Darg, tint);
+ fp.sym = idf.sym;
+ last.next = fp;
+ last = fp;
+ }
+ }
+ return (fps, last);
+}
+
+addfnptrs(d: ref Decl, link: int)
+{
+ fps, last, polys: ref Decl;
+
+ polys = encpolys(d);
+ if(int(d.ty.flags&FULLARGS)){
+ if(link)
+ adjfnptrs(d, d.ty.polys, polys);
+ return;
+ }
+ d.ty.flags |= FULLARGS;
+ fps = last = nil;
+ (fps, last) = addptrs(d.ty.polys, fps, last, link, d.src);
+ (fps, last) = addptrs(polys, fps, last, link, d.src);
+ for(last = d.ty.ids; last != nil && last.next != nil; last = last.next)
+ ;
+ if(last != nil)
+ last.next = fps;
+ else
+ d.ty.ids = fps;
+ d.offset = idoffsets(d.ty.ids, MaxTemp, IBY2WD);
+}
+
+rmfnptrs(d: ref Decl)
+{
+ n: int;
+ id, idt, idf: ref Decl;
+
+ if(int(d.ty.flags&FULLARGS))
+ d.ty.flags &= ~FULLARGS;
+ else
+ return;
+ n = 0;
+ for(id = d.ty.ids; id != nil; id = id.next)
+ n++;
+ for(idt = d.ty.polys; idt != nil; idt = idt.next)
+ for(idf = idt.ty.ids; idf != nil; idf = idf.next)
+ n -= 2;
+ for(idt = encpolys(d); idt != nil; idt = idt.next)
+ for(idf = idt.ty.ids; idf != nil; idf = idf.next)
+ n -= 2;
+ if(n == 0){
+ d.ty.ids = nil;
+ return;
+ }
+ for(id = d.ty.ids; --n > 0; id = id.next)
+ ;
+ id.next = nil;
+ d.offset = idoffsets(d.ty.ids, MaxTemp, IBY2WD);
+}
+
+local(d: ref Decl): int
+{
+ for(d = d.dot; d != nil; d = d.dot)
+ if(d.store == Dtype && d.ty.kind == Tmodule)
+ return 0;
+ return 1;
+}
+
+lmodule(d: ref Decl): ref Decl
+{
+ for(d = d.dot; d != nil; d = d.dot)
+ if(d.store == Dtype && d.ty.kind == Tmodule)
+ return d;
+ return nil;
+}
+
+outerpolys(n: ref Node): ref Decl
+{
+ d: ref Decl;
+
+ if(n.op == Odot){
+ d = n.right.decl;
+ if(d == nil)
+ fatal("decl() outeradt nil");
+ d = d.dot;
+ if(d != nil && d.store == Dtype && d.ty.kind == Tadt)
+ return d.ty.polys;
+ }
+ return nil;
+}
+
+encpolys(d: ref Decl): ref Decl
+{
+ if((d = d.dot) == nil)
+ return nil;
+ return d.ty.polys;
+}
+
+fnlookup(s: ref Sym, t: ref Type): (ref Decl, ref Node)
+{
+ id: ref Decl;
+ mod: ref Node;
+
+ id = nil;
+ mod = nil;
+ if(t.kind == Tpoly || t.kind == Tmodule)
+ id = namedot(t.ids, s);
+ else if(t.kind == Tref){
+ t = t.tof;
+ if(t.kind == Tadt){
+ id = namedot(t.ids, s);
+ if(t.decl != nil && t.decl.timport != nil)
+ mod = t.decl.timport.eimport;
+ }
+ else if(t.kind == Tadtpick){
+ id = namedot(t.ids, s);
+ if(t.decl != nil && t.decl.timport != nil)
+ mod = t.decl.timport.eimport;
+ t = t.decl.dot.ty;
+ if(id == nil)
+ id = namedot(t.ids, s);
+ if(t.decl != nil && t.decl.timport != nil)
+ mod = t.decl.timport.eimport;
+ }
+ }
+ if(id == nil){
+ id = lookup(s);
+ if(id != nil)
+ mod = id.eimport;
+ }
+ return (id, mod);
+}
+
+isimpmod(s: ref Sym): int
+{
+ d: ref Decl;
+
+ for(d = impmods; d != nil; d = d.next)
+ if(d.sym == s)
+ return 1;
+ return 0;
+}
+
+dequal(d1: ref Decl, d2: ref Decl, full: int): int
+{
+ return d1.sym == d2.sym &&
+ d1.store == d2.store &&
+ d1.implicit == d2.implicit &&
+ d1.cyc == d2.cyc &&
+ (!full || tequal(d1.ty, d2.ty)) &&
+ (!full || d1.store == Dfn || sametree(d1.init, d2.init));
+}
+
+tzero(t: ref Type): int
+{
+ return t.kind == Texception || tmustzero(t);
+}
+
+isptr(t: ref Type): int
+{
+ return t.kind == Texception || tattr[t.kind].isptr;
+}
+
+# can d share the same stack location as another local ?
+shareloc(d: ref Decl)
+{
+ z: int;
+ t, tt: ref Type;
+ dd, res: ref Decl;
+
+ if(d.store != Dlocal || d.nid != byte 1)
+ return;
+ t = d.ty;
+ res = nil;
+ for(dd = fndecls; dd != nil; dd = dd.next){
+ if(d == dd)
+ fatal("d==dd in shareloc");
+ if(dd.store != Dlocal || dd.nid != byte 1 || dd.link != nil || dd.tref != 0)
+ continue;
+ tt = dd.ty;
+ if(t.size != tt.size || t.align != tt.align)
+ continue;
+ z = tzero(t)+tzero(tt);
+ if(z > 0)
+ continue; # for now
+ if(t == tt || tequal(t, tt))
+ res = dd;
+ else{
+ if(z == 1)
+ continue;
+ if(z == 0 || isptr(t) || isptr(tt) || mktdesc(t) == mktdesc(tt))
+ res = dd;
+ }
+ if(res != nil){
+ d.link = res;
+ res.tref = 1;
+ return;
+ }
+ }
+ return;
+}
+
+freeloc(d: ref Decl)
+{
+ if(d.link != nil)
+ d.link.tref = 0;
+}
diff --git a/appl/cmd/limbo/dis.b b/appl/cmd/limbo/dis.b
new file mode 100644
index 00000000..7d79553e
--- /dev/null
+++ b/appl/cmd/limbo/dis.b
@@ -0,0 +1,560 @@
+
+NAMELEN: con 28;
+
+cache: array of byte;
+ncached: int;
+ndatum: int;
+startoff: int;
+lastoff: int;
+lastkind: int;
+
+discon(val: int)
+{
+ if(val >= -64 && val <= 63){
+ bout.putb(byte (val & ~16r80));
+ return;
+ }
+ if(val >= -8192 && val <= 8191){
+ bout.putb(byte ((val>>8) & ~16rC0 | 16r80));
+ bout.putb(byte val);
+ return;
+ }
+ if(val < 0 && ((val >> 29) & 7) != 7
+ || val > 0 && (val >> 29) != 0)
+ fatal("overflow in constant 16r"+hex(val, 0));
+ bout.putb(byte(val>>24 | 16rC0));
+ bout.putb(byte(val>>16));
+ bout.putb(byte(val>>8));
+ bout.putb(byte val);
+}
+
+disword(w: int)
+{
+ bout.putb(byte(w >> 24));
+ bout.putb(byte(w >> 16));
+ bout.putb(byte(w >> 8));
+ bout.putb(byte w);
+}
+
+disdata(kind, n: int)
+{
+ if(n < DMAX && n != 0)
+ bout.putb(byte((kind << DBYTE) | n));
+ else{
+ bout.putb(byte kind << DBYTE);
+ discon(n);
+ }
+}
+
+dismod(m: ref Decl)
+{
+ fileoff := bout.seek(big 0, 1);
+ name := array of byte m.sym.name;
+ n := len name;
+ if(n > NAMELEN-1)
+ n = NAMELEN-1;
+ bout.write(name, n);
+ bout.putb(byte 0);
+ for(m = m.ty.tof.ids; m != nil; m = m.next){
+ case m.store{
+ Dglobal =>
+ discon(-1);
+ discon(-1);
+ disword(sign(m));
+ bout.puts(".mp");
+ bout.putb(byte 0);
+ Dfn =>
+ discon(m.pc.pc);
+ discon(m.desc.id);
+ disword(sign(m));
+ if(m.dot.ty.kind == Tadt){
+ bout.puts(m.dot.sym.name);
+ bout.putb(byte '.');
+ }
+ bout.puts(m.sym.name);
+ bout.putb(byte 0);
+ * =>
+ fatal("unknown kind in dismod: "+declconv(m));
+ }
+ }
+ if(debug['s'])
+ print("%bd linkage bytes start %bd\n", bout.seek(big 0, 1) - fileoff, fileoff);
+}
+
+dispath()
+{
+ sp := array of byte srcpath();
+ bout.write(sp, len sp);
+ bout.putb(byte 0);
+}
+
+disentry(e: ref Decl)
+{
+ if(e == nil){
+ discon(-1);
+ discon(-1);
+ return;
+ }
+ discon(e.pc.pc);
+ discon(e.desc.id);
+}
+
+disdesc(d: ref Desc)
+{
+ fileoff := bout.seek(big 0, 1);
+ for(; d != nil; d = d.next){
+ discon(d.id);
+ discon(d.size);
+ discon(d.nmap);
+ bout.write(d.map, d.nmap);
+ }
+ if(debug['s'])
+ print("%bd type descriptor bytes start %bd\n", bout.seek(big 0, 1) - fileoff, fileoff);
+}
+
+disvar(nil: int, ids: ref Decl)
+{
+ fileoff := bout.seek(big 0, 1);
+ lastkind = -1;
+ ncached = 0;
+ ndatum = 0;
+
+ for(d := ids; d != nil; d = d.next)
+ if(d.store == Dglobal && d.init != nil)
+ disdatum(d.offset, d.init);
+
+ disflush(-1, -1, 0);
+
+ bout.putb(byte 0);
+
+ if(debug['s'])
+ print("%bd data bytes start %bd\n", bout.seek(big 0, 1) - fileoff, fileoff);
+}
+
+disldt(size: int, ds: ref Decl)
+{
+ if(0){
+ discon(size);
+ disvar(size, ds);
+ return;
+ }
+
+ m := 0;
+ for(d := ds; d != nil; d = d.next)
+ if(d.store == Dglobal && d.init != nil)
+ m++;
+ discon(m);
+ n: ref Node;
+ for(d = ds; d != nil; d = d.next){
+ if(d.store == Dglobal && d.init != nil){
+ n = d.init;
+ if(n.ty.kind != Tiface)
+ nerror(n, "disldt: not Tiface");
+ discon(int n.c.val);
+ for(id := n.decl.ty.ids; id != nil; id = id.next){
+ disword(sign(id));
+ if(id.dot.ty.kind == Tadt){
+ s := array of byte id.dot.sym.name;
+ bout.write(s, len s);
+ bout.putb(byte '.');
+ }
+ s := array of byte id.sym.name;
+ bout.write(s, len s);
+ bout.putb(byte 0);
+ }
+ }
+ }
+ discon(0);
+}
+
+disdatum(offset: int, n: ref Node)
+{
+ c: ref Case;
+ lab: Label;
+ id: ref Decl;
+ wild: ref Node;
+ i, e: int;
+
+ case n.ty.kind{
+ Tbyte =>
+ disbyte(offset, byte n.c.val);
+ Tint or
+ Tfix =>
+ disint(offset, int n.c.val);
+ Tbig =>
+ disbig(offset, n.c.val);
+ Tstring =>
+ disstring(offset, n.decl.sym);
+ Treal =>
+ disreal(offset, n.c.rval);
+ Tadt or
+ Tadtpick or
+ Ttuple =>
+ id = n.ty.ids;
+ for(n = n.left; n != nil; n = n.right){
+ disdatum(offset + id.offset, n.left);
+ id = id.next;
+ }
+ Tany =>
+ break;
+ Tcase =>
+ c = n.ty.cse;
+ disint(offset, c.nlab);
+ offset += IBY2WD;
+ for(i = 0; i < c.nlab; i++){
+ lab = c.labs[i];
+ disint(offset, int lab.start.c.val);
+ offset += IBY2WD;
+ disint(offset, int lab.stop.c.val+1);
+ offset += IBY2WD;
+ disint(offset, lab.inst.pc);
+ offset += IBY2WD;
+ }
+ if(c.iwild != nil)
+ disint(offset, c.iwild.pc);
+ else
+ disint(offset, -1);
+ Tcasel =>
+ c = n.ty.cse;
+ disint(offset, c.nlab);
+ offset += 2*IBY2WD;
+ for(i = 0; i < c.nlab; i++){
+ lab = c.labs[i];
+ disbig(offset, lab.start.c.val);
+ offset += IBY2LG;
+ disbig(offset, lab.stop.c.val+big 1);
+ offset += IBY2LG;
+ disint(offset, lab.inst.pc);
+ offset += 2*IBY2WD;
+ }
+ if(c.iwild != nil)
+ disint(offset, c.iwild.pc);
+ else
+ disint(offset, -1);
+ Tcasec =>
+ c = n.ty.cse;
+ disint(offset, c.nlab);
+ offset += IBY2WD;
+ for(i = 0; i < c.nlab; i++){
+ lab = c.labs[i];
+ disstring(offset, lab.start.decl.sym);
+ offset += IBY2WD;
+ if(lab.stop != lab.start)
+ disstring(offset, lab.stop.decl.sym);
+ offset += IBY2WD;
+ disint(offset, lab.inst.pc);
+ offset += IBY2WD;
+ }
+ if(c.iwild != nil)
+ disint(offset, c.iwild.pc);
+ else
+ disint(offset, -1);
+ Tgoto =>
+ c = n.ty.cse;
+ disint(offset, n.ty.size/IBY2WD-1);
+ offset += IBY2WD;
+ for(i = 0; i < c.nlab; i++){
+ disint(offset, c.labs[i].inst.pc);
+ offset += IBY2WD;
+ }
+ if(c.iwild != nil)
+ disint(offset, c.iwild.pc);
+ Tarray =>
+ disflush(-1, -1, 0);
+ disdata(DEFA, 1); # 1 is ignored
+ discon(offset);
+ disword(n.ty.tof.decl.desc.id);
+ disword(int n.left.c.val);
+
+ if(n.right == nil)
+ break;
+
+ disdata(DIND, 1); # 1 is ignored
+ discon(offset);
+ disword(0);
+
+ c = n.right.ty.cse;
+ wild = nil;
+ if(c.wild != nil)
+ wild = c.wild.right;
+ last := 0;
+ esz := n.ty.tof.size;
+ for(i = 0; i < c.nlab; i++){
+ e = int c.labs[i].start.c.val;
+ if(wild != nil){
+ for(; last < e; last++)
+ disdatum(esz * last, wild);
+ }
+ last = e;
+ e = int c.labs[i].stop.c.val;
+ elem := c.labs[i].node.right;
+ for(; last <= e; last++)
+ disdatum(esz * last, elem);
+ }
+ if(wild != nil)
+ for(e = int n.left.c.val; last < e; last++)
+ disdatum(esz * last, wild);
+
+ disflush(-1, -1, 0);
+ disdata(DAPOP, 1); # 1 is ignored
+ discon(0);
+ Tiface =>
+ disint(offset, int n.c.val);
+ offset += IBY2WD;
+ for(id = n.decl.ty.ids; id != nil; id = id.next){
+ offset = align(offset, IBY2WD);
+ disint(offset, sign(id));
+ offset += IBY2WD;
+
+ name: array of byte;
+ if(id.dot.ty.kind == Tadt){
+ name = array of byte id.dot.sym.name;
+ disbytes(offset, name);
+ offset += len name;
+ disbyte(offset, byte '.');
+ offset++;
+ }
+ name = array of byte id.sym.name;
+ disbytes(offset, name);
+ offset += len name;
+ disbyte(offset, byte 0);
+ offset++;
+ }
+ * =>
+ fatal("can't gen global "+nodeconv(n));
+ }
+}
+
+disexc(es: ref Except)
+{
+ e: ref Except;
+
+ n := 0;
+ for(e = es; e != nil; e = e.next)
+ if(int e.p1.reach || int e.p2.reach)
+ n++;
+ discon(n);
+ for(e = es; e != nil; e = e.next){
+ if(!int e.p1.reach && !int e.p2.reach)
+ continue;
+ c := e.c;
+ discon(e.d.offset);
+ discon(getpc(e.p1));
+ discon(getpc(e.p2));
+ if(e.desc != nil)
+ discon(e.desc.id);
+ else
+ discon(-1);
+ discon(c.nlab|(e.ne<<16));
+ for(i := 0; i < c.nlab; i++){
+ lab := c.labs[i];
+ d := lab.start.decl;
+ if(lab.start.ty.kind == Texception)
+ d = d.init.decl;
+ bout.puts(d.sym.name);
+ bout.putb(byte 0);
+ discon(lab.inst.pc);
+ }
+ if(c.iwild == nil)
+ discon(-1);
+ else
+ discon(c.iwild.pc);
+ }
+ discon(0);
+}
+
+disbyte(off: int, v: byte)
+{
+ disflush(DEFB, off, 1);
+ cache[ncached++] = v;
+ ndatum++;
+}
+
+disbytes(off: int, v: array of byte)
+{
+ n := len v;
+ disflush(DEFB, off, n);
+ cache[ncached:] = v;
+ ncached += n;
+ ndatum += n;
+}
+
+disint(off, v: int)
+{
+ disflush(DEFW, off, IBY2WD);
+ cache[ncached++] = byte(v >> 24);
+ cache[ncached++] = byte(v >> 16);
+ cache[ncached++] = byte(v >> 8);
+ cache[ncached++] = byte(v);
+ ndatum++;
+}
+
+disbig(off: int, v: big)
+{
+ disflush(DEFL, off, IBY2LG);
+ iv := int(v >> 32);
+ cache[ncached++] = byte(iv >> 24);
+ cache[ncached++] = byte(iv >> 16);
+ cache[ncached++] = byte(iv >> 8);
+ cache[ncached++] = byte(iv);
+ iv = int v;
+ cache[ncached++] = byte(iv >> 24);
+ cache[ncached++] = byte(iv >> 16);
+ cache[ncached++] = byte(iv >> 8);
+ cache[ncached++] = byte(iv);
+ ndatum++;
+}
+
+disreal(off: int, v: real)
+{
+ disflush(DEFF, off, IBY2LG);
+ export_real(cache[ncached:ncached+8], array[] of {v});
+ ncached += IBY2LG;
+ ndatum++;
+}
+
+disstring(offset: int, sym: ref Sym)
+{
+ disflush(-1, -1, 0);
+ d := array of byte sym.name;
+ disdata(DEFS, len d);
+ discon(offset);
+ bout.write(d, len d);
+}
+
+disflush(kind, off, size: int)
+{
+ if(kind != lastkind || off != lastoff){
+ if(lastkind != -1 && ncached){
+ disdata(lastkind, ndatum);
+ discon(startoff);
+ bout.write(cache, ncached);
+ }
+ startoff = off;
+ lastkind = kind;
+ ncached = 0;
+ ndatum = 0;
+ }
+ lastoff = off + size;
+ while(kind >= 0 && ncached + size >= len cache){
+ c := array[ncached + 1024] of byte;
+ c[0:] = cache;
+ cache = c;
+ }
+}
+
+dismode := array[int Aend] of
+{
+ int Aimm => byte AIMM,
+ int Amp => byte AMP,
+ int Ampind => byte(AMP|AIND),
+ int Afp => byte AFP,
+ int Afpind => byte(AFP|AIND),
+ int Apc => byte AIMM,
+ int Adesc => byte AIMM,
+ int Aoff => byte AIMM,
+ int Anoff => byte AIMM,
+ int Aerr => byte AXXX,
+ int Anone => byte AXXX,
+ int Aldt => byte AIMM,
+};
+
+disregmode := array[int Aend] of
+{
+ int Aimm => byte AXIMM,
+ int Amp => byte AXINM,
+ int Ampind => byte AXNON,
+ int Afp => byte AXINF,
+ int Afpind => byte AXNON,
+ int Apc => byte AXIMM,
+ int Adesc => byte AXIMM,
+ int Aoff => byte AXIMM,
+ int Anoff => byte AXIMM,
+ int Aerr => byte AXNON,
+ int Anone => byte AXNON,
+ int Aldt => byte AXIMM,
+};
+
+MAXCON: con 4;
+MAXADDR: con 2*MAXCON;
+MAXINST: con 3*MAXADDR+2;
+NIBUF: con 1024;
+
+ibuf: array of byte;
+nibuf: int;
+
+disinst(in: ref Inst)
+{
+ fileoff := bout.seek(big 0, 1);
+ ibuf = array[NIBUF] of byte;
+ nibuf = 0;
+ for(; in != nil; in = in.next){
+ if(in.op == INOOP)
+ continue;
+ if(nibuf >= NIBUF-MAXINST){
+ bout.write(ibuf, nibuf);
+ nibuf = 0;
+ }
+ ibuf[nibuf++] = byte in.op;
+ o := dismode[int in.sm] << SRC;
+ o |= dismode[int in.dm] << DST;
+ o |= disregmode[int in.mm];
+ ibuf[nibuf++] = o;
+ if(in.mm != Anone)
+ disaddr(in.mm, in.m);
+ if(in.sm != Anone)
+ disaddr(in.sm, in.s);
+ if(in.dm != Anone)
+ disaddr(in.dm, in.d);
+ }
+ if(nibuf > 0)
+ bout.write(ibuf, nibuf);
+ ibuf = nil;
+
+ if(debug['s'])
+ print("%bd instruction bytes start %bd\n", bout.seek(big 0, 1) - fileoff, fileoff);
+}
+
+disaddr(m: byte, a: Addr)
+{
+ val := 0;
+ case int m{
+ int Aimm or
+ int Apc or
+ int Adesc =>
+ val = a.offset;
+ int Aoff =>
+ val = a.decl.iface.offset;
+ int Anoff =>
+ val = -(a.decl.iface.offset+1);
+ int Afp or
+ int Amp or
+ int Aldt =>
+ val = a.reg;
+ int Afpind or
+ int Ampind =>
+ disbcon(a.reg);
+ val = a.offset;
+ }
+ disbcon(val);
+}
+
+disbcon(val: int)
+{
+ if(val >= -64 && val <= 63){
+ ibuf[nibuf++] = byte(val & ~16r80);
+ return;
+ }
+ if(val >= -8192 && val <= 8191){
+ ibuf[nibuf++] = byte(val>>8 & ~16rC0 | 16r80);
+ ibuf[nibuf++] = byte val;
+ return;
+ }
+ if(val < 0 && ((val >> 29) & 7) != 7
+ || val > 0 && (val >> 29) != 0)
+ fatal("overflow in constant 16r"+hex(val, 0));
+ ibuf[nibuf++] = byte(val>>24 | 16rC0);
+ ibuf[nibuf++] = byte(val>>16);
+ ibuf[nibuf++] = byte(val>>8);
+ ibuf[nibuf++] = byte val;
+}
diff --git a/appl/cmd/limbo/disoptab.m b/appl/cmd/limbo/disoptab.m
new file mode 100644
index 00000000..a2e51a8b
--- /dev/null
+++ b/appl/cmd/limbo/disoptab.m
@@ -0,0 +1,355 @@
+movetab:= array [Mend]of
+{
+ Mas => array[Tend] of
+ {
+ Tadt => IMOVM,
+ Tadtpick => IMOVM,
+ Tarray => IMOVP,
+ Tbig => IMOVL,
+ Tbyte => IMOVB,
+ Tchan => IMOVP,
+ Treal => IMOVF,
+ Tint => IMOVW,
+ Tlist => IMOVP,
+ Tmodule => IMOVP,
+ Tref => IMOVP,
+ Tstring => IMOVP,
+ Ttuple => IMOVM,
+ Texception => IMOVM,
+ Tfix => IMOVW,
+ Tpoly => IMOVP,
+
+ Tany => IMOVP,
+
+ * => 0
+ },
+ Mcons => array[Tend] of
+ {
+ Tadt => ICONSM,
+ Tadtpick => 0,
+ Tarray => ICONSP,
+ Tbig => ICONSL,
+ Tbyte => ICONSB,
+ Tchan => ICONSP,
+ Treal => ICONSF,
+ Tint => ICONSW,
+ Tlist => ICONSP,
+ Tmodule => ICONSP,
+ Tref => ICONSP,
+ Tstring => ICONSP,
+ Ttuple => ICONSM,
+ Texception => ICONSM,
+ Tfix => ICONSW,
+ Tpoly => ICONSP,
+
+ Tany => ICONSP,
+
+ * => 0
+ },
+ Mhd => array[Tend] of
+ {
+ Tadt => IHEADM,
+ Tadtpick => 0,
+ Tarray => IHEADP,
+ Tbig => IHEADL,
+ Tbyte => IHEADB,
+ Tchan => IHEADP,
+ Treal => IHEADF,
+ Tint => IHEADW,
+ Tlist => IHEADP,
+ Tmodule => IHEADP,
+ Tref => IHEADP,
+ Tstring => IHEADP,
+ Ttuple => IHEADM,
+ Texception => IHEADM,
+ Tfix => IHEADW,
+ Tpoly => IHEADP,
+
+ Tany => IHEADP,
+
+ * => 0
+ },
+ Mtl => array[Tend] of
+ {
+ Tlist => ITAIL,
+
+ * => 0
+ },
+};
+
+chantab := array[Tend] of
+{
+ Tadt => INEWCM,
+ Tadtpick => 0,
+ Tarray => INEWCP,
+ Tbig => INEWCL,
+ Tbyte => INEWCB,
+ Tchan => INEWCP,
+ Treal => INEWCF,
+ Tint => INEWCW,
+ Tlist => INEWCP,
+ Tmodule => INEWCP,
+ Tref => INEWCP,
+ Tstring => INEWCP,
+ Ttuple => INEWCM,
+ Texception => INEWCM,
+ Tfix => INEWCW,
+ Tpoly => INEWCP,
+
+ Tany => INEWCP,
+
+ * => 0
+};
+
+opind := array[Tend] of
+{
+ Tbyte => 1,
+ Tint => 2,
+ Tbig => 3,
+ Treal => 4,
+ Tstring => 5,
+ Tfix => 6,
+
+ * => 0
+};
+
+disoptab := array[Oend+1] of
+{
+ # opcode default byte word big real string fixed
+ Oadd => array[7] of {0, IADDB, IADDW, IADDL, IADDF, IADDC, IADDW,},
+ Oaddas => array[7] of {0, IADDB, IADDW, IADDL, IADDF, IADDC, IADDW,},
+ Oand => array[7] of {0, IANDB, IANDW, IANDL, 0, 0, 0,},
+ Oandas => array[7] of {0, IANDB, IANDW, IANDL, 0, 0, 0,},
+ Odec => array[7] of {0, ISUBB, ISUBW, ISUBL, ISUBF, 0, ISUBW,},
+ Odiv => array[7] of {0, IDIVB, IDIVW, IDIVL, IDIVF, 0, IDIVX,},
+ Odivas => array[7] of {0, IDIVB, IDIVW, IDIVL, IDIVF, 0, IDIVX,},
+ Oeq => array[7] of {IBEQW, IBEQB, IBEQW, IBEQL, IBEQF, IBEQC, IBEQW,},
+ Oexp => array[7] of {0, 0, IEXPW, IEXPL, IEXPF, 0, 0,},
+ Oexpas => array[7] of {0, 0, IEXPW, IEXPL, IEXPF, 0, 0,},
+ Ogeq => array[7] of {0, IBGEB, IBGEW, IBGEL, IBGEF, IBGEC, IBGEW,},
+ Ogt => array[7] of {0, IBGTB, IBGTW, IBGTL, IBGTF, IBGTC, IBGTW,},
+ Oinc => array[7] of {0, IADDB, IADDW, IADDL, IADDF, 0, IADDW,},
+ Oinds => array[7] of {0, 0, IINDC, 0, 0, 0, 0,},
+ Oindx => array[7] of {0, 0, IINDX, 0, 0, 0, 0,},
+ Olen => array[7] of {ILENA, 0, 0, 0, 0, ILENC, 0,},
+ Oleq => array[7] of {0, IBLEB, IBLEW, IBLEL, IBLEF, IBLEC, IBLEW,},
+ Olsh => array[7] of {0, ISHLB, ISHLW, ISHLL, 0, 0, 0,},
+ Olshas => array[7] of {0, ISHLB, ISHLW, ISHLL, 0, 0, 0,},
+ Olt => array[7] of {0, IBLTB, IBLTW, IBLTL, IBLTF, IBLTC, IBLTW,},
+ Omod => array[7] of {0, IMODB, IMODW, IMODL, 0, 0, 0,},
+ Omodas => array[7] of {0, IMODB, IMODW, IMODL, 0, 0, 0,},
+ Omul => array[7] of {0, IMULB, IMULW, IMULL, IMULF, 0, IMULX,},
+ Omulas => array[7] of {0, IMULB, IMULW, IMULL, IMULF, 0, IMULX,},
+ Oneg => array[7] of {0, 0, 0, 0, INEGF, 0, 0, },
+ Oneq => array[7] of {IBNEW, IBNEB, IBNEW, IBNEL, IBNEF, IBNEC, IBNEW,},
+ Oor => array[7] of {0, IORB, IORW, IORL, 0, 0, 0,},
+ Ooras => array[7] of {0, IORB, IORW, IORL, 0, 0, 0,},
+ Orsh => array[7] of {0, ISHRB, ISHRW, ISHRL, 0, 0, 0,},
+ Orshas => array[7] of {0, ISHRB, ISHRW, ISHRL, 0, 0, 0,},
+ Oslice => array[7] of {ISLICEA,0, 0, 0, 0, ISLICEC, 0,},
+ Osub => array[7] of {0, ISUBB, ISUBW, ISUBL, ISUBF, 0, ISUBW,},
+ Osubas => array[7] of {0, ISUBB, ISUBW, ISUBL, ISUBF, 0, ISUBW,},
+ Oxor => array[7] of {0, IXORB, IXORW, IXORL, 0, 0, 0,},
+ Oxoras => array[7] of {0, IXORB, IXORW, IXORL, 0, 0, 0,},
+};
+
+isbyteinst := array [256] of
+{
+ IMULB => 1,
+ ISUBB => 1,
+ IADDB => 1,
+ IDIVB => 1,
+ IORB => 1,
+ IXORB => 1,
+ ISHLB => 1,
+ ISHRB => 1,
+ IMODB => 1,
+ IANDB => 1,
+ IBEQB => 1,
+ IBNEB => 1,
+ IBLTB => 1,
+ IBLEB => 1,
+ IBGTB => 1,
+ IBGEB => 1,
+
+ * => 0,
+};
+
+instname := array[256] of
+{
+ "nop",
+ "alt",
+ "nbalt",
+ "goto",
+ "call",
+ "frame",
+ "spawn",
+ "runt",
+ "load",
+ "mcall",
+ "mspawn",
+ "mframe",
+ "ret",
+ "jmp",
+ "case",
+ "exit",
+ "new",
+ "newa",
+ "newcb",
+ "newcw",
+ "newcf",
+ "newcp",
+ "newcm",
+ "newcmp",
+ "send",
+ "recv",
+ "consb",
+ "consw",
+ "consp",
+ "consf",
+ "consm",
+ "consmp",
+ "headb",
+ "headw",
+ "headp",
+ "headf",
+ "headm",
+ "headmp",
+ "tail",
+ "lea",
+ "indx",
+ "movp",
+ "movm",
+ "movmp",
+ "movb",
+ "movw",
+ "movf",
+ "cvtbw",
+ "cvtwb",
+ "cvtfw",
+ "cvtwf",
+ "cvtca",
+ "cvtac",
+ "cvtwc",
+ "cvtcw",
+ "cvtfc",
+ "cvtcf",
+ "addb",
+ "addw",
+ "addf",
+ "subb",
+ "subw",
+ "subf",
+ "mulb",
+ "mulw",
+ "mulf",
+ "divb",
+ "divw",
+ "divf",
+ "modw",
+ "modb",
+ "andb",
+ "andw",
+ "orb",
+ "orw",
+ "xorb",
+ "xorw",
+ "shlb",
+ "shlw",
+ "shrb",
+ "shrw",
+ "insc",
+ "indc",
+ "addc",
+ "lenc",
+ "lena",
+ "lenl",
+ "beqb",
+ "bneb",
+ "bltb",
+ "bleb",
+ "bgtb",
+ "bgeb",
+ "beqw",
+ "bnew",
+ "bltw",
+ "blew",
+ "bgtw",
+ "bgew",
+ "beqf",
+ "bnef",
+ "bltf",
+ "blef",
+ "bgtf",
+ "bgef",
+ "beqc",
+ "bnec",
+ "bltc",
+ "blec",
+ "bgtc",
+ "bgec",
+ "slicea",
+ "slicela",
+ "slicec",
+ "indw",
+ "indf",
+ "indb",
+ "negf",
+ "movl",
+ "addl",
+ "subl",
+ "divl",
+ "modl",
+ "mull",
+ "andl",
+ "orl",
+ "xorl",
+ "shll",
+ "shrl",
+ "bnel",
+ "bltl",
+ "blel",
+ "bgtl",
+ "bgel",
+ "beql",
+ "cvtlf",
+ "cvtfl",
+ "cvtlw",
+ "cvtwl",
+ "cvtlc",
+ "cvtcl",
+ "headl",
+ "consl",
+ "newcl",
+ "casec",
+ "indl",
+ "movpc",
+ "tcmp",
+ "mnewz",
+ "cvtrf",
+ "cvtfr",
+ "cvtws",
+ "cvtsw",
+ "lsrw",
+ "lsrl",
+ "eclr",
+ "newz",
+ "newaz",
+ "raise",
+ "casel",
+ "mulx",
+ "divx",
+ "cvtxx",
+ "mulx0",
+ "divx0",
+ "cvtxx0",
+ "mulx1",
+ "divx1",
+ "cvtxx1",
+ "cvtfx",
+ "cvtxf",
+ "expw",
+ "expl",
+ "expf",
+ "self",
+};
diff --git a/appl/cmd/limbo/ecom.b b/appl/cmd/limbo/ecom.b
new file mode 100644
index 00000000..978882ab
--- /dev/null
+++ b/appl/cmd/limbo/ecom.b
@@ -0,0 +1,2345 @@
+maxstack: int; # max size of a stack frame called
+
+precasttab := array[Tend] of array of ref Type;
+
+optabinit()
+{
+ ct := array[Tend] of ref Type;
+ for(i := 0; i < Tend; i++)
+ precasttab[i] = ct;
+ precasttab[Tstring] = array[Tend] of { Tbyte => tint, Tfix => treal, };
+ precasttab[Tbig] = array[Tend] of { Tbyte => tint, Tfix => treal, };
+ precasttab[Treal] = array[Tend] of { Tbyte => tint, };
+ precasttab[Tfix] = array[Tend] of { Tbyte => tint, Tstring => treal, Tbig => treal, };
+ precasttab[Tbyte] = array[Tend] of { Tstring => tint, Tbig => tint, Treal => tint, Tfix => tint, };
+
+ casttab = array[Tend] of { * => array[Tend] of {* => 0}};
+
+ casttab[Tint][Tint] = IMOVW;
+ casttab[Tbig][Tbig] = IMOVL;
+ casttab[Treal][Treal] = IMOVF;
+ casttab[Tbyte][Tbyte] = IMOVB;
+ casttab[Tstring][Tstring] = IMOVP;
+ casttab[Tfix][Tfix] = ICVTXX; # never same type
+
+ casttab[Tint][Tbyte] = ICVTWB;
+ casttab[Tint][Treal] = ICVTWF;
+ casttab[Tint][Tstring] = ICVTWC;
+ casttab[Tint][Tfix] = ICVTXX;
+ casttab[Tbyte][Tint] = ICVTBW;
+ casttab[Treal][Tint] = ICVTFW;
+ casttab[Tstring][Tint] = ICVTCW;
+ casttab[Tfix][Tint] = ICVTXX;
+
+ casttab[Tint][Tbig] = ICVTWL;
+ casttab[Treal][Tbig] = ICVTFL;
+ casttab[Tstring][Tbig] = ICVTCL;
+ casttab[Tbig][Tint] = ICVTLW;
+ casttab[Tbig][Treal] = ICVTLF;
+ casttab[Tbig][Tstring] = ICVTLC;
+
+ casttab[Treal][Tstring] = ICVTFC;
+ casttab[Tstring][Treal] = ICVTCF;
+
+ casttab[Treal][Tfix] = ICVTFX;
+ casttab[Tfix][Treal] = ICVTXF;
+
+ casttab[Tstring][Tarray] = ICVTCA;
+ casttab[Tarray][Tstring] = ICVTAC;
+
+ #
+ # placeholders; fixed in precasttab
+ #
+ casttab[Tbyte][Tstring] = 16rff;
+ casttab[Tstring][Tbyte] = 16rff;
+ casttab[Tbyte][Treal] = 16rff;
+ casttab[Treal][Tbyte] = 16rff;
+ casttab[Tbyte][Tbig] = 16rff;
+ casttab[Tbig][Tbyte] = 16rff;
+ casttab[Tfix][Tbyte] = 16rff;
+ casttab[Tbyte][Tfix] = 16rff;
+ casttab[Tfix][Tbig] = 16rff;
+ casttab[Tbig][Tfix] = 16rff;
+ casttab[Tfix][Tstring] = 16rff;
+ casttab[Tstring][Tfix] = 16rff;
+}
+
+#
+# global variable and constant initialization checking
+#
+vcom(ids: ref Decl): int
+{
+ ok := 1;
+ for(v := ids; v != nil; v = v.next)
+ ok &= varcom(v);
+ for(v = ids; v != nil; v = v.next)
+ v.init = simplify(v.init);
+ return ok;
+}
+
+simplify(n: ref Node): ref Node
+{
+ if(n == nil)
+ return nil;
+ if(debug['F'])
+ print("simplify %s\n", nodeconv(n));
+ n = efold(rewrite(n));
+ if(debug['F'])
+ print("simplified %s\n", nodeconv(n));
+ return n;
+}
+
+isfix(n: ref Node): int
+{
+ if(n.ty.kind == Tint || n.ty.kind == Tfix){
+ if(n.op == Ocast)
+ return n.left.ty.kind == Tint || n.left.ty.kind == Tfix;
+ return 1;
+ }
+ return 0;
+}
+
+#
+# rewrite an expression to make it easiser to compile,
+# or give the correct results
+#
+rewrite(n: ref Node): ref Node
+{
+ v: big;
+ t: ref Type;
+ d: ref Decl;
+ nn: ref Node;
+
+ if(n == nil)
+ return nil;
+
+ left := n.left;
+ right := n.right;
+
+ #
+ # rewrites
+ #
+ case n.op{
+ Oname =>
+ d = n.decl;
+ if(d.importid != nil){
+ left = mkbin(Omdot, dupn(1, n.src, d.eimport), mkdeclname(n.src, d.importid));
+ left.ty = n.ty;
+ return rewrite(left);
+ }
+ if((t = n.ty).kind == Texception){
+ if(int t.cons)
+ fatal("cons in rewrite Oname");
+ n = mkbin(Oadd, n, mkconst(n.src, big(2*IBY2WD)));
+ n = mkunary(Oind, n);
+ n.ty = t;
+ n.left.ty = n.left.left.ty = tint;
+ return rewrite(n);
+ }
+ Odas =>
+ n.op = Oas;
+ return rewrite(n);
+ Oneg =>
+ n.left = rewrite(left);
+ if(n.ty == treal)
+ break;
+ left = n.left;
+ n.right = left;
+ n.left = mkconst(n.src, big 0);
+ n.left.ty = n.ty;
+ n.op = Osub;
+ Ocomp =>
+ v = big 0;
+ v = ~v;
+ n.right = mkconst(n.src, v);
+ n.right.ty = n.ty;
+ n.left = rewrite(left);
+ n.op = Oxor;
+ Oinc or
+ Odec or
+ Opreinc or
+ Opredec =>
+ n.left = rewrite(left);
+ case n.ty.kind{
+ Treal =>
+ n.right = mkrconst(n.src, 1.0);
+ Tint or
+ Tbig or
+ Tbyte or
+ Tfix =>
+ n.right = mkconst(n.src, big 1);
+ n.right.ty = n.ty;
+ * =>
+ fatal("can't rewrite inc/dec "+nodeconv(n));
+ }
+ if(n.op == Opreinc)
+ n.op = Oaddas;
+ else if(n.op == Opredec)
+ n.op = Osubas;
+ Oslice =>
+ if(right.left.op == Onothing)
+ right.left = mkconst(right.left.src, big 0);
+ n.left = rewrite(left);
+ n.right = rewrite(right);
+ Oindex =>
+ n.op = Oindx;
+ n.left = rewrite(left);
+ n.right = rewrite(right);
+ n = mkunary(Oind, n);
+ n.ty = n.left.ty;
+ n.left.ty = tint;
+ Oload =>
+ n.right = mkn(Oname, nil, nil);
+ n.right.src = n.left.src;
+ n.right.decl = n.ty.tof.decl;
+ n.right.ty = n.ty;
+ n.left = rewrite(left);
+ Ocast =>
+ if(left.ty.kind == Texception){
+ n = rewrite(left);
+ break;
+ }
+ n.op = Ocast;
+ t = precasttab[left.ty.kind][n.ty.kind];
+ if(t != nil){
+ n.left = mkunary(Ocast, left);
+ n.left.ty = t;
+ return rewrite(n);
+ }
+ n.left = rewrite(left);
+ Oraise =>
+ if(left.ty == tstring)
+ ;
+ else if(left.ty.cons == byte 0)
+ break;
+ else if(left.op != Ocall || left.left.ty.kind == Tfn){
+ left = mkunary(Ocall, left);
+ left.ty = left.left.ty;
+ }
+ n.left = rewrite(left);
+ Ocall =>
+ t = left.ty;
+ if(t.kind == Tref)
+ t = t.tof;
+ if(t.kind == Tfn){
+ if(left.ty.kind == Tref){ # call by function reference
+ n.left = mkunary(Oind, left);
+ n.left.ty = t;
+ return rewrite(n);
+ }
+ d = nil;
+ if(left.op == Oname)
+ d = left.decl;
+ else if(left.op == Omdot && left.right.op == Odot)
+ d = left.right.right.decl;
+ else if(left.op == Omdot || left.op == Odot)
+ d = left.right.decl;
+ else if(left.op != Oind)
+ fatal("cannot deal with call " + nodeconv(n) + " in rewrite");
+ if(ispoly(d))
+ addfnptrs(d, 0);
+ n.left = rewrite(left);
+ if(right != nil)
+ n.right = rewrite(right);
+ if(d != nil && int d.inline == 1)
+ n = simplify(inline(n));
+ break;
+ }
+ case n.ty.kind{
+ Tref =>
+ n = mkunary(Oref, n);
+ n.ty = n.left.ty;
+ n.left.ty = n.left.ty.tof;
+ n.left.left.ty = n.left.ty;
+ return rewrite(n);
+ Tadt =>
+ n.op = Otuple;
+ n.right = nil;
+ if(n.ty.tags != nil){
+ n.left = nn = mkunary(Oseq, mkconst(n.src, big left.right.decl.tag));
+ if(right != nil){
+ nn.right = right;
+ nn.src.stop = right.src.stop;
+ }
+ n.ty = left.right.decl.ty.tof;
+ }else
+ n.left = right;
+ return rewrite(n);
+ Tadtpick =>
+ n.op = Otuple;
+ n.right = nil;
+ n.left = nn = mkunary(Oseq, mkconst(n.src, big left.right.decl.tag));
+ if(right != nil){
+ nn.right = right;
+ nn.src.stop = right.src.stop;
+ }
+ n.ty = left.right.decl.ty.tof;
+ return rewrite(n);
+ Texception =>
+ if(n.ty.cons == byte 0)
+ return n.left;
+ if(left.op == Omdot){
+ left.right.ty = left.ty;
+ left = left.right;
+ }
+ n.op = Otuple;
+ n.right = nil;
+ n.left = nn = mkunary(Oseq, left.decl.init);
+ nn.right = mkunary(Oseq, mkconst(n.src, big 0));
+ nn.right.right = right;
+ n.ty = mkexbasetype(n.ty);
+ n = mkunary(Oref, n);
+ n.ty = internaltype(mktype(n.src.start, n.src.stop, Tref, t, nil));
+ return rewrite(n);
+ * =>
+ fatal("can't deal with "+nodeconv(n)+" in rewrite/Ocall");
+ }
+ Omdot =>
+ #
+ # what about side effects from left?
+ #
+ d = right.decl;
+ case d.store{
+ Dfn =>
+ n.left = rewrite(left);
+ if(right.op == Odot){
+ n.right = dupn(1, left.src, right.right);
+ n.right.ty = d.ty;
+ }
+ Dconst or
+ Dtag or
+ Dtype =>
+ # handled by fold
+ return n;
+ Dglobal =>
+ right.op = Oconst;
+ right.c = ref Const(big d.offset, 0.);
+ right.ty = tint;
+
+ n.left = left = mkunary(Oind, left);
+ left.ty = tint;
+ n.op = Oadd;
+ n = mkunary(Oind, n);
+ n.ty = n.left.ty;
+ n.left.ty = tint;
+ n.left = rewrite(n.left);
+ return n;
+ Darg =>
+ return n;
+ * =>
+ fatal("can't deal with "+nodeconv(n)+" in rewrite/Omdot");
+ }
+ Odot =>
+ #
+ # what about side effects from left?
+ #
+ d = right.decl;
+ case d.store{
+ Dfn =>
+ if(right.left != nil){
+ n = mkbin(Omdot, dupn(1, left.src, right.left), right);
+ right.left = nil;
+ n.ty = d.ty;
+ return rewrite(n);
+ }
+ if(left.ty.kind == Tpoly){
+ n = mkbin(Omdot, mkdeclname(left.src, d.link), mkdeclname(left.src, d.link.next));
+ n.ty = d.ty;
+ return rewrite(n);
+ }
+ n.op = Oname;
+ n.decl = d;
+ n.right = nil;
+ n.left = nil;
+ return n;
+ Dconst or
+ Dtag or
+ Dtype =>
+ # handled by fold
+ return n;
+ }
+ if(istuple(left))
+ return n; # handled by fold
+ right.op = Oconst;
+ right.c = ref Const(big d.offset, 0.);
+ right.ty = tint;
+
+ if(left.ty.kind != Tref){
+ n.left = mkunary(Oadr, left);
+ n.left.ty = tint;
+ }
+ n.op = Oadd;
+ n = mkunary(Oind, n);
+ n.ty = n.left.ty;
+ n.left.ty = tint;
+ n.left = rewrite(n.left);
+ return n;
+ Oadr =>
+ left = rewrite(left);
+ n.left = left;
+ if(left.op == Oind)
+ return left.left;
+ Otagof =>
+ if(n.decl == nil){
+ n.op = Oind;
+ return rewrite(n);
+ }
+ return n;
+ Omul or
+ Odiv =>
+ left = n.left = rewrite(left);
+ right = n.right = rewrite(right);
+ if(n.ty.kind == Tfix && isfix(left) && isfix(right)){
+ if(left.op == Ocast && tequal(left.ty, n.ty))
+ n.left = left.left;
+ if(right.op == Ocast && tequal(right.ty, n.ty))
+ n.right = right.left;
+ }
+ Oself =>
+ if(newfnptr)
+ return n;
+ if(selfdecl == nil){
+ d = selfdecl = mkids(n.src, enter(".self", 5), tany, nil);
+ installids(Dglobal, d);
+ d.refs++;
+ }
+ nn = mkn(Oload, nil, nil);
+ nn.src = n.src;
+ nn.left = mksconst(n.src, enterstring("$self"));
+ nn.ty = impdecl.ty;
+ usetype(nn.ty);
+ usetype(nn.ty.tof);
+ nn = rewrite(nn);
+ nn.op = Oself;
+ return nn;
+ Ofnptr =>
+ if(n.flags == byte 0){
+ # module
+ if(left == nil)
+ left = mkn(Oself, nil, nil);
+ return rewrite(left);
+ }
+ right.flags = n.flags;
+ n = right;
+ d = n.decl;
+ if(int n.flags == FNPTR2){
+ if(left != nil && left.op != Oname)
+ fatal("not Oname for addiface");
+ if(left == nil){
+ addiface(nil, d);
+ if(newfnptr)
+ n.flags |= byte FNPTRN;
+ }
+ else
+ addiface(left.decl, d);
+ n.ty = tint;
+ return n;
+ }
+ if(int n.flags == FNPTRA){
+ n = mkdeclname(n.src, d.link);
+ n.ty = tany;
+ return n;
+ }
+ if(int n.flags == (FNPTRA|FNPTR2)){
+ n = mkdeclname(n.src, d.link.next);
+ n.ty = tint;
+ return n;
+ }
+ Ochan =>
+ if(left == nil)
+ left = n.left = mkconst(n.src, big 0);
+ n.left = rewrite(left);
+ * =>
+ n.left = rewrite(left);
+ n.right = rewrite(right);
+ }
+
+ return n;
+}
+
+#
+# label a node with sethi-ullman numbers and addressablity
+# genaddr interprets addable to generate operands,
+# so a change here mandates a change there.
+#
+# addressable:
+# const Rconst $value may also be Roff or Rdesc or Rnoff
+# Asmall(local) Rreg value(FP)
+# Asmall(global) Rmreg value(MP)
+# ind(Rareg) Rreg value(FP)
+# ind(Ramreg) Rmreg value(MP)
+# ind(Rreg) Radr *value(FP)
+# ind(Rmreg) Rmadr *value(MP)
+# ind(Raadr) Radr value(value(FP))
+# ind(Ramadr) Rmadr value(value(MP))
+#
+# almost addressable:
+# adr(Rreg) Rareg
+# adr(Rmreg) Ramreg
+# add(const, Rareg) Rareg
+# add(const, Ramreg) Ramreg
+# add(const, Rreg) Raadr
+# add(const, Rmreg) Ramadr
+# add(const, Raadr) Raadr
+# add(const, Ramadr) Ramadr
+# adr(Radr) Raadr
+# adr(Rmadr) Ramadr
+#
+# strangely addressable:
+# fn Rpc
+# mdot(module,exp) Rmpc
+#
+sumark(n: ref Node): ref Node
+{
+ if(n == nil)
+ return nil;
+
+ n.temps = byte 0;
+ n.addable = Rcant;
+
+ left := n.left;
+ right := n.right;
+ if(left != nil){
+ sumark(left);
+ n.temps = left.temps;
+ }
+ if(right != nil){
+ sumark(right);
+ if(right.temps == n.temps)
+ n.temps++;
+ else if(right.temps > n.temps)
+ n.temps = right.temps;
+ }
+
+ case n.op{
+ Oadr =>
+ case int left.addable{
+ int Rreg =>
+ n.addable = Rareg;
+ int Rmreg =>
+ n.addable = Ramreg;
+ int Radr =>
+ n.addable = Raadr;
+ int Rmadr =>
+ n.addable = Ramadr;
+ }
+ Oind =>
+ case int left.addable{
+ int Rreg =>
+ n.addable = Radr;
+ int Rmreg =>
+ n.addable = Rmadr;
+ int Rareg =>
+ n.addable = Rreg;
+ int Ramreg =>
+ n.addable = Rmreg;
+ int Raadr =>
+ n.addable = Radr;
+ int Ramadr =>
+ n.addable = Rmadr;
+ }
+ Oname =>
+ case n.decl.store{
+ Darg or
+ Dlocal =>
+ n.addable = Rreg;
+ Dglobal =>
+ n.addable = Rmreg;
+ if(LDT && n.decl.ty.kind == Tiface)
+ n.addable = Rldt;
+ Dtype =>
+ #
+ # check for inferface to load
+ #
+ if(n.decl.ty.kind == Tmodule)
+ n.addable = Rmreg;
+ Dfn =>
+ if(int n.flags & FNPTR){
+ if(int n.flags == FNPTR2)
+ n.addable = Roff;
+ else if(int n.flags == FNPTR2|FNPTRN)
+ n.addable = Rnoff;
+ }
+ else
+ n.addable = Rpc;
+ * =>
+ fatal("cannot deal with "+declconv(n.decl)+" in Oname in "+nodeconv(n));
+ }
+ Omdot =>
+ n.addable = Rmpc;
+ Oconst =>
+ case n.ty.kind{
+ Tint or
+ Tfix =>
+ v := int n.c.val;
+ if(v < 0 && ((v >> 29) & 7) != 7
+ || v > 0 && (v >> 29) != 0){
+ n.decl = globalconst(n);
+ n.addable = Rmreg;
+ }else
+ n.addable = Rconst;
+ Tbig =>
+ n.decl = globalBconst(n);
+ n.addable = Rmreg;
+ Tbyte =>
+ n.decl = globalbconst(n);
+ n.addable = Rmreg;
+ Treal =>
+ n.decl = globalfconst(n);
+ n.addable = Rmreg;
+ Tstring =>
+ n.decl = globalsconst(n);
+ n.addable = Rmreg;
+ * =>
+ fatal("cannot const in sumark "+typeconv(n.ty));
+ }
+ Oadd =>
+ if(right.addable == Rconst){
+ case int left.addable{
+ int Rareg =>
+ n.addable = Rareg;
+ int Ramreg =>
+ n.addable = Ramreg;
+ int Rreg or
+ int Raadr =>
+ n.addable = Raadr;
+ int Rmreg or
+ int Ramadr =>
+ n.addable = Ramadr;
+ }
+ }
+ }
+ if(n.addable < Rcant)
+ n.temps = byte 0;
+ else if(n.temps == byte 0)
+ n.temps = byte 1;
+ return n;
+}
+
+mktn(t: ref Type): ref Node
+{
+ n := mkn(Oname, nil, nil);
+ usedesc(mktdesc(t));
+ n.ty = t;
+ if(t.decl == nil)
+ fatal("mktn nil decl t "+typeconv(t));
+ n.decl = t.decl;
+ n.addable = Rdesc;
+ return n;
+}
+
+# does a tuple of the form (a, b, ...) form a contiguous block
+# of memory on the stack when offsets are assigned later
+# - only when (a, b, ...) := rhs and none of the names nil
+# can we guarantee this
+#
+tupblk0(n: ref Node, d: ref Decl): (int, ref Decl)
+{
+ ok, nid: int;
+
+ case(n.op){
+ Otuple =>
+ for(n = n.left; n != nil; n = n.right){
+ (ok, d) = tupblk0(n.left, d);
+ if(!ok)
+ return (0, nil);
+ }
+ return (1, d);
+ Oname =>
+ if(n.decl == nildecl)
+ return (0, nil);
+ if(d != nil && d.next != n.decl)
+ return (0, nil);
+ nid = int n.decl.nid;
+ if(d == nil && nid == 1)
+ return (0, nil);
+ if(d != nil && nid != 0)
+ return (0, nil);
+ return (1, n.decl);
+ }
+ return (0, nil);
+}
+
+# could force locals to be next to each other
+# - need to shuffle locals list
+# - later
+#
+tupblk(n: ref Node): ref Node
+{
+ ok: int;
+ d: ref Decl;
+
+ if(n.op != Otuple)
+ return nil;
+ d = nil;
+ (ok, d) = tupblk0(n, d);
+ if(!ok)
+ return nil;
+ while(n.op == Otuple)
+ n = n.left.left;
+ if(n.op != Oname || n.decl.nid == byte 1)
+ fatal("bad tupblk");
+ return n;
+}
+
+# for cprof
+esrc(src: Src, osrc: Src, nto: ref Node): Src
+{
+ if(nto != nil && src.start != 0 && src.stop != 0)
+ return src;
+ return osrc;
+}
+
+#
+# compile an expression with an implicit assignment
+# note: you are not allowed to use nto.src
+#
+# need to think carefully about the types used in moves
+#
+ecom(src: Src, nto, n: ref Node): ref Node
+{
+ tleft, tright, tto, ttn: ref Node;
+ t: ref Type;
+ p: ref Inst;
+
+ if(debug['e']){
+ print("ecom: %s\n", nodeconv(n));
+ if(nto != nil)
+ print("ecom nto: %s\n", nodeconv(nto));
+ }
+
+ if(n.addable < Rcant){
+ #
+ # think carefully about the type used here
+ #
+ if(nto != nil)
+ genmove(src, Mas, n.ty, n, nto);
+ return nto;
+ }
+
+ left := n.left;
+ right := n.right;
+ op := n.op;
+ case op{
+ * =>
+ fatal("can't ecom "+nodeconv(n));
+ return nto;
+ Oif =>
+ p = bcom(left, 1, nil);
+ ecom(right.left.src, nto, right.left);
+ if(right.right != nil){
+ pp := p;
+ p = genrawop(right.left.src, IJMP, nil, nil, nil);
+ patch(pp, nextinst());
+ ecom(right.right.src, nto, right.right);
+ }
+ patch(p, nextinst());
+ Ocomma =>
+ ttn = left.left;
+ ecom(left.src, nil, left);
+ ecom(right.src, nto, right);
+ tfree(ttn);
+ Oname =>
+ if(n.addable == Rpc){
+ if(nto != nil)
+ genmove(src, Mas, n.ty, n, nto);
+ return nto;
+ }
+ fatal("can't ecom "+nodeconv(n));
+ Onothing =>
+ break;
+ Oused =>
+ if(nto != nil)
+ fatal("superflous used "+nodeconv(left)+" nto "+nodeconv(nto));
+ tto = talloc(left.ty, nil);
+ ecom(left.src, tto, left);
+ tfree(tto);
+ Oas =>
+ if(right.ty == tany)
+ right.ty = n.ty;
+ if(left.op == Oname && left.decl.ty == tany){
+ if(nto == nil)
+ nto = tto = talloc(right.ty, nil);
+ left = nto;
+ nto = nil;
+ }
+ if(left.op == Oinds){
+ indsascom(src, nto, n);
+ tfree(tto);
+ break;
+ }
+ if(left.op == Oslice){
+ slicelcom(src, nto, n);
+ tfree(tto);
+ break;
+ }
+
+ if(left.op == Otuple){
+ if(!tupsaliased(right, left)){
+ if((tn := tupblk(left)) != nil){
+ tn.ty = n.ty;
+ ecom(n.right.src, tn, right);
+ if(nto != nil)
+ genmove(src, Mas, n.ty, tn, nto);
+ tfree(tto);
+ break;
+ }
+ if((tn = tupblk(right)) != nil){
+ tn.ty = n.ty;
+ tuplcom(tn, left);
+ if(nto != nil)
+ genmove(src, Mas, n.ty, tn, nto);
+ tfree(tto);
+ break;
+ }
+ if(nto == nil && right.op == Otuple && left.ty.kind != Tadtpick){
+ tuplrcom(right, left);
+ tfree(tto);
+ break;
+ }
+ }
+ if(right.addable >= Ralways
+ || right.op != Oname
+ || tupaliased(right, left)){
+ tright = talloc(n.ty, nil);
+ ecom(n.right.src, tright, right);
+ right = tright;
+ }
+ tuplcom(right, n.left);
+ if(nto != nil)
+ genmove(src, Mas, n.ty, right, nto);
+ tfree(tright);
+ tfree(tto);
+ break;
+ }
+
+ #
+ # check for left/right aliasing and build right into temporary
+ #
+ if(right.op == Otuple){
+ if(!tupsaliased(left, right) && (tn := tupblk(right)) != nil){
+ tn.ty = n.ty;
+ right = tn;
+ }
+ else if(left.op != Oname || tupaliased(left, right))
+ right = ecom(right.src, tright = talloc(right.ty, nil), right);
+ }
+
+ #
+ # think carefully about types here
+ #
+ if(left.addable >= Rcant)
+ (left, tleft) = eacom(left, nto);
+ ecom(n.src, left, right);
+ if(nto != nil)
+ genmove(src, Mas, nto.ty, left, nto);
+ tfree(tleft);
+ tfree(tright);
+ tfree(tto);
+ Ochan =>
+ if(left != nil && left.addable >= Rcant)
+ (left, tleft) = eacom(left, nto);
+ genchan(src, left, n.ty.tof, nto);
+ tfree(tleft);
+ Oinds =>
+ if(right.addable < Ralways){
+ if(left.addable >= Rcant)
+ (left, tleft) = eacom(left, nil);
+ }else if(left.temps <= right.temps){
+ right = ecom(right.src, tright = talloc(right.ty, nil), right);
+ if(left.addable >= Rcant)
+ (left, tleft) = eacom(left, nil);
+ }else{
+ (left, tleft) = eacom(left, nil);
+ right = ecom(right.src, tright = talloc(right.ty, nil), right);
+ }
+ genop(n.src, op, left, right, nto);
+ tfree(tleft);
+ tfree(tright);
+ Osnd =>
+ if(right.addable < Rcant){
+ if(left.addable >= Rcant)
+ (left, tleft) = eacom(left, nto);
+ }else if(left.temps < right.temps){
+ (right, tright) = eacom(right, nto);
+ if(left.addable >= Rcant)
+ (left, tleft) = eacom(left, nil);
+ }else{
+ (left, tleft) = eacom(left, nto);
+ (right, tright) = eacom(right, nil);
+ }
+ p = genrawop(n.src, ISEND, right, nil, left);
+ p.m.offset = n.ty.size; # for optimizer
+ if(nto != nil)
+ genmove(src, Mas, right.ty, right, nto);
+ tfree(tleft);
+ tfree(tright);
+ Orcv =>
+ if(nto == nil){
+ ecom(n.src, tto = talloc(n.ty, nil), n);
+ tfree(tto);
+ return nil;
+ }
+ if(left.addable >= Rcant)
+ (left, tleft) = eacom(left, nto);
+ if(left.ty.kind == Tchan){
+ p = genrawop(src, IRECV, left, nil, nto);
+ p.m.offset = n.ty.size; # for optimizer
+ }else{
+ recvacom(src, nto, n);
+ }
+ tfree(tleft);
+ Ocons =>
+ #
+ # another temp which can go with analysis
+ #
+ if(left.addable >= Rcant)
+ (left, tleft) = eacom(left, nil);
+ if(!sameaddr(right, nto)){
+ ecom(right.src, tto = talloc(n.ty, nto), right);
+ genmove(src, Mcons, left.ty, left, tto);
+ if(!sameaddr(tto, nto))
+ genmove(src, Mas, nto.ty, tto, nto);
+ }else
+ genmove(src, Mcons, left.ty, left, nto);
+ tfree(tleft);
+ tfree(tto);
+ Ohd =>
+ if(left.addable >= Rcant)
+ (left, tleft) = eacom(left, nto);
+ genmove(src, Mhd, nto.ty, left, nto);
+ tfree(tleft);
+ Otl =>
+ if(left.addable >= Rcant)
+ (left, tleft) = eacom(left, nto);
+ genmove(src, Mtl, left.ty, left, nto);
+ tfree(tleft);
+ Otuple =>
+ if((tn := tupblk(n)) != nil){
+ tn.ty = n.ty;
+ genmove(src, Mas, n.ty, tn, nto);
+ break;
+ }
+ tupcom(nto, n);
+ Oadd or
+ Osub or
+ Omul or
+ Odiv or
+ Omod or
+ Oand or
+ Oor or
+ Oxor or
+ Olsh or
+ Orsh or
+ Oexp =>
+ #
+ # check for 2 operand forms
+ #
+ if(sameaddr(nto, left)){
+ if(right.addable >= Rcant)
+ (right, tright) = eacom(right, nto);
+ genop(src, op, right, nil, nto);
+ tfree(tright);
+ break;
+ }
+
+ if(opcommute[op] && sameaddr(nto, right) && n.ty != tstring){
+ if(left.addable >= Rcant)
+ (left, tleft) = eacom(left, nto);
+ genop(src, opcommute[op], left, nil, nto);
+ tfree(tleft);
+ break;
+ }
+
+ if(right.addable < left.addable
+ && opcommute[op]
+ && n.ty != tstring){
+ op = opcommute[op];
+ left = right;
+ right = n.left;
+ }
+ if(left.addable < Ralways){
+ if(right.addable >= Rcant)
+ (right, tright) = eacom(right, nto);
+ }else if(right.temps <= left.temps){
+ left = ecom(left.src, tleft = talloc(left.ty, nto), left);
+ if(right.addable >= Rcant)
+ (right, tright) = eacom(right, nil);
+ }else{
+ (right, tright) = eacom(right, nto);
+ left = ecom(left.src, tleft = talloc(left.ty, nil), left);
+ }
+
+ #
+ # check for 2 operand forms
+ #
+ if(sameaddr(nto, left))
+ genop(src, op, right, nil, nto);
+ else if(opcommute[op] && sameaddr(nto, right) && n.ty != tstring)
+ genop(src, opcommute[op], left, nil, nto);
+ else
+ genop(src, op, right, left, nto);
+ tfree(tleft);
+ tfree(tright);
+ Oaddas or
+ Osubas or
+ Omulas or
+ Odivas or
+ Omodas or
+ Oexpas or
+ Oandas or
+ Ooras or
+ Oxoras or
+ Olshas or
+ Orshas =>
+ if(left.op == Oinds){
+ indsascom(src, nto, n);
+ break;
+ }
+ if(right.addable < Rcant){
+ if(left.addable >= Rcant)
+ (left, tleft) = eacom(left, nto);
+ }else if(left.temps < right.temps){
+ (right, tright) = eacom(right, nto);
+ if(left.addable >= Rcant)
+ (left, tleft) = eacom(left, nil);
+ }else{
+ (left, tleft) = eacom(left, nto);
+ (right, tright) = eacom(right, nil);
+ }
+ genop(n.src, op, right, nil, left);
+ if(nto != nil)
+ genmove(src, Mas, left.ty, left, nto);
+ tfree(tleft);
+ tfree(tright);
+ Olen =>
+ if(left.addable >= Rcant)
+ (left, tleft) = eacom(left, nto);
+ op = -1;
+ t = left.ty;
+ if(t == tstring)
+ op = ILENC;
+ else if(t.kind == Tarray)
+ op = ILENA;
+ else if(t.kind == Tlist)
+ op = ILENL;
+ else
+ fatal("can't len "+nodeconv(n));
+ genrawop(src, op, left, nil, nto);
+ tfree(tleft);
+ Oneg =>
+ if(left.addable >= Rcant)
+ (left, tleft) = eacom(left, nto);
+ genop(n.src, op, left, nil, nto);
+ tfree(tleft);
+ Oinc or
+ Odec =>
+ if(left.op == Oinds){
+ indsascom(src, nto, n);
+ break;
+ }
+ if(left.addable >= Rcant)
+ (left, tleft) = eacom(left, nil);
+ if(nto != nil)
+ genmove(src, Mas, left.ty, left, nto);
+ if(right.addable >= Rcant)
+ fatal("inc/dec amount not addressable: "+nodeconv(n));
+ genop(n.src, op, right, nil, left);
+ tfree(tleft);
+ Ospawn =>
+ if(left.left.op == Oind)
+ fpcall(n.src, op, left, nto);
+ else
+ callcom(n.src, op, left, nto);
+ Oraise =>
+ if(left.addable >= Rcant)
+ (left, tleft) = eacom(left, nil);
+ genrawop(n.src, IRAISE, left, nil, nil);
+ tfree(tleft);
+ Ocall =>
+ if(left.op == Oind)
+ fpcall(esrc(src, n.src, nto), op, n, nto);
+ else
+ callcom(esrc(src, n.src, nto), op, n, nto);
+ Oref =>
+ t = left.ty;
+ if(left.op == Oname && left.decl.store == Dfn || left.op == Omdot && left.right.op == Oname && left.right.decl.store == Dfn){ # create a function reference
+ mod, ind: ref Node;
+
+ d := left.decl;
+ if(left.op == Omdot){
+ d = left.right.decl;
+ mod = left.left;
+ }
+ else if(d.eimport != nil)
+ mod = d.eimport;
+ else{
+ mod = rewrite(mkn(Oself, nil, nil));
+ addiface(nil, d);
+ }
+ sumark(mod);
+ tto = talloc(n.ty, nto);
+ genrawop(src, INEW, mktn(usetype(tfnptr)), nil, tto);
+ tright = ref znode;
+ tright.src = src;
+ tright.op = Oind;
+ tright.left = tto;
+ tright.right = nil;
+ tright.ty = tany;
+ sumark(tright);
+ ecom(src, tright, mod);
+ ind = mkunary(Oind, mkbin(Oadd, dupn(0, src, tto), mkconst(src, big IBY2WD)));
+ ind.ty = ind.left.ty = ind.left.right.ty = tint;
+ tright.op = Oas;
+ tright.left = ind;
+ tright.right = mkdeclname(src, d);
+ tright.ty = tright.right.ty = tint;
+ sumark(tright);
+ if(mod.op == Oself && newfnptr)
+ tright.right.addable = Rnoff;
+ else
+ tright.right.addable = Roff;
+ ecom(src, nil, tright);
+ if(!sameaddr(tto, nto))
+ genmove(src, Mas, n.ty, tto, nto);
+ tfree(tto);
+ break;
+ }
+ if(left.op == Oname && left.decl.store == Dtype){
+ genrawop(src, INEW, mktn(t), nil, nto);
+ break;
+ }
+ if(t.kind == Tadt && t.tags != nil){
+ pickdupcom(src, nto, left);
+ break;
+ }
+
+ tt := t;
+ if(left.op == Oconst && left.decl.store == Dtag)
+ t = left.decl.ty.tof;
+
+ #
+ # could eliminate temp if nto does not occur
+ # in tuple initializer
+ #
+ tto = talloc(n.ty, nto);
+ genrawop(src, INEW, mktn(t), nil, tto);
+ tright = ref znode;
+ tright.op = Oind;
+ tright.left = tto;
+ tright.right = nil;
+ tright.ty = tt;
+ sumark(tright);
+ ecom(src, tright, left);
+ if(!sameaddr(tto, nto))
+ genmove(src, Mas, n.ty, tto, nto);
+ tfree(tto);
+ Oload =>
+ if(left.addable >= Rcant)
+ (left, tleft) = eacom(left, nto);
+ tright = talloc(tint, nil);
+ if(LDT)
+ genrawop(src, ILOAD, left, right, nto);
+ else{
+ genrawop(src, ILEA, right, nil, tright);
+ genrawop(src, ILOAD, left, tright, nto);
+ }
+ tfree(tleft);
+ tfree(tright);
+ Ocast =>
+ if(left.addable >= Rcant)
+ (left, tleft) = eacom(left, nto);
+ t = left.ty;
+ if(t.kind == Tfix || n.ty.kind == Tfix){
+ op = casttab[t.kind][n.ty.kind];
+ if(op == ICVTXX)
+ genfixcastop(src, op, left, nto);
+ else{
+ ttn = sumark(mkrconst(src, scale2(t, n.ty)));
+ genrawop(src, op, left, ttn, nto);
+ }
+ }
+ else
+ genrawop(src, casttab[t.kind][n.ty.kind], left, nil, nto);
+ tfree(tleft);
+ Oarray =>
+ if(left.addable >= Rcant)
+ (left, tleft) = eacom(left, nto);
+ if(arrayz)
+ genrawop(esrc(src, left.src, nto), INEWAZ, left, mktn(n.ty.tof), nto);
+ else
+ genrawop(esrc(src, left.src, nto), INEWA, left, mktn(n.ty.tof), nto);
+ if(right != nil)
+ arraycom(nto, right);
+ tfree(tleft);
+ Oslice =>
+ tn := right.right;
+ right = right.left;
+
+ #
+ # make the left node of the slice directly addressable
+ # therefore, if it's len is taken (via tn),
+ # left's tree won't be rewritten
+ #
+ if(left.addable >= Rcant)
+ (left, tleft) = eacom(left, nil);
+
+ if(tn.op == Onothing){
+ tn = mkn(Olen, left, nil);
+ tn.src = src;
+ tn.ty = tint;
+ sumark(tn);
+ }
+ if(tn.addable < Ralways){
+ if(right.addable >= Rcant)
+ (right, tright) = eacom(right, nil);
+ }else if(right.temps <= tn.temps){
+ tn = ecom(tn.src, ttn = talloc(tn.ty, nil), tn);
+ if(right.addable >= Rcant)
+ (right, tright) = eacom(right, nil);
+ }else{
+ (right, tright) = eacom(right, nil);
+ tn = ecom(tn.src, ttn = talloc(tn.ty, nil), tn);
+ }
+ op = ISLICEA;
+ if(nto.ty == tstring)
+ op = ISLICEC;
+
+ #
+ # overwrite the destination last,
+ # since it might be used in computing the slice bounds
+ #
+ if(!sameaddr(left, nto))
+ ecom(left.src, nto, left);
+
+ genrawop(src, op, right, tn, nto);
+ tfree(tleft);
+ tfree(tright);
+ tfree(ttn);
+ Oindx =>
+ if(right.addable < Rcant){
+ if(left.addable >= Rcant)
+ (left, tleft) = eacom(left, nto);
+ }else if(left.temps < right.temps){
+ (right, tright) = eacom(right, nto);
+ if(left.addable >= Rcant)
+ (left, tleft) = eacom(left, nil);
+ }else{
+ (left, tleft) = eacom(left, nto);
+ (right, tright) = eacom(right, nil);
+ }
+ if(nto.addable >= Ralways)
+ nto = ecom(src, tto = talloc(nto.ty, nil), nto);
+ op = IINDX;
+ case left.ty.tof.size{
+ IBY2LG =>
+ op = IINDL;
+ if(left.ty.tof == treal)
+ op = IINDF;
+ IBY2WD =>
+ op = IINDW;
+ 1 =>
+ op = IINDB;
+ }
+ genrawop(src, op, left, nto, right);
+ if(tleft != nil && tleft.decl != nil)
+ tfreelater(tleft);
+ else
+ tfree(tleft);
+ tfree(tright);
+ tfree(tto);
+ Oind =>
+ (n, tleft) = eacom(n, nto);
+ genmove(src, Mas, n.ty, n, nto);
+ tfree(tleft);
+ Onot or
+ Oandand or
+ Ooror or
+ Oeq or
+ Oneq or
+ Olt or
+ Oleq or
+ Ogt or
+ Ogeq =>
+ p = bcom(n, 1, nil);
+ genmove(src, Mas, tint, sumark(mkconst(src, big 1)), nto);
+ pp := genrawop(src, IJMP, nil, nil, nil);
+ patch(p, nextinst());
+ genmove(src, Mas, tint, sumark(mkconst(src, big 0)), nto);
+ patch(pp, nextinst());
+ Oself =>
+ if(newfnptr){
+ if(nto != nil)
+ genrawop(src, ISELF, nil, nil, nto);
+ break;
+ }
+ tn := sumark(mkdeclname(src, selfdecl));
+ p = genbra(src, Oneq, tn, sumark(mkdeclname(src, nildecl)));
+ n.op = Oload;
+ ecom(src, tn, n);
+ patch(p, nextinst());
+ genmove(src, Mas, n.ty, tn, nto);
+ }
+ return nto;
+}
+
+#
+# compile exp n to yield an addressable expression
+# use reg to build a temporary; if t is a temp, it is usable
+#
+# note that 0adr's are strange as they are only used
+# for calculating the addresses of fields within adt's.
+# therefore an Oind is the parent or grandparent of the Oadr,
+# and we pick off all of the cases where Oadr's argument is not
+# addressable by looking from the Oind.
+#
+eacom(n, t: ref Node): (ref Node, ref Node)
+{
+ reg: ref Node;
+
+ if(n.op == Ocomma){
+ tn := n.left.left;
+ ecom(n.left.src, nil, n.left);
+ nn := eacom(n.right, t);
+ tfree(tn);
+ return nn;
+ }
+
+ if(debug['e'] || debug['E'])
+ print("eacom: %s\n", nodeconv(n));
+
+ left := n.left;
+ if(n.op != Oind){
+ ecom(n.src, reg = talloc(n.ty, t), n);
+ reg.src = n.src;
+ return (reg, reg);
+ }
+
+ if(left.op == Oadd && left.right.op == Oconst){
+ if(left.left.op == Oadr){
+ (left.left.left, reg) = eacom(left.left.left, t);
+ sumark(n);
+ if(n.addable >= Rcant)
+ fatal("eacom can't make node addressable: "+nodeconv(n));
+ return (n, reg);
+ }
+ reg = talloc(left.left.ty, t);
+ ecom(left.left.src, reg, left.left);
+ left.left.decl = reg.decl;
+ left.left.addable = Rreg;
+ left.left = reg;
+ left.addable = Raadr;
+ n.addable = Radr;
+ }else if(left.op == Oadr){
+ reg = talloc(left.left.ty, t);
+ ecom(left.left.src, reg, left.left);
+
+ #
+ # sleaze: treat the temp as the type of the field, not the enclosing structure
+ #
+ reg.ty = n.ty;
+ reg.src = n.src;
+ return (reg, reg);
+ }else{
+ reg = talloc(left.ty, t);
+ ecom(left.src, reg, left);
+ n.left = reg;
+ n.addable = Radr;
+ }
+ return (n, reg);
+}
+
+#
+# compile an assignment to an array slice
+#
+slicelcom(src: Src, nto, n: ref Node): ref Node
+{
+ tleft, tright, tv: ref Node;
+
+ left := n.left.left;
+ right := n.left.right.left;
+ v := n.right;
+ if(right.addable < Ralways){
+ if(left.addable >= Rcant)
+ (left, tleft) = eacom(left, nto);
+ }else if(left.temps <= right.temps){
+ right = ecom(right.src, tright = talloc(right.ty, nto), right);
+ if(left.addable >= Rcant)
+ (left, tleft) = eacom(left, nil);
+ }else{
+ (left, tleft) = eacom(left, nil); # dangle on right and v
+ right = ecom(right.src, tright = talloc(right.ty, nil), right);
+ }
+
+ case n.op{
+ Oas =>
+ if(v.addable >= Rcant)
+ (v, tv) = eacom(v, nil);
+ }
+
+ genrawop(n.src, ISLICELA, v, right, left);
+ if(nto != nil)
+ genmove(src, Mas, n.ty, left, nto);
+ tfree(tleft);
+ tfree(tv);
+ tfree(tright);
+ return nto;
+}
+
+#
+# compile an assignment to a string location
+#
+indsascom(src: Src, nto, n: ref Node): ref Node
+{
+ tleft, tright, tv, tu, u: ref Node;
+
+ left := n.left.left;
+ right := n.left.right;
+ v := n.right;
+ if(right.addable < Ralways){
+ if(left.addable >= Rcant)
+ (left, tleft) = eacom(left, nto);
+ }else if(left.temps <= right.temps){
+ right = ecom(right.src, tright = talloc(right.ty, nto), right);
+ if(left.addable >= Rcant)
+ (left, tleft) = eacom(left, nil);
+ }else{
+ (left, tleft) = eacom(left, nil); # dangle on right and v
+ right = ecom(right.src, tright = talloc(right.ty, nil), right);
+ }
+
+ case n.op{
+ Oas =>
+ if(v.addable >= Rcant)
+ (v, tv) = eacom(v, nil);
+ Oinc or
+ Odec =>
+ if(v.addable >= Rcant)
+ fatal("inc/dec amount not addable");
+ u = tu = talloc(tint, nil);
+ genop(n.left.src, Oinds, left, right, u);
+ if(nto != nil)
+ genmove(src, Mas, n.ty, u, nto);
+ nto = nil;
+ genop(n.src, n.op, v, nil, u);
+ v = u;
+ Oaddas or
+ Osubas or
+ Omulas or
+ Odivas or
+ Omodas or
+ Oexpas or
+ Oandas or
+ Ooras or
+ Oxoras or
+ Olshas or
+ Orshas =>
+ if(v.addable >= Rcant)
+ (v, tv) = eacom(v, nil);
+ u = tu = talloc(tint, nil);
+ genop(n.left.src, Oinds, left, right, u);
+ genop(n.src, n.op, v, nil, u);
+ v = u;
+ }
+
+ genrawop(n.src, IINSC, v, right, left);
+ tfree(tleft);
+ tfree(tv);
+ tfree(tright);
+ tfree(tu);
+ if(nto != nil)
+ genmove(src, Mas, n.ty, v, nto);
+ return nto;
+}
+
+callcom(src: Src, op: int, n, ret: ref Node)
+{
+ tmod, tind: ref Node;
+ callee: ref Decl;
+
+ args := n.right;
+ nfn := n.left;
+ case(nfn.op){
+ Odot =>
+ callee = nfn.right.decl;
+ nfn.addable = Rpc;
+ Omdot =>
+ callee = nfn.right.decl;
+ Oname =>
+ callee = nfn.decl;
+ * =>
+ callee = nil;
+ fatal("bad call op in callcom");
+ }
+ if(nfn.addable != Rpc && nfn.addable != Rmpc)
+ fatal("can't gen call addresses");
+ if(nfn.ty.tof != tnone && ret == nil){
+ ecom(src, tmod = talloc(nfn.ty.tof, nil), n);
+ tfree(tmod);
+ return;
+ }
+ if(ispoly(callee))
+ addfnptrs(callee, 0);
+ if(nfn.ty.varargs != byte 0){
+ d := dupdecl(nfn.right.decl);
+ nfn.decl = d;
+ d.desc = gendesc(d, idoffsets(nfn.ty.ids, MaxTemp, MaxAlign), nfn.ty.ids);
+ }
+
+ frame := talloc(tint, nil);
+
+ mod := nfn.left;
+ ind := nfn.right;
+ if(nfn.addable == Rmpc){
+ if(mod.addable >= Rcant)
+ (mod, tmod) = eacom(mod, nil); # dangle always
+ if(ind.op != Oname && ind.addable >= Ralways){
+ tind = talloc(ind.ty, nil);
+ ecom(ind.src, tind, ind);
+ ind = tind;
+ }
+ else if(ind.decl != nil && ind.decl.store != Darg)
+ ind.addable = Roff;
+ }
+
+ #
+ # stop nested uncalled frames
+ # otherwise exception handling very complicated
+ #
+ for(a := args; a != nil; a = a.right){
+ if(hascall(a.left)){
+ tn := talloc(a.left.ty, nil);
+ ecom(a.left.src, tn, a.left);
+ a.left = tn;
+ tn.flags |= byte TEMP;
+ }
+ }
+
+ #
+ # allocate the frame
+ #
+ if(nfn.addable == Rmpc && nfn.ty.varargs == byte 0){
+ genrawop(src, IMFRAME, mod, ind, frame);
+ }else if(nfn.op == Odot){
+ genrawop(src, IFRAME, nfn.left, nil, frame);
+ }else{
+ in := genrawop(src, IFRAME, nil, nil, frame);
+ in.sm = Adesc;
+ in.s.decl = nfn.decl;
+ }
+
+ #
+ # build a fake node for the argument area
+ #
+ toff := ref znode;
+ tadd := ref znode;
+ pass := ref znode;
+ toff.op = Oconst;
+ toff.c = ref Const(big 0, 0.0); # jrf - added initialization
+ toff.addable = Rconst;
+ toff.ty = tint;
+ tadd.op = Oadd;
+ tadd.addable = Raadr;
+ tadd.left = frame;
+ tadd.right = toff;
+ tadd.ty = tint;
+ pass.op = Oind;
+ pass.addable = Radr;
+ pass.left = tadd;
+
+ #
+ # compile all the args
+ #
+ d := nfn.ty.ids;
+ off := 0;
+ for(a = args; a != nil; a = a.right){
+ off = d.offset;
+ toff.c.val = big off;
+ if(d.ty.kind == Tpoly)
+ pass.ty = a.left.ty;
+ else
+ pass.ty = d.ty;
+ ecom(a.left.src, pass, a.left);
+ d = d.next;
+ if(int a.left.flags & TEMP)
+ tfree(a.left);
+ }
+ if(off > maxstack)
+ maxstack = off;
+
+ #
+ # pass return value
+ #
+ if(ret != nil){
+ toff.c.val = big(REGRET*IBY2WD);
+ pass.ty = nfn.ty.tof;
+ p := genrawop(src, ILEA, ret, nil, pass);
+ p.m.offset = ret.ty.size; # for optimizer
+ }
+
+ #
+ # call it
+ #
+ iop: int;
+ if(nfn.addable == Rmpc){
+ iop = IMCALL;
+ if(op == Ospawn)
+ iop = IMSPAWN;
+ genrawop(src, iop, frame, ind, mod);
+ tfree(tmod);
+ tfree(tind);
+ }else if(nfn.op == Odot){
+ iop = ICALL;
+ if(op == Ospawn)
+ iop = ISPAWN;
+ genrawop(src, iop, frame, nil, nfn.right);
+ }else{
+ iop = ICALL;
+ if(op == Ospawn)
+ iop = ISPAWN;
+ in := genrawop(src, iop, frame, nil, nil);
+ in.d.decl = nfn.decl;
+ in.dm = Apc;
+ }
+ tfree(frame);
+}
+
+#
+# initialization code for arrays
+# a must be addressable (< Rcant)
+#
+arraycom(a, elems: ref Node)
+{
+ top, out: ref Inst;
+ ri, n, wild: ref Node;
+
+ if(debug['A'])
+ print("arraycom: %s %s\n", nodeconv(a), nodeconv(elems));
+
+ # c := elems.ty.cse;
+ # don't use c.wild in case we've been inlined
+ wild = nil;
+ for(e := elems; e != nil; e = e.right)
+ for(q := e.left.left; q != nil; q = q.right)
+ if(q.left.op == Owild)
+ wild = e.left;
+ if(wild != nil)
+ arraydefault(a, wild.right);
+
+ tindex := ref znode;
+ fake := ref znode;
+ tmp := talloc(tint, nil);
+ tindex.op = Oindx;
+ tindex.addable = Rcant;
+ tindex.left = a;
+ tindex.right = nil;
+ tindex.ty = tint;
+ fake.op = Oind;
+ fake.addable = Radr;
+ fake.left = tmp;
+ fake.ty = a.ty.tof;
+
+ for(e = elems; e != nil; e = e.right){
+ #
+ # just duplicate the initializer for Oor
+ #
+ for(q = e.left.left; q != nil; q = q.right){
+ if(q.left.op == Owild)
+ continue;
+
+ body := e.left.right;
+ if(q.right != nil)
+ body = dupn(0, nosrc, body);
+ top = nil;
+ out = nil;
+ ri = nil;
+ if(q.left.op == Orange){
+ #
+ # for(i := q.left.left; i <= q.left.right; i++)
+ #
+ ri = talloc(tint, nil);
+ ri.src = q.left.src;
+ ecom(q.left.src, ri, q.left.left);
+
+ # i <= q.left.right;
+ n = mkn(Oleq, ri, q.left.right);
+ n.src = q.left.src;
+ n.ty = tint;
+ top = nextinst();
+ out = bcom(n, 1, nil);
+
+ tindex.right = ri;
+ }else{
+ tindex.right = q.left;
+ }
+
+ tindex.addable = Rcant;
+ tindex.src = q.left.src;
+ ecom(tindex.src, tmp, tindex);
+
+ ecom(body.src, fake, body);
+
+ if(q.left.op == Orange){
+ # i++
+ n = mkbin(Oinc, ri, sumark(mkconst(ri.src, big 1)));
+ n.ty = tint;
+ n.addable = Rcant;
+ ecom(n.src, nil, n);
+
+ # jump to test
+ patch(genrawop(q.left.src, IJMP, nil, nil, nil), top);
+ patch(out, nextinst());
+ tfree(ri);
+ }
+ }
+ }
+ tfree(tmp);
+}
+
+#
+# default initialization code for arrays.
+# compiles to
+# n = len a;
+# while(n){
+# n--;
+# a[n] = elem;
+# }
+#
+arraydefault(a, elem: ref Node)
+{
+ e: ref Node;
+
+ if(debug['A'])
+ print("arraydefault: %s %s\n", nodeconv(a), nodeconv(elem));
+
+ t := mkn(Olen, a, nil);
+ t.src = elem.src;
+ t.ty = tint;
+ t.addable = Rcant;
+ n := talloc(tint, nil);
+ n.src = elem.src;
+ ecom(t.src, n, t);
+
+ top := nextinst();
+ out := bcom(n, 1, nil);
+
+ t = mkbin(Odec, n, sumark(mkconst(elem.src, big 1)));
+ t.ty = tint;
+ t.addable = Rcant;
+ ecom(t.src, nil, t);
+
+ if(elem.addable >= Rcant)
+ (elem, e) = eacom(elem, nil);
+
+ t = mkn(Oindx, a, n);
+ t.src = elem.src;
+ t = mkbin(Oas, mkunary(Oind, t), elem);
+ t.ty = elem.ty;
+ t.left.ty = elem.ty;
+ t.left.left.ty = tint;
+ sumark(t);
+ ecom(t.src, nil, t);
+
+ patch(genrawop(t.src, IJMP, nil, nil, nil), top);
+
+ tfree(n);
+ tfree(e);
+ patch(out, nextinst());
+}
+
+tupcom(nto, n: ref Node)
+{
+ if(debug['Y'])
+ print("tupcom %s\nto %s\n", nodeconv(n), nodeconv(nto));
+
+ #
+ # build a fake node for the tuple
+ #
+ toff := ref znode;
+ tadd := ref znode;
+ fake := ref znode;
+ tadr := ref znode;
+ toff.op = Oconst;
+ toff.c = ref Const(big 0, 0.0); # no val => may get fatal error below (jrf)
+ toff.ty = tint;
+ tadr.op = Oadr;
+ tadr.left = nto;
+ tadr.ty = tint;
+ tadd.op = Oadd;
+ tadd.left = tadr;
+ tadd.right = toff;
+ tadd.ty = tint;
+ fake.op = Oind;
+ fake.left = tadd;
+ sumark(fake);
+ if(fake.addable >= Rcant)
+ fatal("tupcom: bad value exp "+nodeconv(fake));
+
+ #
+ # compile all the exps
+ #
+ d := n.ty.ids;
+ for(e := n.left; e != nil; e = e.right){
+ toff.c.val = big d.offset;
+ fake.ty = d.ty;
+ ecom(e.left.src, fake, e.left);
+ d = d.next;
+ }
+}
+
+tuplcom(n, nto: ref Node)
+{
+ if(debug['Y'])
+ print("tuplcom %s\nto %s\n", nodeconv(n), nodeconv(nto));
+
+ #
+ # build a fake node for the tuple
+ #
+ toff := ref znode;
+ tadd := ref znode;
+ fake := ref znode;
+ tadr := ref znode;
+ toff.op = Oconst;
+ toff.c = ref Const(big 0, 0.0); # no val => may get fatal error below (jrf)
+ toff.ty = tint;
+ tadr.op = Oadr;
+ tadr.left = n;
+ tadr.ty = tint;
+ tadd.op = Oadd;
+ tadd.left = tadr;
+ tadd.right = toff;
+ tadd.ty = tint;
+ fake.op = Oind;
+ fake.left = tadd;
+ sumark(fake);
+ if(fake.addable >= Rcant)
+ fatal("tuplcom: bad value exp for "+nodeconv(fake));
+
+ #
+ # compile all the exps
+ #
+ tas := ref znode;
+ d := nto.ty.ids;
+ if(nto.ty.kind == Tadtpick)
+ d = nto.ty.tof.ids.next;
+ for(e := nto.left; e != nil; e = e.right){
+ as := e.left;
+ if(as.op != Oname || as.decl != nildecl){
+ toff.c.val = big d.offset;
+ fake.ty = d.ty;
+ fake.src = as.src;
+ if(as.addable < Rcant)
+ genmove(as.src, Mas, d.ty, fake, as);
+ else{
+ tas.op = Oas;
+ tas.ty = d.ty;
+ tas.src = as.src;
+ tas.left = as;
+ tas.right = fake;
+ tas.addable = Rcant;
+ ecom(as.src, nil, tas);
+ }
+ }
+ d = d.next;
+ }
+}
+
+tuplrcom(n: ref Node, nto: ref Node)
+{
+ s, d, tas: ref Node;
+ de: ref Decl;
+
+ tas = ref znode;
+ de = nto.ty.ids;
+ for((s, d) = (n.left, nto.left); s != nil && d != nil; (s, d) = (s.right, d.right)){
+ if(d.left.op != Oname || d.left.decl != nildecl){
+ tas.op = Oas;
+ tas.ty = de.ty;
+ tas.src = s.left.src;
+ tas.left = d.left;
+ tas.right = s.left;
+ sumark(tas);
+ ecom(tas.src, nil, tas);
+ }
+ de = de.next;
+ }
+ if(s != nil || d != nil)
+ fatal("tuplrcom");
+}
+
+#
+# boolean compiler
+# fall through when condition == true
+#
+bcom(n: ref Node, true: int, b: ref Inst): ref Inst
+{
+ tleft, tright: ref Node;
+
+ if(n.op == Ocomma){
+ tn := n.left.left;
+ ecom(n.left.src, nil, n.left);
+ b = bcom(n.right, true, b);
+ tfree(tn);
+ return b;
+ }
+
+ if(debug['b'])
+ print("bcom %s %d\n", nodeconv(n), true);
+
+ left := n.left;
+ right := n.right;
+ op := n.op;
+ case op{
+ Onothing =>
+ return b;
+ Onot =>
+ return bcom(n.left, !true, b);
+ Oandand =>
+ if(!true)
+ return oror(n, true, b);
+ return andand(n, true, b);
+ Ooror =>
+ if(!true)
+ return andand(n, true, b);
+ return oror(n, true, b);
+ Ogt or
+ Ogeq or
+ Oneq or
+ Oeq or
+ Olt or
+ Oleq =>
+ break;
+ * =>
+ if(n.ty.kind == Tint){
+ right = mkconst(n.src, big 0);
+ right.addable = Rconst;
+ left = n;
+ op = Oneq;
+ break;
+ }
+ fatal("can't bcom "+nodeconv(n));
+ return b;
+ }
+
+ if(true)
+ op = oprelinvert[op];
+
+ if(left.addable < right.addable){
+ t := left;
+ left = right;
+ right = t;
+ op = opcommute[op];
+ }
+
+ if(right.addable < Ralways){
+ if(left.addable >= Rcant)
+ (left, tleft) = eacom(left, nil);
+ }else if(left.temps <= right.temps){
+ right = ecom(right.src, tright = talloc(right.ty, nil), right);
+ if(left.addable >= Rcant)
+ (left, tleft) = eacom(left, nil);
+ }else{
+ (left, tleft) = eacom(left, nil);
+ right = ecom(right.src, tright = talloc(right.ty, nil), right);
+ }
+ bb := genbra(n.src, op, left, right);
+ bb.branch = b;
+ tfree(tleft);
+ tfree(tright);
+ return bb;
+}
+
+andand(n: ref Node, true: int, b: ref Inst): ref Inst
+{
+ if(debug['b'])
+ print("andand %s\n", nodeconv(n));
+ b = bcom(n.left, true, b);
+ b = bcom(n.right, true, b);
+ return b;
+}
+
+oror(n: ref Node, true: int, b: ref Inst): ref Inst
+{
+ if(debug['b'])
+ print("oror %s\n", nodeconv(n));
+ bb := bcom(n.left, !true, nil);
+ b = bcom(n.right, true, b);
+ patch(bb, nextinst());
+ return b;
+}
+
+#
+# generate code for a recva expression
+# this is just a hacked up small alt
+#
+recvacom(src: Src, nto, n: ref Node)
+{
+ p: ref Inst;
+
+ left := n.left;
+
+ labs := array[1] of Label;
+ labs[0].isptr = left.addable >= Rcant;
+ c := ref Case;
+ c.nlab = 1;
+ c.nsnd = 0;
+ c.offset = 0;
+ c.labs = labs;
+ talt := mktalt(c);
+
+ which := talloc(tint, nil);
+ tab := talloc(talt, nil);
+
+ #
+ # build the node for the address of each channel,
+ # the values to send, and the storage for values received
+ #
+ off := ref znode;
+ adr := ref znode;
+ add := ref znode;
+ slot := ref znode;
+ off.op = Oconst;
+ off.c = ref Const(big 0, 0.0); # jrf - added initialization
+ off.ty = tint;
+ off.addable = Rconst;
+ adr.op = Oadr;
+ adr.left = tab;
+ adr.ty = tint;
+ add.op = Oadd;
+ add.left = adr;
+ add.right = off;
+ add.ty = tint;
+ slot.op = Oind;
+ slot.left = add;
+ sumark(slot);
+
+ #
+ # gen the channel
+ # this sleaze is lying to the garbage collector
+ #
+ off.c.val = big(2*IBY2WD);
+ if(left.addable < Rcant)
+ genmove(src, Mas, tint, left, slot);
+ else{
+ slot.ty = left.ty;
+ ecom(src, slot, left);
+ slot.ty = nil;
+ }
+
+ #
+ # gen the value
+ #
+ off.c.val += big IBY2WD;
+ p = genrawop(left.src, ILEA, nto, nil, slot);
+ p.m.offset = nto.ty.size; # for optimizer
+
+ #
+ # number of senders and receivers
+ #
+ off.c.val = big 0;
+ genmove(src, Mas, tint, sumark(mkconst(src, big 0)), slot);
+ off.c.val += big IBY2WD;
+ genmove(src, Mas, tint, sumark(mkconst(src, big 1)), slot);
+ off.c.val += big IBY2WD;
+
+ p = genrawop(src, IALT, tab, nil, which);
+ p.m.offset = talt.size; # for optimizer
+ tfree(which);
+ tfree(tab);
+}
+
+#
+# generate code to duplicate an adt with pick fields
+# this is just a hacked up small pick
+# n is Oind(exp)
+#
+pickdupcom(src: Src, nto, n: ref Node)
+{
+ jmps: ref Inst;
+
+ if(n.op != Oind)
+ fatal("pickdupcom not Oind: " + nodeconv(n));
+
+ t := n.ty;
+ nlab := t.decl.tag;
+
+ #
+ # generate global which has case labels
+ #
+ d := mkids(src, enter(".c"+string nlabel++, 0), mktype(src.start, src.stop, Tcase, nil, nil), nil);
+ d.init = mkdeclname(src, d);
+
+ clab := ref znode;
+ clab.addable = Rmreg;
+ clab.left = nil;
+ clab.right = nil;
+ clab.op = Oname;
+ clab.ty = d.ty;
+ clab.decl = d;
+
+ #
+ # generate a temp to hold the real value
+ # then generate a case on the tag
+ #
+ orig := n.left;
+ tmp := talloc(orig.ty, nil);
+ ecom(src, tmp, orig);
+ orig = mkunary(Oind, tmp);
+ orig.ty = tint;
+ sumark(orig);
+
+ dest := mkunary(Oind, nto);
+ dest.ty = nto.ty.tof;
+ sumark(dest);
+
+ genrawop(src, ICASE, orig, nil, clab);
+
+ labs := array[nlab] of Label;
+
+ i := 0;
+ jmps = nil;
+ for(tg := t.tags; tg != nil; tg = tg.next){
+ stg := tg;
+ for(; tg.next != nil; tg = tg.next)
+ if(stg.ty != tg.next.ty)
+ break;
+ start := sumark(simplify(mkdeclname(src, stg)));
+ stop := start;
+ node := start;
+ if(stg != tg){
+ stop = sumark(simplify(mkdeclname(src, tg)));
+ node = mkbin(Orange, start, stop);
+ }
+
+ labs[i].start = start;
+ labs[i].stop = stop;
+ labs[i].node = node;
+ labs[i++].inst = nextinst();
+
+ genrawop(src, INEW, mktn(tg.ty.tof), nil, nto);
+ genmove(src, Mas, tg.ty.tof, orig, dest);
+
+ j := genrawop(src, IJMP, nil, nil, nil);
+ j.branch = jmps;
+ jmps = j;
+ }
+
+ #
+ # this should really be a runtime error
+ #
+ wild := genrawop(src, IJMP, nil, nil, nil);
+ patch(wild, wild);
+
+ patch(jmps, nextinst());
+ tfree(tmp);
+
+ if(i > nlab)
+ fatal("overflowed label tab for pickdupcom");
+
+ c := ref Case;
+ c.nlab = i;
+ c.nsnd = 0;
+ c.labs = labs;
+ c.iwild = wild;
+
+ d.ty.cse = c;
+ usetype(d.ty);
+ installids(Dglobal, d);
+}
+
+#
+# see if name n occurs anywhere in e
+#
+tupaliased(n, e: ref Node): int
+{
+ for(;;){
+ if(e == nil)
+ return 0;
+ if(e.op == Oname && e.decl == n.decl)
+ return 1;
+ if(tupaliased(n, e.left))
+ return 1;
+ e = e.right;
+ }
+ return 0;
+}
+
+#
+# see if any name in n occurs anywere in e
+#
+tupsaliased(n, e: ref Node): int
+{
+ for(;;){
+ if(n == nil)
+ return 0;
+ if(n.op == Oname && tupaliased(n, e))
+ return 1;
+ if(tupsaliased(n.left, e))
+ return 1;
+ n = n.right;
+ }
+ return 0;
+}
+
+#
+# put unaddressable constants in the global data area
+#
+globalconst(n: ref Node): ref Decl
+{
+ s := enter(".i." + hex(int n.c.val, 8), 0);
+ d := s.decl;
+ if(d == nil){
+ d = mkids(n.src, s, tint, nil);
+ installids(Dglobal, d);
+ d.init = n;
+ d.refs++;
+ }
+ return d;
+}
+
+globalBconst(n: ref Node): ref Decl
+{
+ s := enter(".B." + bhex(n.c.val, 16), 0);
+ d := s.decl;
+ if(d == nil){
+ d = mkids(n.src, s, tbig, nil);
+ installids(Dglobal, d);
+ d.init = n;
+ d.refs++;
+ }
+ return d;
+}
+
+globalbconst(n: ref Node): ref Decl
+{
+ s := enter(".b." + hex(int n.c.val & 16rff, 2), 0);
+ d := s.decl;
+ if(d == nil){
+ d = mkids(n.src, s, tbyte, nil);
+ installids(Dglobal, d);
+ d.init = n;
+ d.refs++;
+ }
+ return d;
+}
+
+globalfconst(n: ref Node): ref Decl
+{
+ ba := array[8] of byte;
+ export_real(ba, array[] of {n.c.rval});
+ fs := ".f.";
+ for(i := 0; i < 8; i++)
+ fs += hex(int ba[i], 2);
+ if(fs != ".f." + bhex(math->realbits64(n.c.rval), 16))
+ fatal("bad globalfconst number");
+ s := enter(fs, 0);
+ d := s.decl;
+ if(d == nil){
+ d = mkids(n.src, s, treal, nil);
+ installids(Dglobal, d);
+ d.init = n;
+ d.refs++;
+ }
+ return d;
+}
+
+globalsconst(n: ref Node): ref Decl
+{
+ s := n.decl.sym;
+ n.decl = nil;
+ d := s.decl;
+ if(d == nil){
+ d = mkids(n.src, s, tstring, nil);
+ installids(Dglobal, d);
+ d.init = n;
+ }
+ d.refs++;
+ n.decl = d;
+ return d;
+}
+
+#
+# make a global of type t
+# used to make initialized data
+#
+globalztup(t: ref Type): ref Decl
+{
+ z := ".z." + string t.size + ".";
+ desc := t.decl.desc;
+ for(i := 0; i < desc.nmap; i++)
+ z += hex(int desc.map[i], 2);
+ s := enter(z, 0);
+ d := s.decl;
+ if(d == nil){
+ d = mkids(t.src, s, t, nil);
+ installids(Dglobal, d);
+ d.init = nil;
+ }
+ d.refs++;
+ return d;
+}
+
+subst(d: ref Decl, e: ref Node, n: ref Node): ref Node
+{
+ if(n == nil)
+ return nil;
+ if(n.op == Oname){
+ if(d == n.decl){
+ n = dupn(0, nosrc, e);
+ n.ty = d.ty;
+ }
+ return n;
+ }
+ n.left = subst(d, e, n.left);
+ n.right = subst(d, e, n.right);
+ return n;
+}
+
+inline(n: ref Node): ref Node
+{
+ e, tn: ref Node;
+ t: ref Type;
+ d: ref Decl;
+
+if(debug['z']) sys->print("inline1: %s\n", nodeconv(n));
+ if(n.left.op == Oname)
+ d = n.left.decl;
+ else
+ d = n.left.right.decl;
+ e = d.init;
+ t = e.ty;
+ e = dupn(1, n.src, e.right.left.left);
+ n = n.right;
+ for(d = t.ids; d != nil && n != nil; d = d.next){
+ if(hasside(n.left, 0) && occurs(d, e) != 1){
+ tn = talloc(d.ty, nil);
+ e = mkbin(Ocomma, mkbin(Oas, tn, n.left), subst(d, tn, e));
+ e.ty = e.right.ty;
+ e.left.ty = d.ty;
+ }
+ else
+ e = subst(d, n.left, e);
+ n = n.right;
+ }
+ if(d != nil || n != nil)
+ fatal("bad arg match in inline()");
+if(debug['z']) sys->print("inline2: %s\n", nodeconv(e));
+ return e;
+}
+
+fpcall(src: Src, op: int, n: ref Node, ret: ref Node)
+{
+ tp, e, mod, ind: ref Node;
+
+ e = n.left.left;
+ if(e.addable >= Rcant)
+ (e, tp) = eacom(e, nil);
+ mod = mkunary(Oind, e);
+ ind = mkunary(Oind, mkbin(Oadd, dupn(0, src, e), mkconst(src, big IBY2WD)));
+ n.left = mkbin(Omdot, mod, ind);
+ n.left.ty = e.ty.tof;
+ mod.ty = ind.ty = ind.left.ty = ind.left.right.ty = tint;
+ sumark(n);
+ callcom(src, op, n, ret);
+ tfree(tp);
+}
diff --git a/appl/cmd/limbo/gen.b b/appl/cmd/limbo/gen.b
new file mode 100644
index 00000000..062980fb
--- /dev/null
+++ b/appl/cmd/limbo/gen.b
@@ -0,0 +1,1012 @@
+ blocks: int; # nesting of blocks while generating code
+ zinst: Inst;
+ firstinst: ref Inst;
+ lastinst: ref Inst;
+
+include "disoptab.m";
+
+addrmode := array[int Rend] of
+{
+ int Rreg => Afp,
+ int Rmreg => Amp,
+ int Roff => Aoff,
+ int Rnoff => Anoff,
+ int Rdesc => Adesc,
+ int Rdescp => Adesc,
+ int Rconst => Aimm,
+ int Radr => Afpind,
+ int Rmadr => Ampind,
+ int Rpc => Apc,
+ int Rldt => Aldt,
+ * => Aerr,
+};
+
+wtemp: ref Decl;
+bigtemp: ref Decl;
+ntemp: int;
+retnode: ref Node;
+nilnode: ref Node;
+
+blockstack: array of int;
+blockdep: int;
+nblocks: int;
+ntoz: ref Node;
+
+#znode: Node;
+
+genstart()
+{
+ d := mkdecl(nosrc, Dlocal, tint);
+ d.sym = enter(".ret", 0);
+ d.offset = IBY2WD * REGRET;
+
+ retnode = ref znode;
+ retnode.op = Oname;
+ retnode.addable = Rreg;
+ retnode.decl = d;
+ retnode.ty = tint;
+
+ zinst.op = INOP;
+ zinst.sm = Anone;
+ zinst.dm = Anone;
+ zinst.mm = Anone;
+
+ firstinst = ref zinst;
+ lastinst = firstinst;
+
+ nilnode = ref znode;
+ nilnode.op = Oname;
+ nilnode.addable = Rmreg;
+ nilnode.decl = nildecl;
+ nilnode.ty = nildecl.ty;
+
+ blocks = -1;
+ blockdep = 0;
+ nblocks = 0;
+}
+
+#
+# manage nested control flow blocks
+#
+pushblock(): int
+{
+ if(blockdep >= len blockstack){
+ bs := array[blockdep + 32] of int;
+ bs[0:] = blockstack;
+ blockstack = bs;
+ }
+ blockstack[blockdep++] = blocks;
+ return blocks = nblocks++;
+}
+
+repushblock(b: int)
+{
+ blockstack[blockdep++] = blocks;
+ blocks = b;
+}
+
+popblock()
+{
+ blocks = blockstack[blockdep -= 1];
+}
+
+tinit()
+{
+ wtemp = nil;
+ bigtemp = nil;
+}
+
+tdecls(): ref Decl
+{
+ for(d := wtemp; d != nil; d = d.next){
+ if(d.tref != 1)
+ fatal("temporary "+d.sym.name+" has "+string(d.tref-1)+" references");
+ }
+
+ for(d = bigtemp; d != nil; d = d.next){
+ if(d.tref != 1)
+ fatal("temporary "+d.sym.name+" has "+string(d.tref-1)+" references");
+ }
+
+ return appdecls(wtemp, bigtemp);
+}
+
+talloc(t: ref Type, nok: ref Node): ref Node
+{
+ ok, d: ref Decl;
+
+ ok = nil;
+ if(nok != nil)
+ ok = nok.decl;
+ if(ok == nil || ok.tref == 0 || tattr[ok.ty.kind].isbig != tattr[t.kind].isbig || ok.ty.align != t.align)
+ ok = nil;
+ n := ref znode;
+ n.op = Oname;
+ n.addable = Rreg;
+ n.ty = t;
+ if(tattr[t.kind].isbig){
+ desc := mktdesc(t);
+ if(ok != nil && ok.desc == desc){
+ ok.tref++;
+ ok.refs++;
+ n.decl = ok;
+ return n;
+ }
+ for(d = bigtemp; d != nil; d = d.next){
+ if(d.tref == 1 && d.desc == desc && d.ty.align == t.align){
+ d.tref++;
+ d.refs++;
+ n.decl = d;
+ return n;
+ }
+ }
+ d = mkdecl(nosrc, Dlocal, t);
+ d.desc = desc;
+ d.tref = 2;
+ d.refs = 1;
+ d.sym = enter(".b"+string ntemp++, 0);
+ d.next = bigtemp;
+ bigtemp = d;
+ n.decl = d;
+ return n;
+ }
+ if(ok != nil
+ && tattr[ok.ty.kind].isptr == tattr[t.kind].isptr
+ && ok.ty.size == t.size){
+ ok.tref++;
+ n.decl = ok;
+ return n;
+ }
+ for(d = wtemp; d != nil; d = d.next){
+ if(d.tref == 1
+ && tattr[d.ty.kind].isptr == tattr[t.kind].isptr
+ && d.ty.size == t.size
+ && d.ty.align == t.align){
+ d.tref++;
+ n.decl = d;
+ return n;
+ }
+ }
+ d = mkdecl(nosrc, Dlocal, t);
+ d.tref = 2;
+ d.refs = 1;
+ d.sym = enter(".t"+string ntemp++, 0);
+ d.next = wtemp;
+ wtemp = d;
+ n.decl = d;
+ return n;
+}
+
+tfree(n: ref Node)
+{
+ if(n == nil || n.decl == nil)
+ return;
+ d := n.decl;
+ if(d.tref == 0)
+ return;
+
+ if(d.tref == 1)
+ fatal("double free of temporary " + d.sym.name);
+ if (--d.tref == 1)
+ zcom1(n, nil);
+
+ #
+ # nil out any pointers so we don't
+ # hang onto references
+ #
+#
+# costs ~7% in instruction count
+# if(d.tref != 1)
+# return;
+# if(!tattr[d.ty.kind].isbig){
+# if(tattr[d.ty.kind].isptr){ # or tmustzero()
+# nilnode.decl.refs++;
+# genmove(lastinst.src, Mas, d.ty, nilnode, n);
+# }
+# }else{
+# if(d.desc.nmap != 0){ # tmustzero() is better
+# zn := ref znode;
+# zn.op = Oname;
+# zn.addable = Rmreg;
+# zn.decl = globalztup(d.ty);
+# zn.ty = d.ty;
+# genmove(lastinst.src, Mas, d.ty, zn, n);
+# }
+# }
+}
+
+tfreelater(n: ref Node)
+{
+ if(n == nil || n.decl == nil)
+ return;
+ d := n.decl;
+ if(d.tref == 0)
+ return;
+
+ if(d.tref == 1)
+ fatal("double free of temporary " + d.sym.name);
+ if (--d.tref == 1){
+ nn := mkn(Oname, nil, nil);
+ *nn = *n;
+ nn.left = ntoz;
+ ntoz = nn;
+ d.tref++;
+ }
+}
+
+tfreenow()
+{
+ nn: ref Node;
+
+ for(n := ntoz; n != nil; n = nn){
+ nn = n.left;
+ n.left = nil;
+ if(n.decl.tref != 2)
+ fatal(sprint("bad free of temporary %s", n.decl.sym.name));
+ --n.decl.tref;
+ zcom1(n, nil);
+ }
+ ntoz = nil;
+}
+
+#
+# realloc a temporary after it's been released
+#
+tacquire(n: ref Node): ref Node
+{
+ if(n == nil || n.decl == nil)
+ return n;
+ d := n.decl;
+ if(d.tref == 0)
+ return n;
+ # if(d.tref != 1)
+ # fatal("tacquire ref != 1: "+string d.tref);
+ d.tref++;
+ return n;
+}
+
+trelease(n: ref Node)
+{
+ if(n == nil || n.decl == nil)
+ return;
+ d := n.decl;
+ if(d.tref == 0)
+ return;
+ if(d.tref == 1)
+ fatal("double release of temporary " + d.sym.name);
+ d.tref--;
+}
+
+mkinst(): ref Inst
+{
+ in := lastinst.next;
+ if(in == nil){
+ in = ref zinst;
+ lastinst.next = in;
+ }
+ lastinst = in;
+ in.block = blocks;
+ if(blocks < 0)
+ fatal("mkinst no block");
+ return in;
+}
+
+nextinst(): ref Inst
+{
+ in := lastinst.next;
+ if(in != nil)
+ return in;
+ in = ref zinst;
+ lastinst.next = in;
+ return in;
+}
+
+#
+# allocate a node for returning
+#
+retalloc(n, nn: ref Node): ref Node
+{
+ if(nn.ty == tnone)
+ return nil;
+ n = ref znode;
+ n.op = Oind;
+ n.addable = Radr;
+ n.left = dupn(1, n.src, retnode);
+ n.ty = nn.ty;
+ return n;
+}
+
+genrawop(src: Src, op: int, s, m, d: ref Node): ref Inst
+{
+ in := mkinst();
+ in.op = op;
+ in.src = src;
+ if(s != nil){
+ in.s = genaddr(s);
+ in.sm = addrmode[int s.addable];
+ }
+ if(m != nil){
+ in.m = genaddr(m);
+ in.mm = addrmode[int m.addable];
+ if(in.mm == Ampind || in.mm == Afpind)
+ fatal("illegal addressing mode in register "+nodeconv(m));
+ }
+ if(d != nil){
+ in.d = genaddr(d);
+ in.dm = addrmode[int d.addable];
+ }
+ return in;
+}
+
+genop(src: Src, op: int, s, m, d: ref Node): ref Inst
+{
+ iop := disoptab[op][opind[d.ty.kind]];
+ if(iop == 0)
+ fatal("can't deal with op "+opconv(op)+" on "+nodeconv(s)+" "+nodeconv(m)+" "+nodeconv(d)+" in genop");
+ if(iop == IMULX || iop == IDIVX)
+ return genfixop(src, iop, s, m, d);
+ in := mkinst();
+ in.op = iop;
+ in.src = src;
+ if(s != nil){
+ in.s = genaddr(s);
+ in.sm = addrmode[int s.addable];
+ }
+ if(m != nil){
+ in.m = genaddr(m);
+ in.mm = addrmode[int m.addable];
+ if(in.mm == Ampind || in.mm == Afpind)
+ fatal("illegal addressing mode in register "+nodeconv(m));
+ }
+ if(d != nil){
+ in.d = genaddr(d);
+ in.dm = addrmode[int d.addable];
+ }
+ return in;
+}
+
+genbra(src: Src, op: int, s, m: ref Node): ref Inst
+{
+ t := s.ty;
+ if(t == tany)
+ t = m.ty;
+ iop := disoptab[op][opind[t.kind]];
+ if(iop == 0)
+ fatal("can't deal with op "+opconv(op)+" on "+nodeconv(s)+" "+nodeconv(m)+" in genbra");
+ in := mkinst();
+ in.op = iop;
+ in.src = src;
+ if(s != nil){
+ in.s = genaddr(s);
+ in.sm = addrmode[int s.addable];
+ }
+ if(m != nil){
+ in.m = genaddr(m);
+ in.mm = addrmode[int m.addable];
+ if(in.mm == Ampind || in.mm == Afpind)
+ fatal("illegal addressing mode in register "+nodeconv(m));
+ }
+ return in;
+}
+
+genchan(src: Src, sz: ref Node, mt: ref Type, d: ref Node): ref Inst
+{
+ reg: Addr;
+
+ regm := Anone;
+ reg.decl = nil;
+ reg.reg = 0;
+ reg.offset = 0;
+ op := chantab[mt.kind];
+ if(op == 0)
+ fatal("can't deal with op "+string mt.kind+" in genchan");
+
+ case mt.kind{
+ Tadt or
+ Tadtpick or
+ Ttuple =>
+ td := mktdesc(mt);
+ if(td.nmap != 0){
+ op++; # sleazy
+ usedesc(td);
+ regm = Adesc;
+ reg.decl = mt.decl;
+ }else{
+ regm = Aimm;
+ reg.offset = mt.size;
+ }
+ }
+ in := mkinst();
+ in.op = op;
+ in.src = src;
+ in.s = reg;
+ in.sm = regm;
+ if(sz != nil){
+ in.m = genaddr(sz);
+ in.mm = addrmode[int sz.addable];
+ }
+ if(d != nil){
+ in.d = genaddr(d);
+ in.dm = addrmode[int d.addable];
+ }
+ return in;
+}
+
+genmove(src: Src, how: int, mt: ref Type, s, d: ref Node): ref Inst
+{
+ reg: Addr;
+
+ regm := Anone;
+ reg.decl = nil;
+ reg.reg = 0;
+ reg.offset = 0;
+ op := movetab[how][mt.kind];
+ if(op == 0)
+ fatal("can't deal with op "+string how+" on "+nodeconv(s)+" "+nodeconv(d)+" in genmove");
+
+ case mt.kind{
+ Tadt or
+ Tadtpick or
+ Ttuple or
+ Texception =>
+ if(mt.size == 0 && how == Mas)
+ return nil;
+ td := mktdesc(mt);
+ if(td.nmap != 0){
+ op++; # sleazy
+ usedesc(td);
+ regm = Adesc;
+ reg.decl = mt.decl;
+ }else{
+ regm = Aimm;
+ reg.offset = mt.size;
+ }
+ }
+ in := mkinst();
+ in.op = op;
+ in.src = src;
+ if(s != nil){
+ in.s = genaddr(s);
+ in.sm = addrmode[int s.addable];
+ }
+ in.m = reg;
+ in.mm = regm;
+ if(d != nil){
+ in.d = genaddr(d);
+ in.dm = addrmode[int d.addable];
+ }
+ if(s.addable == Rpc)
+ in.op = IMOVPC;
+ return in;
+}
+
+patch(b, dst: ref Inst)
+{
+ n: ref Inst;
+
+ for(; b != nil; b = n){
+ n = b.branch;
+ b.branch = dst;
+ }
+}
+
+getpc(i: ref Inst): int
+{
+ if(i.pc == 0 && i != firstinst && (firstinst.op != INOOP || i != firstinst.next)){
+ do
+ i = i.next;
+ while(i != nil && i.pc == 0);
+ if(i == nil || i.pc == 0)
+ fatal("bad instruction in getpc");
+ }
+ return i.pc;
+}
+
+#
+# follow all possible paths from n,
+# marking reached code, compressing branches, and reclaiming unreached insts
+#
+reach(in: ref Inst)
+{
+ foldbranch(in);
+ last := in;
+ for(in = in.next; in != nil; in = in.next){
+ if(in.reach == byte 0)
+ last.next = in.next;
+ else
+ last = in;
+ }
+ lastinst = last;
+}
+
+foldbranch(in: ref Inst)
+{
+ while(in != nil && in.reach != byte 1){
+ in.reach = byte 1;
+ if(in.branch != nil)
+ while(in.branch.op == IJMP){
+ if(in == in.branch || in.branch == in.branch.branch)
+ break;
+ in.branch = in.branch.branch;
+ }
+ case in.op{
+ IGOTO or
+ ICASE or
+ ICASEL or
+ ICASEC or
+ IEXC =>
+ foldbranch(in.d.decl.ty.cse.iwild);
+ lab := in.d.decl.ty.cse.labs;
+ n := in.d.decl.ty.cse.nlab;
+ for(i := 0; i < n; i++)
+ foldbranch(lab[i].inst);
+ if(in.op == IEXC)
+ in.op = INOOP;
+ return;
+ IEXC0 =>
+ foldbranch(in.branch);
+ in.op = INOOP;
+ break;
+ IRET or
+ IEXIT or
+ IRAISE =>
+ return;
+ IJMP =>
+ b := in.branch;
+ case b.op{
+ ICASE or
+ ICASEL or
+ ICASEC or
+ IRET or
+ IEXIT =>
+ next := in.next;
+ *in = *b;
+ in.next = next;
+ # b.reach = byte 1;
+ continue;
+ }
+ foldbranch(in.branch);
+ return;
+ * =>
+ if(in.branch != nil)
+ foldbranch(in.branch);
+ }
+
+ in = in.next;
+ }
+}
+
+#
+# convert the addressable node into an operand
+# see the comment for sumark
+#
+genaddr(n: ref Node): Addr
+{
+ a: Addr;
+
+ a.reg = 0;
+ a.offset = 0;
+ a.decl = nil;
+ case int n.addable{
+ int Rreg =>
+ if(n.decl != nil)
+ a.decl = n.decl;
+ else
+ a = genaddr(n.left);
+ int Rmreg =>
+ if(n.decl != nil)
+ a.decl = n.decl;
+ else
+ a = genaddr(n.left);
+ int Rdesc =>
+ a.decl = n.ty.decl;
+ int Roff or
+ int Rnoff =>
+ a.decl = n.decl;
+ int Rconst =>
+ a.offset = int n.c.val;
+ int Radr =>
+ a = genaddr(n.left);
+ int Rmadr =>
+ a = genaddr(n.left);
+ int Rareg or
+ int Ramreg =>
+ a = genaddr(n.left);
+ if(n.op == Oadd)
+ a.reg += int n.right.c.val;
+ int Raadr or
+ int Ramadr =>
+ a = genaddr(n.left);
+ if(n.op == Oadd)
+ a.offset += int n.right.c.val;
+ int Rldt =>
+ a.decl = n.decl;
+ int Rdescp or
+ int Rpc =>
+ a.decl = n.decl;
+ * =>
+ fatal("can't deal with "+nodeconv(n)+" in genaddr");
+ }
+ return a;
+}
+
+sameaddr(n, m: ref Node): int
+{
+ if(n.addable != m.addable)
+ return 0;
+ a := genaddr(n);
+ b := genaddr(m);
+ return a.offset == b.offset && a.reg == b.reg && a.decl == b.decl;
+}
+
+resolvedesc(mod: ref Decl, length: int, id: ref Decl): int
+{
+ last: ref Desc;
+
+ g := gendesc(mod, length, id);
+ g.used = 0;
+ last = nil;
+ for(d := descriptors; d != nil; d = d.next){
+ if(!d.used){
+ if(last != nil)
+ last.next = d.next;
+ else
+ descriptors = d.next;
+ continue;
+ }
+ last = d;
+ }
+
+ g.next = descriptors;
+ descriptors = g;
+
+ descid := 0;
+ for(d = descriptors; d != nil; d = d.next)
+ d.id = descid++;
+ if(g.id != 0)
+ fatal("bad global descriptor id");
+
+ return descid;
+}
+
+resolvemod(m: ref Decl): int
+{
+ for(id := m.ty.ids; id != nil; id = id.next){
+ case id.store{
+ Dfn =>
+ id.iface.pc = id.pc;
+ id.iface.desc = id.desc;
+ Dtype =>
+ if(id.ty.kind != Tadt)
+ break;
+ for(d := id.ty.ids; d != nil; d = d.next){
+ if(d.store == Dfn){
+ d.iface.pc = d.pc;
+ d.iface.desc = d.desc;
+ }
+ }
+ }
+ }
+ # for addiface
+ for(id = m.ty.tof.ids; id != nil; id = id.next){
+ if(id.store == Dfn){
+ if(id.pc == nil)
+ id.pc = id.iface.pc;
+ if(id.desc == nil)
+ id.desc = id.iface.desc;
+ }
+ }
+ return int m.ty.tof.decl.init.c.val;
+}
+
+#
+# place the Tiface decs in another list
+#
+resolveldts(d: ref Decl): (ref Decl, ref Decl)
+{
+ d1, ld1, d2, ld2, n: ref Decl;
+
+ d1 = d2 = nil;
+ ld1 = ld2 = nil;
+ for( ; d != nil; d = n){
+ n = d.next;
+ d.next = nil;
+ if(d.ty.kind == Tiface){
+ if(d2 == nil)
+ d2 = d;
+ else
+ ld2.next = d;
+ ld2 = d;
+ }
+ else{
+ if(d1 == nil)
+ d1 = d;
+ else
+ ld1.next = d;
+ ld1 = d;
+ }
+ }
+ return (d1, d2);
+}
+
+#
+# fix up all pc's
+# finalize all data offsets
+# fix up instructions with offsets too large
+#
+resolvepcs(inst: ref Inst): int
+{
+ d: ref Decl;
+
+ pc := 0;
+ for(in := inst; in != nil; in = in.next){
+ if(in.reach == byte 0 || in.op == INOP)
+ fatal("unreachable pc: "+instconv(in));
+ if(in.op == INOOP){
+ in.pc = pc;
+ continue;
+ }
+ d = in.s.decl;
+ if(d != nil){
+ if(in.sm == Adesc){
+ if(d.desc != nil)
+ in.s.offset = d.desc.id;
+ }else
+ in.s.reg += d.offset;
+ }
+ r := in.s.reg;
+ off := in.s.offset;
+ if((in.sm == Afpind || in.sm == Ampind)
+ && (r >= MaxReg || off >= MaxReg))
+ fatal("big offset in "+instconv(in));
+
+ d = in.m.decl;
+ if(d != nil){
+ if(in.mm == Adesc){
+ if(d.desc != nil)
+ in.m.offset = d.desc.id;
+ }else
+ in.m.reg += d.offset;
+ }
+ v := 0;
+ case int in.mm{
+ int Anone =>
+ break;
+ int Aimm or
+ int Apc or
+ int Adesc =>
+ v = in.m.offset;
+ int Aoff or
+ int Anoff =>
+ v = in.m.decl.iface.offset;
+ int Afp or
+ int Amp or
+ int Aldt =>
+ v = in.m.reg;
+ if(v < 0)
+ v = 16r8000;
+ * =>
+ fatal("can't deal with "+instconv(in)+"'s m mode");
+ }
+ if(v > 16r7fff || v < -16r8000){
+ case in.op{
+ IALT or
+ IINDX =>
+ rewritedestreg(in, IMOVW, RTemp);
+ * =>
+ op := IMOVW;
+ if(isbyteinst[in.op])
+ op = IMOVB;
+ in = rewritesrcreg(in, op, RTemp, pc++);
+ }
+ }
+
+ d = in.d.decl;
+ if(d != nil){
+ if(in.dm == Apc)
+ in.d.offset = d.pc.pc;
+ else
+ in.d.reg += d.offset;
+ }
+ r = in.d.reg;
+ off = in.d.offset;
+ if((in.dm == Afpind || in.dm == Ampind)
+ && (r >= MaxReg || off >= MaxReg))
+ fatal("big offset in "+instconv(in));
+
+ in.pc = pc;
+ pc++;
+ }
+ for(in = inst; in != nil; in = in.next){
+ d = in.s.decl;
+ if(d != nil && in.sm == Apc)
+ in.s.offset = d.pc.pc;
+ d = in.d.decl;
+ if(d != nil && in.dm == Apc)
+ in.d.offset = d.pc.pc;
+ if(in.branch != nil){
+ in.dm = Apc;
+ in.d.offset = in.branch.pc;
+ }
+ }
+ return pc;
+}
+
+#
+# fixp up a big register constant uses as a source
+# ugly: smashes the instruction
+#
+rewritesrcreg(in: ref Inst, op: int, treg: int, pc: int): ref Inst
+{
+ a := in.m;
+ am := in.mm;
+ in.mm = Afp;
+ in.m.reg = treg;
+ in.m.decl = nil;
+
+ new := ref *in;
+
+ *in = zinst;
+ in.src = new.src;
+ in.next = new;
+ in.op = op;
+ in.s = a;
+ in.sm = am;
+ in.dm = Afp;
+ in.d.reg = treg;
+ in.pc = pc;
+ in.reach = byte 1;
+ in.block = new.block;
+ return new;
+}
+
+#
+# fix up a big register constant by moving to the destination
+# after the instruction completes
+#
+rewritedestreg(in: ref Inst, op: int, treg: int): ref Inst
+{
+ n := ref zinst;
+ n.next = in.next;
+ in.next = n;
+ n.src = in.src;
+ n.op = op;
+ n.sm = Afp;
+ n.s.reg = treg;
+ n.d = in.m;
+ n.dm = in.mm;
+ n.reach = byte 1;
+ n.block = in.block;
+
+ in.mm = Afp;
+ in.m.reg = treg;
+ in.m.decl = nil;
+
+ return n;
+}
+
+instconv(in: ref Inst): string
+{
+ if(in.op == INOP)
+ return "nop";
+ op := "";
+ if(in.op >= 0 && in.op < 256)
+ op = instname[in.op];
+ if(op == nil)
+ op = "?"+string in.op+"?";
+ s := "\t" + op + "\t";
+ comma := "";
+ if(in.sm != Anone){
+ s += addrconv(in.sm, in.s);
+ comma = ",";
+ }
+ if(in.mm != Anone){
+ s += comma;
+ s += addrconv(in.mm, in.m);
+ comma = ",";
+ }
+ if(in.dm != Anone){
+ s += comma;
+ s += addrconv(in.dm, in.d);
+ }
+
+ if(!asmsym)
+ return s;
+
+ if(in.s.decl != nil && in.sm == Adesc){
+ s += "\t#";
+ s += dotconv(in.s.decl);
+ }
+ if(0 && in.m.decl != nil){
+ s += "\t#";
+ s += dotconv(in.m.decl);
+ }
+ if(in.d.decl != nil && in.dm == Apc){
+ s += "\t#";
+ s += dotconv(in.d.decl);
+ }
+ s += "\t#";
+ s += srcconv(in.src);
+ return s;
+}
+
+addrconv(am: byte, a: Addr): string
+{
+ s := "";
+ case int am{
+ int Anone =>
+ break;
+ int Aimm or
+ int Apc or
+ int Adesc =>
+ s = "$" + string a.offset;
+ int Aoff =>
+ s = "$" + string a.decl.iface.offset;
+ int Anoff =>
+ s = "-$" + string a.decl.iface.offset;
+ int Afp =>
+ s = string a.reg + "(fp)";
+ int Afpind =>
+ s = string a.offset + "(" + string a.reg + "(fp))";
+ int Amp =>
+ s = string a.reg + "(mp)";
+ int Ampind =>
+ s = string a.offset + "(" + string a.reg + "(mp))";
+ int Aldt =>
+ s = "$" + string a.reg;
+ * =>
+ s = string a.offset + "(" + string a.reg + "(?" + string am + "?))";
+ }
+ return s;
+}
+
+genstore(src: Src, n: ref Node, offset: int)
+{
+ de := mkdecl(nosrc, Dlocal, tint);
+ de.sym = nil;
+ de.offset = offset;
+
+ d := ref znode;
+ d.op = Oname;
+ d.addable = Rreg;
+ d.decl = de;
+ d.ty = tint;
+ genrawop(src, IMOVW, n, nil, d);
+}
+
+genfixop(src: Src, op: int, s, m, d: ref Node): ref Inst
+{
+ p, a: int;
+ mm: ref Node;
+
+ if(m == nil)
+ mm = d;
+ else
+ mm = m;
+ (op, p, a) = fixop(op, mm.ty, s.ty, d.ty);
+ if(op == IMOVW){ # just zero d
+ s = sumark(mkconst(src, big 0));
+ return genrawop(src, op, s, nil, d);
+ }
+ if(op != IMULX && op != IDIVX)
+ genstore(src, sumark(mkconst(src, big a)), STemp);
+ genstore(src, sumark(mkconst(src, big p)), DTemp);
+ i := genrawop(src, op, s, m, d);
+ return i;
+}
+
+genfixcastop(src: Src, op: int, s, d: ref Node): ref Inst
+{
+ p, a: int;
+ m: ref Node;
+
+ (op, p, a) = fixop(op, s.ty, tint, d.ty);
+ if(op == IMOVW){ # just zero d
+ s = sumark(mkconst(src, big 0));
+ return genrawop(src, op, s, nil, d);
+ }
+ m = sumark(mkconst(src, big p));
+ if(op != ICVTXX)
+ genstore(src, sumark(mkconst(src, big a)), STemp);
+ return genrawop(src, op, s, m, d);
+}
diff --git a/appl/cmd/limbo/isa.m b/appl/cmd/limbo/isa.m
new file mode 100644
index 00000000..9e9936d0
--- /dev/null
+++ b/appl/cmd/limbo/isa.m
@@ -0,0 +1,247 @@
+#
+# VM instruction set
+#
+ INOP,
+ IALT,
+ INBALT,
+ IGOTO,
+ ICALL,
+ IFRAME,
+ ISPAWN,
+ IRUNT,
+ ILOAD,
+ IMCALL,
+ IMSPAWN,
+ IMFRAME,
+ IRET,
+ IJMP,
+ ICASE,
+ IEXIT,
+ INEW,
+ INEWA,
+ INEWCB,
+ INEWCW,
+ INEWCF,
+ INEWCP,
+ INEWCM,
+ INEWCMP,
+ ISEND,
+ IRECV,
+ ICONSB,
+ ICONSW,
+ ICONSP,
+ ICONSF,
+ ICONSM,
+ ICONSMP,
+ IHEADB,
+ IHEADW,
+ IHEADP,
+ IHEADF,
+ IHEADM,
+ IHEADMP,
+ ITAIL,
+ ILEA,
+ IINDX,
+ IMOVP,
+ IMOVM,
+ IMOVMP,
+ IMOVB,
+ IMOVW,
+ IMOVF,
+ ICVTBW,
+ ICVTWB,
+ ICVTFW,
+ ICVTWF,
+ ICVTCA,
+ ICVTAC,
+ ICVTWC,
+ ICVTCW,
+ ICVTFC,
+ ICVTCF,
+ IADDB,
+ IADDW,
+ IADDF,
+ ISUBB,
+ ISUBW,
+ ISUBF,
+ IMULB,
+ IMULW,
+ IMULF,
+ IDIVB,
+ IDIVW,
+ IDIVF,
+ IMODW,
+ IMODB,
+ IANDB,
+ IANDW,
+ IORB,
+ IORW,
+ IXORB,
+ IXORW,
+ ISHLB,
+ ISHLW,
+ ISHRB,
+ ISHRW,
+ IINSC,
+ IINDC,
+ IADDC,
+ ILENC,
+ ILENA,
+ ILENL,
+ IBEQB,
+ IBNEB,
+ IBLTB,
+ IBLEB,
+ IBGTB,
+ IBGEB,
+ IBEQW,
+ IBNEW,
+ IBLTW,
+ IBLEW,
+ IBGTW,
+ IBGEW,
+ IBEQF,
+ IBNEF,
+ IBLTF,
+ IBLEF,
+ IBGTF,
+ IBGEF,
+ IBEQC,
+ IBNEC,
+ IBLTC,
+ IBLEC,
+ IBGTC,
+ IBGEC,
+ ISLICEA,
+ ISLICELA,
+ ISLICEC,
+ IINDW,
+ IINDF,
+ IINDB,
+ INEGF,
+ IMOVL,
+ IADDL,
+ ISUBL,
+ IDIVL,
+ IMODL,
+ IMULL,
+ IANDL,
+ IORL,
+ IXORL,
+ ISHLL,
+ ISHRL,
+ IBNEL,
+ IBLTL,
+ IBLEL,
+ IBGTL,
+ IBGEL,
+ IBEQL,
+ ICVTLF,
+ ICVTFL,
+ ICVTLW,
+ ICVTWL,
+ ICVTLC,
+ ICVTCL,
+ IHEADL,
+ ICONSL,
+ INEWCL,
+ ICASEC,
+ IINDL,
+ IMOVPC,
+ ITCMP,
+ IMNEWZ,
+ ICVTRF,
+ ICVTFR,
+ ICVTWS,
+ ICVTSW,
+ ILSRW,
+ ILSRL,
+ IECLR,
+ INEWZ,
+ INEWAZ,
+ IRAISE,
+ ICASEL,
+ IMULX,
+ IDIVX,
+ ICVTXX,
+ IMULX0,
+ IDIVX0,
+ ICVTXX0,
+ IMULX1,
+ IDIVX1,
+ ICVTXX1,
+ ICVTFX,
+ ICVTXF,
+ IEXPW,
+ IEXPL,
+ IEXPF,
+ ISELF,
+ # add new operators here
+ MAXDIS: con iota;
+
+XMAGIC: con 819248; # Normal magic
+SMAGIC: con 923426; # Signed module
+
+AMP: con 16r00; # Src/Dst op addressing
+AFP: con 16r01;
+AIMM: con 16r2;
+AXXX: con 16r03;
+AIND: con 16r04;
+AMASK: con 16r07;
+AOFF: con 16r08;
+AVAL: con 16r10;
+
+ARM: con 16rC0; # Middle op addressing
+AXNON: con 16r00;
+AXIMM: con 16r40;
+AXINF: con 16r80;
+AXINM: con 16rC0;
+
+DEFZ: con 0;
+DEFB: con 1; # Byte
+DEFW: con 2; # Word
+DEFS: con 3; # Utf-string
+DEFF: con 4; # Real value
+DEFA: con 5; # Array
+DIND: con 6; # Set index
+DAPOP: con 7; # Restore address register
+DEFL: con 8; # BIG
+
+DADEPTH: con 4; # Array address stack size
+
+REGLINK: con 0;
+REGFRAME: con 1;
+REGMOD: con 2;
+REGTYP: con 3;
+REGRET: con 4;
+NREG: con 5;
+
+IBY2WD: con 4;
+IBY2FT: con 8;
+IBY2LG: con 8;
+
+MUSTCOMPILE: con 1<<0;
+DONTCOMPILE: con 1<<1;
+SHAREMP: con 1<<2;
+DYNMOD: con 1<<3;
+HASLDT0: con 1<<4;
+HASEXCEPT: con 1<<5;
+HASLDT: con 1<<6;
+
+DMAX: con 1 << 4;
+
+#define DTYPE(x) (x>>4)
+#define DBYTE(x, l) ((x<<4)|l)
+#define DMAX (1<<4)
+#define DLEN(x) (x& (DMAX-1))
+
+DBYTE: con 4;
+SRC: con 3;
+DST: con 0;
+
+#define SRC(x) ((x)<<3)
+#define DST(x) ((x)<<0)
+#define USRC(x) (((x)>>3)&AMASK)
+#define UDST(x) ((x)&AMASK)
+#define UXSRC(x) ((x)&(AMASK<<3))
+#define UXDST(x) ((x)&(AMASK<<0))
diff --git a/appl/cmd/limbo/lex.b b/appl/cmd/limbo/lex.b
new file mode 100644
index 00000000..ae87b4a9
--- /dev/null
+++ b/appl/cmd/limbo/lex.b
@@ -0,0 +1,1146 @@
+Leof: con -1;
+Linestart: con 0;
+
+Mlower,
+Mupper,
+Munder,
+Mdigit,
+Msign,
+Mexp,
+Mhex,
+Mradix: con byte 1 << iota;
+Malpha: con Mupper|Mlower|Munder;
+
+HashSize: con 1024;
+
+Keywd: adt
+{
+ name: string;
+ token: int;
+};
+
+#
+# internals
+#
+savec: int;
+files: array of ref File; # files making up the module, sorted by absolute line
+nfiles: int;
+lastfile := 0; # index of last file looked up
+incpath := array[MaxIncPath] of string;
+symbols := array[HashSize] of ref Sym;
+strings := array[HashSize] of ref Sym;
+map := array[256] of byte;
+bins := array [MaxInclude] of ref Iobuf;
+bin: ref Iobuf;
+linestack := array[MaxInclude] of (int, int);
+lineno: int;
+linepos: int;
+bstack: int;
+lasttok: int;
+lastyylval: YYSTYPE;
+dowarn: int;
+maxerr: int;
+dosym: int;
+toterrors: int;
+fabort: int;
+srcdir: string;
+outfile: string;
+stderr: ref Sys->FD;
+dontinline: int;
+
+escmap := array[256] of
+{
+ '\'' => '\'',
+ '"' => '"',
+ '\\' => '\\',
+ 'a' => '\a',
+ 'b' => '\b',
+ 'f' => '\f',
+ 'n' => '\n',
+ 'r' => '\r',
+ 't' => '\t',
+ 'v' => '\v',
+ '0' => '\u0000',
+
+ * => -1
+};
+unescmap := array[256] of
+{
+ '\'' => '\'',
+ '"' => '"',
+ '\\' => '\\',
+ '\a' => 'a',
+ '\b' => 'b',
+ '\f' => 'f',
+ '\n' => 'n',
+ '\r' => 'r',
+ '\t' => 't',
+ '\v' => 'v',
+ '\u0000' => '0',
+
+ * => 0
+};
+
+keywords := array [] of
+{
+ Keywd("adt", Ladt),
+ Keywd("alt", Lalt),
+ Keywd("array", Larray),
+ Keywd("big", Ltid),
+ Keywd("break", Lbreak),
+ Keywd("byte", Ltid),
+ Keywd("case", Lcase),
+ Keywd("chan", Lchan),
+ Keywd("con", Lcon),
+ Keywd("continue", Lcont),
+ Keywd("cyclic", Lcyclic),
+ Keywd("do", Ldo),
+ Keywd("else", Lelse),
+ Keywd("exception", Lexcept),
+ Keywd("exit", Lexit),
+ Keywd("fixed", Lfix),
+ Keywd("fn", Lfn),
+ Keywd("for", Lfor),
+ Keywd("hd", Lhd),
+ Keywd("if", Lif),
+ Keywd("implement", Limplement),
+ Keywd("import", Limport),
+ Keywd("include", Linclude),
+ Keywd("int", Ltid),
+ Keywd("len", Llen),
+ Keywd("list", Llist),
+ Keywd("load", Lload),
+ Keywd("module", Lmodule),
+ Keywd("nil", Lnil),
+ Keywd("of", Lof),
+ Keywd("or", Lor),
+ Keywd("pick", Lpick),
+ Keywd("raise", Lraise),
+ Keywd("raises", Lraises),
+ Keywd("real", Ltid),
+ Keywd("ref", Lref),
+ Keywd("return", Lreturn),
+ Keywd("self", Lself),
+ Keywd("spawn", Lspawn),
+ Keywd("string", Ltid),
+ Keywd("tagof", Ltagof),
+ Keywd("tl", Ltl),
+ Keywd("to", Lto),
+ Keywd("type", Ltype),
+ Keywd("while", Lwhile),
+};
+
+tokwords := array[] of
+{
+ Keywd("&=", Landeq),
+ Keywd("|=", Loreq),
+ Keywd("^=", Lxoreq),
+ Keywd("<<=", Llsheq),
+ Keywd(">>=", Lrsheq),
+ Keywd("+=", Laddeq),
+ Keywd("-=", Lsubeq),
+ Keywd("*=", Lmuleq),
+ Keywd("/=", Ldiveq),
+ Keywd("%=", Lmodeq),
+ Keywd("**=", Lexpeq),
+ Keywd(":=", Ldeclas),
+ Keywd("||", Loror),
+ Keywd("&&", Landand),
+ Keywd("::", Lcons),
+ Keywd("==", Leq),
+ Keywd("!=", Lneq),
+ Keywd("<=", Lleq),
+ Keywd(">=", Lgeq),
+ Keywd("<<", Llsh),
+ Keywd(">>", Lrsh),
+ Keywd("<-", Lcomm),
+ Keywd("++", Linc),
+ Keywd("--", Ldec),
+ Keywd("->", Lmdot),
+ Keywd("=>", Llabs),
+ Keywd("**", Lexp),
+ Keywd("EOF", Leof),
+};
+
+lexinit()
+{
+ for(i := 0; i < 256; i++){
+ map[i] = byte 0;
+ if(i == '_' || i > 16ra0)
+ map[i] |= Munder;
+ if(i >= 'A' && i <= 'Z')
+ map[i] |= Mupper;
+ if(i >= 'a' && i <= 'z')
+ map[i] |= Mlower;
+ if(i >= 'A' && i <= 'F' || i >= 'a' && i <= 'f')
+ map[i] |= Mhex;
+ if(i == 'e' || i == 'E')
+ map[i] |= Mexp;
+ if(i == 'r' || i == 'R')
+ map[i] |= Mradix;
+ if(i == '-' || i == '+')
+ map[i] |= Msign;
+ if(i >= '0' && i <= '9')
+ map[i] |= Mdigit;
+ }
+
+ for(i = 0; i < len keywords; i++)
+ enter(keywords[i].name, keywords[i].token);
+}
+
+cmap(c: int): byte
+{
+ if(c<0)
+ return byte 0;
+ if(c<256)
+ return map[c];
+ return Mlower;
+}
+
+lexstart(in: string)
+{
+ savec = 0;
+ bstack = 0;
+ nfiles = 0;
+ addfile(ref File(in, 1, 0, -1, nil, 0, -1));
+ bin = bins[bstack];
+ lineno = 1;
+ linepos = Linestart;
+
+ (srcdir, nil) = str->splitr(in, "/");
+}
+
+getc(): int
+{
+ if(c := savec){
+ if(savec >= 0){
+ linepos++;
+ savec = 0;
+ }
+ return c;
+ }
+ c = bin.getc();
+ if(c < 0){
+ savec = -1;
+ return savec;
+ }
+ linepos++;
+ return c;
+}
+
+#
+# dumps '\u0000' chararcters
+#
+ungetc(c: int)
+{
+ if(c > 0)
+ linepos--;
+ savec = c;
+}
+
+addinclude(s: string)
+{
+ for(i := 0; i < MaxIncPath; i++){
+ if(incpath[i] == nil){
+ incpath[i] = s;
+ return;
+ }
+ }
+ fatal("out of include path space");
+}
+
+addfile(f: ref File): int
+{
+ if(lastfile >= nfiles)
+ lastfile = 0;
+ if(nfiles >= len files){
+ nf := array[nfiles+32] of ref File;
+ nf[0:] = files;
+ files = nf;
+ }
+ files[nfiles] = f;
+ return nfiles++;
+}
+
+#
+# include a new file
+#
+includef(file: ref Sym)
+{
+ linestack[bstack] = (lineno, linepos);
+ bstack++;
+ if(bstack >= MaxInclude)
+ fatal(lineconv(lineno<<PosBits)+": include file depth too great");
+ buf := file.name;
+ if(buf[0] != '/')
+ buf = srcdir+buf;
+ b := bufio->open(buf, Bufio->OREAD);
+ for(i := 0; b == nil && i < MaxIncPath && incpath[i] != nil && file.name[0] != '/'; i++){
+ buf = incpath[i] + "/" + file.name;
+ b = bufio->open(buf, Bufio->OREAD);
+ }
+ bins[bstack] = b;
+ if(bins[bstack] == nil){
+ yyerror("can't include "+file.name+": "+sprint("%r"));
+ bstack--;
+ }else{
+ addfile(ref File(buf, lineno+1, -lineno, lineno, nil, 0, -1));
+ lineno++;
+ linepos = Linestart;
+ }
+ bin = bins[bstack];
+}
+
+#
+# we hit eof in the current file
+# revert to the file which included it.
+#
+popinclude()
+{
+ savec = 0;
+ bstack--;
+ bin = bins[bstack];
+ (oline, opos) := linestack[bstack];
+ (f, ln) := fline(oline);
+ lineno++;
+ linepos = opos;
+ addfile(ref File(f.name, lineno, ln-lineno, f.in, f.act, f.actoff, -1));
+}
+
+#
+# convert an absolute Line into a file and line within the file
+#
+fline(absline: int): (ref File, int)
+{
+ if(absline < files[lastfile].abs
+ || lastfile+1 < nfiles && absline >= files[lastfile+1].abs){
+ lastfile = 0;
+ l := 0;
+ r := nfiles - 1;
+ while(l <= r){
+ m := (r + l) / 2;
+ s := files[m].abs;
+ if(s <= absline){
+ l = m + 1;
+ lastfile = m;
+ }else
+ r = m - 1;
+ }
+ }
+ return (files[lastfile], absline + files[lastfile].off);
+}
+
+#
+# read a comment; process #line file renamings
+#
+lexcom(): int
+{
+ i := 0;
+ buf := "";
+ while((c := getc()) != '\n'){
+ if(c == Bufio->EOF)
+ return -1;
+ buf[i++] = c;
+ }
+
+ lineno++;
+ linepos = Linestart;
+
+ if(len buf < 6
+ || buf[len buf - 1] != '"'
+ || buf[:5] != "line " && buf[:5] != "line\t")
+ return 0;
+ for(s := 5; buf[s] == ' ' || buf[s] == '\t'; s++)
+ ;
+ if((cmap(buf[s]) & Mdigit) == byte 0)
+ return 0;
+ n := 0;
+ for(; (cmap(c = buf[s]) & Mdigit) != byte 0; s++)
+ n = n * 10 + c - '0';
+ for(; buf[s] == ' ' || buf[s] == '\t'; s++)
+ ;
+ if(buf[s++] != '"')
+ return 0;
+ buf = buf[s:len buf - 1];
+ f := files[nfiles - 1];
+ if(n == f.off+lineno && buf == f.name)
+ return 1;
+ act := f.name;
+ actline := lineno + f.off;
+ if(f.act != nil){
+ actline += f.actoff;
+ act = f.act;
+ }
+ addfile(ref File(buf, lineno, n-lineno, f.in, act, actline - n, -1));
+
+ return 1;
+}
+
+curline(): Line
+{
+ return (lineno << PosBits) | (linepos & PosMask);
+}
+
+lineconv(line: Line): string
+{
+ line >>= PosBits;
+ if(line < 0)
+ return "<noline>";
+ (f, ln) := fline(line);
+ s := "";
+ if(f.in >= 0){
+ s = ": " + lineconv(f.in << PosBits);
+ }
+ if(f.act != nil)
+ s = " [ " + f.act + ":" + string(f.actoff+ln) + " ]" + s;
+ return f.name + ":" + string ln + s;
+}
+
+posconv(s: Line): string
+{
+ if(s < 0)
+ return "nopos";
+ spos := s & PosMask;
+ s >>= PosBits;
+ (f, ln) := fline(s);
+ return f.name + ":" + string ln + "." + string spos;
+}
+
+srcconv(src: Src): string
+{
+ s := posconv(src.start);
+ s[len s] = ',';
+ s += posconv(src.stop);
+ return s;
+}
+
+lexid(c: int): int
+{
+ id := "";
+ i := 0;
+ for(;;){
+ if(i < StrSize)
+ id[i++] = c;
+ c = getc();
+ if(c == Bufio->EOF
+ || (cmap(c) & (Malpha|Mdigit)) == byte 0){
+ ungetc(c);
+ break;
+ }
+ }
+ sym := enter(id, Lid);
+ t := sym.token;
+ if(t == Lid || t == Ltid)
+ yyctxt.lval.tok.v.idval = sym;
+ return t;
+}
+
+maxfast := array[37] of
+{
+ 2 => 31,
+ 4 => 15,
+ 8 => 10,
+ 10 => 9,
+ 16 => 7,
+ 32 => 6,
+ * => 0,
+};
+
+strtoi(t: string, bbase: big): big
+{
+ #
+ # do the first part in ints
+ #
+ v := 0;
+ bv: big;
+ base := int bbase;
+ n := maxfast[base];
+
+ neg := 0;
+ i := 0;
+ if(i < len t && t[i] == '-'){
+ neg = 1;
+ i++;
+ }else if(i < len t && t[i] == '+')
+ i++;
+
+ for(; i < len t; i++){
+ c := t[i];
+ if(c >= '0' && c <= '9')
+ c -= '0';
+ else if(c >= 'a' && c <= 'z')
+ c -= 'a' - 10;
+ else
+ c -= 'A' - 10;
+ if(c >= base){
+ yyerror("digit '"+t[i:i+1]+"' is not radix "+string base);
+ return big -1;
+ }
+ if(i < n)
+ v = v * base + c;
+ else{
+ if(i == n)
+ bv = big v;
+ bv = bv * bbase + big c;
+ }
+ }
+ if(i <= n)
+ bv = big v;
+ if(neg)
+ return -bv;
+ return bv;
+}
+
+digit(c: int, base: int): int
+{
+ ck: byte;
+ cc: int;
+
+ cc = c;
+ ck = cmap(c);
+ if((ck & Mdigit) != byte 0)
+ c -= '0';
+ else if((ck & Mlower) != byte 0)
+ c = c - 'a' + 10;
+ else if((ck & Mupper) != byte 0)
+ c = c - 'A' + 10;
+ else if((ck & Munder) != byte 0)
+ ;
+ else
+ return -1;
+ if(c >= base){
+ s := "z";
+ s[0] = cc;
+ yyerror("digit '" + s + "' not radix " + string base);
+ }
+ return c;
+}
+
+strtodb(t: string, base: int): real
+{
+ num, dem, rbase: real;
+ neg, eneg, dig, exp, c, d: int;
+
+ t[len t] = 0;
+
+ num = 0.0;
+ rbase = real base;
+ neg = 0;
+ dig = 0;
+ exp = 0;
+ eneg = 0;
+
+ i := 0;
+ c = t[i++];
+ if(c == '-' || c == '+'){
+ if(c == '-')
+ neg = 1;
+ c = t[i++];
+ }
+ while((d = digit(c, base)) >= 0){
+ num = num*rbase + real d;
+ c = t[i++];
+ }
+ if(c == '.')
+ c = t[i++];
+ while((d = digit(c, base)) >= 0){
+ num = num*rbase + real d;
+ dig++;
+ c = t[i++];
+ }
+ if(c == 'e' || c == 'E'){
+ c = t[i++];
+ if(c == '-' || c == '+'){
+ if(c == '-'){
+ dig = -dig;
+ eneg = 1;
+ }
+ c = t[i++];
+ }
+ while((d = digit(c, base)) >= 0){
+ exp = exp*base + d;
+ c = t[i++];
+ }
+ }
+ exp -= dig;
+ if(exp < 0){
+ exp = -exp;
+ eneg = !eneg;
+ }
+ dem = rpow(rbase, exp);
+ if(eneg)
+ num /= dem;
+ else
+ num *= dem;
+ if(neg)
+ return -num;
+ return num;
+}
+
+#
+# parse a numeric identifier
+# format [0-9]+(r[0-9A-Za-z]+)?
+# or ([0-9]+(\.[0-9]*)?|\.[0-9]+)([eE][+-]?[0-9]+)?
+#
+lexnum(c: int): int
+{
+ Int, Radix, RadixSeen, Frac, ExpSeen, ExpSignSeen, Exp, FracB: con iota;
+
+ i := 0;
+ buf := "";
+ buf[i++] = c;
+ state := Int;
+ if(c == '.')
+ state = Frac;
+ radix := "";
+
+done: for(;;){
+ c = getc();
+ if(c == Bufio->EOF){
+ yyerror("end of file in numeric constant");
+ return Leof;
+ }
+
+ ck := cmap(c);
+ case state{
+ Int =>
+ if((ck & Mdigit) != byte 0)
+ break;
+ if((ck & Mexp) != byte 0){
+ state = ExpSeen;
+ break;
+ }
+ if((ck & Mradix) != byte 0){
+ radix = buf;
+ buf = "";
+ i = 0;
+ state = RadixSeen;
+ break;
+ }
+ if(c == '.'){
+ state = Frac;
+ break;
+ }
+ break done;
+ RadixSeen or
+ Radix =>
+ if((ck & (Mdigit|Malpha)) != byte 0){
+ state = Radix;
+ break;
+ }
+ if(c == '.'){
+ state = FracB;
+ break;
+ }
+ break done;
+ Frac =>
+ if((ck & Mdigit) != byte 0)
+ break;
+ if((ck & Mexp) != byte 0)
+ state = ExpSeen;
+ else
+ break done;
+ FracB =>
+ if((ck & (Mdigit|Malpha)) != byte 0)
+ break;
+ break done;
+ ExpSeen =>
+ if((ck & Msign) != byte 0){
+ state = ExpSignSeen;
+ break;
+ }
+ if((ck & Mdigit) != byte 0){
+ state = Exp;
+ break;
+ }
+ break done;
+ ExpSignSeen or
+ Exp =>
+ if((ck & Mdigit) != byte 0){
+ state = Exp;
+ break;
+ }
+ break done;
+ }
+ buf[i++] = c;
+ }
+
+ ungetc(c);
+ v: big;
+ case state{
+ * =>
+ yyerror("malformed numerical constant '"+radix+buf+"'");
+ yyctxt.lval.tok.v.ival = big 0;
+ return Lconst;
+ Radix =>
+ v = strtoi(radix, big 10);
+ if(v < big 2 || v > big 36){
+ yyerror("radix '"+radix+"' is not between 2 and 36");
+ break;
+ }
+ v = strtoi(buf[1:], v);
+ Int =>
+ v = strtoi(buf, big 10);
+ Frac or
+ Exp =>
+ yyctxt.lval.tok.v.rval = real buf;
+ return Lrconst;
+ FracB =>
+ v = strtoi(radix, big 10);
+ if(v < big 2 || v > big 36){
+ yyerror("radix '"+radix+"' is not between 2 and 36");
+ break;
+ }
+ yyctxt.lval.tok.v.rval = strtodb(buf[1:], int v);
+ return Lrconst;
+ }
+ yyctxt.lval.tok.v.ival = v;
+ return Lconst;
+}
+
+escchar(): int
+{
+ c := getc();
+ if(c == Bufio->EOF)
+ return Bufio->EOF;
+ if(c == 'u'){
+ v := 0;
+ for(i := 0; i < 4; i++){
+ c = getc();
+ ck := cmap(c);
+ if(c == Bufio->EOF || (ck & (Mdigit|Mhex)) == byte 0){
+ yyerror("malformed \\u escape sequence");
+ ungetc(c);
+ break;
+ }
+ if((ck & Mdigit) != byte 0)
+ c -= '0';
+ else if((ck & Mlower) != byte 0)
+ c = c - 'a' + 10;
+ else if((ck & Mupper) != byte 0)
+ c = c - 'A' + 10;
+ v = v * 16 + c;
+ }
+ return v;
+ }
+ if(c < len escmap && (v := escmap[c]) >= 0)
+ return v;
+ s := "";
+ s[0] = c;
+ yyerror("unrecognized escape \\"+s);
+ return c;
+}
+
+lexstring()
+{
+ s := "";
+ i := 0;
+loop: for(;;){
+ case c := getc(){
+ '\\' =>
+ c = escchar();
+ if(c != Bufio->EOF)
+ s[i++] = c;
+ Bufio->EOF =>
+ yyerror("end of file in string constant");
+ break loop;
+ '\n' =>
+ yyerror("newline in string constant");
+ lineno++;
+ linepos = Linestart;
+ break loop;
+ '"' =>
+ break loop;
+ * =>
+ s[i++] = c;
+ }
+ }
+ yyctxt.lval.tok.v.idval = enterstring(s);
+}
+
+lex(): int
+{
+ for(;;){
+ yyctxt.lval.tok.src.start = (lineno << PosBits) | (linepos & PosMask);
+ case c := getc(){
+ Bufio->EOF =>
+ bin.close();
+ if(bstack == 0)
+ return Leof;
+ popinclude();
+ '#' =>
+ if(lexcom() < 0){
+ bin.close();
+ if(bstack == 0)
+ return Leof;
+ popinclude();
+ }
+ '\n' =>
+ lineno++;
+ linepos = Linestart;
+ ' ' or
+ '\t' or
+ '\r' or
+ '\v' =>
+ ;
+ '"' =>
+ lexstring();
+ return Lsconst;
+ '\'' =>
+ c = getc();
+ if(c == '\\')
+ c = escchar();
+ if(c == Bufio->EOF){
+ yyerror("end of file in character constant");
+ return Bufio->EOF;
+ }else
+ yyctxt.lval.tok.v.ival = big c;
+ c = getc();
+ if(c != '\''){
+ yyerror("missing closing '");
+ ungetc(c);
+ }
+ return Lconst;
+ '(' or
+ ')' or
+ '[' or
+ ']' or
+ '{' or
+ '}' or
+ ',' or
+ ';' or
+ '~' =>
+ return c;
+ ':' =>
+ c = getc();
+ if(c == ':')
+ return Lcons;
+ if(c == '=')
+ return Ldeclas;
+ ungetc(c);
+ return ':';
+ '.' =>
+ c = getc();
+ ungetc(c);
+ if(c != Bufio->EOF && (cmap(c) & Mdigit) != byte 0)
+ return lexnum('.');
+ return '.';
+ '|' =>
+ c = getc();
+ if(c == '=')
+ return Loreq;
+ if(c == '|')
+ return Loror;
+ ungetc(c);
+ return '|';
+ '&' =>
+ c = getc();
+ if(c == '=')
+ return Landeq;
+ if(c == '&')
+ return Landand;
+ ungetc(c);
+ return '&';
+ '^' =>
+ c = getc();
+ if(c == '=')
+ return Lxoreq;
+ ungetc(c);
+ return '^';
+ '*' =>
+ c = getc();
+ if(c == '=')
+ return Lmuleq;
+ if(c == '*'){
+ c = getc();
+ if(c == '=')
+ return Lexpeq;
+ ungetc(c);
+ return Lexp;
+ }
+ ungetc(c);
+ return '*';
+ '/' =>
+ c = getc();
+ if(c == '=')
+ return Ldiveq;
+ ungetc(c);
+ return '/';
+ '%' =>
+ c = getc();
+ if(c == '=')
+ return Lmodeq;
+ ungetc(c);
+ return '%';
+ '=' =>
+ c = getc();
+ if(c == '=')
+ return Leq;
+ if(c == '>')
+ return Llabs;
+ ungetc(c);
+ return '=';
+ '!' =>
+ c = getc();
+ if(c == '=')
+ return Lneq;
+ ungetc(c);
+ return '!';
+ '>' =>
+ c = getc();
+ if(c == '=')
+ return Lgeq;
+ if(c == '>'){
+ c = getc();
+ if(c == '=')
+ return Lrsheq;
+ ungetc(c);
+ return Lrsh;
+ }
+ ungetc(c);
+ return '>';
+ '<' =>
+ c = getc();
+ if(c == '=')
+ return Lleq;
+ if(c == '-')
+ return Lcomm;
+ if(c == '<'){
+ c = getc();
+ if(c == '=')
+ return Llsheq;
+ ungetc(c);
+ return Llsh;
+ }
+ ungetc(c);
+ return '<';
+ '+' =>
+ c = getc();
+ if(c == '=')
+ return Laddeq;
+ if(c == '+')
+ return Linc;
+ ungetc(c);
+ return '+';
+ '-' =>
+ c = getc();
+ if(c == '=')
+ return Lsubeq;
+ if(c == '-')
+ return Ldec;
+ if(c == '>')
+ return Lmdot;
+ ungetc(c);
+ return '-';
+ '0' to '9' =>
+ return lexnum(c);
+ * =>
+ if((cmap(c) & Malpha) != byte 0)
+ return lexid(c);
+ s := "";
+ s[0] = c;
+ yyerror("unknown character '"+s+"'");
+ }
+ }
+}
+
+YYLEX.lex(nil: self ref YYLEX): int
+{
+ t := lex();
+ yyctxt.lval.tok.src.stop = (lineno << PosBits) | (linepos & PosMask);
+ lasttok = t;
+ lastyylval = yyctxt.lval;
+ return t;
+}
+
+toksp(t: int): string
+{
+ case(t){
+ Lconst =>
+ return sprint("%bd", lastyylval.tok.v.ival);
+ Lrconst =>
+ return sprint("%f", lastyylval.tok.v.rval);
+ Lsconst =>
+ return sprint("\"%s\"", lastyylval.tok.v.idval.name);
+ Ltid or Lid =>
+ return lastyylval.tok.v.idval.name;
+ }
+ for(i := 0; i < len keywords; i++)
+ if(t == keywords[i].token)
+ return keywords[i].name;
+ for(i = 0; i < len tokwords; i++)
+ if(t == tokwords[i].token)
+ return tokwords[i].name;
+ if(t < 0 || t > 255)
+ fatal(sprint("bad token %d in toksp()", t));
+ buf := "Z";
+ buf[0] = t;
+ return buf;
+}
+
+enterstring(name: string): ref Sym
+{
+ h := 0;
+ n := len name;
+ for(i := 0; i < n; i++){
+ c := d := name[i];
+ c ^= c << 6;
+ h += (c << 11) ^ (c >> 1);
+ h ^= (d << 14) + (d << 7) + (d << 4) + d;
+ }
+
+ h &= HashSize-1;
+ for(s := strings[h]; s != nil; s = s.next){
+ sn := s.name;
+ if(len sn == n && sn == name)
+ return s;
+ }
+
+
+ s = ref Sym;
+ s.token = -1;
+ s.name = name;
+ s.hash = h;
+ s.next = strings[h];
+ strings[h] = s;
+ return s;
+}
+
+stringcat(s, t: ref Sym): ref Sym
+{
+ return enterstring(s.name+t.name);
+}
+
+enter(name: string, token: int): ref Sym
+{
+ h := 0;
+ n := len name;
+ for(i := 0; i < n; i++){
+ c := d := name[i];
+ c ^= c << 6;
+ h += (c << 11) ^ (c >> 1);
+ h ^= (d << 14) + (d << 7) + (d << 4) + d;
+ }
+
+ h &= HashSize-1;
+ for(s := symbols[h]; s != nil; s = s.next){
+ sn := s.name;
+ if(len sn == n && sn == name)
+ return s;
+ }
+
+ if(token == 0)
+ token = Lid;
+ s = ref Sym;
+ s.token = token;
+ s.name = name;
+ s.hash = h;
+ s.next = symbols[h];
+ symbols[h] = s;
+ return s;
+}
+
+stringpr(sym: ref Sym): string
+{
+ s := sym.name;
+ n := len s;
+ if(n > 10)
+ n = 10;
+ sb := "\"";
+ for(i := 0; i < n; i++){
+ case c := s[i]{
+ '\\' or
+ '"' or
+ '\n' or
+ '\r' or
+ '\t' or
+ '\b' or
+ '\a' or
+ '\v' or
+ '\u0000' =>
+ sb[len sb] = '\\';
+ sb[len sb] = unescmap[c];
+ * =>
+ sb[len sb] = c;
+ }
+ }
+ if(n != len s)
+ sb += "...";
+ sb[len sb] = '"';
+ return sb;
+}
+
+warn(line: Line, msg: string)
+{
+ if(errors || !dowarn)
+ return;
+ fprint(stderr, "%s: warning: %s\n", lineconv(line), msg);
+}
+
+nwarn(n: ref Node, msg: string)
+{
+ if(errors || !dowarn)
+ return;
+ fprint(stderr, "%s: warning: %s\n", lineconv(n.src.start), msg);
+}
+
+error(line: Line, msg: string)
+{
+ errors++;
+ if(errors > maxerr)
+ return;
+ fprint(stderr, "%s: %s\n", lineconv(line), msg);
+ if(errors == maxerr)
+ fprint(stderr, "too many errors, stopping\n");
+}
+
+nerror(n: ref Node, msg: string)
+{
+ errors++;
+ if(errors > maxerr)
+ return;
+ fprint(stderr, "%s: %s\n", lineconv(n.src.start), msg);
+ if(errors == maxerr)
+ fprint(stderr, "too many errors, stopping\n");
+}
+
+YYLEX.error(nil: self ref YYLEX, msg: string)
+{
+ errors++;
+ if(errors > maxerr)
+ return;
+ if(lasttok != 0)
+ fprint(stderr, "%s: near ` %s ` : %s\n", lineconv(lineno<<PosBits), toksp(lasttok), msg);
+ else
+ fprint(stderr, "%s: %s\n", lineconv(lineno<<PosBits), msg);
+ if(errors == maxerr)
+ fprint(stderr, "too many errors, stopping\n");
+}
+
+yyerror(msg: string)
+{
+ yyctxt.error(msg);
+}
+
+fatal(msg: string)
+{
+ if(errors == 0 || fabort)
+ fprint(stderr, "fatal limbo compiler error: %s\n", msg);
+ if(bout != nil)
+ sys->remove(outfile);
+ if(fabort){
+ n: ref Node;
+ if(n.ty == nil); # abort
+ }
+ raise "fail:error";
+}
+
+hex(v, n: int): string
+{
+ return sprint("%.*ux", n, v);
+}
+
+bhex(v: big, n: int): string
+{
+ return sprint("%.*bux", n, v);
+}
diff --git a/appl/cmd/limbo/limbo.b b/appl/cmd/limbo/limbo.b
new file mode 100644
index 00000000..c8f51779
--- /dev/null
+++ b/appl/cmd/limbo/limbo.b
@@ -0,0 +1,3099 @@
+implement Limbo;
+
+#line 2 "limbo.y"
+include "limbo.m";
+include "draw.m";
+
+Limbo: module {
+
+ init: fn(ctxt: ref Draw->Context, argv: list of string);
+
+ YYSTYPE: adt{
+ tok: Tok;
+ ids: ref Decl;
+ node: ref Node;
+ ty: ref Type;
+ types: ref Typelist;
+ };
+
+ YYLEX: adt {
+ lval: YYSTYPE;
+ lex: fn(nil: self ref YYLEX): int;
+ error: fn(nil: self ref YYLEX, err: string);
+ };
+Landeq: con 57346;
+Loreq: con 57347;
+Lxoreq: con 57348;
+Llsheq: con 57349;
+Lrsheq: con 57350;
+Laddeq: con 57351;
+Lsubeq: con 57352;
+Lmuleq: con 57353;
+Ldiveq: con 57354;
+Lmodeq: con 57355;
+Lexpeq: con 57356;
+Ldeclas: con 57357;
+Lload: con 57358;
+Loror: con 57359;
+Landand: con 57360;
+Lcons: con 57361;
+Leq: con 57362;
+Lneq: con 57363;
+Lleq: con 57364;
+Lgeq: con 57365;
+Llsh: con 57366;
+Lrsh: con 57367;
+Lexp: con 57368;
+Lcomm: con 57369;
+Linc: con 57370;
+Ldec: con 57371;
+Lof: con 57372;
+Lref: con 57373;
+Lif: con 57374;
+Lelse: con 57375;
+Lfn: con 57376;
+Lexcept: con 57377;
+Lraises: con 57378;
+Lmdot: con 57379;
+Lto: con 57380;
+Lor: con 57381;
+Lrconst: con 57382;
+Lconst: con 57383;
+Lid: con 57384;
+Ltid: con 57385;
+Lsconst: con 57386;
+Llabs: con 57387;
+Lnil: con 57388;
+Llen: con 57389;
+Lhd: con 57390;
+Ltl: con 57391;
+Ltagof: con 57392;
+Limplement: con 57393;
+Limport: con 57394;
+Linclude: con 57395;
+Lcon: con 57396;
+Ltype: con 57397;
+Lmodule: con 57398;
+Lcyclic: con 57399;
+Ladt: con 57400;
+Larray: con 57401;
+Llist: con 57402;
+Lchan: con 57403;
+Lself: con 57404;
+Ldo: con 57405;
+Lwhile: con 57406;
+Lfor: con 57407;
+Lbreak: con 57408;
+Lalt: con 57409;
+Lcase: con 57410;
+Lpick: con 57411;
+Lcont: con 57412;
+Lreturn: con 57413;
+Lexit: con 57414;
+Lspawn: con 57415;
+Lraise: con 57416;
+Lfix: con 57417;
+
+};
+
+#line 27 "limbo.y"
+ #
+ # lex.b
+ #
+ signdump: string; # name of function for sig debugging
+ superwarn: int;
+ debug: array of int;
+ noline: Line;
+ nosrc: Src;
+ arrayz: int;
+ emitcode: string; # emit stub routines for system module functions
+ emitdyn: int; # emit as above but for dynamic modules
+ emitsbl: string; # emit symbol file for sysm modules
+ emitstub: int; # emit type and call frames for system modules
+ emittab: string; # emit table of runtime functions for this module
+ errors: int;
+ mustcompile: int;
+ dontcompile: int;
+ asmsym: int; # generate symbols in assembly language?
+ bout: ref Bufio->Iobuf; # output file
+ bsym: ref Bufio->Iobuf; # symbol output file; nil => no sym out
+ gendis: int; # generate dis or asm?
+ fixss: int;
+ newfnptr: int; # ISELF and -ve indices
+ optims: int;
+
+ #
+ # decls.b
+ #
+ scope: int;
+ # impmod: ref Sym; # name of implementation module
+ impmods: ref Decl; # name of implementation module(s)
+ nildecl: ref Decl; # declaration for limbo's nil
+ selfdecl: ref Decl; # declaration for limbo's self
+
+ #
+ # types.b
+ #
+ tany: ref Type;
+ tbig: ref Type;
+ tbyte: ref Type;
+ terror: ref Type;
+ tint: ref Type;
+ tnone: ref Type;
+ treal: ref Type;
+ tstring: ref Type;
+ texception: ref Type;
+ tunknown: ref Type;
+ tfnptr: ref Type;
+ rtexception: ref Type;
+ descriptors: ref Desc; # list of all possible descriptors
+ tattr: array of Tattr;
+
+ #
+ # nodes.b
+ #
+ opcommute: array of int;
+ oprelinvert: array of int;
+ isused: array of int;
+ casttab: array of array of int; # instruction to cast from [1] to [2]
+
+ nfns: int; # functions defined
+ nfnexp: int;
+ fns: array of ref Decl; # decls for fns defined
+ tree: ref Node; # root of parse tree
+
+ parset: int; # time to parse
+ checkt: int; # time to typecheck
+ gent: int; # time to generate code
+ writet: int; # time to write out code
+ symt: int; # time to write out symbols
+YYEOFCODE: con 1;
+YYERRCODE: con 2;
+YYMAXDEPTH: con 200;
+
+#line 1630 "limbo.y"
+
+
+include "keyring.m";
+
+sys: Sys;
+ print, fprint, sprint: import sys;
+
+bufio: Bufio;
+ Iobuf: import bufio;
+
+str: String;
+
+keyring:Keyring;
+ md5: import keyring;
+
+math: Math;
+ import_real, export_real, isnan: import math;
+
+yyctxt: ref YYLEX;
+
+canonnan: real;
+
+debug = array[256] of {* => 0};
+
+noline = -1;
+nosrc = Src(-1, -1);
+
+infile: string;
+
+# front end
+include "arg.m";
+include "lex.b";
+include "types.b";
+include "nodes.b";
+include "decls.b";
+
+include "typecheck.b";
+
+# back end
+include "gen.b";
+include "ecom.b";
+include "asm.b";
+include "dis.b";
+include "sbl.b";
+include "stubs.b";
+include "com.b";
+include "optim.b";
+
+init(nil: ref Draw->Context, argv: list of string)
+{
+ s: string;
+
+ sys = load Sys Sys->PATH;
+ keyring = load Keyring Keyring->PATH;
+ math = load Math Math->PATH;
+ bufio = load Bufio Bufio->PATH;
+ if(bufio == nil){
+ sys->print("can't load %s: %r\n", Bufio->PATH);
+ raise("fail:bad module");
+ }
+ str = load String String->PATH;
+ if(str == nil){
+ sys->print("can't load %s: %r\n", String->PATH);
+ raise("fail:bad module");
+ }
+
+ stderr = sys->fildes(2);
+ yyctxt = ref YYLEX;
+
+ math->FPcontrol(0, Math->INVAL|Math->ZDIV|Math->OVFL|Math->UNFL|Math->INEX);
+ na := array[1] of {0.};
+ import_real(array[8] of {byte 16r7f, * => byte 16rff}, na);
+ canonnan = na[0];
+ if(!isnan(canonnan))
+ fatal("bad canonical NaN");
+
+ lexinit();
+ typeinit();
+ optabinit();
+
+ gendis = 1;
+ asmsym = 0;
+ maxerr = 20;
+ ofile := "";
+ ext := "";
+
+ arg := Arg.init(argv);
+ while(c := arg.opt()){
+ case c{
+ 'Y' =>
+ emitsbl = arg.arg();
+ if(emitsbl == nil)
+ usage();
+ 'C' =>
+ dontcompile = 1;
+ 'D' =>
+ #
+ # debug flags:
+ #
+ # a alt compilation
+ # A array constructor compilation
+ # b boolean and branch compilation
+ # c case compilation
+ # d function declaration
+ # D descriptor generation
+ # e expression compilation
+ # E addressable expression compilation
+ # f print arguments for compiled functions
+ # F constant folding
+ # g print out globals
+ # m module declaration and type checking
+ # n nil references
+ # s print sizes of output file sections
+ # S type signing
+ # t type checking function bodies
+ # T timing
+ # v global var and constant compilation
+ # x adt verification
+ # Y tuple compilation
+ # z Z bug fixes
+ #
+ s = arg.arg();
+ for(i := 0; i < len s; i++){
+ c = s[i];
+ if(c < len debug)
+ debug[c] = 1;
+ }
+ 'I' =>
+ s = arg.arg();
+ if(s == "")
+ usage();
+ addinclude(s);
+ 'G' =>
+ asmsym = 1;
+ 'S' =>
+ gendis = 0;
+ 'a' =>
+ emitstub = 1;
+ 'A' =>
+ emitstub = emitdyn = 1;
+ 'c' =>
+ mustcompile = 1;
+ 'e' =>
+ maxerr = 1000;
+ 'f' =>
+ fabort = 1;
+ 'F' =>
+ newfnptr = 1;
+ 'g' =>
+ dosym = 1;
+ 'i' =>
+ dontinline = 1;
+ 'o' =>
+ ofile = arg.arg();
+ 'O' =>
+ optims = 1;
+ 's' =>
+ s = arg.arg();
+ if(s != nil)
+ fixss = int s;
+ 't' =>
+ emittab = arg.arg();
+ if(emittab == nil)
+ usage();
+ 'T' =>
+ emitcode = arg.arg();
+ if(emitcode == nil)
+ usage();
+ 'd' =>
+ emitcode = arg.arg();
+ if(emitcode == nil)
+ usage();
+ emitdyn = 1;
+ 'w' =>
+ superwarn = dowarn;
+ dowarn = 1;
+ 'x' =>
+ ext = arg.arg();
+ 'X' =>
+ signdump = arg.arg();
+ 'z' =>
+ arrayz = 1;
+ * =>
+ usage();
+ }
+ }
+
+ addinclude("/module");
+
+ argv = arg.argv;
+ arg = nil;
+
+ if(argv == nil){
+ usage();
+ }else if(ofile != nil){
+ if(len argv != 1)
+ usage();
+ translate(hd argv, ofile, mkfileext(ofile, ".dis", ".sbl"));
+ }else{
+ pr := len argv != 1;
+ if(ext == ""){
+ ext = ".s";
+ if(gendis)
+ ext = ".dis";
+ }
+ for(; argv != nil; argv = tl argv){
+ file := hd argv;
+ (nil, s) = str->splitr(file, "/");
+ if(pr)
+ print("%s:\n", s);
+ out := mkfileext(s, ".b", ext);
+ translate(file, out, mkfileext(out, ext, ".sbl"));
+ }
+ }
+ if (toterrors > 0)
+ raise("fail:errors");
+}
+
+usage()
+{
+ fprint(stderr, "usage: limbo [-GSagwe] [-I incdir] [-o outfile] [-{T|t|d} module] [-D debug] file ...\n");
+ raise("fail:usage");
+}
+
+mkfileext(file, oldext, ext: string): string
+{
+ n := len file;
+ n2 := len oldext;
+ if(n >= n2 && file[n-n2:] == oldext)
+ file = file[:n-n2];
+ return file + ext;
+}
+
+translate(in, out, dbg: string)
+{
+ infile = in;
+ outfile = out;
+ errors = 0;
+ bins[0] = bufio->open(in, Bufio->OREAD);
+ if(bins[0] == nil){
+ fprint(stderr, "can't open %s: %r\n", in);
+ toterrors++;
+ return;
+ }
+ doemit := emitcode != "" || emitstub || emittab != "" || emitsbl != "";
+ if(!doemit){
+ bout = bufio->create(out, Bufio->OWRITE, 8r666);
+ if(bout == nil){
+ fprint(stderr, "can't open %s: %r\n", out);
+ toterrors++;
+ bins[0].close();
+ return;
+ }
+ if(dosym){
+ bsym = bufio->create(dbg, Bufio->OWRITE, 8r666);
+ if(bsym == nil)
+ fprint(stderr, "can't open %s: %r\n", dbg);
+ }
+ }
+
+ lexstart(in);
+
+ popscopes();
+ typestart();
+ declstart();
+ nfnexp = 0;
+
+ parset = sys->millisec();
+ yyparse(yyctxt);
+ parset = sys->millisec() - parset;
+
+ checkt = sys->millisec();
+ entry := typecheck(!doemit);
+ checkt = sys->millisec() - checkt;
+
+ modcom(entry);
+
+ fns = nil;
+ nfns = 0;
+ descriptors = nil;
+
+ if(debug['T'])
+ print("times: parse=%d type=%d: gen=%d write=%d symbols=%d\n",
+ parset, checkt, gent, writet, symt);
+
+ if(bout != nil)
+ bout.close();
+ if(bsym != nil)
+ bsym.close();
+ toterrors += errors;
+ if(errors && bout != nil)
+ sys->remove(out);
+ if(errors && bsym != nil)
+ sys->remove(dbg);
+}
+
+pwd(): string
+{
+ workdir := load Workdir Workdir->PATH;
+ if(workdir == nil)
+ cd := "/";
+ else
+ cd = workdir->init();
+ # sys->print("pwd: %s\n", cd);
+ return cd;
+}
+
+cleanname(s: string): string
+{
+ ls, path: list of string;
+
+ if(s == nil)
+ return nil;
+ if(s[0] != '/' && s[0] != '\\')
+ (nil, ls) = sys->tokenize(pwd(), "/\\");
+ for( ; ls != nil; ls = tl ls)
+ path = hd ls :: path;
+ (nil, ls) = sys->tokenize(s, "/\\");
+ for( ; ls != nil; ls = tl ls){
+ n := hd ls;
+ if(n == ".")
+ ;
+ else if (n == ".."){
+ if(path != nil)
+ path = tl path;
+ }
+ else
+ path = n :: path;
+ }
+ p := "";
+ for( ; path != nil; path = tl path)
+ p = "/" + hd path + p;
+ if(p == nil)
+ p = "/";
+ # sys->print("cleanname: %s\n", p);
+ return p;
+}
+
+srcpath(): string
+{
+ srcp := cleanname(infile);
+ # sys->print("srcpath: %s\n", srcp);
+ return srcp;
+}
+yyexca := array[] of {-1, 1,
+ 1, -1,
+ -2, 0,
+-1, 3,
+ 1, 3,
+ -2, 0,
+-1, 17,
+ 39, 88,
+ 50, 62,
+ 54, 88,
+ 98, 62,
+ -2, 252,
+-1, 211,
+ 59, 29,
+ 71, 29,
+ -2, 0,
+-1, 230,
+ 1, 2,
+ -2, 0,
+-1, 273,
+ 50, 176,
+ -2, 257,
+-1, 308,
+ 59, 41,
+ 71, 41,
+ 91, 41,
+ -2, 0,
+-1, 310,
+ 72, 175,
+ 85, 150,
+ 86, 150,
+ 87, 150,
+ 89, 150,
+ 90, 150,
+ 91, 150,
+ -2, 0,
+-1, 380,
+ 50, 62,
+ 98, 62,
+ -2, 252,
+-1, 381,
+ 72, 175,
+ 85, 150,
+ 86, 150,
+ 87, 150,
+ 89, 150,
+ 90, 150,
+ 91, 150,
+ -2, 0,
+-1, 387,
+ 53, 71,
+ 54, 71,
+ -2, 110,
+-1, 389,
+ 53, 72,
+ 54, 72,
+ -2, 112,
+-1, 421,
+ 72, 175,
+ 85, 150,
+ 86, 150,
+ 87, 150,
+ 89, 150,
+ 90, 150,
+ 91, 150,
+ -2, 0,
+-1, 428,
+ 72, 175,
+ 85, 150,
+ 86, 150,
+ 87, 150,
+ 89, 150,
+ 90, 150,
+ 91, 150,
+ -2, 0,
+-1, 443,
+ 53, 71,
+ 54, 71,
+ -2, 111,
+-1, 444,
+ 53, 72,
+ 54, 72,
+ -2, 113,
+-1, 452,
+ 71, 279,
+ 98, 279,
+ -2, 163,
+-1, 469,
+ 72, 175,
+ 85, 150,
+ 86, 150,
+ 87, 150,
+ 89, 150,
+ 90, 150,
+ 91, 150,
+ -2, 0,
+-1, 486,
+ 50, 126,
+ 98, 126,
+ -2, 239,
+-1, 491,
+ 71, 276,
+ -2, 0,
+-1, 503,
+ 59, 47,
+ 71, 47,
+ -2, 0,
+-1, 508,
+ 59, 41,
+ 71, 41,
+ 91, 41,
+ -2, 0,
+-1, 514,
+ 72, 175,
+ 85, 150,
+ 86, 150,
+ 87, 150,
+ 89, 150,
+ 90, 150,
+ 91, 150,
+ -2, 0,
+-1, 548,
+ 72, 175,
+ 85, 150,
+ 86, 150,
+ 87, 150,
+ 89, 150,
+ 90, 150,
+ 91, 150,
+ -2, 0,
+-1, 554,
+ 71, 154,
+ 72, 175,
+ 85, 150,
+ 86, 150,
+ 87, 150,
+ 89, 150,
+ 90, 150,
+ 91, 150,
+ -2, 0,
+-1, 562,
+ 56, 59,
+ 62, 59,
+ -2, 62,
+-1, 568,
+ 72, 175,
+ 85, 150,
+ 86, 150,
+ 87, 150,
+ 89, 150,
+ 90, 150,
+ 91, 150,
+ -2, 0,
+-1, 573,
+ 71, 157,
+ 72, 175,
+ 85, 150,
+ 86, 150,
+ 87, 150,
+ 89, 150,
+ 90, 150,
+ 91, 150,
+ -2, 0,
+-1, 577,
+ 72, 176,
+ -2, 163,
+-1, 596,
+ 71, 160,
+ 72, 175,
+ 85, 150,
+ 86, 150,
+ 87, 150,
+ 89, 150,
+ 90, 150,
+ 91, 150,
+ -2, 0,
+-1, 602,
+ 71, 168,
+ 72, 175,
+ 85, 150,
+ 86, 150,
+ 87, 150,
+ 89, 150,
+ 90, 150,
+ 91, 150,
+ -2, 0,
+-1, 606,
+ 72, 175,
+ 85, 150,
+ 86, 150,
+ 87, 150,
+ 89, 150,
+ 90, 150,
+ 91, 150,
+ -2, 0,
+-1, 609,
+ 50, 62,
+ 56, 171,
+ 62, 171,
+ 98, 62,
+ -2, 252,
+};
+YYNPROD: con 284;
+YYPRIVATE: con 57344;
+yytoknames: array of string;
+yystates: array of string;
+yydebug: con 0;
+YYLAST: con 2727;
+yyact := array[] of {
+ 379, 591, 453, 364, 505, 384, 412, 310, 369, 314,
+ 359, 451, 449, 185, 84, 83, 432, 298, 270, 15,
+ 8, 49, 213, 102, 320, 12, 42, 110, 48, 78,
+ 79, 80, 4, 35, 198, 51, 23, 459, 363, 6,
+ 458, 3, 6, 544, 486, 491, 365, 14, 382, 21,
+ 14, 353, 400, 293, 350, 423, 225, 285, 118, 330,
+ 286, 226, 223, 46, 31, 112, 465, 11, 105, 517,
+ 566, 599, 308, 186, 164, 165, 166, 167, 168, 169,
+ 170, 171, 172, 173, 174, 175, 176, 43, 117, 309,
+ 182, 183, 184, 349, 71, 10, 349, 205, 10, 208,
+ 93, 286, 286, 422, 32, 37, 119, 114, 40, 294,
+ 349, 294, 32, 585, 44, 286, 119, 428, 427, 426,
+ 547, 430, 429, 431, 231, 232, 233, 234, 235, 236,
+ 237, 238, 239, 240, 241, 242, 485, 244, 245, 246,
+ 247, 248, 249, 250, 251, 252, 253, 254, 255, 256,
+ 257, 258, 259, 260, 261, 262, 263, 264, 265, 186,
+ 6, 483, 273, 230, 482, 22, 481, 438, 14, 22,
+ 271, 424, 267, 210, 5, 409, 407, 565, 279, 187,
+ 513, 410, 284, 87, 420, 419, 418, 228, 94, 288,
+ 85, 312, 311, 90, 289, 99, 269, 415, 217, 202,
+ 5, 415, 47, 92, 82, 22, 209, 26, 303, 25,
+ 212, 19, 24, 218, 229, 508, 10, 354, 96, 601,
+ 98, 95, 100, 595, 101, 88, 89, 86, 572, 194,
+ 195, 17, 87, 557, 553, 18, 297, 19, 536, 85,
+ 525, 77, 90, 313, 326, 305, 490, 13, 512, 112,
+ 323, 318, 92, 82, 468, 207, 399, 17, 87, 383,
+ 498, 18, 215, 23, 479, 85, 316, 467, 90, 6,
+ 398, 2, 500, 13, 88, 89, 86, 14, 92, 82,
+ 194, 195, 361, 186, 43, 282, 219, 340, 194, 195,
+ 77, 114, 193, 211, 487, 499, 338, 182, 500, 559,
+ 88, 89, 86, 336, 194, 195, 488, 535, 87, 324,
+ 341, 44, 87, 325, 580, 85, 77, 579, 90, 85,
+ 381, 348, 90, 206, 19, 10, 358, 357, 92, 82,
+ 214, 393, 92, 82, 604, 33, 389, 387, 391, 448,
+ 614, 194, 195, 402, 45, 539, 194, 195, 18, 392,
+ 88, 89, 86, 356, 88, 89, 86, 321, 194, 195,
+ 192, 194, 195, 403, 404, 530, 77, 281, 317, 108,
+ 77, 416, 493, 19, 19, 421, 436, 495, 612, 186,
+ 301, 385, 604, 435, 564, 437, 507, 216, 603, 493,
+ 434, 441, 439, 115, 115, 600, 562, 116, 116, 452,
+ 543, 340, 183, 444, 443, 504, 414, 45, 316, 493,
+ 22, 18, 493, 480, 493, 597, 336, 493, 588, 70,
+ 574, 493, 63, 555, 540, 73, 473, 494, 469, 433,
+ 478, 442, 476, 76, 75, 69, 68, 74, 291, 18,
+ 54, 55, 62, 60, 61, 64, 87, 290, 268, 452,
+ 157, 91, 120, 85, 91, 104, 90, 65, 66, 67,
+ 159, 489, 507, 39, 497, 103, 92, 82, 194, 195,
+ 594, 510, 186, 77, 568, 477, 168, 487, 36, 518,
+ 523, 466, 522, 515, 516, 511, 406, 417, 88, 89,
+ 86, 87, 452, 527, 523, 529, 528, 408, 85, 329,
+ 533, 90, 593, 526, 77, 91, 224, 91, 532, 537,
+ 106, 92, 82, 34, 545, 91, 401, 177, 546, 541,
+ 523, 331, 552, 397, 335, 556, 91, 592, 299, 554,
+ 332, 300, 201, 88, 89, 86, 158, 200, 161, 197,
+ 162, 163, 560, 563, 441, 316, 179, 446, 445, 77,
+ 160, 159, 570, 328, 227, 577, 569, 575, 571, 573,
+ 81, 477, 181, 97, 177, 346, 180, 523, 178, 583,
+ 345, 41, 584, 203, 577, 606, 587, 138, 139, 140,
+ 137, 135, 586, 72, 561, 548, 386, 327, 414, 222,
+ 596, 221, 549, 73, 598, 477, 475, 577, 602, 605,
+ 91, 76, 75, 45, 607, 74, 611, 18, 474, 471,
+ 613, 425, 137, 135, 196, 477, 199, 91, 39, 188,
+ 91, 91, 19, 91, 204, 524, 243, 360, 538, 307,
+ 91, 183, 168, 287, 29, 220, 141, 142, 138, 139,
+ 140, 137, 135, 368, 91, 91, 30, 121, 1, 464,
+ 272, 274, 315, 477, 123, 124, 125, 126, 127, 128,
+ 129, 130, 131, 132, 133, 134, 136, 542, 156, 155,
+ 154, 153, 152, 151, 149, 150, 145, 146, 147, 148,
+ 144, 143, 141, 142, 138, 139, 140, 137, 135, 582,
+ 343, 581, 413, 503, 502, 590, 27, 589, 91, 144,
+ 143, 141, 142, 138, 139, 140, 137, 135, 28, 283,
+ 16, 411, 306, 355, 91, 9, 551, 550, 521, 520,
+ 91, 7, 450, 337, 266, 506, 292, 371, 109, 295,
+ 296, 107, 113, 111, 20, 87, 38, 0, 0, 199,
+ 0, 91, 85, 0, 0, 90, 0, 99, 342, 0,
+ 0, 91, 91, 319, 322, 92, 82, 0, 0, 0,
+ 0, 87, 0, 0, 0, 91, 91, 0, 85, 91,
+ 96, 90, 98, 95, 0, 0, 0, 88, 89, 86,
+ 0, 92, 82, 0, 0, 0, 0, 0, 0, 0,
+ 0, 87, 282, 77, 0, 0, 0, 0, 85, 0,
+ 0, 90, 0, 88, 89, 86, 0, 333, 91, 0,
+ 455, 92, 82, 0, 0, 0, 0, 91, 0, 77,
+ 0, 91, 0, 347, 0, 50, 91, 0, 91, 351,
+ 0, 0, 0, 88, 89, 86, 0, 91, 0, 0,
+ 52, 53, 454, 91, 0, 0, 59, 72, 0, 77,
+ 390, 57, 58, 0, 63, 0, 0, 73, 0, 0,
+ 395, 396, 0, 0, 0, 76, 75, 69, 68, 74,
+ 0, 18, 54, 55, 62, 60, 61, 64, 405, 0,
+ 0, 0, 91, 0, 0, 0, 91, 0, 0, 65,
+ 66, 67, 145, 146, 147, 148, 144, 143, 141, 142,
+ 138, 139, 140, 137, 135, 77, 0, 91, 0, 0,
+ 0, 0, 0, 366, 0, 0, 0, 196, 0, 0,
+ 91, 0, 0, 0, 0, 0, 447, 0, 50, 0,
+ 456, 0, 0, 0, 0, 460, 0, 461, 0, 0,
+ 0, 0, 0, 52, 53, 56, 97, 0, 0, 59,
+ 378, 0, 472, 0, 57, 58, 0, 63, 370, 0,
+ 73, 0, 0, 0, 0, 0, 0, 0, 76, 75,
+ 380, 68, 74, 0, 18, 54, 55, 62, 60, 61,
+ 64, 367, 509, 366, 0, 0, 13, 0, 0, 0,
+ 0, 496, 65, 66, 67, 501, 0, 0, 50, 372,
+ 0, 0, 0, 373, 374, 377, 375, 376, 77, 0,
+ 0, 0, 0, 52, 53, 56, 501, 0, 0, 59,
+ 378, 0, 0, 0, 57, 58, 0, 63, 370, 534,
+ 73, 0, 0, 0, 0, 0, 0, 0, 76, 75,
+ 380, 68, 74, 0, 18, 54, 55, 62, 60, 61,
+ 64, 367, 470, 366, 0, 0, 13, 0, 0, 0,
+ 0, 0, 65, 66, 67, 0, 0, 0, 50, 372,
+ 0, 0, 0, 373, 374, 377, 375, 376, 77, 0,
+ 0, 0, 0, 52, 53, 56, 0, 0, 0, 59,
+ 378, 0, 0, 0, 57, 58, 0, 63, 370, 0,
+ 73, 0, 0, 0, 0, 0, 0, 0, 76, 75,
+ 380, 68, 74, 0, 18, 54, 55, 62, 60, 61,
+ 64, 367, 440, 366, 0, 0, 13, 0, 0, 0,
+ 0, 0, 65, 66, 67, 0, 0, 0, 50, 372,
+ 0, 0, 0, 373, 374, 377, 375, 376, 77, 0,
+ 0, 0, 0, 52, 53, 56, 0, 0, 0, 59,
+ 378, 0, 0, 0, 57, 58, 0, 63, 370, 0,
+ 73, 0, 0, 0, 0, 0, 0, 0, 76, 75,
+ 380, 68, 74, 0, 18, 54, 55, 62, 60, 61,
+ 64, 367, 362, 608, 0, 0, 13, 0, 0, 0,
+ 0, 0, 65, 66, 67, 0, 0, 0, 50, 372,
+ 0, 0, 0, 373, 374, 377, 375, 376, 77, 0,
+ 0, 0, 0, 52, 53, 610, 0, 0, 0, 59,
+ 378, 0, 0, 0, 57, 58, 0, 63, 370, 0,
+ 73, 0, 0, 0, 0, 0, 0, 0, 76, 75,
+ 609, 68, 74, 0, 18, 54, 55, 62, 60, 61,
+ 64, 367, 576, 0, 0, 0, 13, 0, 0, 0,
+ 0, 0, 65, 66, 67, 0, 0, 50, 0, 372,
+ 0, 0, 0, 373, 374, 377, 375, 376, 77, 0,
+ 0, 0, 52, 53, 454, 0, 0, 0, 59, 378,
+ 0, 0, 0, 57, 58, 0, 63, 370, 0, 73,
+ 0, 0, 0, 0, 0, 0, 0, 76, 75, 380,
+ 68, 74, 0, 18, 54, 55, 62, 60, 61, 64,
+ 367, 366, 0, 0, 0, 13, 0, 0, 0, 0,
+ 0, 65, 66, 67, 0, 0, 50, 0, 372, 0,
+ 0, 0, 373, 374, 377, 375, 376, 77, 0, 0,
+ 0, 52, 53, 56, 0, 0, 0, 59, 378, 0,
+ 0, 0, 57, 58, 0, 63, 370, 0, 73, 0,
+ 0, 0, 0, 0, 0, 0, 76, 75, 380, 68,
+ 74, 0, 18, 54, 55, 62, 60, 61, 64, 367,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 65, 66, 67, 50, 0, 0, 0, 372, 0, 0,
+ 0, 373, 374, 377, 375, 376, 77, 0, 52, 53,
+ 56, 0, 0, 0, 59, 72, 0, 0, 0, 57,
+ 58, 0, 63, 0, 0, 73, 0, 0, 0, 0,
+ 0, 0, 0, 76, 75, 69, 275, 74, 0, 18,
+ 54, 55, 62, 60, 61, 64, 0, 0, 0, 50,
+ 0, 0, 0, 0, 0, 278, 0, 276, 277, 67,
+ 0, 0, 0, 0, 52, 53, 56, 0, 0, 0,
+ 59, 72, 0, 77, 280, 57, 58, 0, 63, 0,
+ 0, 73, 0, 0, 0, 0, 0, 0, 0, 76,
+ 75, 69, 68, 74, 0, 18, 54, 55, 62, 60,
+ 61, 64, 0, 0, 50, 0, 0, 0, 0, 0,
+ 0, 0, 0, 65, 66, 67, 0, 0, 0, 52,
+ 53, 56, 0, 0, 0, 59, 72, 0, 0, 77,
+ 57, 58, 0, 63, 0, 0, 73, 0, 0, 0,
+ 0, 0, 0, 0, 76, 75, 69, 68, 74, 0,
+ 18, 54, 55, 62, 60, 61, 64, 0, 0, 0,
+ 52, 53, 56, 0, 0, 0, 59, 72, 65, 66,
+ 67, 57, 58, 0, 63, 0, 0, 73, 0, 0,
+ 0, 0, 0, 0, 77, 76, 75, 69, 68, 74,
+ 0, 18, 54, 55, 62, 60, 61, 64, 0, 0,
+ 0, 0, 0, 0, 0, 0, 87, 0, 0, 65,
+ 66, 67, 0, 85, 0, 0, 90, 0, 99, 0,
+ 0, 0, 0, 0, 0, 77, 92, 82, 149, 150,
+ 145, 146, 147, 148, 144, 143, 141, 142, 138, 139,
+ 140, 137, 135, 463, 462, 0, 0, 101, 88, 89,
+ 86, 123, 124, 125, 126, 127, 128, 129, 130, 131,
+ 132, 133, 134, 136, 77, 156, 155, 154, 153, 152,
+ 151, 149, 150, 145, 146, 147, 148, 144, 143, 141,
+ 142, 138, 139, 140, 137, 135, 155, 154, 153, 152,
+ 151, 149, 150, 145, 146, 147, 148, 144, 143, 141,
+ 142, 138, 139, 140, 137, 135, 123, 124, 125, 126,
+ 127, 128, 129, 130, 131, 132, 133, 134, 136, 567,
+ 156, 155, 154, 153, 152, 151, 149, 150, 145, 146,
+ 147, 148, 144, 143, 141, 142, 138, 139, 140, 137,
+ 135, 154, 153, 152, 151, 149, 150, 145, 146, 147,
+ 148, 144, 143, 141, 142, 138, 139, 140, 137, 135,
+ 0, 123, 124, 125, 126, 127, 128, 129, 130, 131,
+ 132, 133, 134, 136, 558, 156, 155, 154, 153, 152,
+ 151, 149, 150, 145, 146, 147, 148, 144, 143, 141,
+ 142, 138, 139, 140, 137, 135, 152, 151, 149, 150,
+ 145, 146, 147, 148, 144, 143, 141, 142, 138, 139,
+ 140, 137, 135, 0, 0, 0, 123, 124, 125, 126,
+ 127, 128, 129, 130, 131, 132, 133, 134, 136, 531,
+ 156, 155, 154, 153, 152, 151, 149, 150, 145, 146,
+ 147, 148, 144, 143, 141, 142, 138, 139, 140, 137,
+ 135, 151, 149, 150, 145, 146, 147, 148, 144, 143,
+ 141, 142, 138, 139, 140, 137, 135, 0, 0, 0,
+ 0, 123, 124, 125, 126, 127, 128, 129, 130, 131,
+ 132, 133, 134, 136, 484, 156, 155, 154, 153, 152,
+ 151, 149, 150, 145, 146, 147, 148, 144, 143, 141,
+ 142, 138, 139, 140, 137, 135, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 123, 124, 125, 126,
+ 127, 128, 129, 130, 131, 132, 133, 134, 136, 352,
+ 156, 155, 154, 153, 152, 151, 149, 150, 145, 146,
+ 147, 148, 144, 143, 141, 142, 138, 139, 140, 137,
+ 135, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 123, 124, 125, 126, 127, 128, 129, 130, 131,
+ 132, 133, 134, 136, 344, 156, 155, 154, 153, 152,
+ 151, 149, 150, 145, 146, 147, 148, 144, 143, 141,
+ 142, 138, 139, 140, 137, 135, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 123, 124, 125, 126,
+ 127, 128, 129, 130, 131, 132, 133, 134, 136, 304,
+ 156, 155, 154, 153, 152, 151, 149, 150, 145, 146,
+ 147, 148, 144, 143, 141, 142, 138, 139, 140, 137,
+ 135, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 123, 124, 125, 126, 127, 128, 129, 130, 131,
+ 132, 133, 134, 136, 302, 156, 155, 154, 153, 152,
+ 151, 149, 150, 145, 146, 147, 148, 144, 143, 141,
+ 142, 138, 139, 140, 137, 135, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 123, 124, 125, 126,
+ 127, 128, 129, 130, 131, 132, 133, 134, 136, 191,
+ 156, 155, 154, 153, 152, 151, 149, 150, 145, 146,
+ 147, 148, 144, 143, 141, 142, 138, 139, 140, 137,
+ 135, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 123, 124, 125, 126, 127, 128, 129, 130, 131,
+ 132, 133, 134, 136, 190, 156, 155, 154, 153, 152,
+ 151, 149, 150, 145, 146, 147, 148, 144, 143, 141,
+ 142, 138, 139, 140, 137, 135, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 123, 124, 125, 126,
+ 127, 128, 129, 130, 131, 132, 133, 134, 136, 189,
+ 156, 155, 154, 153, 152, 151, 149, 150, 145, 146,
+ 147, 148, 144, 143, 141, 142, 138, 139, 140, 137,
+ 135, 0, 87, 0, 0, 0, 87, 0, 0, 85,
+ 0, 0, 90, 388, 0, 0, 90, 0, 0, 0,
+ 0, 0, 92, 394, 0, 0, 92, 82, 0, 0,
+ 0, 0, 0, 0, 122, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 88, 89, 86, 0, 88, 89,
+ 86, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 77, 0, 0, 0, 77, 123, 124, 125, 126, 127,
+ 128, 129, 130, 131, 132, 133, 134, 136, 0, 156,
+ 155, 154, 153, 152, 151, 149, 150, 145, 146, 147,
+ 148, 144, 143, 141, 142, 138, 139, 140, 137, 135,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 123, 124,
+ 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,
+ 136, 578, 156, 155, 154, 153, 152, 151, 149, 150,
+ 145, 146, 147, 148, 144, 143, 141, 142, 138, 139,
+ 140, 137, 135, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 123, 124, 125, 126, 127, 128, 129, 130, 131,
+ 132, 133, 134, 136, 519, 156, 155, 154, 153, 152,
+ 151, 149, 150, 145, 146, 147, 148, 144, 143, 141,
+ 142, 138, 139, 140, 137, 135, 0, 0, 0, 123,
+ 124, 125, 126, 127, 128, 129, 130, 131, 132, 133,
+ 134, 136, 492, 156, 155, 154, 153, 152, 151, 149,
+ 150, 145, 146, 147, 148, 144, 143, 141, 142, 138,
+ 139, 140, 137, 135, 0, 0, 0, 339, 123, 124,
+ 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,
+ 136, 0, 156, 155, 154, 153, 152, 151, 149, 150,
+ 145, 146, 147, 148, 144, 143, 141, 142, 138, 139,
+ 140, 137, 135, 0, 0, 0, 334, 123, 124, 125,
+ 126, 127, 128, 129, 130, 131, 132, 133, 134, 136,
+ 0, 156, 155, 154, 153, 152, 151, 149, 150, 145,
+ 146, 147, 148, 144, 143, 141, 142, 138, 139, 140,
+ 137, 135, 0, 514, 123, 124, 125, 126, 127, 128,
+ 129, 130, 131, 132, 133, 134, 136, 0, 156, 155,
+ 154, 153, 152, 151, 149, 150, 145, 146, 147, 148,
+ 144, 143, 141, 142, 138, 139, 140, 137, 135, 0,
+ 457, 123, 124, 125, 126, 127, 128, 129, 130, 131,
+ 132, 133, 134, 136, 0, 156, 155, 154, 153, 152,
+ 151, 149, 150, 145, 146, 147, 148, 144, 143, 141,
+ 142, 138, 139, 140, 137, 135, 156, 155, 154, 153,
+ 152, 151, 149, 150, 145, 146, 147, 148, 144, 143,
+ 141, 142, 138, 139, 140, 137, 135,
+};
+yypact := array[] of {
+ 198,-1000, 351, 172,-1000, 140,-1000,-1000, 137, 135,
+ 692, 630, 14, 274, 463,-1000, 424, 530,-1000, 285,
+ -35, 130,-1000,-1000,-1000,-1000,-1000,1507,1507,1507,
+1507, 752, 583, 116, 144, 413, 396, -19, 460, 335,
+-1000, 351, 18,-1000,-1000,-1000, 393,-1000,2272,-1000,
+ 391, 497,1548,1548,1548,1548,1548,1548,1548,1548,
+1548,1548,1548,1548,1548, 523, 501, 521,1548, 376,
+1548,-1000,1507, 579,-1000,-1000,-1000, 580,2217,2162,
+2107, 288,-1000,-1000,-1000, 752, 494, 752, 492, 487,
+ 530,-1000, 532,-1000,-1000, 752,1507, 251,1507, 134,
+ 223, 530, 260, 348, 530, 216, 752, 551, 549, -36,
+-1000, 456, 6, -37,-1000,-1000,-1000, 512,-1000, 285,
+-1000, 172,-1000,1507,1507,1507,1507,1507,1507,1507,
+1507,1507,1507,1507,1507, 622,1507,1507,1507,1507,
+1507,1507,1507,1507,1507,1507,1507,1507,1507,1507,
+1507,1507,1507,1507,1507,1507,1507,1507,1507, 389,
+ 544,1396,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,
+-1000,-1000,-1000,-1000,-1000,-1000,-1000,1452, 307, 215,
+ 752,1507,-1000,-1000,-1000, 17,2667,-1000,1507,-1000,
+-1000,-1000,-1000,1507, 388, 379, 415, 752, 13, 415,
+ 752, 752, 579, 452, 308,2052,-1000,1507,1997,-1000,
+ 752, 627, 2,-1000,-1000, 121, 285,-1000,-1000, 351,
+ 415,-1000,-1000, 334, 273, 273, 250,-1000,-1000,-1000,
+ 172,2667,2667,2667,2667,2667,2667,2667,2667,2667,
+2667,2667,2667,1507,2667, 575, 575, 575, 575, 543,
+ 543, 604, 604, 669, 669, 669, 669, 866, 866,1624,
+1848,1794,1741,1741,1687,2688, 547, -38,-1000, 406,
+ 511, 449, -39,2667,-1000,1548, 476, 485, 752,2554,
+ 479,1548,1507, 415,2515,-1000,1507, 260, 650,1942,
+ 529, 524, 415,-1000, 752, 415, 415, 413, 12, 415,
+ 752,-1000,-1000,1887,-1000, 11, 146,-1000, 625, 212,
+1121,-1000,-1000, 8, 188,-1000, 319, 546,-1000, 415,
+-1000,2277, 415,-1000,-1000,-1000,2667,-1000,-1000,1507,
+1396,2273, 722, 415, 478, 200,-1000, 185, -46, 471,
+2667,-1000,1507,-1000,-1000, 452, 452, 415,-1000, 407,
+-1000, 415,-1000, 104,-1000,-1000, 447, 103,-1000, 110,
+-1000, 351,-1000,-1000,-1000, 437, 114,-1000, 5, 99,
+ 572, 32, 370, 370,1507,1507,1507, 95,1507,2667,
+ 376,1051,-1000,-1000, 351,-1000,-1000,-1000, 752,-1000,
+ 415, 506, 505,2667,1548, 415, 415, 269, 808,-1000,
+1507, 752,2630, -2, -5, 415, 752,-1000,1587,-1000,
+ -21,-1000,-1000,-1000, 431, 197, 183, 696,-1000,-1000,
+-1000, 981, 570, 752,-1000,1507, 569, 557,1329,1507,
+ 194, 354, 94,-1000, 92, 89,1832, 64,-1000, 4,
+-1000,-1000, 244,-1000,-1000,-1000,-1000, 415, 808, 175,
+ -53,-1000,2477, 365,1548,-1000, 415,-1000,-1000,-1000,
+ 415, 305, 752,1507,-1000, 190, 219, 403, 145, 911,
+ 420,1507, 176,2593,1507,1507, -17, 429,2424, 808,
+ 609,-1000,-1000,-1000,-1000,-1000,-1000, 193,-1000, 169,
+-1000, 808,1507, 808,1507,-1000, 293,1777, 351,1507,
+ 752, 235, 167, 626,-1000, 283, 368,-1000, 625,-1000,
+ 341, 3,-1000,1507,1329, 48, 545, 553,-1000, 808,
+ 163,-1000, 361,2477,1507,-1000,-1000,2667,-1000,2667,
+-1000,-1000, 162,1722, 227,-1000,-1000, 337, 327,-1000,
+ 325, 106, 0,-1000,-1000,1667, 426,1507,1329,1507,
+ 157,-1000, 358,-1000,1260,-1000,2371,-1000,-1000,-1000,
+ 255, 427,-1000, 252,-1000,-1000, 808,-1000,1329, 41,
+-1000, 542,-1000,1260,-1000, 356, 114,2477, 468,-1000,
+-1000, 152,-1000, 353,-1000,1507, -1, 333,-1000, 148,
+-1000, 326,-1000,-1000,-1000,-1000,1260,-1000, 535,-1000,
+-1000,-1000,1191,-1000, 468, 316,1329, 278, 114, 376,
+1548,-1000,-1000,-1000,-1000,
+};
+yypgo := array[] of {
+ 0, 528, 736, 105, 33, 24, 419, 15, 14, 46,
+ 734, 733, 732, 34, 731, 728, 27, 727, 16, 4,
+ 725, 108, 8, 0, 21, 35, 13, 724, 723, 94,
+ 25, 67, 26, 12, 722, 11, 2, 38, 41, 32,
+ 721, 22, 3, 7, 719, 718, 717, 716, 715, 20,
+ 713, 712, 711, 10, 710, 697, 695, 1, 694, 693,
+ 692, 6, 5, 691, 689, 667, 19, 23, 652, 9,
+ 651, 18, 650, 649, 17, 648, 647, 643, 633,
+};
+yyr1 := array[] of {
+ 0, 76, 75, 75, 38, 38, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 30, 30, 37,
+ 37, 37, 37, 37, 37, 37, 66, 66, 48, 51,
+ 51, 51, 50, 50, 50, 50, 50, 49, 49, 73,
+ 73, 53, 53, 53, 52, 52, 52, 62, 62, 61,
+ 61, 60, 58, 58, 58, 59, 59, 59, 19, 20,
+ 20, 9, 10, 10, 6, 6, 74, 74, 74, 74,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 7, 7, 8, 8, 13, 13, 21, 21,
+ 2, 2, 2, 3, 3, 4, 4, 14, 14, 15,
+ 15, 16, 16, 16, 16, 11, 12, 12, 12, 12,
+ 5, 5, 5, 5, 40, 67, 67, 67, 41, 41,
+ 41, 54, 54, 43, 43, 43, 77, 77, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 17, 17, 18, 18, 44, 45, 45, 46, 47, 47,
+ 63, 64, 64, 36, 36, 36, 36, 36, 55, 56,
+ 56, 57, 57, 57, 57, 22, 22, 23, 23, 23,
+ 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
+ 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
+ 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
+ 23, 23, 23, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 25, 25, 25,
+ 78, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 29, 29, 31, 72, 72, 71, 71, 70,
+ 70, 70, 70, 65, 65, 32, 32, 32, 32, 27,
+ 27, 28, 28, 26, 26, 33, 33, 34, 34, 35,
+ 35, 69, 68, 68,
+};
+yyr2 := array[] of {
+ 0, 0, 5, 1, 1, 2, 2, 1, 1, 2,
+ 2, 4, 4, 4, 4, 4, 6, 1, 3, 3,
+ 5, 5, 4, 6, 5, 1, 4, 7, 6, 0,
+ 2, 1, 4, 2, 5, 5, 1, 8, 11, 0,
+ 4, 0, 2, 1, 1, 1, 5, 0, 2, 5,
+ 4, 4, 2, 2, 1, 2, 4, 4, 1, 1,
+ 3, 1, 1, 3, 6, 4, 1, 2, 3, 4,
+ 1, 1, 1, 3, 6, 2, 3, 3, 3, 3,
+ 4, 1, 1, 4, 3, 6, 1, 3, 0, 3,
+ 3, 3, 5, 1, 3, 1, 5, 0, 1, 1,
+ 3, 3, 3, 3, 3, 1, 1, 1, 3, 3,
+ 2, 3, 2, 3, 4, 4, 2, 0, 3, 2,
+ 4, 2, 4, 0, 2, 2, 3, 5, 2, 2,
+ 4, 3, 4, 6, 2, 5, 7, 10, 6, 8,
+ 3, 3, 3, 3, 3, 6, 5, 8, 2, 8,
+ 0, 2, 0, 1, 2, 2, 4, 2, 2, 4,
+ 2, 2, 4, 1, 3, 1, 3, 1, 2, 2,
+ 4, 1, 1, 3, 1, 0, 1, 1, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 4, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 1, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 6, 8, 7,
+ 5, 3, 6, 4, 2, 2, 2, 1, 4, 3,
+ 0, 4, 3, 3, 4, 6, 2, 2, 1, 1,
+ 1, 6, 1, 1, 3, 1, 3, 1, 1, 1,
+ 3, 3, 2, 1, 0, 1, 1, 3, 3, 0,
+ 1, 1, 2, 1, 3, 1, 2, 1, 3, 1,
+ 3, 2, 2, 4,
+};
+yychk := array[] of {
+-1000, -75, 73, -38, -39, 2, -37, -40, -49, -48,
+ -29, -31, -30, 75, -9, -66, -54, 59, 63, 39,
+ -10, -9, 59, -39, 72, 72, 72, 4, 16, 4,
+ 16, 50, 98, 61, 50, -4, 54, -3, -2, 39,
+ -21, 41, -32, -31, -29, 59, 98, 72, -23, -24,
+ 17, -25, 32, 33, 64, 65, 34, 43, 44, 38,
+ 67, 68, 66, 46, 69, 81, 82, 83, 60, 59,
+ -6, -29, 39, 49, 61, 58, 57, 97, -23, -23,
+ -23, -1, 60, -7, -8, 46, 83, 39, 81, 82,
+ 49, -6, 59, -31, 72, 77, 74, -1, 76, 51,
+ 78, 80, -67, 52, 59, 87, 50, -14, 34, -15,
+ -16, -11, -30, -12, -31, 59, 63, -9, 40, 98,
+ 59, -76, 72, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 38, 16, 37, 34, 35,
+ 36, 32, 33, 31, 30, 26, 27, 28, 29, 24,
+ 25, 23, 22, 21, 20, 19, 18, 59, 39, 54,
+ 53, 41, 43, 44, -24, -24, -24, -24, -24, -24,
+ -24, -24, -24, -24, -24, -24, -24, 41, 45, 45,
+ 45, 41, -24, -24, -24, -26, -23, -3, 39, 72,
+ 72, 72, 72, 4, 53, 54, -1, 45, -13, -1,
+ 45, 45, -21, 41, -1, -23, 72, 4, -23, 72,
+ 39, 70, -21, -41, 70, 2, 39, -29, -21, 70,
+ -1, 40, 40, 98, 50, 50, 98, 42, -31, -29,
+ -38, -23, -23, -23, -23, -23, -23, -23, -23, -23,
+ -23, -23, -23, 4, -23, -23, -23, -23, -23, -23,
+ -23, -23, -23, -23, -23, -23, -23, -23, -23, -23,
+ -23, -23, -23, -23, -23, -23, -27, -26, 59, -25,
+ -71, -22, -72, -23, -70, 60, 81, 82, 79, -23,
+ 42, 60, 70, -1, -23, 40, 98, -78, -23, -23,
+ 59, 59, -1, 40, 98, -1, -1, -4, -74, -1,
+ 79, 72, 72, -23, 72, -13, -51, 2, 70, 87,
+ -43, 71, 70, -32, -69, -68, -9, 34, -16, -1,
+ -5, 84, -1, -5, 59, 63, -23, 40, 42, 50,
+ 98, 45, 45, -1, 42, 45, -24, -28, -26, 42,
+ -23, -41, 98, 40, 72, 41, 41, -1, -67, 98,
+ 42, -1, 72, 40, 71, -50, -9, -49, -66, -53,
+ 2, 70, 71, -37, -42, -9, 2, 70, -77, -22,
+ 47, -17, 88, 92, 93, 95, 96, 94, 39, -23,
+ 59, -43, 40, 71, -62, 62, 40, -7, 46, -8,
+ -1, -22, -71, -23, 60, -1, -1, 45, 70, 71,
+ 98, 45, -23, -74, -74, -1, 79, 72, 50, 72,
+ 71, -52, -61, -60, -9, 91, -69, 50, 72, 71,
+ 70, -43, 98, 50, 72, 39, 87, 86, 85, 90,
+ 89, 91, -18, 59, -18, -22, -23, -22, 72, -26,
+ 71, -61, -9, -7, -8, 42, 42, -1, 70, -33,
+ -34, -35, -23, -36, 34, 2, -1, 40, 42, 42,
+ -1, -1, 77, 76, -73, 87, 50, 70, 71, -43,
+ 71, 39, -1, -23, 39, 39, -42, -9, -23, 70,
+ 59, 72, 72, 72, 72, 72, 40, 50, 62, -33,
+ 71, 98, 55, 56, 62, 72, -1, -23, 70, 76,
+ 79, -1, -58, -59, 2, -19, -20, 59, 70, 71,
+ 51, -26, 72, 4, 40, -22, -22, 86, 50, 70,
+ -44, -45, -36, -23, 16, 71, -35, -23, -36, -23,
+ 72, 72, -69, -23, -1, 72, 71, -62, 2, 62,
+ 56, -53, -65, 59, 40, -23, -42, 72, 40, 39,
+ -46, -47, -36, 71, -43, 62, -23, 71, 72, 72,
+ -19, -9, 59, -19, 59, 71, 70, 72, 48, -22,
+ -42, -22, 71, -43, 62, -36, 2, -23, 70, 62,
+ 62, -63, -64, -36, -42, 72, 40, -36, 62, -55,
+ -56, -57, 59, 34, 2, 71, -43, 62, -22, 72,
+ 62, 71, -43, 62, 56, -36, 40, -57, 2, 59,
+ 34, -57, 62, -42, 62,
+};
+yydef := array[] of {
+ 0, -2, 0, -2, 4, 0, 7, 8, 0, 0,
+ 0, 17, 0, 0, 0, 25, 0, -2, 253, 0,
+ 61, 0, 62, 5, 6, 9, 10, 0, 0, 0,
+ 0, 0, 0, 0, 0, 117, 0, 95, 93, 97,
+ 121, 0, 0, 265, 266, 252, 0, 1, 0, 177,
+ 0, 213, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 252,
+ 0, 237, 0, 0, 248, 249, 250, 0, 0, 0,
+ 0, 0, 70, 71, 72, 0, 0, 0, 0, 0,
+ 88, 81, 82, 18, 19, 0, 0, 0, 0, 0,
+ 0, 88, 0, 0, 88, 0, 0, 0, 0, 98,
+ 99, 0, 0, 105, 17, 106, 107, 0, 254, 0,
+ 63, 0, 11, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 269, 0,
+ 0, 175, 246, 247, 214, 215, 216, 217, 218, 219,
+ 220, 221, 222, 223, 224, 225, 226, 0, 0, 0,
+ 0, 0, 234, 235, 236, 0, 273, 240, 0, 13,
+ 12, 14, 15, 0, 0, 0, 75, 0, 0, 86,
+ 0, 0, 0, 0, 0, 0, 22, 0, 0, 26,
+ 0, -2, 0, 114, 123, 0, 0, 116, 122, 0,
+ 94, 90, 91, 0, 0, 0, 0, 89, 267, 268,
+ -2, 178, 179, 180, 181, 182, 183, 184, 185, 186,
+ 187, 188, 189, 0, 191, 193, 194, 195, 196, 197,
+ 198, 199, 200, 201, 202, 203, 204, 205, 206, 207,
+ 208, 209, 210, 211, 212, 192, 0, 270, 242, 243,
+ 255, 0, 0, -2, 258, 259, 0, 0, 0, 0,
+ 0, 0, 0, 231, 0, 239, 0, 0, 0, 0,
+ 73, 84, 76, 77, 0, 78, 79, 117, 0, 66,
+ 0, 20, 21, 0, 24, 0, 0, 31, -2, 0,
+ -2, 119, 123, 0, 0, 47, 0, 0, 100, 101,
+ 102, 0, 103, 104, 108, 109, 190, 238, 244, 175,
+ 0, 0, 0, 262, 0, 0, 233, 0, 271, 0,
+ 274, 241, 0, 65, 16, 0, 0, 87, 80, 0,
+ 83, 67, 23, 0, 28, 30, 0, 0, 36, 0,
+ 43, 0, 118, 124, 125, 0, 0, 123, 0, 0,
+ 0, 0, 152, 152, 175, 0, 175, 0, 0, 176,
+ -2, -2, 115, 96, 281, 282, 92, -2, 0, -2,
+ 0, 0, 256, 257, 70, 260, 261, 0, 0, 230,
+ 272, 0, 0, 0, 0, 68, 0, 27, 0, 33,
+ 39, 42, 44, 45, 0, 0, 0, 151, 128, 129,
+ 123, -2, 0, 0, 134, 0, 0, 0, -2, 0,
+ 0, 0, 0, 153, 0, 0, 0, 0, 148, 0,
+ 120, 48, 0, -2, -2, 245, 251, 227, 0, 0,
+ 275, 277, -2, 0, 165, 167, 232, 64, 74, 85,
+ 69, 0, 0, 0, 37, 0, 0, 0, 0, -2,
+ 131, 0, 0, 0, 175, 175, 0, 0, 0, 0,
+ 0, 140, 141, 142, 143, 144, -2, 0, 283, 0,
+ 229, -2, 0, 0, 0, 32, 0, 0, 0, 0,
+ 0, 0, 0, -2, 54, 0, 58, 59, -2, 130,
+ 264, 0, 132, 0, -2, 0, 0, 0, 151, 0,
+ 0, 123, 0, 163, 0, 228, 278, 164, 166, 280,
+ 34, 35, 0, 0, 0, 50, 51, 52, 53, 55,
+ 0, 0, 0, 263, 127, 0, 135, 175, -2, 175,
+ 0, 123, 0, 146, -2, 155, 0, 40, 46, 49,
+ 0, 0, -2, 0, 60, 38, 0, 133, -2, 0,
+ 138, 0, 145, -2, 158, 0, 167, -2, 0, 56,
+ 57, 0, 123, 0, 136, 175, 0, 0, 156, 0,
+ 123, 0, 171, 172, 174, 149, -2, 161, 0, 139,
+ 159, 147, -2, 169, 0, 0, -2, 0, 174, -2,
+ 172, 173, 162, 137, 170,
+};
+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, 64, 3, 3, 3, 36, 23, 3,
+ 39, 40, 34, 32, 98, 33, 54, 35, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 50, 72,
+ 26, 4, 27, 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, 41, 3, 42, 22, 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, 70, 21, 71, 65,
+};
+yytok2 := array[] of {
+ 2, 3, 5, 6, 7, 8, 9, 10, 11, 12,
+ 13, 14, 15, 16, 17, 18, 19, 20, 24, 25,
+ 28, 29, 30, 31, 37, 38, 43, 44, 45, 46,
+ 47, 48, 49, 51, 52, 53, 55, 56, 57, 58,
+ 59, 60, 61, 62, 63, 66, 67, 68, 69, 73,
+ 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
+ 84, 85, 86, 87, 88, 89, 90, 91, 92, 93,
+ 94, 95, 96, 97,
+};
+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 151 "limbo.y"
+{
+ impmods = yys[yypt-1].yyv.ids;
+ }
+2=>
+#line 154 "limbo.y"
+{
+ tree = rotater(yys[yypt-0].yyv.node);
+ }
+3=>
+#line 158 "limbo.y"
+{
+ impmods = nil;
+ tree = rotater(yys[yypt-0].yyv.node);
+ }
+4=>
+yyval.node = yys[yyp+1].yyv.node;
+5=>
+#line 166 "limbo.y"
+{
+ if(yys[yypt-1].yyv.node == nil)
+ yyval.node = yys[yypt-0].yyv.node;
+ else if(yys[yypt-0].yyv.node == nil)
+ yyval.node = yys[yypt-1].yyv.node;
+ else
+ yyval.node = mkbin(Oseq, yys[yypt-1].yyv.node, yys[yypt-0].yyv.node);
+ }
+6=>
+#line 177 "limbo.y"
+{
+ yyval.node = nil;
+ }
+7=>
+yyval.node = yys[yyp+1].yyv.node;
+8=>
+yyval.node = yys[yyp+1].yyv.node;
+9=>
+yyval.node = yys[yyp+1].yyv.node;
+10=>
+yyval.node = yys[yyp+1].yyv.node;
+11=>
+#line 185 "limbo.y"
+{
+ yyval.node = mkbin(Oas, yys[yypt-3].yyv.node, yys[yypt-1].yyv.node);
+ }
+12=>
+#line 189 "limbo.y"
+{
+ yyval.node = mkbin(Oas, yys[yypt-3].yyv.node, yys[yypt-1].yyv.node);
+ }
+13=>
+#line 193 "limbo.y"
+{
+ yyval.node = mkbin(Odas, yys[yypt-3].yyv.node, yys[yypt-1].yyv.node);
+ }
+14=>
+#line 197 "limbo.y"
+{
+ yyval.node = mkbin(Odas, yys[yypt-3].yyv.node, yys[yypt-1].yyv.node);
+ }
+15=>
+#line 201 "limbo.y"
+{
+ yyerror("illegal declaration");
+ yyval.node = nil;
+ }
+16=>
+#line 206 "limbo.y"
+{
+ yyerror("illegal declaration");
+ yyval.node = nil;
+ }
+17=>
+yyval.node = yys[yyp+1].yyv.node;
+18=>
+#line 214 "limbo.y"
+{
+ yyval.node = mkbin(Oseq, yys[yypt-2].yyv.node, yys[yypt-0].yyv.node);
+ }
+19=>
+#line 220 "limbo.y"
+{
+ includef(yys[yypt-1].yyv.tok.v.idval);
+ yyval.node = nil;
+ }
+20=>
+#line 225 "limbo.y"
+{
+ yyval.node = typedecl(yys[yypt-4].yyv.ids, yys[yypt-1].yyv.ty);
+ }
+21=>
+#line 229 "limbo.y"
+{
+ yyval.node = importdecl(yys[yypt-1].yyv.node, yys[yypt-4].yyv.ids);
+ yyval.node.src.start = yys[yypt-4].yyv.ids.src.start;
+ yyval.node.src.stop = yys[yypt-0].yyv.tok.src.stop;
+ }
+22=>
+#line 235 "limbo.y"
+{
+ yyval.node = vardecl(yys[yypt-3].yyv.ids, yys[yypt-1].yyv.ty);
+ }
+23=>
+#line 239 "limbo.y"
+{
+ yyval.node = mkbin(Ovardecli, vardecl(yys[yypt-5].yyv.ids, yys[yypt-3].yyv.ty), varinit(yys[yypt-5].yyv.ids, yys[yypt-1].yyv.node));
+ }
+24=>
+#line 243 "limbo.y"
+{
+ yyval.node = condecl(yys[yypt-4].yyv.ids, yys[yypt-1].yyv.node);
+ }
+25=>
+yyval.node = yys[yyp+1].yyv.node;
+26=>
+#line 250 "limbo.y"
+{
+ yyval.node = exdecl(yys[yypt-3].yyv.ids, nil);
+ }
+27=>
+#line 254 "limbo.y"
+{
+ yyval.node = exdecl(yys[yypt-6].yyv.ids, revids(yys[yypt-2].yyv.ids));
+ }
+28=>
+#line 260 "limbo.y"
+{
+ yys[yypt-5].yyv.ids.src.stop = yys[yypt-0].yyv.tok.src.stop;
+ yyval.node = moddecl(yys[yypt-5].yyv.ids, rotater(yys[yypt-1].yyv.node));
+ }
+29=>
+#line 267 "limbo.y"
+{
+ yyval.node = nil;
+ }
+30=>
+#line 271 "limbo.y"
+{
+ if(yys[yypt-1].yyv.node == nil)
+ yyval.node = yys[yypt-0].yyv.node;
+ else if(yys[yypt-0].yyv.node == nil)
+ yyval.node = yys[yypt-1].yyv.node;
+ else
+ yyval.node = mkn(Oseq, yys[yypt-1].yyv.node, yys[yypt-0].yyv.node);
+ }
+31=>
+#line 280 "limbo.y"
+{
+ yyval.node = nil;
+ }
+32=>
+#line 286 "limbo.y"
+{
+ yyval.node = fielddecl(Dglobal, typeids(yys[yypt-3].yyv.ids, yys[yypt-1].yyv.ty));
+ }
+33=>
+yyval.node = yys[yyp+1].yyv.node;
+34=>
+#line 291 "limbo.y"
+{
+ yyval.node = typedecl(yys[yypt-4].yyv.ids, yys[yypt-1].yyv.ty);
+ }
+35=>
+#line 295 "limbo.y"
+{
+ yyval.node = condecl(yys[yypt-4].yyv.ids, yys[yypt-1].yyv.node);
+ }
+36=>
+yyval.node = yys[yyp+1].yyv.node;
+37=>
+#line 302 "limbo.y"
+{
+ yys[yypt-7].yyv.ids.src.stop = yys[yypt-1].yyv.tok.src.stop;
+ yyval.node = adtdecl(yys[yypt-7].yyv.ids, rotater(yys[yypt-2].yyv.node));
+ yyval.node.ty.polys = yys[yypt-4].yyv.ids;
+ yyval.node.ty.val = rotater(yys[yypt-0].yyv.node);
+ }
+38=>
+#line 309 "limbo.y"
+{
+ yys[yypt-10].yyv.ids.src.stop = yys[yypt-0].yyv.tok.src.stop;
+ yyval.node = adtdecl(yys[yypt-10].yyv.ids, rotater(yys[yypt-1].yyv.node));
+ yyval.node.ty.polys = yys[yypt-7].yyv.ids;
+ yyval.node.ty.val = rotater(yys[yypt-4].yyv.node);
+ }
+39=>
+#line 318 "limbo.y"
+{
+ yyval.node = nil;
+ }
+40=>
+#line 322 "limbo.y"
+{
+ yyval.node = yys[yypt-1].yyv.node;
+ }
+41=>
+#line 328 "limbo.y"
+{
+ yyval.node = nil;
+ }
+42=>
+#line 332 "limbo.y"
+{
+ if(yys[yypt-1].yyv.node == nil)
+ yyval.node = yys[yypt-0].yyv.node;
+ else if(yys[yypt-0].yyv.node == nil)
+ yyval.node = yys[yypt-1].yyv.node;
+ else
+ yyval.node = mkn(Oseq, yys[yypt-1].yyv.node, yys[yypt-0].yyv.node);
+ }
+43=>
+#line 341 "limbo.y"
+{
+ yyval.node = nil;
+ }
+44=>
+yyval.node = yys[yyp+1].yyv.node;
+45=>
+yyval.node = yys[yyp+1].yyv.node;
+46=>
+#line 349 "limbo.y"
+{
+ yyval.node = condecl(yys[yypt-4].yyv.ids, yys[yypt-1].yyv.node);
+ }
+47=>
+#line 355 "limbo.y"
+{
+ yyval.node = nil;
+ }
+48=>
+#line 359 "limbo.y"
+{
+ if(yys[yypt-1].yyv.node == nil)
+ yyval.node = yys[yypt-0].yyv.node;
+ else if(yys[yypt-0].yyv.node == nil)
+ yyval.node = yys[yypt-1].yyv.node;
+ else
+ yyval.node = mkn(Oseq, yys[yypt-1].yyv.node, yys[yypt-0].yyv.node);
+ }
+49=>
+#line 370 "limbo.y"
+{
+ for(d := yys[yypt-4].yyv.ids; d != nil; d = d.next)
+ d.cyc = byte 1;
+ yyval.node = fielddecl(Dfield, typeids(yys[yypt-4].yyv.ids, yys[yypt-1].yyv.ty));
+ }
+50=>
+#line 376 "limbo.y"
+{
+ yyval.node = fielddecl(Dfield, typeids(yys[yypt-3].yyv.ids, yys[yypt-1].yyv.ty));
+ }
+51=>
+#line 382 "limbo.y"
+{
+ yyval.node = yys[yypt-1].yyv.node;
+ }
+52=>
+#line 388 "limbo.y"
+{
+ yys[yypt-1].yyv.node.right.right = yys[yypt-0].yyv.node;
+ yyval.node = yys[yypt-1].yyv.node;
+ }
+53=>
+#line 393 "limbo.y"
+{
+ yyval.node = nil;
+ }
+54=>
+#line 397 "limbo.y"
+{
+ yyval.node = nil;
+ }
+55=>
+#line 403 "limbo.y"
+{
+ yyval.node = mkn(Opickdecl, nil, mkn(Oseq, fielddecl(Dtag, yys[yypt-1].yyv.ids), nil));
+ typeids(yys[yypt-1].yyv.ids, mktype(yys[yypt-1].yyv.ids.src.start, yys[yypt-1].yyv.ids.src.stop, Tadtpick, nil, nil));
+ }
+56=>
+#line 408 "limbo.y"
+{
+ yys[yypt-3].yyv.node.right.right = yys[yypt-2].yyv.node;
+ yyval.node = mkn(Opickdecl, yys[yypt-3].yyv.node, mkn(Oseq, fielddecl(Dtag, yys[yypt-1].yyv.ids), nil));
+ typeids(yys[yypt-1].yyv.ids, mktype(yys[yypt-1].yyv.ids.src.start, yys[yypt-1].yyv.ids.src.stop, Tadtpick, nil, nil));
+ }
+57=>
+#line 414 "limbo.y"
+{
+ yyval.node = mkn(Opickdecl, nil, mkn(Oseq, fielddecl(Dtag, yys[yypt-1].yyv.ids), nil));
+ typeids(yys[yypt-1].yyv.ids, mktype(yys[yypt-1].yyv.ids.src.start, yys[yypt-1].yyv.ids.src.stop, Tadtpick, nil, nil));
+ }
+58=>
+#line 421 "limbo.y"
+{
+ yyval.ids = revids(yys[yypt-0].yyv.ids);
+ }
+59=>
+#line 427 "limbo.y"
+{
+ yyval.ids = mkids(yys[yypt-0].yyv.tok.src, yys[yypt-0].yyv.tok.v.idval, nil, nil);
+ }
+60=>
+#line 431 "limbo.y"
+{
+ yyval.ids = mkids(yys[yypt-0].yyv.tok.src, yys[yypt-0].yyv.tok.v.idval, nil, yys[yypt-2].yyv.ids);
+ }
+61=>
+#line 437 "limbo.y"
+{
+ yyval.ids = revids(yys[yypt-0].yyv.ids);
+ }
+62=>
+#line 443 "limbo.y"
+{
+ yyval.ids = mkids(yys[yypt-0].yyv.tok.src, yys[yypt-0].yyv.tok.v.idval, nil, nil);
+ }
+63=>
+#line 447 "limbo.y"
+{
+ yyval.ids = mkids(yys[yypt-0].yyv.tok.src, yys[yypt-0].yyv.tok.v.idval, nil, yys[yypt-2].yyv.ids);
+ }
+64=>
+#line 453 "limbo.y"
+{
+ yyval.ty = mktype(yys[yypt-5].yyv.tok.src.start, yys[yypt-0].yyv.tok.src.stop, Tfix, nil, nil);
+ yyval.ty.val = mkbin(Oseq, yys[yypt-3].yyv.node, yys[yypt-1].yyv.node);
+ }
+65=>
+#line 458 "limbo.y"
+{
+ yyval.ty = mktype(yys[yypt-3].yyv.tok.src.start, yys[yypt-0].yyv.tok.src.stop, Tfix, nil, nil);
+ yyval.ty.val = yys[yypt-1].yyv.node;
+ }
+66=>
+#line 465 "limbo.y"
+{
+ yyval.types = addtype(yys[yypt-0].yyv.ty, nil);
+ }
+67=>
+#line 469 "limbo.y"
+{
+ yyval.types = addtype(yys[yypt-0].yyv.ty, nil);
+ yys[yypt-0].yyv.ty.flags |= CYCLIC;
+ }
+68=>
+#line 474 "limbo.y"
+{
+ yyval.types = addtype(yys[yypt-0].yyv.ty, yys[yypt-2].yyv.types);
+ }
+69=>
+#line 478 "limbo.y"
+{
+ yyval.types = addtype(yys[yypt-0].yyv.ty, yys[yypt-3].yyv.types);
+ yys[yypt-0].yyv.ty.flags |= CYCLIC;
+ }
+70=>
+#line 485 "limbo.y"
+{
+ yyval.ty = mkidtype(yys[yypt-0].yyv.tok.src, yys[yypt-0].yyv.tok.v.idval);
+ }
+71=>
+#line 489 "limbo.y"
+{
+ yyval.ty = yys[yypt-0].yyv.ty;
+ }
+72=>
+#line 493 "limbo.y"
+{
+ yyval.ty = yys[yypt-0].yyv.ty;
+ }
+73=>
+#line 497 "limbo.y"
+{
+ yyval.ty = mkarrowtype(yys[yypt-2].yyv.ty.src.start, yys[yypt-0].yyv.tok.src.stop, yys[yypt-2].yyv.ty, yys[yypt-0].yyv.tok.v.idval);
+ }
+74=>
+#line 501 "limbo.y"
+{
+ yyval.ty = mkarrowtype(yys[yypt-5].yyv.ty.src.start, yys[yypt-3].yyv.tok.src.stop, yys[yypt-5].yyv.ty, yys[yypt-3].yyv.tok.v.idval);
+ yyval.ty = mkinsttype(yys[yypt-5].yyv.ty.src, yyval.ty, yys[yypt-1].yyv.types);
+ }
+75=>
+#line 506 "limbo.y"
+{
+ yyval.ty = mktype(yys[yypt-1].yyv.tok.src.start, yys[yypt-0].yyv.ty.src.stop, Tref, yys[yypt-0].yyv.ty, nil);
+ }
+76=>
+#line 510 "limbo.y"
+{
+ yyval.ty = mktype(yys[yypt-2].yyv.tok.src.start, yys[yypt-0].yyv.ty.src.stop, Tchan, yys[yypt-0].yyv.ty, nil);
+ }
+77=>
+#line 514 "limbo.y"
+{
+ if(yys[yypt-1].yyv.ids.next == nil)
+ yyval.ty = yys[yypt-1].yyv.ids.ty;
+ else
+ yyval.ty = mktype(yys[yypt-2].yyv.tok.src.start, yys[yypt-0].yyv.tok.src.stop, Ttuple, nil, revids(yys[yypt-1].yyv.ids));
+ }
+78=>
+#line 521 "limbo.y"
+{
+ yyval.ty = mktype(yys[yypt-2].yyv.tok.src.start, yys[yypt-0].yyv.ty.src.stop, Tarray, yys[yypt-0].yyv.ty, nil);
+ }
+79=>
+#line 525 "limbo.y"
+{
+ yyval.ty = mktype(yys[yypt-2].yyv.tok.src.start, yys[yypt-0].yyv.ty.src.stop, Tlist, yys[yypt-0].yyv.ty, nil);
+ }
+80=>
+#line 529 "limbo.y"
+{
+ yys[yypt-1].yyv.ty.src.start = yys[yypt-3].yyv.tok.src.start;
+ yys[yypt-1].yyv.ty.polys = yys[yypt-2].yyv.ids;
+ yys[yypt-1].yyv.ty.eraises = yys[yypt-0].yyv.node;
+ yyval.ty = yys[yypt-1].yyv.ty;
+ }
+81=>
+yyval.ty = yys[yyp+1].yyv.ty;
+82=>
+#line 549 "limbo.y"
+{
+ yyval.ty = mkidtype(yys[yypt-0].yyv.tok.src, yys[yypt-0].yyv.tok.v.idval);
+ }
+83=>
+#line 553 "limbo.y"
+{
+ yyval.ty = mkinsttype(yys[yypt-3].yyv.tok.src, mkidtype(yys[yypt-3].yyv.tok.src, yys[yypt-3].yyv.tok.v.idval), yys[yypt-1].yyv.types);
+ }
+84=>
+#line 559 "limbo.y"
+{
+ yyval.ty = mkdottype(yys[yypt-2].yyv.ty.src.start, yys[yypt-0].yyv.tok.src.stop, yys[yypt-2].yyv.ty, yys[yypt-0].yyv.tok.v.idval);
+ }
+85=>
+#line 563 "limbo.y"
+{
+ yyval.ty = mkdottype(yys[yypt-5].yyv.ty.src.start, yys[yypt-3].yyv.tok.src.stop, yys[yypt-5].yyv.ty, yys[yypt-3].yyv.tok.v.idval);
+ yyval.ty = mkinsttype(yys[yypt-5].yyv.ty.src, yyval.ty, yys[yypt-1].yyv.types);
+ }
+86=>
+#line 570 "limbo.y"
+{
+ yyval.ids = mkids(yys[yypt-0].yyv.ty.src, nil, yys[yypt-0].yyv.ty, nil);
+ }
+87=>
+#line 574 "limbo.y"
+{
+ yyval.ids = mkids(yys[yypt-2].yyv.ids.src, nil, yys[yypt-0].yyv.ty, yys[yypt-2].yyv.ids);
+ }
+88=>
+#line 580 "limbo.y"
+{
+ yyval.ids = nil;
+ }
+89=>
+#line 584 "limbo.y"
+{
+ yyval.ids = polydecl(yys[yypt-1].yyv.ids);
+ }
+90=>
+#line 590 "limbo.y"
+{
+ yyval.ty = mktype(yys[yypt-2].yyv.tok.src.start, yys[yypt-0].yyv.tok.src.stop, Tfn, tnone, yys[yypt-1].yyv.ids);
+ }
+91=>
+#line 594 "limbo.y"
+{
+ yyval.ty = mktype(yys[yypt-2].yyv.tok.src.start, yys[yypt-0].yyv.tok.src.stop, Tfn, tnone, nil);
+ yyval.ty.varargs = byte 1;
+ }
+92=>
+#line 599 "limbo.y"
+{
+ yyval.ty = mktype(yys[yypt-4].yyv.tok.src.start, yys[yypt-0].yyv.tok.src.stop, Tfn, tnone, yys[yypt-3].yyv.ids);
+ yyval.ty.varargs = byte 1;
+ }
+93=>
+#line 606 "limbo.y"
+{
+ yyval.ty = yys[yypt-0].yyv.ty;
+ }
+94=>
+#line 610 "limbo.y"
+{
+ yys[yypt-2].yyv.ty.tof = yys[yypt-0].yyv.ty;
+ yys[yypt-2].yyv.ty.src.stop = yys[yypt-0].yyv.ty.src.stop;
+ yyval.ty = yys[yypt-2].yyv.ty;
+ }
+95=>
+#line 618 "limbo.y"
+{
+ yyval.ty = yys[yypt-0].yyv.ty;
+ }
+96=>
+#line 622 "limbo.y"
+{
+ yyval.ty = yys[yypt-4].yyv.ty;
+ yyval.ty.val = rotater(yys[yypt-1].yyv.node);
+ }
+97=>
+#line 629 "limbo.y"
+{
+ yyval.ids = nil;
+ }
+98=>
+yyval.ids = yys[yyp+1].yyv.ids;
+99=>
+yyval.ids = yys[yyp+1].yyv.ids;
+100=>
+#line 637 "limbo.y"
+{
+ yyval.ids = appdecls(yys[yypt-2].yyv.ids, yys[yypt-0].yyv.ids);
+ }
+101=>
+#line 643 "limbo.y"
+{
+ yyval.ids = typeids(yys[yypt-2].yyv.ids, yys[yypt-0].yyv.ty);
+ }
+102=>
+#line 647 "limbo.y"
+{
+ yyval.ids = typeids(yys[yypt-2].yyv.ids, yys[yypt-0].yyv.ty);
+ for(d := yyval.ids; d != nil; d = d.next)
+ d.implicit = byte 1;
+ }
+103=>
+#line 653 "limbo.y"
+{
+ yyval.ids = mkids(yys[yypt-2].yyv.node.src, enter("junk", 0), yys[yypt-0].yyv.ty, nil);
+ yyval.ids.store = Darg;
+ yyerror("illegal argument declaraion");
+ }
+104=>
+#line 659 "limbo.y"
+{
+ yyval.ids = mkids(yys[yypt-2].yyv.node.src, enter("junk", 0), yys[yypt-0].yyv.ty, nil);
+ yyval.ids.store = Darg;
+ yyerror("illegal argument declaraion");
+ }
+105=>
+#line 667 "limbo.y"
+{
+ yyval.ids = revids(yys[yypt-0].yyv.ids);
+ }
+106=>
+#line 673 "limbo.y"
+{
+ yyval.ids = mkids(yys[yypt-0].yyv.tok.src, yys[yypt-0].yyv.tok.v.idval, nil, nil);
+ yyval.ids.store = Darg;
+ }
+107=>
+#line 678 "limbo.y"
+{
+ yyval.ids = mkids(yys[yypt-0].yyv.tok.src, nil, nil, nil);
+ yyval.ids.store = Darg;
+ }
+108=>
+#line 683 "limbo.y"
+{
+ yyval.ids = mkids(yys[yypt-0].yyv.tok.src, yys[yypt-0].yyv.tok.v.idval, nil, yys[yypt-2].yyv.ids);
+ yyval.ids.store = Darg;
+ }
+109=>
+#line 688 "limbo.y"
+{
+ yyval.ids = mkids(yys[yypt-0].yyv.tok.src, nil, nil, yys[yypt-2].yyv.ids);
+ yyval.ids.store = Darg;
+ }
+110=>
+#line 695 "limbo.y"
+{
+ yyval.ty = yys[yypt-0].yyv.ty;
+ }
+111=>
+#line 699 "limbo.y"
+{
+ yyval.ty = mktype(yys[yypt-1].yyv.tok.src.start, yys[yypt-0].yyv.tok.src.stop, Tref, yys[yypt-0].yyv.ty, nil);
+ }
+112=>
+#line 703 "limbo.y"
+{
+ yyval.ty = yys[yypt-0].yyv.ty;
+ }
+113=>
+#line 707 "limbo.y"
+{
+ yyval.ty = mktype(yys[yypt-1].yyv.tok.src.start, yys[yypt-0].yyv.tok.src.stop, Tref, yys[yypt-0].yyv.ty, nil);
+ }
+114=>
+#line 713 "limbo.y"
+{
+ yyval.node = fndecl(yys[yypt-3].yyv.node, yys[yypt-2].yyv.ty, yys[yypt-0].yyv.node);
+ nfns++;
+ # patch up polydecs
+ if(yys[yypt-3].yyv.node.op == Odot){
+ if(yys[yypt-3].yyv.node.right.left != nil){
+ yys[yypt-2].yyv.ty.polys = yys[yypt-3].yyv.node.right.left.decl;
+ yys[yypt-3].yyv.node.right.left = nil;
+ }
+ if(yys[yypt-3].yyv.node.left.op == Oname && yys[yypt-3].yyv.node.left.left != nil){
+ yyval.node.decl = yys[yypt-3].yyv.node.left.left.decl;
+ yys[yypt-3].yyv.node.left.left = nil;
+ }
+ }
+ else{
+ if(yys[yypt-3].yyv.node.left != nil){
+ yys[yypt-2].yyv.ty.polys = yys[yypt-3].yyv.node.left.decl;
+ yys[yypt-3].yyv.node.left = nil;
+ }
+ }
+ yys[yypt-2].yyv.ty.eraises = yys[yypt-1].yyv.node;
+ yyval.node.src = yys[yypt-3].yyv.node.src;
+ }
+115=>
+#line 739 "limbo.y"
+{
+ yyval.node = mkn(Otuple, rotater(yys[yypt-1].yyv.node), nil);
+ yyval.node.src.start = yys[yypt-3].yyv.tok.src.start;
+ yyval.node.src.stop = yys[yypt-0].yyv.tok.src.stop;
+ }
+116=>
+#line 745 "limbo.y"
+{
+ yyval.node = mkn(Otuple, mkunary(Oseq, yys[yypt-0].yyv.node), nil);
+ yyval.node.src.start = yys[yypt-1].yyv.tok.src.start;
+ yyval.node.src.stop = yys[yypt-0].yyv.node.src.stop;
+ }
+117=>
+#line 751 "limbo.y"
+{
+ yyval.node = nil;
+ }
+118=>
+#line 757 "limbo.y"
+{
+ if(yys[yypt-1].yyv.node == nil){
+ yys[yypt-1].yyv.node = mkn(Onothing, nil, nil);
+ yys[yypt-1].yyv.node.src.start = curline();
+ yys[yypt-1].yyv.node.src.stop = yys[yypt-1].yyv.node.src.start;
+ }
+ yyval.node = rotater(yys[yypt-1].yyv.node);
+ yyval.node.src.start = yys[yypt-2].yyv.tok.src.start;
+ yyval.node.src.stop = yys[yypt-0].yyv.tok.src.stop;
+ }
+119=>
+#line 768 "limbo.y"
+{
+ yyval.node = mkn(Onothing, nil, nil);
+ }
+120=>
+#line 772 "limbo.y"
+{
+ yyval.node = mkn(Onothing, nil, nil);
+ }
+121=>
+#line 778 "limbo.y"
+{
+ yyval.node = mkname(yys[yypt-1].yyv.tok.src, yys[yypt-1].yyv.tok.v.idval);
+ if(yys[yypt-0].yyv.ids != nil){
+ yyval.node.left = mkn(Onothing, nil ,nil);
+ yyval.node.left.decl = yys[yypt-0].yyv.ids;
+ }
+ }
+122=>
+#line 786 "limbo.y"
+{
+ yyval.node = mkbin(Odot, yys[yypt-3].yyv.node, mkname(yys[yypt-1].yyv.tok.src, yys[yypt-1].yyv.tok.v.idval));
+ if(yys[yypt-0].yyv.ids != nil){
+ yyval.node.right.left = mkn(Onothing, nil ,nil);
+ yyval.node.right.left.decl = yys[yypt-0].yyv.ids;
+ }
+ }
+123=>
+#line 796 "limbo.y"
+{
+ yyval.node = nil;
+ }
+124=>
+#line 800 "limbo.y"
+{
+ if(yys[yypt-1].yyv.node == nil)
+ yyval.node = yys[yypt-0].yyv.node;
+ else if(yys[yypt-0].yyv.node == nil)
+ yyval.node = yys[yypt-1].yyv.node;
+ else
+ yyval.node = mkbin(Oseq, yys[yypt-1].yyv.node, yys[yypt-0].yyv.node);
+ }
+125=>
+#line 809 "limbo.y"
+{
+ if(yys[yypt-1].yyv.node == nil)
+ yyval.node = yys[yypt-0].yyv.node;
+ else
+ yyval.node = mkbin(Oseq, yys[yypt-1].yyv.node, yys[yypt-0].yyv.node);
+ }
+128=>
+#line 822 "limbo.y"
+{
+ yyval.node = mkn(Onothing, nil, nil);
+ yyval.node.src.start = curline();
+ yyval.node.src.stop = yyval.node.src.start;
+ }
+129=>
+#line 828 "limbo.y"
+{
+ yyval.node = mkn(Onothing, nil, nil);
+ yyval.node.src.start = curline();
+ yyval.node.src.stop = yyval.node.src.start;
+ }
+130=>
+#line 834 "limbo.y"
+{
+ yyval.node = mkn(Onothing, nil, nil);
+ yyval.node.src.start = curline();
+ yyval.node.src.stop = yyval.node.src.start;
+ }
+131=>
+#line 840 "limbo.y"
+{
+ if(yys[yypt-1].yyv.node == nil){
+ yys[yypt-1].yyv.node = mkn(Onothing, nil, nil);
+ yys[yypt-1].yyv.node.src.start = curline();
+ yys[yypt-1].yyv.node.src.stop = yys[yypt-1].yyv.node.src.start;
+ }
+ yyval.node = mkscope(rotater(yys[yypt-1].yyv.node));
+ }
+132=>
+#line 849 "limbo.y"
+{
+ yyerror("illegal declaration");
+ yyval.node = mkn(Onothing, nil, nil);
+ yyval.node.src.start = curline();
+ yyval.node.src.stop = yyval.node.src.start;
+ }
+133=>
+#line 856 "limbo.y"
+{
+ yyerror("illegal declaration");
+ yyval.node = mkn(Onothing, nil, nil);
+ yyval.node.src.start = curline();
+ yyval.node.src.stop = yyval.node.src.start;
+ }
+134=>
+#line 863 "limbo.y"
+{
+ yyval.node = yys[yypt-1].yyv.node;
+ }
+135=>
+#line 867 "limbo.y"
+{
+ yyval.node = mkn(Oif, yys[yypt-2].yyv.node, mkunary(Oseq, yys[yypt-0].yyv.node));
+ yyval.node.src.start = yys[yypt-4].yyv.tok.src.start;
+ yyval.node.src.stop = yys[yypt-0].yyv.node.src.stop;
+ }
+136=>
+#line 873 "limbo.y"
+{
+ yyval.node = mkn(Oif, yys[yypt-4].yyv.node, mkbin(Oseq, yys[yypt-2].yyv.node, yys[yypt-0].yyv.node));
+ yyval.node.src.start = yys[yypt-6].yyv.tok.src.start;
+ yyval.node.src.stop = yys[yypt-0].yyv.node.src.stop;
+ }
+137=>
+#line 879 "limbo.y"
+{
+ yyval.node = mkunary(Oseq, yys[yypt-0].yyv.node);
+ if(yys[yypt-2].yyv.node.op != Onothing)
+ yyval.node.right = yys[yypt-2].yyv.node;
+ yyval.node = mkbin(Ofor, yys[yypt-4].yyv.node, yyval.node);
+ yyval.node.decl = yys[yypt-9].yyv.ids;
+ if(yys[yypt-6].yyv.node.op != Onothing)
+ yyval.node = mkbin(Oseq, yys[yypt-6].yyv.node, yyval.node);
+ }
+138=>
+#line 889 "limbo.y"
+{
+ yyval.node = mkn(Ofor, yys[yypt-2].yyv.node, mkunary(Oseq, yys[yypt-0].yyv.node));
+ yyval.node.src.start = yys[yypt-4].yyv.tok.src.start;
+ yyval.node.src.stop = yys[yypt-0].yyv.node.src.stop;
+ yyval.node.decl = yys[yypt-5].yyv.ids;
+ }
+139=>
+#line 896 "limbo.y"
+{
+ yyval.node = mkn(Odo, yys[yypt-2].yyv.node, yys[yypt-5].yyv.node);
+ yyval.node.src.start = yys[yypt-6].yyv.tok.src.start;
+ yyval.node.src.stop = yys[yypt-1].yyv.tok.src.stop;
+ yyval.node.decl = yys[yypt-7].yyv.ids;
+ }
+140=>
+#line 903 "limbo.y"
+{
+ yyval.node = mkn(Obreak, nil, nil);
+ yyval.node.decl = yys[yypt-1].yyv.ids;
+ yyval.node.src = yys[yypt-2].yyv.tok.src;
+ }
+141=>
+#line 909 "limbo.y"
+{
+ yyval.node = mkn(Ocont, nil, nil);
+ yyval.node.decl = yys[yypt-1].yyv.ids;
+ yyval.node.src = yys[yypt-2].yyv.tok.src;
+ }
+142=>
+#line 915 "limbo.y"
+{
+ yyval.node = mkn(Oret, yys[yypt-1].yyv.node, nil);
+ yyval.node.src = yys[yypt-2].yyv.tok.src;
+ if(yys[yypt-1].yyv.node.op == Onothing)
+ yyval.node.left = nil;
+ else
+ yyval.node.src.stop = yys[yypt-1].yyv.node.src.stop;
+ }
+143=>
+#line 924 "limbo.y"
+{
+ yyval.node = mkn(Ospawn, yys[yypt-1].yyv.node, nil);
+ yyval.node.src.start = yys[yypt-2].yyv.tok.src.start;
+ yyval.node.src.stop = yys[yypt-1].yyv.node.src.stop;
+ }
+144=>
+#line 930 "limbo.y"
+{
+ yyval.node = mkn(Oraise, yys[yypt-1].yyv.node, nil);
+ yyval.node.src.start = yys[yypt-2].yyv.tok.src.start;
+ yyval.node.src.stop = yys[yypt-1].yyv.node.src.stop;
+ }
+145=>
+#line 936 "limbo.y"
+{
+ yyval.node = mkn(Ocase, yys[yypt-3].yyv.node, caselist(yys[yypt-1].yyv.node, nil));
+ yyval.node.src = yys[yypt-3].yyv.node.src;
+ yyval.node.decl = yys[yypt-5].yyv.ids;
+ }
+146=>
+#line 942 "limbo.y"
+{
+ yyval.node = mkn(Oalt, caselist(yys[yypt-1].yyv.node, nil), nil);
+ yyval.node.src = yys[yypt-3].yyv.tok.src;
+ yyval.node.decl = yys[yypt-4].yyv.ids;
+ }
+147=>
+#line 948 "limbo.y"
+{
+ yyval.node = mkn(Opick, mkbin(Odas, mkname(yys[yypt-5].yyv.tok.src, yys[yypt-5].yyv.tok.v.idval), yys[yypt-3].yyv.node), caselist(yys[yypt-1].yyv.node, nil));
+ yyval.node.src.start = yys[yypt-5].yyv.tok.src.start;
+ yyval.node.src.stop = yys[yypt-3].yyv.node.src.stop;
+ yyval.node.decl = yys[yypt-7].yyv.ids;
+ }
+148=>
+#line 955 "limbo.y"
+{
+ yyval.node = mkn(Oexit, nil, nil);
+ yyval.node.src = yys[yypt-1].yyv.tok.src;
+ }
+149=>
+#line 960 "limbo.y"
+{
+ if(yys[yypt-6].yyv.node == nil){
+ yys[yypt-6].yyv.node = mkn(Onothing, nil, nil);
+ yys[yypt-6].yyv.node.src.start = yys[yypt-6].yyv.node.src.stop = curline();
+ }
+ yys[yypt-6].yyv.node = mkscope(rotater(yys[yypt-6].yyv.node));
+ yyval.node = mkbin(Oexstmt, yys[yypt-6].yyv.node, mkn(Oexcept, yys[yypt-3].yyv.node, caselist(yys[yypt-1].yyv.node, nil)));
+ }
+150=>
+#line 975 "limbo.y"
+{
+ yyval.ids = nil;
+ }
+151=>
+#line 979 "limbo.y"
+{
+ if(yys[yypt-1].yyv.ids.next != nil)
+ yyerror("only one identifier allowed in a label");
+ yyval.ids = yys[yypt-1].yyv.ids;
+ }
+152=>
+#line 987 "limbo.y"
+{
+ yyval.ids = nil;
+ }
+153=>
+#line 991 "limbo.y"
+{
+ yyval.ids = mkids(yys[yypt-0].yyv.tok.src, yys[yypt-0].yyv.tok.v.idval, nil, nil);
+ }
+154=>
+#line 997 "limbo.y"
+{
+ yys[yypt-1].yyv.node.left.right.right = yys[yypt-0].yyv.node;
+ yyval.node = yys[yypt-1].yyv.node;
+ }
+155=>
+#line 1004 "limbo.y"
+{
+ yyval.node = mkunary(Oseq, mkscope(mkunary(Olabel, rotater(yys[yypt-1].yyv.node))));
+ }
+156=>
+#line 1008 "limbo.y"
+{
+ yys[yypt-3].yyv.node.left.right.right = yys[yypt-2].yyv.node;
+ yyval.node = mkbin(Oseq, mkscope(mkunary(Olabel, rotater(yys[yypt-1].yyv.node))), yys[yypt-3].yyv.node);
+ }
+157=>
+#line 1015 "limbo.y"
+{
+ yys[yypt-1].yyv.node.left.right = mkscope(yys[yypt-0].yyv.node);
+ yyval.node = yys[yypt-1].yyv.node;
+ }
+158=>
+#line 1022 "limbo.y"
+{
+ yyval.node = mkunary(Oseq, mkunary(Olabel, rotater(yys[yypt-1].yyv.node)));
+ }
+159=>
+#line 1026 "limbo.y"
+{
+ yys[yypt-3].yyv.node.left.right = mkscope(yys[yypt-2].yyv.node);
+ yyval.node = mkbin(Oseq, mkunary(Olabel, rotater(yys[yypt-1].yyv.node)), yys[yypt-3].yyv.node);
+ }
+160=>
+#line 1033 "limbo.y"
+{
+ yys[yypt-1].yyv.node.left.right = mkscope(yys[yypt-0].yyv.node);
+ yyval.node = yys[yypt-1].yyv.node;
+ }
+161=>
+#line 1040 "limbo.y"
+{
+ yyval.node = mkunary(Oseq, mkunary(Olabel, rotater(yys[yypt-1].yyv.node)));
+ }
+162=>
+#line 1044 "limbo.y"
+{
+ yys[yypt-3].yyv.node.left.right = mkscope(yys[yypt-2].yyv.node);
+ yyval.node = mkbin(Oseq, mkunary(Olabel, rotater(yys[yypt-1].yyv.node)), yys[yypt-3].yyv.node);
+ }
+163=>
+yyval.node = yys[yyp+1].yyv.node;
+164=>
+#line 1052 "limbo.y"
+{
+ yyval.node = mkbin(Orange, yys[yypt-2].yyv.node, yys[yypt-0].yyv.node);
+ }
+165=>
+#line 1056 "limbo.y"
+{
+ yyval.node = mkn(Owild, nil, nil);
+ yyval.node.src = yys[yypt-0].yyv.tok.src;
+ }
+166=>
+#line 1061 "limbo.y"
+{
+ yyval.node = mkbin(Oseq, yys[yypt-2].yyv.node, yys[yypt-0].yyv.node);
+ }
+167=>
+#line 1065 "limbo.y"
+{
+ yyval.node = mkn(Onothing, nil, nil);
+ yyval.node.src.start = curline();
+ yyval.node.src.stop = yyval.node.src.start;
+ }
+168=>
+#line 1073 "limbo.y"
+{
+ yys[yypt-1].yyv.node.left.right = mkscope(yys[yypt-0].yyv.node);
+ yyval.node = yys[yypt-1].yyv.node;
+ }
+169=>
+#line 1080 "limbo.y"
+{
+ yyval.node = mkunary(Oseq, mkunary(Olabel, rotater(yys[yypt-1].yyv.node)));
+ }
+170=>
+#line 1084 "limbo.y"
+{
+ yys[yypt-3].yyv.node.left.right = mkscope(yys[yypt-2].yyv.node);
+ yyval.node = mkbin(Oseq, mkunary(Olabel, rotater(yys[yypt-1].yyv.node)), yys[yypt-3].yyv.node);
+ }
+171=>
+#line 1091 "limbo.y"
+{
+ yyval.node = mkname(yys[yypt-0].yyv.tok.src, yys[yypt-0].yyv.tok.v.idval);
+ }
+172=>
+#line 1095 "limbo.y"
+{
+ yyval.node = mkn(Owild, nil, nil);
+ yyval.node.src = yys[yypt-0].yyv.tok.src;
+ }
+173=>
+#line 1100 "limbo.y"
+{
+ yyval.node = mkbin(Oseq, yys[yypt-2].yyv.node, yys[yypt-0].yyv.node);
+ }
+174=>
+#line 1104 "limbo.y"
+{
+ yyval.node = mkn(Onothing, nil, nil);
+ yyval.node.src.start = curline();
+ yyval.node.src.stop = yyval.node.src.start;
+ }
+175=>
+#line 1112 "limbo.y"
+{
+ yyval.node = mkn(Onothing, nil, nil);
+ yyval.node.src.start = curline();
+ yyval.node.src.stop = yyval.node.src.start;
+ }
+176=>
+yyval.node = yys[yyp+1].yyv.node;
+177=>
+yyval.node = yys[yyp+1].yyv.node;
+178=>
+#line 1122 "limbo.y"
+{
+ yyval.node = mkbin(Oas, yys[yypt-2].yyv.node, yys[yypt-0].yyv.node);
+ }
+179=>
+#line 1126 "limbo.y"
+{
+ yyval.node = mkbin(Oandas, yys[yypt-2].yyv.node, yys[yypt-0].yyv.node);
+ }
+180=>
+#line 1130 "limbo.y"
+{
+ yyval.node = mkbin(Ooras, yys[yypt-2].yyv.node, yys[yypt-0].yyv.node);
+ }
+181=>
+#line 1134 "limbo.y"
+{
+ yyval.node = mkbin(Oxoras, yys[yypt-2].yyv.node, yys[yypt-0].yyv.node);
+ }
+182=>
+#line 1138 "limbo.y"
+{
+ yyval.node = mkbin(Olshas, yys[yypt-2].yyv.node, yys[yypt-0].yyv.node);
+ }
+183=>
+#line 1142 "limbo.y"
+{
+ yyval.node = mkbin(Orshas, yys[yypt-2].yyv.node, yys[yypt-0].yyv.node);
+ }
+184=>
+#line 1146 "limbo.y"
+{
+ yyval.node = mkbin(Oaddas, yys[yypt-2].yyv.node, yys[yypt-0].yyv.node);
+ }
+185=>
+#line 1150 "limbo.y"
+{
+ yyval.node = mkbin(Osubas, yys[yypt-2].yyv.node, yys[yypt-0].yyv.node);
+ }
+186=>
+#line 1154 "limbo.y"
+{
+ yyval.node = mkbin(Omulas, yys[yypt-2].yyv.node, yys[yypt-0].yyv.node);
+ }
+187=>
+#line 1158 "limbo.y"
+{
+ yyval.node = mkbin(Odivas, yys[yypt-2].yyv.node, yys[yypt-0].yyv.node);
+ }
+188=>
+#line 1162 "limbo.y"
+{
+ yyval.node = mkbin(Omodas, yys[yypt-2].yyv.node, yys[yypt-0].yyv.node);
+ }
+189=>
+#line 1166 "limbo.y"
+{
+ yyval.node = mkbin(Oexpas, yys[yypt-2].yyv.node, yys[yypt-0].yyv.node);
+ }
+190=>
+#line 1170 "limbo.y"
+{
+ yyval.node = mkbin(Osnd, yys[yypt-3].yyv.node, yys[yypt-0].yyv.node);
+ }
+191=>
+#line 1174 "limbo.y"
+{
+ yyval.node = mkbin(Odas, yys[yypt-2].yyv.node, yys[yypt-0].yyv.node);
+ }
+192=>
+#line 1178 "limbo.y"
+{
+ yyval.node = mkn(Oload, yys[yypt-0].yyv.node, nil);
+ yyval.node.src.start = yys[yypt-2].yyv.tok.src.start;
+ yyval.node.src.stop = yys[yypt-0].yyv.node.src.stop;
+ yyval.node.ty = mkidtype(yys[yypt-1].yyv.tok.src, yys[yypt-1].yyv.tok.v.idval);
+ }
+193=>
+#line 1185 "limbo.y"
+{
+ yyval.node = yyval.node = mkbin(Oexp, yys[yypt-2].yyv.node, yys[yypt-0].yyv.node);
+ }
+194=>
+#line 1189 "limbo.y"
+{
+ yyval.node = mkbin(Omul, yys[yypt-2].yyv.node, yys[yypt-0].yyv.node);
+ }
+195=>
+#line 1193 "limbo.y"
+{
+ yyval.node = mkbin(Odiv, yys[yypt-2].yyv.node, yys[yypt-0].yyv.node);
+ }
+196=>
+#line 1197 "limbo.y"
+{
+ yyval.node = mkbin(Omod, yys[yypt-2].yyv.node, yys[yypt-0].yyv.node);
+ }
+197=>
+#line 1201 "limbo.y"
+{
+ yyval.node = mkbin(Oadd, yys[yypt-2].yyv.node, yys[yypt-0].yyv.node);
+ }
+198=>
+#line 1205 "limbo.y"
+{
+ yyval.node = mkbin(Osub, yys[yypt-2].yyv.node, yys[yypt-0].yyv.node);
+ }
+199=>
+#line 1209 "limbo.y"
+{
+ yyval.node = mkbin(Orsh, yys[yypt-2].yyv.node, yys[yypt-0].yyv.node);
+ }
+200=>
+#line 1213 "limbo.y"
+{
+ yyval.node = mkbin(Olsh, yys[yypt-2].yyv.node, yys[yypt-0].yyv.node);
+ }
+201=>
+#line 1217 "limbo.y"
+{
+ yyval.node = mkbin(Olt, yys[yypt-2].yyv.node, yys[yypt-0].yyv.node);
+ }
+202=>
+#line 1221 "limbo.y"
+{
+ yyval.node = mkbin(Ogt, yys[yypt-2].yyv.node, yys[yypt-0].yyv.node);
+ }
+203=>
+#line 1225 "limbo.y"
+{
+ yyval.node = mkbin(Oleq, yys[yypt-2].yyv.node, yys[yypt-0].yyv.node);
+ }
+204=>
+#line 1229 "limbo.y"
+{
+ yyval.node = mkbin(Ogeq, yys[yypt-2].yyv.node, yys[yypt-0].yyv.node);
+ }
+205=>
+#line 1233 "limbo.y"
+{
+ yyval.node = mkbin(Oeq, yys[yypt-2].yyv.node, yys[yypt-0].yyv.node);
+ }
+206=>
+#line 1237 "limbo.y"
+{
+ yyval.node = mkbin(Oneq, yys[yypt-2].yyv.node, yys[yypt-0].yyv.node);
+ }
+207=>
+#line 1241 "limbo.y"
+{
+ yyval.node = mkbin(Oand, yys[yypt-2].yyv.node, yys[yypt-0].yyv.node);
+ }
+208=>
+#line 1245 "limbo.y"
+{
+ yyval.node = mkbin(Oxor, yys[yypt-2].yyv.node, yys[yypt-0].yyv.node);
+ }
+209=>
+#line 1249 "limbo.y"
+{
+ yyval.node = mkbin(Oor, yys[yypt-2].yyv.node, yys[yypt-0].yyv.node);
+ }
+210=>
+#line 1253 "limbo.y"
+{
+ yyval.node = mkbin(Ocons, yys[yypt-2].yyv.node, yys[yypt-0].yyv.node);
+ }
+211=>
+#line 1257 "limbo.y"
+{
+ yyval.node = mkbin(Oandand, yys[yypt-2].yyv.node, yys[yypt-0].yyv.node);
+ }
+212=>
+#line 1261 "limbo.y"
+{
+ yyval.node = mkbin(Ooror, yys[yypt-2].yyv.node, yys[yypt-0].yyv.node);
+ }
+213=>
+yyval.node = yys[yyp+1].yyv.node;
+214=>
+#line 1268 "limbo.y"
+{
+ yys[yypt-0].yyv.node.src.start = yys[yypt-1].yyv.tok.src.start;
+ yyval.node = yys[yypt-0].yyv.node;
+ }
+215=>
+#line 1273 "limbo.y"
+{
+ yyval.node = mkunary(Oneg, yys[yypt-0].yyv.node);
+ yyval.node.src.start = yys[yypt-1].yyv.tok.src.start;
+ }
+216=>
+#line 1278 "limbo.y"
+{
+ yyval.node = mkunary(Onot, yys[yypt-0].yyv.node);
+ yyval.node.src.start = yys[yypt-1].yyv.tok.src.start;
+ }
+217=>
+#line 1283 "limbo.y"
+{
+ yyval.node = mkunary(Ocomp, yys[yypt-0].yyv.node);
+ yyval.node.src.start = yys[yypt-1].yyv.tok.src.start;
+ }
+218=>
+#line 1288 "limbo.y"
+{
+ yyval.node = mkunary(Oind, yys[yypt-0].yyv.node);
+ yyval.node.src.start = yys[yypt-1].yyv.tok.src.start;
+ }
+219=>
+#line 1293 "limbo.y"
+{
+ yyval.node = mkunary(Opreinc, yys[yypt-0].yyv.node);
+ yyval.node.src.start = yys[yypt-1].yyv.tok.src.start;
+ }
+220=>
+#line 1298 "limbo.y"
+{
+ yyval.node = mkunary(Opredec, yys[yypt-0].yyv.node);
+ yyval.node.src.start = yys[yypt-1].yyv.tok.src.start;
+ }
+221=>
+#line 1303 "limbo.y"
+{
+ yyval.node = mkunary(Orcv, yys[yypt-0].yyv.node);
+ yyval.node.src.start = yys[yypt-1].yyv.tok.src.start;
+ }
+222=>
+#line 1308 "limbo.y"
+{
+ yyval.node = mkunary(Ohd, yys[yypt-0].yyv.node);
+ yyval.node.src.start = yys[yypt-1].yyv.tok.src.start;
+ }
+223=>
+#line 1313 "limbo.y"
+{
+ yyval.node = mkunary(Otl, yys[yypt-0].yyv.node);
+ yyval.node.src.start = yys[yypt-1].yyv.tok.src.start;
+ }
+224=>
+#line 1318 "limbo.y"
+{
+ yyval.node = mkunary(Olen, yys[yypt-0].yyv.node);
+ yyval.node.src.start = yys[yypt-1].yyv.tok.src.start;
+ }
+225=>
+#line 1323 "limbo.y"
+{
+ yyval.node = mkunary(Oref, yys[yypt-0].yyv.node);
+ yyval.node.src.start = yys[yypt-1].yyv.tok.src.start;
+ }
+226=>
+#line 1328 "limbo.y"
+{
+ yyval.node = mkunary(Otagof, yys[yypt-0].yyv.node);
+ yyval.node.src.start = yys[yypt-1].yyv.tok.src.start;
+ }
+227=>
+#line 1333 "limbo.y"
+{
+ yyval.node = mkn(Oarray, yys[yypt-3].yyv.node, nil);
+ yyval.node.ty = mktype(yys[yypt-5].yyv.tok.src.start, yys[yypt-0].yyv.ty.src.stop, Tarray, yys[yypt-0].yyv.ty, nil);
+ yyval.node.src = yyval.node.ty.src;
+ }
+228=>
+#line 1339 "limbo.y"
+{
+ yyval.node = mkn(Oarray, yys[yypt-5].yyv.node, yys[yypt-1].yyv.node);
+ yyval.node.src.start = yys[yypt-7].yyv.tok.src.start;
+ yyval.node.src.stop = yys[yypt-0].yyv.tok.src.stop;
+ }
+229=>
+#line 1345 "limbo.y"
+{
+ yyval.node = mkn(Onothing, nil, nil);
+ yyval.node.src.start = yys[yypt-5].yyv.tok.src.start;
+ yyval.node.src.stop = yys[yypt-4].yyv.tok.src.stop;
+ yyval.node = mkn(Oarray, yyval.node, yys[yypt-1].yyv.node);
+ yyval.node.src.start = yys[yypt-6].yyv.tok.src.start;
+ yyval.node.src.stop = yys[yypt-0].yyv.tok.src.stop;
+ }
+230=>
+#line 1354 "limbo.y"
+{
+ yyval.node = etolist(yys[yypt-1].yyv.node);
+ yyval.node.src.start = yys[yypt-4].yyv.tok.src.start;
+ yyval.node.src.stop = yys[yypt-0].yyv.tok.src.stop;
+ }
+231=>
+#line 1360 "limbo.y"
+{
+ yyval.node = mkn(Ochan, nil, nil);
+ yyval.node.ty = mktype(yys[yypt-2].yyv.tok.src.start, yys[yypt-0].yyv.ty.src.stop, Tchan, yys[yypt-0].yyv.ty, nil);
+ yyval.node.src = yyval.node.ty.src;
+ }
+232=>
+#line 1366 "limbo.y"
+{
+ yyval.node = mkn(Ochan, yys[yypt-3].yyv.node, nil);
+ yyval.node.ty = mktype(yys[yypt-5].yyv.tok.src.start, yys[yypt-0].yyv.ty.src.stop, Tchan, yys[yypt-0].yyv.ty, nil);
+ yyval.node.src = yyval.node.ty.src;
+ }
+233=>
+#line 1372 "limbo.y"
+{
+ yyval.node = mkunary(Ocast, yys[yypt-0].yyv.node);
+ yyval.node.ty = mktype(yys[yypt-3].yyv.tok.src.start, yys[yypt-0].yyv.node.src.stop, Tarray, mkidtype(yys[yypt-1].yyv.tok.src, yys[yypt-1].yyv.tok.v.idval), nil);
+ yyval.node.src = yyval.node.ty.src;
+ }
+234=>
+#line 1378 "limbo.y"
+{
+ yyval.node = mkunary(Ocast, yys[yypt-0].yyv.node);
+ yyval.node.src.start = yys[yypt-1].yyv.tok.src.start;
+ yyval.node.ty = mkidtype(yyval.node.src, yys[yypt-1].yyv.tok.v.idval);
+ }
+235=>
+#line 1384 "limbo.y"
+{
+ yyval.node = mkunary(Ocast, yys[yypt-0].yyv.node);
+ yyval.node.src.start = yys[yypt-1].yyv.tok.src.start;
+ yyval.node.ty = mkidtype(yyval.node.src, yys[yypt-1].yyv.tok.v.idval);
+ }
+236=>
+#line 1390 "limbo.y"
+{
+ yyval.node = mkunary(Ocast, yys[yypt-0].yyv.node);
+ yyval.node.src.start = yys[yypt-1].yyv.tok.src.start;
+ yyval.node.ty = yys[yypt-1].yyv.ty;
+ }
+237=>
+yyval.node = yys[yyp+1].yyv.node;
+238=>
+#line 1399 "limbo.y"
+{
+ yyval.node = mkn(Ocall, yys[yypt-3].yyv.node, yys[yypt-1].yyv.node);
+ yyval.node.src.start = yys[yypt-3].yyv.node.src.start;
+ yyval.node.src.stop = yys[yypt-0].yyv.tok.src.stop;
+ }
+239=>
+#line 1405 "limbo.y"
+{
+ yyval.node = yys[yypt-1].yyv.node;
+ if(yys[yypt-1].yyv.node.op == Oseq)
+ yyval.node = mkn(Otuple, rotater(yys[yypt-1].yyv.node), nil);
+ else
+ yyval.node.flags |= byte PARENS;
+ yyval.node.src.start = yys[yypt-2].yyv.tok.src.start;
+ yyval.node.src.stop = yys[yypt-0].yyv.tok.src.stop;
+ }
+240=>
+#line 1415 "limbo.y"
+{
+# n := mkdeclname($1, mkids($1, enter(".fn"+string nfnexp++, 0), nil, nil));
+# $<node>$ = fndef(n, $2);
+# nfns++;
+ }
+241=>
+#line 1420 "limbo.y"
+{
+# $$ = fnfinishdef($<node>3, $4);
+# $$ = mkdeclname($1, $$.left.decl);
+ yyerror("urt unk");
+ yyval.node = nil;
+ }
+242=>
+#line 1427 "limbo.y"
+{
+ yyval.node = mkbin(Odot, yys[yypt-2].yyv.node, mkname(yys[yypt-0].yyv.tok.src, yys[yypt-0].yyv.tok.v.idval));
+ }
+243=>
+#line 1431 "limbo.y"
+{
+ yyval.node = mkbin(Omdot, yys[yypt-2].yyv.node, yys[yypt-0].yyv.node);
+ }
+244=>
+#line 1435 "limbo.y"
+{
+ yyval.node = mkbin(Oindex, yys[yypt-3].yyv.node, yys[yypt-1].yyv.node);
+ yyval.node.src.stop = yys[yypt-0].yyv.tok.src.stop;
+ }
+245=>
+#line 1440 "limbo.y"
+{
+ if(yys[yypt-3].yyv.node.op == Onothing)
+ yys[yypt-3].yyv.node.src = yys[yypt-2].yyv.tok.src;
+ if(yys[yypt-1].yyv.node.op == Onothing)
+ yys[yypt-1].yyv.node.src = yys[yypt-2].yyv.tok.src;
+ yyval.node = mkbin(Oslice, yys[yypt-5].yyv.node, mkbin(Oseq, yys[yypt-3].yyv.node, yys[yypt-1].yyv.node));
+ yyval.node.src.stop = yys[yypt-0].yyv.tok.src.stop;
+ }
+246=>
+#line 1449 "limbo.y"
+{
+ yyval.node = mkunary(Oinc, yys[yypt-1].yyv.node);
+ yyval.node.src.stop = yys[yypt-0].yyv.tok.src.stop;
+ }
+247=>
+#line 1454 "limbo.y"
+{
+ yyval.node = mkunary(Odec, yys[yypt-1].yyv.node);
+ yyval.node.src.stop = yys[yypt-0].yyv.tok.src.stop;
+ }
+248=>
+#line 1459 "limbo.y"
+{
+ yyval.node = mksconst(yys[yypt-0].yyv.tok.src, yys[yypt-0].yyv.tok.v.idval);
+ }
+249=>
+#line 1463 "limbo.y"
+{
+ yyval.node = mkconst(yys[yypt-0].yyv.tok.src, yys[yypt-0].yyv.tok.v.ival);
+ if(yys[yypt-0].yyv.tok.v.ival > big 16r7fffffff || yys[yypt-0].yyv.tok.v.ival < big -16r7fffffff)
+ yyval.node.ty = tbig;
+ }
+250=>
+#line 1469 "limbo.y"
+{
+ yyval.node = mkrconst(yys[yypt-0].yyv.tok.src, yys[yypt-0].yyv.tok.v.rval);
+ }
+251=>
+#line 1473 "limbo.y"
+{
+ yyval.node = mkbin(Oindex, yys[yypt-5].yyv.node, rotater(mkbin(Oseq, yys[yypt-3].yyv.node, yys[yypt-1].yyv.node)));
+ yyval.node.src.stop = yys[yypt-0].yyv.tok.src.stop;
+ }
+252=>
+#line 1480 "limbo.y"
+{
+ yyval.node = mkname(yys[yypt-0].yyv.tok.src, yys[yypt-0].yyv.tok.v.idval);
+ }
+253=>
+#line 1484 "limbo.y"
+{
+ yyval.node = mknil(yys[yypt-0].yyv.tok.src);
+ }
+254=>
+#line 1490 "limbo.y"
+{
+ yyval.node = mkn(Otuple, rotater(yys[yypt-1].yyv.node), nil);
+ yyval.node.src.start = yys[yypt-2].yyv.tok.src.start;
+ yyval.node.src.stop = yys[yypt-0].yyv.tok.src.stop;
+ }
+255=>
+yyval.node = yys[yyp+1].yyv.node;
+256=>
+#line 1499 "limbo.y"
+{
+ yyval.node = mkbin(Oseq, yys[yypt-2].yyv.node, yys[yypt-0].yyv.node);
+ }
+257=>
+yyval.node = yys[yyp+1].yyv.node;
+258=>
+yyval.node = yys[yyp+1].yyv.node;
+259=>
+#line 1509 "limbo.y"
+{
+ yyval.node = mkn(Otype, nil, nil);
+ yyval.node.ty = mkidtype(yys[yypt-0].yyv.tok.src, yys[yypt-0].yyv.tok.v.idval);
+ yyval.node.src = yyval.node.ty.src;
+ }
+260=>
+#line 1515 "limbo.y"
+{
+ yyval.node = mkn(Otype, nil, nil);
+ yyval.node.ty = mktype(yys[yypt-2].yyv.tok.src.start, yys[yypt-0].yyv.ty.src.stop, Tarray, yys[yypt-0].yyv.ty, nil);
+ yyval.node.src = yyval.node.ty.src;
+ }
+261=>
+#line 1521 "limbo.y"
+{
+ yyval.node = mkn(Otype, nil, nil);
+ yyval.node.ty = mktype(yys[yypt-2].yyv.tok.src.start, yys[yypt-0].yyv.ty.src.stop, Tlist, yys[yypt-0].yyv.ty, nil);
+ yyval.node.src = yyval.node.ty.src;
+ }
+262=>
+#line 1527 "limbo.y"
+{
+ yyval.node = mkn(Otype, nil ,nil);
+ yyval.node.ty = yys[yypt-0].yyv.ty;
+ yyval.node.ty.flags |= CYCLIC;
+ yyval.node.src = yyval.node.ty.src;
+ }
+263=>
+#line 1536 "limbo.y"
+{
+ yyval.node = mkname(yys[yypt-0].yyv.tok.src, yys[yypt-0].yyv.tok.v.idval);
+ }
+264=>
+#line 1540 "limbo.y"
+{
+ yyval.node = nil;
+ }
+265=>
+yyval.node = yys[yyp+1].yyv.node;
+266=>
+yyval.node = yys[yyp+1].yyv.node;
+267=>
+#line 1548 "limbo.y"
+{
+ yyval.node = mkbin(Oseq, yys[yypt-2].yyv.node, yys[yypt-0].yyv.node);
+ }
+268=>
+#line 1552 "limbo.y"
+{
+ yyval.node = mkbin(Oseq, yys[yypt-2].yyv.node, yys[yypt-0].yyv.node);
+ }
+269=>
+#line 1558 "limbo.y"
+{
+ yyval.node = nil;
+ }
+270=>
+#line 1562 "limbo.y"
+{
+ yyval.node = rotater(yys[yypt-0].yyv.node);
+ }
+271=>
+yyval.node = yys[yyp+1].yyv.node;
+272=>
+yyval.node = yys[yyp+1].yyv.node;
+273=>
+yyval.node = yys[yyp+1].yyv.node;
+274=>
+#line 1573 "limbo.y"
+{
+ yyval.node = mkbin(Oseq, yys[yypt-2].yyv.node, yys[yypt-0].yyv.node);
+ }
+275=>
+#line 1579 "limbo.y"
+{
+ yyval.node = rotater(yys[yypt-0].yyv.node);
+ }
+276=>
+#line 1583 "limbo.y"
+{
+ yyval.node = rotater(yys[yypt-1].yyv.node);
+ }
+277=>
+yyval.node = yys[yyp+1].yyv.node;
+278=>
+#line 1590 "limbo.y"
+{
+ yyval.node = mkbin(Oseq, yys[yypt-2].yyv.node, yys[yypt-0].yyv.node);
+ }
+279=>
+#line 1596 "limbo.y"
+{
+ yyval.node = mkn(Oelem, nil, yys[yypt-0].yyv.node);
+ yyval.node.src = yys[yypt-0].yyv.node.src;
+ }
+280=>
+#line 1601 "limbo.y"
+{
+ yyval.node = mkbin(Oelem, rotater(yys[yypt-2].yyv.node), yys[yypt-0].yyv.node);
+ }
+281=>
+#line 1607 "limbo.y"
+{
+ if(yys[yypt-1].yyv.node.op == Oseq)
+ yys[yypt-1].yyv.node.right.left = rotater(yys[yypt-0].yyv.node);
+ else
+ yys[yypt-1].yyv.node.left = rotater(yys[yypt-0].yyv.node);
+ yyval.node = yys[yypt-1].yyv.node;
+ }
+282=>
+#line 1617 "limbo.y"
+{
+ yyval.node = typedecl(yys[yypt-1].yyv.ids, mktype(yys[yypt-1].yyv.ids.src.start, yys[yypt-0].yyv.tok.src.stop, Tpoly, nil, nil));
+ }
+283=>
+#line 1621 "limbo.y"
+{
+ if(yys[yypt-3].yyv.node.op == Oseq)
+ yys[yypt-3].yyv.node.right.left = rotater(yys[yypt-2].yyv.node);
+ else
+ yys[yypt-3].yyv.node.left = rotater(yys[yypt-2].yyv.node);
+ yyval.node = mkbin(Oseq, yys[yypt-3].yyv.node, typedecl(yys[yypt-1].yyv.ids, mktype(yys[yypt-1].yyv.ids.src.start, yys[yypt-0].yyv.tok.src.stop, Tpoly, nil, nil)));
+ }
+ }
+ }
+
+ return yyn;
+}
diff --git a/appl/cmd/limbo/limbo.m b/appl/cmd/limbo/limbo.m
new file mode 100644
index 00000000..8c2efce0
--- /dev/null
+++ b/appl/cmd/limbo/limbo.m
@@ -0,0 +1,527 @@
+include "sys.m";
+include "math.m";
+include "string.m";
+include "bufio.m";
+include "isa.m";
+include "workdir.m";
+
+# internal dis ops
+IEXC: con MAXDIS;
+IEXC0: con (MAXDIS+1);
+INOOP: con (MAXDIS+2);
+
+# temporary
+LDT: con 1;
+
+STemp: con NREG * IBY2WD;
+RTemp: con STemp + IBY2WD;
+DTemp: con RTemp + IBY2WD;
+MaxTemp: con DTemp + IBY2WD;
+MaxReg: con 1 << 16;
+MaxAlign: con IBY2LG;
+StrSize: con 256;
+MaxIncPath: con 32; # max directories in include path
+MaxScope: con 64; # max nested {}
+MaxInclude: con 32; # max nested include ""
+ScopeBuiltin,
+ScopeNils,
+ScopeGlobal: con iota;
+
+Line: type int;
+PosBits: con 10;
+PosMask: con (1 << PosBits) - 1;
+
+Src: adt
+{
+ start: Line;
+ stop: Line;
+};
+
+File: adt
+{
+ name: string;
+ abs: int; # absolute line of start of the part of file
+ off: int; # offset to line in the file
+ in: int; # absolute line where included
+ act: string; # name of real file with #line fake file
+ actoff: int; # offset from fake line to real line
+ sbl: int; # symbol file number
+};
+
+Val: adt
+{
+ idval: ref Sym;
+ ival: big;
+ rval: real;
+};
+
+Tok: adt
+{
+ src: Src;
+ v: Val;
+};
+
+#
+# addressing modes
+#
+ Aimm, # immediate
+ Amp, # global
+ Ampind, # global indirect
+ Afp, # activation frame
+ Afpind, # frame indirect
+ Apc, # branch
+ Adesc, # type descriptor immediate
+ Aoff, # offset in module description table
+ Anoff, # above encoded as a -ve
+ Aerr, # error
+ Anone, # no operand
+ Aldt, # linkage descriptor table immediate
+ Aend: con byte iota;
+
+Addr: adt
+{
+ reg: int;
+ offset: int;
+ decl: cyclic ref Decl;
+};
+
+Inst: adt
+{
+ src: Src;
+ op: int; # could be a byte
+ pc: int;
+ reach: byte; # could a control path reach this instruction?
+ sm: byte; # operand addressing modes
+ mm: byte;
+ dm: byte;
+ s: cyclic Addr; # operands
+ m: cyclic Addr;
+ d: cyclic Addr;
+ branch: cyclic ref Inst; # branch destination
+ next: cyclic ref Inst;
+ block: int; # blocks nested inside
+};
+
+Case: adt
+{
+ nlab: int;
+ nsnd: int;
+ offset: int; # offset in mp
+ labs: cyclic array of Label;
+ wild: cyclic ref Node; # if nothing matches
+ iwild: cyclic ref Inst;
+};
+
+Label: adt
+{
+ node: cyclic ref Node;
+ isptr: int; # true if the labelled alt channel is a pointer
+ start: cyclic ref Node; # value in range [start, stop) => code
+ stop: cyclic ref Node;
+ inst: cyclic ref Inst;
+};
+
+#
+# storage classes
+#
+ Dtype,
+ Dfn,
+ Dglobal,
+ Darg,
+ Dlocal,
+ Dconst,
+ Dfield,
+ Dtag, # pick tags
+ Dimport, # imported identifier
+ Dunbound, # unbound identified
+ Dundef,
+ Dwundef, # undefined, but don't whine
+
+ Dend: con iota;
+
+Decl: adt
+{
+ src: Src; # where declaration
+ sym: cyclic ref Sym; # name
+ store: int; # storage class
+ nid: byte; # block grouping for locals
+ inline: byte; # inline function
+ handler: byte; # fn has exception handler(s)
+ das: byte; # declared with :=
+ dot: cyclic ref Decl; # parent adt or module
+ ty: cyclic ref Type;
+ refs: int; # number of references
+ offset: int;
+ tag: int; # union tag
+
+ scope: int; # in which it was declared
+ next: cyclic ref Decl; # list in same scope, field or argument list, etc.
+ old: cyclic ref Decl; # declaration of the symbol in enclosing scope
+
+ eimport: cyclic ref Node; # expr from which imported
+ importid: cyclic ref Decl; # identifier imported
+ timport: cyclic ref Decl; # stack of identifiers importing a type
+
+ init: cyclic ref Node; # data initialization
+ tref: int; # 1 => is a tmp; >=2 => tmp in use
+ cycle: byte; # can create a cycle
+ cyc: byte; # so labelled in source
+ cycerr: byte; # delivered an error message for cycle?
+ implicit: byte; # implicit first argument in an adt?
+
+ iface: cyclic ref Decl; # used external declarations in a module
+
+ locals: cyclic ref Decl; # locals for a function
+ link: cyclic ref Decl; # pointer to parent function or function argument or local share or parent type dec
+ pc: cyclic ref Inst; # start of function
+ # endpc: cyclic ref Inst; # limit of function - unused
+
+# should be able to move this to Type
+ desc: ref Desc; # heap descriptor
+};
+
+Desc: adt
+{
+ id: int; # dis type identifier
+ used: int; # actually used in output?
+ map: array of byte; # byte map of pointers
+ size: int; # length of the object
+ nmap: int; # length of good bytes in map
+ next: cyclic ref Desc;
+};
+
+Dlist: adt
+{
+ d: ref Decl;
+ next: cyclic ref Dlist;
+};
+
+Except: adt
+{
+ p1: ref Inst; # first pc covered
+ p2: ref Inst; # last pc not covered
+ c: ref Case; # exception case instructions
+ d: ref Decl; # exception definition if any
+ zn: ref Node; # list of nodes to zero in handler
+ desc: ref Desc; # descriptor map for above
+ ne: int; # number of exceptions (ie not strings) in case
+ next: cyclic ref Except;
+};
+
+Sym: adt
+{
+ token: int;
+ name: string;
+ hash: int;
+ next: cyclic ref Sym;
+ decl: cyclic ref Decl;
+ unbound: cyclic ref Decl; # place holder for unbound symbols
+};
+
+#
+# ops for nodes
+#
+ Oadd,
+ Oaddas,
+ Oadr,
+ Oadtdecl,
+ Oalt,
+ Oand,
+ Oandand,
+ Oandas,
+ Oarray,
+ Oas,
+ Obreak,
+ Ocall,
+ Ocase,
+ Ocast,
+ Ochan,
+ Ocomma,
+ Ocomp,
+ Ocondecl,
+ Ocons,
+ Oconst,
+ Ocont,
+ Odas,
+ Odec,
+ Odiv,
+ Odivas,
+ Odo,
+ Odot,
+ Oelem,
+ Oeq,
+ Oexcept,
+ Oexdecl,
+ Oexit,
+ Oexp,
+ Oexpas,
+ Oexstmt,
+ Ofielddecl,
+ Ofnptr,
+ Ofor,
+ Ofunc,
+ Ogeq,
+ Ogt,
+ Ohd,
+ Oif,
+ Oimport,
+ Oinc,
+ Oind,
+ Oindex,
+ Oinds,
+ Oindx,
+ Oinv,
+ Ojmp,
+ Olabel,
+ Olen,
+ Oleq,
+ Oload,
+ Olsh,
+ Olshas,
+ Olt,
+ Omdot,
+ Omod,
+ Omodas,
+ Omoddecl,
+ Omul,
+ Omulas,
+ Oname,
+ Oneg,
+ Oneq,
+ Onot,
+ Onothing,
+ Oor,
+ Ooras,
+ Ooror,
+ Opick,
+ Opickdecl,
+ Opredec,
+ Opreinc,
+ Oraise,
+ Orange,
+ Orcv,
+ Oref,
+ Oret,
+ Orsh,
+ Orshas,
+ Oscope,
+ Oself,
+ Oseq,
+ Oslice,
+ Osnd,
+ Ospawn,
+ Osub,
+ Osubas,
+ Otagof,
+ Otl,
+ Otuple,
+ Otype,
+ Otypedecl,
+ Oused,
+ Ovardecl,
+ Ovardecli,
+ Owild,
+ Oxor,
+ Oxoras,
+
+ Oend: con iota + 1;
+
+#
+# moves
+#
+ Mas,
+ Mcons,
+ Mhd,
+ Mtl,
+
+ Mend: con iota;
+
+#
+# addressability
+#
+ Rreg, # v(fp)
+ Rmreg, # v(mp)
+ Roff, # $v
+ Rnoff, # $v encoded as -ve
+ Rdesc, # $v
+ Rdescp, # $v
+ Rconst, # $v
+ Ralways, # preceeding are always addressable
+ Radr, # v(v(fp))
+ Rmadr, # v(v(mp))
+ Rcant, # following are not quite addressable
+ Rpc, # branch address
+ Rmpc, # cross module branch address
+ Rareg, # $v(fp)
+ Ramreg, # $v(mp)
+ Raadr, # $v(v(fp))
+ Ramadr, # $v(v(mp))
+ Rldt, # $v
+
+ Rend: con byte iota;
+
+
+Const: adt
+{
+ val: big;
+ rval: real;
+};
+
+PARENS: con 1;
+TEMP: con 2;
+FNPTRA: con 4; # argument
+FNPTR2: con 8; # 2nd parameter
+FNPTRN: con 16; # use -ve offset
+FNPTR: con FNPTRA|FNPTR2|FNPTRN;
+
+Node: adt
+{
+ src: Src;
+ op: int;
+ addable: byte;
+ flags: byte;
+ temps: byte;
+ left: cyclic ref Node;
+ right: cyclic ref Node;
+ ty: cyclic ref Type;
+ decl: cyclic ref Decl;
+ c: ref Const; # for Oconst
+};
+
+ #
+ # types visible to limbo
+ #
+ Tnone,
+ Tadt,
+ Tadtpick, # pick case of an adt
+ Tarray,
+ Tbig, # 64 bit int
+ Tbyte, # 8 bit unsigned int
+ Tchan,
+ Treal,
+ Tfn,
+ Tint, # 32 bit int
+ Tlist,
+ Tmodule,
+ Tref,
+ Tstring,
+ Ttuple,
+ Texception,
+ Tfix,
+ Tpoly,
+
+ #
+ # internal use types
+ #
+ Tainit, # array initializers
+ Talt, # alt channels
+ Tany, # type of nil
+ Tarrow, # unresolved ty->ty types
+ Tcase, # case labels
+ Tcasel, # case big labels
+ Tcasec, # case string labels
+ Tdot, # unresolved ty.id types
+ Terror,
+ Tgoto, # goto labels
+ Tid, # id with unknown type
+ Tiface, # module interface
+ Texcept, # exception handler tables
+ Tinst, # instantiated adt
+
+ Tend: con iota;
+
+ #
+ # marks for various phases of verifing types
+ #
+ OKbind, # type decls are bound
+ OKverify, # type looks ok
+ OKsized, # started figuring size
+ OKref, # recorded use of type
+ OKclass, # equivalence class found
+ OKcyc, # checked for cycles
+ OKcycsize, # checked for cycles and size
+ OKmodref: # started checking for a module handle
+
+ con byte 1 << iota;
+ OKmask: con byte 16rff;
+
+ #
+ # recursive marks
+ #
+ TReq,
+ TRcom,
+ TRcyc,
+ TRvis:
+ con byte 1 << iota;
+
+# type flags
+FULLARGS: con byte 1; # all hidden args added
+INST: con byte 2; # instantiated adt
+CYCLIC: con byte 4; # cyclic type
+POLY: con byte 8; # polymorphic types inside
+NOPOLY: con byte 16; # no polymorphic types inside
+
+# must put some picks in here
+Type: adt
+{
+ src: Src;
+ kind: int;
+ ok: byte; # set when type is verified
+ varargs: byte; # if a function, ends with vargs?
+ linkall: byte; # put all iface fns in external linkage?
+ rec: byte; # in the middle of recursive type
+ pr: byte; # in the middle of printing a recursive type
+ cons: byte; # exception constant
+ flags: byte;
+ sbl: int; # slot in .sbl adt table
+ sig: int; # signature for dynamic type check
+ size: int; # storage required, in bytes
+ align: int; # alignment in bytes
+ decl: cyclic ref Decl;
+ tof: cyclic ref Type;
+ ids: cyclic ref Decl;
+ tags: cyclic ref Decl;# tagged fields in an adt
+ polys: cyclic ref Decl;# polymorphic fields in fn or adt
+ cse: cyclic ref Case;# case or goto labels
+ teq: cyclic ref Type;# temporary equiv class for equiv checking
+ tcom: cyclic ref Type;# temporary equiv class for compat checking
+ eq: cyclic ref Teq; # real equiv class
+ eraises: cyclic ref Node; # for Tfn only
+ val: cyclic ref Node; # for Tfix, Tfn, Tadt only
+ tlist: cyclic ref Typelist; # for Tinst only
+ tmap: cyclic ref Tpair; # for Tadt only
+};
+
+#
+# type equivalence classes
+#
+Teq: adt
+{
+ id: int; # for signing
+ ty: cyclic ref Type;# an instance of the class
+ eq: cyclic ref Teq; # used to link eq sets
+};
+
+Tattr: adt
+{
+ isptr: int;
+ refable: int;
+ conable: int;
+ isbig: int;
+ vis: int; # type visible to users
+};
+
+Tpair: adt
+{
+ t1: cyclic ref Type;
+ t2: cyclic ref Type;
+ nxt: cyclic ref Tpair;
+};
+
+Typelist: adt
+{
+ t: cyclic ref Type;
+ nxt: cyclic ref Typelist;
+};
+
+Sother, Sloop, Sscope : con iota;
diff --git a/appl/cmd/limbo/limbo.y b/appl/cmd/limbo/limbo.y
new file mode 100644
index 00000000..0c56bd1b
--- /dev/null
+++ b/appl/cmd/limbo/limbo.y
@@ -0,0 +1,1973 @@
+%{
+include "limbo.m";
+include "draw.m";
+
+%}
+
+%module Limbo
+{
+ init: fn(ctxt: ref Draw->Context, argv: list of string);
+
+ YYSTYPE: adt{
+ tok: Tok;
+ ids: ref Decl;
+ node: ref Node;
+ ty: ref Type;
+ types: ref Typelist;
+ };
+
+ YYLEX: adt {
+ lval: YYSTYPE;
+ lex: fn(nil: self ref YYLEX): int;
+ error: fn(nil: self ref YYLEX, err: string);
+ };
+}
+
+%{
+ #
+ # lex.b
+ #
+ signdump: string; # name of function for sig debugging
+ superwarn: int;
+ debug: array of int;
+ noline: Line;
+ nosrc: Src;
+ arrayz: int;
+ emitcode: string; # emit stub routines for system module functions
+ emitdyn: int; # emit as above but for dynamic modules
+ emitsbl: string; # emit symbol file for sysm modules
+ emitstub: int; # emit type and call frames for system modules
+ emittab: string; # emit table of runtime functions for this module
+ errors: int;
+ mustcompile: int;
+ dontcompile: int;
+ asmsym: int; # generate symbols in assembly language?
+ bout: ref Bufio->Iobuf; # output file
+ bsym: ref Bufio->Iobuf; # symbol output file; nil => no sym out
+ gendis: int; # generate dis or asm?
+ fixss: int;
+ newfnptr: int; # ISELF and -ve indices
+ optims: int;
+
+ #
+ # decls.b
+ #
+ scope: int;
+ # impmod: ref Sym; # name of implementation module
+ impmods: ref Decl; # name of implementation module(s)
+ nildecl: ref Decl; # declaration for limbo's nil
+ selfdecl: ref Decl; # declaration for limbo's self
+
+ #
+ # types.b
+ #
+ tany: ref Type;
+ tbig: ref Type;
+ tbyte: ref Type;
+ terror: ref Type;
+ tint: ref Type;
+ tnone: ref Type;
+ treal: ref Type;
+ tstring: ref Type;
+ texception: ref Type;
+ tunknown: ref Type;
+ tfnptr: ref Type;
+ rtexception: ref Type;
+ descriptors: ref Desc; # list of all possible descriptors
+ tattr: array of Tattr;
+
+ #
+ # nodes.b
+ #
+ opcommute: array of int;
+ oprelinvert: array of int;
+ isused: array of int;
+ casttab: array of array of int; # instruction to cast from [1] to [2]
+
+ nfns: int; # functions defined
+ nfnexp: int;
+ fns: array of ref Decl; # decls for fns defined
+ tree: ref Node; # root of parse tree
+
+ parset: int; # time to parse
+ checkt: int; # time to typecheck
+ gent: int; # time to generate code
+ writet: int; # time to write out code
+ symt: int; # time to write out symbols
+%}
+
+%type <ty> type fnarg fnargret fnargretp adtk fixtype iditype dotiditype
+%type <ids> ids rids nids nrids tuplist forms ftypes ftype
+ bclab bctarg ptags rptags polydec
+%type <node> zexp exp monexp term elist zelist celist
+ idatom idterms idterm idlist
+ initlist elemlist elem qual
+ decl topdecls topdecl fndef fbody stmt stmts qstmts qbodies cqstmts cqbodies
+ mdecl adtdecl mfield mfields field fields fnname
+ pstmts pbodies pqual pfields pfbody pdecl dfield dfields
+ eqstmts eqbodies idexc edecl raises tpoly tpolys texp export exportlist forpoly
+%type <types> types
+
+%right <tok.src> '=' Landeq Loreq Lxoreq Llsheq Lrsheq
+ Laddeq Lsubeq Lmuleq Ldiveq Lmodeq Lexpeq Ldeclas
+%left <tok.src> Lload
+%left <tok.src> Loror
+%left <tok.src> Landand
+%right <tok.src> Lcons
+%left <tok.src> '|'
+%left <tok.src> '^'
+%left <tok.src> '&'
+%left <tok.src> Leq Lneq
+%left <tok.src> '<' '>' Lleq Lgeq
+%left <tok.src> Llsh Lrsh
+%left <tok.src> '+' '-'
+%left <tok.src> '*' '/' '%'
+%right <tok.src> Lexp
+%right <tok.src> Lcomm
+
+%left <tok.src> '(' ')' '[' ']' Linc Ldec Lof Lref
+%right <tok.src> Lif Lelse Lfn ':' Lexcept Lraises
+%left <tok.src> Lmdot
+%left <tok.src> '.'
+
+%left <tok.src> Lto
+%left <tok.src> Lor
+
+
+%nonassoc <tok.v.rval> Lrconst
+%nonassoc <tok.v.ival> Lconst
+%nonassoc <tok.v.idval> Lid Ltid Lsconst
+%nonassoc <tok.src> Llabs Lnil
+ '!' '~' Llen Lhd Ltl Ltagof
+ '{' '}' ';'
+ Limplement Limport Linclude
+ Lcon Ltype Lmodule Lcyclic
+ Ladt Larray Llist Lchan Lself
+ Ldo Lwhile Lfor Lbreak
+ Lalt Lcase Lpick Lcont
+ Lreturn Lexit Lspawn Lraise Lfix
+%%
+prog : Limplement ids ';'
+ {
+ impmods = $2;
+ } topdecls
+ {
+ tree = rotater($5);
+ }
+ | topdecls
+ {
+ impmods = nil;
+ tree = rotater($1);
+ }
+ ;
+
+topdecls: topdecl
+ | topdecls topdecl
+ {
+ if($1 == nil)
+ $$ = $2;
+ else if($2 == nil)
+ $$ = $1;
+ else
+ $$ = mkbin(Oseq, $1, $2);
+ }
+ ;
+
+topdecl : error ';'
+ {
+ $$ = nil;
+ }
+ | decl
+ | fndef
+ | adtdecl ';'
+ | mdecl ';'
+ | idatom '=' exp ';'
+ {
+ $$ = mkbin(Oas, $1, $3);
+ }
+ | idterm '=' exp ';'
+ {
+ $$ = mkbin(Oas, $1, $3);
+ }
+ | idatom Ldeclas exp ';'
+ {
+ $$ = mkbin(Odas, $1, $3);
+ }
+ | idterm Ldeclas exp ';'
+ {
+ $$ = mkbin(Odas, $1, $3);
+ }
+ | idterms ':' type ';'
+ {
+ yyerror("illegal declaration");
+ $$ = nil;
+ }
+ | idterms ':' type '=' exp ';'
+ {
+ yyerror("illegal declaration");
+ $$ = nil;
+ }
+ ;
+
+idterms : idterm
+ | idterms ',' idterm
+ {
+ $$ = mkbin(Oseq, $1, $3);
+ }
+ ;
+
+decl : Linclude Lsconst ';'
+ {
+ includef($2);
+ $$ = nil;
+ }
+ | ids ':' Ltype type ';'
+ {
+ $$ = typedecl($1, $4);
+ }
+ | ids ':' Limport exp ';'
+ {
+ $$ = importdecl($4, $1);
+ $$.src.start = $1.src.start;
+ $$.src.stop = $5.stop;
+ }
+ | ids ':' type ';'
+ {
+ $$ = vardecl($1, $3);
+ }
+ | ids ':' type '=' exp ';'
+ {
+ $$ = mkbin(Ovardecli, vardecl($1, $3), varinit($1, $5));
+ }
+ | ids ':' Lcon exp ';'
+ {
+ $$ = condecl($1, $4);
+ }
+ | edecl
+ ;
+
+edecl : ids ':' Lexcept ';'
+ {
+ $$ = exdecl($1, nil);
+ }
+ | ids ':' Lexcept '(' tuplist ')' ';'
+ {
+ $$ = exdecl($1, revids($5));
+ }
+ ;
+
+mdecl : ids ':' Lmodule '{' mfields '}'
+ {
+ $1.src.stop = $6.stop;
+ $$ = moddecl($1, rotater($5));
+ }
+ ;
+
+mfields :
+ {
+ $$ = nil;
+ }
+ | mfields mfield
+ {
+ if($1 == nil)
+ $$ = $2;
+ else if($2 == nil)
+ $$ = $1;
+ else
+ $$ = mkn(Oseq, $1, $2);
+ }
+ | error
+ {
+ $$ = nil;
+ }
+ ;
+
+mfield : ids ':' type ';'
+ {
+ $$ = fielddecl(Dglobal, typeids($1, $3));
+ }
+ | adtdecl ';'
+ | ids ':' Ltype type ';'
+ {
+ $$ = typedecl($1, $4);
+ }
+ | ids ':' Lcon exp ';'
+ {
+ $$ = condecl($1, $4);
+ }
+ | edecl
+ ;
+
+adtdecl : ids ':' Ladt polydec '{' fields '}' forpoly
+ {
+ $1.src.stop = $7.stop;
+ $$ = adtdecl($1, rotater($6));
+ $$.ty.polys = $4;
+ $$.ty.val = rotater($8);
+ }
+ | ids ':' Ladt polydec Lfor '{' tpolys '}' '{' fields '}'
+ {
+ $1.src.stop = $11.stop;
+ $$ = adtdecl($1, rotater($10));
+ $$.ty.polys = $4;
+ $$.ty.val = rotater($7);
+ }
+ ;
+
+forpoly :
+ {
+ $$ = nil;
+ }
+ | Lfor '{' tpolys '}'
+ {
+ $$ = $3;
+ }
+ ;
+
+fields :
+ {
+ $$ = nil;
+ }
+ | fields field
+ {
+ if($1 == nil)
+ $$ = $2;
+ else if($2 == nil)
+ $$ = $1;
+ else
+ $$ = mkn(Oseq, $1, $2);
+ }
+ | error
+ {
+ $$ = nil;
+ }
+ ;
+
+field : dfield
+ | pdecl
+ | ids ':' Lcon exp ';'
+ {
+ $$ = condecl($1, $4);
+ }
+ ;
+
+dfields :
+ {
+ $$ = nil;
+ }
+ | dfields dfield
+ {
+ if($1 == nil)
+ $$ = $2;
+ else if($2 == nil)
+ $$ = $1;
+ else
+ $$ = mkn(Oseq, $1, $2);
+ }
+ ;
+
+dfield : ids ':' Lcyclic type ';'
+ {
+ for(d := $1; d != nil; d = d.next)
+ d.cyc = byte 1;
+ $$ = fielddecl(Dfield, typeids($1, $4));
+ }
+ | ids ':' type ';'
+ {
+ $$ = fielddecl(Dfield, typeids($1, $3));
+ }
+ ;
+
+pdecl : Lpick '{' pfields '}'
+ {
+ $$ = $3;
+ }
+ ;
+
+pfields : pfbody dfields
+ {
+ $1.right.right = $2;
+ $$ = $1;
+ }
+ | pfbody error
+ {
+ $$ = nil;
+ }
+ | error
+ {
+ $$ = nil;
+ }
+ ;
+
+pfbody : ptags Llabs
+ {
+ $$ = mkn(Opickdecl, nil, mkn(Oseq, fielddecl(Dtag, $1), nil));
+ typeids($1, mktype($1.src.start, $1.src.stop, Tadtpick, nil, nil));
+ }
+ | pfbody dfields ptags Llabs
+ {
+ $1.right.right = $2;
+ $$ = mkn(Opickdecl, $1, mkn(Oseq, fielddecl(Dtag, $3), nil));
+ typeids($3, mktype($3.src.start, $3.src.stop, Tadtpick, nil, nil));
+ }
+ | pfbody error ptags Llabs
+ {
+ $$ = mkn(Opickdecl, nil, mkn(Oseq, fielddecl(Dtag, $3), nil));
+ typeids($3, mktype($3.src.start, $3.src.stop, Tadtpick, nil, nil));
+ }
+ ;
+
+ptags : rptags
+ {
+ $$ = revids($1);
+ }
+ ;
+
+rptags : Lid
+ {
+ $$ = mkids($<tok.src>1, $1, nil, nil);
+ }
+ | rptags Lor Lid
+ {
+ $$ = mkids($<tok.src>3, $3, nil, $1);
+ }
+ ;
+
+ids : rids
+ {
+ $$ = revids($1);
+ }
+ ;
+
+rids : Lid
+ {
+ $$ = mkids($<tok.src>1, $1, nil, nil);
+ }
+ | rids ',' Lid
+ {
+ $$ = mkids($<tok.src>3, $3, nil, $1);
+ }
+ ;
+
+fixtype : Lfix '(' exp ',' exp ')'
+ {
+ $$ = mktype($1.start, $6.stop, Tfix, nil, nil);
+ $$.val = mkbin(Oseq, $3, $5);
+ }
+ | Lfix '(' exp ')'
+ {
+ $$ = mktype($1.start, $4.stop, Tfix, nil, nil);
+ $$.val = $3;
+ }
+ ;
+
+types : type
+ {
+ $$ = addtype($1, nil);
+ }
+ | Lcyclic type
+ {
+ $$ = addtype($2, nil);
+ $2.flags |= CYCLIC;
+ }
+ | types ',' type
+ {
+ $$ = addtype($3, $1);
+ }
+ | types ',' Lcyclic type
+ {
+ $$ = addtype($4, $1);
+ $4.flags |= CYCLIC;
+ }
+ ;
+
+type : Ltid
+ {
+ $$ = mkidtype($<tok.src>1, $1);
+ }
+ | iditype
+ {
+ $$ = $1;
+ }
+ | dotiditype
+ {
+ $$ = $1;
+ }
+ | type Lmdot Lid
+ {
+ $$ = mkarrowtype($1.src.start, $<tok.src>3.stop, $1, $3);
+ }
+ | type Lmdot Lid '[' types ']'
+ {
+ $$ = mkarrowtype($1.src.start, $<tok.src>3.stop, $1, $3);
+ $$ = mkinsttype($1.src, $$, $5);
+ }
+ | Lref type
+ {
+ $$ = mktype($1.start, $2.src.stop, Tref, $2, nil);
+ }
+ | Lchan Lof type
+ {
+ $$ = mktype($1.start, $3.src.stop, Tchan, $3, nil);
+ }
+ | '(' tuplist ')'
+ {
+ if($2.next == nil)
+ $$ = $2.ty;
+ else
+ $$ = mktype($1.start, $3.stop, Ttuple, nil, revids($2));
+ }
+ | Larray Lof type
+ {
+ $$ = mktype($1.start, $3.src.stop, Tarray, $3, nil);
+ }
+ | Llist Lof type
+ {
+ $$ = mktype($1.start, $3.src.stop, Tlist, $3, nil);
+ }
+ | Lfn polydec fnargretp raises
+ {
+ $3.src.start = $1.start;
+ $3.polys = $2;
+ $3.eraises = $4;
+ $$ = $3;
+ }
+ | fixtype
+# | Lexcept
+# {
+# $$ = mktype($1.start, $1.stop, Texception, nil, nil);
+# $$.cons = byte 1;
+# }
+# | Lexcept '(' tuplist ')'
+# {
+# $$ = mktype($1.start, $4.stop, Texception, nil, revids($3));
+# $$.cons = byte 1;
+# }
+ ;
+
+iditype : Lid
+ {
+ $$ = mkidtype($<tok.src>1, $1);
+ }
+ | Lid '[' types ']'
+ {
+ $$ = mkinsttype($<tok.src>1, mkidtype($<tok.src>1, $1), $3);
+ }
+ ;
+
+dotiditype : type '.' Lid
+ {
+ $$ = mkdottype($1.src.start, $<tok.src>3.stop, $1, $3);
+ }
+ | type '.' Lid '[' types ']'
+ {
+ $$ = mkdottype($1.src.start, $<tok.src>3.stop, $1, $3);
+ $$ = mkinsttype($1.src, $$, $5);
+ }
+ ;
+
+tuplist : type
+ {
+ $$ = mkids($1.src, nil, $1, nil);
+ }
+ | tuplist ',' type
+ {
+ $$ = mkids($1.src, nil, $3, $1);
+ }
+ ;
+
+polydec :
+ {
+ $$ = nil;
+ }
+ | '[' ids ']'
+ {
+ $$ = polydecl($2);
+ }
+ ;
+
+fnarg : '(' forms ')'
+ {
+ $$ = mktype($1.start, $3.stop, Tfn, tnone, $2);
+ }
+ | '(' '*' ')'
+ {
+ $$ = mktype($1.start, $3.stop, Tfn, tnone, nil);
+ $$.varargs = byte 1;
+ }
+ | '(' ftypes ',' '*' ')'
+ {
+ $$ = mktype($1.start, $5.stop, Tfn, tnone, $2);
+ $$.varargs = byte 1;
+ }
+ ;
+
+fnargret: fnarg %prec ':'
+ {
+ $$ = $1;
+ }
+ | fnarg ':' type
+ {
+ $1.tof = $3;
+ $1.src.stop = $3.src.stop;
+ $$ = $1;
+ }
+ ;
+
+fnargretp: fnargret %prec '='
+ {
+ $$ = $1;
+ }
+ | fnargret Lfor '{' tpolys '}'
+ {
+ $$ = $1;
+ $$.val = rotater($4);
+ }
+ ;
+
+forms :
+ {
+ $$ = nil;
+ }
+ | ftypes
+ ;
+
+ftypes : ftype
+ | ftypes ',' ftype
+ {
+ $$ = appdecls($1, $3);
+ }
+ ;
+
+ftype : nids ':' type
+ {
+ $$ = typeids($1, $3);
+ }
+ | nids ':' adtk
+ {
+ $$ = typeids($1, $3);
+ for(d := $$; d != nil; d = d.next)
+ d.implicit = byte 1;
+ }
+ | idterms ':' type
+ {
+ $$ = mkids($1.src, enter("junk", 0), $3, nil);
+ $$.store = Darg;
+ yyerror("illegal argument declaraion");
+ }
+ | idterms ':' adtk
+ {
+ $$ = mkids($1.src, enter("junk", 0), $3, nil);
+ $$.store = Darg;
+ yyerror("illegal argument declaraion");
+ }
+ ;
+
+nids : nrids
+ {
+ $$ = revids($1);
+ }
+ ;
+
+nrids : Lid
+ {
+ $$ = mkids($<tok.src>1, $1, nil, nil);
+ $$.store = Darg;
+ }
+ | Lnil
+ {
+ $$ = mkids($1, nil, nil, nil);
+ $$.store = Darg;
+ }
+ | nrids ',' Lid
+ {
+ $$ = mkids($<tok.src>3, $3, nil, $1);
+ $$.store = Darg;
+ }
+ | nrids ',' Lnil
+ {
+ $$ = mkids($3, nil, nil, $1);
+ $$.store = Darg;
+ }
+ ;
+
+adtk : Lself iditype
+ {
+ $$ = $2;
+ }
+ | Lself Lref iditype
+ {
+ $$ = mktype($<tok.src>2.start, $<tok.src>3.stop, Tref, $3, nil);
+ }
+ | Lself dotiditype
+ {
+ $$ = $2;
+ }
+ | Lself Lref dotiditype
+ {
+ $$ = mktype($<tok.src>2.start, $<tok.src>3.stop, Tref, $3, nil);
+ }
+ ;
+
+fndef : fnname fnargretp raises fbody
+ {
+ $$ = fndecl($1, $2, $4);
+ nfns++;
+ # patch up polydecs
+ if($1.op == Odot){
+ if($1.right.left != nil){
+ $2.polys = $1.right.left.decl;
+ $1.right.left = nil;
+ }
+ if($1.left.op == Oname && $1.left.left != nil){
+ $$.decl = $1.left.left.decl;
+ $1.left.left = nil;
+ }
+ }
+ else{
+ if($1.left != nil){
+ $2.polys = $1.left.decl;
+ $1.left = nil;
+ }
+ }
+ $2.eraises = $3;
+ $$.src = $1.src;
+ }
+ ;
+
+raises : Lraises '(' idlist ')'
+ {
+ $$ = mkn(Otuple, rotater($3), nil);
+ $$.src.start = $1.start;
+ $$.src.stop = $4.stop;
+ }
+ | Lraises idatom
+ {
+ $$ = mkn(Otuple, mkunary(Oseq, $2), nil);
+ $$.src.start = $1.start;
+ $$.src.stop = $2.src.stop;
+ }
+ | %prec Lraises
+ {
+ $$ = nil;
+ }
+ ;
+
+fbody : '{' stmts '}'
+ {
+ if($2 == nil){
+ $2 = mkn(Onothing, nil, nil);
+ $2.src.start = curline();
+ $2.src.stop = $2.src.start;
+ }
+ $$ = rotater($2);
+ $$.src.start = $1.start;
+ $$.src.stop = $3.stop;
+ }
+ | error '}'
+ {
+ $$ = mkn(Onothing, nil, nil);
+ }
+ | error '{' stmts '}'
+ {
+ $$ = mkn(Onothing, nil, nil);
+ }
+ ;
+
+fnname : Lid polydec
+ {
+ $$ = mkname($<tok.src>1, $1);
+ if($2 != nil){
+ $$.left = mkn(Onothing, nil ,nil);
+ $$.left.decl = $2;
+ }
+ }
+ | fnname '.' Lid polydec
+ {
+ $$ = mkbin(Odot, $1, mkname($<tok.src>3, $3));
+ if($4 != nil){
+ $$.right.left = mkn(Onothing, nil ,nil);
+ $$.right.left.decl = $4;
+ }
+ }
+ ;
+
+stmts :
+ {
+ $$ = nil;
+ }
+ | stmts decl
+ {
+ if($1 == nil)
+ $$ = $2;
+ else if($2 == nil)
+ $$ = $1;
+ else
+ $$ = mkbin(Oseq, $1, $2);
+ }
+ | stmts stmt
+ {
+ if($1 == nil)
+ $$ = $2;
+ else
+ $$ = mkbin(Oseq, $1, $2);
+ }
+ ;
+
+elists : '(' elist ')'
+ | elists ',' '(' elist ')'
+ ;
+
+stmt : error ';'
+ {
+ $$ = mkn(Onothing, nil, nil);
+ $$.src.start = curline();
+ $$.src.stop = $$.src.start;
+ }
+ | error '}'
+ {
+ $$ = mkn(Onothing, nil, nil);
+ $$.src.start = curline();
+ $$.src.stop = $$.src.start;
+ }
+ | error '{' stmts '}'
+ {
+ $$ = mkn(Onothing, nil, nil);
+ $$.src.start = curline();
+ $$.src.stop = $$.src.start;
+ }
+ | '{' stmts '}'
+ {
+ if($2 == nil){
+ $2 = mkn(Onothing, nil, nil);
+ $2.src.start = curline();
+ $2.src.stop = $2.src.start;
+ }
+ $$ = mkscope(rotater($2));
+ }
+ | elists ':' type ';'
+ {
+ yyerror("illegal declaration");
+ $$ = mkn(Onothing, nil, nil);
+ $$.src.start = curline();
+ $$.src.stop = $$.src.start;
+ }
+ | elists ':' type '=' exp';'
+ {
+ yyerror("illegal declaration");
+ $$ = mkn(Onothing, nil, nil);
+ $$.src.start = curline();
+ $$.src.stop = $$.src.start;
+ }
+ | zexp ';'
+ {
+ $$ = $1;
+ }
+ | Lif '(' exp ')' stmt
+ {
+ $$ = mkn(Oif, $3, mkunary(Oseq, $5));
+ $$.src.start = $1.start;
+ $$.src.stop = $5.src.stop;
+ }
+ | Lif '(' exp ')' stmt Lelse stmt
+ {
+ $$ = mkn(Oif, $3, mkbin(Oseq, $5, $7));
+ $$.src.start = $1.start;
+ $$.src.stop = $7.src.stop;
+ }
+ | bclab Lfor '(' zexp ';' zexp ';' zexp ')' stmt
+ {
+ $$ = mkunary(Oseq, $10);
+ if($8.op != Onothing)
+ $$.right = $8;
+ $$ = mkbin(Ofor, $6, $$);
+ $$.decl = $1;
+ if($4.op != Onothing)
+ $$ = mkbin(Oseq, $4, $$);
+ }
+ | bclab Lwhile '(' zexp ')' stmt
+ {
+ $$ = mkn(Ofor, $4, mkunary(Oseq, $6));
+ $$.src.start = $2.start;
+ $$.src.stop = $6.src.stop;
+ $$.decl = $1;
+ }
+ | bclab Ldo stmt Lwhile '(' zexp ')' ';'
+ {
+ $$ = mkn(Odo, $6, $3);
+ $$.src.start = $2.start;
+ $$.src.stop = $7.stop;
+ $$.decl = $1;
+ }
+ | Lbreak bctarg ';'
+ {
+ $$ = mkn(Obreak, nil, nil);
+ $$.decl = $2;
+ $$.src = $1;
+ }
+ | Lcont bctarg ';'
+ {
+ $$ = mkn(Ocont, nil, nil);
+ $$.decl = $2;
+ $$.src = $1;
+ }
+ | Lreturn zexp ';'
+ {
+ $$ = mkn(Oret, $2, nil);
+ $$.src = $1;
+ if($2.op == Onothing)
+ $$.left = nil;
+ else
+ $$.src.stop = $2.src.stop;
+ }
+ | Lspawn exp ';'
+ {
+ $$ = mkn(Ospawn, $2, nil);
+ $$.src.start = $1.start;
+ $$.src.stop = $2.src.stop;
+ }
+ | Lraise zexp ';'
+ {
+ $$ = mkn(Oraise, $2, nil);
+ $$.src.start = $1.start;
+ $$.src.stop = $2.src.stop;
+ }
+ | bclab Lcase exp '{' cqstmts '}'
+ {
+ $$ = mkn(Ocase, $3, caselist($5, nil));
+ $$.src = $3.src;
+ $$.decl = $1;
+ }
+ | bclab Lalt '{' qstmts '}'
+ {
+ $$ = mkn(Oalt, caselist($4, nil), nil);
+ $$.src = $2;
+ $$.decl = $1;
+ }
+ | bclab Lpick Lid Ldeclas exp '{' pstmts '}'
+ {
+ $$ = mkn(Opick, mkbin(Odas, mkname($<tok.src>3, $3), $5), caselist($7, nil));
+ $$.src.start = $<tok.src>3.start;
+ $$.src.stop = $5.src.stop;
+ $$.decl = $1;
+ }
+ | Lexit ';'
+ {
+ $$ = mkn(Oexit, nil, nil);
+ $$.src = $1;
+ }
+ | '{' stmts '}' Lexcept idexc '{' eqstmts '}'
+ {
+ if($2 == nil){
+ $2 = mkn(Onothing, nil, nil);
+ $2.src.start = $2.src.stop = curline();
+ }
+ $2 = mkscope(rotater($2));
+ $$ = mkbin(Oexstmt, $2, mkn(Oexcept, $5, caselist($7, nil)));
+ }
+# | stmt Lexcept idexc '{' eqstmts '}'
+# {
+# $$ = mkbin(Oexstmt, $1, mkn(Oexcept, $3, caselist($5, nil)));
+# }
+ ;
+
+bclab :
+ {
+ $$ = nil;
+ }
+ | ids ':'
+ {
+ if($1.next != nil)
+ yyerror("only one identifier allowed in a label");
+ $$ = $1;
+ }
+ ;
+
+bctarg :
+ {
+ $$ = nil;
+ }
+ | Lid
+ {
+ $$ = mkids($<tok.src>1, $1, nil, nil);
+ }
+ ;
+
+qstmts : qbodies stmts
+ {
+ $1.left.right.right = $2;
+ $$ = $1;
+ }
+ ;
+
+qbodies : qual Llabs
+ {
+ $$ = mkunary(Oseq, mkscope(mkunary(Olabel, rotater($1))));
+ }
+ | qbodies stmts qual Llabs
+ {
+ $1.left.right.right = $2;
+ $$ = mkbin(Oseq, mkscope(mkunary(Olabel, rotater($3))), $1);
+ }
+ ;
+
+cqstmts : cqbodies stmts
+ {
+ $1.left.right = mkscope($2);
+ $$ = $1;
+ }
+ ;
+
+cqbodies : qual Llabs
+ {
+ $$ = mkunary(Oseq, mkunary(Olabel, rotater($1)));
+ }
+ | cqbodies stmts qual Llabs
+ {
+ $1.left.right = mkscope($2);
+ $$ = mkbin(Oseq, mkunary(Olabel, rotater($3)), $1);
+ }
+ ;
+
+eqstmts : eqbodies stmts
+ {
+ $1.left.right = mkscope($2);
+ $$ = $1;
+ }
+ ;
+
+eqbodies : qual Llabs
+ {
+ $$ = mkunary(Oseq, mkunary(Olabel, rotater($1)));
+ }
+ | eqbodies stmts qual Llabs
+ {
+ $1.left.right = mkscope($2);
+ $$ = mkbin(Oseq, mkunary(Olabel, rotater($3)), $1);
+ }
+ ;
+
+qual : exp
+ | exp Lto exp
+ {
+ $$ = mkbin(Orange, $1, $3);
+ }
+ | '*'
+ {
+ $$ = mkn(Owild, nil, nil);
+ $$.src = $1;
+ }
+ | qual Lor qual
+ {
+ $$ = mkbin(Oseq, $1, $3);
+ }
+ | error
+ {
+ $$ = mkn(Onothing, nil, nil);
+ $$.src.start = curline();
+ $$.src.stop = $$.src.start;
+ }
+ ;
+
+pstmts : pbodies stmts
+ {
+ $1.left.right = mkscope($2);
+ $$ = $1;
+ }
+ ;
+
+pbodies : pqual Llabs
+ {
+ $$ = mkunary(Oseq, mkunary(Olabel, rotater($1)));
+ }
+ | pbodies stmts pqual Llabs
+ {
+ $1.left.right = mkscope($2);
+ $$ = mkbin(Oseq, mkunary(Olabel, rotater($3)), $1);
+ }
+ ;
+
+pqual : Lid
+ {
+ $$ = mkname($<tok>1.src, $1);
+ }
+ | '*'
+ {
+ $$ = mkn(Owild, nil, nil);
+ $$.src = $1;
+ }
+ | pqual Lor pqual
+ {
+ $$ = mkbin(Oseq, $1, $3);
+ }
+ | error
+ {
+ $$ = mkn(Onothing, nil, nil);
+ $$.src.start = curline();
+ $$.src.stop = $$.src.start;
+ }
+ ;
+
+zexp :
+ {
+ $$ = mkn(Onothing, nil, nil);
+ $$.src.start = curline();
+ $$.src.stop = $$.src.start;
+ }
+ | exp
+ ;
+
+exp : monexp
+ | exp '=' exp
+ {
+ $$ = mkbin(Oas, $1, $3);
+ }
+ | exp Landeq exp
+ {
+ $$ = mkbin(Oandas, $1, $3);
+ }
+ | exp Loreq exp
+ {
+ $$ = mkbin(Ooras, $1, $3);
+ }
+ | exp Lxoreq exp
+ {
+ $$ = mkbin(Oxoras, $1, $3);
+ }
+ | exp Llsheq exp
+ {
+ $$ = mkbin(Olshas, $1, $3);
+ }
+ | exp Lrsheq exp
+ {
+ $$ = mkbin(Orshas, $1, $3);
+ }
+ | exp Laddeq exp
+ {
+ $$ = mkbin(Oaddas, $1, $3);
+ }
+ | exp Lsubeq exp
+ {
+ $$ = mkbin(Osubas, $1, $3);
+ }
+ | exp Lmuleq exp
+ {
+ $$ = mkbin(Omulas, $1, $3);
+ }
+ | exp Ldiveq exp
+ {
+ $$ = mkbin(Odivas, $1, $3);
+ }
+ | exp Lmodeq exp
+ {
+ $$ = mkbin(Omodas, $1, $3);
+ }
+ | exp Lexpeq exp
+ {
+ $$ = mkbin(Oexpas, $1, $3);
+ }
+ | exp Lcomm '=' exp
+ {
+ $$ = mkbin(Osnd, $1, $4);
+ }
+ | exp Ldeclas exp
+ {
+ $$ = mkbin(Odas, $1, $3);
+ }
+ | Lload Lid exp %prec Lload
+ {
+ $$ = mkn(Oload, $3, nil);
+ $$.src.start = $<tok.src.start>1;
+ $$.src.stop = $3.src.stop;
+ $$.ty = mkidtype($<tok.src>2, $2);
+ }
+ | exp Lexp exp
+ {
+ $$ = $$ = mkbin(Oexp, $1, $3);
+ }
+ | exp '*' exp
+ {
+ $$ = mkbin(Omul, $1, $3);
+ }
+ | exp '/' exp
+ {
+ $$ = mkbin(Odiv, $1, $3);
+ }
+ | exp '%' exp
+ {
+ $$ = mkbin(Omod, $1, $3);
+ }
+ | exp '+' exp
+ {
+ $$ = mkbin(Oadd, $1, $3);
+ }
+ | exp '-' exp
+ {
+ $$ = mkbin(Osub, $1, $3);
+ }
+ | exp Lrsh exp
+ {
+ $$ = mkbin(Orsh, $1, $3);
+ }
+ | exp Llsh exp
+ {
+ $$ = mkbin(Olsh, $1, $3);
+ }
+ | exp '<' exp
+ {
+ $$ = mkbin(Olt, $1, $3);
+ }
+ | exp '>' exp
+ {
+ $$ = mkbin(Ogt, $1, $3);
+ }
+ | exp Lleq exp
+ {
+ $$ = mkbin(Oleq, $1, $3);
+ }
+ | exp Lgeq exp
+ {
+ $$ = mkbin(Ogeq, $1, $3);
+ }
+ | exp Leq exp
+ {
+ $$ = mkbin(Oeq, $1, $3);
+ }
+ | exp Lneq exp
+ {
+ $$ = mkbin(Oneq, $1, $3);
+ }
+ | exp '&' exp
+ {
+ $$ = mkbin(Oand, $1, $3);
+ }
+ | exp '^' exp
+ {
+ $$ = mkbin(Oxor, $1, $3);
+ }
+ | exp '|' exp
+ {
+ $$ = mkbin(Oor, $1, $3);
+ }
+ | exp Lcons exp
+ {
+ $$ = mkbin(Ocons, $1, $3);
+ }
+ | exp Landand exp
+ {
+ $$ = mkbin(Oandand, $1, $3);
+ }
+ | exp Loror exp
+ {
+ $$ = mkbin(Ooror, $1, $3);
+ }
+ ;
+
+monexp : term
+ | '+' monexp
+ {
+ $2.src.start = $1.start;
+ $$ = $2;
+ }
+ | '-' monexp
+ {
+ $$ = mkunary(Oneg, $2);
+ $$.src.start = $1.start;
+ }
+ | '!' monexp
+ {
+ $$ = mkunary(Onot, $2);
+ $$.src.start = $1.start;
+ }
+ | '~' monexp
+ {
+ $$ = mkunary(Ocomp, $2);
+ $$.src.start = $1.start;
+ }
+ | '*' monexp
+ {
+ $$ = mkunary(Oind, $2);
+ $$.src.start = $1.start;
+ }
+ | Linc monexp
+ {
+ $$ = mkunary(Opreinc, $2);
+ $$.src.start = $1.start;
+ }
+ | Ldec monexp
+ {
+ $$ = mkunary(Opredec, $2);
+ $$.src.start = $1.start;
+ }
+ | Lcomm monexp
+ {
+ $$ = mkunary(Orcv, $2);
+ $$.src.start = $1.start;
+ }
+ | Lhd monexp
+ {
+ $$ = mkunary(Ohd, $2);
+ $$.src.start = $1.start;
+ }
+ | Ltl monexp
+ {
+ $$ = mkunary(Otl, $2);
+ $$.src.start = $1.start;
+ }
+ | Llen monexp
+ {
+ $$ = mkunary(Olen, $2);
+ $$.src.start = $1.start;
+ }
+ | Lref monexp
+ {
+ $$ = mkunary(Oref, $2);
+ $$.src.start = $1.start;
+ }
+ | Ltagof monexp
+ {
+ $$ = mkunary(Otagof, $2);
+ $$.src.start = $1.start;
+ }
+ | Larray '[' exp ']' Lof type
+ {
+ $$ = mkn(Oarray, $3, nil);
+ $$.ty = mktype($1.start, $6.src.stop, Tarray, $6, nil);
+ $$.src = $$.ty.src;
+ }
+ | Larray '[' exp ']' Lof '{' initlist '}'
+ {
+ $$ = mkn(Oarray, $3, $7);
+ $$.src.start = $1.start;
+ $$.src.stop = $8.stop;
+ }
+ | Larray '[' ']' Lof '{' initlist '}'
+ {
+ $$ = mkn(Onothing, nil, nil);
+ $$.src.start = $2.start;
+ $$.src.stop = $3.stop;
+ $$ = mkn(Oarray, $$, $6);
+ $$.src.start = $1.start;
+ $$.src.stop = $7.stop;
+ }
+ | Llist Lof '{' celist '}'
+ {
+ $$ = etolist($4);
+ $$.src.start = $1.start;
+ $$.src.stop = $5.stop;
+ }
+ | Lchan Lof type
+ {
+ $$ = mkn(Ochan, nil, nil);
+ $$.ty = mktype($1.start, $3.src.stop, Tchan, $3, nil);
+ $$.src = $$.ty.src;
+ }
+ | Lchan '[' exp ']' Lof type
+ {
+ $$ = mkn(Ochan, $3, nil);
+ $$.ty = mktype($1.start, $6.src.stop, Tchan, $6, nil);
+ $$.src = $$.ty.src;
+ }
+ | Larray Lof Ltid monexp
+ {
+ $$ = mkunary(Ocast, $4);
+ $$.ty = mktype($1.start, $4.src.stop, Tarray, mkidtype($<tok.src>3, $3), nil);
+ $$.src = $$.ty.src;
+ }
+ | Ltid monexp
+ {
+ $$ = mkunary(Ocast, $2);
+ $$.src.start = $<tok.src>1.start;
+ $$.ty = mkidtype($$.src, $1);
+ }
+ | Lid monexp
+ {
+ $$ = mkunary(Ocast, $2);
+ $$.src.start = $<tok.src>1.start;
+ $$.ty = mkidtype($$.src, $1);
+ }
+ | fixtype monexp
+ {
+ $$ = mkunary(Ocast, $2);
+ $$.src.start = $<tok.src>1.start;
+ $$.ty = $1;
+ }
+ ;
+
+term : idatom
+ | term '(' zelist ')'
+ {
+ $$ = mkn(Ocall, $1, $3);
+ $$.src.start = $1.src.start;
+ $$.src.stop = $4.stop;
+ }
+ | '(' elist ')'
+ {
+ $$ = $2;
+ if($2.op == Oseq)
+ $$ = mkn(Otuple, rotater($2), nil);
+ else
+ $$.flags |= byte PARENS;
+ $$.src.start = $1.start;
+ $$.src.stop = $3.stop;
+ }
+ | Lfn fnargret
+ {
+# n := mkdeclname($1, mkids($1, enter(".fn"+string nfnexp++, 0), nil, nil));
+# $<node>$ = fndef(n, $2);
+# nfns++;
+ } fbody
+ {
+# $$ = fnfinishdef($<node>3, $4);
+# $$ = mkdeclname($1, $$.left.decl);
+ yyerror("urt unk");
+ $$ = nil;
+ }
+ | term '.' Lid
+ {
+ $$ = mkbin(Odot, $1, mkname($<tok.src>3, $3));
+ }
+ | term Lmdot term
+ {
+ $$ = mkbin(Omdot, $1, $3);
+ }
+ | term '[' export ']'
+ {
+ $$ = mkbin(Oindex, $1, $3);
+ $$.src.stop = $4.stop;
+ }
+ | term '[' zexp ':' zexp ']'
+ {
+ if($3.op == Onothing)
+ $3.src = $4;
+ if($5.op == Onothing)
+ $5.src = $4;
+ $$ = mkbin(Oslice, $1, mkbin(Oseq, $3, $5));
+ $$.src.stop = $6.stop;
+ }
+ | term Linc
+ {
+ $$ = mkunary(Oinc, $1);
+ $$.src.stop = $2.stop;
+ }
+ | term Ldec
+ {
+ $$ = mkunary(Odec, $1);
+ $$.src.stop = $2.stop;
+ }
+ | Lsconst
+ {
+ $$ = mksconst($<tok.src>1, $1);
+ }
+ | Lconst
+ {
+ $$ = mkconst($<tok.src>1, $1);
+ if($1 > big 16r7fffffff || $1 < big -16r7fffffff)
+ $$.ty = tbig;
+ }
+ | Lrconst
+ {
+ $$ = mkrconst($<tok.src>1, $1);
+ }
+ | term '[' exportlist ',' export ']'
+ {
+ $$ = mkbin(Oindex, $1, rotater(mkbin(Oseq, $3, $5)));
+ $$.src.stop = $6.stop;
+ }
+ ;
+
+idatom : Lid
+ {
+ $$ = mkname($<tok.src>1, $1);
+ }
+ | Lnil
+ {
+ $$ = mknil($<tok.src>1);
+ }
+ ;
+
+idterm : '(' idlist ')'
+ {
+ $$ = mkn(Otuple, rotater($2), nil);
+ $$.src.start = $1.start;
+ $$.src.stop = $3.stop;
+ }
+ ;
+
+exportlist : export
+ | exportlist ',' export
+ {
+ $$ = mkbin(Oseq, $1, $3);
+ }
+ ;
+
+export : exp
+ | texp
+ ;
+
+texp : Ltid
+ {
+ $$ = mkn(Otype, nil, nil);
+ $$.ty = mkidtype($<tok.src>1, $1);
+ $$.src = $$.ty.src;
+ }
+ | Larray Lof type
+ {
+ $$ = mkn(Otype, nil, nil);
+ $$.ty = mktype($1.start, $3.src.stop, Tarray, $3, nil);
+ $$.src = $$.ty.src;
+ }
+ | Llist Lof type
+ {
+ $$ = mkn(Otype, nil, nil);
+ $$.ty = mktype($1.start, $3.src.stop, Tlist, $3, nil);
+ $$.src = $$.ty.src;
+ }
+ | Lcyclic type
+ {
+ $$ = mkn(Otype, nil ,nil);
+ $$.ty = $2;
+ $$.ty.flags |= CYCLIC;
+ $$.src = $$.ty.src;
+ }
+ ;
+
+idexc : Lid
+ {
+ $$ = mkname($<tok.src>1, $1);
+ }
+ | # empty
+ {
+ $$ = nil;
+ }
+ ;
+
+idlist : idterm
+ | idatom
+ | idlist ',' idterm
+ {
+ $$ = mkbin(Oseq, $1, $3);
+ }
+ | idlist ',' idatom
+ {
+ $$ = mkbin(Oseq, $1, $3);
+ }
+ ;
+
+zelist :
+ {
+ $$ = nil;
+ }
+ | elist
+ {
+ $$ = rotater($1);
+ }
+ ;
+
+celist : elist
+ | elist ','
+ ;
+
+elist : exp
+ | elist ',' exp
+ {
+ $$ = mkbin(Oseq, $1, $3);
+ }
+ ;
+
+initlist : elemlist
+ {
+ $$ = rotater($1);
+ }
+ | elemlist ','
+ {
+ $$ = rotater($1);
+ }
+ ;
+
+elemlist : elem
+ | elemlist ',' elem
+ {
+ $$ = mkbin(Oseq, $1, $3);
+ }
+ ;
+
+elem : exp
+ {
+ $$ = mkn(Oelem, nil, $1);
+ $$.src = $1.src;
+ }
+ | qual Llabs exp
+ {
+ $$ = mkbin(Oelem, rotater($1), $3);
+ }
+ ;
+
+tpolys : tpoly dfields
+ {
+ if($1.op == Oseq)
+ $1.right.left = rotater($2);
+ else
+ $1.left = rotater($2);
+ $$ = $1;
+ }
+ ;
+
+tpoly : ids Llabs
+ {
+ $$ = typedecl($1, mktype($1.src.start, $2.stop, Tpoly, nil, nil));
+ }
+ | tpoly dfields ids Llabs
+ {
+ if($1.op == Oseq)
+ $1.right.left = rotater($2);
+ else
+ $1.left = rotater($2);
+ $$ = mkbin(Oseq, $1, typedecl($3, mktype($3.src.start, $4.stop, Tpoly, nil, nil)));
+ }
+ ;
+
+%%
+
+include "keyring.m";
+
+sys: Sys;
+ print, fprint, sprint: import sys;
+
+bufio: Bufio;
+ Iobuf: import bufio;
+
+str: String;
+
+keyring:Keyring;
+ md5: import keyring;
+
+math: Math;
+ import_real, export_real, isnan: import math;
+
+yyctxt: ref YYLEX;
+
+canonnan: real;
+
+debug = array[256] of {* => 0};
+
+noline = -1;
+nosrc = Src(-1, -1);
+
+infile: string;
+
+# front end
+include "arg.m";
+include "lex.b";
+include "types.b";
+include "nodes.b";
+include "decls.b";
+
+include "typecheck.b";
+
+# back end
+include "gen.b";
+include "ecom.b";
+include "asm.b";
+include "dis.b";
+include "sbl.b";
+include "stubs.b";
+include "com.b";
+include "optim.b";
+
+init(nil: ref Draw->Context, argv: list of string)
+{
+ s: string;
+
+ sys = load Sys Sys->PATH;
+ keyring = load Keyring Keyring->PATH;
+ math = load Math Math->PATH;
+ bufio = load Bufio Bufio->PATH;
+ if(bufio == nil){
+ sys->print("can't load %s: %r\n", Bufio->PATH);
+ raise("fail:bad module");
+ }
+ str = load String String->PATH;
+ if(str == nil){
+ sys->print("can't load %s: %r\n", String->PATH);
+ raise("fail:bad module");
+ }
+
+ stderr = sys->fildes(2);
+ yyctxt = ref YYLEX;
+
+ math->FPcontrol(0, Math->INVAL|Math->ZDIV|Math->OVFL|Math->UNFL|Math->INEX);
+ na := array[1] of {0.};
+ import_real(array[8] of {byte 16r7f, * => byte 16rff}, na);
+ canonnan = na[0];
+ if(!isnan(canonnan))
+ fatal("bad canonical NaN");
+
+ lexinit();
+ typeinit();
+ optabinit();
+
+ gendis = 1;
+ asmsym = 0;
+ maxerr = 20;
+ ofile := "";
+ ext := "";
+
+ arg := Arg.init(argv);
+ while(c := arg.opt()){
+ case c{
+ 'Y' =>
+ emitsbl = arg.arg();
+ if(emitsbl == nil)
+ usage();
+ 'C' =>
+ dontcompile = 1;
+ 'D' =>
+ #
+ # debug flags:
+ #
+ # a alt compilation
+ # A array constructor compilation
+ # b boolean and branch compilation
+ # c case compilation
+ # d function declaration
+ # D descriptor generation
+ # e expression compilation
+ # E addressable expression compilation
+ # f print arguments for compiled functions
+ # F constant folding
+ # g print out globals
+ # m module declaration and type checking
+ # n nil references
+ # s print sizes of output file sections
+ # S type signing
+ # t type checking function bodies
+ # T timing
+ # v global var and constant compilation
+ # x adt verification
+ # Y tuple compilation
+ # z Z bug fixes
+ #
+ s = arg.arg();
+ for(i := 0; i < len s; i++){
+ c = s[i];
+ if(c < len debug)
+ debug[c] = 1;
+ }
+ 'I' =>
+ s = arg.arg();
+ if(s == "")
+ usage();
+ addinclude(s);
+ 'G' =>
+ asmsym = 1;
+ 'S' =>
+ gendis = 0;
+ 'a' =>
+ emitstub = 1;
+ 'A' =>
+ emitstub = emitdyn = 1;
+ 'c' =>
+ mustcompile = 1;
+ 'e' =>
+ maxerr = 1000;
+ 'f' =>
+ fabort = 1;
+ 'F' =>
+ newfnptr = 1;
+ 'g' =>
+ dosym = 1;
+ 'i' =>
+ dontinline = 1;
+ 'o' =>
+ ofile = arg.arg();
+ 'O' =>
+ optims = 1;
+ 's' =>
+ s = arg.arg();
+ if(s != nil)
+ fixss = int s;
+ 't' =>
+ emittab = arg.arg();
+ if(emittab == nil)
+ usage();
+ 'T' =>
+ emitcode = arg.arg();
+ if(emitcode == nil)
+ usage();
+ 'd' =>
+ emitcode = arg.arg();
+ if(emitcode == nil)
+ usage();
+ emitdyn = 1;
+ 'w' =>
+ superwarn = dowarn;
+ dowarn = 1;
+ 'x' =>
+ ext = arg.arg();
+ 'X' =>
+ signdump = arg.arg();
+ 'z' =>
+ arrayz = 1;
+ * =>
+ usage();
+ }
+ }
+
+ addinclude("/module");
+
+ argv = arg.argv;
+ arg = nil;
+
+ if(argv == nil){
+ usage();
+ }else if(ofile != nil){
+ if(len argv != 1)
+ usage();
+ translate(hd argv, ofile, mkfileext(ofile, ".dis", ".sbl"));
+ }else{
+ pr := len argv != 1;
+ if(ext == ""){
+ ext = ".s";
+ if(gendis)
+ ext = ".dis";
+ }
+ for(; argv != nil; argv = tl argv){
+ file := hd argv;
+ (nil, s) = str->splitr(file, "/");
+ if(pr)
+ print("%s:\n", s);
+ out := mkfileext(s, ".b", ext);
+ translate(file, out, mkfileext(out, ext, ".sbl"));
+ }
+ }
+ if (toterrors > 0)
+ raise("fail:errors");
+}
+
+usage()
+{
+ fprint(stderr, "usage: limbo [-GSagwe] [-I incdir] [-o outfile] [-{T|t|d} module] [-D debug] file ...\n");
+ raise("fail:usage");
+}
+
+mkfileext(file, oldext, ext: string): string
+{
+ n := len file;
+ n2 := len oldext;
+ if(n >= n2 && file[n-n2:] == oldext)
+ file = file[:n-n2];
+ return file + ext;
+}
+
+translate(in, out, dbg: string)
+{
+ infile = in;
+ outfile = out;
+ errors = 0;
+ bins[0] = bufio->open(in, Bufio->OREAD);
+ if(bins[0] == nil){
+ fprint(stderr, "can't open %s: %r\n", in);
+ toterrors++;
+ return;
+ }
+ doemit := emitcode != "" || emitstub || emittab != "" || emitsbl != "";
+ if(!doemit){
+ bout = bufio->create(out, Bufio->OWRITE, 8r666);
+ if(bout == nil){
+ fprint(stderr, "can't open %s: %r\n", out);
+ toterrors++;
+ bins[0].close();
+ return;
+ }
+ if(dosym){
+ bsym = bufio->create(dbg, Bufio->OWRITE, 8r666);
+ if(bsym == nil)
+ fprint(stderr, "can't open %s: %r\n", dbg);
+ }
+ }
+
+ lexstart(in);
+
+ popscopes();
+ typestart();
+ declstart();
+ nfnexp = 0;
+
+ parset = sys->millisec();
+ yyparse(yyctxt);
+ parset = sys->millisec() - parset;
+
+ checkt = sys->millisec();
+ entry := typecheck(!doemit);
+ checkt = sys->millisec() - checkt;
+
+ modcom(entry);
+
+ fns = nil;
+ nfns = 0;
+ descriptors = nil;
+
+ if(debug['T'])
+ print("times: parse=%d type=%d: gen=%d write=%d symbols=%d\n",
+ parset, checkt, gent, writet, symt);
+
+ if(bout != nil)
+ bout.close();
+ if(bsym != nil)
+ bsym.close();
+ toterrors += errors;
+ if(errors && bout != nil)
+ sys->remove(out);
+ if(errors && bsym != nil)
+ sys->remove(dbg);
+}
+
+pwd(): string
+{
+ workdir := load Workdir Workdir->PATH;
+ if(workdir == nil)
+ cd := "/";
+ else
+ cd = workdir->init();
+ # sys->print("pwd: %s\n", cd);
+ return cd;
+}
+
+cleanname(s: string): string
+{
+ ls, path: list of string;
+
+ if(s == nil)
+ return nil;
+ if(s[0] != '/' && s[0] != '\\')
+ (nil, ls) = sys->tokenize(pwd(), "/\\");
+ for( ; ls != nil; ls = tl ls)
+ path = hd ls :: path;
+ (nil, ls) = sys->tokenize(s, "/\\");
+ for( ; ls != nil; ls = tl ls){
+ n := hd ls;
+ if(n == ".")
+ ;
+ else if (n == ".."){
+ if(path != nil)
+ path = tl path;
+ }
+ else
+ path = n :: path;
+ }
+ p := "";
+ for( ; path != nil; path = tl path)
+ p = "/" + hd path + p;
+ if(p == nil)
+ p = "/";
+ # sys->print("cleanname: %s\n", p);
+ return p;
+}
+
+srcpath(): string
+{
+ srcp := cleanname(infile);
+ # sys->print("srcpath: %s\n", srcp);
+ return srcp;
+}
diff --git a/appl/cmd/limbo/mkfile b/appl/cmd/limbo/mkfile
new file mode 100644
index 00000000..2a555510
--- /dev/null
+++ b/appl/cmd/limbo/mkfile
@@ -0,0 +1,35 @@
+<../../../mkconfig
+
+TARG= limbo.dis\
+
+MODULES=\
+ arg.m\
+ disoptab.m\
+ isa.m\
+ limbo.m\
+ opname.m\
+ asm.b\
+ com.b\
+ decls.b\
+ dis.b\
+ ecom.b\
+ gen.b\
+ lex.b\
+ nodes.b\
+ optim.b\
+ sbl.b\
+ stubs.b\
+ typecheck.b\
+ types.b\
+
+SYSMODULES= \
+ bufio.m\
+ draw.m\
+ keyring.m\
+ math.m\
+ string.m\
+ sys.m\
+
+DISBIN=$ROOT/dis
+
+<$ROOT/mkfiles/mkdis
diff --git a/appl/cmd/limbo/nodes.b b/appl/cmd/limbo/nodes.b
new file mode 100644
index 00000000..61e97dc0
--- /dev/null
+++ b/appl/cmd/limbo/nodes.b
@@ -0,0 +1,1402 @@
+include "opname.m";
+
+znode: Node;
+
+isused = array[Oend] of
+{
+ Oas => 1,
+ Odas => 1,
+ Oaddas => 1,
+ Osubas => 1,
+ Omulas => 1,
+ Odivas => 1,
+ Omodas => 1,
+ Oexpas => 1,
+ Oandas => 1,
+ Ooras => 1,
+ Oxoras => 1,
+ Olshas => 1,
+ Onothing => 1,
+ Orshas => 1,
+ Oinc => 1,
+ Odec => 1,
+ Opreinc => 1,
+ Opredec => 1,
+ Ocall => 1,
+ Oraise => 1,
+ Ospawn => 1,
+ Osnd => 1,
+ Orcv => 1,
+
+ * => 0
+};
+
+sideeffect := array[Oend] of
+{
+ Oas => 1,
+ Odas => 1,
+ Oaddas => 1,
+ Osubas => 1,
+ Omulas => 1,
+ Odivas => 1,
+ Omodas => 1,
+ Oexpas => 1,
+ Oandas => 1,
+ Ooras => 1,
+ Oxoras => 1,
+ Olshas => 1,
+ Orshas => 1,
+ Oinc => 1,
+ Odec => 1,
+ Opreinc => 1,
+ Opredec => 1,
+ Ocall => 1,
+ Oraise => 1,
+ Ospawn => 1,
+ Osnd => 1,
+ Orcv => 1,
+
+ Oadr => 1,
+ Oarray => 1,
+ Ocast => 1,
+ Ochan => 1,
+ Ocons => 1,
+ Odiv => 1,
+ Odot => 1,
+ Oind => 1,
+ Oindex => 1,
+ Oinds => 1,
+ Oindx => 1,
+ Olen => 1,
+ Oload => 1,
+ Omod => 1,
+ Oref => 1,
+
+ * => 0
+};
+
+opcommute = array[Oend] of
+{
+ Oeq => Oeq,
+ Oneq => Oneq,
+ Olt => Ogt,
+ Ogt => Olt,
+ Ogeq => Oleq,
+ Oleq => Ogeq,
+ Oadd => Oadd,
+ Omul => Omul,
+ Oxor => Oxor,
+ Oor => Oor,
+ Oand => Oand,
+
+ * => 0
+};
+
+oprelinvert = array[Oend] of
+{
+
+ Oeq => Oneq,
+ Oneq => Oeq,
+ Olt => Ogeq,
+ Ogt => Oleq,
+ Ogeq => Olt,
+ Oleq => Ogt,
+
+ * => 0
+};
+
+isrelop := array[Oend] of
+{
+
+ Oeq => 1,
+ Oneq => 1,
+ Olt => 1,
+ Oleq => 1,
+ Ogt => 1,
+ Ogeq => 1,
+ Oandand => 1,
+ Ooror => 1,
+ Onot => 1,
+
+ * => 0
+};
+
+ipow(x: big, n: int): big
+{
+ inv: int;
+ r: big;
+
+ inv = 0;
+ if(n < 0){
+ n = -n;
+ inv = 1;
+ }
+ r = big 1;
+ for(;;){
+ if(n&1)
+ r *= x;
+ if((n >>= 1) == 0)
+ break;
+ x *= x;
+ }
+ if(inv)
+ r = big 1/r;
+ return r;
+}
+
+rpow(x: real, n: int): real
+{
+ inv: int;
+ r: real;
+
+ inv = 0;
+ if(n < 0){
+ n = -n;
+ inv = 1;
+ }
+ r = 1.0;
+ for(;;){
+ if(n&1)
+ r *= x;
+ if((n >>= 1) == 0)
+ break;
+ x *= x;
+ }
+ if(inv)
+ r = 1.0/r;
+ return r;
+}
+
+real2fix(v: real, t: ref Type): big
+{
+ return big(v/scale(t));
+}
+
+fix2fix(v: big, f: ref Type, t: ref Type): big
+{
+ return big(real v * (scale(f)/scale(t)));
+}
+
+fix2real(v: big, f: ref Type): real
+{
+ return real v * scale(f);
+}
+
+istuple(n: ref Node): int
+{
+ d: ref Decl;
+
+ case(n.op){
+ Otuple =>
+ return 1;
+ Oname =>
+ d = n.decl;
+ if(d.importid != nil)
+ d = d.importid;
+ return d.store == Dconst && (n.ty.kind == Ttuple || n.ty.kind == Tadt);
+ Odot =>
+ return 0; # istuple(n.left);
+ }
+ return 0;
+}
+
+tuplemem(n: ref Node, d: ref Decl): ref Node
+{
+ ty: ref Type;
+ ids: ref Decl;
+
+ ty = n.ty;
+ n = n.left;
+ for(ids = ty.ids; ids != nil; ids = ids.next){
+ if(ids.sym == d.sym)
+ break;
+ else
+ n = n.right;
+ }
+ if(n == nil)
+ fatal("tuplemem cannot cope !\n");
+ return n.left;
+}
+
+varcom(v: ref Decl): int
+{
+ n := v.init;
+ n = fold(n);
+ v.init = n;
+ if(debug['v'])
+ print("variable '%s' val %s\n", v.sym.name, expconv(n));
+ if(n == nil)
+ return 1;
+
+ tn := ref znode;
+ tn.op = Oname;
+ tn.decl = v;
+ tn.src = v.src;
+ tn.ty = v.ty;
+ return initable(tn, n, 0);
+}
+
+initable(v, n: ref Node, allocdep: int): int
+{
+ case n.ty.kind{
+ Tiface or
+ Tgoto or
+ Tcase or
+ Tcasel or
+ Tcasec or
+ Talt or
+ Texcept =>
+ return 1;
+ Tint or
+ Tbig or
+ Tbyte or
+ Treal or
+ Tstring or
+ Tfix =>
+ if(n.op != Oconst)
+ break;
+ return 1;
+ Tadt or
+ Tadtpick or
+ Ttuple =>
+ if(n.op == Otuple)
+ n = n.left;
+ else if(n.op == Ocall)
+ n = n.right;
+ else
+ break;
+ for(; n != nil; n = n.right)
+ if(!initable(v, n.left, allocdep))
+ return 0;
+ return 1;
+ Tarray =>
+ if(n.op != Oarray)
+ break;
+ if(allocdep >= DADEPTH){
+ nerror(v, expconv(v)+"s initializer has arrays nested more than "+string allocdep+" deep");
+ return 0;
+ }
+ allocdep++;
+ usedesc(mktdesc(n.ty.tof));
+ if(n.left.op != Oconst){
+ nerror(v, expconv(v)+"s size is not a constant");
+ return 0;
+ }
+ for(e := n.right; e != nil; e = e.right)
+ if(!initable(v, e.left.right, allocdep))
+ return 0;
+ return 1;
+ Tany =>
+ return 1;
+ Tref or
+ Tlist or
+ Tpoly or
+ * =>
+ nerror(v, "can't initialize "+etconv(v));
+ return 0;
+ }
+ nerror(v, expconv(v)+"s initializer, "+expconv(n)+", is not a constant expression");
+ return 0;
+}
+
+#
+# merge together two sorted lists, yielding a sorted list
+#
+elemmerge(e, f: ref Node): ref Node
+{
+ r := rock := ref Node;
+ while(e != nil && f != nil){
+ if(e.left.left.c.val <= f.left.left.c.val){
+ r.right = e;
+ e = e.right;
+ }else{
+ r.right = f;
+ f = f.right;
+ }
+ r = r.right;
+ }
+ if(e != nil)
+ r.right = e;
+ else
+ r.right = f;
+ return rock.right;
+}
+
+#
+# recursively split lists and remerge them after they are sorted
+#
+recelemsort(e: ref Node, n: int): ref Node
+{
+ if(n <= 1)
+ return e;
+ m := n / 2 - 1;
+ ee := e;
+ for(i := 0; i < m; i++)
+ ee = ee.right;
+ r := ee.right;
+ ee.right = nil;
+ return elemmerge(recelemsort(e, n / 2),
+ recelemsort(r, (n + 1) / 2));
+}
+
+#
+# sort the elems by index; wild card is first
+#
+elemsort(e: ref Node): ref Node
+{
+ n := 0;
+ for(ee := e; ee != nil; ee = ee.right){
+ if(ee.left.left.op == Owild)
+ ee.left.left.c = ref Const(big -1, 0.);
+ n++;
+ }
+ return recelemsort(e, n);
+}
+
+sametree(n1: ref Node, n2: ref Node): int
+{
+ if(n1 == n2)
+ return 1;
+ if(n1 == nil || n2 == nil)
+ return 0;
+ if(n1.op != n2.op || n1.ty != n2.ty)
+ return 0;
+ if(n1.op == Oconst){
+ case(n1.ty.kind){
+ Tbig or
+ Tbyte or
+ Tint =>
+ return n1.c.val == n2.c.val;
+ Treal =>
+ return n1.c.rval == n2.c.rval;
+ Tfix =>
+ return n1.c.val == n2.c.val && tequal(n1.ty, n2.ty);
+ Tstring =>
+ return n1.decl.sym == n2.decl.sym;
+ }
+ return 0;
+ }
+ return n1.decl == n2.decl && sametree(n1.left, n2.left) && sametree(n1.right, n2.right);
+}
+
+occurs(d: ref Decl, n: ref Node): int
+{
+ if(n == nil)
+ return 0;
+ if(n.op == Oname){
+ if(d == n.decl)
+ return 1;
+ return 0;
+ }
+ return occurs(d, n.left) + occurs(d, n.right);
+}
+
+#
+# left and right subtrees the same
+#
+folds(n: ref Node): ref Node
+{
+ if(hasside(n, 1))
+ return n;
+ case(n.op){
+ Oeq or
+ Oleq or
+ Ogeq =>
+ n.c = ref Const(big 1, 0.0);
+ Osub =>
+ n.c = ref Const(big 0, 0.0);
+ Oxor or
+ Oneq or
+ Olt or
+ Ogt =>
+ n.c = ref Const(big 0, 0.0);
+ Oand or
+ Oor or
+ Oandand or
+ Ooror =>
+ return n.left;
+ * =>
+ return n;
+ }
+ n.op = Oconst;
+ n.left = n.right = nil;
+ n.decl = nil;
+ return n;
+}
+
+#
+# constant folding for typechecked expressions
+#
+fold(n: ref Node): ref Node
+{
+ if(n == nil)
+ return nil;
+ if(debug['F'])
+ print("fold %s\n", nodeconv(n));
+ n = efold(n);
+ if(debug['F'])
+ print("folded %s\n", nodeconv(n));
+ return n;
+}
+
+efold(n: ref Node): ref Node
+{
+ d: ref Decl;
+
+ if(n == nil)
+ return nil;
+
+ left := n.left;
+ right := n.right;
+ case n.op{
+ Oname =>
+ d = n.decl;
+ if(d.importid != nil)
+ d = d.importid;
+ if(d.store != Dconst){
+ if(d.store == Dtag){
+ n.op = Oconst;
+ n.ty = tint;
+ n.c = ref Const(big d.tag, 0.);
+ }
+ break;
+ }
+ case n.ty.kind{
+ Tbig =>
+ n.op = Oconst;
+ n.c = ref Const(d.init.c.val, 0.);
+ Tbyte =>
+ n.op = Oconst;
+ n.c = ref Const(big byte d.init.c.val, 0.);
+ Tint or
+ Tfix =>
+ n.op = Oconst;
+ n.c = ref Const(big int d.init.c.val, 0.);
+ Treal =>
+ n.op = Oconst;
+ n.c = ref Const(big 0, d.init.c.rval);
+ Tstring =>
+ n.op = Oconst;
+ n.decl = d.init.decl;
+ Ttuple =>
+ *n = *d.init;
+ Tadt =>
+ *n = *d.init;
+ n = rewrite(n); # was call
+ Texception =>
+ if(n.ty.cons == byte 0)
+ fatal("non-const exception type in efold");
+ n.op = Oconst;
+ * =>
+ fatal("unknown const type "+typeconv(n.ty)+" in efold");
+ }
+ Oadd =>
+ left = efold(left);
+ right = efold(right);
+ n.left = left;
+ n.right = right;
+ if(n.ty == tstring && right.op == Oconst){
+ if(left.op == Oconst)
+ n = mksconst(n.src, stringcat(left.decl.sym, right.decl.sym));
+ else if(left.op == Oadd && left.ty == tstring && left.right.op == Oconst){
+ left.right = mksconst(n.src, stringcat(left.right.decl.sym, right.decl.sym));
+ n = left;
+ }
+ }
+ Olen =>
+ left = efold(left);
+ n.left = left;
+ if(left.ty == tstring && left.op == Oconst)
+ n = mkconst(n.src, big len left.decl.sym.name);
+ Oslice =>
+ if(right.left.op == Onothing)
+ right.left = mkconst(right.left.src, big 0);
+ n.left = efold(left);
+ n.right = efold(right);
+ Oinds =>
+ n.left = left = efold(left);
+ n.right = right = efold(right);
+ if(right.op == Oconst && left.op == Oconst){
+ ;
+ }
+ Ocast =>
+ n.op = Ocast;
+ left = efold(left);
+ n.left = left;
+ if(n.ty == left.ty || n.ty.kind == Tfix && tequal(n.ty, left.ty))
+ return left;
+ if(left.op == Oconst)
+ return foldcast(n, left);
+ Odot or
+ Omdot =>
+ #
+ # what about side effects from left?
+ #
+ d = right.decl;
+ case d.store{
+ Dconst or
+ Dtag or
+ Dtype =>
+ #
+ # set it up as a name and let that case do the hard work
+ #
+ n.op = Oname;
+ n.decl = d;
+ n.left = nil;
+ n.right = nil;
+ return efold(n);
+ }
+ n.left = efold(left);
+ if(n.left.op == Otuple)
+ n = tuplemem(n.left, d);
+ else
+ n.right = efold(right);
+ Otagof =>
+ if(n.decl != nil){
+ n.op = Oconst;
+ n.left = nil;
+ n.right = nil;
+ n.c = ref Const(big n.decl.tag, 0.);
+ return efold(n);
+ }
+ n.left = efold(left);
+ Oif =>
+ n.left = left = efold(left);
+ n.right = right = efold(right);
+ if(left.op == Oconst){
+ if(left.c.val != big 0)
+ return right.left;
+ else
+ return right.right;
+ }
+ * =>
+ n.left = efold(left);
+ n.right = efold(right);
+ }
+
+ left = n.left;
+ right = n.right;
+ if(left == nil)
+ return n;
+
+ if(right == nil){
+ if(left.op == Oconst){
+ if(left.ty == tint || left.ty == tbyte || left.ty == tbig)
+ return foldc(n);
+ if(left.ty == treal)
+ return foldr(n);
+ }
+ return n;
+ }
+
+ if(left.op == Oconst){
+ case n.op{
+ Olsh or
+ Orsh =>
+ if(left.c.val == big 0 && !hasside(right, 1))
+ return left;
+ Ooror =>
+ if(left.ty == tint || left.ty == tbyte || left.ty == tbig){
+ if(left.c.val == big 0){
+ n = mkbin(Oneq, right, mkconst(right.src, big 0));
+ n.ty = right.ty;
+ n.left.ty = right.ty;
+ return efold(n);
+ }
+ left.c.val = big 1;
+ return left;
+ }
+ Oandand =>
+ if(left.ty == tint || left.ty == tbyte || left.ty == tbig){
+ if(left.c.val == big 0)
+ return left;
+ n = mkbin(Oneq, right, mkconst(right.src, big 0));
+ n.ty = right.ty;
+ n.left.ty = right.ty;
+ return efold(n);
+ }
+ }
+ }
+ if(left.op == Oconst && right.op != Oconst
+ && opcommute[n.op]
+ && n.ty != tstring){
+ n.op = opcommute[n.op];
+ n.left = right;
+ n.right = left;
+ left = right;
+ right = n.right;
+ }
+ if(right.op == Oconst && left.op == n.op && left.right.op == Oconst
+ && (n.op == Oadd || n.op == Omul || n.op == Oor || n.op == Oxor || n.op == Oand)
+ && n.ty != tstring){
+ n.left = left.left;
+ left.left = right;
+ right = efold(left);
+ n.right = right;
+ left = n.left;
+ }
+ if(right.op == Oconst){
+ if(n.op == Oexp && left.ty == treal){
+ if(left.op == Oconst)
+ return foldr(n);
+ return n;
+ }
+ if(right.ty == tint || right.ty == tbyte || left.ty == tbig){
+ if(left.op == Oconst)
+ return foldc(n);
+ return foldvc(n);
+ }
+ if(right.ty == treal && left.op == Oconst)
+ return foldr(n);
+ }
+ if(sametree(left, right))
+ return folds(n);
+ return n;
+}
+
+#
+# does evaluating the node have any side effects?
+#
+hasside(n: ref Node, strict: int): int
+{
+ for(; n != nil; n = n.right){
+ if(sideeffect[n.op] && (strict || n.op != Oadr && n.op != Oind))
+ return 1;
+ if(hasside(n.left, strict))
+ return 1;
+ }
+ return 0;
+}
+
+hascall(n: ref Node): int
+{
+ for(; n != nil; n = n.right){
+ if(n.op == Ocall || n.op == Ospawn)
+ return 1;
+ if(hascall(n.left))
+ return 1;
+ }
+ return 0;
+}
+
+hasasgns(n: ref Node): int
+{
+ if(n == nil)
+ return 0;
+ if(n.op != Ocall && isused[n.op] && n.op != Onothing)
+ return 1;
+ return hasasgns(n.left) || hasasgns(n.right);
+}
+
+nodes(n: ref Node): int
+{
+ if(n == nil)
+ return 0;
+ return 1+nodes(n.left)+nodes(n.right);
+}
+
+foldcast(n, left: ref Node): ref Node
+{
+ case left.ty.kind{
+ Tint =>
+ left.c.val = big int left.c.val;
+ return foldcasti(n, left);
+ Tbyte =>
+ left.c.val = big byte left.c.val;
+ return foldcasti(n, left);
+ Tbig =>
+ return foldcasti(n, left);
+ Treal =>
+ case n.ty.kind{
+ Tint or
+ Tbyte or
+ Tbig =>
+ left.c.val = big left.c.rval;
+ Tfix =>
+ left.c.val = real2fix(left.c.rval, n.ty);
+ Tstring =>
+ return mksconst(n.src, enterstring(string left.c.rval));
+ * =>
+ return n;
+ }
+ Tfix =>
+ case n.ty.kind{
+ Tint or
+ Tbyte or
+ Tbig =>
+ left.c.val = big fix2real(left.c.val, left.ty);
+ Treal =>
+ left.c.rval = fix2real(left.c.val, left.ty);
+ Tfix =>
+ if(tequal(left.ty, n.ty))
+ return left;
+ left.c.val = fix2fix(left.c.val, left.ty, n.ty);
+ Tstring =>
+ return mksconst(n.src, enterstring(string fix2real(left.c.val, left.ty)));
+ * =>
+ return n;
+ }
+ break;
+ Tstring =>
+ case n.ty.kind{
+ Tint or
+ Tbyte or
+ Tbig =>
+ left.c = ref Const(big left.decl.sym.name, 0.);
+ Treal =>
+ left.c = ref Const(big 0, real left.decl.sym.name);
+ Tfix =>
+ left.c = ref Const(real2fix(real left.decl.sym.name, n.ty), 0.);
+ * =>
+ return n;
+ }
+ * =>
+ return n;
+ }
+ left.ty = n.ty;
+ left.src = n.src;
+ return left;
+}
+
+#
+# left is some kind of int type
+#
+foldcasti(n, left: ref Node): ref Node
+{
+ case n.ty.kind{
+ Tint =>
+ left.c.val = big int left.c.val;
+ Tbyte =>
+ left.c.val = big byte left.c.val;
+ Tbig =>
+ ;
+ Treal =>
+ left.c.rval = real left.c.val;
+ Tfix =>
+ left.c.val = real2fix(real left.c.val, n.ty);
+ Tstring =>
+ return mksconst(n.src, enterstring(string left.c.val));
+ * =>
+ return n;
+ }
+ left.ty = n.ty;
+ left.src = n.src;
+ return left;
+}
+
+#
+# right is a const int
+#
+foldvc(n: ref Node): ref Node
+{
+ left := n.left;
+ right := n.right;
+ case n.op{
+ Oadd or
+ Osub or
+ Oor or
+ Oxor or
+ Olsh or
+ Orsh or
+ Ooror =>
+ if(right.c.val == big 0)
+ return left;
+ if(n.op == Ooror && !hasside(left, 1))
+ return right;
+ Oand =>
+ if(right.c.val == big 0 && !hasside(left, 1))
+ return right;
+ Omul =>
+ if(right.c.val == big 1)
+ return left;
+ if(right.c.val == big 0 && !hasside(left, 1))
+ return right;
+ Odiv =>
+ if(right.c.val == big 1)
+ return left;
+ Omod =>
+ if(right.c.val == big 1 && !hasside(left, 1)){
+ right.c.val = big 0;
+ return right;
+ }
+ Oexp =>
+ if(right.c.val == big 0){
+ right.c.val = big 1;
+ return right;
+ }
+ if(right.c.val == big 1)
+ return left;
+ Oandand =>
+ if(right.c.val != big 0)
+ return left;
+ if(!hasside(left, 1))
+ return right;
+ Oneq =>
+ if(!isrelop[left.op])
+ return n;
+ if(right.c.val == big 0)
+ return left;
+ n.op = Onot;
+ n.right = nil;
+ Oeq =>
+ if(!isrelop[left.op])
+ return n;
+ if(right.c.val != big 0)
+ return left;
+ n.op = Onot;
+ n.right = nil;
+ }
+ return n;
+}
+
+#
+# left and right are const ints
+#
+foldc(n: ref Node): ref Node
+{
+ v: big;
+ rv, nb: int;
+
+ left := n.left;
+ right := n.right;
+ case n.op{
+ Oadd =>
+ v = left.c.val + right.c.val;
+ Osub =>
+ v = left.c.val - right.c.val;
+ Omul =>
+ v = left.c.val * right.c.val;
+ Odiv =>
+ if(right.c.val == big 0){
+ nerror(n, "divide by 0 in constant expression");
+ return n;
+ }
+ v = left.c.val / right.c.val;
+ Omod =>
+ if(right.c.val == big 0){
+ nerror(n, "mod by 0 in constant expression");
+ return n;
+ }
+ v = left.c.val % right.c.val;
+ Oexp =>
+ if(left.c.val == big 0 && right.c.val < big 0){
+ nerror(n, "0 to negative power in constant expression");
+ return n;
+ }
+ v = ipow(left.c.val, int right.c.val);
+ Oand =>
+ v = left.c.val & right.c.val;
+ Oor =>
+ v = left.c.val | right.c.val;
+ Oxor =>
+ v = left.c.val ^ right.c.val;
+ Olsh =>
+ v = left.c.val;
+ rv = int right.c.val;
+ if(rv < 0 || rv >= n.ty.size * 8){
+ nwarn(n, "shift amount "+string rv+" out of range");
+ rv = 0;
+ }
+ if(rv == 0)
+ break;
+ v <<= rv;
+ Orsh =>
+ v = left.c.val;
+ rv = int right.c.val;
+ nb = n.ty.size * 8;
+ if(rv < 0 || rv >= nb){
+ nwarn(n, "shift amount "+string rv+" out of range");
+ rv = 0;
+ }
+ if(rv == 0)
+ break;
+ v >>= rv;
+ Oneg =>
+ v = -left.c.val;
+ Ocomp =>
+ v = ~left.c.val;
+ Oeq =>
+ v = big(left.c.val == right.c.val);
+ Oneq =>
+ v = big(left.c.val != right.c.val);
+ Ogt =>
+ v = big(left.c.val > right.c.val);
+ Ogeq =>
+ v = big(left.c.val >= right.c.val);
+ Olt =>
+ v = big(left.c.val < right.c.val);
+ Oleq =>
+ v = big(left.c.val <= right.c.val);
+ Oandand =>
+ v = big(int left.c.val && int right.c.val);
+ Ooror =>
+ v = big(int left.c.val || int right.c.val);
+ Onot =>
+ v = big(left.c.val == big 0);
+ * =>
+ return n;
+ }
+ if(n.ty == tint)
+ v = big int v;
+ else if(n.ty == tbyte)
+ v = big byte v;
+ n.left = nil;
+ n.right = nil;
+ n.decl = nil;
+ n.op = Oconst;
+ n.c = ref Const(v, 0.);
+ return n;
+}
+
+#
+# left and right are const reals
+#
+foldr(n: ref Node): ref Node
+{
+ rv := 0.;
+ v := big 0;
+
+ left := n.left;
+ right := n.right;
+ case n.op{
+ Ocast =>
+ return n;
+ Oadd =>
+ rv = left.c.rval + right.c.rval;
+ Osub =>
+ rv = left.c.rval - right.c.rval;
+ Omul =>
+ rv = left.c.rval * right.c.rval;
+ Odiv =>
+ rv = left.c.rval / right.c.rval;
+ Oexp =>
+ rv = rpow(left.c.rval, int right.c.val);
+ Oneg =>
+ rv = -left.c.rval;
+ Oinv =>
+ if(left.c.rval == 0.0){
+ error(n.src.start, "divide by 0 in fixed point type");
+ return n;
+ }
+ rv = 1.0/left.c.rval;
+ Oeq =>
+ v = big(left.c.rval == right.c.rval);
+ Oneq =>
+ v = big(left.c.rval != right.c.rval);
+ Ogt =>
+ v = big(left.c.rval > right.c.rval);
+ Ogeq =>
+ v = big(left.c.rval >= right.c.rval);
+ Olt =>
+ v = big(left.c.rval < right.c.rval);
+ Oleq =>
+ v = big(left.c.rval <= right.c.rval);
+ * =>
+ return n;
+ }
+ n.left = nil;
+ n.right = nil;
+ n.op = Oconst;
+
+ if(isnan(rv))
+ rv = canonnan;
+
+ n.c = ref Const(v, rv);
+ return n;
+}
+
+varinit(d: ref Decl, e: ref Node): ref Node
+{
+ n := mkdeclname(e.src, d);
+ if(d.next == nil)
+ return mkbin(Oas, n, e);
+ return mkbin(Oas, n, varinit(d.next, e));
+}
+
+#
+# given: an Oseq list with left == next or the last child
+# make a list with the right == next
+# ie: Oseq(Oseq(a, b),c) ==> Oseq(a, Oseq(b, Oseq(c, nil))))
+#
+rotater(e: ref Node): ref Node
+{
+ if(e == nil)
+ return e;
+ if(e.op != Oseq)
+ return mkunary(Oseq, e);
+ e.right = mkunary(Oseq, e.right);
+ while(e.left.op == Oseq){
+ left := e.left;
+ e.left = left.right;
+ left.right = e;
+ e = left;
+ }
+ return e;
+}
+
+#
+# reverse the case labels list
+#
+caselist(s, nr: ref Node): ref Node
+{
+ r := s.right;
+ s.right = nr;
+ if(r == nil)
+ return s;
+ return caselist(r, s);
+}
+
+#
+# e is a seq of expressions; make into cons's to build a list
+#
+etolist(e: ref Node): ref Node
+{
+ if(e == nil)
+ return nil;
+ n := mknil(e.src);
+ n.src.start = n.src.stop;
+ if(e.op != Oseq)
+ return mkbin(Ocons, e, n);
+ e.right = mkbin(Ocons, e.right, n);
+ while(e.left.op == Oseq){
+ e.op = Ocons;
+ left := e.left;
+ e.left = left.right;
+ left.right = e;
+ e = left;
+ }
+ e.op = Ocons;
+ return e;
+}
+
+dupn(resrc: int, src: Src, n: ref Node): ref Node
+{
+ nn := ref *n;
+ if(resrc)
+ nn.src = src;
+ if(nn.left != nil)
+ nn.left = dupn(resrc, src, nn.left);
+ if(nn.right != nil)
+ nn.right = dupn(resrc, src, nn.right);
+ return nn;
+}
+
+mkn(op: int, left, right: ref Node): ref Node
+{
+ n := ref Node;
+ n.op = op;
+ n.flags = byte 0;
+ n.left = left;
+ n.right = right;
+ return n;
+}
+
+mkunary(op: int, left: ref Node): ref Node
+{
+ n := ref Node;
+ n.src = left.src;
+ n.op = op;
+ n.flags = byte 0;
+ n.left = left;
+ return n;
+}
+
+mkbin(op: int, left, right: ref Node): ref Node
+{
+ n := ref Node;
+ n.src.start = left.src.start;
+ n.src.stop = right.src.stop;
+ n.op = op;
+ n.flags = byte 0;
+ n.left = left;
+ n.right = right;
+ return n;
+}
+
+mkdeclname(src: Src, d: ref Decl): ref Node
+{
+ n := ref Node;
+ n.src = src;
+ n.op = Oname;
+ n.flags = byte 0;
+ n.decl = d;
+ n.ty = d.ty;
+ d.refs++;
+ return n;
+}
+
+mknil(src: Src): ref Node
+{
+ return mkdeclname(src, nildecl);
+}
+
+mkname(src: Src, s: ref Sym): ref Node
+{
+ n := ref Node;
+ n.src = src;
+ n.op = Oname;
+ n.flags = byte 0;
+ if(s.unbound == nil){
+ s.unbound = mkdecl(src, Dunbound, nil);
+ s.unbound.sym = s;
+ }
+ n.decl = s.unbound;
+ return n;
+}
+
+mkconst(src: Src, v: big): ref Node
+{
+ n := ref Node;
+ n.src = src;
+ n.op = Oconst;
+ n.flags = byte 0;
+ n.ty = tint;
+ n.c = ref Const(v, 0.);
+ return n;
+}
+
+mkrconst(src: Src, v: real): ref Node
+{
+ n := ref Node;
+ n.src = src;
+ n.op = Oconst;
+ n.flags = byte 0;
+ n.ty = treal;
+ n.c = ref Const(big 0, v);
+ return n;
+}
+
+mksconst(src: Src, s: ref Sym): ref Node
+{
+ n := ref Node;
+ n.src = src;
+ n.op = Oconst;
+ n.flags = byte 0;
+ n.ty = tstring;
+ n.decl = mkdecl(src, Dconst, tstring);
+ n.decl.sym = s;
+ return n;
+}
+
+opconv(op: int): string
+{
+ if(op < 0 || op > Oend)
+ return "op "+string op;
+ return opname[op];
+}
+
+etconv(n: ref Node): string
+{
+ s := expconv(n);
+ if(n.ty == tany || n.ty == tnone || n.ty == terror)
+ return s;
+ s += " of type ";
+ s += typeconv(n.ty);
+ return s;
+}
+
+expconv(n: ref Node): string
+{
+ return "'" + subexpconv(n) + "'";
+}
+
+subexpconv(n: ref Node): string
+{
+ if(n == nil)
+ return "";
+ s := "";
+ if(int n.flags & PARENS)
+ s[len s] = '(';
+ case n.op{
+ Obreak or
+ Ocont =>
+ s += opname[n.op];
+ if(n.decl != nil)
+ s += " "+n.decl.sym.name;
+ Oexit or
+ Owild =>
+ s += opname[n.op];
+ Onothing =>
+ ;
+ Oadr or
+ Oused =>
+ s += subexpconv(n.left);
+ Oseq =>
+ s += eprintlist(n, ", ");
+ Oname =>
+ if(n.decl == nil)
+ s += "<nil>";
+ else
+ s += n.decl.sym.name;
+ Oconst =>
+ if(n.ty.kind == Tstring){
+ s += stringpr(n.decl.sym);
+ break;
+ }
+ if(n.decl != nil && n.decl.sym != nil){
+ s += n.decl.sym.name;
+ break;
+ }
+ case n.ty.kind{
+ Tbig or
+ Tint or
+ Tbyte =>
+ s += string n.c.val;
+ Treal =>
+ s += string n.c.rval;
+ Tfix =>
+ s += string n.c.val + "(" + string n.ty.val.c.rval + ")";
+ * =>
+ s += opname[n.op];
+ }
+ Ocast =>
+ s += typeconv(n.ty);
+ s[len s] = ' ';
+ s += subexpconv(n.left);
+ Otuple =>
+ if(n.ty != nil && n.ty.kind == Tadt)
+ s += n.ty.decl.sym.name;
+ s[len s] = '(';
+ s += eprintlist(n.left, ", ");
+ s[len s] = ')';
+ Ochan =>
+ if(n.left != nil){
+ s += "chan [";
+ s += subexpconv(n.left);
+ s += "] of ";
+ s += typeconv(n.ty.tof);
+ }
+ else
+ s += "chan of "+typeconv(n.ty.tof);
+ Oarray =>
+ s += "array [";
+ if(n.left != nil)
+ s += subexpconv(n.left);
+ s += "] of ";
+ if(n.right != nil){
+ s += "{";
+ s += eprintlist(n.right, ", ");
+ s += "}";
+ }else{
+ s += typeconv(n.ty.tof);
+ }
+ Oelem or
+ Olabel =>
+ if(n.left != nil){
+ s += eprintlist(n.left, " or ");
+ s += " =>";
+ }
+ s += subexpconv(n.right);
+ Orange =>
+ s += subexpconv(n.left);
+ s += " to ";
+ s += subexpconv(n.right);
+ Ospawn =>
+ s += "spawn ";
+ s += subexpconv(n.left);
+ Oraise =>
+ s += "raise ";
+ s += subexpconv(n.left);
+ Ocall =>
+ s += subexpconv(n.left);
+ s += "(";
+ s += eprintlist(n.right, ", ");
+ s += ")";
+ Oinc or
+ Odec =>
+ s += subexpconv(n.left);
+ s += opname[n.op];
+ Oindex or
+ Oindx or
+ Oinds =>
+ s += subexpconv(n.left);
+ s += "[";
+ s += subexpconv(n.right);
+ s += "]";
+ Oslice =>
+ s += subexpconv(n.left);
+ s += "[";
+ s += subexpconv(n.right.left);
+ s += ":";
+ s += subexpconv(n.right.right);
+ s += "]";
+ Oload =>
+ s += "load ";
+ s += typeconv(n.ty);
+ s += " ";
+ s += subexpconv(n.left);
+ Oref or
+ Olen or
+ Ohd or
+ Otl or
+ Otagof =>
+ s += opname[n.op];
+ s[len s] = ' ';
+ s += subexpconv(n.left);
+ * =>
+ if(n.right == nil){
+ s += opname[n.op];
+ s += subexpconv(n.left);
+ }else{
+ s += subexpconv(n.left);
+ s += opname[n.op];
+ s += subexpconv(n.right);
+ }
+ }
+ if(int n.flags & PARENS)
+ s[len s] = ')';
+ return s;
+}
+
+eprintlist(elist: ref Node, sep: string): string
+{
+ if(elist == nil)
+ return "";
+ s := "";
+ for(; elist.right != nil; elist = elist.right){
+ if(elist.op == Onothing)
+ continue;
+ if(elist.left.op == Ofnptr)
+ return s;
+ s += subexpconv(elist.left);
+ if(elist.right.left.op != Ofnptr)
+ s += sep;
+ }
+ s += subexpconv(elist.left);
+ return s;
+}
+
+nodeconv(n: ref Node): string
+{
+ return nprint(n, 0);
+}
+
+nprint(n: ref Node, indent: int): string
+{
+ if(n == nil)
+ return "";
+ s := "\n";
+ for(i := 0; i < indent; i++)
+ s[len s] = ' ';
+ case n.op{
+ Oname =>
+ if(n.decl == nil)
+ s += "<nil>";
+ else
+ s += n.decl.sym.name;
+ Oconst =>
+ if(n.decl != nil && n.decl.sym != nil)
+ s += n.decl.sym.name;
+ else
+ s += opconv(n.op);
+ if(n.ty == tint || n.ty == tbyte || n.ty == tbig)
+ s += " (" + string n.c.val + ")";
+ * =>
+ s += opconv(n.op);
+ }
+ s += " " + typeconv(n.ty) + " " + string n.addable + " " + string n.temps;
+ indent += 2;
+ s += nprint(n.left, indent);
+ s += nprint(n.right, indent);
+ return s;
+}
diff --git a/appl/cmd/limbo/opname.m b/appl/cmd/limbo/opname.m
new file mode 100644
index 00000000..50da6ec9
--- /dev/null
+++ b/appl/cmd/limbo/opname.m
@@ -0,0 +1,109 @@
+opname := array[Oend+1] of
+{
+ "unknown",
+
+ Oadd => "+",
+ Oaddas => "+=",
+ Oadr => "adr",
+ Oadtdecl => "adtdecl",
+ Oalt => "alt",
+ Oand => "&",
+ Oandand => "&&",
+ Oandas => "&=",
+ Oarray => "array",
+ Oas => "=",
+ Obreak => "break",
+ Ocall => "call",
+ Ocase => "case",
+ Ocast => "cast",
+ Ochan => "chan",
+ Ocomma => ",",
+ Ocomp => "~",
+ Ocondecl => "condecl",
+ Ocons => "::",
+ Oconst => "const",
+ Ocont => "continue",
+ Odas => ":=",
+ Odec => "--",
+ Odiv => "/",
+ Odivas => "/=",
+ Odo => "do",
+ Odot => ".",
+ Oelem => "elem",
+ Oeq => "==",
+ Oexcept => "except",
+ Oexdecl => "exdecl",
+ Oexit => "exit",
+ Oexp => "**",
+ Oexpas => "**=",
+ Oexstmt => "exstat",
+ Ofielddecl => "fielddecl",
+ Ofnptr => "fnptr",
+ Ofor => "for",
+ Ofunc => "fn(){}",
+ Ogeq => ">=",
+ Ogt => ">",
+ Ohd => "hd",
+ Oif => "if",
+ Oimport => "import",
+ Oinc => "++",
+ Oind => "*",
+ Oindex => "index",
+ Oinds => "inds",
+ Oindx => "indx",
+ Oinv => "inv",
+ Ojmp => "jmp",
+ Olabel => "label",
+ Olen => "len",
+ Oleq => "<=",
+ Oload => "load",
+ Olsh => "<<",
+ Olshas => "<<=",
+ Olt => "<",
+ Omdot => "->",
+ Omod => "%",
+ Omodas => "%=",
+ Omoddecl => "moddecl",
+ Omul => "*",
+ Omulas => "*=",
+ Oname => "name",
+ Oneg => "-",
+ Oneq => "!=",
+ Onot => "!",
+ Onothing => "nothing",
+ Oor => "|",
+ Ooras => "|=",
+ Ooror => "||",
+ Opick => "pick",
+ Opickdecl => "pickdec",
+ Opredec => "--",
+ Opreinc => "++",
+ Oraise => "raise",
+ Orange => "range",
+ Orcv => "<-",
+ Oref => "ref",
+ Oret => "return",
+ Orsh => ">>",
+ Orshas => ">>=",
+ Oscope => "scope",
+ Oself => "self",
+ Oseq => "seq",
+ Oslice => "slice",
+ Osnd => "<-=",
+ Ospawn => "spawn",
+ Osub => "-",
+ Osubas => "-=",
+ Otagof => "tagof",
+ Otl => "tl",
+ Otuple => "tuple",
+ Otype => "type",
+ Otypedecl => "typedecl",
+ Oused => "used",
+ Ovardecl => "vardecl",
+ Ovardecli => "vardecli",
+ Owild => "*",
+ Oxor => "^",
+ Oxoras => "^=",
+
+ Oend => "unknown"
+};
diff --git a/appl/cmd/limbo/optim.b b/appl/cmd/limbo/optim.b
new file mode 100644
index 00000000..ac437fab
--- /dev/null
+++ b/appl/cmd/limbo/optim.b
@@ -0,0 +1,3 @@
+optim(nil: ref Inst, nil: ref Decl)
+{
+}
diff --git a/appl/cmd/limbo/sbl.b b/appl/cmd/limbo/sbl.b
new file mode 100644
index 00000000..0ae69d5f
--- /dev/null
+++ b/appl/cmd/limbo/sbl.b
@@ -0,0 +1,397 @@
+
+sbltname := array[Tend] of
+{
+ Tnone => byte 'n',
+ Tadt => byte 'a',
+ Tadtpick => byte 'a',
+ Tarray => byte 'A',
+ Tbig => byte 'B',
+ Tbyte => byte 'b',
+ Tchan => byte 'C',
+ Treal => byte 'f',
+ Tfn => byte 'F',
+ Tint => byte 'i',
+ Tlist => byte 'L',
+ Tmodule => byte 'm',
+ Tref => byte 'R',
+ Tstring => byte 's',
+ Ttuple => byte 't',
+ Texception => byte 't',
+ Tfix => byte 'i',
+ Tpoly => byte 'P',
+
+ Tainit => byte '?',
+ Talt => byte '?',
+ Tany => byte 'N',
+ Tarrow => byte '?',
+ Tcase => byte '?',
+ Tcasel => byte '?',
+ Tcasec => byte '?',
+ Tdot => byte '?',
+ Terror => byte '?',
+ Tgoto => byte '?',
+ Tid => byte '?',
+ Tiface => byte '?',
+ Texcept => byte '?',
+ Tinst => byte '?',
+};
+sbltadtpick: con byte 'p';
+
+sfiles: ref Sym;
+ftail: ref Sym;
+nsfiles: int;
+blockid: int;
+lastf: int;
+lastline: int;
+
+MAXSBLINT: con 12;
+MAXSBLSRC: con 6*(MAXSBLINT+1);
+
+sblmod(m: ref Decl)
+{
+ bsym.puts("limbo .sbl 2.1\n");
+ bsym.puts(m.sym.name);
+ bsym.putb(byte '\n');
+
+ blockid = 0;
+ nsfiles = 0;
+ sfiles = ftail = nil;
+ lastf = 0;
+ lastline = 0;
+}
+
+sblfile(name: string): int
+{
+ i := 0;
+ for(s := sfiles; s != nil; s = s.next){
+ if(s.name == name)
+ return i;
+ i++;
+ }
+ s = ref Sym;
+ s.name = name;
+ s.next = nil;
+ if(sfiles == nil)
+ sfiles = s;
+ else
+ ftail.next = s;
+ ftail = s;
+ nsfiles = i + 1;
+ return i;
+}
+
+filename(s: string): string
+{
+ (nil, file) := str->splitr(s, "/ \\");
+ return file;
+}
+
+sblfiles()
+{
+ for(i := 0; i < nfiles; i++)
+ files[i].sbl = sblfile(files[i].name);
+ bsym.puts(string nsfiles);
+ bsym.putb(byte '\n');
+ for(s := sfiles; s != nil; s = s.next){
+ bsym.puts(filename(s.name));
+ bsym.putb(byte '\n');
+ }
+}
+
+sblint(buf: array of byte, off, v: int): int
+{
+ if(v == 0){
+ buf[off++] = byte '0';
+ return off;
+ }
+ stop := off + MAXSBLINT;
+ if(v < 0){
+ buf[off++] = byte '-';
+ v = -v;
+ }
+ n := stop;
+ while(v > 0){
+ buf[n -= 1] = byte(v % 10 + '0');
+ v = v / 10;
+ }
+ while(n < stop)
+ buf[off++] = buf[n++];
+ return off;
+}
+
+sblsrcconvb(buf: array of byte, off: int, src: Src): int
+{
+ (startf, startl) := fline(src.start >> PosBits);
+ (stopf, stopl) := fline(src.stop >> PosBits);
+ if(lastf != startf.sbl){
+ off = sblint(buf, off, startf.sbl);
+ buf[off++] = byte ':';
+ }
+ if(lastline != startl){
+ off = sblint(buf, off, startl);
+ buf[off++] = byte '.';
+ }
+ off = sblint(buf, off, (src.start & PosMask));
+ buf[off++] = byte ',';
+ if(startf.sbl != stopf.sbl){
+ off = sblint(buf, off, stopf.sbl);
+ buf[off++] = byte ':';
+ }
+ if(startl != stopl){
+ off = sblint(buf, off, stopl);
+ buf[off++] = byte '.';
+ }
+ off = sblint(buf, off, (src.stop & PosMask));
+ buf[off++] = byte ' ';
+ lastf = stopf.sbl;
+ lastline = stopl;
+ return off;
+}
+
+sblsrcconv(src: Src): string
+{
+ s := "";
+ (startf, startl) := fline(src.start >> PosBits);
+ (stopf, stopl) := fline(src.stop >> PosBits);
+ if(lastf != startf.sbl){
+ s += string startf.sbl;
+ s[len s] = ':';
+ }
+ if(lastline != startl){
+ s += string startl;
+ s[len s] = '.';
+ }
+ s += string (src.start & PosMask);
+ s[len s] = ',';
+ if(startf.sbl != stopf.sbl){
+ s += string stopf.sbl;
+ s[len s] = ':';
+ }
+ if(startl != stopl){
+ s += string stopl;
+ s[len s] = '.';
+ }
+ s += string (src.stop & PosMask);
+ s[len s] = ' ';
+ lastf = stopf.sbl;
+ lastline = stopl;
+ return s;
+}
+
+isnilsrc(s: Src): int
+{
+ return s.start == 0 && s.stop == 0;
+}
+
+isnilstopsrc(s: Src): int
+{
+ return s.stop == 0;
+}
+
+sblinst(in: ref Inst, ninst: int)
+{
+ src: Src;
+
+ MAXSBL: con 8*1024;
+ buf := array[MAXSBL] of byte;
+ n := 0;
+ bsym.puts(string ninst);
+ bsym.putb(byte '\n');
+ sblblocks := array[nblocks] of {* => -1};
+ for(; in != nil; in = in.next){
+ if(in.op == INOOP)
+ continue;
+ if(in.src.start < 0)
+ fatal("no file specified for "+instconv(in));
+ if(n >= (MAXSBL - MAXSBLSRC - MAXSBLINT - 1)){
+ bsym.write(buf, n);
+ n = 0;
+ }
+ if(isnilsrc(in.src))
+ in.src = src;
+ else if(isnilstopsrc(in.src)){ # how does this happen ?
+ in.src.stop = in.src.start;
+ in.src.stop++;
+ }
+ n = sblsrcconvb(buf, n, in.src);
+ src = in.src;
+ b := sblblocks[in.block];
+ if(b < 0)
+ sblblocks[in.block] = b = blockid++;
+ n = sblint(buf, n, b);
+ buf[n++] = byte '\n';
+ }
+ if(n > 0)
+ bsym.write(buf, n);
+}
+
+sblty(tys: array of ref Decl, ntys: int)
+{
+ bsym.puts(string ntys);
+ bsym.putb(byte '\n');
+ for(i := 0; i < ntys; i++){
+ d := tys[i];
+ d.ty.sbl = i;
+ }
+ for(i = 0; i < ntys; i++){
+ d := tys[i];
+ sbltype(d.ty, 1);
+ }
+}
+
+sblfn(fns: array of ref Decl, nfns: int)
+{
+ bsym.puts(string nfns);
+ bsym.putb(byte '\n');
+ for(i := 0; i < nfns; i++){
+ f := fns[i];
+ if(ispoly(f))
+ rmfnptrs(f);
+ bsym.puts(string f.pc.pc);
+ bsym.putb(byte ':');
+ if(f.dot != nil && f.dot.ty.kind == Tadt){
+ bsym.puts(f.dot.sym.name);
+ bsym.putb(byte '.');
+ }
+ bsym.puts(f.sym.name);
+ bsym.putb(byte '\n');
+ sbldecl(f.ty.ids, Darg);
+ sbldecl(f.locals, Dlocal);
+ sbltype(f.ty.tof, 0);
+ }
+}
+
+sblvar(vars: ref Decl)
+{
+ sbldecl(vars, Dglobal);
+}
+
+isvis(id: ref Decl): int
+{
+ if(!tattr[id.ty.kind].vis
+ || id.sym == nil
+ || id.sym.name == ""
+ || id.sym.name[0] == '.')
+ return 0;
+ if(id.ty == tstring && id.init != nil && id.init.op == Oconst)
+ return 0;
+ if(id.src.start < 0 || id.src.stop < 0)
+ return 0;
+ return 1;
+}
+
+sbldecl(ids: ref Decl, store: int)
+{
+ n := 0;
+ for(id := ids; id != nil; id = id.next){
+ if(id.store != store || !isvis(id))
+ continue;
+ n++;
+ }
+ bsym.puts(string n);
+ bsym.putb(byte '\n');
+ for(id = ids; id != nil; id = id.next){
+ if(id.store != store || !isvis(id))
+ continue;
+ bsym.puts(string id.offset);
+ bsym.putb(byte ':');
+ bsym.puts(id.sym.name);
+ bsym.putb(byte ':');
+ bsym.puts(sblsrcconv(id.src));
+ sbltype(id.ty, 0);
+ bsym.putb(byte '\n');
+ }
+}
+
+sbltype(t: ref Type, force: int)
+{
+ if(t.kind == Tadtpick)
+ t = t.decl.dot.ty;
+
+ d := t.decl;
+ if(!force && d != nil && d.ty.sbl >= 0){
+ bsym.putb(byte '@');
+ bsym.puts(string d.ty.sbl);
+ bsym.putb(byte '\n');
+ return;
+ }
+
+ if(t.rec != byte 0)
+ fatal("recursive sbl type: "+typeconv(t));
+
+ t.rec = byte 1;
+ case t.kind{
+ * =>
+ fatal("bad type in sbltype: "+typeconv(t));
+ Tnone or
+ Tany or
+ Tint or
+ Tbig or
+ Tbyte or
+ Treal or
+ Tstring or
+ Tfix or
+ Tpoly =>
+ bsym.putb(sbltname[t.kind]);
+ Tfn =>
+ bsym.putb(sbltname[t.kind]);
+ sbldecl(t.ids, Darg);
+ sbltype(t.tof, 0);
+ Tarray or
+ Tlist or
+ Tchan or
+ Tref =>
+ bsym.putb(sbltname[t.kind]);
+ if(t.kind == Tref && t.tof.kind == Tfn){
+ tattr[Tany].vis = 1;
+ sbltype(tfnptr, 0);
+ tattr[Tany].vis = 0;
+ }
+ else
+ sbltype(t.tof, 0);
+ Ttuple or
+ Texception =>
+ bsym.putb(sbltname[t.kind]);
+ bsym.puts(string t.size);
+ bsym.putb(byte '.');
+ sbldecl(t.ids, Dfield);
+ Tadt =>
+ if(t.tags != nil)
+ bsym.putb(sbltadtpick);
+ else
+ bsym.putb(sbltname[t.kind]);
+ if(d.dot != nil && !isimpmod(d.dot.sym))
+ bsym.puts(d.dot.sym.name + "->");
+ bsym.puts(d.sym.name);
+ bsym.putb(byte ' ');
+ bsym.puts(sblsrcconv(d.src));
+ bsym.puts(string d.ty.size);
+ bsym.putb(byte '\n');
+ sbldecl(t.ids, Dfield);
+ if(t.tags != nil){
+ bsym.puts(string t.decl.tag);
+ bsym.putb(byte '\n');
+ lastt : ref Type = nil;
+ for(tg := t.tags; tg != nil; tg = tg.next){
+ bsym.puts(tg.sym.name);
+ bsym.putb(byte ':');
+ bsym.puts(sblsrcconv(tg.src));
+ if(lastt == tg.ty){
+ bsym.putb(byte '\n');
+ }else{
+ bsym.puts(string tg.ty.size);
+ bsym.putb(byte '\n');
+ sbldecl(tg.ty.ids, Dfield);
+ }
+ lastt = tg.ty;
+ }
+ }
+ Tmodule =>
+ bsym.putb(sbltname[t.kind]);
+ bsym.puts(d.sym.name);
+ bsym.putb(byte '\n');
+ bsym.puts(sblsrcconv(d.src));
+ sbldecl(t.ids, Dglobal);
+ }
+ t.rec = byte 0;
+}
diff --git a/appl/cmd/limbo/stubs.b b/appl/cmd/limbo/stubs.b
new file mode 100644
index 00000000..acb24b81
--- /dev/null
+++ b/appl/cmd/limbo/stubs.b
@@ -0,0 +1,575 @@
+#
+# write out some stub C code for limbo modules
+#
+emit(globals: ref Decl)
+{
+ for(m := globals; m != nil; m = m.next){
+ if(m.store != Dtype || m.ty.kind != Tmodule)
+ continue;
+ m.ty = usetype(m.ty);
+ for(d := m.ty.ids; d != nil; d = d.next){
+ d.ty = usetype(d.ty);
+ if(d.store == Dglobal || d.store == Dfn)
+ modrefable(d.ty);
+ if(d.store == Dtype && d.ty.kind == Tadt){
+ for(id := d.ty.ids; id != nil; id = id.next){
+ id.ty = usetype(id.ty);
+ modrefable(d.ty);
+ }
+ }
+ }
+ }
+ if(emitstub){
+ print("#pragma hjdicks x4\n");
+ print("#pragma pack x4\n");
+ adtstub(globals);
+ modstub(globals);
+ print("#pragma pack off\n");
+ print("#pragma hjdicks off\n");
+ }
+ if(emittab != nil)
+ modtab(globals);
+ if(emitcode != nil)
+ modcode(globals);
+ if(emitsbl != nil)
+ modsbl(globals);
+}
+
+modsbl(globals: ref Decl)
+{
+ for(d := globals; d != nil; d = d.next)
+ if(d.store == Dtype && d.ty.kind == Tmodule && d.sym.name == emitsbl)
+ break;
+
+ if(d == nil)
+ return;
+ bsym = bufio->fopen(sys->fildes(1), Bufio->OWRITE);
+
+ sblmod(d);
+ sblfiles();
+ n := 0;
+ genstart();
+ for(id := d.ty.tof.ids; id != nil; id = id.next){
+ if(id.sym.name == ".mp")
+ continue;
+ pushblock();
+ id.pc = genrawop(id.src, INOP, nil, nil, nil);
+ id.pc.pc = n++;
+ popblock();
+ }
+ firstinst = firstinst.next;
+ sblinst(firstinst, n);
+# (adts, nadts) := findadts(globals);
+ sblty(adts, nadts);
+ fs := array[n] of ref Decl;
+ n = 0;
+ for(id = d.ty.tof.ids; id != nil; id = id.next){
+ if(id.sym.name == ".mp")
+ continue;
+ fs[n] = id;
+ n++;
+ }
+ sblfn(fs, n);
+ sblvar(nil);
+}
+
+lowercase(f: string): string
+{
+ for(i := 0; i < len f; i++)
+ if(f[i] >= 'A' && f[i] <= 'Z')
+ f[i] += 'a' - 'A';
+ return f;
+}
+
+modcode(globals: ref Decl)
+{
+ buf: string;
+
+ if(emitdyn){
+ buf = lowercase(emitcode);
+ print("#include \"%s.h\"\n", buf);
+ }
+ else{
+ print("#include <lib9.h>\n");
+ print("#include <isa.h>\n");
+ print("#include <interp.h>\n");
+ print("#include \"%smod.h\"\n", emitcode);
+ }
+ print("\n");
+
+ for(d := globals; d != nil; d = d.next)
+ if(d.store == Dtype && d.ty.kind == Tmodule && d.sym.name == emitcode)
+ break;
+
+ if(d == nil)
+ return;
+
+ #
+ # stub types
+ #
+ for(id := d.ty.ids; id != nil; id = id.next){
+ if(id.store == Dtype && id.ty.kind == Tadt){
+ id.ty = usetype(id.ty);
+ print("Type*\tT_%s;\n", id.sym.name);
+ }
+ }
+
+ #
+ # type maps
+ #
+ if(emitdyn){
+ for(id = d.ty.ids; id != nil; id = id.next)
+ if(id.store == Dtype && id.ty.kind == Tadt)
+ print("uchar %s_map[] = %s_%s_map;\n",
+ id.sym.name, emitcode, id.sym.name);
+ }
+
+ #
+ # heap allocation and garbage collection for a type
+ #
+ if(emitdyn){
+ for(id = d.ty.ids; id != nil; id = id.next)
+ if(id.store == Dtype && id.ty.kind == Tadt){
+ print("\n%s_%s*\n%salloc%s(void)\n{\n\tHeap *h;\n\n\th = heap(T_%s);\n\treturn H2D(%s_%s*, h);\n}\n", emitcode, id.sym.name, emitcode, id.sym.name, id.sym.name, emitcode, id.sym.name);
+ print("\nvoid\n%sfree%s(Heap *h, int swept)\n{\n\t%s_%s *d;\n\n\td = H2D(%s_%s*, h);\n\tfreeheap(h, swept);\n}\n", emitcode, id.sym.name, emitcode, id.sym.name, emitcode, id.sym.name);
+ }
+ }
+
+ #
+ # initialization function
+ #
+ if(emitdyn)
+ print("\nvoid\n%sinit(void)\n{\n", emitcode);
+ else{
+ print("\nvoid\n%smodinit(void)\n{\n", emitcode);
+ print("\tbuiltinmod(\"$%s\", %smodtab);\n", emitcode, emitcode);
+ }
+ for(id = d.ty.ids; id != nil; id = id.next)
+ if(id.store == Dtype && id.ty.kind == Tadt){
+ if(emitdyn)
+ print("\tT_%s = dtype(%sfree%s, %s_%s_size, %s_map, sizeof(%s_map));\n",
+ id.sym.name, emitcode, id.sym.name, emitcode, id.sym.name, id.sym.name, id.sym.name);
+ else
+ print("\tT_%s = dtype(freeheap, sizeof(%s), %smap, sizeof(%smap));\n",
+ id.sym.name, id.sym.name, id.sym.name, id.sym.name);
+ }
+ print("}\n");
+
+ #
+ # end function
+ #
+ if(emitdyn){
+ print("\nvoid\n%send(void)\n{\n", emitcode);
+ for(id = d.ty.ids; id != nil; id = id.next)
+ if(id.store == Dtype && id.ty.kind == Tadt)
+ print("\tfreetype(T_%s);\n", id.sym.name);
+ print("}\n");
+ }
+
+ #
+ # stub functions
+ #
+ for(id = d.ty.tof.ids; id != nil; id = id.next){
+ print("\nvoid\n%s_%s(void *fp)\n{\n\tF_%s_%s *f = fp;\n",
+ id.dot.sym.name, id.sym.name,
+ id.dot.sym.name, id.sym.name);
+ if(id.ty.tof != tnone && tattr[id.ty.tof.kind].isptr)
+ print("\n\tdestroy(*f->ret);\n\t*f->ret = H;\n");
+ print("}\n");
+ }
+
+ if(emitdyn)
+ print("\n#include \"%smod.h\"\n", buf);
+}
+
+modtab(globals: ref Decl)
+{
+ print("typedef struct{char *name; long sig; void (*fn)(void*); int size; int np; uchar map[16];} Runtab;\n");
+ for(d := globals; d != nil; d = d.next){
+ if(d.store == Dtype && d.ty.kind == Tmodule && d.sym.name == emittab){
+ n := 0;
+ print("Runtab %smodtab[]={\n", d.sym.name);
+ for(id := d.ty.tof.ids; id != nil; id = id.next){
+ n++;
+ print("\t\"");
+ if(id.dot != d)
+ print("%s.", id.dot.sym.name);
+ print("%s\",0x%ux,%s_%s,", id.sym.name, sign(id),
+ id.dot.sym.name, id.sym.name);
+ if(id.ty.varargs != byte 0)
+ print("0,0,{0},");
+ else{
+ md := mkdesc(idoffsets(id.ty.ids, MaxTemp, MaxAlign), id.ty.ids);
+ print("%d,%d,%s,", md.size, md.nmap, mapconv(md));
+ }
+ print("\n");
+ }
+ print("\t0\n};\n");
+ print("#define %smodlen %d\n", d.sym.name, n);
+ }
+ }
+}
+
+#
+# produce activation records for all the functions in modules
+#
+modstub(globals: ref Decl)
+{
+ for(d := globals; d != nil; d = d.next){
+ if(d.store != Dtype || d.ty.kind != Tmodule)
+ continue;
+ arg := 0;
+ for(id := d.ty.tof.ids; id != nil; id = id.next){
+ s := id.dot.sym.name + "_" + id.sym.name;
+ if(emitdyn && id.dot.dot != nil)
+ s = id.dot.dot.sym.name + "_" + s;
+ print("void %s(void*);\ntypedef struct F_%s F_%s;\nstruct F_%s\n{\n",
+ s, s, s, s);
+ print(" WORD regs[NREG-1];\n");
+ if(id.ty.tof != tnone)
+ print(" %s* ret;\n", ctypeconv(id.ty.tof));
+ else
+ print(" WORD noret;\n");
+ print(" uchar temps[%d];\n", MaxTemp-NREG*IBY2WD);
+ offset := MaxTemp;
+ for(m := id.ty.ids; m != nil; m = m.next){
+ p := "";
+ if(m.sym != nil)
+ p = m.sym.name;
+ else
+ p = "arg"+string arg;
+
+ #
+ # explicit pads for structure alignment
+ #
+ t := m.ty;
+ (offset, nil) = stubalign(offset, t.align, nil);
+ if(offset != m.offset)
+ yyerror("module stub must not contain data objects");
+ # fatal("modstub bad offset");
+ print(" %s %s;\n", ctypeconv(t), p);
+ arg++;
+ offset += t.size;
+#ZZZ need to align?
+ }
+ if(id.ty.varargs != byte 0)
+ print(" WORD vargs;\n");
+ print("};\n");
+ }
+ for(id = d.ty.ids; id != nil; id = id.next)
+ if(id.store == Dconst)
+ constub(id);
+ }
+}
+
+chanstub(in: string, id: ref Decl)
+{
+ print("typedef %s %s_%s;\n", ctypeconv(id.ty.tof), in, id.sym.name);
+ desc := mktdesc(id.ty.tof);
+ print("#define %s_%s_size %d\n", in, id.sym.name, desc.size);
+ print("#define %s_%s_map %s\n", in, id.sym.name, mapconv(desc));
+}
+
+#
+# produce c structs for all adts
+#
+adtstub(globals: ref Decl)
+{
+ t, tt: ref Type;
+ m, d, id: ref Decl;
+
+ for(m = globals; m != nil; m = m.next){
+ if(m.store != Dtype || m.ty.kind != Tmodule)
+ continue;
+ for(d = m.ty.ids; d != nil; d = d.next){
+ if(d.store != Dtype)
+ continue;
+ t = usetype(d.ty);
+ d.ty = t;
+ s := dotprint(d.ty.decl, '_');
+ case d.ty.kind{
+ Tadt =>
+ print("typedef struct %s %s;\n", s, s);
+ Tint or
+ Tbyte or
+ Treal or
+ Tbig or
+ Tfix =>
+ print("typedef %s %s;\n", ctypeconv(t), s);
+ }
+ }
+ }
+ for(m = globals; m != nil; m = m.next){
+ if(m.store != Dtype || m.ty.kind != Tmodule)
+ continue;
+ for(d = m.ty.ids; d != nil; d = d.next){
+ if(d.store != Dtype)
+ continue;
+ t = d.ty;
+ if(t.kind == Tadt || t.kind == Ttuple && t.decl.sym != anontupsym){
+ if(t.tags != nil){
+ pickadtstub(t);
+ continue;
+ }
+ s := dotprint(t.decl, '_');
+ print("struct %s\n{\n", s);
+
+ offset := 0;
+ for(id = t.ids; id != nil; id = id.next){
+ if(id.store == Dfield){
+ tt = id.ty;
+ (offset, nil) = stubalign(offset, tt.align, nil);
+ if(offset != id.offset)
+ fatal("adtstub bad offset");
+ print(" %s %s;\n", ctypeconv(tt), id.sym.name);
+ offset += tt.size;
+ }
+ }
+ if(t.ids == nil){
+ print(" char dummy[1];\n");
+ offset = 1;
+ }
+ (offset, nil)= stubalign(offset, t.align, nil);
+#ZZZ
+(offset, nil) = stubalign(offset, IBY2WD, nil);
+ if(offset != t.size && t.ids != nil)
+ fatal("adtstub: bad size");
+ print("};\n");
+
+ for(id = t.ids; id != nil; id = id.next)
+ if(id.store == Dconst)
+ constub(id);
+
+ for(id = t.ids; id != nil; id = id.next)
+ if(id.ty.kind == Tchan)
+ chanstub(s, id);
+
+ desc := mktdesc(t);
+ if(offset != desc.size && t.ids != nil)
+ fatal("adtstub: bad desc size");
+ print("#define %s_size %d\n", s, offset);
+ print("#define %s_map %s\n", s, mapconv(desc));
+#ZZZ
+if(0)
+ print("struct %s_check {int s[2*(sizeof(%s)==%s_size)-1];};\n", s, s, s);
+ }else if(t.kind == Tchan)
+ chanstub(m.sym.name, d);
+ }
+ }
+}
+
+#
+# emit an expicit pad field for aligning emitted c structs
+# according to limbo's definition
+#
+stubalign(offset: int, a: int, s: string): (int, string)
+{
+ x := offset & (a-1);
+ if(x == 0)
+ return (offset, s);
+ x = a - x;
+ if(s != nil)
+ s += sprint("uchar\t_pad%d[%d]; ", offset, x);
+ else
+ print("\tuchar\t_pad%d[%d];\n", offset, x);
+ offset += x;
+ if((offset & (a-1)) || x >= a)
+ fatal("compiler stub misalign");
+ return (offset, s);
+}
+
+constub(id: ref Decl)
+{
+ s := id.dot.sym.name + "_" + id.sym.name;
+ case id.ty.kind{
+ Tbyte =>
+ print("#define %s %d\n", s, int id.init.c.val & 16rff);
+ Tint or
+ Tfix =>
+ print("#define %s %d\n", s, int id.init.c.val);
+ Tbig =>
+ print("#define %s %bd\n", s, id.init.c.val);
+ Treal =>
+ print("#define %s %g\n", s, id.init.c.rval);
+ Tstring =>
+ print("#define %s \"%s\"\n", s, id.init.decl.sym.name);
+ }
+}
+
+mapconv(d: ref Desc): string
+{
+ s := "{";
+ for(i := 0; i < d.nmap; i++)
+ s += "0x" + hex(int d.map[i], 0) + ",";
+ if(i == 0)
+ s += "0";
+ s += "}";
+ return s;
+}
+
+dotprint(d: ref Decl, dot: int): string
+{
+ s : string;
+ if(d.dot != nil){
+ s = dotprint(d.dot, dot);
+ s[len s] = dot;
+ }
+ if(d.sym == nil)
+ return s;
+ return s + d.sym.name;
+}
+
+ckindname := array[Tend] of
+{
+ Tnone => "void",
+ Tadt => "struct",
+ Tadtpick => "?adtpick?",
+ Tarray => "Array*",
+ Tbig => "LONG",
+ Tbyte => "BYTE",
+ Tchan => "Channel*",
+ Treal => "REAL",
+ Tfn => "?fn?",
+ Tint => "WORD",
+ Tlist => "List*",
+ Tmodule => "Modlink*",
+ Tref => "?ref?",
+ Tstring => "String*",
+ Ttuple => "?tuple?",
+ Texception => "?exception",
+ Tfix => "WORD",
+ Tpoly => "void*",
+
+ Tainit => "?ainit?",
+ Talt => "?alt?",
+ Tany => "void*",
+ Tarrow => "?arrow?",
+ Tcase => "?case?",
+ Tcasel => "?casel?",
+ Tcasec => "?casec?",
+ Tdot => "?dot?",
+ Terror => "?error?",
+ Tgoto => "?goto?",
+ Tid => "?id?",
+ Tiface => "?iface?",
+ Texcept => "?except?",
+ Tinst => "?inst?",
+};
+
+ctypeconv(t: ref Type): string
+{
+ if(t == nil)
+ return "void";
+ s := "";
+ case t.kind{
+ Terror =>
+ return "type error";
+ Tref =>
+ s = ctypeconv(t.tof);
+ s += "*";
+ Tarray or
+ Tlist or
+ Tint or
+ Tbig or
+ Tstring or
+ Treal or
+ Tbyte or
+ Tnone or
+ Tany or
+ Tchan or
+ Tmodule or
+ Tfix or
+ Tpoly =>
+ return ckindname[t.kind];
+ Tadt or
+ Ttuple =>
+ if(t.decl.sym != anontupsym)
+ return dotprint(t.decl, '_');
+ s += "struct{ ";
+ offset := 0;
+ for(id := t.ids; id != nil; id = id.next){
+ tt := id.ty;
+ (offset, s) = stubalign(offset, tt.align, s);
+ if(offset != id.offset)
+ fatal("ctypeconv tuple bad offset");
+ s += ctypeconv(tt);
+ s += " ";
+ s += id.sym.name;
+ s += "; ";
+ offset += tt.size;
+ }
+ (offset, s) = stubalign(offset, t.align, s);
+ if(offset != t.size)
+ fatal(sprint("ctypeconv tuple bad t=%s size=%d offset=%d", typeconv(t), t.size, offset));
+ s += "}";
+ * =>
+ fatal("no C equivalent for type " + string t.kind);
+ }
+ return s;
+}
+
+pickadtstub(t: ref Type)
+{
+ tt: ref Type;
+ desc: ref Desc;
+ id, tg: ref Decl;
+ ok: byte;
+ offset, tgoffset: int;
+
+ buf := dotprint(t.decl, '_');
+ offset = 0;
+ for(tg = t.tags; tg != nil; tg = tg.next)
+ print("#define %s_%s %d\n", buf, tg.sym.name, offset++);
+ print("struct %s\n{\n", buf);
+ print(" int pick;\n");
+ offset = IBY2WD;
+ for(id = t.ids; id != nil; id = id.next){
+ if(id.store == Dfield){
+ tt = id.ty;
+ (offset, nil) = stubalign(offset, tt.align, nil);
+ if(offset != id.offset)
+ fatal("pickadtstub bad offset");
+ print(" %s %s;\n", ctypeconv(tt), id.sym.name);
+ offset += tt.size;
+ }
+ }
+ print(" union{\n");
+ for(tg = t.tags; tg != nil; tg = tg.next){
+ tgoffset = offset;
+ print(" struct{\n");
+ for(id = tg.ty.ids; id != nil; id = id.next){
+ if(id.store == Dfield){
+ tt = id.ty;
+ (tgoffset, nil) = stubalign(tgoffset, tt.align, nil);
+ if(tgoffset != id.offset)
+ fatal("pickadtstub bad offset");
+ print(" %s %s;\n", ctypeconv(tt), id.sym.name);
+ tgoffset += tt.size;
+ }
+ }
+ if(tg.ty.ids == nil)
+ print(" char dummy[1];\n");
+ print(" } %s;\n", tg.sym.name);
+ }
+ print(" } u;\n");
+ print("};\n");
+
+ for(id = t.ids; id != nil; id = id.next)
+ if(id.store == Dconst)
+ constub(id);
+
+ for(id = t.ids; id != nil; id = id.next)
+ if(id.ty.kind == Tchan)
+ chanstub(buf, id);
+
+ for(tg = t.tags; tg != nil; tg = tg.next){
+ ok = tg.ty.tof.ok;
+ tg.ty.tof.ok = OKverify;
+ sizetype(tg.ty.tof);
+ tg.ty.tof.ok = OKmask;
+ desc = mktdesc(tg.ty.tof);
+ tg.ty.tof.ok = ok;
+ print("#define %s_%s_size %d\n", buf, tg.sym.name, tg.ty.size);
+ print("#define %s_%s_map %s\n", buf, tg.sym.name, mapconv(desc));
+ }
+}
diff --git a/appl/cmd/limbo/typecheck.b b/appl/cmd/limbo/typecheck.b
new file mode 100644
index 00000000..fc0d43e4
--- /dev/null
+++ b/appl/cmd/limbo/typecheck.b
@@ -0,0 +1,3223 @@
+fndecls: ref Decl;
+labstack: array of ref Node;
+maxlabdep: int;
+inexcept: ref Node;
+nexc: int;
+fndec: ref Decl;
+
+increfs(id: ref Decl)
+{
+ for( ; id != nil; id = id.link)
+ id.refs++;
+}
+
+fninline(d: ref Decl): int
+{
+ left, right: ref Node;
+
+ n := d.init;
+ if(dontinline || d.inline < byte 0 || d.locals != nil || ispoly(d) || n.ty.tof.kind == Tnone || nodes(n) >= 100)
+ return 0;
+ n = n.right;
+ if(n.op == Oseq && n.right == nil)
+ n = n.left;
+ #
+ # inline
+ # (a) return e;
+ # (b) if(c) return e1; else return e2;
+ # (c) if(c) return e1; return e2;
+ #
+ case(n.op){
+ Oret =>
+ break;
+ Oif =>
+ right = n.right;
+ if(right.right == nil || right.left.op != Oret || right.right.op != Oret || !tequal(right.left.left.ty, right.right.left.ty))
+ return 0;
+ break;
+ Oseq =>
+ left = n.left;
+ right = n.right;
+ if(left.op != Oif || left.right.right != nil || left.right.left.op != Oret || right.op != Oseq || right.right != nil || right.left.op != Oret || !tequal(left.right.left.left.ty, right.left.left.ty))
+ return 0;
+ break;
+ * =>
+ return 0;
+ }
+ if(occurs(d, n) || hasasgns(n))
+ return 0;
+ if(n.op == Oseq){
+ left.right.right = right.left;
+ n = left;
+ right = n.right;
+ d.init.right.right = nil;
+ }
+ if(n.op == Oif){
+ n.ty = right.ty = right.left.left.ty;
+ right.left = right.left.left;
+ right.right = right.right.left;
+ d.init.right.left = mkunary(Oret, n);
+ }
+ return 1;
+}
+
+rewind(n: ref Node)
+{
+ r, nn: ref Node;
+
+ r = n;
+ nn = n.left;
+ for(n = n.right; n != nil; n = n.right){
+ if(n.right == nil){
+ r.left = nn;
+ r.right = n.left;
+ }
+ else
+ nn = mkbin(Oindex, nn, n.left);
+ }
+}
+
+ckmod(n: ref Node, id: ref Decl)
+{
+ t: ref Type;
+ d, idc: ref Decl;
+ mod: ref Node;
+
+ if(id == nil)
+ fatal("can't find function: " + nodeconv(n));
+ idc = nil;
+ mod = nil;
+ if(n.op == Oname){
+ idc = id;
+ mod = id.eimport;
+ }
+ else if(n.op == Omdot)
+ mod = n.left;
+ else if(n.op == Odot){
+ idc = id.dot;
+ t = n.left.ty;
+ if(t.kind == Tref)
+ t = t.tof;
+ if(t.kind == Tadtpick)
+ t = t.decl.dot.ty;
+ d = t.decl;
+ while(d != nil && d.link != nil)
+ d = d.link;
+ if(d != nil && d.timport != nil)
+ mod = d.timport.eimport;
+ n.right.left = mod;
+ }
+ if(mod != nil && mod.ty.kind != Tmodule){
+ nerror(n, "cannot use " + expconv(n) + " as a function reference");
+ return;
+ }
+ if(mod != nil){
+ if(valistype(mod)){
+ nerror(n, "cannot use " + expconv(n) + " as a function reference because " + expconv(mod) + " is a module interface");
+ return;
+ }
+ }else if(idc != nil && idc.dot != nil && !isimpmod(idc.dot.sym)){
+ nerror(n, "cannot use " + expconv(n) + " without importing " + idc.sym.name + " from a variable");
+ return;
+ }
+ if(mod != nil)
+ modrefable(n.ty);
+}
+
+addref(n: ref Node)
+{
+ nn: ref Node;
+
+ nn = mkn(0, nil, nil);
+ *nn = *n;
+ n.op = Oref;
+ n.left = nn;
+ n.right = nil;
+ n.decl = nil;
+ n.ty = usetype(mktype(n.src.start, n.src.stop, Tref, nn.ty, nil));
+}
+
+fnref(n: ref Node, id: ref Decl)
+{
+ id.inline = byte -1;
+ ckmod(n, id);
+ addref(n);
+ while(id.link != nil)
+ id = id.link;
+ if(ispoly(id) && encpolys(id) != nil)
+ nerror(n, "cannot have a polymorphic adt function reference " + id.sym.name);
+}
+
+typecheck(checkimp: int): ref Decl
+{
+ entry, d, m: ref Decl;
+
+ if(errors)
+ return nil;
+
+ #
+ # generate the set of all functions
+ # compile one function at a time
+ #
+ gdecl(tree);
+ gbind(tree);
+ fns = array[nfns] of ref Decl;
+ i := gcheck(tree, fns, 0);
+ if(i != nfns)
+ fatal("wrong number of functions found in gcheck");
+
+ maxlabdep = 0;
+ for(i = 0; i < nfns; i++){
+ d = fns[i];
+ if(d != nil)
+ fndec = d;
+ if(d != nil)
+ fncheck(d);
+ fndec = nil;
+ }
+
+ if(errors)
+ return nil;
+
+ entry = nil;
+ if(checkimp){
+ im: ref Decl;
+ dm: ref Dlist;
+
+ if(impmods == nil){
+ yyerror("no implementation module");
+ return nil;
+ }
+ for(im = impmods; im != nil; im = im.next){
+ for(dm = impdecls; dm != nil; dm = dm.next)
+ if(dm.d.sym == im.sym)
+ break;
+ if(dm == nil || dm.d.ty == nil){
+ yyerror("no definition for implementation module "+im.sym.name);
+ return nil;
+ }
+ }
+
+ #
+ # can't check the module spec until all types and imports are determined,
+ # which happens in scheck
+ #
+ for(dm = impdecls; dm != nil; dm = dm.next){
+ im = dm.d;
+ im.refs++;
+ im.ty = usetype(im.ty);
+ if(im.store != Dtype || im.ty.kind != Tmodule){
+ error(im.src.start, "cannot implement "+declconv(im));
+ return nil;
+ }
+ }
+
+ # now check any multiple implementations
+ impdecl = modimp(impdecls, impmods);
+
+ s := enter("init", 0);
+ for(dm = impdecls; dm != nil; dm = dm.next){
+ im = dm.d;
+ for(m = im.ty.ids; m != nil; m = m.next){
+ m.ty = usetype(m.ty);
+ m.refs++;
+
+ if(m.sym == s && m.ty.kind == Tfn && entry == nil)
+ entry = m;
+
+ if(m.store == Dglobal || m.store == Dfn)
+ modrefable(m.ty);
+
+ if(m.store == Dtype && m.ty.kind == Tadt){
+ for(d = m.ty.ids; d != nil; d = d.next){
+ d.ty = usetype(d.ty);
+ modrefable(d.ty);
+ d.refs++;
+ }
+ }
+ }
+ checkrefs(im.ty.ids);
+ }
+ }
+ if(errors)
+ return nil;
+ gsort(tree);
+ tree = nil;
+ return entry;
+}
+#
+# introduce all global declarations
+# also adds all fields to adts and modules
+# note the complications due to nested Odas expressions
+#
+gdecl(n: ref Node)
+{
+ for(;;){
+ if(n == nil)
+ return;
+ if(n.op != Oseq)
+ break;
+ gdecl(n.left);
+ n = n.right;
+ }
+ case n.op{
+ Oimport =>
+ importdecled(n);
+ gdasdecl(n.right);
+ Oadtdecl =>
+ adtdecled(n);
+ Ocondecl =>
+ condecled(n);
+ gdasdecl(n.right);
+ Oexdecl =>
+ exdecled(n);
+ Omoddecl =>
+ moddecled(n);
+ Otypedecl =>
+ typedecled(n);
+ Ovardecl =>
+ vardecled(n);
+ Ovardecli =>
+ vardecled(n.left);
+ gdasdecl(n.right);
+ Ofunc =>
+ fndecled(n);
+ Oas or
+ Odas or
+ Onothing =>
+ gdasdecl(n);
+ * =>
+ fatal("can't deal with "+opconv(n.op)+" in gdecl");
+ }
+}
+
+#
+# bind all global type ids,
+# including those nested inside modules
+# this needs to be done, since we may use such
+# a type later in a nested scope, so if we bound
+# the type ids then, the type could get bound
+# to a nested declaration
+#
+gbind(n: ref Node)
+{
+ ids: ref Decl;
+
+ for(;;){
+ if(n == nil)
+ return;
+ if(n.op != Oseq)
+ break;
+ gbind(n.left);
+ n = n.right;
+ }
+ case n.op{
+ Oas or
+ Ocondecl or
+ Odas or
+ Oexdecl or
+ Ofunc or
+ Oimport or
+ Onothing or
+ Ovardecl or
+ Ovardecli =>
+ break;
+ Ofielddecl =>
+ bindtypes(n.decl.ty);
+ Otypedecl =>
+ bindtypes(n.decl.ty);
+ if(n.left != nil)
+ gbind(n.left);
+ Opickdecl =>
+ gbind(n.left);
+ d := n.right.left.decl;
+ bindtypes(d.ty);
+ repushids(d.ty.ids);
+ gbind(n.right.right);
+ # get new ids for undefined types; propagate outwards
+ ids = popids(d.ty.ids);
+ if(ids != nil)
+ installids(Dundef, ids);
+ Oadtdecl or
+ Omoddecl =>
+ bindtypes(n.ty);
+ if(n.ty.polys != nil)
+ repushids(n.ty.polys);
+ repushids(n.ty.ids);
+ gbind(n.left);
+ # get new ids for undefined types; propagate outwards
+ ids = popids(n.ty.ids);
+ if(ids != nil)
+ installids(Dundef, ids);
+ if(n.ty.polys != nil)
+ popids(n.ty.polys);
+ * =>
+ fatal("can't deal with "+opconv(n.op)+" in gbind");
+ }
+}
+
+#
+# check all of the global declarations
+# bind all type ids referred to within types at the global level
+# record decls for defined functions
+#
+gcheck(n: ref Node, fns: array of ref Decl, nfns: int): int
+{
+ ok, allok: int;
+
+ for(;;){
+ if(n == nil)
+ return nfns;
+ if(n.op != Oseq)
+ break;
+ nfns = gcheck(n.left, fns, nfns);
+ n = n.right;
+ }
+
+ case n.op{
+ Ofielddecl =>
+ if(n.decl.ty.eraises != nil)
+ raisescheck(n.decl.ty);
+ Onothing or
+ Opickdecl =>
+ break;
+ Otypedecl =>
+ tcycle(n.ty);
+ Oadtdecl or
+ Omoddecl =>
+ if(n.ty.polys != nil)
+ repushids(n.ty.polys);
+ repushids(n.ty.ids);
+ if(gcheck(n.left, nil, 0))
+ fatal("gcheck fn decls nested in modules or adts");
+ if(popids(n.ty.ids) != nil)
+ fatal("gcheck installs new ids in a module or adt");
+ if(n.ty.polys != nil)
+ popids(n.ty.polys);
+ Ovardecl =>
+ varcheck(n, 1);
+ Ocondecl =>
+ concheck(n, 1);
+ Oexdecl =>
+ excheck(n, 1);
+ Oimport =>
+ importcheck(n, 1);
+ Ovardecli =>
+ varcheck(n.left, 1);
+ (ok, allok) = echeck(n.right, 0, 1, nil);
+ if(ok){
+ if(allok)
+ n.right = fold(n.right);
+ globalas(n.right.left, n.right.right, allok);
+ }
+ Oas or
+ Odas =>
+ (ok, allok) = echeck(n, 0, 1, nil);
+ if(ok){
+ if(allok)
+ n = fold(n);
+ globalas(n.left, n.right, allok);
+ }
+ Ofunc =>
+ (ok, allok) = echeck(n.left, 0, 1, n);
+ if(ok && n.ty.eraises != nil)
+ raisescheck(n.ty);
+ d : ref Decl = nil;
+ if(ok)
+ d = fnchk(n);
+ fns[nfns++] = d;
+ * =>
+ fatal("can't deal with "+opconv(n.op)+" in gcheck");
+ }
+ return nfns;
+}
+
+#
+# check for unused expression results
+# make sure the any calculated expression has
+# a destination
+#
+checkused(n: ref Node): ref Node
+{
+ #
+ # only nil; and nil = nil; should have type tany
+ #
+ if(n.ty == tany){
+ if(n.op == Oname)
+ return n;
+ if(n.op == Oas)
+ return checkused(n.right);
+ fatal("line "+lineconv(n.src.start)+" checkused "+nodeconv(n));
+ }
+
+ if(n.op == Ocall && n.left.ty.kind == Tfn && n.left.ty.tof != tnone){
+ n = mkunary(Oused, n);
+ n.ty = n.left.ty;
+ return n;
+ }
+ if(n.op == Ocall && isfnrefty(n.left.ty)){
+ if(n.left.ty.tof.tof != tnone){
+ n = mkunary(Oused, n);
+ n.ty = n.left.ty;
+ }
+ return n;
+ }
+ if(isused[n.op] && (n.op != Ocall || n.left.ty.kind == Tfn))
+ return n;
+ t := n.ty;
+ if(t.kind == Tfn)
+ nerror(n, "function "+expconv(n)+" not called");
+ else if(t.kind == Tadt && t.tags != nil || t.kind == Tadtpick)
+ nerror(n, "expressions cannot have type "+typeconv(t));
+ else if(n.op == Otuple){
+ for(nn := n.left; nn != nil; nn = nn.right)
+ checkused(nn.left);
+ }
+ else
+ nwarn(n, "result of expression "+expconv(n)+" not used");
+ n = mkunary(Oused, n);
+ n.ty = n.left.ty;
+ return n;
+}
+
+fncheck(d: ref Decl)
+{
+ n := d.init;
+ if(debug['t'])
+ print("typecheck tree: %s\n", nodeconv(n));
+
+ fndecls = nil;
+ adtp := outerpolys(n.left);
+ if(n.left.op == Odot)
+ repushids(adtp);
+ if(d.ty.polys != nil)
+ repushids(d.ty.polys);
+ repushids(d.ty.ids);
+
+ labdep = 0;
+ labstack = array[maxlabdep] of ref Node;
+ n.right = scheck(n.right, d.ty.tof, Sother);
+ if(labdep != 0)
+ fatal("unbalanced label stack in fncheck");
+ labstack = nil;
+
+ d.locals = appdecls(popids(d.ty.ids), fndecls);
+ if(d.ty.polys != nil)
+ popids(d.ty.polys);
+ if(n.left.op == Odot)
+ popids(adtp);
+ fndecls = nil;
+
+ checkrefs(d.ty.ids);
+ checkrefs(d.ty.polys);
+ checkrefs(d.locals);
+
+ checkraises(n);
+
+ d.inline = byte fninline(d);
+}
+
+scheck(n: ref Node, ret: ref Type, kind : int): ref Node
+{
+ s: ref Sym;
+ rok: int;
+
+ top := n;
+ last: ref Node = nil;
+ for(; n != nil; n = n.right){
+ left := n.left;
+ right := n.right;
+ case n.op{
+ Ovardecl =>
+ vardecled(n);
+ varcheck(n, 0);
+ if (nested() && tmustzero(n.decl.ty))
+ decltozero(n);
+# else if (inloop() && tmustzero(n.decl.ty))
+# decltozero(n);
+ return top;
+ Ovardecli =>
+ vardecled(left);
+ varcheck(left, 0);
+ echeck(right, 0, 0, nil);
+ if (nested() && tmustzero(left.decl.ty))
+ decltozero(left);
+ return top;
+ Otypedecl =>
+ typedecled(n);
+ bindtypes(n.ty);
+ tcycle(n.ty);
+ return top;
+ Ocondecl =>
+ condecled(n);
+ concheck(n, 0);
+ return top;
+ Oexdecl =>
+ exdecled(n);
+ excheck(n, 0);
+ return top;
+ Oimport =>
+ importdecled(n);
+ importcheck(n, 0);
+ return top;
+ Ofunc =>
+ fatal("scheck func");
+ Oscope =>
+ if (kind == Sother)
+ kind = Sscope;
+ pushscope(n, kind);
+ if (left != nil)
+ fatal("Oscope has left field");
+ echeck(left, 0, 0, nil);
+ n.right = scheck(right, ret, Sother);
+ d := popscope();
+ fndecls = appdecls(fndecls, d);
+ return top;
+ Olabel =>
+ echeck(left, 0, 0, nil);
+ n.right = scheck(right, ret, Sother);
+ return top;
+ Oseq =>
+ n.left = scheck(left, ret, Sother);
+ # next time will check n.right
+ Oif =>
+ (rok, nil) = echeck(left, 0, 0, nil);
+ if(rok && left.op != Onothing && left.ty != tint)
+ nerror(n, "if conditional must be an int, not "+etconv(left));
+ right.left = scheck(right.left, ret, Sother);
+ # next time will check n.right.right
+ n = right;
+ Ofor =>
+ (rok, nil) = echeck(left, 0, 0, nil);
+ if(rok && left.op != Onothing && left.ty != tint)
+ nerror(n, "for conditional must be an int, not "+etconv(left));
+ #
+ # do the continue clause before the body
+ # this reflects the ordering of declarations
+ #
+ pushlabel(n);
+ right.right = scheck(right.right, ret, Sother);
+ right.left = scheck(right.left, ret, Sloop);
+ labdep--;
+ if(n.decl != nil && !n.decl.refs)
+ nwarn(n, "label "+n.decl.sym.name+" never referenced");
+ return top;
+ Odo =>
+ (rok, nil) = echeck(left, 0, 0, nil);
+ if(rok && left.op != Onothing && left.ty != tint)
+ nerror(n, "do conditional must be an int, not "+etconv(left));
+ pushlabel(n);
+ n.right = scheck(n.right, ret, Sloop);
+ labdep--;
+ if(n.decl != nil && !n.decl.refs)
+ nwarn(n, "label "+n.decl.sym.name+" never referenced");
+ return top;
+ Oalt or
+ Ocase or
+ Opick or
+ Oexcept =>
+ pushlabel(n);
+ case n.op{
+ Oalt =>
+ altcheck(n, ret);
+ Ocase =>
+ casecheck(n, ret);
+ Opick =>
+ pickcheck(n, ret);
+ Oexcept =>
+ exccheck(n, ret);
+ }
+ labdep--;
+ if(n.decl != nil && !n.decl.refs)
+ nwarn(n, "label "+n.decl.sym.name+" never referenced");
+ return top;
+ Oret =>
+ (rok, nil) = echeck(left, 0, 0, nil);
+ if(!rok)
+ return top;
+ if(left == nil){
+ if(ret != tnone)
+ nerror(n, "return of nothing from a fn of "+typeconv(ret));
+ }else if(ret == tnone){
+ if(left.ty != tnone)
+ nerror(n, "return "+etconv(left)+" from a fn with no return type");
+ }else if(!tcompat(ret, left.ty, 0))
+ nerror(n, "return "+etconv(left)+" from a fn of "+typeconv(ret));
+ return top;
+ Obreak or
+ Ocont =>
+ s = nil;
+ if(n.decl != nil)
+ s = n.decl.sym;
+ for(i := 0; i < labdep; i++){
+ if(s == nil || labstack[i].decl != nil && labstack[i].decl.sym == s){
+ if(n.op == Ocont
+ && labstack[i].op != Ofor && labstack[i].op != Odo)
+ continue;
+ if(s != nil)
+ labstack[i].decl.refs++;
+ return top;
+ }
+ }
+ nerror(n, "no appropriate target for "+expconv(n));
+ return top;
+ Oexit or
+ Onothing =>
+ return top;
+ Oexstmt =>
+ fndec.handler = byte 1;
+ n.left = scheck(left, ret, Sother);
+ n.right = scheck(right, ret, Sother);
+ return top;
+ * =>
+ (nil, rok) = echeck(n, 0, 0, nil);
+ if(rok)
+ n = checkused(n);
+ if(last == nil)
+ return n;
+ last.right = n;
+ return top;
+ }
+ last = n;
+ }
+ return top;
+}
+
+pushlabel(n: ref Node)
+{
+ s: ref Sym;
+
+ if(labdep >= maxlabdep){
+ maxlabdep += MaxScope;
+ labs := array[maxlabdep] of ref Node;
+ labs[:] = labstack;
+ labstack = labs;
+ }
+ if(n.decl != nil){
+ s = n.decl.sym;
+ n.decl.refs = 0;
+ for(i := 0; i < labdep; i++)
+ if(labstack[i].decl != nil && labstack[i].decl.sym == s)
+ nerror(n, "label " + s.name + " duplicated on line " + lineconv(labstack[i].decl.src.start));
+ }
+ labstack[labdep++] = n;
+}
+
+varcheck(n: ref Node, isglobal: int)
+{
+ t := validtype(n.ty, nil);
+ t = topvartype(t, n.decl, isglobal, 0);
+ last := n.left.decl;
+ for(ids := n.decl; ids != last.next; ids = ids.next){
+ ids.ty = t;
+ shareloc(ids);
+ }
+ if(t.eraises != nil)
+ raisescheck(t);
+}
+
+concheck(n: ref Node, isglobal: int)
+{
+ t: ref Type;
+ init: ref Node;
+
+ pushscope(nil, Sother);
+ installids(Dconst, iota);
+ (ok, allok) := echeck(n.right, 0, isglobal, nil);
+ popscope();
+
+ init = n.right;
+ if(!ok){
+ t = terror;
+ }else{
+ t = init.ty;
+ if(!tattr[t.kind].conable){
+ nerror(init, "cannot have a "+typeconv(t)+" constant");
+ allok = 0;
+ }
+ }
+
+ last := n.left.decl;
+ for(ids := n.decl; ids != last.next; ids = ids.next)
+ ids.ty = t;
+
+ if(!allok)
+ return;
+
+ i := 0;
+ for(ids = n.decl; ids != last.next; ids = ids.next){
+ if(ok){
+ iota.init.c.val = big i;
+ ids.init = dupn(0, nosrc, init);
+ if(!varcom(ids))
+ ok = 0;
+ }
+ i++;
+ }
+}
+
+exname(d: ref Decl): string
+{
+ s := "";
+ m := impmods.sym;
+ if(d.dot != nil)
+ m = d.dot.sym;
+ if(m != nil)
+ s += m.name+".";
+ if(fndec != nil)
+ s += fndec.sym.name+".";
+ s += string (scope-ScopeGlobal)+"."+d.sym.name;
+ return s;
+}
+
+excheck(n: ref Node, isglobal: int)
+{
+ t: ref Type;
+ ids, last: ref Decl;
+
+ t = validtype(n.ty, nil);
+ t = topvartype(t, n.decl, isglobal, 0);
+ last = n.left.decl;
+ for(ids = n.decl; ids != last.next; ids = ids.next){
+ ids.ty = t;
+ ids.init = mksconst(n.src, enterstring(exname(ids)));
+ # ids.init = mksconst(n.src, enterstring(ids.sym.name));
+ }
+}
+
+importcheck(n: ref Node, isglobal: int)
+{
+ (ok, nil) := echeck(n.right, 1, isglobal, nil);
+ if(!ok)
+ return;
+
+ m := n.right;
+ if(m.ty.kind != Tmodule || m.op != Oname){
+ nerror(n, "cannot import from "+etconv(m));
+ return;
+ }
+
+ last := n.left.decl;
+ for(id := n.decl; id != last.next; id = id.next){
+ v := namedot(m.ty.ids, id.sym);
+ if(v == nil){
+ error(id.src.start, id.sym.name+" is not a member of "+expconv(m));
+ id.store = Dwundef;
+ continue;
+ }
+ id.store = v.store;
+ v.ty = validtype(v.ty, nil);
+ id.ty = t := v.ty;
+ if(id.store == Dtype && t.decl != nil){
+ id.timport = t.decl.timport;
+ t.decl.timport = id;
+ }
+ id.init = v.init;
+ id.importid = v;
+ id.eimport = m;
+ }
+}
+
+rewcall(n: ref Node, d: ref Decl): ref Decl
+{
+ # put original function back now we're type checked
+ while(d.link != nil)
+ d = d.link;
+ if(n.op == Odot)
+ n.right.decl = d;
+ else if(n.op == Omdot){
+ n.right.right.decl = d;
+ n.right.right.ty = d.ty;
+ }
+ else
+ fatal("bad op in Ocall rewcall");
+ n.ty = n.right.ty = d.ty;
+ d.refs++;
+ usetype(d.ty);
+ return d;
+}
+
+isfnrefty(t: ref Type): int
+{
+ return t.kind == Tref && t.tof.kind == Tfn;
+}
+
+isfnref(d: ref Decl): int
+{
+ case(d.store){
+ Dglobal or
+ Darg or
+ Dlocal or
+ Dfield or
+ Dimport =>
+ return isfnrefty(d.ty);
+ }
+ return 0;
+}
+
+tagopt: int;
+
+#
+# annotate the expression with types
+#
+echeck(n: ref Node, typeok, isglobal: int, par: ref Node): (int, int)
+{
+ tg, id, callee: ref Decl;
+ t, tt: ref Type;
+ ok, allok, max, nocheck, kidsok: int;
+
+ ok = allok = 1;
+ if(n == nil)
+ return (1, 1);
+
+ if(n.op == Oseq){
+ for( ; n != nil && n.op == Oseq; n = n.right){
+ (okl, allokl) := echeck(n.left, typeok == 2, isglobal, n);
+ ok &= okl;
+ allok &= allokl;
+ n.ty = tnone;
+ }
+ if(n == nil)
+ return (ok, allok);
+ }
+
+ left := n.left;
+ right := n.right;
+
+ nocheck = 0;
+ if(n.op == Odot || n.op == Omdot || n.op == Ocall || n.op == Oref || n.op == Otagof || n.op == Oindex)
+ nocheck = 1;
+ if(n.op != Odas # special case
+ && n.op != Oload) # can have better error recovery
+ (ok, allok) = echeck(left, nocheck, isglobal, n);
+ if(n.op != Odas # special case
+ && n.op != Odot # special check
+ && n.op != Omdot # special check
+ && n.op != Ocall # can have better error recovery
+ && n.op != Oindex){
+ (okr, allokr) := echeck(right, 0, isglobal, n);
+ ok &= okr;
+ allok &= allokr;
+ }
+ if(!ok){
+ n.ty = terror;
+ return (0, 0);
+ }
+
+ case n.op{
+ Odas =>
+ (ok, allok) = echeck(right, 0, isglobal, n);
+ if(!ok)
+ right.ty = terror;
+ if(!isglobal && !dasdecl(left)){
+ ok = 0;
+ }else if(!specific(right.ty) || !declasinfer(left, right.ty)){
+ nerror(n, "cannot declare "+expconv(left)+" from "+etconv(right));
+ declaserr(left);
+ ok = 0;
+ }
+ if(right.ty.kind == Texception)
+ left.ty = n.ty = mkextuptype(right.ty);
+ else{
+ left.ty = n.ty = right.ty;
+ usedty(n.ty);
+ }
+ if (nested() && tmustzero(left.ty))
+ decltozero(left);
+ return (ok, allok & ok);
+ Oseq or
+ Onothing =>
+ n.ty = tnone;
+ Owild =>
+ n.ty = tint;
+ Ocast =>
+ t = usetype(n.ty);
+ n.ty = t;
+ tt = left.ty;
+ if(tcompat(t, tt, 0)){
+ left.ty = t;
+ break;
+ }
+ if(tt.kind == Tarray){
+ if(tt.tof == tbyte && t == tstring)
+ break;
+ }else if(t.kind == Tarray){
+ if(t.tof == tbyte && tt == tstring)
+ break;
+ }else if(casttab[tt.kind][t.kind]){
+ break;
+ }
+ nerror(n, "cannot make a "+typeconv(n.ty)+" from "+etconv(left));
+ return (0, 0);
+ Ochan =>
+ n.ty = usetype(n.ty);
+ if(left != nil && left.ty.kind != Tint){
+ nerror(n, "channel size "+etconv(left)+" is not an int");
+ return (0, 0);
+ }
+ Oload =>
+ n.ty = usetype(n.ty);
+ (nil, kidsok) = echeck(left, 0, isglobal, n);
+ if(n.ty.kind != Tmodule){
+ nerror(n, "cannot load a "+typeconv(n.ty));
+ return (0, 0);
+ }
+ if(!kidsok){
+ allok = 0;
+ break;
+ }
+ if(left.ty != tstring){
+ nerror(n, "cannot load a module from "+etconv(left));
+ allok = 0;
+ break;
+ }
+if(n.ty.tof.decl.refs != 0)
+n.ty.tof.decl.refs++;
+n.ty.decl.refs++;
+ usetype(n.ty.tof);
+ Oref =>
+ t = left.ty;
+ if(t.kind != Tadt && t.kind != Tadtpick && t.kind != Tfn && t.kind != Ttuple){
+ nerror(n, "cannot make a ref from "+etconv(left));
+ return (0, 0);
+ }
+ if(!tagopt && t.kind == Tadt && t.tags != nil && valistype(left)){
+ nerror(n, "instances of ref "+expconv(left)+" must be qualified with a pick tag");
+ return (0, 0);
+ }
+ if(t.kind == Tadtpick)
+ t.tof = usetype(t.tof);
+ n.ty = usetype(mktype(n.src.start, n.src.stop, Tref, t, nil));
+ Oarray =>
+ max = 0;
+ if(right != nil){
+ max = assignindices(n);
+ if(max < 0)
+ return (0, 0);
+ if(!specific(right.left.ty)){
+ nerror(n, "type for array not specific");
+ return (0, 0);
+ }
+ n.ty = mktype(n.src.start, n.src.stop, Tarray, right.left.ty, nil);
+ }
+ n.ty = usetype(n.ty);
+
+ if(left.op == Onothing)
+ n.left = left = mkconst(n.left.src, big max);
+
+ if(left.ty.kind != Tint){
+ nerror(n, "array size "+etconv(left)+" is not an int");
+ return (0, 0);
+ }
+ Oelem =>
+ n.ty = right.ty;
+ Orange =>
+ if(left.ty != right.ty
+ || left.ty != tint && left.ty != tstring){
+ nerror(left, "range "+etconv(left)+" to "+etconv(right)+" is not an int or string range");
+ return (0, 0);
+ }
+ n.ty = left.ty;
+ Oname =>
+ id = n.decl;
+ if(id == nil){
+ nerror(n, "name with no declaration");
+ return (0, 0);
+ }
+ if(id.store == Dunbound){
+ s := id.sym;
+ id = s.decl;
+ if(id == nil)
+ id = undefed(n.src, s);
+ # save a little space
+ s.unbound = nil;
+ n.decl = id;
+ id.refs++;
+ }
+ n.ty = id.ty = usetype(id.ty);
+ case id.store{
+ Dfn or
+ Dglobal or
+ Darg or
+ Dlocal or
+ Dimport or
+ Dfield or
+ Dtag =>
+ break;
+ Dunbound =>
+ fatal("unbound symbol found in echeck");
+ Dundef =>
+ nerror(n, id.sym.name+" is not declared");
+ id.store = Dwundef;
+ return (0, 0);
+ Dwundef =>
+ return (0, 0);
+ Dconst =>
+ if(id.init == nil){
+ nerror(n, id.sym.name+"'s value cannot be determined");
+ id.store = Dwundef;
+ return (0, 0);
+ }
+ Dtype =>
+ if(typeok)
+ break;
+ nerror(n, declconv(id)+" is not a variable");
+ return (0, 0);
+ * =>
+ fatal("echeck: unknown symbol storage");
+ }
+
+ if(n.ty == nil){
+ nerror(n, declconv(id)+"'s type is not fully defined");
+ id.store = Dwundef;
+ return (0, 0);
+ }
+ if(id.importid != nil && valistype(id.eimport)
+ && id.store != Dconst && id.store != Dtype && id.store != Dfn){
+ nerror(n, "cannot use "+expconv(n)+" because "+expconv(id.eimport)+" is a module interface");
+ return (0, 0);
+ }
+ if(n.ty.kind == Texception && !int n.ty.cons && par != nil && par.op != Oraise && par.op != Odot){
+ nn := mkn(0, nil, nil);
+ *nn = *n;
+ n.op = Ocast;
+ n.left = nn;
+ n.decl = nil;
+ n.ty = usetype(mkextuptype(n.ty));
+ }
+ # function name as function reference
+ if(id.store == Dfn && (par == nil || (par.op != Odot && par.op != Omdot && par.op != Ocall && par.op != Ofunc)))
+ fnref(n, id);
+ Oconst =>
+ if(n.ty == nil){
+ nerror(n, "no type in "+expconv(n));
+ return (0, 0);
+ }
+ Oas =>
+ t = right.ty;
+ if(t.kind == Texception)
+ t = mkextuptype(t);
+ if(!tcompat(left.ty, t, 1)){
+ nerror(n, "type clash in "+etconv(left)+" = "+etconv(right));
+ return (0, 0);
+ }
+ if(t == tany)
+ t = left.ty;
+ n.ty = t;
+ left.ty = t;
+ if(t.kind == Tadt && t.tags != nil || t.kind == Tadtpick)
+ if(left.ty.kind != Tadtpick || right.ty.kind != Tadtpick)
+ nerror(n, "expressions cannot have type "+typeconv(t));
+ if(left.ty.kind == Texception){
+ nerror(n, "cannot assign to an exception");
+ return (0, 0);
+ }
+ if(islval(left))
+ break;
+ return (0, 0);
+ Osnd =>
+ if(left.ty.kind != Tchan){
+ nerror(n, "cannot send on "+etconv(left));
+ return (0, 0);
+ }
+ if(!tcompat(left.ty.tof, right.ty, 0)){
+ nerror(n, "type clash in "+etconv(left)+" <-= "+etconv(right));
+ return (0, 0);
+ }
+ t = right.ty;
+ if(t == tany)
+ t = left.ty.tof;
+ n.ty = t;
+ Orcv =>
+ t = left.ty;
+ if(t.kind == Tarray)
+ t = t.tof;
+ if(t.kind != Tchan){
+ nerror(n, "cannot receive on "+etconv(left));
+ return (0, 0);
+ }
+ if(left.ty.kind == Tarray)
+ n.ty = usetype(mktype(n.src.start, n.src.stop, Ttuple, nil,
+ mkids(n.src, nil, tint, mkids(n.src, nil, t.tof, nil))));
+ else
+ n.ty = t.tof;
+ Ocons =>
+ if(right.ty.kind != Tlist && right.ty != tany){
+ nerror(n, "cannot :: to "+etconv(right));
+ return (0, 0);
+ }
+ n.ty = right.ty;
+ if(right.ty == tany)
+ n.ty = usetype(mktype(n.src.start, n.src.stop, Tlist, left.ty, nil));
+ else if(!tcompat(right.ty.tof, left.ty, 0)){
+ t = tparent(right.ty.tof, left.ty);
+ if(!tcompat(t, left.ty, 0)){
+ nerror(n, "type clash in "+etconv(left)+" :: "+etconv(right));
+ return (0, 0);
+ }
+ else
+ n.ty = usetype(mktype(n.src.start, n.src.stop, Tlist, t, nil));
+ }
+ Ohd or
+ Otl =>
+ if(left.ty.kind != Tlist || left.ty.tof == nil){
+ nerror(n, "cannot "+opconv(n.op)+" "+etconv(left));
+ return (0, 0);
+ }
+ if(n.op == Ohd)
+ n.ty = left.ty.tof;
+ else
+ n.ty = left.ty;
+ Otuple =>
+ n.ty = usetype(mktype(n.src.start, n.src.stop, Ttuple, nil, tuplefields(left)));
+ Ospawn =>
+ if(left.op != Ocall || left.left.ty.kind != Tfn && !isfnrefty(left.left.ty)){
+ nerror(left, "cannot spawn "+expconv(left));
+ return (0, 0);
+ }
+ if(left.ty != tnone){
+ nerror(left, "cannot spawn functions which return values, such as "+etconv(left));
+ return (0, 0);
+ }
+ Oraise =>
+ if(left.op == Onothing){
+ if(inexcept == nil){
+ nerror(n, expconv(n)+": empty raise not in exception handler");
+ return (0, 0);
+ }
+ n.left = dupn(1, n.src, inexcept);
+ break;
+ }
+ if(left.ty != tstring && left.ty.kind != Texception){
+ nerror(n, expconv(n)+": raise argument "+etconv(left)+" is not a string or exception");
+ return (0, 0);
+ }
+ if((left.op != Ocall || left.left.ty.kind == Tfn) && left.ty.ids != nil && int left.ty.cons){
+ nerror(n, "too few exception arguments");
+ return (0, 0);
+ }
+ Ocall =>
+ (nil, kidsok) = echeck(right, 0, isglobal, nil);
+ t = left.ty;
+ usedty(t);
+ pure := 1;
+ if(t.kind == Tref){
+ pure = 0;
+ t = t.tof;
+ }
+ if(t.kind != Tfn)
+ return callcast(n, kidsok, allok);
+ n.ty = t.tof;
+ if(!kidsok){
+ allok = 0;
+ break;
+ }
+
+ #
+ # get the name to call and any associated module
+ #
+ mod: ref Node = nil;
+ callee = nil;
+ id = nil;
+ tt = nil;
+ if(left.op == Odot){
+ callee = left.right.decl;
+ id = callee.dot;
+ right = passimplicit(left, right);
+ n.right = right;
+ tt = left.left.ty;
+ if(tt.kind == Tref)
+ tt = tt.tof;
+ ttt := tt;
+ if(tt.kind == Tadtpick)
+ ttt = tt.decl.dot.ty;
+ dd := ttt.decl;
+ while(dd != nil && dd.link != nil)
+ dd = dd.link;
+ if(dd != nil && dd.timport != nil)
+ mod = dd.timport.eimport;
+
+ #
+ # stash the import module under a rock,
+ # because we won't be able to get it later
+ # after scopes are popped
+ #
+ left.right.left = mod;
+ }else if(left.op == Omdot){
+ if(left.right.op == Odot){
+ callee = left.right.right.decl;
+ right = passimplicit(left.right, right);
+ n.right = right;
+ tt = left.right.left.ty;
+ if(tt.kind == Tref)
+ tt = tt.tof;
+ }else
+ callee = left.right.decl;
+ mod = left.left;
+ }else if(left.op == Oname){
+ callee = left.decl;
+ id = callee;
+ mod = id.eimport;
+ }else if(pure){
+ nerror(left, expconv(left)+" is not a function name");
+ allok = 0;
+ break;
+ }
+ if(pure && callee == nil)
+ fatal("can't find called function: "+nodeconv(left));
+ if(callee != nil && callee.store != Dfn && !isfnref(callee)){
+ nerror(left, expconv(left)+" is not a function");
+ allok = 0;
+ break;
+ }
+ if(mod != nil && mod.ty.kind != Tmodule){
+ nerror(left, "cannot call "+expconv(left));
+ allok = 0;
+ break;
+ }
+ if(mod != nil){
+ if(valistype(mod)){
+ nerror(left, "cannot call "+expconv(left)+" because "+expconv(mod)+" is a module interface");
+ allok = 0;
+ break;
+ }
+ }else if(id != nil && id.dot != nil && !isimpmod(id.dot.sym)){
+ nerror(left, "cannot call "+expconv(left)+" without importing "+id.sym.name+" from a variable");
+ allok = 0;
+ break;
+ }
+ if(mod != nil)
+ modrefable(left.ty);
+ if(callee != nil && callee.store != Dfn)
+ callee = nil;
+ if(t.varargs != byte 0){
+ t = mkvarargs(left, right);
+ if(left.ty.kind == Tref)
+ left.ty = usetype(mktype(t.src.start, t.src.stop, Tref, t, nil));
+ else
+ left.ty = t;
+ }
+ else if(ispoly(callee) || isfnrefty(left.ty) && left.ty.tof.polys != nil){
+ unifysrc = n.src;
+ if(!argncompat(n, t.ids, right)){
+ allok = 0;
+ break;
+ }
+ (okp, tp) := tunify(left.ty, calltype(left.ty, right, n.ty));
+ if(!okp){
+ nerror(n, "function call type mismatch (" + typeconv(left.ty)+" vs "+typeconv(calltype(left.ty, right, n.ty))+")");
+ allok = 0;
+ }
+ else{
+ (n.ty, tp) = expandtype(n.ty, nil, nil, tp);
+ n.ty = usetype(n.ty);
+ if(ispoly(callee) && tt != nil && (tt.kind == Tadt || tt.kind == Tadtpick) && int (tt.flags&INST))
+ callee = rewcall(left, callee);
+ n.right = passfns(n.src, callee, left, right, tt, tp);
+ }
+ }
+ else if(!argcompat(n, t.ids, right))
+ allok = 0;
+ Odot =>
+ t = left.ty;
+ if(t.kind == Tref)
+ t = t.tof;
+ case t.kind{
+ Tadt or
+ Tadtpick or
+ Ttuple or
+ Texception or
+ Tpoly =>
+ id = namedot(t.ids, right.decl.sym);
+ if(id == nil){
+ id = namedot(t.tags, right.decl.sym);
+ if(id != nil && !valistype(left)){
+ nerror(n, expconv(left)+" is not a type");
+ return (0, 0);
+ }
+ }
+ if(id == nil){
+ id = namedot(t.polys, right.decl.sym);
+ if(id != nil && !valistype(left)){
+ nerror(n, expconv(left)+" is not a type");
+ return (0, 0);
+ }
+ }
+ if(id == nil && t.kind == Tadtpick)
+ id = namedot(t.decl.dot.ty.ids, right.decl.sym);
+ if(id == nil){
+ for(tg = t.tags; tg != nil; tg = tg.next){
+ id = namedot(tg.ty.ids, right.decl.sym);
+ if(id != nil)
+ break;
+ }
+ if(id != nil){
+ nerror(n, "cannot yet index field "+right.decl.sym.name+" of "+etconv(left));
+ return (0, 0);
+ }
+ }
+ if(id == nil)
+ break;
+ if(id.store == Dfield && valistype(left)){
+ nerror(n, expconv(left)+" is not a value");
+ return (0, 0);
+ }
+ id.ty = validtype(id.ty, t.decl);
+ id.ty = usetype(id.ty);
+ break;
+ * =>
+ nerror(left, etconv(left)+" cannot be qualified with .");
+ return (0, 0);
+ }
+ if(id == nil){
+ nerror(n, expconv(right)+" is not a member of "+etconv(left));
+ return (0, 0);
+ }
+ if(id.ty == tunknown){
+ nerror(n, "illegal forward reference to "+expconv(n));
+ return (0, 0);
+ }
+
+ increfs(id);
+ right.decl = id;
+ n.ty = id.ty;
+ if((id.store == Dconst || id.store == Dtag) && hasside(left, 1))
+ nwarn(left, "result of expression "+etconv(left)+" ignored");
+ # function name as function reference
+ if(id.store == Dfn && (par == nil || (par.op != Omdot && par.op != Ocall && par.op != Ofunc)))
+ fnref(n, id);
+ Omdot =>
+ t = left.ty;
+ if(t.kind != Tmodule){
+ nerror(left, etconv(left)+" cannot be qualified with ->");
+ return (0, 0);
+ }
+ id = nil;
+ if(right.op == Oname){
+ id = namedot(t.ids, right.decl.sym);
+ }else if(right.op == Odot){
+ (ok, kidsok) = echeck(right, 0, isglobal, n);
+ allok &= kidsok;
+ if(!ok)
+ return (0, 0);
+ tt = right.left.ty;
+ if(tt.kind == Tref)
+ tt = tt.tof;
+ if(right.ty.kind == Tfn
+ && tt.kind == Tadt
+ && tt.decl.dot == t.decl)
+ id = right.right.decl;
+ }
+ if(id == nil){
+ nerror(n, expconv(right)+" is not a member of "+etconv(left));
+ return (0, 0);
+ }
+ if(id.store != Dconst && id.store != Dtype && id.store != Dtag){
+ if(valistype(left)){
+ nerror(n, expconv(left)+" is not a value");
+ return (0, 0);
+ }
+ }else if(hasside(left, 1))
+ nwarn(left, "result of expression "+etconv(left)+" ignored");
+ if(!typeok && id.store == Dtype){
+ nerror(n, expconv(n)+" is a type, not a value");
+ return (0, 0);
+ }
+ if(id.ty == tunknown){
+ nerror(n, "illegal forward reference to "+expconv(n));
+ return (0, 0);
+ }
+ id.refs++;
+ right.decl = id;
+ n.ty = id.ty = usetype(id.ty);
+ if(id.store == Dglobal)
+ modrefable(id.ty);
+ # function name as function reference
+ if(id.store == Dfn && (par == nil || (par.op != Ocall && par.op != Ofunc)))
+ fnref(n, id);
+ Otagof =>
+ n.ty = tint;
+ t = left.ty;
+ if(t.kind == Tref)
+ t = t.tof;
+ id = nil;
+ case left.op{
+ Oname =>
+ id = left.decl;
+ Odot =>
+ id = left.right.decl;
+ Omdot =>
+ if(left.right.op == Odot)
+ id = left.right.right.decl;
+ }
+ if(id != nil && id.store == Dtag
+ || id != nil && id.store == Dtype && t.kind == Tadt && t.tags != nil)
+ n.decl = id;
+ else if(t.kind == Tadt && t.tags != nil || t.kind == Tadtpick)
+ n.decl = nil;
+ else{
+ nerror(n, "cannot get the tag value for "+etconv(left));
+ return (1, 0);
+ }
+ Oind =>
+ t = left.ty;
+ if(t.kind != Tref || (t.tof.kind != Tadt && t.tof.kind != Tadtpick && t.tof.kind != Ttuple)){
+ nerror(n, "cannot * "+etconv(left));
+ return (0, 0);
+ }
+ n.ty = t.tof;
+ for(tg = t.tof.tags; tg != nil; tg = tg.next)
+ tg.ty.tof = usetype(tg.ty.tof);
+ Oindex =>
+ if(valistype(left)){
+ tagopt = 1;
+ (nil, kidsok) = echeck(right, 2, isglobal, n);
+ tagopt = 0;
+ if(!kidsok)
+ return (0, 0);
+ if((t = exptotype(n)) == nil){
+ nerror(n, expconv(right) + " is not a type list");
+ return (0, 0);
+ }
+ if(!typeok){
+ nerror(n, expconv(left) + " is not a variable");
+ return (0, 0);
+ }
+ *n = *(n.left);
+ n.ty = usetype(t);
+ break;
+ }
+ if(0 && right.op == Oseq){ # a[e1, e2, ...]
+ # array creation to do before we allow this
+ rewind(n);
+ return echeck(n, typeok, isglobal, par);
+ }
+ t = left.ty;
+ (nil, kidsok) = echeck(right, 0, isglobal, n);
+ if(t.kind != Tarray && t != tstring){
+ nerror(n, "cannot index "+etconv(left));
+ return (0, 0);
+ }
+ if(t == tstring){
+ n.op = Oinds;
+ n.ty = tint;
+ }else{
+ n.ty = t.tof;
+ }
+ if(!kidsok){
+ allok = 0;
+ break;
+ }
+ if(right.ty != tint){
+ nerror(n, "cannot index "+etconv(left)+" with "+etconv(right));
+ allok = 0;
+ break;
+ }
+ Oslice =>
+ t = n.ty = left.ty;
+ if(t.kind != Tarray && t != tstring){
+ nerror(n, "cannot slice "+etconv(left)+" with '"+subexpconv(right.left)+":"+subexpconv(right.right)+"'");
+ return (0, 0);
+ }
+ if(right.left.ty != tint && right.left.op != Onothing
+ || right.right.ty != tint && right.right.op != Onothing){
+ nerror(n, "cannot slice "+etconv(left)+" with '"+subexpconv(right.left)+":"+subexpconv(right.right)+"'");
+ return (1, 0);
+ }
+ Olen =>
+ t = left.ty;
+ n.ty = tint;
+ if(t.kind != Tarray && t.kind != Tlist && t != tstring){
+ nerror(n, "len requires an array, string or list in "+etconv(left));
+ return (1, 0);
+ }
+ Ocomp or
+ Onot or
+ Oneg =>
+ n.ty = left.ty;
+usedty(n.ty);
+ case left.ty.kind{
+ Tint =>
+ return (1, allok);
+ Treal or
+ Tfix =>
+ if(n.op == Oneg)
+ return (1, allok);
+ Tbig or
+ Tbyte =>
+ if(n.op == Oneg || n.op == Ocomp)
+ return (1, allok);
+ }
+ nerror(n, "cannot apply "+opconv(n.op)+" to "+etconv(left));
+ return (0, 0);
+ Oinc or
+ Odec or
+ Opreinc or
+ Opredec =>
+ n.ty = left.ty;
+ case left.ty.kind{
+ Tint or
+ Tbig or
+ Tbyte or
+ Treal =>
+ break;
+ * =>
+ nerror(n, "cannot apply "+opconv(n.op)+" to "+etconv(left));
+ return (0, 0);
+ }
+ if(islval(left))
+ break;
+ return(0, 0);
+ Oadd or
+ Odiv or
+ Omul or
+ Osub =>
+ if(mathchk(n, 1))
+ break;
+ return (0, 0);
+ Oexp or
+ Oexpas =>
+ n.ty = left.ty;
+ if(n.ty != tint && n.ty != tbig && n.ty != treal){
+ nerror(n, "exponend " + etconv(left) + " is not int or real");
+ return (0, 0);
+ }
+ if(right.ty != tint){
+ nerror(n, "exponent " + etconv(right) + " is not int");
+ return (0, 0);
+ }
+ if(n.op == Oexpas && !islval(left))
+ return (0, 0);
+ break;
+ # if(mathchk(n, 0)){
+ # if(n.ty != tint){
+ # nerror(n, "exponentiation operands not int");
+ # return (0, 0);
+ # }
+ # break;
+ # }
+ # return (0, 0);
+ Olsh or
+ Orsh =>
+ if(shiftchk(n))
+ break;
+ return (0, 0);
+ Oandand or
+ Ooror =>
+ if(left.ty != tint){
+ nerror(n, opconv(n.op)+"'s left operand is not an int: "+etconv(left));
+ allok = 0;
+ }
+ if(right.ty != tint){
+ nerror(n, opconv(n.op)+"'s right operand is not an int: "+etconv(right));
+ allok = 0;
+ }
+ n.ty = tint;
+ Oand or
+ Omod or
+ Oor or
+ Oxor =>
+ if(mathchk(n, 0))
+ break;
+ return (0, 0);
+ Oaddas or
+ Odivas or
+ Omulas or
+ Osubas =>
+ if(mathchk(n, 1) && islval(left))
+ break;
+ return (0, 0);
+ Olshas or
+ Orshas =>
+ if(shiftchk(n) && islval(left))
+ break;
+ return (0, 0);
+ Oandas or
+ Omodas or
+ Oxoras or
+ Ooras =>
+ if(mathchk(n, 0) && islval(left))
+ break;
+ return (0, 0);
+ Olt or
+ Oleq or
+ Ogt or
+ Ogeq =>
+ if(!mathchk(n, 1))
+ return (0, 0);
+ n.ty = tint;
+ Oeq or
+ Oneq =>
+ case left.ty.kind{
+ Tint or
+ Tbig or
+ Tbyte or
+ Treal or
+ Tstring or
+ Tref or
+ Tlist or
+ Tarray or
+ Tchan or
+ Tany or
+ Tmodule or
+ Tfix or
+ Tpoly =>
+ if(!tcompat(left.ty, right.ty, 0) && !tcompat(right.ty, left.ty, 0))
+ break;
+ t = left.ty;
+ if(t == tany)
+ t = right.ty;
+ if(t == tany)
+ t = tint;
+ if(left.ty == tany)
+ left.ty = t;
+ if(right.ty == tany)
+ right.ty = t;
+ n.ty = tint;
+usedty(n.ty);
+ return (1, allok);
+ }
+ nerror(n, "cannot compare "+etconv(left)+" to "+etconv(right));
+ return (0, 0);
+ Otype =>
+ if(!typeok){
+ nerror(n, expconv(n) + " is not a variable");
+ return (0, 0);
+ }
+ n.ty = usetype(n.ty);
+ * =>
+ fatal("unknown op in typecheck: "+opconv(n.op));
+ }
+usedty(n.ty);
+ return (1, allok);
+}
+
+#
+# n is syntactically a call, but n.left is not a fn
+# check if it's the contructor for an adt
+#
+callcast(n: ref Node, kidsok, allok: int): (int, int)
+{
+ id: ref Decl;
+
+ left := n.left;
+ right := n.right;
+ id = nil;
+ case left.op{
+ Oname =>
+ id = left.decl;
+ Omdot =>
+ if(left.right.op == Odot)
+ id = left.right.right.decl;
+ else
+ id = left.right.decl;
+ Odot =>
+ id = left.right.decl;
+ }
+ if(id == nil || (id.store != Dtype && id.store != Dtag && id.ty.kind != Texception)){
+ nerror(left, expconv(left)+" is not a function or type name");
+ return (0, 0);
+ }
+ if(id.store == Dtag)
+ return tagcast(n, left, right, id, kidsok, allok);
+ t := left.ty;
+ n.ty = t;
+ if(!kidsok)
+ return (1, 0);
+
+ if(t.kind == Tref)
+ t = t.tof;
+ tt := mktype(n.src.start, n.src.stop, Ttuple, nil, tuplefields(right));
+ if(t.kind == Tadt && tcompat(t, tt, 1)){
+ if(right == nil)
+ *n = *n.left;
+ return (1, allok);
+ }
+
+ # try an exception with args
+ tt = mktype(n.src.start, n.src.stop, Texception, nil, tuplefields(right));
+ tt.cons = byte 1;
+ if(t.kind == Texception && t.cons == byte 1 && tcompat(t, tt, 1)){
+ if(right == nil)
+ *n = *n.left;
+ return (1, allok);
+ }
+
+ # try a cast
+ if(t.kind != Texception && right != nil && right.right == nil){ # Oseq but single expression
+ right = right.left;
+ n.op = Ocast;
+ n.left = right;
+ n.right = nil;
+ n.ty = mkidtype(n.src, id.sym);
+ return echeck(n, 0, 0, nil);
+ }
+
+ nerror(left, "cannot make a "+expconv(left)+" from '("+subexpconv(right)+")'");
+ return (0, 0);
+}
+
+tagcast(n, left, right: ref Node, id: ref Decl, kidsok, allok: int): (int, int)
+{
+ left.ty = id.ty;
+ if(left.op == Omdot)
+ left.right.ty = id.ty;
+ n.ty = id.ty;
+ if(!kidsok)
+ return (1, 0);
+ id.ty.tof = usetype(id.ty.tof);
+ if(right != nil)
+ right.ty = id.ty.tof;
+ tt := mktype(n.src.start, n.src.stop, Ttuple, nil, mkids(nosrc, nil, tint, tuplefields(right)));
+ tt.ids.store = Dfield;
+ if(tcompat(id.ty.tof, tt, 1))
+ return (1, allok);
+
+ nerror(left, "cannot make a "+expconv(left)+" from '("+subexpconv(right)+")'");
+ return (0, 0);
+}
+
+valistype(n: ref Node): int
+{
+ case n.op{
+ Oname =>
+ if(n.decl.store == Dtype)
+ return 1;
+ Omdot =>
+ return valistype(n.right);
+ }
+ return 0;
+}
+
+islval(n: ref Node): int
+{
+ s := marklval(n);
+ if(s == 1)
+ return 1;
+ if(s == 0)
+ nerror(n, "cannot assign to "+expconv(n));
+ else
+ circlval(n, n);
+ return 0;
+}
+
+#
+# check to see if n is an lval
+#
+marklval(n: ref Node): int
+{
+ if(n == nil)
+ return 0;
+ case n.op{
+ Oname =>
+ return storespace[n.decl.store] && n.ty.kind != Texception; #ZZZZ && n.decl.tagged == nil;
+ Odot =>
+ if(n.right.decl.store != Dfield)
+ return 0;
+ if(n.right.decl.cycle != byte 0 && n.right.decl.cyc == byte 0)
+ return -1;
+ if(n.left.ty.kind != Tref && marklval(n.left) == 0)
+ nwarn(n, "assignment to "+etconv(n)+" ignored");
+ return 1;
+ Omdot =>
+ if(n.right.decl.store == Dglobal)
+ return 1;
+ return 0;
+ Oind =>
+ for(id := n.ty.ids; id != nil; id = id.next)
+ if(id.cycle != byte 0 && id.cyc == byte 0)
+ return -1;
+ return 1;
+ Oslice =>
+ if(n.right.right.op != Onothing || n.ty == tstring)
+ return 0;
+ return 1;
+ Oinds =>
+ #
+ # make sure we don't change a string constant
+ #
+ case n.left.op{
+ Oconst =>
+ return 0;
+ Oname =>
+ return storespace[n.left.decl.store];
+ Odot or
+ Omdot =>
+ if(n.left.right.decl != nil)
+ return storespace[n.left.right.decl.store];
+ }
+ return 1;
+ Oindex or
+ Oindx =>
+ return 1;
+ Otuple =>
+ for(nn := n.left; nn != nil; nn = nn.right){
+ s := marklval(nn.left);
+ if(s != 1)
+ return s;
+ }
+ return 1;
+ * =>
+ return 0;
+ }
+ return 0;
+}
+
+#
+# n has a circular field assignment.
+# find it and print an error message.
+#
+circlval(n, lval: ref Node): int
+{
+ if(n == nil)
+ return 0;
+ case n.op{
+ Oname =>
+ break;
+ Odot =>
+ if(n.right.decl.cycle != byte 0 && n.right.decl.cyc == byte 0){
+ nerror(lval, "cannot assign to "+expconv(lval)+" because field '"+n.right.decl.sym.name
+ +"' of "+expconv(n.left)+" could complete a cycle to "+expconv(n.left));
+ return -1;
+ }
+ return 1;
+ Oind =>
+ for(id := n.ty.ids; id != nil; id = id.next){
+ if(id.cycle != byte 0 && id.cyc == byte 0){
+ nerror(lval, "cannot assign to "+expconv(lval)+" because field '"+id.sym.name
+ +"' of "+expconv(n)+" could complete a cycle to "+expconv(n));
+ return -1;
+ }
+ }
+ return 1;
+ Oslice =>
+ if(n.right.right.op != Onothing || n.ty == tstring)
+ return 0;
+ return 1;
+ Oindex or
+ Oinds or
+ Oindx =>
+ return 1;
+ Otuple =>
+ for(nn := n.left; nn != nil; nn = nn.right){
+ s := circlval(nn.left, lval);
+ if(s != 1)
+ return s;
+ }
+ return 1;
+ * =>
+ return 0;
+ }
+ return 0;
+}
+
+mathchk(n: ref Node, realok: int): int
+{
+ lt := n.left.ty;
+ rt := n.right.ty;
+ if(rt != lt && !tequal(lt, rt)){
+ nerror(n, "type clash in "+etconv(n.left)+" "+opconv(n.op)+" "+etconv(n.right));
+ return 0;
+ }
+ n.ty = rt;
+ case rt.kind{
+ Tint or
+ Tbig or
+ Tbyte =>
+ return 1;
+ Tstring =>
+ case n.op{
+ Oadd or
+ Oaddas or
+ Ogt or
+ Ogeq or
+ Olt or
+ Oleq =>
+ return 1;
+ }
+ Treal or
+ Tfix =>
+ if(realok)
+ return 1;
+ }
+ nerror(n, "cannot "+opconv(n.op)+" "+etconv(n.left)+" and "+etconv(n.right));
+ return 0;
+}
+
+shiftchk(n: ref Node): int
+{
+ right := n.right;
+ left := n.left;
+ n.ty = left.ty;
+ case n.ty.kind{
+ Tint or
+ Tbyte or
+ Tbig =>
+ if(right.ty.kind != Tint){
+ nerror(n, "shift "+etconv(right)+" is not an int");
+ return 0;
+ }
+ return 1;
+ }
+ nerror(n, "cannot "+opconv(n.op)+" "+etconv(left)+" by "+etconv(right));
+ return 0;
+}
+
+#
+# check for any tany's in t
+#
+specific(t: ref Type): int
+{
+ if(t == nil)
+ return 0;
+ case t.kind{
+ Terror or
+ Tnone or
+ Tint or
+ Tbig or
+ Tstring or
+ Tbyte or
+ Treal or
+ Tfn or
+ Tadt or
+ Tadtpick or
+ Tmodule or
+ Tfix =>
+ return 1;
+ Tany =>
+ return 0;
+ Tpoly =>
+ return 1;
+ Tref or
+ Tlist or
+ Tarray or
+ Tchan =>
+ return specific(t.tof);
+ Ttuple or
+ Texception =>
+ for(d := t.ids; d != nil; d = d.next)
+ if(!specific(d.ty))
+ return 0;
+ return 1;
+ }
+ fatal("unknown type in specific: "+typeconv(t));
+ return 0;
+}
+
+#
+# infer the type of all variable in n from t
+# n is the left-hand exp of a := exp
+#
+declasinfer(n: ref Node, t: ref Type): int
+{
+ if(t.kind == Texception){
+ if(int t.cons)
+ return 0;
+ t = mkextuptype(t);
+ }
+ case n.op{
+ Otuple =>
+ if(t.kind != Ttuple && t.kind != Tadt && t.kind != Tadtpick)
+ return 0;
+ ok := 1;
+ n.ty = t;
+ n = n.left;
+ ids := t.ids;
+ if(t.kind == Tadtpick)
+ ids = t.tof.ids.next;
+ for(; n != nil && ids != nil; ids = ids.next){
+ if(ids.store != Dfield)
+ continue;
+ ok &= declasinfer(n.left, ids.ty);
+ n = n.right;
+ }
+ for(; ids != nil; ids = ids.next)
+ if(ids.store == Dfield)
+ break;
+ if(n != nil || ids != nil)
+ return 0;
+ return ok;
+ Oname =>
+ topvartype(t, n.decl, 0, 0);
+ if(n.decl == nildecl)
+ return 1;
+ n.decl.ty = t;
+ n.ty = t;
+ shareloc(n.decl);
+ return 1;
+ }
+ fatal("unknown op in declasinfer: "+nodeconv(n));
+ return 0;
+}
+
+#
+# an error occured in declaring n;
+# set all decl identifiers to Dwundef
+# so further errors are squashed.
+#
+declaserr(n: ref Node)
+{
+ case n.op{
+ Otuple =>
+ for(n = n.left; n != nil; n = n.right)
+ declaserr(n.left);
+ return;
+ Oname =>
+ if(n.decl != nildecl)
+ n.decl.store = Dwundef;
+ return;
+ }
+ fatal("unknown op in declaserr: "+nodeconv(n));
+}
+
+argcompat(n: ref Node, f: ref Decl, a: ref Node): int
+{
+ for(; a != nil; a = a.right){
+ if(f == nil){
+ nerror(n, expconv(n.left)+": too many function arguments");
+ return 0;
+ }
+ if(!tcompat(f.ty, a.left.ty, 0)){
+ nerror(n, expconv(n.left)+": argument type mismatch: expected "+typeconv(f.ty)+" saw "+etconv(a.left));
+ return 0;
+ }
+ if(a.left.ty == tany)
+ a.left.ty = f.ty;
+ f = f.next;
+ }
+ if(f != nil){
+ nerror(n, expconv(n.left)+": too few function arguments");
+ return 0;
+ }
+ return 1;
+}
+
+argncompat(n: ref Node, f: ref Decl, a: ref Node): int
+{
+ for(; a != nil; a = a.right){
+ if(f == nil){
+ nerror(n, expconv(n.left)+": too many function arguments");
+ return 0;
+ }
+ f = f.next;
+ }
+ if(f != nil){
+ nerror(n, expconv(n.left)+": too few function arguments");
+ return 0;
+ }
+ return 1;
+}
+
+#
+# fn is Odot(adt, methid)
+# pass adt implicitly if needed
+# if not, any side effect of adt will be ingored
+#
+passimplicit(fname, args: ref Node): ref Node
+{
+ t := fname.ty;
+ if(t.ids == nil || t.ids.implicit == byte 0){
+ if(hasside(fname.left, 1))
+ nwarn(fname, "result of expression "+expconv(fname.left)+" ignored");
+ return args;
+ }
+ n := fname.left;
+ if(n.op == Oname && n.decl.store == Dtype){
+ nerror(n, expconv(n)+" is a type and cannot be a self argument");
+ n = mkn(Onothing, nil, nil);
+ n.src = fname.src;
+ n.ty = t.ids.ty;
+ }
+ args = mkn(Oseq, n, args);
+ args.src = n.src;
+ return args;
+}
+
+mem(t: ref Type, d: ref Decl): int
+{
+ for( ; d != nil; d = d.next)
+ if(d.ty == t) # was if(d.ty == t || tequal(d.ty, t))
+ return 1;
+ return 0;
+}
+
+memp(t: ref Type, f: ref Decl): int
+{
+ return mem(t, f.ty.polys) || mem(t, encpolys(f));
+}
+
+passfns0(src: Src, fun: ref Decl, args0: ref Node, args: ref Node, a: ref Node, tp: ref Tpair, polys: ref Decl): (ref Node, ref Node)
+{
+ id, idt, idf: ref Decl;
+ sym: ref Sym;
+ tt: ref Type;
+ na, mod: ref Node;
+
+ for(idt = polys; idt != nil; idt = idt.next){
+ tt = valtmap(idt.ty, tp);
+ if(tt.kind == Tpoly && fndec != nil && !memp(tt, fndec))
+ error(src.start, "cannot determine the instantiated type of " + typeconv(tt));
+ for(idf = idt.ty.ids; idf != nil; idf = idf.next){
+ sym = idf.sym;
+ (id, mod) = fnlookup(sym, tt);
+ while(id != nil && id.link != nil)
+ id = id.link;
+ if(id == nil) # error flagged already
+ continue;
+ id.refs++;
+ id.inline = byte -1;
+ if(tt.kind == Tmodule){ # mod an actual parameter
+ for(;;){
+ if(args0 != nil && tequal(tt, args0.left.ty)){
+ mod = args0.left;
+ break;
+ }
+ if(args0 != nil)
+ args0 = args0.right;
+ }
+ }
+ if(mod == nil && (dot := lmodule(id)) != nil && !isimpmod(dot.sym))
+ error(src.start, "cannot use " + id.sym.name + " without importing " + id.dot.sym.name + " from a variable");
+
+ n := mkn(Ofnptr, mod, mkdeclname(src, id));
+ n.src = src;
+ n.decl = fun;
+ if(tt.kind == Tpoly)
+ n.flags = byte FNPTRA;
+ else
+ n.flags = byte 0;
+ na = mkn(Oseq, n, nil);
+ if(a == nil)
+ args = na;
+ else
+ a.right = na;
+
+ n = mkn(Ofnptr, mod, mkdeclname(src, id));
+ n.src = src;
+ n.decl = fun;
+ if(tt.kind == Tpoly)
+ n.flags = byte (FNPTRA|FNPTR2);
+ else
+ n.flags = byte FNPTR2;
+ a = na.right = mkn(Oseq, n, nil);
+ }
+ if(args0 != nil)
+ args0 = args0.right;
+ }
+ return (args, a);
+}
+
+passfns(src: Src, fun: ref Decl, left: ref Node, args: ref Node, adtt: ref Type, tp: ref Tpair): ref Node
+{
+ a, args0: ref Node;
+
+ a = nil;
+ args0 = args;
+ if(args != nil)
+ for(a = args; a.right != nil; a = a.right)
+ ;
+ if(ispoly(fun))
+ polys := fun.ty.polys;
+ else
+ polys = left.ty.tof.polys;
+ (args, a) = passfns0(src, fun, args0, args, a, tp, polys);
+ if(adtt != nil){
+ if(ispoly(fun))
+ polys = encpolys(fun);
+ else
+ polys = nil;
+ (args, a) = passfns0(src, fun, args0, args, a, adtt.tmap, polys);
+ }
+ return args;
+}
+
+#
+# check the types for a function with a variable number of arguments
+# last typed argument must be a constant string, and must use the
+# print format for describing arguments.
+#
+mkvarargs(n, args: ref Node): ref Type
+{
+ last: ref Decl;
+
+ nt := copytypeids(n.ty);
+ n.ty = nt;
+ f := n.ty.ids;
+ last = nil;
+ if(f == nil){
+ nerror(n, expconv(n)+"'s type is illegal");
+ return nt;
+ }
+ s := args;
+ for(a := args; a != nil; a = a.right){
+ if(f == nil)
+ break;
+ if(!tcompat(f.ty, a.left.ty, 0)){
+ nerror(n, expconv(n)+": argument type mismatch: expected "+typeconv(f.ty)+" saw "+etconv(a.left));
+ return nt;
+ }
+ if(a.left.ty == tany)
+ a.left.ty = f.ty;
+ last = f;
+ f = f.next;
+ s = a;
+ }
+ if(f != nil){
+ nerror(n, expconv(n)+": too few function arguments");
+ return nt;
+ }
+ s.left = fold(s.left);
+ s = s.left;
+ if(s.ty != tstring || s.op != Oconst){
+ nerror(args, expconv(n)+": format argument "+etconv(s)+" is not a string constant");
+ return nt;
+ }
+ fmtcheck(n, s, a);
+ va := tuplefields(a);
+ if(last == nil)
+ nt.ids = va;
+ else
+ last.next = va;
+ return nt;
+}
+
+#
+# check that a print style format string matches it's arguments
+#
+fmtcheck(f, fmtarg, va: ref Node)
+{
+ fmt := fmtarg.decl.sym;
+ s := fmt.name;
+ ns := 0;
+ while(ns < len s){
+ c := s[ns++];
+ if(c != '%')
+ continue;
+
+ verb := -1;
+ n1 := 0;
+ n2 := 0;
+ dot := 0;
+ flag := 0;
+ flags := "";
+ fmtstart := ns - 1;
+ while(ns < len s && verb < 0){
+ c = s[ns++];
+ case c{
+ * =>
+ nerror(f, expconv(f)+": invalid character "+s[ns-1:ns]+" in format '"+s[fmtstart:ns]+"'");
+ return;
+ '.' =>
+ if(dot){
+ nerror(f, expconv(f)+": invalid format '"+s[fmtstart:ns]+"'");
+ return;
+ }
+ n1 = 1;
+ dot = 1;
+ continue;
+ '*' =>
+ if(!n1)
+ n1 = 1;
+ else if(!n2 && dot)
+ n2 = 1;
+ else{
+ nerror(f, expconv(f)+": invalid format '"+s[fmtstart:ns]+"'");
+ return;
+ }
+ if(va == nil){
+ nerror(f, expconv(f)+": too few arguments for format '"+s[fmtstart:ns]+"'");
+ return;
+ }
+ if(va.left.ty.kind != Tint){
+ nerror(f, expconv(f)+": format '"+s[fmtstart:ns]+"' incompatible with argument "+etconv(va.left));
+ return;
+ }
+ va = va.right;
+ '0' to '9' =>
+ while(ns < len s && s[ns] >= '0' && s[ns] <= '9')
+ ns++;
+ if(!n1)
+ n1 = 1;
+ else if(!n2 && dot)
+ n2 = 1;
+ else{
+ nerror(f, expconv(f)+": invalid format '"+s[fmtstart:ns]+"'");
+ return;
+ }
+ '+' or
+ '-' or
+ '#' or
+ ',' or
+ 'b' or
+ 'u' =>
+ for(i := 0; i < flag; i++){
+ if(flags[i] == c){
+ nerror(f, expconv(f)+": duplicate flag "+s[ns-1:ns]+" in format '"+s[fmtstart:ns]+"'");
+ return;
+ }
+ }
+ flags[flag++] = c;
+ '%' or
+ 'r' =>
+ verb = Tnone;
+ 'H' =>
+ verb = Tany;
+ 'c' =>
+ verb = Tint;
+ 'd' or
+ 'o' or
+ 'x' or
+ 'X' =>
+ verb = Tint;
+ for(i := 0; i < flag; i++){
+ if(flags[i] == 'b'){
+ verb = Tbig;
+ break;
+ }
+ }
+ 'e' or
+ 'f' or
+ 'g' or
+ 'E' or
+ 'G' =>
+ verb = Treal;
+ 's' or
+ 'q' =>
+ verb = Tstring;
+ }
+ }
+ if(verb != Tnone){
+ if(verb < 0){
+ nerror(f, expconv(f)+": incomplete format '"+s[fmtstart:ns]+"'");
+ return;
+ }
+ if(va == nil){
+ nerror(f, expconv(f)+": too few arguments for format '"+s[fmtstart:ns]+"'");
+ return;
+ }
+ ty := va.left.ty;
+ if(ty.kind == Texception)
+ ty = mkextuptype(ty);
+ case verb{
+ Tint =>
+ case ty.kind{
+ Tstring or
+ Tarray or
+ Tref or
+ Tchan or
+ Tlist or
+ Tmodule =>
+ if(c == 'x' || c == 'X')
+ verb = ty.kind;
+ }
+ Tany =>
+ if(tattr[ty.kind].isptr)
+ verb = ty.kind;
+ }
+ if(verb != ty.kind){
+ nerror(f, expconv(f)+": format '"+s[fmtstart:ns]+"' incompatible with argument "+etconv(va.left));
+ return;
+ }
+ va = va.right;
+ }
+ }
+ if(va != nil)
+ nerror(f, expconv(f)+": more arguments than formats");
+}
+
+tuplefields(n: ref Node): ref Decl
+{
+ h, last: ref Decl;
+
+ for(; n != nil; n = n.right){
+ d := mkdecl(n.left.src, Dfield, n.left.ty);
+ if(h == nil)
+ h = d;
+ else
+ last.next = d;
+ last = d;
+ }
+ return h;
+}
+
+#
+# make explicit indices for every element in an array initializer
+# return the maximum index
+# sort the indices and check for duplicates
+#
+assignindices(ar: ref Node): int
+{
+ wild, off, q: ref Node;
+
+ amax := 16r7fffffff;
+ size := dupn(0, nosrc, ar.left);
+ if(size.ty == tint){
+ size = fold(size);
+ if(size.op == Oconst)
+ amax = int size.c.val;
+ }
+
+ inits := ar.right;
+ max := -1;
+ last := -1;
+ t := inits.left.ty;
+ wild = nil;
+ nlab := 0;
+ ok := 1;
+ for(n := inits; n != nil; n = n.right){
+ if(!tcompat(t, n.left.ty, 0)){
+ t = tparent(t, n.left.ty);
+ if(!tcompat(t, n.left.ty, 0)){
+ nerror(n.left, "inconsistent types "+typeconv(t)+" and "+typeconv(n.left.ty)+" in array initializer");
+ return -1;
+ }
+ else
+ inits.left.ty = t;
+ }
+ if(t == tany)
+ t = n.left.ty;
+
+ #
+ # make up an index if there isn't one
+ #
+ if(n.left.left == nil)
+ n.left.left = mkn(Oseq, mkconst(n.left.right.src, big(last + 1)), nil);
+
+ for(q = n.left.left; q != nil; q = q.right){
+ off = q.left;
+ if(off.ty != tint){
+ nerror(off, "array index "+etconv(off)+" is not an int");
+ ok = 0;
+ continue;
+ }
+ off = fold(off);
+ case off.op{
+ Owild =>
+ if(wild != nil)
+ nerror(off, "array index * duplicated on line "+lineconv(wild.src.start));
+ wild = off;
+ continue;
+ Orange =>
+ if(off.left.op != Oconst || off.right.op != Oconst){
+ nerror(off, "range "+expconv(off)+" is not constant");
+ off = nil;
+ }else if(off.left.c.val < big 0 || off.right.c.val >= big amax){
+ nerror(off, "array index "+expconv(off)+" out of bounds");
+ off = nil;
+ }else
+ last = int off.right.c.val;
+ Oconst =>
+ last = int off.c.val;
+ if(off.c.val < big 0 || off.c.val >= big amax){
+ nerror(off, "array index "+expconv(off)+" out of bounds");
+ off = nil;
+ }
+ Onothing =>
+ # get here from a syntax error
+ off = nil;
+ * =>
+ nerror(off, "array index "+expconv(off)+" is not constant");
+ off = nil;
+ }
+
+ nlab++;
+ if(off == nil){
+ off = mkconst(n.left.right.src, big(last));
+ ok = 0;
+ }
+ if(last > max)
+ max = last;
+ q.left = off;
+ }
+ }
+
+ #
+ # fix up types of nil elements
+ #
+ for(n = inits; n != nil; n = n.right)
+ if(n.left.ty == tany)
+ n.left.ty = t;
+
+ if(!ok)
+ return -1;
+
+ c := checklabels(inits, tint, nlab, "array index");
+ t = mktype(inits.src.start, inits.src.stop, Tainit, nil, nil);
+ inits.ty = t;
+ t.cse = c;
+
+ return max + 1;
+}
+
+#
+# check the labels of a case statment
+#
+casecheck(cn: ref Node, ret: ref Type)
+{
+ wild: ref Node;
+
+ (rok, nil) := echeck(cn.left, 0, 0, nil);
+ cn.right = scheck(cn.right, ret, Sother);
+ if(!rok)
+ return;
+ arg := cn.left;
+
+ t := arg.ty;
+ if(t != tint && t != tbig && t != tstring){
+ nerror(cn, "case argument "+etconv(arg)+" is not an int or big or string");
+ return;
+ }
+
+ wild = nil;
+ nlab := 0;
+ ok := 1;
+ for(n := cn.right; n != nil; n = n.right){
+ q := n.left.left;
+ if(n.left.right.right == nil)
+ nwarn(q, "no body for case qualifier "+expconv(q));
+ for(; q != nil; q = q.right){
+ left := fold(q.left);
+ q.left = left;
+ case left.op{
+ Owild =>
+ if(wild != nil)
+ nerror(left, "case qualifier * duplicated on line "+lineconv(wild.src.start));
+ wild = left;
+ Orange =>
+ if(left.ty != t)
+ nerror(left, "case qualifier "+etconv(left)+" clashes with "+etconv(arg));
+ else if(left.left.op != Oconst || left.right.op != Oconst){
+ nerror(left, "case range "+expconv(left)+" is not constant");
+ ok = 0;
+ }
+ nlab++;
+ * =>
+ if(left.ty != t){
+ nerror(left, "case qualifier "+etconv(left)+" clashes with "+etconv(arg));
+ ok = 0;
+ }else if(left.op != Oconst){
+ nerror(left, "case qualifier "+expconv(left)+" is not constant");
+ ok = 0;
+ }
+ nlab++;
+ }
+ }
+ }
+
+ if(!ok)
+ return;
+
+ c := checklabels(cn.right, t, nlab, "case qualifier");
+ op := Tcase;
+ if(t == tbig)
+ op = Tcasel;
+ else if(t == tstring)
+ op = Tcasec;
+ t = mktype(cn.src.start, cn.src.stop, op, nil, nil);
+ cn.ty = t;
+ t.cse = c;
+}
+
+#
+# check the labels and bodies of a pick statment
+#
+pickcheck(n: ref Node, ret: ref Type)
+{
+ qs, q, w: ref Node;
+
+ arg := n.left.right;
+ (nil, allok) := echeck(arg, 0, 0, nil);
+ if(!allok)
+ return;
+ t := arg.ty;
+ if(t.kind == Tref)
+ t = t.tof;
+ if(arg.ty.kind != Tref || t.kind != Tadt || t.tags == nil){
+ nerror(arg, "pick argument "+etconv(arg)+" is not a ref adt with pick tags");
+ return;
+ }
+ argty := usetype(mktype(arg.ty.src.start, arg.ty.src.stop, Tref, t, nil));
+
+ arg = n.left.left;
+ pushscope(nil, Sother);
+ dasdecl(arg);
+ arg.decl.ty = argty;
+ arg.ty = argty;
+
+ tags := array[t.decl.tag] of ref Node;
+ w = nil;
+ ok := 1;
+ nlab := 0;
+ for(qs = n.right; qs != nil; qs = qs.right){
+ qt : ref Node = nil;
+ for(q = qs.left.left; q != nil; q = q.right){
+ left := q.left;
+ case left.op{
+ Owild =>
+ # left.ty = tnone;
+ left.ty = t;
+ if(w != nil)
+ nerror(left, "pick qualifier * duplicated on line "+lineconv(w.src.start));
+ w = left;
+ Oname =>
+ id := namedot(t.tags, left.decl.sym);
+ if(id == nil){
+ nerror(left, "pick qualifier "+expconv(left)+" is not a member of "+etconv(arg));
+ ok = 0;
+ continue;
+ }
+
+ left.decl = id;
+ left.ty = id.ty;
+
+ if(tags[id.tag] != nil){
+ nerror(left, "pick qualifier "+expconv(left)+" duplicated on line "+lineconv(tags[id.tag].src.start));
+ ok = 0;
+ }
+ tags[id.tag] = left;
+ nlab++;
+ * =>
+ fatal("pickcheck can't handle "+nodeconv(q));
+ }
+
+ if(qt == nil)
+ qt = left;
+ else if(!tequal(qt.ty, left.ty))
+ nerror(left, "type clash in pick qualifiers "+etconv(qt)+" and "+etconv(left));
+ }
+
+ argty.tof = t;
+ if(qt != nil)
+ argty.tof = qt.ty;
+ qs.left.right = scheck(qs.left.right, ret, Sother);
+ if(qs.left.right == nil)
+ nwarn(qs.left.left, "no body for pick qualifier "+expconv(qs.left.left));
+ }
+ argty.tof = t;
+ for(qs = n.right; qs != nil; qs = qs.right)
+ for(q = qs.left.left; q != nil; q = q.right)
+ q.left = fold(q.left);
+
+ d := popscope();
+ d.refs++;
+ if(d.next != nil)
+ fatal("pickcheck: installing more than one id");
+ fndecls = appdecls(fndecls, d);
+
+ if(!ok)
+ return;
+
+ c := checklabels(n.right, tint, nlab, "pick qualifier");
+ t = mktype(n.src.start, n.src.stop, Tcase, nil, nil);
+ n.ty = t;
+ t.cse = c;
+}
+
+exccheck(en: ref Node, ret: ref Type)
+{
+ ed: ref Decl;
+ wild: ref Node;
+ qt: ref Type;
+
+ pushscope(nil, Sother);
+ if(en.left == nil)
+ en.left = mkdeclname(en.src, mkids(en.src, enter(".ex"+string nexc++, 0), texception, nil));
+ oinexcept := inexcept;
+ inexcept = en.left;
+ dasdecl(en.left);
+ en.left.ty = en.left.decl.ty = texception;
+ ed = en.left.decl;
+ # en.right = scheck(en.right, ret, Sother);
+ t := tstring;
+ wild = nil;
+ nlab := 0;
+ ok := 1;
+ for(n := en.right; n != nil; n = n.right){
+ qt = nil;
+ for(q := n.left.left; q != nil; q = q.right){
+ left := q.left;
+ case left.op{
+ Owild =>
+ left.ty = texception;
+ if(wild != nil)
+ nerror(left, "exception qualifier * duplicated on line "+lineconv(wild.src.start));
+ wild = left;
+ Orange =>
+ left.ty = tnone;
+ nerror(left, "exception qualifier "+expconv(left)+" is illegal");
+ ok = 0;
+ * =>
+ (rok, nil) := echeck(left, 0, 0, nil);
+ if(!rok){
+ ok = 0;
+ break;
+ }
+ left = q.left = fold(left);
+ if(left.ty != t && left.ty.kind != Texception){
+ nerror(left, "exception qualifier "+etconv(left)+" is not a string or exception");
+ ok = 0;
+ }else if(left.op != Oconst){
+ nerror(left, "exception qualifier "+expconv(left)+" is not constant");
+ ok = 0;
+ }
+ else if(left.ty != t)
+ left.ty = mkextype(left.ty);
+ nlab++;
+ }
+
+ if(qt == nil)
+ qt = left.ty;
+ else if(!tequal(qt, left.ty))
+ qt = texception;
+ }
+
+ if(qt != nil)
+ ed.ty = qt;
+ n.left.right = scheck(n.left.right, ret, Sother);
+ if(n.left.right.right == nil)
+ nwarn(n.left.left, "no body for exception qualifier " + expconv(n.left.left));
+ }
+ ed.ty = texception;
+ inexcept = oinexcept;
+ if(!ok)
+ return;
+ c := checklabels(en.right, texception, nlab, "exception qualifier");
+ t = mktype(en.src.start, en.src.stop, Texcept, nil, nil);
+ en.ty = t;
+ t.cse = c;
+ ed = popscope();
+ fndecls = appdecls(fndecls, ed);
+}
+
+#
+# check array and case labels for validity
+#
+checklabels(inits: ref Node, ctype: ref Type, nlab: int, title: string): ref Case
+{
+ n, q, wild: ref Node;
+
+ labs := array[nlab] of Label;
+ i := 0;
+ wild = nil;
+ for(n = inits; n != nil; n = n.right){
+ for(q = n.left.left; q != nil; q = q.right){
+ case q.left.op{
+ Oconst =>
+ labs[i].start = q.left;
+ labs[i].stop = q.left;
+ labs[i++].node = n.left;
+ Orange =>
+ labs[i].start = q.left.left;
+ labs[i].stop = q.left.right;
+ labs[i++].node = n.left;
+ Owild =>
+ wild = n.left;
+ * =>
+ fatal("bogus index in checklabels");
+ }
+ }
+ }
+
+ if(i != nlab)
+ fatal("bad label count: "+string nlab+" then "+string i);
+
+ casesort(ctype, array[nlab] of Label, labs, 0, nlab);
+ for(i = 0; i < nlab; i++){
+ p := labs[i].stop;
+ if(casecmp(ctype, labs[i].start, p) > 0)
+ nerror(labs[i].start, "unmatchable "+title+" "+expconv(labs[i].node));
+ for(e := i + 1; e < nlab; e++){
+ if(casecmp(ctype, labs[e].start, p) <= 0)
+ nerror(labs[e].start, title+" '"+eprintlist(labs[e].node.left, " or ")
+ +"' overlaps with '"+eprintlist(labs[e-1].node.left, " or ")+"' on line "
+ +lineconv(p.src.start));
+
+ #
+ # check for merging case labels
+ #
+ if(ctype != tint
+ || labs[e].start.c.val != p.c.val+big 1
+ || labs[e].node != labs[i].node)
+ break;
+ p = labs[e].stop;
+ }
+ if(e != i + 1){
+ labs[i].stop = p;
+ labs[i+1:] = labs[e:nlab];
+ nlab -= e - (i + 1);
+ }
+ }
+
+ c := ref Case;
+ c.nlab = nlab;
+ c.nsnd = 0;
+ c.labs = labs;
+ c.wild = wild;
+
+ return c;
+}
+
+symcmp(a: ref Sym, b: ref Sym): int
+{
+ if(a.name < b.name)
+ return -1;
+ if(a.name > b.name)
+ return 1;
+ return 0;
+}
+
+matchcmp(na: ref Node, nb: ref Node): int
+{
+ a := na.decl.sym;
+ b := nb.decl.sym;
+ la := len a.name;
+ lb := len b.name;
+ sa := la > 0 && a.name[la-1] == '*';
+ sb := lb > 0 && b.name[lb-1] == '*';
+ if(sa){
+ if(sb){
+ if(la == lb)
+ return symcmp(a, b);
+ return lb-la;
+ }
+ else
+ return 1;
+ }
+ else{
+ if(sb)
+ return -1;
+ else{
+ if(na.ty == tstring){
+ if(nb.ty == tstring)
+ return symcmp(a, b);
+ else
+ return 1;
+ }
+ else{
+ if(nb.ty == tstring)
+ return -1;
+ else
+ return symcmp(a, b);
+ }
+ }
+ }
+}
+
+casecmp(ty: ref Type, a, b: ref Node): int
+{
+ if(ty == tint || ty == tbig){
+ if(a.c.val < b.c.val)
+ return -1;
+ if(a.c.val > b.c.val)
+ return 1;
+ return 0;
+ }
+ if(ty == texception)
+ return matchcmp(a, b);
+ return symcmp(a.decl.sym, b.decl.sym);
+}
+
+casesort(t: ref Type, aux, labs: array of Label, start, stop: int)
+{
+ n := stop - start;
+ if(n <= 1)
+ return;
+ top := mid := start + n / 2;
+
+ casesort(t, aux, labs, start, top);
+ casesort(t, aux, labs, mid, stop);
+
+ #
+ # merge together two sorted label arrays, yielding a sorted array
+ #
+ n = 0;
+ base := start;
+ while(base < top && mid < stop){
+ if(casecmp(t, labs[base].start, labs[mid].start) <= 0)
+ aux[n++] = labs[base++];
+ else
+ aux[n++] = labs[mid++];
+ }
+ if(base < top)
+ aux[n:] = labs[base:top];
+ else if(mid < stop)
+ aux[n:] = labs[mid:stop];
+ labs[start:] = aux[:stop-start];
+}
+
+#
+# binary search for the label corresponding to a given value
+#
+findlab(ty: ref Type, v: ref Node, labs: array of Label, nlab: int): int
+{
+ if(nlab <= 1)
+ return 0;
+ m : int;
+ l := 1;
+ r := nlab - 1;
+ while(l <= r){
+ m = (r + l) / 2;
+ if(casecmp(ty, labs[m].start, v) <= 0)
+ l = m + 1;
+ else
+ r = m - 1;
+ }
+ m = l - 1;
+ if(casecmp(ty, labs[m].start, v) > 0
+ || casecmp(ty, labs[m].stop, v) < 0)
+ fatal("findlab out of range");
+ return m;
+}
+
+altcheck(an: ref Node, ret: ref Type)
+{
+ n, q, left, op, wild: ref Node;
+
+ an.left = scheck(an.left, ret, Sother);
+
+ ok := 1;
+ nsnd := 0;
+ nrcv := 0;
+ wild = nil;
+ for(n = an.left; n != nil; n = n.right){
+ q = n.left.right.left;
+ if(n.left.right.right == nil)
+ nwarn(q, "no body for alt guard "+expconv(q));
+ for(; q != nil; q = q.right){
+ left = q.left;
+ case left.op{
+ Owild =>
+ if(wild != nil)
+ nerror(left, "alt guard * duplicated on line "+lineconv(wild.src.start));
+ wild = left;
+ Orange =>
+ nerror(left, "alt guard "+expconv(left)+" is illegal");
+ ok = 0;
+ * =>
+ op = hascomm(left);
+ if(op == nil){
+ nerror(left, "alt guard "+expconv(left)+" has no communication");
+ ok = 0;
+ break;
+ }
+ if(op.op == Osnd)
+ nsnd++;
+ else
+ nrcv++;
+ }
+ }
+ }
+
+ if(!ok)
+ return;
+
+ c := ref Case;
+ c.nlab = nsnd + nrcv;
+ c.nsnd = nsnd;
+ c.wild = wild;
+
+ an.ty = mktalt(c);
+}
+
+hascomm(n: ref Node): ref Node
+{
+ if(n == nil)
+ return nil;
+ if(n.op == Osnd || n.op == Orcv)
+ return n;
+ r := hascomm(n.left);
+ if(r != nil)
+ return r;
+ return hascomm(n.right);
+}
+
+raisescheck(t: ref Type)
+{
+ if(t.kind != Tfn)
+ return;
+ n := t.eraises;
+ for(nn := n.left; nn != nil; nn = nn.right){
+ (ok, nil) := echeck(nn.left, 0, 0, nil);
+ if(ok && nn.left.ty.kind != Texception)
+ nerror(n, expconv(nn.left) + ": illegal raises expression");
+ }
+}
+
+Elist: adt{
+ d: ref Decl;
+ nxt: cyclic ref Elist;
+};
+
+emerge(el1: ref Elist, el2: ref Elist): ref Elist
+{
+ f: int;
+ el, nxt: ref Elist;
+
+ for( ; el1 != nil; el1 = nxt){
+ f = 0;
+ for(el = el2; el != nil; el = el.nxt){
+ if(el1.d == el.d){
+ f = 1;
+ break;
+ }
+ }
+ nxt = el1.nxt;
+ if(!f){
+ el1.nxt = el2;
+ el2 = el1;
+ }
+ }
+ return el2;
+}
+
+equals(n: ref Node): ref Elist
+{
+ q, nn: ref Node;
+ e, el: ref Elist;
+
+ el = nil;
+ for(q = n.left.left; q != nil; q = q.right){
+ nn = q.left;
+ if(nn.op == Owild)
+ return nil;
+ if(nn.ty.kind != Texception)
+ continue;
+ e = ref Elist(nn.decl, el);
+ el = e;
+ }
+ return el;
+}
+
+caught(d: ref Decl, n: ref Node): int
+{
+ q, nn: ref Node;
+
+ for(n = n.right; n != nil; n = n.right){
+ for(q = n.left.left; q != nil; q = q.right){
+ nn = q.left;
+ if(nn.op == Owild)
+ return 1;
+ if(nn.ty.kind != Texception)
+ continue;
+ if(d == nn.decl)
+ return 1;
+ }
+ }
+ return 0;
+}
+
+raisecheck(n: ref Node, ql: ref Elist): ref Elist
+{
+ exc: int;
+ e: ref Node;
+ el, nel, nxt: ref Elist;
+
+ if(n == nil)
+ return nil;
+ el = nil;
+ for(; n != nil; n = n.right){
+ case(n.op){
+ Oscope =>
+ return raisecheck(n.right, ql);
+ Olabel or
+ Odo =>
+ return raisecheck(n.right, ql);
+ Oif or
+ Ofor =>
+ return emerge(raisecheck(n.right.left, ql),
+ raisecheck(n.right.right, ql));
+ Oalt or
+ Ocase or
+ Opick or
+ Oexcept =>
+ exc = n.op == Oexcept;
+ for(n = n.right; n != nil; n = n.right){
+ ql = nil;
+ if(exc)
+ ql = equals(n);
+ el = emerge(raisecheck(n.left.right, ql), el);
+ }
+ return el;
+ Oseq =>
+ el = emerge(raisecheck(n.left, ql), el);
+ break;
+ Oexstmt =>
+ el = raisecheck(n.left, ql);
+ nel = nil;
+ for( ; el != nil; el = nxt){
+ nxt = el.nxt;
+ if(!caught(el.d, n.right)){
+ el.nxt = nel;
+ nel = el;
+ }
+ }
+ return emerge(nel, raisecheck(n.right, ql));
+ Oraise =>
+ e = n.left;
+ if(e.ty != nil && e.ty.kind == Texception){
+ if(e.ty.cons == byte 0)
+ return ql;
+ if(e.op == Ocall)
+ e = e.left;
+ if(e.op == Omdot)
+ e = e.right;
+ if(e.op != Oname)
+ fatal("exception " + nodeconv(e) + " not a name");
+ el = ref Elist(e.decl, nil);
+ return el;
+ }
+ return nil;
+ * =>
+ return nil;
+ }
+ }
+ return el;
+}
+
+checkraises(n: ref Node)
+{
+ f: int;
+ d: ref Decl;
+ e, el: ref Elist;
+ es, nn: ref Node;
+
+ el = raisecheck(n.right, nil);
+ es = n.ty.eraises;
+ if(es != nil){
+ for(nn = es.left; nn != nil; nn = nn.right){
+ d = nn.left.decl;
+ f = 0;
+ for(e = el; e != nil; e = e.nxt){
+ if(d == e.d){
+ f = 1;
+ e.d = nil;
+ break;
+ }
+ }
+ if(!f)
+ nwarn(n, "function " + expconv(n.left) + " does not raise " + d.sym.name + " but declared");
+ }
+ }
+ for(e = el; e != nil; e = e.nxt)
+ if(e.d != nil)
+ nwarn(n, "function " + expconv(n.left) + " raises " + e.d.sym.name + " but not declared");
+}
+
+# sort all globals in modules now that we've finished with 'last' pointers
+# and before any code generation
+#
+gsort(n: ref Node)
+{
+ for(;;){
+ if(n == nil)
+ return;
+ if(n.op != Oseq)
+ break;
+ gsort(n.left);
+ n = n.right;
+ }
+ if(n.op == Omoddecl && int (n.ty.ok & OKverify)){
+ n.ty.ids = namesort(n.ty.ids);
+ sizeids(n.ty.ids, 0);
+ }
+}
diff --git a/appl/cmd/limbo/types.b b/appl/cmd/limbo/types.b
new file mode 100644
index 00000000..8be8f16d
--- /dev/null
+++ b/appl/cmd/limbo/types.b
@@ -0,0 +1,4234 @@
+
+kindname := array [Tend] of
+{
+ Tnone => "no type",
+ Tadt => "adt",
+ Tadtpick => "adt",
+ Tarray => "array",
+ Tbig => "big",
+ Tbyte => "byte",
+ Tchan => "chan",
+ Treal => "real",
+ Tfn => "fn",
+ Tint => "int",
+ Tlist => "list",
+ Tmodule => "module",
+ Tref => "ref",
+ Tstring => "string",
+ Ttuple => "tuple",
+ Texception => "exception",
+ Tfix => "fixed point",
+ Tpoly => "polymorphic",
+
+ Tainit => "array initializers",
+ Talt => "alt channels",
+ Tany => "polymorphic type",
+ Tarrow => "->",
+ Tcase => "case int labels",
+ Tcasel => "case big labels",
+ Tcasec => "case string labels",
+ Tdot => ".",
+ Terror => "type error",
+ Tgoto => "goto labels",
+ Tid => "id",
+ Tiface => "module interface",
+ Texcept => "exception handler table",
+ Tinst => "instantiated type",
+};
+
+tattr = array[Tend] of
+{
+ # isptr refable conable big vis
+ Tnone => Tattr(0, 0, 0, 0, 0),
+ Tadt => Tattr(0, 1, 1, 1, 1),
+ Tadtpick => Tattr(0, 1, 0, 1, 1),
+ Tarray => Tattr(1, 0, 0, 0, 1),
+ Tbig => Tattr(0, 0, 1, 1, 1),
+ Tbyte => Tattr(0, 0, 1, 0, 1),
+ Tchan => Tattr(1, 0, 0, 0, 1),
+ Treal => Tattr(0, 0, 1, 1, 1),
+ Tfn => Tattr(0, 1, 0, 0, 1),
+ Tint => Tattr(0, 0, 1, 0, 1),
+ Tlist => Tattr(1, 0, 0, 0, 1),
+ Tmodule => Tattr(1, 0, 0, 0, 1),
+ Tref => Tattr(1, 0, 0, 0, 1),
+ Tstring => Tattr(1, 0, 1, 0, 1),
+ Ttuple => Tattr(0, 1, 1, 1, 1),
+ Texception => Tattr(0, 0, 0, 1, 1),
+ Tfix => Tattr(0, 0, 1, 0, 1),
+ Tpoly => Tattr(1, 0, 0, 0, 1),
+
+ Tainit => Tattr(0, 0, 0, 1, 0),
+ Talt => Tattr(0, 0, 0, 1, 0),
+ Tany => Tattr(1, 0, 0, 0, 0),
+ Tarrow => Tattr(0, 0, 0, 0, 1),
+ Tcase => Tattr(0, 0, 0, 1, 0),
+ Tcasel => Tattr(0, 0, 0, 1, 0),
+ Tcasec => Tattr(0, 0, 0, 1, 0),
+ Tdot => Tattr(0, 0, 0, 0, 1),
+ Terror => Tattr(0, 1, 1, 0, 0),
+ Tgoto => Tattr(0, 0, 0, 1, 0),
+ Tid => Tattr(0, 0, 0, 0, 1),
+ Tiface => Tattr(0, 0, 0, 1, 0),
+ Texcept => Tattr(0, 0, 0, 1, 0),
+ Tinst => Tattr(0, 1, 1, 1, 1),
+};
+
+eqclass: array of ref Teq;
+
+ztype: Type;
+eqrec: int;
+eqset: int;
+adts: array of ref Decl;
+nadts: int;
+anontupsym: ref Sym;
+unifysrc: Src;
+
+addtmap(t1: ref Type, t2: ref Type, tph: ref Tpair): ref Tpair
+{
+ tp: ref Tpair;
+
+ tp = ref Tpair;
+ tp.t1 = t1;
+ tp.t2 = t2;
+ tp.nxt = tph;
+ return tp;
+}
+
+valtmap(t: ref Type, tp: ref Tpair): ref Type
+{
+ for( ; tp != nil; tp = tp.nxt)
+ if(tp.t1 == t)
+ return tp.t2;
+ return t;
+}
+
+addtype(t: ref Type, hdl: ref Typelist): ref Typelist
+{
+ tll := ref Typelist;
+ tll.t = t;
+ tll.nxt = nil;
+ if(hdl == nil)
+ return tll;
+ for(p := hdl; p.nxt != nil; p = p.nxt)
+ ;
+ p.nxt = tll;
+ return hdl;
+}
+
+typeinit()
+{
+ anontupsym = enter(".tuple", 0);
+
+ ztype.sbl = -1;
+ ztype.ok = byte 0;
+ ztype.rec = byte 0;
+
+ tbig = mktype(noline, noline, Tbig, nil, nil);
+ tbig.size = IBY2LG;
+ tbig.align = IBY2LG;
+ tbig.ok = OKmask;
+
+ tbyte = mktype(noline, noline, Tbyte, nil, nil);
+ tbyte.size = 1;
+ tbyte.align = 1;
+ tbyte.ok = OKmask;
+
+ tint = mktype(noline, noline, Tint, nil, nil);
+ tint.size = IBY2WD;
+ tint.align = IBY2WD;
+ tint.ok = OKmask;
+
+ treal = mktype(noline, noline, Treal, nil, nil);
+ treal.size = IBY2FT;
+ treal.align = IBY2FT;
+ treal.ok = OKmask;
+
+ tstring = mktype(noline, noline, Tstring, nil, nil);
+ tstring.size = IBY2WD;
+ tstring.align = IBY2WD;
+ tstring.ok = OKmask;
+
+ texception = mktype(noline, noline, Texception, nil, nil);
+ texception.size = IBY2WD;
+ texception.align = IBY2WD;
+ texception.ok = OKmask;
+
+ tany = mktype(noline, noline, Tany, nil, nil);
+ tany.size = IBY2WD;
+ tany.align = IBY2WD;
+ tany.ok = OKmask;
+
+ tnone = mktype(noline, noline, Tnone, nil, nil);
+ tnone.size = 0;
+ tnone.align = 1;
+ tnone.ok = OKmask;
+
+ terror = mktype(noline, noline, Terror, nil, nil);
+ terror.size = 0;
+ terror.align = 1;
+ terror.ok = OKmask;
+
+ tunknown = mktype(noline, noline, Terror, nil, nil);
+ tunknown.size = 0;
+ tunknown.align = 1;
+ tunknown.ok = OKmask;
+
+ tfnptr = mktype(noline, noline, Ttuple, nil, nil);
+ id := tfnptr.ids = mkids(nosrc, nil, tany, nil);
+ id.store = Dfield;
+ id.offset = 0;
+ id.sym = enter("t0", 0);
+ id.src = Src(0, 0);
+ id = tfnptr.ids.next = mkids(nosrc, nil, tint, nil);
+ id.store = Dfield;
+ id.offset = IBY2WD;
+ id.sym = enter("t1", 0);
+ id.src = Src(0, 0);
+
+ rtexception = mktype(noline, noline, Tref, texception, nil);
+ rtexception.size = IBY2WD;
+ rtexception.align = IBY2WD;
+ rtexception.ok = OKmask;
+}
+
+typestart()
+{
+ descriptors = nil;
+ nfns = 0;
+ adts = nil;
+ nadts = 0;
+ selfdecl = nil;
+ if(tfnptr.decl != nil)
+ tfnptr.decl.desc = nil;
+
+ eqclass = array[Tend] of ref Teq;
+
+ typebuiltin(mkids(nosrc, enter("int", 0), nil, nil), tint);
+ typebuiltin(mkids(nosrc, enter("big", 0), nil, nil), tbig);
+ typebuiltin(mkids(nosrc, enter("byte", 0), nil, nil), tbyte);
+ typebuiltin(mkids(nosrc, enter("string", 0), nil, nil), tstring);
+ typebuiltin(mkids(nosrc, enter("real", 0), nil, nil), treal);
+}
+
+modclass(): ref Teq
+{
+ return eqclass[Tmodule];
+}
+
+mktype(start: Line, stop: Line, kind: int, tof: ref Type, args: ref Decl): ref Type
+{
+ t := ref ztype;
+ t.src.start = start;
+ t.src.stop = stop;
+ t.kind = kind;
+ t.tof = tof;
+ t.ids = args;
+ return t;
+}
+
+nalt: int;
+mktalt(c: ref Case): ref Type
+{
+ t := mktype(noline, noline, Talt, nil, nil);
+ t.decl = mkdecl(nosrc, Dtype, t);
+ t.decl.sym = enter(".a"+string nalt++, 0);
+ t.cse = c;
+ return usetype(t);
+}
+
+#
+# copy t and the top level of ids
+#
+copytypeids(t: ref Type): ref Type
+{
+ last: ref Decl;
+
+ nt := ref *t;
+ for(id := t.ids; id != nil; id = id.next){
+ new := ref *id;
+ if(last == nil)
+ nt.ids = new;
+ else
+ last.next = new;
+ last = new;
+ }
+ return nt;
+}
+
+#
+# make each of the ids have type t
+#
+typeids(ids: ref Decl, t: ref Type): ref Decl
+{
+ if(ids == nil)
+ return nil;
+
+ ids.ty = t;
+ for(id := ids.next; id != nil; id = id.next)
+ id.ty = t;
+ return ids;
+}
+
+typebuiltin(d: ref Decl, t: ref Type)
+{
+ d.ty = t;
+ t.decl = d;
+ installids(Dtype, d);
+}
+
+fielddecl(store: int, ids: ref Decl): ref Node
+{
+ n := mkn(Ofielddecl, nil, nil);
+ n.decl = ids;
+ for(; ids != nil; ids = ids.next)
+ ids.store = store;
+ return n;
+}
+
+typedecl(ids: ref Decl, t: ref Type): ref Node
+{
+ if(t.decl == nil)
+ t.decl = ids;
+ n := mkn(Otypedecl, nil, nil);
+ n.decl = ids;
+ n.ty = t;
+ for(; ids != nil; ids = ids.next)
+ ids.ty = t;
+ return n;
+}
+
+typedecled(n: ref Node)
+{
+ installids(Dtype, n.decl);
+}
+
+adtdecl(ids: ref Decl, fields: ref Node): ref Node
+{
+ n := mkn(Oadtdecl, nil, nil);
+ t := mktype(ids.src.start, ids.src.stop, Tadt, nil, nil);
+ n.decl = ids;
+ n.left = fields;
+ n.ty = t;
+ t.decl = ids;
+ for(; ids != nil; ids = ids.next)
+ ids.ty = t;
+ return n;
+}
+
+adtdecled(n: ref Node)
+{
+ d := n.ty.decl;
+ installids(Dtype, d);
+ if(n.ty.polys != nil){
+ pushscope(nil, Sother);
+ installids(Dtype, n.ty.polys);
+ }
+ pushscope(nil, Sother);
+ fielddecled(n.left);
+ n.ty.ids = popscope();
+ if(n.ty.polys != nil)
+ n.ty.polys = popscope();
+ for(ids := n.ty.ids; ids != nil; ids = ids.next)
+ ids.dot = d;
+}
+
+fielddecled(n: ref Node)
+{
+ for(; n != nil; n = n.right){
+ case n.op{
+ Oseq =>
+ fielddecled(n.left);
+ Oadtdecl =>
+ adtdecled(n);
+ return;
+ Otypedecl =>
+ typedecled(n);
+ return;
+ Ofielddecl =>
+ installids(Dfield, n.decl);
+ return;
+ Ocondecl =>
+ condecled(n);
+ gdasdecl(n.right);
+ return;
+ Oexdecl =>
+ exdecled(n);
+ return;
+ Opickdecl =>
+ pickdecled(n);
+ return;
+ * =>
+ fatal("can't deal with "+opname[n.op]+" in fielddecled");
+ }
+ }
+}
+
+pickdecled(n: ref Node): int
+{
+ if(n == nil)
+ return 0;
+ tag := pickdecled(n.left);
+ pushscope(nil, Sother);
+ fielddecled(n.right.right);
+ d := n.right.left.decl;
+ d.ty.ids = popscope();
+ installids(Dtag, d);
+ for(; d != nil; d = d.next)
+ d.tag = tag++;
+ return tag;
+}
+
+#
+# make the tuple type used to initialize adt t
+#
+mkadtcon(t: ref Type): ref Type
+{
+ last: ref Decl;
+
+ nt := ref *t;
+ nt.ids = nil;
+ nt.kind = Ttuple;
+ for(id := t.ids; id != nil; id = id.next){
+ if(id.store != Dfield)
+ continue;
+ new := ref *id;
+ new.cyc = byte 0;
+ if(last == nil)
+ nt.ids = new;
+ else
+ last.next = new;
+ last = new;
+ }
+ last.next = nil;
+ return nt;
+}
+
+#
+# make the tuple type used to initialize t,
+# an adt with pick fields tagged by tg
+#
+mkadtpickcon(t, tgt: ref Type): ref Type
+{
+ last := mkids(tgt.decl.src, nil, tint, nil);
+ last.store = Dfield;
+ nt := mktype(t.src.start, t.src.stop, Ttuple, nil, last);
+ for(id := t.ids; id != nil; id = id.next){
+ if(id.store != Dfield)
+ continue;
+ new := ref *id;
+ new.cyc = byte 0;
+ last.next = new;
+ last = new;
+ }
+ for(id = tgt.ids; id != nil; id = id.next){
+ if(id.store != Dfield)
+ continue;
+ new := ref *id;
+ new.cyc = byte 0;
+ last.next = new;
+ last = new;
+ }
+ last.next = nil;
+ return nt;
+}
+
+#
+# make an identifier type
+#
+mkidtype(src: Src, s: ref Sym): ref Type
+{
+ t := mktype(src.start, src.stop, Tid, nil, nil);
+ if(s.unbound == nil){
+ s.unbound = mkdecl(src, Dunbound, nil);
+ s.unbound.sym = s;
+ }
+ t.decl = s.unbound;
+ return t;
+}
+
+#
+# make a qualified type for t->s
+#
+mkarrowtype(start: Line, stop: Line, t: ref Type, s: ref Sym): ref Type
+{
+ t = mktype(start, stop, Tarrow, t, nil);
+ if(s.unbound == nil){
+ s.unbound = mkdecl(Src(start, stop), Dunbound, nil);
+ s.unbound.sym = s;
+ }
+ t.decl = s.unbound;
+ return t;
+}
+
+#
+# make a qualified type for t.s
+#
+mkdottype(start: Line, stop: Line, t: ref Type, s: ref Sym): ref Type
+{
+ t = mktype(start, stop, Tdot, t, nil);
+ if(s.unbound == nil){
+ s.unbound = mkdecl(Src(start, stop), Dunbound, nil);
+ s.unbound.sym = s;
+ }
+ t.decl = s.unbound;
+ return t;
+}
+
+mkinsttype(src: Src, tt: ref Type, tyl: ref Typelist): ref Type
+{
+ t := mktype(src.start, src.stop, Tinst, tt, nil);
+ t.tlist = tyl;
+ return t;
+}
+
+#
+# look up the name f in the fields of a module, adt, or tuple
+#
+namedot(ids: ref Decl, s: ref Sym): ref Decl
+{
+ for(; ids != nil; ids = ids.next)
+ if(ids.sym == s)
+ return ids;
+ return nil;
+}
+
+#
+# complete the declaration of an adt
+# methods frames get sized in module definition or during function definition
+# place the methods at the end of the field list
+#
+adtdefd(t: ref Type)
+{
+ next, aux, store, auxhd, tagnext: ref Decl;
+
+ if(debug['x'])
+ print("adt %s defd\n", typeconv(t));
+ d := t.decl;
+ tagnext = nil;
+ store = nil;
+ for(id := t.polys; id != nil; id = id.next){
+ id.store = Dtype;
+ id.ty = verifytypes(id.ty, d, nil);
+ }
+ for(id = t.ids; id != nil; id = next){
+ if(id.store == Dtag){
+ if(t.tags != nil)
+ error(id.src.start, "only one set of pick fields allowed");
+ tagnext = pickdefd(t, id);
+ next = tagnext;
+ if(store != nil)
+ store.next = next;
+ else
+ t.ids = next;
+ continue;
+ }else{
+ id.dot = d;
+ next = id.next;
+ store = id;
+ }
+ }
+ aux = nil;
+ store = nil;
+ auxhd = nil;
+ seentags := 0;
+ for(id = t.ids; id != nil; id = next){
+ if(id == tagnext)
+ seentags = 1;
+
+ next = id.next;
+ id.dot = d;
+ id.ty = topvartype(verifytypes(id.ty, d, nil), id, 1, 1);
+ if(id.store == Dfield && id.ty.kind == Tfn)
+ id.store = Dfn;
+ if(id.store == Dfn || id.store == Dconst){
+ if(store != nil)
+ store.next = next;
+ else
+ t.ids = next;
+ if(aux != nil)
+ aux.next = id;
+ else
+ auxhd = id;
+ aux = id;
+ }else{
+ if(seentags)
+ error(id.src.start, "pick fields must be the last data fields in an adt");
+ store = id;
+ }
+ }
+ if(aux != nil)
+ aux.next = nil;
+ if(store != nil)
+ store.next = auxhd;
+ else
+ t.ids = auxhd;
+
+ for(id = t.tags; id != nil; id = id.next){
+ id.ty = verifytypes(id.ty, d, nil);
+ if(id.ty.tof == nil)
+ id.ty.tof = mkadtpickcon(t, id.ty);
+ }
+}
+
+#
+# assemble the data structure for an adt with a pick clause.
+# since the scoping rules for adt pick fields are strange,
+# we have a cutomized check for overlapping defitions.
+#
+pickdefd(t: ref Type, tg: ref Decl): ref Decl
+{
+ lasttg : ref Decl = nil;
+ d := t.decl;
+ t.tags = tg;
+ tag := 0;
+ while(tg != nil){
+ tt := tg.ty;
+ if(tt.kind != Tadtpick || tg.tag != tag)
+ break;
+ tt.decl = tg;
+ lasttg = tg;
+ for(; tg != nil; tg = tg.next){
+ if(tg.ty != tt)
+ break;
+ tag++;
+ lasttg = tg;
+ tg.dot = d;
+ }
+ for(id := tt.ids; id != nil; id = id.next){
+ xid := namedot(t.ids, id.sym);
+ if(xid != nil)
+ error(id.src.start, "redeclaration of "+declconv(id)+
+ " previously declared as "+storeconv(xid)+" on line "+lineconv(xid.src.start));
+ id.dot = d;
+ }
+ }
+ if(lasttg == nil){
+ error(t.src.start, "empty pick field declaration in "+typeconv(t));
+ t.tags = nil;
+ }else
+ lasttg.next = nil;
+ d.tag = tag;
+ return tg;
+}
+
+moddecl(ids: ref Decl, fields: ref Node): ref Node
+{
+ n := mkn(Omoddecl, mkn(Oseq, nil, nil), nil);
+ t := mktype(ids.src.start, ids.src.stop, Tmodule, nil, nil);
+ n.decl = ids;
+ n.left = fields;
+ n.ty = t;
+ return n;
+}
+
+moddecled(n: ref Node)
+{
+ d := n.decl;
+ installids(Dtype, d);
+ isimp := 0;
+ for(ids := d; ids != nil; ids = ids.next){
+ for(im := impmods; im != nil; im = im.next){
+ if(ids.sym == im.sym){
+ isimp = 1;
+ d = ids;
+ dm := ref Dlist;
+ dm.d = ids;
+ dm.next = nil;
+ if(impdecls == nil)
+ impdecls = dm;
+ else{
+ for(dl := impdecls; dl.next != nil; dl = dl.next)
+ ;
+ dl.next = dm;
+ }
+ }
+ }
+ ids.ty = n.ty;
+ }
+ pushscope(nil, Sother);
+ fielddecled(n.left);
+
+ d.ty.ids = popscope();
+
+ #
+ # make the current module the . parent of all contained decls.
+ #
+ for(ids = d.ty.ids; ids != nil; ids = ids.next)
+ ids.dot = d;
+
+ t := d.ty;
+ t.decl = d;
+ if(debug['m'])
+ print("declare module %s\n", d.sym.name);
+
+ #
+ # add the iface declaration in case it's needed later
+ #
+ installids(Dglobal, mkids(d.src, enter(".m."+d.sym.name, 0), tnone, nil));
+
+ if(isimp){
+ for(ids = d.ty.ids; ids != nil; ids = ids.next){
+ s := ids.sym;
+ if(s.decl != nil && s.decl.scope >= scope){
+ dot := s.decl.dot;
+ if(s.decl.store != Dwundef && dot != nil && dot != d && isimpmod(dot.sym) && dequal(ids, s.decl, 0))
+ continue;
+ redecl(ids);
+ ids.old = s.decl.old;
+ }else
+ ids.old = s.decl;
+ s.decl = ids;
+ ids.scope = scope;
+ }
+ }
+}
+
+#
+# for each module in id,
+# link by field ext all of the decls for
+# functions needed in external linkage table
+# collect globals and make a tuple for all of them
+#
+mkiface(m: ref Decl): ref Type
+{
+ iface := last := ref Decl;
+ globals := glast := mkdecl(m.src, Dglobal, mktype(m.src.start, m.src.stop, Tadt, nil, nil));
+ for(id := m.ty.ids; id != nil; id = id.next){
+ case id.store{
+ Dglobal =>
+ glast = glast.next = dupdecl(id);
+ id.iface = globals;
+ glast.iface = id;
+ Dfn =>
+ id.iface = last = last.next = dupdecl(id);
+ last.iface = id;
+ Dtype =>
+ if(id.ty.kind != Tadt)
+ break;
+ for(d := id.ty.ids; d != nil; d = d.next){
+ if(d.store == Dfn){
+ d.iface = last = last.next = dupdecl(d);
+ last.iface = d;
+ }
+ }
+ }
+ }
+ last.next = nil;
+ iface = namesort(iface.next);
+
+ if(globals.next != nil){
+ glast.next = nil;
+ globals.ty.ids = namesort(globals.next);
+ globals.ty.decl = globals;
+ globals.sym = enter(".mp", 0);
+ globals.dot = m;
+ globals.next = iface;
+ iface = globals;
+ }
+
+ #
+ # make the interface type and install an identifier for it
+ # the iface has a ref count if it is loaded
+ #
+ t := mktype(m.src.start, m.src.stop, Tiface, nil, iface);
+ id = enter(".m."+m.sym.name, 0).decl;
+ t.decl = id;
+ id.ty = t;
+
+ #
+ # dummy node so the interface is initialized
+ #
+ id.init = mkn(Onothing, nil, nil);
+ id.init.ty = t;
+ id.init.decl = id;
+ return t;
+}
+
+joiniface(mt, t: ref Type)
+{
+ iface := t.ids;
+ globals := iface;
+ if(iface != nil && iface.store == Dglobal)
+ iface = iface.next;
+ for(id := mt.tof.ids; id != nil; id = id.next){
+ case id.store{
+ Dglobal =>
+ for(d := id.ty.ids; d != nil; d = d.next)
+ d.iface.iface = globals;
+ Dfn =>
+ id.iface.iface = iface;
+ iface = iface.next;
+ * =>
+ fatal("unknown store "+storeconv(id)+" in joiniface");
+ }
+ }
+ if(iface != nil)
+ fatal("join iface not matched");
+ mt.tof = t;
+}
+
+addiface(m: ref Decl, d: ref Decl)
+{
+ t: ref Type;
+ id, last, dd, lastorig: ref Decl;
+
+ if(d == nil || !local(d))
+ return;
+ modrefable(d.ty);
+ if(m == nil){
+ if(impdecls.next != nil)
+ for(dl := impdecls; dl != nil; dl = dl.next)
+ if(dl.d.ty.tof != impdecl.ty.tof) # impdecl last
+ addiface(dl.d, d);
+ addiface(impdecl, d);
+ return;
+ }
+ t = m.ty.tof;
+ last = nil;
+ lastorig = nil;
+ for(id = t.ids; id != nil; id = id.next){
+ if(d == id || d == id.iface)
+ return;
+ last = id;
+ if(id.tag == 0)
+ lastorig = id;
+ }
+ dd = dupdecl(d);
+ if(d.dot == nil)
+ d.dot = dd.dot = m;
+ d.iface = dd;
+ dd.iface = d;
+ if(last == nil)
+ t.ids = dd;
+ else
+ last.next = dd;
+ dd.tag = 1; # mark so not signed
+ if(lastorig == nil)
+ t.ids = namesort(t.ids);
+ else
+ lastorig.next = namesort(lastorig.next);
+}
+
+#
+# eliminate unused declarations from interfaces
+# label offset within interface
+#
+narrowmods()
+{
+ id: ref Decl;
+ for(eq := modclass(); eq != nil; eq = eq.eq){
+ t := eq.ty.tof;
+
+ if(t.linkall == byte 0){
+ last : ref Decl = nil;
+ for(id = t.ids; id != nil; id = id.next){
+ if(id.refs == 0){
+ if(last == nil)
+ t.ids = id.next;
+ else
+ last.next = id.next;
+ }else
+ last = id;
+ }
+
+ #
+ # need to resize smaller interfaces
+ #
+ resizetype(t);
+ }
+
+ offset := 0;
+ for(id = t.ids; id != nil; id = id.next)
+ id.offset = offset++;
+
+ #
+ # rathole to stuff number of entries in interface
+ #
+ t.decl.init.c = ref Const;
+ t.decl.init.c.val = big offset;
+ }
+}
+
+#
+# check to see if any data field of module m if referenced.
+# if so, mark all data in m
+#
+moddataref()
+{
+ for(eq := modclass(); eq != nil; eq = eq.eq){
+ id := eq.ty.tof.ids;
+ if(id != nil && id.store == Dglobal && id.refs)
+ for(id = eq.ty.ids; id != nil; id = id.next)
+ if(id.store == Dglobal)
+ modrefable(id.ty);
+ }
+}
+
+#
+# move the global declarations in interface to the front
+#
+modglobals(mod, globals: ref Decl): ref Decl
+{
+ #
+ # make a copy of all the global declarations
+ # used for making a type descriptor for globals ONLY
+ # note we now have two declarations for the same variables,
+ # which is apt to cause problems if code changes
+ #
+ # here we fix up the offsets for the real declarations
+ #
+ idoffsets(mod.ty.ids, 0, 1);
+
+ last := head := ref Decl;
+ for(id := mod.ty.ids; id != nil; id = id.next)
+ if(id.store == Dglobal)
+ last = last.next = dupdecl(id);
+
+ last.next = globals;
+ return head.next;
+}
+
+#
+# snap all id type names to the actual type
+# check that all types are completely defined
+# verify that the types look ok
+#
+validtype(t: ref Type, inadt: ref Decl): ref Type
+{
+ if(t == nil)
+ return t;
+ bindtypes(t);
+ t = verifytypes(t, inadt, nil);
+ cycsizetype(t);
+ teqclass(t);
+ return t;
+}
+
+usetype(t: ref Type): ref Type
+{
+ if(t == nil)
+ return t;
+ t = validtype(t, nil);
+ reftype(t);
+ return t;
+}
+
+internaltype(t: ref Type): ref Type
+{
+ bindtypes(t);
+ t.ok = OKverify;
+ sizetype(t);
+ t.ok = OKmask;
+ return t;
+}
+
+#
+# checks that t is a valid top-level type
+#
+topvartype(t: ref Type, id: ref Decl, tyok: int, polyok: int): ref Type
+{
+ if(t.kind == Tadt && t.tags != nil || t.kind == Tadtpick)
+ error(id.src.start, "cannot declare "+id.sym.name+" with type "+typeconv(t));
+ if(!tyok && t.kind == Tfn)
+ error(id.src.start, "cannot declare "+id.sym.name+" to be a function");
+ if(!polyok && (t.kind == Tadt || t.kind == Tadtpick) && ispolyadt(t))
+ error(id.src.start, "cannot declare " + id.sym.name + " of a polymorphic type");
+ return t;
+}
+
+toptype(src: Src, t: ref Type): ref Type
+{
+ if(t.kind == Tadt && t.tags != nil || t.kind == Tadtpick)
+ error(src.start, typeconv(t)+", an adt with pick fields, must be used with ref");
+ if(t.kind == Tfn)
+ error(src.start, "data cannot have a fn type like "+typeconv(t));
+ return t;
+}
+
+comtype(src: Src, t: ref Type, adtd: ref Decl): ref Type
+{
+ if(adtd == nil && (t.kind == Tadt || t.kind == Tadtpick) && ispolyadt(t))
+ error(src.start, "polymorphic type " + typeconv(t) + " illegal here");
+ return t;
+}
+
+usedty(t: ref Type)
+{
+ if(t != nil && (t.ok | OKmodref) != OKmask)
+ fatal("used ty " + stypeconv(t) + " " + hex(int t.ok, 2));
+}
+
+bindtypes(t: ref Type)
+{
+ id: ref Decl;
+
+ if(t == nil)
+ return;
+ if((t.ok & OKbind) == OKbind)
+ return;
+ t.ok |= OKbind;
+ case t.kind{
+ Tadt =>
+ if(t.polys != nil){
+ pushscope(nil, Sother);
+ installids(Dtype, t.polys);
+ }
+ if(t.val != nil)
+ mergepolydecs(t);
+ if(t.polys != nil){
+ popscope();
+ for(id = t.polys; id != nil; id = id.next)
+ bindtypes(id.ty);
+ }
+ Tadtpick or
+ Tmodule or
+ Terror or
+ Tint or
+ Tbig or
+ Tstring or
+ Treal or
+ Tbyte or
+ Tnone or
+ Tany or
+ Tiface or
+ Tainit or
+ Talt or
+ Tcase or
+ Tcasel or
+ Tcasec or
+ Tgoto or
+ Texcept or
+ Tfix or
+ Tpoly =>
+ break;
+ Tarray or
+ Tarrow or
+ Tchan or
+ Tdot or
+ Tlist or
+ Tref =>
+ bindtypes(t.tof);
+ Tid =>
+ id = t.decl.sym.decl;
+ if(id == nil)
+ id = undefed(t.src, t.decl.sym);
+ # save a little space
+ id.sym.unbound = nil;
+ t.decl = id;
+ Ttuple or
+ Texception =>
+ for(id = t.ids; id != nil; id = id.next)
+ bindtypes(id.ty);
+ Tfn =>
+ if(t.polys != nil){
+ pushscope(nil, Sother);
+ installids(Dtype, t.polys);
+ }
+ for(id = t.ids; id != nil; id = id.next)
+ bindtypes(id.ty);
+ bindtypes(t.tof);
+ if(t.val != nil)
+ mergepolydecs(t);
+ if(t.polys != nil){
+ popscope();
+ for(id = t.polys; id != nil; id = id.next)
+ bindtypes(id.ty);
+ }
+ Tinst =>
+ bindtypes(t.tof);
+ for(tyl := t.tlist; tyl != nil; tyl = tyl.nxt)
+ bindtypes(tyl.t);
+ * =>
+ fatal("bindtypes: unknown type kind "+string t.kind);
+ }
+}
+
+#
+# walk the type checking for validity
+#
+verifytypes(t: ref Type, adtt: ref Decl, poly: ref Decl): ref Type
+{
+ id: ref Decl;
+
+ if(t == nil)
+ return nil;
+ if((t.ok & OKverify) == OKverify)
+ return t;
+ t.ok |= OKverify;
+if((t.ok & (OKverify|OKbind)) != (OKverify|OKbind))
+fatal("verifytypes bogus ok for " + stypeconv(t));
+ cyc := t.flags&CYCLIC;
+ case t.kind{
+ Terror or
+ Tint or
+ Tbig or
+ Tstring or
+ Treal or
+ Tbyte or
+ Tnone or
+ Tany or
+ Tiface or
+ Tainit or
+ Talt or
+ Tcase or
+ Tcasel or
+ Tcasec or
+ Tgoto or
+ Texcept =>
+ break;
+ Tfix =>
+ n := t.val;
+ ok: int;
+ max := 0.0;
+ if(n.op == Oseq){
+ (ok, nil) = echeck(n.left, 0, 0, n);
+ (ok1, nil) := echeck(n.right, 0, 0, n);
+ if(!ok || !ok1)
+ return terror;
+ if(n.left.ty != treal || n.right.ty != treal){
+ error(t.src.start, "fixed point scale/maximum not real");
+ return terror;
+ }
+ n.right = fold(n.right);
+ if(n.right.op != Oconst){
+ error(t.src.start, "fixed point maximum not constant");
+ return terror;
+ }
+ if((max = n.right.c.rval) <= 0.0){
+ error(t.src.start, "non-positive fixed point maximum");
+ return terror;
+ }
+ n = n.left;
+ }
+ else{
+ (ok, nil) = echeck(n, 0, 0, nil);
+ if(!ok)
+ return terror;
+ if(n.ty != treal){
+ error(t.src.start, "fixed point scale not real");
+ return terror;
+ }
+ }
+ n = t.val = fold(n);
+ if(n.op != Oconst){
+ error(t.src.start, "fixed point scale not constant");
+ return terror;
+ }
+ if(n.c.rval <= 0.0){
+ error(t.src.start, "non-positive fixed point scale");
+ return terror;
+ }
+ ckfix(t, max);
+ Tref =>
+ t.tof = comtype(t.src, verifytypes(t.tof, adtt, nil), adtt);
+ if(t.tof != nil && !tattr[t.tof.kind].refable){
+ error(t.src.start, "cannot have a ref " + typeconv(t.tof));
+ return terror;
+ }
+ if(0 && t.tof.kind == Tfn && t.tof.ids != nil && int t.tof.ids.implicit)
+ error(t.src.start, "function references cannot have a self argument");
+ if(0 && t.tof.kind == Tfn && t.polys != nil)
+ error(t.src.start, "function references cannot be polymorphic");
+ Tchan or
+ Tarray or
+ Tlist =>
+ t.tof = comtype(t.src, toptype(t.src, verifytypes(t.tof, adtt, nil)), adtt);
+ Tid =>
+ t.ok &= ~OKverify;
+ t = verifytypes(idtype(t), adtt, nil);
+ Tarrow =>
+ t.ok &= ~OKverify;
+ t = verifytypes(arrowtype(t, adtt), adtt, nil);
+ Tdot =>
+ #
+ # verify the parent adt & lookup the tag fields
+ #
+ t.ok &= ~OKverify;
+ t = verifytypes(dottype(t, adtt), adtt, nil);
+ Tadt =>
+ #
+ # this is where Tadt may get tag fields added
+ #
+ adtdefd(t);
+ Tadtpick =>
+ for(id = t.ids; id != nil; id = id.next){
+ id.ty = topvartype(verifytypes(id.ty, id.dot, nil), id, 0, 1);
+ if(id.store == Dconst)
+ error(t.src.start, "cannot declare a con like "+id.sym.name+" within a pick");
+ }
+ verifytypes(t.decl.dot.ty, nil, nil);
+ Tmodule =>
+ for(id = t.ids; id != nil; id = id.next){
+ id.ty = verifytypes(id.ty, nil, nil);
+ if(id.store == Dglobal && id.ty.kind == Tfn)
+ id.store = Dfn;
+ if(id.store != Dtype && id.store != Dfn)
+ topvartype(id.ty, id, 0, 0);
+ }
+ Ttuple or
+ Texception =>
+ if(t.decl == nil){
+ t.decl = mkdecl(t.src, Dtype, t);
+ t.decl.sym = anontupsym;
+ }
+ i := 0;
+ for(id = t.ids; id != nil; id = id.next){
+ id.store = Dfield;
+ if(id.sym == nil)
+ id.sym = enter("t"+string i, 0);
+ i++;
+ id.ty = toptype(id.src, verifytypes(id.ty, adtt, nil));
+ }
+ Tfn =>
+ last : ref Decl = nil;
+ for(id = t.ids; id != nil; id = id.next){
+ id.store = Darg;
+ id.ty = topvartype(verifytypes(id.ty, adtt, nil), id, 0, 1);
+ if(id.implicit != byte 0){
+ if(poly != nil)
+ selfd := poly;
+ else
+ selfd = adtt;
+ if(selfd == nil)
+ error(t.src.start, "function is not a member of an adt, so can't use self");
+ else if(id != t.ids)
+ error(id.src.start, "only the first argument can use self");
+ else if(id.ty != selfd.ty && (id.ty.kind != Tref || id.ty.tof != selfd.ty))
+ error(id.src.start, "self argument's type must be "+selfd.sym.name+" or ref "+selfd.sym.name);
+ }
+ last = id;
+ }
+ for(id = t.polys; id != nil; id = id.next){
+ if(adtt != nil){
+ for(id1 := adtt.ty.polys; id1 != nil; id1 = id1.next){
+ if(id1.sym == id.sym)
+ id.ty = id1.ty;
+ }
+ }
+ id.store = Dtype;
+ id.ty = verifytypes(id.ty, adtt, nil);
+ }
+ t.tof = comtype(t.src, toptype(t.src, verifytypes(t.tof, adtt, nil)), adtt);
+ if(t.varargs != byte 0 && (last == nil || last.ty != tstring))
+ error(t.src.start, "variable arguments must be preceded by a string");
+ if(t.varargs != byte 0 && t.polys != nil)
+ error(t.src.start, "polymorphic functions must not have variable arguments");
+ Tpoly =>
+ for(id = t.ids; id != nil; id = id.next){
+ id.store = Dfn;
+ id.ty = verifytypes(id.ty, adtt, t.decl);
+ }
+ Tinst =>
+ t.ok &= ~OKverify;
+ t.tof = verifytypes(t.tof, adtt, nil);
+ for(tyl := t.tlist; tyl != nil; tyl = tyl.nxt)
+ tyl.t = verifytypes(tyl.t, adtt, nil);
+ (t, nil) = insttype(t, adtt, nil);
+ t = verifytypes(t, adtt, nil);
+ * =>
+ fatal("verifytypes: unknown type kind "+string t.kind);
+ }
+ if(int cyc)
+ t.flags |= CYCLIC;
+ return t;
+}
+
+#
+# resolve an id type
+#
+idtype(t: ref Type): ref Type
+{
+ id := t.decl;
+ if(id.store == Dunbound)
+ fatal("idtype: unbound decl");
+ tt := id.ty;
+ if(id.store != Dtype && id.store != Dtag){
+ if(id.store == Dundef){
+ id.store = Dwundef;
+ error(t.src.start, id.sym.name+" is not declared");
+ }else if(id.store == Dimport){
+ id.store = Dwundef;
+ error(t.src.start, id.sym.name+"'s type cannot be determined");
+ }else if(id.store != Dwundef)
+ error(t.src.start, id.sym.name+" is not a type");
+ return terror;
+ }
+ if(tt == nil){
+ error(t.src.start, stypeconv(t)+" not fully defined");
+ return terror;
+ }
+ return tt;
+}
+
+#
+# resolve a -> qualified type
+#
+arrowtype(t: ref Type, adtt: ref Decl): ref Type
+{
+ id := t.decl;
+ if(id.ty != nil){
+ if(id.store == Dunbound)
+ fatal("arrowtype: unbound decl has a type");
+ return id.ty;
+ }
+
+ #
+ # special hack to allow module variables to derive other types
+ #
+ tt := t.tof;
+ if(tt.kind == Tid){
+ id = tt.decl;
+ if(id.store == Dunbound)
+ fatal("arrowtype: Tid's decl unbound");
+ if(id.store == Dimport){
+ id.store = Dwundef;
+ error(t.src.start, id.sym.name+"'s type cannot be determined");
+ return terror;
+ }
+
+ #
+ # forward references to module variables can't be resolved
+ #
+ if(id.store != Dtype && (id.ty.ok & OKbind) != OKbind){
+ error(t.src.start, id.sym.name+"'s type cannot be determined");
+ return terror;
+ }
+
+ if(id.store == Dwundef)
+ return terror;
+ tt = id.ty = verifytypes(id.ty, adtt, nil);
+ if(tt == nil){
+ error(t.tof.src.start, typeconv(t.tof)+" is not a module");
+ return terror;
+ }
+ }else
+ tt = verifytypes(t.tof, adtt, nil);
+ t.tof = tt;
+ if(tt == terror)
+ return terror;
+ if(tt.kind != Tmodule){
+ error(t.src.start, typeconv(tt)+" is not a module");
+ return terror;
+ }
+ id = namedot(tt.ids, t.decl.sym);
+ if(id == nil){
+ error(t.src.start, t.decl.sym.name+" is not a member of "+typeconv(tt));
+ return terror;
+ }
+ if(id.store == Dtype && id.ty != nil){
+ t.decl = id;
+ return id.ty;
+ }
+ error(t.src.start, typeconv(t)+" is not a type");
+ return terror;
+}
+
+#
+# resolve a . qualified type
+#
+dottype(t: ref Type, adtt: ref Decl): ref Type
+{
+ if(t.decl.ty != nil){
+ if(t.decl.store == Dunbound)
+ fatal("dottype: unbound decl has a type");
+ return t.decl.ty;
+ }
+ t.tof = tt := verifytypes(t.tof, adtt, nil);
+ if(tt == terror)
+ return terror;
+ if(tt.kind != Tadt){
+ error(t.src.start, typeconv(tt)+" is not an adt");
+ return terror;
+ }
+ id := namedot(tt.tags, t.decl.sym);
+ if(id != nil && id.ty != nil){
+ t.decl = id;
+ return id.ty;
+ }
+ error(t.src.start, t.decl.sym.name+" is not a pick tag of "+typeconv(tt));
+ return terror;
+}
+
+insttype(t: ref Type, adtt: ref Decl, tp: ref Tpair): (ref Type, ref Tpair)
+{
+ src := t.src;
+ if(t.tof.kind != Tadt && t.tof.kind != Tadtpick){
+ error(src.start, typeconv(t.tof) + " is not an adt");
+ return (terror, nil);
+ }
+ if(t.tof.kind == Tadt)
+ ids := t.tof.polys;
+ else
+ ids = t.tof.decl.dot.ty.polys;
+ if(ids == nil){
+ error(src.start, typeconv(t.tof) + " is not a polymorphic adt");
+ return (terror, nil);
+ }
+ for(tyl := t.tlist; tyl != nil && ids != nil; tyl = tyl.nxt){
+ tt := tyl.t;
+ if(!tattr[tt.kind].isptr){
+ error(src.start, typeconv(tt) + " is not a pointer type");
+ return (terror, nil);
+ }
+ unifysrc = src;
+ (ok, nil) := tunify(ids.ty, tt);
+ if(!ok){
+ error(src.start, "type " + typeconv(tt) + " does not match " + typeconv(ids.ty));
+ return (terror, nil);
+ }
+ # usetype(tt);
+ tt = verifytypes(tt, adtt, nil);
+ tp = addtmap(ids.ty, tt, tp);
+ ids = ids.next;
+ }
+ if(tyl != nil){
+ error(src.start, "too many actual types in instantiation");
+ return (terror, nil);
+ }
+ if(ids != nil){
+ error(src.start, "too few actual types in instantiation");
+ return (terror, nil);
+ }
+ tt := t.tof;
+ (t, nil) = expandtype(tt, t, adtt, tp);
+ if(t == tt && adtt == nil)
+ t = duptype(t);
+ if(t != tt)
+ t.tmap = tp;
+ t.src = src;
+ return (t, tp);
+}
+
+#
+# walk a type, putting all adts, modules, and tuples into equivalence classes
+#
+teqclass(t: ref Type)
+{
+ id: ref Decl;
+
+ if(t == nil || (t.ok & OKclass) == OKclass)
+ return;
+ t.ok |= OKclass;
+ case t.kind{
+ Terror or
+ Tint or
+ Tbig or
+ Tstring or
+ Treal or
+ Tbyte or
+ Tnone or
+ Tany or
+ Tiface or
+ Tainit or
+ Talt or
+ Tcase or
+ Tcasel or
+ Tcasec or
+ Tgoto or
+ Texcept or
+ Tfix or
+ Tpoly =>
+ return;
+ Tref =>
+ teqclass(t.tof);
+ return;
+ Tchan or
+ Tarray or
+ Tlist =>
+ teqclass(t.tof);
+#ZZZ elim return to fix recursive chans, etc
+ if(!debug['Z'])
+ return;
+ Tadt or
+ Tadtpick or
+ Ttuple or
+ Texception =>
+ for(id = t.ids; id != nil; id = id.next)
+ teqclass(id.ty);
+ for(tg := t.tags; tg != nil; tg = tg.next)
+ teqclass(tg.ty);
+ for(id = t.polys; id != nil; id = id.next)
+ teqclass(id.ty);
+ Tmodule =>
+ t.tof = mkiface(t.decl);
+ for(id = t.ids; id != nil; id = id.next)
+ teqclass(id.ty);
+ Tfn =>
+ for(id = t.ids; id != nil; id = id.next)
+ teqclass(id.ty);
+ for(id = t.polys; id != nil; id = id.next)
+ teqclass(id.ty);
+ teqclass(t.tof);
+ return;
+ * =>
+ fatal("teqclass: unknown type kind "+string t.kind);
+ }
+
+ #
+ # find an equivalent type
+ # stupid linear lookup could be made faster
+ #
+ if((t.ok & OKsized) != OKsized)
+ fatal("eqclass type not sized: " + stypeconv(t));
+
+ for(teq := eqclass[t.kind]; teq != nil; teq = teq.eq){
+ if(t.size == teq.ty.size && tequal(t, teq.ty)){
+ t.eq = teq;
+ if(t.kind == Tmodule)
+ joiniface(t, t.eq.ty.tof);
+ return;
+ }
+ }
+
+ #
+ # if no equiv type, make one
+ #
+ eqclass[t.kind] = t.eq = ref Teq(0, t, eqclass[t.kind]);
+}
+
+#
+# record that we've used the type
+# using a type uses all types reachable from that type
+#
+reftype(t: ref Type)
+{
+ id: ref Decl;
+
+ if(t == nil || (t.ok & OKref) == OKref)
+ return;
+ t.ok |= OKref;
+ if(t.decl != nil && t.decl.refs == 0)
+ t.decl.refs++;
+ case t.kind{
+ Terror or
+ Tint or
+ Tbig or
+ Tstring or
+ Treal or
+ Tbyte or
+ Tnone or
+ Tany or
+ Tiface or
+ Tainit or
+ Talt or
+ Tcase or
+ Tcasel or
+ Tcasec or
+ Tgoto or
+ Texcept or
+ Tfix or
+ Tpoly =>
+ break;
+ Tref or
+ Tchan or
+ Tarray or
+ Tlist =>
+ if(t.decl != nil){
+ if(nadts >= len adts){
+ a := array[nadts + 32] of ref Decl;
+ a[0:] = adts;
+ adts = a;
+ }
+ adts[nadts++] = t.decl;
+ }
+ reftype(t.tof);
+ Tadt or
+ Tadtpick or
+ Ttuple or
+ Texception =>
+ if(t.kind == Tadt || t.kind == Ttuple && t.decl.sym != anontupsym){
+ if(nadts >= len adts){
+ a := array[nadts + 32] of ref Decl;
+ a[0:] = adts;
+ adts = a;
+ }
+ adts[nadts++] = t.decl;
+ }
+ for(id = t.ids; id != nil; id = id.next)
+ if(id.store != Dfn)
+ reftype(id.ty);
+ for(tg := t.tags; tg != nil; tg = tg.next)
+ reftype(tg.ty);
+ for(id = t.polys; id != nil; id = id.next)
+ reftype(id.ty);
+ if(t.kind == Tadtpick)
+ reftype(t.decl.dot.ty);
+ Tmodule =>
+ #
+ # a module's elements should get used individually
+ # but do the globals for any sbl file
+ #
+ if(bsym != nil)
+ for(id = t.ids; id != nil; id = id.next)
+ if(id.store == Dglobal)
+ reftype(id.ty);
+ break;
+ Tfn =>
+ for(id = t.ids; id != nil; id = id.next)
+ reftype(id.ty);
+ for(id = t.polys; id != nil; id = id.next)
+ reftype(id.ty);
+ reftype(t.tof);
+ * =>
+ fatal("reftype: unknown type kind "+string t.kind);
+ }
+}
+
+#
+# check all reachable types for cycles and illegal forward references
+# find the size of all the types
+#
+cycsizetype(t: ref Type)
+{
+ id: ref Decl;
+
+ if(t == nil || (t.ok & (OKcycsize|OKcyc|OKsized)) == (OKcycsize|OKcyc|OKsized))
+ return;
+ t.ok |= OKcycsize;
+ case t.kind{
+ Terror or
+ Tint or
+ Tbig or
+ Tstring or
+ Treal or
+ Tbyte or
+ Tnone or
+ Tany or
+ Tiface or
+ Tainit or
+ Talt or
+ Tcase or
+ Tcasel or
+ Tcasec or
+ Tgoto or
+ Texcept or
+ Tfix or
+ Tpoly =>
+ t.ok |= OKcyc;
+ sizetype(t);
+ Tref or
+ Tchan or
+ Tarray or
+ Tlist =>
+ cyctype(t);
+ sizetype(t);
+ cycsizetype(t.tof);
+ Tadt or
+ Ttuple or
+ Texception =>
+ cyctype(t);
+ sizetype(t);
+ for(id = t.ids; id != nil; id = id.next)
+ cycsizetype(id.ty);
+ for(tg := t.tags; tg != nil; tg = tg.next){
+ if((tg.ty.ok & (OKcycsize|OKcyc|OKsized)) == (OKcycsize|OKcyc|OKsized))
+ continue;
+ tg.ty.ok |= (OKcycsize|OKcyc|OKsized);
+ for(id = tg.ty.ids; id != nil; id = id.next)
+ cycsizetype(id.ty);
+ }
+ for(id = t.polys; id != nil; id = id.next)
+ cycsizetype(id.ty);
+ Tadtpick =>
+ t.ok &= ~OKcycsize;
+ cycsizetype(t.decl.dot.ty);
+ Tmodule =>
+ cyctype(t);
+ sizetype(t);
+ for(id = t.ids; id != nil; id = id.next)
+ cycsizetype(id.ty);
+ sizeids(t.ids, 0);
+ Tfn =>
+ cyctype(t);
+ sizetype(t);
+ for(id = t.ids; id != nil; id = id.next)
+ cycsizetype(id.ty);
+ for(id = t.polys; id != nil; id = id.next)
+ cycsizetype(id.ty);
+ cycsizetype(t.tof);
+ sizeids(t.ids, MaxTemp);
+#ZZZ need to align?
+ * =>
+ fatal("cycsizetype: unknown type kind "+string t.kind);
+ }
+}
+
+# check for circularity in type declarations
+# - has to be called before verifytypes
+#
+tcycle(t: ref Type)
+{
+ id: ref Decl;
+ tt: ref Type;
+ tll: ref Typelist;
+
+ if(t == nil)
+ return;
+ case(t.kind){
+ * =>
+ ;
+ Tchan or
+ Tarray or
+ Tref or
+ Tlist or
+ Tdot =>
+ tcycle(t.tof);
+ Tfn or
+ Ttuple =>
+ tcycle(t.tof);
+ for(id = t.ids; id != nil; id = id.next)
+ tcycle(id.ty);
+ Tarrow =>
+ if(int(t.rec&TRvis)){
+ error(t.src.start, "circularity in definition of " + typeconv(t));
+ *t = *terror; # break the cycle
+ return;
+ }
+ tt = t.tof;
+ t.rec |= TRvis;
+ tcycle(tt);
+ if(tt.kind == Tid)
+ tt = tt.decl.ty;
+ id = namedot(tt.ids, t.decl.sym);
+ if(id != nil)
+ tcycle(id.ty);
+ t.rec &= ~TRvis;
+ Tid =>
+ if(int(t.rec&TRvis)){
+ error(t.src.start, "circularity in definition of " + typeconv(t));
+ *t = *terror; # break the cycle
+ return;
+ }
+ t.rec |= TRvis;
+ tcycle(t.decl.ty);
+ t.rec &= ~TRvis;
+ Tinst =>
+ tcycle(t.tof);
+ for(tll = t.tlist; tll != nil; tll = tll.nxt)
+ tcycle(tll.t);
+ }
+}
+
+#
+# marks for checking for arcs
+#
+ ArcValue,
+ ArcList,
+ ArcArray,
+ ArcRef,
+ ArcCyc, # cycle found
+ ArcPolycyc:
+ con 1 << iota;
+
+cyctype(t: ref Type)
+{
+ if((t.ok & OKcyc) == OKcyc)
+ return;
+ t.ok |= OKcyc;
+ t.rec |= TRcyc;
+ case t.kind{
+ Terror or
+ Tint or
+ Tbig or
+ Tstring or
+ Treal or
+ Tbyte or
+ Tnone or
+ Tany or
+ Tfn or
+ Tchan or
+ Tarray or
+ Tref or
+ Tlist or
+ Tfix or
+ Tpoly =>
+ break;
+ Tadt or
+ Tmodule or
+ Ttuple or
+ Texception =>
+ for(id := t.ids; id != nil; id = id.next)
+ cycfield(t, id);
+ for(tg := t.tags; tg != nil; tg = tg.next){
+ if((tg.ty.ok & OKcyc) == OKcyc)
+ continue;
+ tg.ty.ok |= OKcyc;
+ for(id = tg.ty.ids; id != nil; id = id.next)
+ cycfield(t, id);
+ }
+ * =>
+ fatal("cyctype: unknown type kind "+string t.kind);
+ }
+ t.rec &= ~TRcyc;
+}
+
+cycfield(base: ref Type, id: ref Decl)
+{
+ if(!storespace[id.store])
+ return;
+ arc := cycarc(base, id.ty);
+
+ if((arc & (ArcCyc|ArcValue)) == (ArcCyc|ArcValue)){
+ if(id.cycerr == byte 0)
+ error(base.src.start, "illegal type cycle without a reference in field "
+ +id.sym.name+" of "+stypeconv(base));
+ id.cycerr = byte 1;
+ }else if(arc & ArcCyc){
+ if((arc & ArcArray) && id.cyc == byte 0 && !(arc & ArcPolycyc)){
+ if(id.cycerr == byte 0)
+ error(base.src.start, "illegal circular reference to type "+typeconv(id.ty)
+ +" in field "+id.sym.name+" of "+stypeconv(base));
+ id.cycerr = byte 1;
+ }
+ id.cycle = byte 1;
+ }else if(id.cyc != byte 0){
+ if(id.cycerr == byte 0)
+ error(id.src.start, "spurious cyclic qualifier for field "+id.sym.name+" of "+stypeconv(base));
+ id.cycerr = byte 1;
+ }
+}
+
+cycarc(base, t: ref Type): int
+{
+ if(t == nil)
+ return 0;
+ if((t.rec & TRcyc) == TRcyc){
+ if(tequal(t, base)){
+ if(t.kind == Tmodule)
+ return ArcCyc | ArcRef;
+ else
+ return ArcCyc | ArcValue;
+ }
+ return 0;
+ }
+ t.rec |= TRcyc;
+ me := 0;
+ case t.kind{
+ Terror or
+ Tint or
+ Tbig or
+ Tstring or
+ Treal or
+ Tbyte or
+ Tnone or
+ Tany or
+ Tchan or
+ Tfn or
+ Tfix or
+ Tpoly =>
+ break;
+ Tarray =>
+ me = cycarc(base, t.tof) & ~ArcValue | ArcArray;
+ Tref =>
+ me = cycarc(base, t.tof) & ~ArcValue | ArcRef;
+ Tlist =>
+ me = cycarc(base, t.tof) & ~ArcValue | ArcList;
+ Tadt or
+ Tadtpick or
+ Tmodule or
+ Ttuple or
+ Texception =>
+ me = 0;
+ arc: int;
+ for(id := t.ids; id != nil; id = id.next){
+ if(!storespace[id.store])
+ continue;
+ arc = cycarc(base, id.ty);
+ if((arc & ArcCyc) && id.cycerr == byte 0)
+ me |= arc;
+ }
+ for(tg := t.tags; tg != nil; tg = tg.next){
+ arc = cycarc(base, tg.ty);
+ if((arc & ArcCyc) && tg.cycerr == byte 0)
+ me |= arc;
+ }
+
+ if(t.kind == Tmodule)
+ me = me & ArcCyc | ArcRef | ArcPolycyc;
+ else
+ me &= ArcCyc | ArcValue | ArcPolycyc;
+ * =>
+ fatal("cycarc: unknown type kind "+string t.kind);
+ }
+ t.rec &= ~TRcyc;
+ if(int (t.flags&CYCLIC))
+ me |= ArcPolycyc;
+ return me;
+}
+
+#
+# set the sizes and field offsets for t
+# look only as deeply as needed to size this type.
+# cycsize type will clean up the rest.
+#
+sizetype(t: ref Type)
+{
+ id: ref Decl;
+ sz, al, s, a: int;
+
+ if(t == nil)
+ return;
+ if((t.ok & OKsized) == OKsized)
+ return;
+ t.ok |= OKsized;
+if((t.ok & (OKverify|OKsized)) != (OKverify|OKsized))
+fatal("sizetype bogus ok for " + stypeconv(t));
+ case t.kind{
+ * =>
+ fatal("sizetype: unknown type kind "+string t.kind);
+ Terror or
+ Tnone or
+ Tbyte or
+ Tint or
+ Tbig or
+ Tstring or
+ Tany or
+ Treal =>
+ fatal(typeconv(t)+" should have a size");
+ Tref or
+ Tchan or
+ Tarray or
+ Tlist or
+ Tmodule or
+ Tfix or
+ Tpoly =>
+ t.size = t.align = IBY2WD;
+ Tadt or
+ Ttuple or
+ Texception =>
+ if(t.tags == nil){
+#ZZZ
+ if(!debug['z']){
+ (sz, t.align) = sizeids(t.ids, 0);
+ t.size = align(sz, t.align);
+ }else{
+ (sz, nil) = sizeids(t.ids, 0);
+ t.align = IBY2LG;
+ t.size = align(sz, IBY2LG);
+ }
+ return;
+ }
+#ZZZ
+ if(!debug['z']){
+ (sz, al) = sizeids(t.ids, IBY2WD);
+ if(al < IBY2WD)
+ al = IBY2WD;
+ }else{
+ (sz, nil) = sizeids(t.ids, IBY2WD);
+ al = IBY2LG;
+ }
+ for(tg := t.tags; tg != nil; tg = tg.next){
+ if((tg.ty.ok & OKsized) == OKsized)
+ continue;
+ tg.ty.ok |= OKsized;
+#ZZZ
+ if(!debug['z']){
+ (s, a) = sizeids(tg.ty.ids, sz);
+ if(a < al)
+ a = al;
+ tg.ty.size = align(s, a);
+ tg.ty.align = a;
+ }else{
+ (s, nil) = sizeids(tg.ty.ids, sz);
+ tg.ty.size = align(s, IBY2LG);
+ tg.ty.align = IBY2LG;
+ }
+ }
+ Tfn =>
+ t.size = 0;
+ t.align = 1;
+ Tainit =>
+ t.size = 0;
+ t.align = 1;
+ Talt =>
+ t.size = t.cse.nlab * 2*IBY2WD + 2*IBY2WD;
+ t.align = IBY2WD;
+ Tcase or
+ Tcasec =>
+ t.size = t.cse.nlab * 3*IBY2WD + 2*IBY2WD;
+ t.align = IBY2WD;
+ Tcasel =>
+ t.size = t.cse.nlab * 6*IBY2WD + 3*IBY2WD;
+ t.align = IBY2LG;
+ Tgoto =>
+ t.size = t.cse.nlab * IBY2WD + IBY2WD;
+ if(t.cse.iwild != nil)
+ t.size += IBY2WD;
+ t.align = IBY2WD;
+ Tiface =>
+ sz = IBY2WD;
+ for(id = t.ids; id != nil; id = id.next){
+ sz = align(sz, IBY2WD) + IBY2WD;
+ sz += len array of byte id.sym.name + 1;
+ if(id.dot.ty.kind == Tadt)
+ sz += len array of byte id.dot.sym.name + 1;
+ }
+ t.size = sz;
+ t.align = IBY2WD;
+ Texcept =>
+ t.size = 0;
+ t.align = IBY2WD;
+ }
+}
+
+sizeids(id: ref Decl, off: int): (int, int)
+{
+ al := 1;
+ for(; id != nil; id = id.next){
+ if(storespace[id.store]){
+ sizetype(id.ty);
+ #
+ # alignment can be 0 if we have
+ # illegal forward declarations.
+ # just patch a; other code will flag an error
+ #
+ a := id.ty.align;
+ if(a == 0)
+ a = 1;
+
+ if(a > al)
+ al = a;
+
+ off = align(off, a);
+ id.offset = off;
+ off += id.ty.size;
+ }
+ }
+ return (off, al);
+}
+
+align(off, align: int): int
+{
+ if(align == 0)
+ fatal("align 0");
+ while(off % align)
+ off++;
+ return off;
+}
+
+#
+# recalculate a type's size
+#
+resizetype(t: ref Type)
+{
+ if((t.ok & OKsized) == OKsized){
+ t.ok &= ~OKsized;
+ cycsizetype(t);
+ }
+}
+
+#
+# check if a module is accessable from t
+# if so, mark that module interface
+#
+modrefable(t: ref Type)
+{
+ id: ref Decl;
+
+ if(t == nil || (t.ok & OKmodref) == OKmodref)
+ return;
+ if((t.ok & OKverify) != OKverify)
+ fatal("modrefable unused type "+stypeconv(t));
+ t.ok |= OKmodref;
+ case t.kind{
+ Terror or
+ Tint or
+ Tbig or
+ Tstring or
+ Treal or
+ Tbyte or
+ Tnone or
+ Tany or
+ Tfix or
+ Tpoly =>
+ break;
+ Tchan or
+ Tref or
+ Tarray or
+ Tlist =>
+ modrefable(t.tof);
+ Tmodule =>
+ t.tof.linkall = byte 1;
+ t.decl.refs++;
+ for(id = t.ids; id != nil; id = id.next){
+ case id.store{
+ Dglobal or
+ Dfn =>
+ modrefable(id.ty);
+ Dtype =>
+ if(id.ty.kind != Tadt)
+ break;
+ for(m := id.ty.ids; m != nil; m = m.next)
+ if(m.store == Dfn)
+ modrefable(m.ty);
+ }
+ }
+ Tfn or
+ Tadt or
+ Ttuple or
+ Texception =>
+ for(id = t.ids; id != nil; id = id.next)
+ if(id.store != Dfn)
+ modrefable(id.ty);
+ for(tg := t.tags; tg != nil; tg = tg.next){
+ # if((tg.ty.ok & OKmodref) == OKmodref)
+ # continue;
+ tg.ty.ok |= OKmodref;
+ for(id = tg.ty.ids; id != nil; id = id.next)
+ modrefable(id.ty);
+ }
+ for(id = t.polys; id != nil; id = id.next)
+ modrefable(id.ty);
+ modrefable(t.tof);
+ Tadtpick =>
+ modrefable(t.decl.dot.ty);
+ * =>
+ fatal("modrefable: unknown type kind "+string t.kind);
+ }
+}
+
+gendesc(d: ref Decl, size: int, decls: ref Decl): ref Desc
+{
+ if(debug['D'])
+ print("generate desc for %s\n", dotconv(d));
+ if(ispoly(d))
+ addfnptrs(d, 0);
+ desc := usedesc(mkdesc(size, decls));
+ return desc;
+}
+
+mkdesc(size: int, d: ref Decl): ref Desc
+{
+ pmap := array[(size+8*IBY2WD-1) / (8*IBY2WD)] of { * => byte 0 };
+ n := descmap(d, pmap, 0);
+ if(n >= 0)
+ n = n / (8*IBY2WD) + 1;
+ else
+ n = 0;
+ return enterdesc(pmap, size, n);
+}
+
+mktdesc(t: ref Type): ref Desc
+{
+usedty(t);
+ if(debug['D'])
+ print("generate desc for %s\n", typeconv(t));
+ if(t.decl == nil){
+ t.decl = mkdecl(t.src, Dtype, t);
+ t.decl.sym = enter("_mktdesc_", 0);
+ }
+ if(t.decl.desc != nil)
+ return t.decl.desc;
+ pmap := array[(t.size+8*IBY2WD-1) / (8*IBY2WD)] of {* => byte 0};
+ n := tdescmap(t, pmap, 0);
+ if(n >= 0)
+ n = n / (8*IBY2WD) + 1;
+ else
+ n = 0;
+ d := enterdesc(pmap, t.size, n);
+ t.decl.desc = d;
+ return d;
+}
+
+enterdesc(map: array of byte, size, nmap: int): ref Desc
+{
+ last : ref Desc = nil;
+ for(d := descriptors; d != nil; d = d.next){
+ if(d.size > size || d.size == size && d.nmap > nmap)
+ break;
+ if(d.size == size && d.nmap == nmap){
+ c := mapcmp(d.map, map, nmap);
+ if(c == 0)
+ return d;
+ if(c > 0)
+ break;
+ }
+ last = d;
+ }
+
+ d = ref Desc(-1, 0, map, size, nmap, nil);
+ if(last == nil){
+ d.next = descriptors;
+ descriptors = d;
+ }else{
+ d.next = last.next;
+ last.next = d;
+ }
+ return d;
+}
+
+mapcmp(a, b: array of byte, n: int): int
+{
+ for(i := 0; i < n; i++)
+ if(a[i] != b[i])
+ return int a[i] - int b[i];
+ return 0;
+}
+
+usedesc(d: ref Desc): ref Desc
+{
+ d.used = 1;
+ return d;
+}
+
+#
+# create the pointer description byte map for every type in decls
+# each bit corresponds to a word, and is 1 if occupied by a pointer
+# the high bit in the byte maps the first word
+#
+descmap(decls: ref Decl, map: array of byte, start: int): int
+{
+ if(debug['D'])
+ print("descmap offset %d\n", start);
+ last := -1;
+ for(d := decls; d != nil; d = d.next){
+ if(d.store == Dtype && d.ty.kind == Tmodule
+ || d.store == Dfn
+ || d.store == Dconst)
+ continue;
+ if(d.store == Dlocal && d.link != nil)
+ continue;
+ m := tdescmap(d.ty, map, d.offset + start);
+ if(debug['D']){
+ if(d.sym != nil)
+ print("descmap %s type %s offset %d returns %d\n", d.sym.name, typeconv(d.ty), d.offset+start, m);
+ else
+ print("descmap type %s offset %d returns %d\n", typeconv(d.ty), d.offset+start, m);
+ }
+ if(m >= 0)
+ last = m;
+ }
+ return last;
+}
+
+tdescmap(t: ref Type, map: array of byte, offset: int): int
+{
+ i, e, bit: int;
+
+ if(t == nil)
+ return -1;
+
+ m := -1;
+ if(t.kind == Talt){
+ lab := t.cse.labs;
+ e = t.cse.nlab;
+ offset += IBY2WD * 2;
+ for(i = 0; i < e; i++){
+ if(lab[i].isptr){
+ bit = offset / IBY2WD % 8;
+ map[offset / (8*IBY2WD)] |= byte 1 << (7 - bit);
+ m = offset;
+ }
+ offset += 2*IBY2WD;
+ }
+ return m;
+ }
+ if(t.kind == Tcasec){
+ e = t.cse.nlab;
+ offset += IBY2WD;
+ for(i = 0; i < e; i++){
+ bit = offset / IBY2WD % 8;
+ map[offset / (8*IBY2WD)] |= byte 1 << (7 - bit);
+ offset += IBY2WD;
+ bit = offset / IBY2WD % 8;
+ map[offset / (8*IBY2WD)] |= byte 1 << (7 - bit);
+ m = offset;
+ offset += 2*IBY2WD;
+ }
+ return m;
+ }
+
+ if(tattr[t.kind].isptr){
+ bit = offset / IBY2WD % 8;
+ map[offset / (8*IBY2WD)] |= byte 1 << (7 - bit);
+ return offset;
+ }
+ if(t.kind == Tadtpick)
+ t = t.tof;
+ if(t.kind == Ttuple || t.kind == Tadt || t.kind == Texception){
+ if(debug['D'])
+ print("descmap adt offset %d\n", offset);
+ if(t.rec != byte 0)
+ fatal("illegal cyclic type "+stypeconv(t)+" in tdescmap");
+ t.rec = byte 1;
+ offset = descmap(t.ids, map, offset);
+ t.rec = byte 0;
+ return offset;
+ }
+
+ return -1;
+}
+
+tcomset: int;
+
+#
+# can a t2 be assigned to a t1?
+# any means Tany matches all types,
+# not just references
+#
+tcompat(t1, t2: ref Type, any: int): int
+{
+ if(t1 == t2)
+ return 1;
+ if(t1 == nil || t2 == nil)
+ return 0;
+ if(t2.kind == Texception && t1.kind != Texception)
+ t2 = mkextuptype(t2);
+ tcomset = 0;
+ ok := rtcompat(t1, t2, any, 0);
+ v := cleartcomrec(t1) + cleartcomrec(t2);
+ if(v != tcomset)
+ fatal("recid t1 "+stypeconv(t1)+" and t2 "+stypeconv(t2)+" not balanced in tcompat: "+string v+" "+string tcomset);
+ return ok;
+}
+
+rtcompat(t1, t2: ref Type, any: int, inaorc: int): int
+{
+ if(t1 == t2)
+ return 1;
+ if(t1 == nil || t2 == nil)
+ return 0;
+ if(t1.kind == Terror || t2.kind == Terror)
+ return 1;
+ if(t2.kind == Texception && t1.kind != Texception)
+ t2 = mkextuptype(t2);
+
+ t1.rec |= TRcom;
+ t2.rec |= TRcom;
+ case t1.kind{
+ * =>
+ fatal("unknown type "+stypeconv(t1)+" v "+stypeconv(t2)+" in rtcompat");
+ return 0;
+ Tstring =>
+ return t2.kind == Tstring || t2.kind == Tany;
+ Texception =>
+ if(t2.kind == Texception && t1.cons == t2.cons){
+ if(assumetcom(t1, t2))
+ return 1;
+ return idcompat(t1.ids, t2.ids, 0, inaorc);
+ }
+ return 0;
+ Tnone or
+ Tint or
+ Tbig or
+ Tbyte or
+ Treal =>
+ return t1.kind == t2.kind;
+ Tfix =>
+ return t1.kind == t2.kind && sametree(t1.val, t2.val);
+ Tany =>
+ if(tattr[t2.kind].isptr)
+ return 1;
+ return any;
+ Tref or
+ Tlist or
+ Tarray or
+ Tchan =>
+ if(t1.kind != t2.kind){
+ if(t2.kind == Tany)
+ return 1;
+ return 0;
+ }
+ if(t1.kind != Tref && assumetcom(t1, t2))
+ return 1;
+ return rtcompat(t1.tof, t2.tof, 0, t1.kind == Tarray || t1.kind == Tchan || inaorc);
+ Tfn =>
+ break;
+ Ttuple =>
+ if(t2.kind == Tadt && t2.tags == nil
+ || t2.kind == Ttuple){
+ if(assumetcom(t1, t2))
+ return 1;
+ return idcompat(t1.ids, t2.ids, any, inaorc);
+ }
+ if(t2.kind == Tadtpick){
+ t2.tof.rec |= TRcom;
+ if(assumetcom(t1, t2.tof))
+ return 1;
+ return idcompat(t1.ids, t2.tof.ids.next, any, inaorc);
+ }
+ return 0;
+ Tadt =>
+ if(t2.kind == Ttuple && t1.tags == nil){
+ if(assumetcom(t1, t2))
+ return 1;
+ return idcompat(t1.ids, t2.ids, any, inaorc);
+ }
+ if(t1.tags != nil && t2.kind == Tadtpick && !inaorc)
+ t2 = t2.decl.dot.ty;
+ Tadtpick =>
+ #if(t2.kind == Ttuple)
+ # return idcompat(t1.tof.ids.next, t2.ids, any, inaorc);
+ break;
+ Tmodule =>
+ if(t2.kind == Tany)
+ return 1;
+ Tpoly =>
+ if(t2.kind == Tany)
+ return 1;
+ }
+ return tequal(t1, t2);
+}
+
+#
+# add the assumption that t1 and t2 are compatable
+#
+assumetcom(t1, t2: ref Type): int
+{
+ r1, r2: ref Type;
+
+ if(t1.tcom == nil && t2.tcom == nil){
+ tcomset += 2;
+ t1.tcom = t2.tcom = t1;
+ }else{
+ if(t1.tcom == nil){
+ r1 = t1;
+ t1 = t2;
+ t2 = r1;
+ }
+ for(r1 = t1.tcom; r1 != r1.tcom; r1 = r1.tcom)
+ ;
+ for(r2 = t2.tcom; r2 != nil && r2 != r2.tcom; r2 = r2.tcom)
+ ;
+ if(r1 == r2)
+ return 1;
+ if(r2 == nil)
+ tcomset++;
+ t2.tcom = t1;
+ for(; t2 != r1; t2 = r2){
+ r2 = t2.tcom;
+ t2.tcom = r1;
+ }
+ }
+ return 0;
+}
+
+cleartcomrec(t: ref Type): int
+{
+ n := 0;
+ for(; t != nil && (t.rec & TRcom) == TRcom; t = t.tof){
+ t.rec &= ~TRcom;
+ if(t.tcom != nil){
+ t.tcom = nil;
+ n++;
+ }
+ if(t.kind == Tadtpick)
+ n += cleartcomrec(t.tof);
+ if(t.kind == Tmodule)
+ t = t.tof;
+ for(id := t.ids; id != nil; id = id.next)
+ n += cleartcomrec(id.ty);
+ for(id = t.tags; id != nil; id = id.next)
+ n += cleartcomrec(id.ty);
+ for(id = t.polys; id != nil; id = id.next)
+ n += cleartcomrec(id.ty);
+ }
+ return n;
+}
+
+#
+# id1 and id2 are the fields in an adt or tuple
+# simple structural check; ignore names
+#
+idcompat(id1, id2: ref Decl, any: int, inaorc: int): int
+{
+ for(; id1 != nil; id1 = id1.next){
+ if(id1.store != Dfield)
+ continue;
+ while(id2 != nil && id2.store != Dfield)
+ id2 = id2.next;
+ if(id2 == nil
+ || id1.store != id2.store
+ || !rtcompat(id1.ty, id2.ty, any, inaorc))
+ return 0;
+ id2 = id2.next;
+ }
+ while(id2 != nil && id2.store != Dfield)
+ id2 = id2.next;
+ return id2 == nil;
+}
+
+#
+# structural equality on types
+# t->recid is used to detect cycles
+# t->rec is used to clear t->recid
+#
+tequal(t1, t2: ref Type): int
+{
+ eqrec = 0;
+ eqset = 0;
+ ok := rtequal(t1, t2);
+ v := cleareqrec(t1) + cleareqrec(t2);
+ if(0 && v != eqset)
+ fatal("recid t1 "+stypeconv(t1)+" and t2 "+stypeconv(t2)+" not balanced in tequal: "+string v+" "+string eqset);
+ eqset = 0;
+ return ok;
+}
+
+rtequal(t1, t2: ref Type): int
+{
+ #
+ # this is just a shortcut
+ #
+ if(t1 == t2)
+ return 1;
+
+ if(t1 == nil || t2 == nil)
+ return 0;
+ if(t1.kind == Terror || t2.kind == Terror)
+ return 1;
+
+ if(t1.kind != t2.kind)
+ return 0;
+
+ if(t1.eq != nil && t2.eq != nil)
+ return t1.eq == t2.eq;
+
+ t1.rec |= TReq;
+ t2.rec |= TReq;
+ case t1.kind{
+ * =>
+ fatal("bogus type "+stypeconv(t1)+" vs "+stypeconv(t2)+" in rtequal");
+ return 0;
+ Tnone or
+ Tbig or
+ Tbyte or
+ Treal or
+ Tint or
+ Tstring =>
+ #
+ # this should always be caught by t1 == t2 check
+ #
+ fatal("bogus value type "+stypeconv(t1)+" vs "+stypeconv(t2)+" in rtequal");
+ return 1;
+ Tfix =>
+ return sametree(t1.val, t2.val);
+ Tref or
+ Tlist or
+ Tarray or
+ Tchan =>
+ if(t1.kind != Tref && assumeteq(t1, t2))
+ return 1;
+ return rtequal(t1.tof, t2.tof);
+ Tfn =>
+ if(t1.varargs != t2.varargs)
+ return 0;
+ if(!idequal(t1.ids, t2.ids, 0, storespace))
+ return 0;
+ # if(!idequal(t1.polys, t2.polys, 1, nil))
+ if(!pyequal(t1, t2))
+ return 0;
+ return rtequal(t1.tof, t2.tof);
+ Ttuple or
+ Texception =>
+ if(t1.kind != t2.kind || t1.cons != t2.cons)
+ return 0;
+ if(assumeteq(t1, t2))
+ return 1;
+ return idequal(t1.ids, t2.ids, 0, storespace);
+ Tadt or
+ Tadtpick or
+ Tmodule =>
+ if(assumeteq(t1, t2))
+ return 1;
+
+ #
+ # compare interfaces when comparing modules
+ #
+ if(t1.kind == Tmodule)
+ return idequal(t1.tof.ids, t2.tof.ids, 1, nil);
+
+ #
+ # picked adts; check parent,
+ # assuming equiv picked fields,
+ # then check picked fields are equiv
+ #
+ if(t1.kind == Tadtpick && !rtequal(t1.decl.dot.ty, t2.decl.dot.ty))
+ return 0;
+
+ #
+ # adts with pick tags: check picked fields for equality
+ #
+ if(!idequal(t1.tags, t2.tags, 1, nil))
+ return 0;
+
+ # if(!idequal(t1.polys, t2.polys, 1, nil))
+ if(!pyequal(t1, t2))
+ return 0;
+ return idequal(t1.ids, t2.ids, 1, storespace);
+ Tpoly =>
+ if(assumeteq(t1, t2))
+ return 1;
+ if(t1.decl.sym != t2.decl.sym)
+ return 0;
+ return idequal(t1.ids, t2.ids, 1, nil);
+ }
+}
+
+assumeteq(t1, t2: ref Type): int
+{
+ r1, r2: ref Type;
+
+ if(t1.teq == nil && t2.teq == nil){
+ eqrec++;
+ eqset += 2;
+ t1.teq = t2.teq = t1;
+ }else{
+ if(t1.teq == nil){
+ r1 = t1;
+ t1 = t2;
+ t2 = r1;
+ }
+ for(r1 = t1.teq; r1 != r1.teq; r1 = r1.teq)
+ ;
+ for(r2 = t2.teq; r2 != nil && r2 != r2.teq; r2 = r2.teq)
+ ;
+ if(r1 == r2)
+ return 1;
+ if(r2 == nil)
+ eqset++;
+ t2.teq = t1;
+ for(; t2 != r1; t2 = r2){
+ r2 = t2.teq;
+ t2.teq = r1;
+ }
+ }
+ return 0;
+}
+
+#
+# checking structural equality for modules, adts, tuples, and fns
+#
+idequal(id1, id2: ref Decl, usenames: int, storeok: array of int): int
+{
+ #
+ # this is just a shortcut
+ #
+ if(id1 == id2)
+ return 1;
+
+ for(; id1 != nil; id1 = id1.next){
+ if(storeok != nil && !storeok[id1.store])
+ continue;
+ while(id2 != nil && storeok != nil && !storeok[id2.store])
+ id2 = id2.next;
+ if(id2 == nil
+ || usenames && id1.sym != id2.sym
+ || id1.store != id2.store
+ || id1.implicit != id2.implicit
+ || id1.cyc != id2.cyc
+ || (id1.dot == nil) != (id2.dot == nil)
+ || id1.dot != nil && id2.dot != nil && id1.dot.ty.kind != id2.dot.ty.kind
+ || !rtequal(id1.ty, id2.ty))
+ return 0;
+ id2 = id2.next;
+ }
+ while(id2 != nil && storeok != nil && !storeok[id2.store])
+ id2 = id2.next;
+ return id1 == nil && id2 == nil;
+}
+
+
+pyequal(t1: ref Type, t2: ref Type): int
+{
+ pt1, pt2: ref Type;
+ id1, id2: ref Decl;
+
+ if(t1 == t2)
+ return 1;
+ id1 = t1.polys;
+ id2 = t2.polys;
+ for(; id1 != nil; id1 = id1.next){
+ if(id2 == nil)
+ return 0;
+ pt1 = id1.ty;
+ pt2 = id2.ty;
+ if(!rtequal(pt1, pt2)){
+ if(t1.tmap != nil)
+ pt1 = valtmap(pt1, t1.tmap);
+ if(t2.tmap != nil)
+ pt2 = valtmap(pt2, t2.tmap);
+ if(!rtequal(pt1, pt2))
+ return 0;
+ }
+ id2 = id2.next;
+ }
+ return id1 == nil && id2 == nil;
+}
+
+cleareqrec(t: ref Type): int
+{
+ n := 0;
+ for(; t != nil && (t.rec & TReq) == TReq; t = t.tof){
+ t.rec &= ~TReq;
+ if(t.teq != nil){
+ t.teq = nil;
+ n++;
+ }
+ if(t.kind == Tadtpick)
+ n += cleareqrec(t.decl.dot.ty);
+ if(t.kind == Tmodule)
+ t = t.tof;
+ for(id := t.ids; id != nil; id = id.next)
+ n += cleareqrec(id.ty);
+ for(id = t.tags; id != nil; id = id.next)
+ n += cleareqrec(id.ty);
+ for(id = t.polys; id != nil; id = id.next)
+ n += cleareqrec(id.ty);
+ }
+ return n;
+}
+
+raisescompat(n1: ref Node, n2: ref Node): int
+{
+ if(n1 == n2)
+ return 1;
+ if(n2 == nil)
+ return 1; # no need to repeat in definition if given in declaration
+ if(n1 == nil)
+ return 0;
+ for((n1, n2) = (n1.left, n2.left); n1 != nil && n2 != nil; (n1, n2) = (n1.right, n2.right)){
+ if(n1.left.decl != n2.left.decl)
+ return 0;
+ }
+ return n1 == n2;
+}
+
+# t1 a polymorphic type
+fnunify(t1: ref Type, t2: ref Type, tp: ref Tpair, swapped: int): (int, ref Tpair)
+{
+ id, ids: ref Decl;
+ sym: ref Sym;
+ ok: int;
+
+ for(ids = t1.ids; ids != nil; ids = ids.next){
+ sym = ids.sym;
+ (id, nil) = fnlookup(sym, t2);
+ if(id != nil)
+ usetype(id.ty);
+ if(id == nil){
+ if(dowarn)
+ error(unifysrc.start, "type " + typeconv(t2) + " does not have a '" + sym.name + "' function");
+ return (0, tp);
+ }
+ else if(id.ty.kind != Tfn){
+ if(dowarn)
+ error(unifysrc.start, typeconv(id.ty) + " is not a function");
+ return (0, tp);
+ }
+ else{
+ (ok, tp) = rtunify(ids.ty, id.ty, tp, !swapped);
+ if(!ok){
+ if(dowarn)
+ error(unifysrc.start, typeconv(ids.ty) + " and " + typeconv(id.ty) + " are not compatible wrt " + sym.name);
+ return (0, tp);
+ }
+ }
+ }
+ return (1, tp);
+}
+
+fncleareqrec(t1: ref Type, t2: ref Type): int
+{
+ id, ids: ref Decl;
+ n: int;
+
+ n = 0;
+ n += cleareqrec(t1);
+ n += cleareqrec(t2);
+ for(ids = t1.ids; ids != nil; ids = ids.next){
+ (id, nil) = fnlookup(ids.sym, t2);
+ if(id == nil)
+ continue;
+ else{
+ n += cleareqrec(ids.ty);
+ n += cleareqrec(id.ty);
+ }
+ }
+ return n;
+}
+
+tunify(t1: ref Type, t2: ref Type): (int, ref Tpair)
+{
+ v: int;
+ p: ref Tpair;
+
+ eqrec = 0;
+ eqset = 0;
+ (ok, tp) := rtunify(t1, t2, nil, 0);
+ v = cleareqrec(t1) + cleareqrec(t2);
+ for(p = tp; p != nil; p = p.nxt)
+ v += fncleareqrec(p.t1, p.t2);
+ if(0 && v != eqset)
+ fatal("recid t1 " + stypeconv(t1) + " and t2 " + stypeconv(t2) + " not balanced in tunify: " + string v + " " + string eqset);
+ return (ok, tp);
+}
+
+rtunify(t1: ref Type, t2: ref Type, tp: ref Tpair, swapped: int): (int, ref Tpair)
+{
+ ok: int;
+
+ t1 = valtmap(t1, tp);
+ t2 = valtmap(t2, tp);
+ if(t1 == t2)
+ return (1, tp);
+ if(t1 == nil || t2 == nil)
+ return (0, tp);
+ if(t1.kind == Terror || t2.kind == Terror)
+ return (1, tp);
+ if(t1.kind != Tpoly && t2.kind == Tpoly){
+ (t1, t2) = (t2, t1);
+ swapped = !swapped;
+ }
+ if(t1.kind == Tpoly){
+ # if(typein(t1, t2))
+ # return (0, tp);
+ if(!tattr[t2.kind].isptr)
+ return (0, tp);
+ if(t2.kind != Tany)
+ tp = addtmap(t1, t2, tp);
+ return fnunify(t1, t2, tp, swapped);
+ }
+ if(t1.kind != Tany && t2.kind == Tany){
+ (t1, t2) = (t2, t1);
+ swapped = !swapped;
+ }
+ if(t1.kind == Tadt && t1.tags != nil && t2.kind == Tadtpick && !swapped)
+ t2 = t2.decl.dot.ty;
+ if(t2.kind == Tadt && t2.tags != nil && t1.kind == Tadtpick && swapped)
+ t1 = t1.decl.dot.ty;
+ if(t1.kind != Tany && t1.kind != t2.kind)
+ return (0, tp);
+ t1.rec |= TReq;
+ t2.rec |= TReq;
+ case(t1.kind){
+ * =>
+ return (tequal(t1, t2), tp);
+ Tany =>
+ return (tattr[t2.kind].isptr, tp);
+ Tref or
+ Tlist or
+ Tarray or
+ Tchan =>
+ if(t1.kind != Tref && assumeteq(t1, t2))
+ return (1, tp);
+ return rtunify(t1.tof, t2.tof, tp, swapped);
+ Tfn =>
+ (ok, tp) = idunify(t1.ids, t2.ids, tp, swapped);
+ if(!ok)
+ return (0, tp);
+ (ok, tp) = idunify(t1.polys, t2.polys, tp, swapped);
+ if(!ok)
+ return (0, tp);
+ return rtunify(t1.tof, t2.tof, tp, swapped);
+ Ttuple =>
+ if(assumeteq(t1, t2))
+ return (1, tp);
+ return idunify(t1.ids, t2.ids, tp, swapped);
+ Tadt or
+ Tadtpick =>
+ if(assumeteq(t1, t2))
+ return (1, tp);
+ (ok, tp) = idunify(t1.polys, t2.polys, tp, swapped);
+ if(!ok)
+ return (0, tp);
+ (ok, tp) = idunify(t1.tags, t2.tags, tp, swapped);
+ if(!ok)
+ return (0, tp);
+ return idunify(t1.ids, t2.ids, tp, swapped);
+ Tmodule =>
+ if(assumeteq(t1, t2))
+ return (1, tp);
+ return idunify(t1.tof.ids, t2.tof.ids, tp, swapped);
+ Tpoly =>
+ return (t1 == t2, tp);
+ }
+ return (1, tp);
+}
+
+idunify(id1: ref Decl, id2: ref Decl, tp: ref Tpair, swapped: int): (int, ref Tpair)
+{
+ ok: int;
+
+ if(id1 == id2)
+ return (1, tp);
+ for(; id1 != nil; id1 = id1.next){
+ if(id2 == nil)
+ return (0, tp);
+ (ok, tp) = rtunify(id1.ty, id2.ty, tp, swapped);
+ if(!ok)
+ return (0, tp);
+ id2 = id2.next;
+ }
+ return (id1 == nil && id2 == nil, tp);
+}
+
+polyequal(id1: ref Decl, id2: ref Decl): int
+{
+ # allow id2 list to have an optional for clause
+ ck2 := 0;
+ for(d := id2; d != nil; d = d.next)
+ if(d.ty.ids != nil)
+ ck2 = 1;
+ for(; id1 != nil; id1 = id1.next){
+ if(id2 == nil
+ || id1.sym != id2.sym
+ || id1.ty.decl != nil && id2.ty.decl != nil && id1.ty.decl.sym != id2.ty.decl.sym)
+ return 0;
+ if(ck2 && !idequal(id1.ty.ids, id2.ty.ids, 1, nil))
+ return 0;
+ id2 = id2.next;
+ }
+ return id1 == nil && id2 == nil;
+}
+
+calltype(f: ref Type, a: ref Node, rt: ref Type): ref Type
+{
+ t: ref Type;
+ id, first, last: ref Decl;
+
+ first = last = nil;
+ t = mktype(f.src.start, f.src.stop, Tfn, rt, nil);
+ if(f.kind == Tref)
+ t.polys = f.tof.polys;
+ else
+ t.polys = f.polys;
+ for( ; a != nil; a = a.right){
+ id = mkdecl(f.src, Darg, a.left.ty);
+ if(last == nil)
+ first = id;
+ else
+ last.next = id;
+ last = id;
+ }
+ t.ids = first;
+ if(f.kind == Tref)
+ t = mktype(f.src.start, f.src.stop, Tref, t, nil);
+ return t;
+}
+
+duptype(t: ref Type): ref Type
+{
+ nt: ref Type;
+
+ nt = ref Type;
+ *nt = *t;
+ nt.ok &= ~(OKverify|OKref|OKclass|OKsized|OKcycsize|OKcyc);
+ nt.flags |= INST;
+ nt.eq = nil;
+ nt.sbl = -1;
+ if(t.decl != nil && (nt.kind == Tadt || nt.kind == Tadtpick || nt.kind == Ttuple)){
+ nt.decl = dupdecl(t.decl);
+ nt.decl.ty = nt;
+ nt.decl.link = t.decl;
+ if(t.decl.dot != nil){
+ nt.decl.dot = dupdecl(t.decl.dot);
+ nt.decl.dot.link = t.decl.dot;
+ }
+ }
+ else
+ nt.decl = nil;
+ return nt;
+}
+
+dpolys(ids: ref Decl): int
+{
+ p: ref Decl;
+
+ for(p = ids; p != nil; p = p.next)
+ if(tpolys(p.ty))
+ return 1;
+ return 0;
+}
+
+tpolys(t: ref Type): int
+{
+ v: int;
+ tyl: ref Typelist;
+
+ if(t == nil)
+ return 0;
+ if(int(t.flags&(POLY|NOPOLY)))
+ return int(t.flags&POLY);
+ case(t.kind){
+ * =>
+ v = 0;
+ break;
+ Tarrow or
+ Tdot or
+ Tpoly =>
+ v = 1;
+ break;
+ Tref or
+ Tlist or
+ Tarray or
+ Tchan =>
+ v = tpolys(t.tof);
+ break;
+ Tid =>
+ v = tpolys(t.decl.ty);
+ break;
+ Tinst =>
+ for(tyl = t.tlist; tyl != nil; tyl = tyl.nxt)
+ if(tpolys(tyl.t)){
+ v = 1;
+ break;
+ }
+ v = tpolys(t.tof);
+ break;
+ Tfn or
+ Tadt or
+ Tadtpick or
+ Ttuple or
+ Texception =>
+ if(t.polys != nil){
+ v = 1;
+ break;
+ }
+ if(int(t.rec&TRvis))
+ return 0;
+ t.rec |= TRvis;
+ v = tpolys(t.tof) || dpolys(t.polys) || dpolys(t.ids) || dpolys(t.tags);
+ t.rec &= ~TRvis;
+ if(t.kind == Tadtpick && v == 0)
+ v = tpolys(t.decl.dot.ty);
+ break;
+ }
+ if(v)
+ t.flags |= POLY;
+ else
+ t.flags |= NOPOLY;
+ return v;
+}
+
+doccurs(ids: ref Decl, tp: ref Tpair): int
+{
+ p: ref Decl;
+
+ for(p = ids; p != nil; p = p.next){
+ if(toccurs(p.ty, tp))
+ return 1;
+ }
+ return 0;
+}
+
+toccurs(t: ref Type, tp: ref Tpair): int
+{
+ o: int;
+
+ if(t == nil)
+ return 0;
+ if(!int(t.flags&(POLY|NOPOLY)))
+ tpolys(t);
+ if(int(t.flags&NOPOLY))
+ return 0;
+ case(t.kind){
+ * =>
+ fatal("unknown type " + string t.kind + " in toccurs");
+ Tnone or
+ Tbig or
+ Tbyte or
+ Treal or
+ Tint or
+ Tstring or
+ Tfix or
+ Tmodule or
+ Terror =>
+ return 0;
+ Tarrow or
+ Tdot =>
+ return 1;
+ Tpoly =>
+ return valtmap(t, tp) != t;
+ Tref or
+ Tlist or
+ Tarray or
+ Tchan =>
+ return toccurs(t.tof, tp);
+ Tid =>
+ return toccurs(t.decl.ty, tp);
+ Tinst =>
+ for(tyl := t.tlist; tyl != nil; tyl = tyl.nxt)
+ if(toccurs(tyl.t, tp))
+ return 1;
+ return toccurs(t.tof, tp);
+ Tfn or
+ Tadt or
+ Tadtpick or
+ Ttuple or
+ Texception =>
+ if(int(t.rec&TRvis))
+ return 0;
+ t.rec |= TRvis;
+ o = toccurs(t.tof, tp) || doccurs(t.polys, tp) || doccurs(t.ids, tp) || doccurs(t.tags, tp);
+ t.rec &= ~TRvis;
+ if(t.kind == Tadtpick && o == 0)
+ o = toccurs(t.decl.dot.ty, tp);
+ return o;
+ }
+ return 0;
+}
+
+expandids(ids: ref Decl, adtt: ref Decl, tp: ref Tpair, sym: int): (ref Decl, ref Tpair)
+{
+ p, q, nids, last: ref Decl;
+
+ nids = last = nil;
+ for(p = ids; p != nil; p = p.next){
+ q = dupdecl(p);
+ (q.ty, tp) = expandtype(p.ty, nil, adtt, tp);
+ if(sym && q.ty.decl != nil)
+ q.sym = q.ty.decl.sym;
+ if(q.store == Dfn)
+ q.link = p;
+ if(nids == nil)
+ nids = q;
+ else
+ last.next = q;
+ last = q;
+ }
+ return (nids, tp);
+}
+
+expandtype(t: ref Type, instt: ref Type, adtt: ref Decl, tp: ref Tpair): (ref Type, ref Tpair)
+{
+ nt: ref Type;
+
+ if(t == nil)
+ return (nil, tp);
+ if(!toccurs(t, tp))
+ return (t, tp);
+ case(t.kind){
+ * =>
+ fatal("unknown type " + string t.kind + " in expandtype");
+ Tpoly =>
+ return (valtmap(t, tp), tp);
+ Tref or
+ Tlist or
+ Tarray or
+ Tchan =>
+ nt = duptype(t);
+ (nt.tof, tp) = expandtype(t.tof, nil, adtt, tp);
+ return (nt, tp);
+ Tid =>
+ return expandtype(idtype(t), nil, adtt, tp);
+ Tdot =>
+ return expandtype(dottype(t, adtt), nil, adtt, tp);
+ Tarrow =>
+ return expandtype(arrowtype(t, adtt), nil, adtt, tp);
+ Tinst =>
+ if((nt = valtmap(t, tp)) != t)
+ return (nt, tp);
+ (t, tp) = insttype(t, adtt, tp);
+ return expandtype(t, nil, adtt, tp);
+ Tfn or
+ Tadt or
+ Tadtpick or
+ Ttuple or
+ Texception =>
+ if((nt = valtmap(t, tp)) != t)
+ return (nt, tp);
+ if(t.kind == Tadt)
+ adtt = t.decl;
+ nt = duptype(t);
+ tp = addtmap(t, nt, tp);
+ if(instt != nil)
+ tp = addtmap(instt, nt, tp);
+ (nt.tof, tp) = expandtype(t.tof, nil, adtt, tp);
+ (nt.polys, tp) = expandids(t.polys, adtt, tp, 1);
+ (nt.ids, tp) = expandids(t.ids, adtt, tp, 0);
+ (nt.tags, tp) = expandids(t.tags, adtt, tp, 0);
+ if(t.kind == Tadt){
+ for(ids := nt.tags; ids != nil; ids = ids.next)
+ ids.ty.decl.dot = nt.decl;
+ }
+ if(t.kind == Tadtpick){
+ (nt.decl.dot.ty, tp) = expandtype(t.decl.dot.ty, nil, adtt, tp);
+ }
+ if(t.tmap != nil){
+ nt.tmap = nil;
+ for(p := t.tmap; p != nil; p = p.nxt)
+ nt.tmap = addtmap(valtmap(p.t1, tp), valtmap(p.t2, tp), nt.tmap);
+ }
+ return (nt, tp);
+ }
+ return (nil, tp);
+}
+
+#
+# create type signatures
+# sign the same information used
+# for testing type equality
+#
+sign(d: ref Decl): int
+{
+ t := d.ty;
+ if(t.sig != 0)
+ return t.sig;
+
+ if(ispoly(d))
+ rmfnptrs(d);
+
+ sigend := -1;
+ sigalloc := 1024;
+ sig: array of byte;
+ while(sigend < 0 || sigend >= sigalloc){
+ sigalloc *= 2;
+ sig = array[sigalloc] of byte;
+ eqrec = 0;
+ sigend = rtsign(t, sig, 0);
+ v := clearrec(t);
+ if(v != eqrec)
+ fatal("recid not balanced in sign: "+string v+" "+string eqrec);
+ eqrec = 0;
+ }
+
+ if(signdump != "" && dotconv(d) == signdump){
+ print("sign %s len %d\n", dotconv(d), sigend);
+ print("%s\n", string sig[:sigend]);
+ }
+
+ md5sig := array[Keyring->MD5dlen] of {* => byte 0};
+ md5(sig, sigend, md5sig, nil);
+
+ for(i := 0; i < Keyring->MD5dlen; i += 4)
+ t.sig ^= int md5sig[i+0] | (int md5sig[i+1]<<8) | (int md5sig[i+2]<<16) | (int md5sig[i+3]<<24);
+
+ if(debug['S'])
+ print("signed %s type %s len %d sig %#ux\n", dotconv(d), typeconv(t), sigend, t.sig);
+ return t.sig;
+}
+
+SIGSELF: con byte 'S';
+SIGVARARGS: con byte '*';
+SIGCYC: con byte 'y';
+SIGREC: con byte '@';
+
+sigkind := array[Tend] of
+{
+ Tnone => byte 'n',
+ Tadt => byte 'a',
+ Tadtpick => byte 'p',
+ Tarray => byte 'A',
+ Tbig => byte 'B',
+ Tbyte => byte 'b',
+ Tchan => byte 'C',
+ Treal => byte 'r',
+ Tfn => byte 'f',
+ Tint => byte 'i',
+ Tlist => byte 'L',
+ Tmodule => byte 'm',
+ Tref => byte 'R',
+ Tstring => byte 's',
+ Ttuple => byte 't',
+ Texception => byte 'e',
+ Tfix => byte 'x',
+ Tpoly => byte 'P',
+
+ * => byte 0,
+};
+
+rtsign(t: ref Type, sig: array of byte, spos: int): int
+{
+ id: ref Decl;
+
+ if(t == nil)
+ return spos;
+
+ if(spos < 0 || spos + 8 >= len sig)
+ return -1;
+
+ if(t.eq != nil && t.eq.id){
+ if(t.eq.id < 0 || t.eq.id > eqrec)
+ fatal("sign rec "+typeconv(t)+" "+string t.eq.id+" "+string eqrec);
+
+ sig[spos++] = SIGREC;
+ name := array of byte string t.eq.id;
+ if(spos + len name > len sig)
+ return -1;
+ sig[spos:] = name;
+ spos += len name;
+ return spos;
+ }
+ if(t.eq != nil){
+ eqrec++;
+ t.eq.id = eqrec;
+ }
+
+ kind := sigkind[t.kind];
+ sig[spos++] = kind;
+ if(kind == byte 0)
+ fatal("no sigkind for "+typeconv(t));
+
+ t.rec = byte 1;
+ case t.kind{
+ * =>
+ fatal("bogus type "+stypeconv(t)+" in rtsign");
+ return -1;
+ Tnone or
+ Tbig or
+ Tbyte or
+ Treal or
+ Tint or
+ Tstring or
+ Tpoly =>
+ return spos;
+ Tfix =>
+ name := array of byte string t.val.c.rval;
+ if(spos + len name - 1 >= len sig)
+ return -1;
+ sig[spos: ] = name;
+ spos += len name;
+ return spos;
+ Tref or
+ Tlist or
+ Tarray or
+ Tchan =>
+ return rtsign(t.tof, sig, spos);
+ Tfn =>
+ if(t.varargs != byte 0)
+ sig[spos++] = SIGVARARGS;
+ if(t.polys != nil)
+ spos = idsign(t.polys, 0, sig, spos);
+ spos = idsign(t.ids, 0, sig, spos);
+ if(t.eraises != nil)
+ spos = raisessign(t.eraises, sig, spos);
+ return rtsign(t.tof, sig, spos);
+ Ttuple =>
+ return idsign(t.ids, 0, sig, spos);
+ Tadt =>
+ #
+ # this is a little different than in rtequal,
+ # since we flatten the adt we used to represent the globals
+ #
+ if(t.eq == nil){
+ if(t.decl.sym.name != ".mp")
+ fatal("no t.eq field for "+typeconv(t));
+ spos--;
+ for(id = t.ids; id != nil; id = id.next){
+ spos = idsign1(id, 1, sig, spos);
+ if(spos < 0 || spos >= len sig)
+ return -1;
+ sig[spos++] = byte ';';
+ }
+ return spos;
+ }
+ if(t.polys != nil)
+ spos = idsign(t.polys, 0, sig, spos);
+ spos = idsign(t.ids, 1, sig, spos);
+ if(spos < 0 || t.tags == nil)
+ return spos;
+
+ #
+ # convert closing ')' to a ',', then sign any tags
+ #
+ sig[spos-1] = byte ',';
+ for(tg := t.tags; tg != nil; tg = tg.next){
+ name := array of byte (tg.sym.name + "=>");
+ if(spos + len name > len sig)
+ return -1;
+ sig[spos:] = name;
+ spos += len name;
+
+ spos = rtsign(tg.ty, sig, spos);
+ if(spos < 0 || spos >= len sig)
+ return -1;
+
+ if(tg.next != nil)
+ sig[spos++] = byte ',';
+ }
+ if(spos >= len sig)
+ return -1;
+ sig[spos++] = byte ')';
+ return spos;
+ Tadtpick =>
+ spos = idsign(t.ids, 1, sig, spos);
+ if(spos < 0)
+ return spos;
+ return rtsign(t.decl.dot.ty, sig, spos);
+ Tmodule =>
+ if(t.tof.linkall == byte 0)
+ fatal("signing a narrowed module");
+
+ if(spos >= len sig)
+ return -1;
+ sig[spos++] = byte '{';
+ for(id = t.tof.ids; id != nil; id = id.next){
+ if(id.tag)
+ continue;
+ if(id.sym.name == ".mp"){
+ spos = rtsign(id.ty, sig, spos);
+ if(spos < 0)
+ return -1;
+ continue;
+ }
+ spos = idsign1(id, 1, sig, spos);
+ if(spos < 0 || spos >= len sig)
+ return -1;
+ sig[spos++] = byte ';';
+ }
+ if(spos >= len sig)
+ return -1;
+ sig[spos++] = byte '}';
+ return spos;
+ }
+}
+
+idsign(id: ref Decl, usenames: int, sig: array of byte, spos: int): int
+{
+ if(spos >= len sig)
+ return -1;
+ sig[spos++] = byte '(';
+ first := 1;
+ for(; id != nil; id = id.next){
+ if(id.store == Dlocal)
+ fatal("local "+id.sym.name+" in idsign");
+
+ if(!storespace[id.store])
+ continue;
+
+ if(!first){
+ if(spos >= len sig)
+ return -1;
+ sig[spos++] = byte ',';
+ }
+
+ spos = idsign1(id, usenames, sig, spos);
+ if(spos < 0)
+ return -1;
+ first = 0;
+ }
+ if(spos >= len sig)
+ return -1;
+ sig[spos++] = byte ')';
+ return spos;
+}
+
+idsign1(id: ref Decl, usenames: int, sig: array of byte, spos: int): int
+{
+ if(usenames){
+ name := array of byte (id.sym.name+":");
+ if(spos + len name >= len sig)
+ return -1;
+ sig[spos:] = name;
+ spos += len name;
+ }
+
+ if(spos + 2 >= len sig)
+ return -1;
+
+ if(id.implicit != byte 0)
+ sig[spos++] = SIGSELF;
+
+ if(id.cyc != byte 0)
+ sig[spos++] = SIGCYC;
+
+ return rtsign(id.ty, sig, spos);
+}
+
+raisessign(n: ref Node, sig: array of byte, spos: int): int
+{
+ if(spos >= len sig)
+ return -1;
+ sig[spos++] = byte '(';
+ for(nn := n.left; nn != nil; nn = nn.right){
+ s := array of byte nn.left.decl.sym.name;
+ if(spos+len s - 1 >= len sig)
+ return -1;
+ sig[spos: ] = s;
+ spos += len s;
+ if(nn.right != nil){
+ if(spos >= len sig)
+ return -1;
+ sig[spos++] = byte ',';
+ }
+ }
+ if(spos >= len sig)
+ return -1;
+ sig[spos++] = byte ')';
+ return spos;
+}
+
+clearrec(t: ref Type): int
+{
+ id: ref Decl;
+
+ n := 0;
+ for(; t != nil && t.rec != byte 0; t = t.tof){
+ t.rec = byte 0;
+ if(t.eq != nil && t.eq.id != 0){
+ t.eq.id = 0;
+ n++;
+ }
+ if(t.kind == Tmodule){
+ for(id = t.tof.ids; id != nil; id = id.next)
+ n += clearrec(id.ty);
+ return n;
+ }
+ if(t.kind == Tadtpick)
+ n += clearrec(t.decl.dot.ty);
+ for(id = t.ids; id != nil; id = id.next)
+ n += clearrec(id.ty);
+ for(id = t.tags; id != nil; id = id.next)
+ n += clearrec(id.ty);
+ for(id = t.polys; id != nil; id = id.next)
+ n += clearrec(id.ty);
+ }
+ return n;
+}
+
+# must a variable of the given type be zeroed ? (for uninitialized declarations inside loops)
+tmustzero(t : ref Type) : int
+{
+ if(t==nil)
+ return 0;
+ if(tattr[t.kind].isptr)
+ return 1;
+ if(t.kind == Tadtpick)
+ t = t.tof;
+ if(t.kind == Ttuple || t.kind == Tadt)
+ return mustzero(t.ids);
+ return 0;
+}
+
+mustzero(decls : ref Decl) : int
+{
+ d : ref Decl;
+
+ for (d = decls; d != nil; d = d.next)
+ if (tmustzero(d.ty))
+ return 1;
+ return 0;
+}
+
+typeconv(t: ref Type): string
+{
+ if(t == nil)
+ return "nothing";
+ return tprint(t);
+}
+
+stypeconv(t: ref Type): string
+{
+ if(t == nil)
+ return "nothing";
+ return stprint(t);
+}
+
+tprint(t: ref Type): string
+{
+ id: ref Decl;
+
+ if(t == nil)
+ return "";
+ s := "";
+ if(t.kind < 0 || t.kind >= Tend){
+ s += "kind ";
+ s += string t.kind;
+ return s;
+ }
+ if(t.pr != byte 0 && t.decl != nil){
+ if(t.decl.dot != nil && !isimpmod(t.decl.dot.sym)){
+ s += t.decl.dot.sym.name;
+ s += "->";
+ }
+ s += t.decl.sym.name;
+ return s;
+ }
+ t.pr = byte 1;
+ case t.kind{
+ Tarrow =>
+ s += tprint(t.tof);
+ s += "->";
+ s += t.decl.sym.name;
+ Tdot =>
+ s += tprint(t.tof);
+ s += ".";
+ s += t.decl.sym.name;
+ Tid or
+ Tpoly =>
+ s += t.decl.sym.name;
+ Tinst =>
+ s += tprint(t.tof);
+ s += "[";
+ for(tyl := t.tlist; tyl != nil; tyl = tyl.nxt){
+ s += tprint(tyl.t);
+ if(tyl.nxt != nil)
+ s += ", ";
+ }
+ s += "]";
+ Tint or
+ Tbig or
+ Tstring or
+ Treal or
+ Tbyte or
+ Tany or
+ Tnone or
+ Terror or
+ Tainit or
+ Talt or
+ Tcase or
+ Tcasel or
+ Tcasec or
+ Tgoto or
+ Tiface or
+ Texception or
+ Texcept =>
+ s += kindname[t.kind];
+ Tfix =>
+ s += kindname[t.kind] + "(" + expconv(t.val) + ")";
+ Tref =>
+ s += "ref ";
+ s += tprint(t.tof);
+ Tchan or
+ Tarray or
+ Tlist =>
+ s += kindname[t.kind];
+ s += " of ";
+ s += tprint(t.tof);
+ Tadtpick =>
+ s += t.decl.dot.sym.name + "." + t.decl.sym.name;
+ Tadt =>
+ if(t.decl.dot != nil && !isimpmod(t.decl.dot.sym))
+ s += t.decl.dot.sym.name + "->";
+ s += t.decl.sym.name;
+ if(t.polys != nil){
+ s += "[";
+ for(id = t.polys; id != nil; id = id.next){
+ if(t.tmap != nil)
+ s += tprint(valtmap(id.ty, t.tmap));
+ else
+ s += id.sym.name;
+ if(id.next != nil)
+ s += ", ";
+ }
+ s += "]";
+ }
+ Tmodule =>
+ s += t.decl.sym.name;
+ Ttuple =>
+ s += "(";
+ for(id = t.ids; id != nil; id = id.next){
+ s += tprint(id.ty);
+ if(id.next != nil)
+ s += ", ";
+ }
+ s += ")";
+ Tfn =>
+ s += "fn";
+ if(t.polys != nil){
+ s += "[";
+ for(id = t.polys; id != nil; id = id.next){
+ s += id.sym.name;
+ if(id.next != nil)
+ s += ", ";
+ }
+ s += "]";
+ }
+ s += "(";
+ for(id = t.ids; id != nil; id = id.next){
+ if(id.sym == nil)
+ s += "nil: ";
+ else{
+ s += id.sym.name;
+ s += ": ";
+ }
+ if(id.implicit != byte 0)
+ s += "self ";
+ s += tprint(id.ty);
+ if(id.next != nil)
+ s += ", ";
+ }
+ if(t.varargs != byte 0 && t.ids != nil)
+ s += ", *";
+ else if(t.varargs != byte 0)
+ s += "*";
+ if(t.tof != nil && t.tof.kind != Tnone){
+ s += "): ";
+ s += tprint(t.tof);
+ }else
+ s += ")";
+ * =>
+ yyerror("tprint: unknown type kind "+string t.kind);
+ }
+ t.pr = byte 0;
+ return s;
+}
+
+stprint(t: ref Type): string
+{
+ if(t == nil)
+ return "";
+ s := "";
+ case t.kind{
+ Tid =>
+ s += "id ";
+ s += t.decl.sym.name;
+ Tadt or
+ Tadtpick or
+ Tmodule =>
+ return kindname[t.kind] + " " + tprint(t);
+ }
+ return tprint(t);
+}
+
+# generalize ref P.A, ref P.B to ref P
+
+# tparent(t1: ref Type, t2: ref Type): ref Type
+# {
+# if(t1 == nil || t2 == nil || t1.kind != Tref || t2.kind != Tref)
+# return t1;
+# t1 = t1.tof;
+# t2 = t2.tof;
+# if(t1 == nil || t2 == nil || t1.kind != Tadtpick || t2.kind != Tadtpick)
+# return t1;
+# t1 = t1.decl.dot.ty;
+# t2 = t2.decl.dot.ty;
+# if(tequal(t1, t2))
+# return mktype(t1.src.start, t1.src.stop, Tref, t1, nil);
+# return t1;
+# }
+
+tparent0(t1: ref Type, t2: ref Type): int
+{
+ id1, id2: ref Decl;
+
+ if(t1 == t2)
+ return 1;
+ if(t1 == nil || t2 == nil)
+ return 0;
+ if(t1.kind == Tadt && t2.kind == Tadtpick)
+ t2 = t2.decl.dot.ty;
+ if(t1.kind == Tadtpick && t2.kind == Tadt)
+ t1 = t1.decl.dot.ty;
+ if(t1.kind != t2.kind)
+ return 0;
+ case(t1.kind){
+ * =>
+ fatal("unknown type " + string t1.kind + " v " + string t2.kind + " in tparent");
+ break;
+ Terror or
+ Tstring or
+ Tnone or
+ Tint or
+ Tbig or
+ Tbyte or
+ Treal or
+ Tany =>
+ return 1;
+ Texception or
+ Tfix or
+ Tfn or
+ Tadt or
+ Tmodule or
+ Tpoly =>
+ return tcompat(t1, t2, 0);
+ Tref or
+ Tlist or
+ Tarray or
+ Tchan =>
+ return tparent0(t1.tof, t2.tof);
+ Ttuple =>
+ for((id1, id2) = (t1.ids, t2.ids); id1 != nil && id2 != nil; (id1, id2) = (id1.next, id2.next))
+ if(!tparent0(id1.ty, id2.ty))
+ return 0;
+ return id1 == nil && id2 == nil;
+ Tadtpick =>
+ return tequal(t1.decl.dot.ty, t2.decl.dot.ty);
+ }
+ return 0;
+}
+
+tparent1(t1: ref Type, t2: ref Type): ref Type
+{
+ t, nt: ref Type;
+ id, id1, id2, idt: ref Decl;
+
+ if(t1.kind == Tadt && t2.kind == Tadtpick)
+ t2 = t2.decl.dot.ty;
+ if(t1.kind == Tadtpick && t2.kind == Tadt)
+ t1 = t1.decl.dot.ty;
+ case(t1.kind){
+ * =>
+ return t1;
+ Tref or
+ Tlist or
+ Tarray or
+ Tchan =>
+ t = tparent1(t1.tof, t2.tof);
+ if(t == t1.tof)
+ return t1;
+ return mktype(t1.src.start, t1.src.stop, t1.kind, t, nil);
+ Ttuple =>
+ nt = nil;
+ id = nil;
+ for((id1, id2) = (t1.ids, t2.ids); id1 != nil && id2 != nil; (id1, id2) = (id1.next, id2.next)){
+ t = tparent1(id1.ty, id2.ty);
+ if(t != id1.ty){
+ if(nt == nil){
+ nt = mktype(t1.src.start, t1.src.stop, Ttuple, nil, dupdecls(t1.ids));
+ for((id, idt) = (nt.ids, t1.ids); idt != id1; (id, idt) = (id.next, idt.next))
+ ;
+ }
+ id.ty = t;
+ }
+ if(id != nil)
+ id = id.next;
+ }
+ if(nt == nil)
+ return t1;
+ return nt;
+ Tadtpick =>
+ if(tequal(t1, t2))
+ return t1;
+ return t1.decl.dot.ty;
+ }
+ return t1;
+}
+
+tparent(t1: ref Type, t2: ref Type): ref Type
+{
+ if(tparent0(t1, t2))
+ return tparent1(t1, t2);
+ return t1;
+}
+
+#
+# make the tuple type used to initialize an exception type
+#
+mkexbasetype(t: ref Type): ref Type
+{
+ if(t.cons == byte 0)
+ fatal("mkexbasetype on non-constant");
+ last := mkids(t.decl.src, nil, tstring, nil);
+ last.store = Dfield;
+ nt := mktype(t.src.start, t.src.stop, Texception, nil, last);
+ nt.cons = byte 0;
+ new := mkids(t.decl.src, nil, tint, nil);
+ new.store = Dfield;
+ last.next = new;
+ last = new;
+ for(id := t.ids; id != nil; id = id.next){
+ new = ref *id;
+ new.cyc = byte 0;
+ last.next = new;
+ last = new;
+ }
+ last.next = nil;
+ return usetype(nt);
+}
+
+#
+# make an instantiated exception type
+#
+mkextype(t: ref Type): ref Type
+{
+ nt: ref Type;
+
+ if(t.cons == byte 0)
+ fatal("mkextype on non-constant");
+ if(t.tof != nil)
+ return t.tof;
+ nt = copytypeids(t);
+ nt.cons = byte 0;
+ t.tof = usetype(nt);
+ return t.tof;
+}
+
+#
+# convert an instantiated exception type to it's underlying type
+#
+mkextuptype(t: ref Type): ref Type
+{
+ id: ref Decl;
+ nt: ref Type;
+
+ if(int t.cons)
+ return t;
+ if(t.tof != nil)
+ return t.tof;
+ id = t.ids;
+ if(id == nil)
+ nt = t;
+ else if(id.next == nil)
+ nt = id.ty;
+ else{
+ nt = copytypeids(t);
+ nt.cons = byte 0;
+ nt.kind = Ttuple;
+ }
+ t.tof = usetype(nt);
+ return t.tof;
+}
+
+ckfix(t: ref Type, max: real)
+{
+ s := t.val.c.rval;
+ if(max == 0.0)
+ k := (big 1<<32) - big 1;
+ else
+ k = big 2 * big (max/s) + big 1;
+ x := big 1;
+ for(p := 0; k > x; p++)
+ x *= big 2;
+ if(p == 0 || p > 32){
+ error(t.src.start, "cannot fit fixed type into an int");
+ return;
+ }
+ if(p < 32)
+ t.val.c.rval /= real (1<<(32-p));
+}
+
+scale(t: ref Type): real
+{
+ n: ref Node;
+
+ if(t.kind == Tint || t.kind == Treal)
+ return 1.0;
+ if(t.kind != Tfix)
+ fatal("scale() on non fixed point type");
+ n = t.val;
+ if(n.op != Oconst)
+ fatal("non constant scale");
+ if(n.ty != treal)
+ fatal("non real scale");
+ return n.c.rval;
+}
+
+scale2(f: ref Type, t: ref Type): real
+{
+ return scale(f)/scale(t);
+}
+
+# put x in normal form
+nf(x: real): (int, int)
+{
+ p: int;
+ m: real;
+
+ p = 0;
+ m = x;
+ while(m >= 1.0){
+ p++;
+ m /= 2.0;
+ }
+ while(m < 0.5){
+ p--;
+ m *= 2.0;
+ }
+ m *= real (1<<16)*real (1<<15);
+ if(m >= real 16r7fffffff - 0.5)
+ return (p, 16r7fffffff);
+ return (p, int m);
+}
+
+ispow2(x: real): int
+{
+ m: int;
+
+ (nil, m) = nf(x);
+ if(m != 1<<30)
+ return 0;
+ return 1;
+}
+
+round(x: real, n: int): (int, int)
+{
+ if(n != 31)
+ fatal("not 31 in round");
+ return nf(x);
+}
+
+fixmul2(sx: real, sy: real, sr: real): (int, int, int)
+{
+ k, n, a: int;
+ alpha: real;
+
+ alpha = (sx*sy)/sr;
+ n = 31;
+ (k, a) = round(1.0/alpha, n);
+ return (IMULX, 1-k, 0);
+}
+
+fixdiv2(sx: real, sy: real, sr: real): (int, int, int)
+{
+ k, n, b: int;
+ beta: real;
+
+ beta = sx/(sy*sr);
+ n = 31;
+ (k, b) = round(beta, n);
+ return (IDIVX, k-1, 0);
+}
+
+fixmul(sx: real, sy: real, sr: real): (int, int, int)
+{
+ k, m, n, a, v: int;
+ W: big;
+ alpha, eps: real;
+
+ alpha = (sx*sy)/sr;
+ if(ispow2(alpha))
+ return fixmul2(sx, sy, sr);
+ n = 31;
+ (k, a) = round(1.0/alpha, n);
+ m = n-k;
+ if(m < -n-1)
+ return (IMOVW, 0, 0); # result is zero whatever the values
+ v = 0;
+ W = big 0;
+ eps = real(1<<m)/(alpha*real(a)) - 1.0;
+ if(eps < 0.0){
+ v = a-1;
+ eps = -eps;
+ }
+ if(m < 0 && real(1<<n)*eps*real(a) >= real(a)-1.0+real(1<<m))
+ W = (big(1)<<(-m)) - big 1;
+ if(v != 0 || W != big 0)
+ m = m<<2|(v != 0)<<1|(W != big 0);
+ if(v == 0 && W == big 0)
+ return (IMULX0, m, a);
+ else
+ return (IMULX1, m, a);
+}
+
+fixdiv(sx: real, sy: real, sr: real): (int, int, int)
+{
+ k, m, n, b, v: int;
+ W: big;
+ beta, eps: real;
+
+ beta = sx/(sy*sr);
+ if(ispow2(beta))
+ return fixdiv2(sx, sy, sr);
+ n = 31;
+ (k, b) = round(beta, n);
+ m = k-n;
+ if(m <= -2*n)
+ return (IMOVW, 0, 0); #result is zero whatever the values
+ v = 0;
+ W = big 0;
+ eps = (real(1<<m)*real(b))/beta - 1.0;
+ if(eps < 0.0)
+ v = 1;
+ if(m < 0)
+ W = (big(1)<<(-m)) - big 1;
+ if(v != 0 || W != big 0)
+ m = m<<2|(v != 0)<<1|(W != big 0);
+ if(v == 0 && W == big 0)
+ return (IDIVX0, m, b);
+ else
+ return (IDIVX1, m, b);
+}
+
+fixcast(sx: real, sr: real): (int, int, int)
+{
+ (op, p, a) := fixmul(sx, 1.0, sr);
+ return (op-IMULX+ICVTXX, p, a);
+}
+
+fixop(op: int, tx: ref Type, ty: ref Type, tr: ref Type): (int, int, int)
+{
+ sx, sy, sr: real;
+
+ sx = scale(tx);
+ sy = scale(ty);
+ sr = scale(tr);
+ if(op == IMULX)
+ return fixmul(sx, sy, sr);
+ else if(op == IDIVX)
+ return fixdiv(sx, sy, sr);
+ else
+ return fixcast(sx, sr);
+}
+
+ispoly(d: ref Decl): int
+{
+ if(d == nil)
+ return 0;
+ t := d.ty;
+ if(t.kind == Tfn){
+ if(t.polys != nil)
+ return 1;
+ if((d = d.dot) == nil)
+ return 0;
+ t = d.ty;
+ return t.kind == Tadt && t.polys != nil;
+ }
+ return 0;
+}
+
+ispolyadt(t: ref Type): int
+{
+ return (t.kind == Tadt || t.kind == Tadtpick) && t.polys != nil && (t.flags & INST) == byte 0;
+}
+
+polydecl(ids: ref Decl): ref Decl
+{
+ id: ref Decl;
+ t: ref Type;
+
+ for(id = ids; id != nil; id = id.next){
+ t = mktype(id.src.start, id.src.stop, Tpoly, nil, nil);
+ id.ty = t;
+ t.decl = id;
+ }
+ return ids;
+}
+
+# try to convert an expression tree to a type
+exptotype(n: ref Node): ref Type
+{
+ t, tt: ref Type;
+ d: ref Decl;
+ tll: ref Typelist;
+ src: Src;
+
+ if(n == nil)
+ return nil;
+ t = nil;
+ case(n.op){
+ Oname =>
+ if((d = n.decl) != nil && d.store == Dtype)
+ t = d.ty;
+ Otype or Ochan =>
+ t = n.ty;
+ Oref =>
+ t = exptotype(n.left);
+ if(t != nil)
+ t = mktype(n.src.start, n.src.stop, Tref, t, nil);
+ Odot =>
+ t = exptotype(n.left);
+ if(t != nil){
+ d = namedot(t.tags, n.right.decl.sym);
+ if(d == nil)
+ t = nil;
+ else
+ t = d.ty;
+ }
+ if(t == nil)
+ t = exptotype(n.right);
+ Omdot =>
+ t = exptotype(n.right);
+ Oindex =>
+ t = exptotype(n.left);
+ if(t != nil){
+ src = n.src;
+ tll = nil;
+ for(n = n.right; n != nil; n = n.right){
+ if(n.op == Oseq)
+ tt = exptotype(n.left);
+ else
+ tt = exptotype(n);
+ if(tt == nil)
+ return nil;
+ tll = addtype(tt, tll);
+ if(n.op != Oseq)
+ break;
+ }
+ t = mkinsttype(src, t, tll);
+ }
+ }
+ return t;
+}
+
+uname(im: ref Decl): string
+{
+ s := "";
+ for(p := im; p != nil; p = p.next){
+ s += p.sym.name;
+ if(p.next != nil)
+ s += "+";
+ }
+ return s;
+}
+
+# check all implementation modules have consistent declarations
+# and create their union if needed
+#
+modimp(dl: ref Dlist, im: ref Decl): ref Decl
+{
+ u, d, dd, ids, dot, last: ref Decl;
+ s: ref Sym;
+
+ if(dl.next == nil)
+ return dl.d;
+ dl0 := dl;
+ sg0 := 0;
+ un := uname(im);
+ installids(Dglobal, mkids(dl.d.src, enter(".m."+un, 0), tnone, nil));
+ u = dupdecl(dl.d);
+ u.sym = enter(un, 0);
+ u.sym.decl = u;
+ u.ty = mktype(u.src.start, u.src.stop, Tmodule, nil, nil);
+ u.ty.decl = u;
+ for( ; dl != nil; dl = dl.next){
+ d = dl.d;
+ ids = d.ty.tof.ids; # iface
+ if(ids != nil && ids.store == Dglobal) # .mp
+ sg := sign(ids);
+ else
+ sg = 0;
+ if(dl == dl0)
+ sg0 = sg;
+ else if(sg != sg0)
+ error(d.src.start, d.sym.name + "'s module data not consistent with that of " + dl0.d.sym.name + "\n");
+ for(ids = d.ty.ids; ids != nil; ids = ids.next){
+ s = ids.sym;
+ if(s.decl != nil && s.decl.scope >= scope){
+ if(ids == s.decl){
+ dd = dupdecl(ids);
+ if(u.ty.ids == nil)
+ u.ty.ids = dd;
+ else
+ last.next = dd;
+ last = dd;
+ continue;
+ }
+ dot = s.decl.dot;
+ if(s.decl.store != Dwundef && dot != nil && dot != d && isimpmod(dot.sym) && dequal(ids, s.decl, 1))
+ ids.refs = s.decl.refs;
+ else
+ redecl(ids);
+ ids.init = s.decl.init;
+ }
+ }
+ }
+ u.ty = usetype(u.ty);
+ return u;
+}
+
+modres(d: ref Decl)
+{
+ ids, id, n, i: ref Decl;
+ t: ref Type;
+
+ for(ids = d.ty.ids; ids != nil; ids = ids.next){
+ id = ids.sym.decl;
+ if(ids != id){
+ n = ids.next;
+ i = ids.iface;
+ t = ids.ty;
+ *ids = *id;
+ ids.next = n;
+ ids.iface = i;
+ ids.ty = t;
+ }
+ }
+}
+
+# update the fields of duplicate declarations in other implementation modules
+# and their union
+#
+modresolve()
+{
+ dl: ref Dlist;
+
+ dl = impdecls;
+ if(dl.next == nil)
+ return;
+ for( ; dl != nil; dl = dl.next)
+ modres(dl.d);
+ modres(impdecl);
+}