shithub: purgatorio

ref: 5edeca01b0622463a65c126ebcc29314013fd928
dir: /appl/cmd/limbo/nodes.b/

View raw version
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;
}