#
/*
 * C compiler
 *
 *
 */

#include "c0.h"

/*
 * Reduce the degree-of-reference by one.
 * e.g. turn "ptr-to-int" into "int".
 */
decref(at)
{
	register t;

	t = at;
	if ((t & ~TYPE) == 0) {
		error("Illegal indirection");
		return(t);
	}
	return((t>>TYLEN) & ~TYPE | t&TYPE);
}

/*
 * Increase the degree of reference by
 * one; e.g. turn "int" to "ptr-to-int".
 */
incref(t)
{
	return(((t&~TYPE)<<TYLEN) | (t&TYPE) | PTR);
}

/*
 * Make a tree that causes a branch to lbl
 * if the tree's value is non-zero together with the cond.
 */
cbranch(t, lbl, cond)
struct tnode *t;
{
	treeout(t);
	outcode("BNNN", CBRANCH, lbl, cond, line);
}

/*
 * Write out a tree.
 */
rcexpr(atp)
struct tnode *atp;
{
	register struct tnode *tp;

	/*
	 * Special optimization
	 */
	if ((tp=atp)->op==INIT && tp->tr1->op==CON) {
		if (tp->type==CHAR) {
			outcode("B1N0", BDATA, tp->tr1->value);
			return;
		} else if (tp->type==INT) {
			outcode("BN", SINIT, tp->tr1->value);
			return;
		}
	}
	treeout(tp);
	outcode("BN", EXPR, line);
}

treeout(atp)
struct tnode *atp;
{
	register struct tnode *tp;
	register struct hshtab *hp;

	if ((tp = atp) == 0) {
		outcode("B", NULLOP);
		return;
	}
	switch(tp->op) {

	case NAME:
		hp = tp->tr1;
		outcode("BNN", NAME, hp->hclass==0?STATIC:hp->hclass, tp->type);
		if (hp->hclass==EXTERN)
			outcode("S", hp->name);
		else
			outcode("N", hp->hoffset);
		return;

	case LCON:
		outcode("BNNN", tp->op, tp->type, tp->lvalue);
		return;

	case CON:
		outcode("BNN", tp->op, tp->type, tp->value);
		return;

	case FCON:
		outcode("BNF", tp->op, tp->type, tp->cstr);
		return;

	case STRING:
		outcode("BNNN", NAME, STATIC, tp->type, tp->tr1);
		return;

	case FSEL:
		treeout(tp->tr1);
		outcode("BNNN",tp->op,tp->type,tp->tr2->bitoffs,tp->tr2->flen);
		return;


	default:
		treeout(tp->tr1);
		if (opdope[tp->op]&BINARY)
			treeout(tp->tr2);
		outcode("BN", tp->op, tp->type);
		return;
	}
}

/*
 * Generate a branch
 */
branch(lab) {
	outcode("BN", BRANCH, lab);
}

/*
 * Generate a label
 */
label(l) {
	outcode("BN", LABEL, l);
}

/*
 * ap is a tree node whose type
 * is some kind of pointer; return the size of the object
 * to which the pointer points.
 */
plength(ap)
struct tname *ap;
{
	register t, l;
	register struct tnode *p;

	p = ap;
	if (p==0 || ((t=p->type)&~TYPE) == 0)		/* not a reference */
		return(1);
	p->type = decref(t);
	l = length(p);
	p->type = t;
	return(l);
}

/*
 * return the number of bytes in the object
 * whose tree node is acs.
 */
length(acs)
struct tnode *acs;
{
	register t, n;
	register struct tnode *cs;

	cs = acs;
	t = cs->type;
	n = 1;
	while ((t&XTYPE) == ARRAY) {
		t = decref(t);
		n = *cs->subsp;
	}
	if ((t&~TYPE)==FUNC)
		return(0);
	if (t>=PTR)
		return(SZPTR*n);
	switch(t&TYPE) {

	case INT:
		return(SZINT*n);

	case CHAR:
		return(n);

	case FLOAT:
		return(SZFLOAT*n);

	case LONG:
		return(SZLONG*n);

	case DOUBLE:
		return(SZDOUB*n);

	case STRUCT:
		if ((t = cs->strp->ssize) == 0)
			error("Undefined structure");
		return(n * t);
	}
	error("Compiler error (length)");
	return(0);
}

/*
 * The number of bytes in an object, rounded up to a word.
 */
rlength(cs)
struct tnode *cs;
{
	return((length(cs)+ALIGN) & ~ALIGN);
}

/*
 * After an "if (...) goto", look to see if the transfer
 * is to a simple label.
 */
simplegoto()
{
	register struct hshtab *csp;

	if ((peeksym=symbol())==NAME && nextchar()==';') {
		csp = csym;
		if (csp->hclass==0 && csp->htype==0) {
			csp->htype = ARRAY;
			if (csp->hoffset==0)
				csp->hoffset = isn++;
		}
		if ((csp->hclass==0||csp->hclass==STATIC)
		 &&  csp->htype==ARRAY) {
			peeksym = -1;
			return(csp->hoffset);
		}
	}
	return(0);
}

/*
 * Return the next non-white-space character
 */
nextchar()
{
	while (spnextchar()==' ')
		peekc = 0;
	return(peekc);
}

/*
 * Return the next character, translating all white space
 * to blank and handling line-ends.
 */
spnextchar()
{
	register c;

	if ((c = peekc)==0)
		c = getchar();
	if (c=='\t' || c=='\014')	/* FF */
		c = ' ';
	else if (c=='\n') {
		c = ' ';
		if (inhdr==0)
			line++;
		inhdr = 0;
	} else if (c=='\001') {	/* SOH, insert marker */
		inhdr++;
		c = ' ';
	}
	peekc = c;
	return(c);
}

/*
 * is a break or continue legal?
 */
chconbrk(l)
{
	if (l==0)
		error("Break/continue error");
}

/*
 * The goto statement.
 */
dogoto()
{
	register struct tnode *np;

	*cp++ = tree();
	build(STAR);
	chkw(np = *--cp, -1);
	rcexpr(block(JUMP,0,NULL,NULL,np));
}

/*
 * The return statement, which has to convert
 * the returned object to the function's type.
 */
doret()
{
	register struct tnode *t;

	if (nextchar() != ';') {
		t = tree();
		*cp++ = &funcblk;
		*cp++ = t;
		build(ASSIGN);
		cp[-1] = cp[-1]->tr2;
		build(RFORCE);
		rcexpr(*--cp);
	}
	branch(retlab);
}

/*
 * Write a character on the error output.
 */
putchar(c)
{
	write(1, &c, 1);
}

/*
 * Coded output:
 *   B: beginning of line; an operator
 *   N: a number
 *   S: a symbol (external)
 *   1: number 1
 *   0: number 0
 */
outcode(s, a)
char *s;
{
	register *ap, *bufp;
	int n;
	register char *np;

	bufp = obuf;
	if (strflg)
		bufp = sbuf;
	ap = &a;
	for (;;) switch(*s++) {
	case 'B':
		putw(*ap++ | (0376<<8), bufp);
		continue;

	case 'N':
		putw(*ap++, bufp);
		continue;

	case 'F':
		n = 1000;
		goto str;

	case 'S':
		n = NCPS;
	str:
		np = *ap++;
		while (n-- && *np) {
			putc(*np++, bufp);
		}
		putc(0, bufp);
		continue;

	case '1':
		putw(1, bufp);
		continue;

	case '0':
		putw(0, bufp);
		continue;

	case '\0':
		return;

	default:
		error("Botch in outcode");
	}
}
