#include	"cc.h"

typedef	struct	Ftab	Ftab;
struct	Ftab
{
	char	op;
	char*	name;
	char	typ;
};
typedef	struct	Gtab	Gtab;
struct	Gtab
{
	char	etype;
	char*	name;
};

Ftab	ftabinit[OEND];
Gtab	gtabinit[NTYPE];

int
isfunct(Node *n)
{
	Type *t, *t1;
	Funct *f;
	Node *l;
	Sym *s;
	int o;

	o = n->op;
	if(n->left == Z)
		goto no;
	t = n->left->type;
	if(t == T)
		goto no;
	f = t->funct;

	switch(o) {
	case OAS:	// put cast on rhs
	case OASI:
	case OASADD:
	case OASAND:
	case OASASHL:
	case OASASHR:
	case OASDIV:
	case OASLDIV:
	case OASLMOD:
	case OASLMUL:
	case OASLSHR:
	case OASMOD:
	case OASMUL:
	case OASOR:
	case OASSUB:
	case OASXOR:
		if(n->right == Z)
			goto no;
		t1 = n->right->type;
		if(t1 == T)
			goto no;
		if(t1->funct == f)
			break;

		l = new(OXXX, Z, Z);
		*l = *n->right;

		n->right->left = l;
		n->right->right = Z;
		n->right->type = t;
		n->right->op = OCAST;

		if(!isfunct(n->right))
			prtree(n, "isfunc !");
		break;

	case OCAST:	// t f(T) or T f(t)
		t1 = n->type;
		if(t1 == T)
			goto no;
		if(f != nil) {
			s = f->castfr[t1->etype];
			if(s == S)
				goto no;
			n->right = n->left;
			goto build;
		}
		f = t1->funct;
		if(f != nil) {
			s = f->castto[t->etype];
			if(s == S)
				goto no;
			n->right = n->left;
			goto build;
		}
		goto no;
	}

	if(f == nil)
		goto no;
	s = f->sym[o];
	if(s == S)
		goto no;

	/*
	 * the answer is yes,
	 * now we rewrite the node
	 * and give diagnostics
	 */
	switch(o) {
	default:
		diag(n, "isfunct op missing %O\n", o);
		goto bad;

	case OADD:	// T f(T, T)
	case OAND:
	case OASHL:
	case OASHR:
	case ODIV:
	case OLDIV:
	case OLMOD:
	case OLMUL:
	case OLSHR:
	case OMOD:
	case OMUL:
	case OOR:
	case OSUB:
	case OXOR:

	case OEQ:	// int f(T, T)
	case OGE:
	case OGT:
	case OHI:
	case OHS:
	case OLE:
	case OLO:
	case OLS:
	case OLT:
	case ONE:
		if(n->right == Z)
			goto bad;
		t1 = n->right->type;
		if(t1 == T)
			goto bad;
		if(t1->funct != f)
			goto bad;
		n->right = new(OLIST, n->left, n->right);
		break;

	case OAS:	// structure copies done by the compiler
	case OASI:
		goto no;

	case OASADD:	// T f(T*, T)
	case OASAND:
	case OASASHL:
	case OASASHR:
	case OASDIV:
	case OASLDIV:
	case OASLMOD:
	case OASLMUL:
	case OASLSHR:
	case OASMOD:
	case OASMUL:
	case OASOR:
	case OASSUB:
	case OASXOR:
		if(n->right == Z)
			goto bad;
		t1 = n->right->type;
		if(t1 == T)
			goto bad;
		if(t1->funct != f)
			goto bad;
		n->right = new(OLIST, new(OADDR, n->left, Z), n->right);
		break;

	case OPOS:	// T f(T)
	case ONEG:
	case ONOT:
	case OCOM:
		n->right = n->left;
		break;


	}

build:
	l = new(ONAME, Z, Z);
	l->sym = s;
	l->type = s->type;
	l->etype = s->type->etype;
	l->xoffset = s->offset;
	l->class = s->class;
	tcomo(l, 0);

	n->op = OFUNC;
	n->left = l;
	n->type = l->type->link;
	if(tcompat(n, T, l->type, tfunct))
		goto bad;
	if(tcoma(n->left, n->right, l->type->down, 1))
		goto bad;
	return 1;

no:
	return 0;

bad:
	diag(n, "cant rewrite typestr for op %O\n", o);
	prtree(n, "isfunct");
	n->type = T;
	return 1;
}

void
dclfunct(Type *t, Sym *s)
{
	Funct *f;
	Node *n;
	Type *f1, *f2, *f3, *f4;
	int o, i, c;
	char str[100];

	if(t->funct)
		return;

	// recognize generated tag of dorm _%d_
	if(t->tag == S)
		goto bad;
	for(i=0; c = t->tag->name[i]; i++) {
		if(c == '_') {
			if(i == 0 || t->tag->name[i+1] == 0)
				continue;
			break;
		}
		if(c < '0' || c > '9')
			break;
	}
	if(c == 0)
		goto bad;

	f = alloc(sizeof(*f));
	for(o=0; o<nelem(f->sym); o++)
		f->sym[o] = S;

	t->funct = f;

	f1 = typ(TFUNC, t);
	f1->down = copytyp(t);
	f1->down->down = t;

	f2 = typ(TFUNC, types[TINT]);
	f2->down = copytyp(t);
	f2->down->down = t;

	f3 = typ(TFUNC, t);
	f3->down = typ(TIND, t);
	f3->down->down = t;

	f4 = typ(TFUNC, t);
	f4->down = t;

	for(i=0;; i++) {
		o = ftabinit[i].op;
		if(o == OXXX)
			break;
		snprint(str, sizeof str, "%s_%s_", t->tag->name, ftabinit[i].name);
		n = new(ONAME, Z, Z);
		n->sym = slookup(str);
		f->sym[o] = n->sym;
		switch(ftabinit[i].typ) {
		default:
			diag(Z, "dclfunct op missing %d\n", ftabinit[i].typ);
			break;

		case 1:	// T f(T,T)	+
			dodecl(xdecl, CEXTERN, f1, n);
			break;

		case 2:	// int f(T,T)	==
			dodecl(xdecl, CEXTERN, f2, n);
			break;

		case 3:	// void f(T*,T)	+=
			dodecl(xdecl, CEXTERN, f3, n);
			break;

		case 4:	// T f(T)	~
			dodecl(xdecl, CEXTERN, f4, n);
			break;
		}
	}
	for(i=0;; i++) {
		o = gtabinit[i].etype;
		if(o == TXXX)
			break;

		/*
		 * OCAST types T1 _T2_T1_(T2)
		 */
		snprint(str, sizeof str, "_%s%s_", gtabinit[i].name, t->tag->name);
		n = new(ONAME, Z, Z);
		n->sym = slookup(str);
		f->castto[o] = n->sym;

		f1 = typ(TFUNC, t);
		f1->down = types[o];
		dodecl(xdecl, CEXTERN, f1, n);

		snprint(str, sizeof str, "%s_%s_", t->tag->name, gtabinit[i].name);
		n = new(ONAME, Z, Z);
		n->sym = slookup(str);
		f->castfr[o] = n->sym;

		f1 = typ(TFUNC, types[o]);
		f1->down = t;
		dodecl(xdecl, CEXTERN, f1, n);
	}
	return;
bad:
	diag(Z, "dclfunct bad %T %s\n", t, s->name);
}

Gtab	gtabinit[NTYPE] =
{
	TCHAR,		"c",
	TUCHAR,		"uc",
	TSHORT,		"h",
	TUSHORT,	"uh",
	TINT,		"i",
	TUINT,		"ui",
	TLONG,		"l",
	TULONG,		"ul",
	TVLONG,		"v",
	TUVLONG,	"uv",
	TFLOAT,		"f",
	TDOUBLE,	"d",
	TXXX
};

Ftab	ftabinit[OEND] =
{
	OADD,		"add",		1,
	OAND,		"and",		1,
	OASHL,		"ashl",		1,
	OASHR,		"ashr",		1,
	ODIV,		"div",		1,
	OLDIV,		"ldiv",		1,
	OLMOD,		"lmod",		1,
	OLMUL,		"lmul",		1,
	OLSHR,		"lshr",		1,
	OMOD,		"mod",		1,
	OMUL,		"mul",		1,
	OOR,		"or",		1,
	OSUB,		"sub",		1,
	OXOR,		"xor",		1,

	OEQ,		"eq",		2,
	OGE,		"ge",		2,
	OGT,		"gt",		2,
	OHI,		"hi",		2,
	OHS,		"hs",		2,
	OLE,		"le",		2,
	OLO,		"lo",		2,
	OLS,		"ls",		2,
	OLT,		"lt",		2,
	ONE,		"ne",		2,

	OASADD,		"asadd",	3,
	OASAND,		"asand",	3,
	OASASHL,	"asashl",	3,
	OASASHR,	"asashr",	3,
	OASDIV,		"asdiv",	3,
	OASLDIV,	"asldiv",	3,
	OASLMOD,	"aslmod",	3,
	OASLMUL,	"aslmul",	3,
	OASLSHR,	"aslshr",	3,
	OASMOD,		"asmod",	3,
	OASMUL,		"asmul",	3,
	OASOR,		"asor",		3,
	OASSUB,		"assub",	3,
	OASXOR,		"asxor",	3,

	OPOS,		"pos",		4,
	ONEG,		"neg",		4,
	OCOM,		"com",		4,
	ONOT,		"not",		4,

//	OPOSTDEC,
//	OPOSTINC,
//	OPREDEC,
//	OPREINC,

	OXXX,
};

//	Node*	nodtestv;

//	Node*	nodvpp;
//	Node*	nodppv;
//	Node*	nodvmm;
//	Node*	nodmmv;
