#include <sys/types.h>
#include <stdarg.h>
#include <stdio.h>
#include "ansi.h"
#include "host.h"
#include "files.h"
#include "hash.h"
#include "il.h"
#include "allocate.h"
#include "config.h"

#undef NULL
#define NULL	0

extern file_pos_t yypos;

static node_t *free_list;

typedef enum {
	_Binary,
	_Unary,
	_Pointer,
	_Other
} node_class_t;

#define is_const_int(x)			((x)->node_kind == _Int_Number)
#define is_const_fp(x)			((x)->node_kind == _FP_Number)
#define is_constant(x)			(is_const_int(x) || is_const_fp(x))

void
free_node(n)
	node_t *n;
{
	assert(n->node_kind != _Ident);
	n->node_kind = 0;
	n->node.unary = free_list;
	free_list = n;
}

static node_t*
alloc_node(kind)
	node_kind_t kind;
{
	node_t *n;
	int i;

	n = free_list;
	if (n == NULL) {
		n = (node_t*) allocate(sizeof(node_t)*64);
		for (i = 1; i<63; i++) {
			n[i].node.unary = &n[i+1];
		}
		free_list = &n[1];
	}
	else {
		free_list = n->node.unary;
		memset(n, 0, sizeof(node_t));
	}

	n->node_def = yypos;
	n->node_kind = kind;
	return n;
}

node_class_t
node_classof(kind)
	node_kind_t kind;
{
	if (kind >= _List && kind <= _Shr) {
		return _Binary;
	}

	if (kind >= _Sizeof && kind <= _Indirect) {
		return _Unary;
	}

	switch (kind) {
	  case _Ident:
		return _Pointer;
	  default:
		break;
	}

	return _Other;
}

node_t*
new_node(node_kind_t kind, ...)
{
	va_list ap;
	node_t *n;

	va_start(ap, kind);

	n = alloc_node(kind);

	switch (node_classof(kind)) {
	  case _Binary:
		n->node.binary.l = va_arg(ap, node_t*);
		n->node.binary.r = va_arg(ap, node_t*);
		break;
	  case _Unary:
		n->node.unary = va_arg(ap, node_t*);
		break;
	  case _Pointer:
		n->node.id.name = va_arg(ap, char*);
		break;
	  case _Other:
		switch (kind) {
		  case _Elipsis:
			break;
		  case _String:
			n->node.str.form = va_arg(ap, char*);
			n->node.str.len = va_arg(ap, int);
			break;
		  case _Sym:
			n->node.sym = va_arg(ap, symbol_t*);
			break;
		  case _Type:
			n->node.typ = va_arg(ap, typeinfo_t*);
			break;
		  case _Cond:
			n->node.cond.bool = va_arg(ap, node_t*);
			n->node.cond.tru = va_arg(ap, node_t*);
			n->node.cond.fals = va_arg(ap, node_t*);
			break;
		  case _FP_Number:
			n->node.fval = va_arg(ap, host_float_t);
			break;
		  case _Int_Number:
			n->node.ival = va_arg(ap, host_int_t);
			break;
		  default:
			fatal(__FILE__,__LINE__,"Unandled noded - (%d)", kind);
			break;
		}
		break;
	  default:
		assert(0);
		break;
	}

	va_end(ap);

	return n;
}

static void
promote(n)
	node_t *n;
{
	node_t *l, *r;
	host_float_t fv;

	assert(n != NULL);

	l = n->node.binary.l;
	r = n->node.binary.r;

	assert(l != NULL);
	assert(r != NULL);

	if (is_const_int(l) && is_const_fp(r)) {
		fv = (host_float_t) l->node.ival;
		l->node_kind = _FP_Number;
		l->node.fval = fv;
	}
	else if (is_const_fp(l) && is_const_int(r)) {
		fv = (host_float_t) r->node.ival;
		r->node_kind = _FP_Number;
		r->node.fval = fv;
	}
}

static void reduce_binary();

/* Force constants to RHS */
static void
distributive(n)
	node_t *n;
{
	node_t *l, *r, *dr;

	assert(n != NULL);

	l = n->node.binary.l;	assert(l != NULL);
	r = n->node.binary.r;	assert(r != NULL);

	if (is_constant(l) && !is_constant(r)) {
		n->node.binary.l = r;
		n->node.binary.r = l;
	}

	if (is_constant(r) && l->node_kind == n->node_kind) {
		dr = l->node.binary.r;
		if (is_constant(dr)) {
			/* Transformation
			 *
			 *		 (n)		(n)
			 *		/   \	   /  \
			 *	  (l)   (C)	 (?)  (l)
			 *	 /  \			 /	 \
			 * (?)  (C)		   (C)	 (C)
			 */

			n->node.binary.l = l->node.binary.l;
			n->node.binary.r = l;
			l->node.binary.l = dr;
			l->node.binary.r = r;
			reduce_binary(l);
		}
	}
}

static void
reduce_binary(n)
	node_t *n;
{
	node_t *l, *r;

	if (n == NULL) return;

	switch (n->node_kind) {
	  case _Add:
		distributive(n);
		promote(n);
		l = n->node.binary.l;	assert(l != NULL);
		r = n->node.binary.r;	assert(r != NULL);
		if (is_constant(l)) {	
			assert(is_constant(r)); /* because distributive() */
			if (is_const_int(l)) {
				n->node_kind = _Int_Number;
				n->node.ival = l->node.ival + r->node.ival;
			}
			else {
				n->node_kind = _FP_Number;
				n->node.fval = l->node.fval + r->node.fval;
			}
			free_node(l); free_node(r);
		}
		break;
	  case _Sub:
		promote(n);
		l = n->node.binary.l;	assert(l != NULL);
		r = n->node.binary.r;	assert(r != NULL);
		if (is_constant(l) && is_constant(r)) {	
			if (is_const_int(l)) {
				n->node_kind = _Int_Number;
				n->node.ival = l->node.ival - r->node.ival;
			}
			else {
				n->node_kind = _FP_Number;
				n->node.fval = l->node.fval - r->node.fval;
			}
			free_node(l); free_node(r);
		}
		break;
	  case _Mul:
		distributive(n);
		promote(n);
		l = n->node.binary.l;	assert(l != NULL);
		r = n->node.binary.r;	assert(r != NULL);
		if (is_constant(l)) {	
			assert(is_constant(r)); /* because distributive() */
			if (is_const_int(l)) {
				n->node_kind = _Int_Number;
				n->node.ival = l->node.ival * r->node.ival;
			}
			else {
				n->node_kind = _FP_Number;
				n->node.fval = l->node.fval * r->node.fval;
			}
			free_node(l); free_node(r);
		}
		break;
	  case _Div:
		promote(n);
		l = n->node.binary.l;	assert(l != NULL);
		r = n->node.binary.r;	assert(r != NULL);
		if (is_constant(l) && is_constant(r)) {	
			if (is_const_int(l)) {
				if (r->node.ival != 0) {
					n->node_kind = _Int_Number;
					n->node.ival = l->node.ival / r->node.ival;
					free_node(l); free_node(r);
				}
			}
			else {
				if (r->node.fval != 0.0) {
					n->node_kind = _FP_Number;
					n->node.fval = l->node.fval / r->node.fval;
					free_node(l); free_node(r);
				}
			}
		}
		break;
	  case _Rem:
		l = n->node.binary.l;	assert(l != NULL);
		r = n->node.binary.r;	assert(r != NULL);
		if (is_const_int(l) && is_const_int(r)) {	
			if (r->node.ival != 0) {
				n->node_kind = _Int_Number;
				n->node.ival = l->node.ival % r->node.ival;
				free_node(l); free_node(r);
			}
		}
		break;
	  case _Shl:
		l = n->node.binary.l;	assert(l != NULL);
		r = n->node.binary.r;	assert(r != NULL);
		if (is_const_int(l) && is_const_int(r)) {
			n->node_kind = _Int_Number;
			n->node.ival = l->node.ival << r->node.ival;
			free_node(l);
			free_node(r);
		}
		break;
	  case _Shr:
		l = n->node.binary.l;	assert(l != NULL);
		r = n->node.binary.r;	assert(r != NULL);
		if (is_const_int(l) && is_const_int(r)) {
			n->node_kind = _Int_Number;
			n->node.ival = l->node.ival >> r->node.ival;
			free_node(l);
			free_node(r);
		}
		break;
	  case _Bor:
		distributive(n);
		l = n->node.binary.l;	assert(l != NULL);
		r = n->node.binary.r;	assert(r != NULL);
		if (is_const_int(l) && is_const_int(r)) {
			n->node_kind = _Int_Number;
			n->node.ival = l->node.ival | r->node.ival;
			free_node(l);
			free_node(r);
		}
		break;
	  case _Band:
		distributive(n);
		l = n->node.binary.l;	assert(l != NULL);
		r = n->node.binary.r;	assert(r != NULL);
		if (is_const_int(l) && is_const_int(r)) {
			n->node_kind = _Int_Number;
			n->node.ival = l->node.ival & r->node.ival;
			free_node(l);
			free_node(r);
		}
		break;
	  case _Xor:
		distributive(n);
		l = n->node.binary.l;	assert(l != NULL);
		r = n->node.binary.r;	assert(r != NULL);
		if (is_const_int(l) && is_const_int(r)) {
			n->node_kind = _Int_Number;
			n->node.ival = l->node.ival ^ r->node.ival;
			free_node(l);
			free_node(r);
		}
		break;
	}
}

static void
reduce_unary(n)
	node_t *n;
{
	node_t *l;
	typeinfo_t *typ;
	symbol_t *sym;
	host_int_t iv;

	assert(n != NULL);

	l = n->node.unary;
	if (l == NULL) return;

	switch (n->node_kind) {
	  case _Sizeof:
		switch (l->node_kind) {
		  case _Type:
			typ = l->node.typ;
			break;
		  case _Sym:
			sym = l->node.sym;
			if (sym == NULL) return;
			typ = sym->sym_type;
			break;
		  default:
			return;
		}
		if (typ == NULL) return;
		iv = type_sizeof(typ);
		free_node(l);
		n->node_kind = _Int_Number;
		n->node.ival = iv;
		break;
	  case _Unary_Plus:
		switch (l->node_kind) {
		  case _Int_Number:
			n->node_kind = _Int_Number;
			n->node.ival = l->node.ival;
			free_node(l);
			break;
		  case _FP_Number:
			n->node_kind = _FP_Number;
			n->node.fval = l->node.fval;
			free_node(l);
			break;
		}
		break;
	  case _Unary_Minus:
		switch (l->node_kind) {
		  case _Int_Number:
			n->node_kind = _Int_Number;
			n->node.ival = -l->node.ival;
			free_node(l);
			break;
		  case _FP_Number:
			n->node_kind = _FP_Number;
			n->node.fval = -l->node.fval;
			free_node(l);
			break;
		}
		break;
	  case _Ones_Complement:
		if (l->node_kind == _Int_Number) {
			n->node_kind = _Int_Number;
			n->node.ival = ~l->node.ival;
			free_node(l);
		}
		break;
	  case _Not:
		if (l->node_kind == _Int_Number) {
			n->node_kind = _Int_Number;
			n->node.ival = !l->node.ival;
			free_node(l);
		}
		break;
	  case _Pre_Inc:
	  case _Pre_Dec:
	  case _Post_Inc:
	  case _Post_Dec:
	  case _Addrof:
	  case _Aggregate:
	  case _Indirect:
		break;
	}
}

void
reduce_node(n)
	node_t *n;
{
	if (n == NULL) return;

	switch (node_classof(n->node_kind)) {
	  case _Binary:
		reduce_node(n->node.binary.l);
		reduce_node(n->node.binary.r);
		reduce_binary(n);
		break;
	  case _Unary:
		reduce_node(n->node.unary);
		reduce_unary(n);
		break;
	  default:
		return;
	}
}

node_t*
access_to(ptr, decl)
	node_t *ptr, *decl;
{
	node_t *n;

	assert(ptr != NULL);
	assert(decl != NULL);
	assert(ptr->node_kind == _Indirect);

	for (n = ptr; n; ) {
		switch (n->node_kind) {
		  case _Indirect:
			if (n->node.unary == NULL) {
				n->node.unary = decl;
				return ptr;
			}
			n = n->node.unary;
			break;
		  default:
			fatal(__FILE__,__LINE__,"Unhandled node (%d)", n->node_kind);
			break;
		}
	}

	assert(0);
	return ptr;
}

node_t*
id_from_typedef(typ)
	typeinfo_t *typ;
{
	symbol_t *basetype;

	assert(typ != NULL);

	basetype = typ->type_base;
	assert(basetype != NULL);

	assert(basetype->sym_ident != NULL);
	assert(basetype->sym_ident->node_kind == _Ident);
	assert(basetype->sym_ident->node.id.name != NULL);
	return new_node(_Ident, basetype->sym_ident->node.id.name);
}


syntax highlighted by Code2HTML, v. 0.9.1