ref: 3efb5bbb4061056e523858b134c555949591efe2
dir: /appl/cmd/limbo/nodes.b/
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; }