shithub: purgatorio

ref: 3efb5bbb4061056e523858b134c555949591efe2
dir: /appl/alphabet/alphabet.b/

View raw version
implement Alphabet, Copy;
include "sys.m";
	sys: Sys;
include "draw.m";
include "readdir.m";
include "sh.m";
	sh: Sh;
	n_BLOCK, n_SEQ, n_LIST, n_ADJ, n_WORD, n_VAR, n_BQ2, n_PIPE: import Sh;
include "sets.m";
	sets: Sets;
	Set: import sets;
include "alphabet/reports.m";
	reports: Reports;
	Report: import reports;
	Modulecmd, Typescmd: import Proxy;
include "alphabet.m";
	evalmod: Eval;
	Context: import evalmod;

Mainsubtypes: module {
	proxy: fn(): chan of ref Proxy->Typescmd[ref Alphabet->Value];
};

# to do:
# - sort out concurrent access to alphabet.
# - if multiple options are given where only one is expected,
#	most modules ignore some values, where they should
#	discard them correctly. this could cause a malicious user
#	to hang up an alphabet expression (waiting for report to end)
# - proper implementation of endpointsrv:
#	- resilience to failures
#	- security of endpoints
#	- no need for write(0)... (or maybe there is)
# - proper implementation of rexecsrv:
#	- should be aware of user

Debug: con 0;
autodeclare := 0;

Module: adt {
	modname:	string;		# used when loading on demand.
	typeset:		ref Typeset;
	sig:			string;
	c:			chan of ref Modulecmd[ref Value];
	m:			Mainmodule;
	def:			ref Sh->Cmd;
	defmods:		ref Strhash[cyclic ref Module];
	refcount:		int;

	find:		fn(ctxt: ref Evalctxt, s: string): (ref Module, string);
	typesig:	fn(m: self ref Module): string;
	run:		fn(m: self ref Module, ctxt: ref Evalctxt,
					errorc: chan of string,
					opts: list of (int, list of ref Value),
					args: list of ref Value): ref Value;
	typename2c:	fn(s: string): int;
	mks:		fn(ctxt: ref Evalctxt, s: string): ref Value;
	mkc:		fn(ctxt: ref Evalctxt, c: ref Sh->Cmd): ref Value;
	ensureloaded:	fn(m: self ref Module): string;
	cvt:		fn(ctxt: ref Evalctxt, v: ref Value, tc: int, errorc: chan of string): ref Value;
};

Evalctxt: adt {
	modules:	ref Strhash[ref Module];
	drawctxt: ref Draw->Context;
	report: ref Report;
#	stopc: chan of int;
};

# used for rewriting expressions.
Rvalue: adt {
	i: ref Sh->Cmd;
	tc: int;
	refcount: int;
	opts: list of (int, list of ref Rvalue);
	args: list of ref Rvalue;

	dup:		fn(t: self ref Rvalue): ref Rvalue;
	free:		fn(v: self ref Rvalue, used: int);
	isstring:	fn(v: self ref Rvalue): int;
	gets:		fn(t: self ref Rvalue): string;
	type2s:	fn(tc: int): string;
	typec:	fn(t: self ref Rvalue): int;
};

Rmodule: adt {
	m: ref Module;

	cvt:		fn(ctxt: ref Revalctxt, v: ref Rvalue, tc: int, errorc: chan of string): ref Rvalue;
	find:		fn(nil: ref Revalctxt, s: string): (ref Rmodule, string);
	typesig:	fn(m: self ref Rmodule): string;
	run:		fn(m: self ref Rmodule, ctxt: ref Revalctxt, errorc: chan of string,
				opts: list of (int, list of ref Rvalue), args: list of ref Rvalue): ref Rvalue;
	mks:		fn(ctxt: ref Revalctxt, s: string): ref Rvalue;
	mkc:		fn(ctxt: ref Revalctxt, c: ref Sh->Cmd): ref Rvalue;
	typename2c:	fn(s: string): int;
};

Revalctxt: adt {
	modules: ref Strhash[ref Module];
	used: ref Strhash[ref Module];
	defs:	int;
	vals: list of ref Rvalue;
};

Renv: adt {
	items: list of ref Rvalue;
	n: int;
};

Typeset: adt {
	name: string;
	c: chan of ref Typescmd[ref Value];
	types: ref Table[cyclic ref Type];		# indexed by external type character
	parent: ref Typeset;

	gettype:	fn(ts: self ref Typeset, tc: int): ref Type;
};

Type: adt {
	id:	int;
	tc:	int;
	transform: list of ref Transform;
	typeset: ref Typeset;
	qname:	string;
	name:	string;
};

Transform: adt {
	dst: int;				# which type we're transforming into.
	all: Set;				# set of all types this transformation can lead to.
	expr: ref Sh->Cmd;		# transformation operation.
};

Table: adt[T] {
	items:	array of list of (int, T);
	nilval:	T;

	new: fn(nslots: int, nilval: T): ref Table[T];
	add:	fn(t: self ref Table, id: int, x: T): int;
	del:	fn(t: self ref Table, id: int): int;
	find:	fn(t: self ref Table, id: int): T;
};

Strhash: adt[T] {
	items:	array of list of (string, T);
	nilval:	T;

	new: fn(nslots: int, nilval: T): ref Strhash[T];
	add:	fn(t: self ref Strhash, id: string, x: T);
	del:	fn(t: self ref Strhash, id: string);
	find:	fn(t: self ref Strhash, id: string): T;
};

Copy: module {
	initcopy: fn(
		typesets: list of ref Typeset,
		roottypeset: ref Typeset,
		modules: ref Strhash[ref Module],
		typebyname: ref Strhash[ref Type],
		typebyc: ref Table[ref Type],
		types: array of ref Type,
		currtypec: int
	): Alphabet;
};

typesets: list of ref Typeset;
roottypeset: ref Typeset;
modules: ref Strhash[ref Module];
typebyname: ref Strhash[ref Type];
typebyc: ref Table[ref Type];	# indexed by internal type character.
types: array of ref Type;		# indexed by id.
currtypec := 16r25a0;		# pretty graphics.

checkload[T](m: T, path: string): T
{
	if(m != nil)
		return m;
	sys->fprint(sys->fildes(2), "alphabet: cannot load %s: %r\n", path);
	raise "fail:bad module";
}

init()
{
	sys = load Sys Sys->PATH;
	sh = load Sh Sh->PATH;
	sets = checkload(load Sets Sets->PATH, Sets->PATH);
	evalmod = checkload(load Eval Eval->PATH, Eval->PATH);
	evalmod->init();
	reports = checkload(load Reports Reports->PATH, Reports->PATH);

	roottypeset = ref Typeset("/", nil, Table[ref Type].new(5, nil), nil);
	typesets = roottypeset :: typesets;
	types = array[] of {
		ref Type(-1, 'c', nil, roottypeset, "/cmd", "cmd"),
		ref Type(-1, 's', nil, roottypeset, "/string", "string"),
		ref Type(-1, 'r', nil, roottypeset, "/status", "status"),
		ref Type(-1, 'f', nil, roottypeset, "/fd", "fd"),
		ref Type(-1, 'w', nil, roottypeset, "/wfd", "wfd"),
		ref Type(-1, 'd', nil, roottypeset, "/data", "data"),
	};
	typebyname = typebyname.new(11, nil);
	typebyc = typebyc.new(11, nil);
	for(i := 0; i < len types; i++){
		types[i].id = i;
		typebyc.add(types[i].tc, types[i]);
		typebyname.add(types[i].qname, types[i]);
		roottypeset.types.add(types[i].tc, types[i]);
	}
#	typebyc.add('a', ref Type(-1, 'a', nil, nil, "/any", "any"));		# not sure about this anymore
	modules = modules.new(3, nil);
}

initcopy(
		xtypesets: list of ref Typeset,
		xroottypeset: ref Typeset,
		xmodules: ref Strhash[ref Module],
		xtypebyname: ref Strhash[ref Type],
		xtypebyc: ref Table[ref Type],
		xtypes: array of ref Type,
		xcurrtypec: int): Alphabet
{
	# XXX must do copy-on-write, and refcounting on typesets.
	typesets = xtypesets;
	roottypeset = xroottypeset;
	modules = xmodules;
	typebyname = xtypebyname;
	typebyc = xtypebyc;
	types = xtypes;
	currtypec = xcurrtypec;
	return load Alphabet "$self";
}

copy(): Alphabet
{
	a := load Copy Alphabet->PATH;
	if(a == nil)
		return nil;
	return a->initcopy(typesets, roottypeset, modules, typebyname, typebyc, types, currtypec);
}

setautodeclare(x: int)
{
	autodeclare = x;
}

quit()
{
	for(ts := typesets; ts != nil; ts = tl ts)
		if((hd ts).c != nil)
			(hd ts).c <-= nil;
	delmods(modules);
}

delmods(mods: ref Strhash[ref Module])
{
	for(i := 0; i < len mods.items; i++){
		for(l := mods.items[i]; l != nil; l = tl l){
			m := (hd l).t1;
			if(--m.refcount == 0){
				if(m.c != nil){
					m.c <-= nil;
					m.c = nil;
				}else if(m.defmods != nil)
					delmods(m.defmods);
				else if(m.m != nil){
					m.m->quit();
					m.m = nil;
				}
			}
		}
	}
}

# XXX could do some more checking to see whether it looks vaguely like
# a valid alphabet expression.
parse(expr: string): (ref Sh->Cmd, string)
{
	return sh->parse(expr);
}

eval(expr: ref Sh->Cmd,
	drawctxt: ref Draw->Context,
	args: list of ref Value): string
{
	spawn reports->reportproc(reportc := chan of string, nil, reply := chan of ref Report);
	r := <-reply;
	reply = nil;
	stderr := sys->fildes(2);
	spawn eval0(expr, "/status", drawctxt, r, reports->r.start("eval"), args, vc := chan of ref Value);
	reports->r.enable();
	v: ref Value;
wait:
	for(;;)alt{
	v = <-vc =>
		if(v != nil)
			v.r().i <-= nil;
	msg := <-reportc =>
		if(msg == nil)
			break wait;
		sys->fprint(stderr, "alphabet: %s\n", msg);
	}
	# we'll always get the value before the report ends.
	if(v == nil)
		return "no value";
	return <-v.r().i;
}

eval0(expr: ref Sh->Cmd,
	dsttype: string,
	drawctxt: ref Draw->Context,
	r: ref Report,
	errorc: chan of string,
	args: list of ref Value,
	vc: chan of ref Value)
{
	c: Eval->Context[ref Value, ref Module, ref Evalctxt];
	ctxt := ref Evalctxt(modules, drawctxt, r);
	tc := -1;
	if(dsttype != nil && (tc = Module.typename2c(dsttype)) == -1){
		report(errorc, "error: unknown type "+dsttype);
		vc <-= nil;
		reports->quit(errorc);
	}

	v := c.eval(expr, ctxt, errorc, args);
	if(tc != -1)
		v = Module.cvt(ctxt, v, tc, errorc);
	vc <-= v;
	reports->quit(errorc);
}

define(name: string, expr: ref Sh->Cmd, errorc: chan of string): string
{
	if(name == nil || name[0] == '/')
		return "bad module name";
	m := modules.find(name);
	if(m != nil)
		return "module already declared";
	sig: string;
	used: ref Strhash[ref Module];
	used = used.new(11, nil);
	(expr, sig) = rewrite0(expr, -1, errorc, used);
	if(sig == nil)
		return "cannot rewrite";
	modules.add(name, ref Module(name, roottypeset, sig, nil, nil, expr, used, 1));
	return nil;
}

typecompat(t0, t1: string): (int, string)
{
	m: ref Module;
	(sig0, err) := evalmod->usage2sig(m, t0);
	if(err != nil)
		return (0, sys->sprint("bad usage %q: %s", t0, err));
	sig1: string;
	(sig1, err) = evalmod->usage2sig(m, t1);
	if(err != nil)
		return (0, sys->sprint("bad usage %q: %s", t1, err));
	return (evalmod->typecompat(sig0, sig1), nil);
}

rewrite(expr: ref Sh->Cmd, dsttype: string, errorc: chan of string): (ref Sh->Cmd, string)
{
	v: ref Value;
	tc := -1;
	if(dsttype != nil){
		tc = Module.typename2c(dsttype);
		if(tc == -1){
			report(errorc, "error: unknown type "+dsttype);
			return (nil, nil);
		}
	}
	sig: string;
	(expr, sig) = rewrite0(expr, tc, errorc, nil);
	if(sig == nil)
		return (nil, nil);

	return (expr, evalmod->cmdusage(v, sig));
}

# XXX different kinds of rewrite:
# could rewrite forcing all names to qualified
# or just leave names as they are.

# return (expr, sig).
# add all modules used by the expression to mods if non-nil.
rewrite0(expr: ref Sh->Cmd, tc: int, errorc: chan of string, used: ref Strhash[ref Module]): (ref Sh->Cmd, string)
{
	m: ref Rmodule;
	ctxt := ref Revalctxt(modules, used, 1, nil);
	(sig, err) := evalmod->blocksig(m, ctxt, expr);
	if(sig == nil){
		report(errorc, "error: cannot get expr type: "+err);
		return (nil, nil);
	}
	args: list of ref Rvalue;
	for(i := len sig - 1; i >= 1; i--)
		args = ref Rvalue(mk(-1, nil, nil), sig[i], 1, nil, nil) :: args;	# N.Vb. cmd node is never used.

	c: Eval->Context[ref Rvalue, ref Rmodule, ref Revalctxt];
	v := c.eval(expr, ctxt, errorc, args);
	if(v != nil && tc != -1)
		v = Rmodule.cvt(ctxt, v, tc, errorc);
	if(v == nil)
		return (nil, nil);
	sig[0] = v.tc;
	v.refcount++;
	expr = gen(v, ref Renv(nil, 0));
	if(len sig > 1){
		t := mkw(Value.type2s(sig[1]));
		for(i = 2; i < len sig; i++)
			t = mk(n_ADJ, t, mkw(Value.type2s(sig[i])));
		expr = mk(n_BLOCK, mk(n_SEQ, mk(n_LIST, t, nil), expr.left), nil);
	}
	return (expr, sig);
}

# generate the expression that gave rise to v.
# it puts in parentenv any values referred to externally.
gen(v: ref Rvalue, parentenv: ref Renv): ref Sh->Cmd
{
	v.refcount--;
	if(v.refcount > 0)
		return mk(n_VAR, mkw(string addenv(parentenv, v)), nil);
	c := v.i;
	(opts, args) := (v.opts, v.args);
	if(opts == nil && args == nil)
		return c;
	env := parentenv;
	if(genblock := needblock(v))
		env = ref Renv(nil, 0);
	for(; opts != nil; opts = tl opts){
		c = mk(n_ADJ, c, mkw(sys->sprint("-%c", (hd opts).t0)));
		for(a := (hd opts).t1; a != nil; a = tl a)
			c = mk(n_ADJ, c, gen(hd a, env));
	}
	if(args != nil && len (hd args).i.word > 1 && (hd args).i.word[0] == '-')
		c = mk(n_ADJ, c, mkw("--"));		# XXX potentially dodgy; some sigs don't interpret "--"?

	# use pipe notation when possible
	arg0: ref Sh->Cmd;
	if(args != nil){
		if((arg0 = gen(hd args, env)).ntype != n_BLOCK){
			c = mk(n_ADJ, c, arg0);
			arg0 = nil;
		}
		args = tl args;
	}
	for(; args != nil; args = tl args)
		c = mk(n_ADJ, c, gen(hd args, env));
	if(arg0 != nil)
		c = mk(n_PIPE, arg0.left, c);
	if(genblock){
		args = rev(env.items);
		m := mkw(Value.type2s((hd args).tc));
		for(a := tl args; a != nil; a = tl a)
			m = mk(n_ADJ, m, mkw(Value.type2s((hd a).tc)));
		c = mk(n_BLOCK, mk(n_SEQ, mk(n_LIST, m, nil), c), nil);
		return gen(ref Rvalue(c, v.tc, 1, nil, args), parentenv);
	}
	return mk(n_BLOCK, c, nil);
}

addenv(env: ref Renv, v: ref Rvalue): int
{
	for(i := env.items; i != nil; i = tl i)
		if(hd i == v)
			return len i;
	env.items = v :: env.items;
	v.refcount++;
	return ++env.n;
}

# need a new block if we have any duplicated values we can resolve locally.
# i.e. for a particular value, if we're the only thing pointing to that value
# and its refcount is > 1 to start with.
needblock(v: ref Rvalue): int
{
	dups := getdups(v, nil);
	for(d := dups; d != nil; d = tl d)
		--(hd d).refcount;
	r := 0;
	for(d = dups; d != nil; d = tl d)
		if((hd d).refcount++ == 0)
			r = 1;
	return r;
}

# find all values which need $ referencing (but don't go any deeper)
getdups(v: ref Rvalue, onto: list of ref Rvalue): list of ref Rvalue
{
	if(v.refcount > 1)
		return v :: onto;
	for(o := v.opts; o != nil; o = tl o)
		for(a := (hd o).t1; a != nil; a = tl a)
			onto = getdups(hd a, onto);
	for(a = v.args; a != nil; a = tl a)
		onto = getdups(hd a, onto);
	return onto;
}

loadtypeset(qname: string, c: chan of ref Typescmd[ref Value], errorc: chan of string): string
{
	tsname := canon(qname);
	if(gettypeset(tsname) != nil)
		return nil;
	(parent, name) := splitqname(tsname);
	if((pts := gettypeset(parent)) == nil)
		return "parent typeset not found";

	if(pts.c != nil){
		if(c != nil)
			return "typecmd channel may only be provided for top-level typesets";
		reply := chan of (chan of ref Typescmd[ref Value], string);
		pts.c <-= ref Typescmd[ref Value].Loadtypes(name, reply);
		err: string;
		(c, err) = <-reply;
		if(c == nil)
			return err;
	}else if(c == nil){
		tsmod := load Mainsubtypes "/dis/alphabet/"+name+"types.dis";
		if(tsmod == nil)
			return sys->sprint("cannot load %q: %r", name+"types.dis");
		c = tsmod->proxy();
	}

	reply := chan of string;
	c <-= ref Typescmd[ref Value].Alphabet(reply);
	a := <-reply;
	ts := ref Typeset(tsname, c, Table[ref Type].new(7, nil), pts);
	typesets = ts :: typesets;
	newtypes: list of ref Type;
	for(i := 0; i < len a; i++){
		tc := a[i];
		if((t := ts.parent.gettype(tc)) == nil){
			t = ref Type(-1, -1, nil, ts, nil, nil);
			sreply := chan of string;
			c <-= ref Typescmd[ref Value].Type2s(tc, sreply);
			t.name = <-sreply;
			# XXX check that type name is syntactically valid.
			t.qname = mkqname(tsname, t.name);
			if(typebyname.find(t.qname) != nil)
				report(errorc, sys->sprint("warning: oops: typename clash on %q", t.qname));
			else
				typebyname.add(t.qname, t);
			newtypes = t :: newtypes;
		}
		ts.types.add(tc, t);
	}
	id := len types;
	types = (array[len types + len newtypes] of ref Type)[0:] = types;
	for(; newtypes != nil; newtypes = tl newtypes){
		types[id] = hd newtypes;
		typebyc.add(currtypec, hd newtypes);
		types[id].tc = currtypec++;
		types[id].id = id;
		id++;
	}
	return nil;
}

autoconvert(src, dst: string, expr: ref Sh->Cmd, errorc: chan of string): string
{
	tdst := typebyname.find(dst);
	if(tdst == nil)
		return "unknown type " + dst;
	tsrc := typebyname.find(src);
	if(tsrc == nil)
		return "unknown type " + src;
	if(tdst.typeset != tsrc.typeset && tdst.typeset != roottypeset && tsrc.typeset != roottypeset)
		return "conversion between incompatible typesets";
	if(expr != nil && expr.ntype == n_WORD){
		# mod -> {(srctype); mod $1}
		expr = mk(n_BLOCK,
			mk(n_SEQ,
				mk(n_LIST, mkw(src), nil),
				mk(n_ADJ,
					mkw(expr.word),
					mk(n_VAR, mkw("1"), nil)
				)
			),
			nil
		);
	}
				
	(e, sig) := rewrite0(expr, tdst.tc, errorc, nil);
	if(sig == nil)
		return "cannot rewrite transformation "+sh->cmd2string(expr);
	if(!evalmod->typecompat(sys->sprint("%c%c", tdst.tc, tsrc.tc), sig))
		return "incompatible module type";
	err := addconversion(tsrc, tdst, e);
	if(err != nil)
		return sys->sprint("bad auto-conversion %s->%s via %s: %s",
					tsrc.qname, tdst.qname, sh->cmd2string(expr), err);
	return nil;
}

mk(ntype: int, left, right: ref Sh->Cmd): ref Sh->Cmd
{
	return ref Sh->Cmd(ntype, left, right, nil, nil);
}
mkw(w: string): ref Sh->Cmd
{
	return ref Sh->Cmd(n_WORD, nil, nil, w, nil);
}

declare(qname: string, usig: string, flags: int): string
{
	return declare0(qname, usig, flags).t1;
}

# declare a module.
# if (flags&ONDEMAND), then we don't need to actually load
# the module (although we do if (flags&CHECK) or if sig==nil,
# in order to check or find out the type signature)
declare0(qname: string, usig: string, flags: int): (ref Module, string)
{
	sig, err: string;
	m: ref Module;
	if(usig != nil){
		(sig, err) = evalmod->usage2sig(m, usig);
		if(sig == nil)
			return (nil, "bad type sig: " + err);
	}
	# if not a qualified name, declare it virtually
	if(qname != nil && qname[0] != '/'){
		if(sig == nil)
			return (nil, "virtual module declaration must include signature");
		m = ref Module(qname, nil, sig, nil, nil, nil, nil, 0);
	}else{
		qname = canon(qname);
		(typeset, mod) := splitqname(qname);
		if((ts := gettypeset(typeset)) == nil)
			return (nil, "unknown typeset");
		if((m = modules.find(qname)) != nil){
			if(m.typeset == ts)
				return (m, nil);
			return (nil, "already imported");
		}
		m = ref Module(mod, ts, sig, nil, nil, nil, nil, 0);
		if(sig == nil || (flags&CHECK) || (flags&ONDEMAND)==0){
			if((e := m.ensureloaded()) != nil)
				return (nil, e);
			if(flags&ONDEMAND){
				if(m.c != nil){
					m.c <-= nil;
					m.c = nil;
				}
				m.m = nil;
			}
		}
	}

	modules.add(qname, m);
	m.refcount++;
	return (m, nil);
}

undeclare(name: string): string
{
	m := modules.find(name);
	if(m == nil)
		return "module not declared";
	modules.del(name);
	if(--m.refcount == 0){
		if(m.c != nil){
			m.c <-= nil;
			m.c = nil;
		}else if(m.defmods != nil){
			delmods(m.defmods);
		}
	}
	return nil;
}

# get info on a module.
# return (qname, usage, def)
getmodule(name: string): (string, string, ref Sh->Cmd)
{
	(qname, sig, def) := getmodule0(name);
	if(sig == nil)
		return (qname, sig, def);
	v: ref Value;
	return (qname, evalmod->cmdusage(v, sig), def);
}

getmodule0(name: string): (string, string, ref Sh->Cmd)
{
	m: ref Module;
	if(name != nil && name[0] != '/'){
		if((m = modules.find(name)) == nil)
			return (nil, nil, nil);
		# XXX could add path searching here.
	}else{
		name = canon(name);
		(typeset, mod) := splitqname(name);
		if((m = modules.find(name)) == nil){
			if(autodeclare == 0)
				return (nil, nil, nil);
			ts := gettypeset(typeset);
			if(ts == nil)
				return (nil, nil, nil);
			m = ref Module(mod, ts, nil, nil, nil, nil, nil, 0);
			if((e := m.ensureloaded()) != nil)
				return (nil, nil, nil);
			if(m.c != nil)
				m.c <-= nil;
		}
	}

	qname := m.modname;
	if(m.def == nil && m.typeset != nil)
		qname = mkqname(m.typeset.name, qname);
	return (qname, m.sig, m.def);
}

getmodules(): list of string
{
	r: list of string;
	for(i := 0; i < len modules.items; i++)
		for(ml := modules.items[i]; ml != nil; ml = tl ml)
			r = (hd ml).t0 :: r;
	return r;
}

#Cmpdeclts: adt {
#	gt: fn(nil: self ref Cmpdeclts, d1, d2: ref Decltypeset): int
#};
#Cmpdeclts.gt(nil: self ref Cmpdeclts, d1, d2: ref Decltypeset)
#{
#	return d1.name > d2.name;
#}
#Cmpstring: adt {
#	gt: fn(nil: self ref Cmpdeclts, d1, d2: string): int
#};
#Cmpstring.gt(nil: self ref Cmpstring, d1, d2: string): int
#{
#	return d1 > d2;
#}
#Cmptype: adt {
#	gt: fn(nil: self ref Cmptype, d1, d2: ref Type): int
#};
#Cmptype.gt(nil: self ref Cmptype, d1, d2: ref Type): int
#{
#	return d1.name > d2.name;
#}
#
#getdecls(): ref Declarations
#{
#	cmptype: ref Cmptype;
#	d := ref Declarations(array[len typesets] of ref Decltypeset);
#	i := 0;
#	ta := array[len types] of ref Type;
#	for(tsl := typesets; tsl != nil; tsl = tl tsl){
#		t := hd tsl;
#		ts := ref Decltypeset;
#		ts.name = t.name;
#
#		# all types in the typeset, in alphabetical order.
#		j := 0;
#		for(k := 0; k < len t.types.items; k++)
#			for(tt := t.types.items[k]; tt != nil; tt = tl tt)
#				ta[j++] = hd tt;
#		sort(cmptype, ta[0:j]);
#		ts.types = array[j] of string;
#		for(k = 0; k < j; k++){
#			ts.types[k] = ta[k].name;
#			ts.alphabet[k] = ta[k].tc;
#		}
#
#		# all modules in the typeset
#		c := gettypesetmodules(ts.name);
#		while((m := <-c) != nil){
#			
#
#	d.types = array[len types] of string;
#	for(i := 0; i < len types; i++){
#		d.alphabet[i] = types[i].tc;
#		d.types[i] = types[i].qname;
#	}
#	

gettypesetmodules(tsname: string): chan of string
{
	ts := gettypeset(tsname);
	if(ts == nil)
		return nil;
	r := chan of string;
	if(ts.c == nil)
		spawn mainmodules(r);
	else
		ts.c <-= ref Typescmd[ref Value].Modules(r);
	return r;
}

mainmodules(r: chan of string)
{
	if((readdir := load Readdir Readdir->PATH) != nil){
		(a, nil) := readdir->init("/dis/alphabet/main", Readdir->NAME|Readdir->COMPACT);
		for(i := 0; i < len a; i++){
			m := a[i].name;
			if((a[i].mode & Sys->DMDIR) == 0 && len m > 4 && m[len m - 4:] == ".dis")
				r <-= m[0:len m - 4];
		}
	}
	r <-= nil;
}

gettypes(ts: string): list of string
{
	r: list of string;
	for(i := 0; i < len types; i++){
		if(ts == nil)
			r = Value.type2s(types[i].tc) :: r;
		else if (types[i].typeset.name == ts)
			r = types[i].name :: r;
	}
	return r;
}

gettypesets(): list of string
{
	r: list of string;
	for(t := typesets; t != nil; t = tl t)
		r = (hd t).name :: r;
	return r;
}

getautoconversions(): list of (string, string, ref Sh->Cmd)
{
	cl: list of (string, string, ref Sh->Cmd);
	for(i := 0; i < len types; i++){
		if(types[i] == nil)
			continue;
		srct := Value.type2s(types[i].tc);
		for(l := types[i].transform; l != nil; l = tl l)
			cl = (srct, Value.type2s(types[(hd l).dst].tc), (hd l).expr) :: cl;
	}
	return cl;
}

importmodule(qname: string): string
{
	qname = canon(qname);
	(typeset, mod) := splitqname(qname);
	if(typeset == nil)
		return "unknown typeset";
	if((m := modules.find(mod)) != nil){
		if(m.typeset == nil)
			return "already defined";
		if(m.typeset.name == typeset)
			return nil;
		return "already imported from "+m.typeset.name;
	}
	if((m = modules.find(qname)) == nil){
		if(autodeclare == 0)
			return "module not declared";
		err: string;
		(m, err) = Module.find(nil, qname);
		if(m == nil)
			return "cannot import: "+ err;
		modules.add(qname, m);
		m.refcount++;
	}
	modules.add(mod, m);
	return nil;
}


gettypeset(name: string): ref Typeset
{
	name = canon(name);
	for(l := typesets; l != nil; l = tl l)
		if((hd l).name == name)
			break;
	if(l == nil)
		return nil;
	return hd l;
}

importtype(qname: string): string
{
	qname = canon(qname);
	(typeset, tname) := splitqname(qname);
	if((ts := gettypeset(typeset)) == nil)
		return "unknown typeset";
	t := typebyname.find(tname);
	if(t != nil){
		if(t.typeset == ts)
			return nil;
		return "type already imported from " + t.typeset.name;
	}
	t = typebyname.find(qname);
	if(t == nil)
		return sys->sprint("%s does not hold type %s", typeset, tname);
	typebyname.add(tname, t);
	return nil;
}

importvalue(v: ref Value, tname: string): (ref Value, string)
{
	if(v == nil || tagof v != tagof Value.Vz)
		return (v, nil);
	if(tname == nil || tname[0] == '/')
		tname = canon(tname);
	t := typebyname.find(tname);
	if(t == nil)
		return (nil, "no such type");
	pick xv := v {
	Vz =>
		if(t.typeset.types.find(xv.i.typec) != t)
			return (nil, "value appears to be of different type");
		xv.i.typec = t.tc;
	}
	return (v, nil);
}

gettype(tc: int): ref Type
{
	return typebyc.find(tc);
}

Typeset.gettype(ts: self ref Typeset, tc: int): ref Type
{
	return ts.types.find(tc);
}

Module.find(ctxt: ref Evalctxt, name: string): (ref Module, string)
{
	mods := modules;
	if(ctxt != nil)
		mods = ctxt.modules;
	m := mods.find(name);
	if(m == nil){
		if(autodeclare == 0 || name == nil || name[0] != '/')
			return (nil, "module not declared");
		err: string;
		(m, err) = declare0(name, nil, 0);
		if(m == nil)
			return (nil, err);
	}else if((err := m.ensureloaded()) != nil)
		return (nil, err);
	return (m, nil);
}

Module.ensureloaded(m: self ref Module): string
{
	if(m.c != nil || m.m != nil || m.def != nil || m.typeset == nil)
		return nil;

	sig: string;
	if(m.typeset.c == nil){
		p := "/dis/alphabet/main/" + m.modname + ".dis";
		mod := load Mainmodule p;
		if(mod == nil)
			return sys->sprint("cannot load %q: %r", p);
		{
			mod->init();
		} exception e {
		"fail:*" =>
			return sys->sprint("init %q failed: %s", m.modname, e[5:]);
		}
		m.m = mod;
		sig = mod->typesig();
	}else{
		reply := chan of (chan of ref Modulecmd[ref Value], string);
		m.typeset.c <-= ref Typescmd[ref Value].Load(m.modname, reply);
		(mc, err) := <-reply;
		if(mc == nil)
			return sys->sprint("cannot load: %s", err);
		m.c = mc;
		sig = gettypesig(m);
	}
	if(m.sig == nil)
		m.sig = sig;
	else if(!evalmod->typecompat(m.sig, sig)){
		v: ref Value;
		if(m.c != nil){
			m.c <-= nil;
			m.c = nil;
		}
		m.m = nil;
		return sys->sprint("%q not compatible with %q (%q vs %q, %d)",
			m.modname+" "+evalmod->cmdusage(v, sig),
			evalmod->cmdusage(v, m.sig), m.sig, sig, m.sig==sig);
	}
	return nil;
}

Module.typesig(m: self ref Module): string
{
	return m.sig;
}

# get the type signature of a module in its native typeset.
# it's not valid to call this on defined or virtually declared modules.
gettypesig(m: ref Module): string
{
	reply := chan of string;
	m.c <-= ref Modulecmd[ref Value].Typesig(reply);
	sig := <-reply;
	origsig := sig;
	for(i := 0; i < len sig; i++){
		tc := sig[i];
		if(tc == '-'){
			i++;
			continue;
		}
		if(tc != '*'){
			t := m.typeset.gettype(sig[i]);
			if(t == nil){
sys->print("no type found for '%c' in sig %q\n", sig[i], origsig);
				return nil;		# XXX is it alright to break here?
			}
			sig[i] = t.tc;
		}
	}
	return sig;
}

Module.run(m: self ref Module, ctxt: ref Evalctxt, errorc: chan of string, opts: list of (int, list of ref Value), args: list of ref Value): ref Value
{
	if(m.c != nil){
		reply := chan of ref Value;
		m.c <-= ref Modulecmd[ref Value].Run(ctxt.drawctxt, ctxt.report, errorc, opts, args, reply);
		if((v := <-reply) != nil){
			pick xv := v {
			Vz =>
				xv.i.typec = m.typeset.types.find(xv.i.typec).tc;
			}
		}
		return v;
	}else if(m.def != nil){
		c: Eval->Context[ref Value, ref Module, ref Evalctxt];
		return c.eval(m.def, ref Evalctxt(m.defmods, ctxt.drawctxt, ctxt.report), errorc, args);
	}else if(m.typeset != nil){
		v := m.m->run(ctxt.drawctxt, ctxt.report, errorc, opts, args);
		free(opts, args, v != nil);
		return v;
	}
	report(errorc, "error: cannot run a virtually declared module");
	return nil;
}

free[V](opts: list of (int, list of V), args: list of V, used: int)
	for{
	V =>
		free: fn(v: self V, used: int);
	}
{
	for(; args != nil; args = tl args)
		(hd args).free(used);
	for(; opts != nil; opts = tl opts)
		for(args = (hd opts).t1; args != nil; args = tl args)
			(hd args).free(used);
}

Module.typename2c(s: string): int
{
	if((t := typebyname.find(s)) == nil)
		return -1;
	return t.tc;
}

Module.cvt(ctxt: ref Evalctxt, v: ref Value, tc: int, errorc: chan of string): ref Value
{
	if(v == nil)
		return nil;
	srctc := v.typec();
	dstid := gettype(tc).id;
	while((vtc := v.typec()) != tc){
		# XXX assumes v always returns a valid typec: might that be dangerous?
		for(l := gettype(vtc).transform; l != nil; l = tl l)
			if((hd l).all.holds(dstid))
				break;
		if(l == nil){
			report(errorc, sys->sprint("error: no way to get from %s to %s", gettype(v.typec()).qname,
					types[dstid].qname));
			v.free(0);
			return nil;		# should only happen the first time.
		}
		t := hd l;
		c: Eval->Context[ref Value, ref Module, ref Evalctxt];
		nv := c.eval(t.expr, ctxt, errorc, v::nil);
		if(nv == nil){
			report(errorc, sys->sprint("error: autoconvert %q failed", sh->cmd2string(t.expr)));
			return nil;
		}
		v = nv;
	}
	return v;
}

Module.mks(nil: ref Evalctxt, s: string): ref Value
{
	return ref Value.Vs(s);
}

Module.mkc(nil: ref Evalctxt, c: ref Sh->Cmd): ref Value
{
	return ref Value.Vc(c);
}

show()
{
	for(i := 0; i < len types; i++){
		if(types[i] == nil)
			continue;
		sys->print("%s =>\n", types[i].qname);
		for(l := types[i].transform; l != nil; l = tl l)
			sys->print("\t%s -> %s {%s}\n", set2s((hd l).all), types[(hd l).dst].qname, sh->cmd2string((hd l).expr));
	}
}

set2s(set: Set): string
{
	s := "{";
	for(i := 0; i < len types; i++){
		if(set.holds(i)){
			if(len s > 1)
				s[len s] = ' ';
			s += types[i].qname;
		}
	}
	return s + "}";
}

Value.dup(v: self ref Value): ref Value
{
	if(v == nil)
		return nil;
	pick xv := v {
	Vr =>
		return nil;
	Vd =>
		return nil;
	Vf or
	Vw =>
		return nil;
	Vz =>
		rc := chan of ref Value;
		gettype(xv.i.typec).typeset.c <-= ref Typescmd[ref Value].Dup(xv, rc);
		nv := <-rc;
		if(nv == nil)
			return nil;
		if(nv == v)
			return v;
		pick nxv := nv {
		Vz =>
			if(nxv.i.typec == xv.i.typec)
				return nxv;
		}
		sys->print("oh dear, invalid duplicated value from typeset %s\n",  gettype(xv.i.typec).typeset.name);
		return nil;
	}
	return v;
}

Value.typec(v: self ref Value): int
{
	pick xv := v {
	Vc =>
		return 'c';
	Vs =>
		return 's';
	Vr =>
		return 'r';
	Vf =>
		return 'f';
	Vw =>
		return 'w';
	Vd =>
		return 'd';
	Vz =>
		return xv.i.typec;
	}
}

Value.typename(v: self ref Value): string
{
	return Value.type2s(v.typec());
}

Value.free(v: self ref Value, used: int)
{
	if(v == nil)
		return;
	pick xv := v {
	Vr =>
		if(!used)
			xv.i <-= "stop";
	Vf or
	Vw=>
		if(!used){
			<-xv.i;
			xv.i <-= nil;
		}
	Vd =>
		if(!used){
			alt{
			xv.i.stop <-= 1 =>
				;
			* =>
				;
			}
		}
	Vz =>
		gettype(xv.i.typec).typeset.c <-= ref Typescmd[ref Value].Free(xv, used, reply := chan of int);
		<-reply;
	}
}

Value.isstring(v: self ref Value): int
{
	return tagof v == tagof Value.Vs;
}
Value.gets(v: self ref Value): string
{
	return v.s().i;
}
Value.c(v: self ref Value): ref Value.Vc
{
	pick xv :=v {Vc => return xv;}
	raise "type error";
}
Value.s(v: self ref Value): ref Value.Vs
{
	pick xv :=v {Vs => return xv;}
	raise "type error";
}
Value.r(v: self ref Value): ref Value.Vr
{
	pick xv :=v {Vr => return xv;}
	raise "type error";
}
Value.f(v: self ref Value): ref Value.Vf
{
	pick xv :=v {Vf => return xv;}
	raise "type error";
}
Value.w(v: self ref Value): ref Value.Vw
{
	pick xv :=v {Vw => return xv;}
	raise "type error";
}
Value.d(v: self ref Value): ref Value.Vd
{
	pick xv :=v {Vd => return xv;}
	raise "type error";
}
Value.z(v: self ref Value): ref Value.Vz
{
	pick xv :=v {Vz => return xv;}
	raise "type error";
}

Value.type2s(tc: int): string
{
	t := gettype(tc);
	if(t == nil)
		return "unknown";
	if(typebyname.find(t.name) == t)
		return t.name;
	return t.qname;
}

Rmodule.find(ctxt: ref Revalctxt, s: string): (ref Rmodule, string)
{
	m := ctxt.modules.find(s);
	if(m == nil){
		if(autodeclare == 0 || s == nil || s[0] != '/')
			return (nil, "module not declared");
		if(ctxt.modules != modules)
			return (nil, "shouldn't happen: module not found in defined block");
		err: string;
		(m, err) = declare0(s, nil, ONDEMAND);
		if(m == nil)
			return (nil, err);
	}
	return (ref Rmodule(m), nil);
}

Rmodule.cvt(ctxt: ref Revalctxt, v: ref Rvalue, tc: int, errorc: chan of string): ref Rvalue
{
	if(v == nil)
		return nil;
	srctc := v.typec();
	dstid := gettype(tc).id;
	while((vtc := v.typec()) != tc){
		# XXX assumes v always returns a valid typec: might that be dangerous?
		for(l := gettype(vtc).transform; l != nil; l = tl l)
			if((hd l).all.holds(dstid))
				break;
		if(l == nil){
			report(errorc, sys->sprint("error: no way to get from %s to %s", gettype(v.typec()).qname,
					types[dstid].qname));
			return nil;		# should only happen the first time.
		}
		t := hd l;
		c: Eval->Context[ref Rvalue, ref Rmodule, ref Revalctxt];
		v = c.eval(t.expr, ctxt, errorc, v::nil);
	}
	return v;
}

Rmodule.typesig(m: self ref Rmodule): string
{
	return m.m.sig;
}

Rmodule.typename2c(name: string): int
{
	return Module.typename2c(name);
}

Rmodule.mks(ctxt: ref Revalctxt, s: string): ref Rvalue
{
	v := ref Rvalue(mkw(s), 's', 0, nil, nil);
	ctxt.vals = v :: ctxt.vals;
	return v;
}

Rmodule.mkc(ctxt: ref Revalctxt, c: ref Sh->Cmd): ref Rvalue
{
	v := ref Rvalue(mk(n_BQ2, c, nil), 'c', 0, nil, nil);
	ctxt.vals = v :: ctxt.vals;
	return v;
}

Rmodule.run(m: self ref Rmodule, ctxt: ref Revalctxt, errorc: chan of string,
		opts: list of (int, list of ref Rvalue), args: list of ref Rvalue): ref Rvalue
{
	if(ctxt.defs && m.m.def != nil){
		c: Eval->Context[ref Rvalue, ref Rmodule, ref Revalctxt];
		nctxt := ref Revalctxt(m.m.defmods, ctxt.used, ctxt.defs, ctxt.vals);
		v := c.eval(m.m.def, nctxt, errorc, args);
		ctxt.vals = nctxt.vals;
		return v;
	}
	name := mkqname(m.m.typeset.name, m.m.modname);
	if(ctxt.used != nil){
		ctxt.used.add(name, m.m);
		m.m.refcount++;
	}
	v := ref Rvalue(mkw(name), m.m.sig[0], 0, opts, args);
	if(args == nil && opts == nil)
		v.i = mk(n_BLOCK, v.i, nil);
	for(; args != nil; args = tl args)
		(hd args).refcount++;
	for(; opts != nil; opts = tl opts)
		for(args = (hd opts).t1; args != nil; args = tl args)
			(hd args).refcount++;
	ctxt.vals = v :: ctxt.vals;
	return v;
}

Rvalue.dup(v: self ref Rvalue): ref Rvalue
{
	return v;
}
	
Rvalue.free(nil: self ref Rvalue, nil: int)
{
	# XXX perhaps there should be some way of finding out whether a particular
	# type will allow duplication of values or not.
}

Rvalue.isstring(v: self ref Rvalue): int
{
	return v.tc == 's';
}

Rvalue.gets(t: self ref Rvalue): string
{
	return t.i.word;
}

Rvalue.type2s(tc: int): string
{
	return Value.type2s(tc);
}

Rvalue.typec(t: self ref Rvalue): int
{
	return t.tc;
}

addconversion(src, dst: ref Type, expr: ref Sh->Cmd): string
{
	# allow the same transform to be added again
	for(l := src.transform; l != nil; l = tl l)
		if((hd l).all.holds(dst.id)){
			if((hd l).dst == dst.id && sh->cmd2string((hd l).expr) == sh->cmd2string(expr))
				return nil;
		}

	reached := array[len types/8+1] of {* => byte 0};
	if((at := ambiguous(dst, reached)) != nil)
		return sys->sprint("ambiguity: %s", at);

	src.transform = ref Transform(dst.id, sets->bytes2set(reached), expr) :: src.transform;
	# check we haven't created ambiguity in nodes that point to src.
	for(i := 0; i < len types; i++){
		for(l = types[i].transform; l != nil; l = tl l){
			if((hd l).all.holds(src.id) && (at = ambiguous(types[i], array[len types/8+1] of {* => byte 0})) != nil){
				src.transform = tl src.transform;
				return sys->sprint("ambiguity: %s", at);
			}
		}
	}
	all := (Sets->None).add(dst.id);
	for(l = types[dst.id].transform; l != nil; l = tl l)
		all = all.X(Sets->A|Sets->B, (hd l).all);
	# add everything pointed to by dst to the all sets of those types
	# that had previously pointed (indirectly) to src
	for(i = 0; i < len types; i++)
		for(l = types[i].transform; l != nil; l = tl l)
			if((hd l).all.holds(src.id))
				(hd l).all = (hd l).all.X(Sets->A|Sets->B, all);
	return nil;
}

ambiguous(t: ref Type, reached: array of byte): string
{
	if((dt := ambiguous1(t, reached)) == nil)
		return nil;
	(nil, at) := findambiguous(t, dt, array[len reached] of {* =>byte 0}, "self "+types[t.id].qname);
	s := hd at;
	for(at = tl at; at != nil; at = tl at)
		s += ", " + hd at;
	return s;
}

# a conversion is ambiguous if there's more than one
# way of reaching the same type.
# return the type at which the ambiguity is found.
ambiguous1(t: ref Type, reached: array of byte): ref Type
{
	if(bsetholds(reached, t.id))
		return t;
	bsetadd(reached, t.id);
	for(l := t.transform; l != nil; l = tl l)
		if((at := ambiguous1(types[(hd l).dst], reached)) != nil)
			return at;
	return nil;
}

findambiguous(t: ref Type, dt: ref Type, reached: array of byte, s: string): (int, list of string)
{
	a: list of string;
	if(t == dt)
		a = s :: nil;
	if(bsetholds(reached, t.id))
		return (1, a);
	bsetadd(reached, t.id);
	for(l := t.transform; l != nil; l = tl l){
		(found, at) := findambiguous(types[(hd l).dst], dt, reached,
				sys->sprint("%s|%s", s, sh->cmd2string((hd l).expr)));	# XXX rewite correctly
		for(; at != nil; at = tl at)
			a = hd at :: a;
		if(found)
			return (1, a);
	}
	return (0, a);
}

bsetholds(x: array of byte, n: int): int
{
	return int x[n >> 3] & (1 << (n & 7));
}

bsetadd(x: array of byte, n: int)
{
	x[n >> 3] |= byte (1 << (n & 7));
}

mkqname(parent, child: string): string
{
	if(parent == "/")
		return parent+child;
	return parent+"/"+child;
}

# splits a canonical qname into typeset and name components.
splitqname(name: string): (string, string)
{
	if(name == nil)
		return (nil, nil);
	for(i := len name - 1; i >= 0; i--)
		if(name[i] == '/')
			break;
	if(i == 0)
		return ("/", name[1:]);
	return (name[0:i], name[i+1:]);
}

# compress multiple slashes into single; remove trailing slashes.
canon(name: string): string
{
	if(name == nil || name[0] != '/')
		return nil;

	slash := nonslash := 0;
	s := "";
	for(i := 0; i < len name; i++){
		c := name[i];
		if(c == '/')
			slash = 1;
		else{
			if(slash){
				s[len s] = '/';
				nonslash++;
				slash = 0;
			}
			s[len s] = c;
		}
	}
	if(slash && !nonslash)
		s[len s] = '/';
	return s;
}

report(errorc: chan of string, s: string)
{
	if(Debug || errorc == nil)
		sys->fprint(sys->fildes(2), "%s\n", s);
	if(errorc != nil)
		errorc <-= s;
}

Table[T].new(nslots: int, nilval: T): ref Table[T]
{
	if(nslots == 0)
		nslots = 13;
	return ref Table[T](array[nslots] of list of (int, T), nilval);
}

Table[T].add(t: self ref Table[T], id: int, x: T): int
{
	slot := id % len t.items;
	for(q := t.items[slot]; q != nil; q = tl q)
		if((hd q).t0 == id)
			return 0;
	t.items[slot] = (id, x) :: t.items[slot];
	return 1;
}

Table[T].del(t: self ref Table[T], id: int): int
{
	slot := id % len t.items;
	
	p: list of (int, T);
	r := 0;
	for(q := t.items[slot]; q != nil; q = tl q){
		if((hd q).t0 == id){
			p = joinip(p, tl q);
			r = 1;
			break;
		}
		p = hd q :: p;
	}
	t.items[slot] = p;
	return r;
}

Table[T].find(t: self ref Table[T], id: int): T
{
	for(p := t.items[id % len t.items]; p != nil; p = tl p)
		if((hd p).t0 == id)
			return (hd p).t1;
	return t.nilval;
}

hashfn(s: string, n: int): int
{
	h := 0;
	m := len s;
	for(i:=0; i<m; i++){
		h = 65599*h+s[i];
	}
	return (h & 16r7fffffff) % n;
}

Strhash[T].new(nslots: int, nilval: T): ref Strhash[T]
{
	if(nslots == 0)
		nslots = 13;
	return ref Strhash[T](array[nslots] of list of (string, T), nilval);
}

Strhash[T].add(t: self ref Strhash, id: string, x: T)
{
	slot := hashfn(id, len t.items);
	t.items[slot] = (id, x) :: t.items[slot];
}

Strhash[T].del(t: self ref Strhash, id: string)
{
	slot := hashfn(id, len t.items);

	p: list of (string, T);
	for(q := t.items[slot]; q != nil; q = tl q)
		if((hd q).t0 != id)
			p = hd q :: p;
	t.items[slot] = p;
}

Strhash[T].find(t: self ref Strhash, id: string): T
{
	for(p := t.items[hashfn(id, len t.items)]; p != nil; p = tl p)
		if((hd p).t0 == id)
			return (hd p).t1;
	return t.nilval;
}

rev[T](x: list of T): list of T
{
	l: list of T;
	for(; x != nil; x = tl x)
		l = hd x :: l;
	return l;
}

# join x to y, leaving result in arbitrary order.
join[T](x, y: list of T): list of T
{
	if(len x > len y)
		(x, y) = (y, x);
	for(; x != nil; x = tl x)
		y = hd x :: y;
	return y;
}

# join x to y, leaving result in arbitrary order.
joinip[T](x, y: list of (int, T)): list of (int, T)
{
	if(len x > len y)
		(x, y) = (y, x);
	for(; x != nil; x = tl x)
		y = hd x :: y;
	return y;
}

sort[S, T](s: S, a: array of T)
	for{
	S =>
		gt: fn(s: self S, x, y: T): int;
	}
{
	mergesort(s, a, array[len a] of T);
}

mergesort[S, T](s: S, a, b: array of T)
	for{
	S =>
		gt: fn(s: self S, x, y: T): int;
	}
{
	r := len a;
	if (r > 1) {
		m := (r-1)/2 + 1;
		mergesort(s, a[0:m], b[0:m]);
		mergesort(s, a[m:], b[m:]);
		b[0:] = a;
		for ((i, j, k) := (0, m, 0); i < m && j < r; k++) {
			if(s.gt(b[i], b[j]))
				a[k] = b[j++];
			else
				a[k] = b[i++];
		}
		if (i < m)
			a[k:] = b[i:m];
		else if (j < r)
			a[k:] = b[j:r];
	}
}