/* term.c -- operations on terms ($Revision: 1.1.1.1 $) */

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

DefineTag(Term, static);

extern Term *mkterm(char *str, Closure *closure) {
	gcdisable();
	Ref(Term *, term, gcnew(Term));
	term->str = str;
	term->closure = closure;
	gcenable();
	RefReturn(term);
}

extern Term *mkstr(char *str) {
	Term *term;
	Ref(char *, string, str);
	term = gcnew(Term);
        term->str = string;
	term->closure = NULL;
        RefEnd(string);
        return term;
}

extern Closure *getclosure(Term *term) {
	if (term->closure == NULL) {
		char *s = term->str;
		assert(s != NULL);
		if (
			((*s == '{' || *s == '@') && s[strlen(s) - 1] == '}')
			|| (*s == '$' && s[1] == '&')
			|| hasprefix(s, "%closure")
		) {
			Closure *c;
			Ref(Term *, tp, term);
			Ref(Tree *, np, parsestring(s));
			if (np == NULL) {
				RefPop2(np, tp);
				return NULL;
			}
			c = extractbindings(np);
			tp->closure = c;
			tp->str = NULL;
			term = tp;
			RefEnd2(np, tp);
		}
	}
	return term->closure;
}

extern char *getstr(Term *term) {
	char *s = term->str;
	Closure *closure = term->closure;
	assert((s == NULL) != (closure == NULL));
	if (s != NULL)
		return s;

#if 0	/* TODO: decide whether getstr() leaves term in closure or string form */
	Ref(Term *, tp, term);
	s = str("%C", closure);
	tp->str = s;
	tp->closure = NULL;
	RefEnd(tp);
	return s;
#else
	return str("%C", closure);
#endif
}

extern Term *termcat(Term *t1, Term *t2) {
	if (t1 == NULL)
		return t2;
	if (t2 == NULL)
		return t1;

	Ref(Term *, term, mkstr(NULL));
	Ref(char *, str1, getstr(t1));
	Ref(char *, str2, getstr(t2));
	term->str = str("%s%s", str1, str2);
	RefEnd2(str2, str1);
	RefReturn(term);
}


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

static size_t TermScan(void *p) {
	Term *term = p;
	term->closure = forward(term->closure);
	term->str = forward(term->str);
	return sizeof (Term);
}

extern Boolean termeq(Term *term, const char *s) {
	assert(term != NULL);
	if (term->str == NULL)
		return FALSE;
	return streq(term->str, s);
}

extern Boolean isclosure(Term *term) {
	assert(term != NULL);
	return term->closure != NULL;
}
