#
/* C compiler
 *
 *
 */

#include "c0.h"

/*
 * Process a single external definition
 */
extdef()
{
	register o;
	int type, sclass, *cb;
	struct str *strp;
	register struct hshtab *ds;

	if(((o=symbol())==EOF) || o==SEMI)
		return;
	peeksym = o;
	type = INT;
	sclass = 0;
	strp = NULL;
	xdflg = FNDEL;
	if (getkeywords(&sclass, &type, &strp)==0) {
		sclass = EXTERN;
		if (peeksym!=NAME)
			goto syntax;
	}
	if (sclass!=EXTERN && sclass!=STATIC)
		error("Illegal storage class");
	do {
		defsym = 0;
		paraml = 0;
		parame = 0;
		decl1(EXTERN, type, 0, strp);
		if ((ds=defsym)==0)
			return;
		funcsym = ds;
		ds->hflag =| FNDEL;
		xdflg = 0;
		if ((ds->type&XTYPE)==FUNC) {
			if ((peeksym=symbol())==LBRACE || peeksym==KEYW) {
				funcblk.type = decref(ds->type);
				setinit(ds);
				if (sclass==EXTERN)
					outcode("BS", SYMDEF, ds->name);
				cfunc();
				return;
			}
			if (paraml)
				error("Inappropriate parameters");
		} else if ((peeksym=symbol())==COMMA || peeksym==SEMI) {
			o = (length(ds)+ALIGN) & ~ALIGN;
			if (sclass==STATIC) {
				setinit(ds);
				outcode("BBSBN", BSS, NLABEL, ds->name, SSPACE, o);
			} else
				outcode("BSN", CSPACE, ds->name, o);
		} else {
			setinit(ds);
			if (sclass==EXTERN)
				outcode("BS", SYMDEF, ds->name);
			outcode("BBS", DATA, NLABEL, ds->name);
			cb = funcbase;
			if ((cinit(nblock(ds), 1)) & ALIGN)
				outcode("B", EVEN);
			funcbase = cb;
		}
	} while ((o=symbol())==COMMA);
	if (o==SEMI)
		return;
syntax:
	if (o==RBRACE) {
		error("Too many }'s");
		peeksym = 0;
		return;
	}
	error("External definition syntax");
	errflush(o);
	statement(0);
}

/*
 * Process a function definition.
 */
cfunc()
{
	register int *cb;

	outcode("BBS", PROG, RLABEL, funcsym->name);
	cb = curbase;
	regvar = 5;
	declist(ARG);
	retlab = isn++;
	if ((peeksym = symbol()) != LBRACE)
		error("Compound statement required");
	statement(1);
	outcode("BNB", LABEL, retlab, RETRN);
	curbase = funcbase = cb;
	blkend();
}

/*
 * Process the initializers for an external definition.
 */
cinit(anp, flex)
struct tnode *anp;
{
	register struct tnode *np;
	register nel, ninit;
	int width, isarray, o, brace, realtype, *cb;
	struct tnode *s;

	cb = funcbase;
	funcbase = curbase;
	np = anp;
	realtype = np->type;
	isarray = 0;
	if ((realtype&XTYPE) == ARRAY)
		isarray++;
	else
		flex = 0;
	width = length(np);
	/*
	 * If it's an array, find the number of elements.
	 * temporarily modify to look like kind of thing it's
	 * an array of.
	 */
	if (isarray) {
		np->type = decref(realtype);
		np->subsp++;
		if (width==0 && flex==0)
			error("0-length row: %.8s", np->tr1->name);
	}
	nel = ldiv(0, width, length(np));
	width = ldiv(0, width, nel);
	brace = 0;
	if ((peeksym=symbol())==LBRACE && (isarray || np->type!=STRUCT)) {
		peeksym = -1;
		brace++;
	}
	ninit = 0;
	do {
		if ((o=symbol())==RBRACE)
			break;
		peeksym = o;
		if (o==STRING && realtype==ARRAY+CHAR) {
			peeksym = -1;
			putstr(0, flex?10000:nel);
			ninit =+ nchstr;
			o = symbol();
			break;
		} else if (np->type==STRUCT)
			strinit(np);
		else if ((np->type&ARRAY)==ARRAY || peeksym==LBRACE)
			cinit(block(NAME,np->type,np->subsp,np->strp,np->tr1), 0);
		else {
			initflg++;
			s = tree();
			initflg = 0;
			*cp++ = np;
			*cp++ = s;
			build(ASSIGN);
			rcexpr(block(INIT, np->type, NULL, NULL, (*--cp)->tr2));
		}
		ninit++;
	} while ((o=symbol())==COMMA && (ninit<nel || brace || flex));
	if (brace==0 || o!=RBRACE)
		peeksym = o;
	/*
	 * If there are too few initializers, allocate
	 * more storage.
	 * If there are too many initializers, extend
	 * the declared size for benefit of "sizeof"
	 */
	if (ninit<nel)
		outcode("BN", SSPACE, (nel-ninit)*width);
	else if (ninit>nel) {
		if (flex && nel==0) {
			np->subsp[-1] = ninit;
			if ((np->type&XTYPE) == ARRAY)
				np->subsp[-1] =* np->subsp[0];
		} else
			error("Too many initializers: %.8s", np->tr1->name);
		nel = ninit;
	}
	curbase = funcbase = cb;
	return(nel*width);
}

/*
 * Initialize a structure
 */
strinit(np)
struct tnode *np;
{
	register struct hshtab **mlp;
	register int o, brace;

	mlp = np->strp->memlist;
	brace = 0;
	if ((o = symbol()) == LBRACE)
		brace++;
	else
		peeksym = o;
	do {
		if ((o=symbol()) == RBRACE)
			break;
		peeksym = o;
		if (*mlp==0) {
			error("Too many structure initializers");
			cinit(nblock(&funcblk), 0);
		} else
			cinit(nblock(*mlp++), 0);
		if (*mlp ==  &structhole) {
			outcode("B", EVEN);
			mlp++;
		}
	} while ((o=symbol())==COMMA && (*mlp || brace));
	if (*mlp)
		outcode("BN", SSPACE, np->strp->ssize - (*mlp)->hoffset);
	outcode("B", EVEN);
	if (o!=RBRACE || brace==0)
		peeksym = o;
}

/*
 * Mark already initialized
 */
setinit(anp)
struct hshtab *anp;
{
	register struct hshtab *np;

	np = anp;
	if (np->hflag&FINIT)
		error("%s multiply defined", np->name);
	np->hflag =| FINIT;
}

/*
 * Process one statement in a function.
 */
statement(d)
{
	register o, o1, o2;
	int o3;
	struct tnode *np;

stmt:
	switch(o=symbol()) {

	case EOF:
		error("Unexpected EOF");
	case SEMI:
		return;

	case LBRACE:
		if (d) {
			if (proflg)
				outcode("BN", PROFIL, isn++);
			outcode("B", SAVE);
			outcode("BN", SETSTK, -blkhed());
		}
		while (!eof) {
			if ((o=symbol())==RBRACE)
				return;
			peeksym = o;
			statement(0);
		}
		error("Missing '}'");
		return;

	case KEYW:
		switch(cval) {

		case GOTO:
			if (o1 = simplegoto())
				branch(o1);
			else 
				dogoto();
			goto semi;

		case RETURN:
			doret();
			goto semi;

		case IF:
			np = pexpr();
			o2 = 0;
			if ((o1=symbol())==KEYW) switch (cval) {
			case GOTO:
				if (o2=simplegoto())
					goto simpif;
				cbranch(np, o2=isn++, 0);
				dogoto();
				label(o2);
				goto hardif;

			case RETURN:
				if (nextchar()==';') {
					o2 = retlab;
					goto simpif;
				}
				cbranch(np, o1=isn++, 0);
				doret();
				label(o1);
				o2++;
				goto hardif;

			case BREAK:
				o2 = brklab;
				goto simpif;

			case CONTIN:
				o2 = contlab;
			simpif:
				chconbrk(o2);
				cbranch(np, o2, 1);
			hardif:
				if ((o=symbol())!=SEMI)
					goto syntax;
				if ((o1=symbol())==KEYW && cval==ELSE) 
					goto stmt;
				peeksym = o1;
				return;
			}
			peeksym = o1;
			cbranch(np, o1=isn++, 0);
			statement(0);
			if ((o=symbol())==KEYW && cval==ELSE) {
				o2 = isn++;
				branch(o2);
				label(o1);
				statement(0);
				label(o2);
				return;
			}
			peeksym = o;
			label(o1);
			return;

		case WHILE:
			o1 = contlab;
			o2 = brklab;
			label(contlab = isn++);
			cbranch(pexpr(), brklab=isn++, 0);
			statement(0);
			branch(contlab);
			label(brklab);
			contlab = o1;
			brklab = o2;
			return;

		case BREAK:
			chconbrk(brklab);
			branch(brklab);
			goto semi;

		case CONTIN:
			chconbrk(contlab);
			branch(contlab);
			goto semi;

		case DO:
			o1 = contlab;
			o2 = brklab;
			contlab = isn++;
			brklab = isn++;
			label(o3 = isn++);
			statement(0);
			label(contlab);
			contlab = o1;
			if ((o=symbol())==KEYW && cval==WHILE) {
				cbranch(tree(), o3, 1);
				label(brklab);
				brklab = o2;
				goto semi;
			}
			goto syntax;

		case CASE:
			o1 = conexp();
			if ((o=symbol())!=COLON)
				goto syntax;
			if (swp==0) {
				error("Case not in switch");
				goto stmt;
			}
			if(swp>=swtab+SWSIZ) {
				error("Switch table overflow");
			} else {
				swp->swlab = isn;
				(swp++)->swval = o1;
				label(isn++);
			}
			goto stmt;

		case SWITCH:
			o1 = brklab;
			brklab = isn++;
			np = pexpr();
			chkw(np, -1);
			rcexpr(block(RFORCE,0,NULL,NULL,np));
			pswitch();
			brklab = o1;
			return;

		case DEFAULT:
			if (swp==0)
				error("Default not in switch");
			if ((o=symbol())!=COLON)
				goto syntax;
			label(deflab = isn++);
			goto stmt;

		case FOR:
			o1 = contlab;
			o2 = brklab;
			contlab = isn++;
			brklab = isn++;
			if (o=forstmt())
				goto syntax;
			label(brklab);
			contlab = o1;
			brklab = o2;
			return;

		case ELSE:
			error("Inappropriate 'else'");
			statement(0);
			return;
		}
		error("Unknown keyword");
		goto syntax;

	case NAME:
		if (nextchar()==':') {
			peekc = 0;
			o1 = csym;
			if (o1->hclass>0) {
				error("Redefinition");
				goto stmt;
			}
			o1->hclass = STATIC;
			o1->htype = ARRAY;
			if (o1->hoffset==0)
				o1->hoffset = isn++;
			label(o1->hoffset);
			goto stmt;
		}
	}
	peeksym = o;
	rcexpr(tree());

semi:
	if ((o=symbol())==SEMI)
		return;
syntax:
	error("Statement syntax");
	errflush(o);
}

/*
 * Process a for statement.
 */
forstmt()
{
	register int l, o, sline;
	int sline1, *ss;
	struct tnode *st;

	if ((o=symbol()) != LPARN)
		return(o);
	if ((o=symbol()) != SEMI) {		/* init part */
		peeksym = o;
		rcexpr(tree());
		if ((o=symbol()) != SEMI)
			return(o);
	}
	label(contlab);
	if ((o=symbol()) != SEMI) {		/* test part */
		peeksym = o;
		cbranch(tree(), brklab, 0);
		if ((o=symbol()) != SEMI)
			return(o);
	}
	if ((peeksym=symbol()) == RPARN) {	/* incr part */
		peeksym = -1;
		statement(0);
		branch(contlab);
		return(0);
	}
	l = contlab;
	contlab = isn++;
	st = tree();
	sline = line;
	if ((o=symbol()) != RPARN)
		return(o);
	ss = funcbase;
	funcbase = curbase;
	statement(0);
	sline1 = line;
	line = sline;
	label(contlab);
	rcexpr(st);
	line = sline1;
	curbase = funcbase = ss;
	branch(l);
	return(0);
}

/*
 * A parenthesized expression,
 * as after "if".
 */
struct tnode *
pexpr()
{
	register o, t;

	if ((o=symbol())!=LPARN)
		goto syntax;
	t = tree();
	if ((o=symbol())!=RPARN)
		goto syntax;
	return(t);
syntax:
	error("Statement syntax");
	errflush(o);
	return(0);
}

/*
 * The switch statement, which involves collecting the
 * constants and labels for the cases.
 */
pswitch()
{
	register struct swtab *cswp, *sswp;
	int dl, swlab;

	cswp = sswp = swp;
	if (swp==0)
		cswp = swp = swtab;
	branch(swlab=isn++);
	dl = deflab;
	deflab = 0;
	statement(0);
	branch(brklab);
	label(swlab);
	if (deflab==0)
		deflab = brklab;
	outcode("BNN", SWIT, deflab, line);
	for (; cswp < swp; cswp++)
		outcode("NN", cswp->swlab, cswp->swval);
	outcode("0");
	label(brklab);
	deflab = dl;
	swp = sswp;
}

/*
 * blkhed is called at the start of each function.
 * It reads the declarations at the start;
 * then assigns storage locations for the
 * parameters (which have been linked into a list,
 * in order of appearance).
 * This list is necessary because in
 * f(a, b) float b; int a; ...
 * the names are seen before the types.
 * Also, the routine adjusts structures involved
 * in some kind of forward-referencing.
 */
/*
 * Structure resembling a block for a register variable.
 */
struct	hshtab	hreg	{ REG, 0, 0, NULL, NULL, 0 };
struct	tnode	areg	{ NAME, 0, NULL, NULL, &hreg};
blkhed()
{
	register pl;
	register struct hshtab *cs;

	autolen = STAUTO;
	declist(0);
	pl = STARG;
	while(paraml) {
		parame->hoffset = 0;
		cs = paraml;
		paraml = paraml->hoffset;
		if (cs->htype==FLOAT)
			cs->htype = DOUBLE;
		cs->hoffset = pl;
		if ((cs->htype&XTYPE) == ARRAY) {
			cs->htype =- (ARRAY-PTR);	/* set ptr */
			cs->subsp++;		/* pop dims */
		}
		pl =+ rlength(cs);
		if (cs->hclass==REG && (hreg.hoffset=goodreg(cs))>=0) {
			cmst[0] = &areg;
			cmst[1] = nblock(cs);
			cp = &cmst[2];
			areg.type = cs->htype;
			cs->hclass = AUTO;
			build(ASSIGN);
			rcexpr(cmst[0]);
			cs->hoffset = hreg.hoffset;
			cs->hclass = REG;
		} else
			cs->hclass = AUTO;
	}
	for (cs=hshtab; cs<hshtab+HSHSIZ; cs++) {
		if (cs->name[0] == '\0')
			continue;
		if (cs->hclass == ARG)
			error("Not an argument: %.8s", cs->name);
		if (stflg)
			prste(cs);
	}
	outcode("BN", SETREG, regvar);
	return(autolen);
}

/*
 * After a function definition, delete local
 * symbols; save those that are external.
 * Also complain about undefineds.
 */
blkend()
{
	register struct hshtab *cs, *ncs;
	register i;

	for (cs=hshtab; cs->name[0] && cs<hshtab+HSHSIZ-1; ++cs);
	ncs = cs;
	do  if (cs->name[0]) {
		if (cs->hclass==0 && (cs->hflag&FNUND)==0) {
			error("%.8s undefined", cs->name);
			cs->hflag =| FNUND;
		}
		if(cs->hflag&FNDEL)
			continue;
		if (cs->hclass!=EXTERN) {
			cs->name[0] = '\0';
			hshused--;
			cs->hflag =& ~(FNUND|FFIELD);
			continue;
		}
		/*
		 * Now-permanent name; must rehash.
		 */
		for (i=0; i<NCPS; i++)
			symbuf[i] = cs->name[i];
		lookup();
		if (csym != cs) {
			cs->name[0] = '\0';
			cs->hflag =& ~(FNUND|FFIELD);
			hshused--;
			lookup();
			csym->hclass = EXTERN;
			csym->htype = cs->htype;
			csym->hoffset = cs->hoffset;
			csym->hsubsp = cs->hsubsp;
			csym->hstrp = cs->hstrp;
			csym->hflag =| cs->hflag&FFIELD | FNDEL;
		} else
			cs->hflag =| FNDEL;
	} while ((cs = (cs<&hshtab[HSHSIZ-1])? ++cs: hshtab) != ncs);
}

/*
 * write out special definitions of local symbols for
 * benefit of the debugger.  None of these are used
 * by the assembler except to save them.
 */
prste(acs)
struct hshtab *acs;
{
	register struct hshtab *cs;
	register nkind;

	cs = acs;
	switch (cs->hclass) {
	case REG:
		nkind = RNAME;
		break;

	case AUTO:
		nkind = ANAME;
		break;

	case STATIC:
		nkind = SNAME;
		break;

	default:
		return;

	}
	outcode("BSN", nkind, cs->name, cs->hoffset);
}

/*
 * In case of error, skip to the next
 * statement delimiter.
 */
errflush(ao)
{
	register o;

	o = ao;
	while(o>RBRACE)	/* ; { } */
		o = symbol();
	peeksym  = o;
}
