/* tree.c: functions for manipulating parse-trees. (create, copy, delete) */

#include "rc.h"

#include "develop.h"

/* convert if followed by ifnot to else */
static Node *desugar_ifnot(Node *n) {
	if (n->type == nBody && n->u[1].p && n->u[1].p->type == nIfnot) {
		/* (body (if c x) (if-not y)) => (body (if c (else x y))) */
		if (n->u[0].p->type == nIf &&
				n->u[0].p->u[1].p->type != nElse) {
			Node *yes = n->u[0].p;
			Node *no = n->u[1].p;
			Node *els = nalloc(offsetof(Node, u[2]));
			els->type = nElse;
			els->u[1].p = no->u[0].p;
			els->u[0].p = yes->u[1].p;
			yes->u[1].p = els;
			n->u[1].p = NULL;
		} else goto fail;
	} else if (n->type == nBody &&
			n->u[1].p && n->u[1].p->type == nBody &&
			n->u[1].p->u[0].p &&
				n->u[1].p->u[0].p->type == nIfnot) {
		/* (body (if c x) (body (if-not y) z)) =>
			(body (if c (else x y)) z) */
		if (n->u[0].p->type == nIf &&
				n->u[0].p->u[1].p->type != nElse) {
			Node *yes = n->u[0].p;
			Node *no = n->u[1].p->u[0].p;
			Node *els = nalloc(offsetof(Node, u[2]));
			els->type = nElse;
			els->u[1].p = no->u[0].p;
			els->u[0].p = yes->u[1].p;
			yes->u[1].p = els;
			n->u[1].p = n->u[1].p->u[1].p;
		} else goto fail;
	}

	return n;
fail:
	rc_error("`if not' must follow `if'");
	return NULL;
}

/* make a new node, pass it back to yyparse. Used to generate the parsetree. */

extern Node *mk(enum nodetype t,...) {
	va_list ap;
	Node *n;
	va_start(ap, t);
	switch (t) {
	default:
		panic("unexpected node in mk");
		/* NOTREACHED */
	case nDup:
		n = nalloc(offsetof(Node, u[3]));
		n->u[0].i = va_arg(ap, int);
		n->u[1].i = va_arg(ap, int);
		n->u[2].i = va_arg(ap, int);
		break;
	case nWord:
		n = nalloc(offsetof(Node, u[3]));
		n->u[0].s = va_arg(ap, char *);
		n->u[1].s = va_arg(ap, char *);
		n->u[2].i = va_arg(ap, int);
		break;
	case nBang: case nNowait:
	case nCount: case nFlat: case nRmfn: case nSubshell:
	case nVar: case nCase: case nIfnot:
		n = nalloc(offsetof(Node, u[1]));
		n->u[0].p = va_arg(ap, Node *);
		break;
	case nAndalso: case nAssign: case nBackq: case nBody: case nBrace: case nConcat:
	case nElse: case nEpilog: case nIf: case nNewfn: case nCbody:
	case nOrelse: case nPre: case nArgs: case nSwitch:
	case nMatch: case nVarsub: case nWhile: case nLappend:
		n = nalloc(offsetof(Node, u[2]));
		n->u[0].p = va_arg(ap, Node *);
		n->u[1].p = va_arg(ap, Node *);
		break;
	case nForin:
		n = nalloc(offsetof(Node, u[3]));
		n->u[0].p = va_arg(ap, Node *);
		n->u[1].p = va_arg(ap, Node *);
		n->u[2].p = va_arg(ap, Node *);
		break;
	case nPipe:
		n = nalloc(offsetof(Node, u[4]));
		n->u[0].i = va_arg(ap, int);
		n->u[1].i = va_arg(ap, int);
		n->u[2].p = va_arg(ap, Node *);
		n->u[3].p = va_arg(ap, Node *);
		break;
	case nRedir:
	case nNmpipe:
		n = nalloc(offsetof(Node, u[3]));
		n->u[0].i = va_arg(ap, int);
		n->u[1].i = va_arg(ap, int);
		n->u[2].p = va_arg(ap, Node *);
		break;
	}
	n->type = t;

	if (0 && RC_DEVELOP) {
		tree_dump(n);
		fprint(2, "---\n");
	}
	n = desugar_ifnot(n);
	va_end(ap);
	return n;
}

/* copy a tree to malloc space. Used when storing the definition of a function */

extern Node *treecpy(Node *s, void *(*alloc)(size_t)) {
	Node *n;
	if (s == NULL)
		return NULL;
	switch (s->type) {
	default:
		panic("unexpected node in treecpy");
		/* NOTREACHED */
	case nDup:
		n = (*alloc)(offsetof(Node, u[3]));
		n->u[0].i = s->u[0].i;
		n->u[1].i = s->u[1].i;
		n->u[2].i = s->u[2].i;
		break;
	case nWord:
		n = (*alloc)(offsetof(Node, u[3]));
		n->u[0].s = strcpy((char *) (*alloc)(strlen(s->u[0].s) + 1), s->u[0].s);
		if (s->u[1].s != NULL) {
			size_t i = strlen(s->u[0].s);
			n->u[1].s = (*alloc)(i);
			memcpy(n->u[1].s, s->u[1].s, i);
		} else
			n->u[1].s = NULL;
		n->u[2].i = s->u[2].i;
		break;
	case nBang: case nNowait: case nCase:
	case nCount: case nFlat: case nRmfn: case nSubshell: case nVar:
		n = (*alloc)(offsetof(Node, u[1]));
		n->u[0].p = treecpy(s->u[0].p, alloc);
		break;
	case nAndalso: case nAssign: case nBackq: case nBody: case nBrace: case nConcat:
	case nElse: case nEpilog: case nIf: case nNewfn: case nCbody:
	case nOrelse: case nPre: case nArgs: case nSwitch:
	case nMatch: case nVarsub: case nWhile: case nLappend:
		n = (*alloc)(offsetof(Node, u[2]));
		n->u[0].p = treecpy(s->u[0].p, alloc);
		n->u[1].p = treecpy(s->u[1].p, alloc);
		break;
	case nForin:
		n = (*alloc)(offsetof(Node, u[3]));
		n->u[0].p = treecpy(s->u[0].p, alloc);
		n->u[1].p = treecpy(s->u[1].p, alloc);
		n->u[2].p = treecpy(s->u[2].p, alloc);
		break;
	case nPipe:
		n = (*alloc)(offsetof(Node, u[4]));
		n->u[0].i = s->u[0].i;
		n->u[1].i = s->u[1].i;
		n->u[2].p = treecpy(s->u[2].p, alloc);
		n->u[3].p = treecpy(s->u[3].p, alloc);
		break;
	case nRedir:
	case nNmpipe:
		n = (*alloc)(offsetof(Node, u[3]));
		n->u[0].i = s->u[0].i;
		n->u[1].i = s->u[1].i;
		n->u[2].p = treecpy(s->u[2].p, alloc);
		break;
	}
	n->type = s->type;
	return n;
}

/* free a function definition that is no longer needed */

extern void treefree(Node *s) {
	if (s == NULL)
		return;
	switch (s->type) {
	default:
		panic("unexpected node in treefree");
		/* NOTREACHED */
	case nDup:
		break;
	case nWord:
		efree(s->u[0].s);
		efree(s->u[1].s);
		break;
	case nBang: case nNowait:
	case nCount: case nFlat: case nRmfn:
	case nSubshell: case nVar: case nCase:
		treefree(s->u[0].p);
		break;
	case nAndalso: case nAssign: case nBackq: case nBody: case nBrace: case nConcat:
	case nElse: case nEpilog: case nIf: case nNewfn:
	case nOrelse: case nPre: case nArgs: case nCbody:
	case nSwitch: case nMatch:  case nVarsub: case nWhile:
	case nLappend:
		treefree(s->u[1].p);
		treefree(s->u[0].p);
		break;
	case nForin:
		treefree(s->u[2].p);
		treefree(s->u[1].p);
		treefree(s->u[0].p);
		break;
	case nPipe:
		treefree(s->u[2].p);
		treefree(s->u[3].p);
		break;
	case nRedir:
	case nNmpipe:
		treefree(s->u[2].p);
	}
	efree(s);
}
