/* closure.c -- operations on bindings, closures, lambdas, and thunks ($Revision: 1.1.1.1 $) */

#include "es.h"
#include "gc.h"

/*
 * Closure garbage collection support
 */

DefineTag(Closure, static);

extern Closure *mkclosure(Tree *tree, Binding *binding) {
	gcdisable();
	Ref(Closure *, closure, gcnew(Closure));
	closure->tree = tree;
	closure->binding = binding;
	gcenable();
	RefReturn(closure);
}

static void *ClosureCopy(void *op) {
	void *np = gcnew(Closure);
	memcpy(np, op, sizeof (Closure));
	return np;
}

static size_t ClosureScan(void *p) {
	Closure *closure = p;
	closure->tree = forward(closure->tree);
	closure->binding = forward(closure->binding);
	return sizeof (Closure);
}

/* revtree -- destructively reverse a list stored in a tree */
static Tree *revtree(Tree *tree) {
	Tree *prev, *next;
	if (tree == NULL)
		return NULL;
	prev = NULL;
	do {
		assert(tree->kind == nList);
		next = tree->u[1].p;
		tree->u[1].p = prev;
		prev = tree;
	} while ((tree = next) != NULL);
	return prev;
}

static char *doconcat(Tree *word) {
	NodeKind k = word->kind;
	assert(gcisblocked());
	assert(k == nWord || k == nQword || k == nConcat);
	if (k != nConcat)
		return word->u[0].s;
	return str("%s%s", doconcat(word->u[0].p), doconcat(word->u[1].p));
}

typedef struct Chain Chain;
struct Chain {
	Closure *closure;
	Chain *next;
};
static Chain *chain = NULL;

static Binding *extract(Tree *tree, Binding *bindings) {
	assert(gcisblocked());

	for (; tree != NULL; tree = tree->u[1].p) {
		Tree *defn = tree->u[0].p;
		assert(tree->kind == nList);
		if (defn != NULL) {
			List *list = NULL;
			Tree *name = defn->u[0].p;
			assert(name->kind == nWord || name->kind == nQword);
			defn = revtree(defn->u[1].p);
			for (; defn != NULL; defn = defn->u[1].p) {
				Term *term;
				Tree *word = defn->u[0].p;
				assert(defn->kind == nList);
				switch (word->kind) {
				case nConcat:
					term = mkstr(doconcat(word));
					break;
				case nWord: case nQword:
					term = mkstr(word->u[0].s);
					break;
				case nPrim: {
					if (streq(word->u[0].s, "nestedbinding")) {
						int i, count;
						Chain *cp;
						if (
							(defn = defn->u[1].p) == NULL
						     || defn->u[0].p->kind != nWord
						     || (count = (atoi(defn->u[0].p->u[0].s))) < 0
						) {
							fail("$&parse", "improper use of $&nestedbinding");
							NOTREACHED;
						}
						for (cp = chain, i = 0;; cp = cp->next, i++) {
							if (cp == NULL) {
								fail("$&parse", "bad count in $&nestedbinding: %d", count);
								NOTREACHED;
							}
							if (i == count)
								break;
						}
						term = mkterm(NULL, cp->closure);
						break;
					}
				}
				FALLTHROUGH;
				case nLambda: case nThunk:
					term = mkterm(NULL, mkclosure(word, NULL));
					break;
				case nCall: case nVar: case nVarsub:
					fail("$&parse", "bad definition in %%closure: %T\n", defn);
					NOTREACHED;
				default:
					NOTREACHED;
				}
				list = mklist(term, list);
			}
			bindings = mkbinding(name->u[0].s, list, bindings);
		}
	}

	return bindings;
}

extern Closure *extractbindings(Tree *tree0) {
	Chain me;
	Tree *volatile tree = tree0;
	Binding *volatile bindings = NULL;

	gcdisable();

	if (tree->kind == nList && tree->u[1].p == NULL)
		tree = tree->u[0].p;

	me.closure = mkclosure(NULL, NULL);
	me.next = chain;
	chain = &me;

	ExceptionHandler

		while (tree->kind == nClosure) {
			bindings = extract(tree->u[0].p, bindings);
			tree = tree->u[1].p;
			if (tree == NULL)
				fail("$&parse", "null body in %%closure");
			if (tree->kind == nList && tree->u[1].p == NULL)
				tree = tree->u[0].p;
		}

	CatchException (e)

		chain = chain->next;
		throw(e);

	EndExceptionHandler

	chain = chain->next;

	Ref(Closure *, result, me.closure);
	result->tree = tree;
	result->binding = bindings;
	gcenable();
	RefReturn(result);
}


/*
 * Binding garbage collection support
 */

DefineTag(Binding, static);

extern Binding *mkbinding(char *name, List *defn, Binding *next) {
	assert(next == NULL || next->name != NULL);
	validatevar(name);
	gcdisable();
	Ref(Binding *, binding, gcnew(Binding));
	binding->name = name;
	binding->defn = defn;
	binding->next = next;
	gcenable();
	RefReturn(binding);
}

extern Binding *reversebindings(Binding *binding) {
	if (binding == NULL)
		return NULL;
	else {
		Binding *prev, *next;
		prev = NULL;
		do {
			next = binding->next;
			binding->next = prev;
			prev = binding;
		} while ((binding = next) != NULL);
		return prev;
	}
}

static void *BindingCopy(void *op) {
	void *np = gcnew(Binding);
	memcpy(np, op, sizeof (Binding));
	return np;
}

static size_t BindingScan(void *p) {
	Binding *binding = p;
	binding->name = forward(binding->name);
	binding->defn = forward(binding->defn);
	binding->next = forward(binding->next);
	return sizeof (Binding);
}
