diff options
Diffstat (limited to 'appl/cmd/limbo')
| -rw-r--r-- | appl/cmd/limbo/arg.m | 50 | ||||
| -rw-r--r-- | appl/cmd/limbo/asm.b | 263 | ||||
| -rw-r--r-- | appl/cmd/limbo/com.b | 1387 | ||||
| -rw-r--r-- | appl/cmd/limbo/decls.b | 1177 | ||||
| -rw-r--r-- | appl/cmd/limbo/dis.b | 560 | ||||
| -rw-r--r-- | appl/cmd/limbo/disoptab.m | 355 | ||||
| -rw-r--r-- | appl/cmd/limbo/ecom.b | 2345 | ||||
| -rw-r--r-- | appl/cmd/limbo/gen.b | 1012 | ||||
| -rw-r--r-- | appl/cmd/limbo/isa.m | 247 | ||||
| -rw-r--r-- | appl/cmd/limbo/lex.b | 1146 | ||||
| -rw-r--r-- | appl/cmd/limbo/limbo.b | 3099 | ||||
| -rw-r--r-- | appl/cmd/limbo/limbo.m | 527 | ||||
| -rw-r--r-- | appl/cmd/limbo/limbo.y | 1973 | ||||
| -rw-r--r-- | appl/cmd/limbo/mkfile | 35 | ||||
| -rw-r--r-- | appl/cmd/limbo/nodes.b | 1402 | ||||
| -rw-r--r-- | appl/cmd/limbo/opname.m | 109 | ||||
| -rw-r--r-- | appl/cmd/limbo/optim.b | 3 | ||||
| -rw-r--r-- | appl/cmd/limbo/sbl.b | 397 | ||||
| -rw-r--r-- | appl/cmd/limbo/stubs.b | 575 | ||||
| -rw-r--r-- | appl/cmd/limbo/typecheck.b | 3223 | ||||
| -rw-r--r-- | appl/cmd/limbo/types.b | 4234 |
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); +} |
