#
/*
 * C compiler, phase 1
 *
 *
 * Handles processing of declarations,
 * except for top-level processing of
 * externals.
 */

#include "c0.h"

/*
 * Process a sequence of declaration statements
 */
declist(sclass)
{
	register sc, offset;
	struct str *strp;
	int type;

	offset = 0;
	sc = sclass;
	while (getkeywords(&sclass, &type, &strp)) {
		offset = declare(sclass, type, offset, strp);
		sclass = sc;
	}
	return(offset+align(INT, offset, 0));
}

/*
 * Read the keywords introducing a declaration statement
 * Store back the storage class, type, and structure descriptor.
 */
getkeywords(scptr, tptr, strptr)
int *scptr, *tptr;
struct str **strptr;
{
	register skw, tkw, longf;
	int o, isadecl, ismos;

	isadecl = 0;
	longf = 0;
	*strptr = NULL;
	tkw = -1;
	skw = *scptr;
	ismos = skw==MOS;
	for (;;) {
		mosflg = ismos;
		switch ((o=symbol())==KEYW? cval: -1) {

		case AUTO:
		case STATIC:
		case EXTERN:
		case REG:
			if (skw && skw!=cval
			 && (skw!=ARG || cval!=REG))
				error("Conflict in storage class");
			skw = cval;
			break;
	
		case LONG:
			longf++;
			break;

		case STRUCT:
			*strptr = strdec(ismos);
			cval = STRUCT;
		case INT:
		case CHAR:
		case FLOAT:
		case DOUBLE:
			if (tkw>=0)
				error("Type clash");
			tkw = cval;
			break;
	
		default:
			peeksym = o;
			if (isadecl==0)
				return(0);
			if (tkw<0)
				tkw = INT;
			if (skw==0)
				skw = xdflg? EXTERN: AUTO;
			if (longf) {
				if (tkw==FLOAT)
					tkw = DOUBLE;
				else if (tkw==INT)
					tkw = LONG;
				else
					error("Misplaced 'long'");
			}
			*scptr = skw;
			*tptr = tkw;
			return(1);
		}
		isadecl++;
	}
}

/*
 * Process a structure declaration; a subroutine
 * of getkeywords.
 */
struct str *
strdec(mosf)
{
	register elsize, o;
	register struct hshtab *ssym;
	int savebits;
	struct hshtab **savememlist;
	int savenmems;
	struct str *strp;
	struct hshtab *ds;
	struct hshtab *mems[NMEMS];

	mosflg = 1;
	ssym = 0;
	if ((o=symbol())==NAME) {
		ssym = csym;
		if (ssym->hclass==0) {
			ssym->hclass = STRTAG;
			ssym->strp = gblock(sizeof(*strp));
			funcbase = curbase;
			ssym->strp->ssize = 0;
			ssym->strp->memlist = NULL;
		}
		if (ssym->hclass != STRTAG)
			redec();
		strp = ssym->strp;
		mosflg = mosf;
		o = symbol();
	} else {
		strp = gblock(sizeof(*strp));
		funcbase = curbase;
		strp->ssize = 0;
		strp->memlist = NULL;
	}
	mosflg = 0;
	if (o != LBRACE) {
		if (ssym==0)
			goto syntax;
		if (ssym->hclass!=STRTAG)
			error("Bad structure name");
		peeksym = o;
	} else {
		ds = defsym;
		mosflg = 0;
		savebits = bitoffs;
		savememlist = memlist;
		savenmems = nmems;
		memlist = mems;
		nmems = 2;
		bitoffs = 0;
		elsize = declist(MOS);
		bitoffs = savebits;
		defsym = ds;
		if (strp->ssize)
			error("%.8s redeclared", ssym->name);
		strp->ssize = elsize;
		*memlist++ = NULL;
		strp->memlist = gblock((memlist-mems)*sizeof(*memlist));
		funcbase = curbase;
		for (o=0; &mems[o] != memlist; o++)
			strp->memlist[o] = mems[o];
		memlist = savememlist;
		nmems = savenmems;
		if ((o = symbol()) != RBRACE)
			goto syntax;
	}
	return(strp);
   syntax:
	decsyn(o);
	return(0);
}

/*
 * Process a comma-separated list of declarators
 */
declare(askw, tkw, offset, strp)
struct str *strp;
{
	register int o;
	register int skw;

	skw = askw;
	do {
		offset =+ decl1(skw, tkw, offset, strp);
	} while ((o=symbol()) == COMMA);
	if (o!=SEMI && (o!=RPARN || skw!=ARG1))
		decsyn(o);
	return(offset);
}

/*
 * Process a single declarator
 */
decl1(askw, tkw, offset, strp)
struct str *strp;
{
	int t1, chkoff, a, elsize;
	register int type, skw;
	register struct hshtab *dsym;
	struct tdim dim;
	struct field *fldp;
	int *dp;

	skw = askw;
	chkoff = 0;
	mosflg = skw==MOS;
	dim.rank = 0;
	if ((peeksym=symbol())==SEMI || peeksym==RPARN)
		return(0);
	/*
	 * Filler field
	 */
	if (peeksym==COLON && skw==MOS) {
		peeksym = -1;
		t1 = conexp();
		elsize = align(tkw, offset, t1);
		bitoffs =+ t1;
		return(elsize);
	}
	if ((t1=getype(&dim)) < 0)
		goto syntax;
	type = 0;
	do
		type = type<<TYLEN | (t1 & XTYPE);
	while (((t1=>>TYLEN) & XTYPE)!=0);
	type =| tkw;
	dsym = defsym;
	if (dim.rank == 0)
		dsym->subsp = NULL;
	else {
		dp = gblock(dim.rank*sizeof(dim.rank));
		funcbase = curbase;
		for (a=0; a<dim.rank; a++) {
			if ((t1 = dp[a] = dim.dimens[a])
			 && (dsym->htype&XTYPE) == ARRAY
			 && dsym->subsp[a] && t1!=dsym->subsp[a])
				redec();
		}
		dsym->subsp = dp;
	}
	if ((type&XTYPE) == FUNC) {
		if (skw==AUTO)
			skw = EXTERN;
		if (skw!=EXTERN)
			error("Bad func. storage class");
	}
	if (!(dsym->hclass==0
	   || ((skw==ARG||skw==REG) && dsym->hclass==ARG1)
	   || (skw==EXTERN && dsym->hclass==EXTERN && dsym->htype==type)))
		if (skw==MOS && dsym->hclass==MOS && dsym->htype==type)
			chkoff = 1;
		else {
			redec();
			goto syntax;
		}
	if (dsym->hclass && (dsym->htype&TYPE)==STRUCT && tkw==STRUCT)
		if (dsym->strp != strp) {
			error("Warning: structure redeclaration");
			nerror--;
		}
	dsym->htype = type;
	if (strp)
		dsym->strp = strp;
	if (skw==ARG1) {
		if (paraml==0)
			paraml = dsym;
		else
			parame->hoffset = dsym;
		parame = dsym;
	}
	elsize = 0;
	if (skw==MOS) {
		elsize = length(dsym);
		if ((peeksym = symbol())==COLON) {
			elsize = 0;
			peeksym = -1;
			t1 = conexp();
			a = align(type, offset, t1);
			if (dsym->hflag&FFIELD) {
				if (dsym->hstrp->bitoffs!=bitoffs
			 	 || dsym->hstrp->flen!=t1)
					redec();
			} else {
				dsym->hstrp = gblock(sizeof(*fldp));
				funcbase = curbase;
			}
			dsym->hflag =| FFIELD;
			dsym->hstrp->bitoffs = bitoffs;
			dsym->hstrp->flen = t1;
			bitoffs =+ t1;
		} else
			a = align(type, offset, 0);
		elsize =+ a;
		offset =+ a;
		if (++nmems >= NMEMS) {
			error("Too many structure members");
			nmems =- NMEMS/2;
			memlist =- NMEMS/2;
		}
		if (a)
			*memlist++ = &structhole;
		if (chkoff && dsym->hoffset != offset)
			redec();
		dsym->hoffset = offset;
		*memlist++ = dsym;
	}
	if (skw==REG && dsym->hclass!=ARG1)
		if ((dsym->hoffset = goodreg(dsym)) < 0)
			skw = AUTO;
	if (skw==AUTO) {
	/*	if (STAUTO < 0) {	*/
			autolen =- rlength(dsym);
			dsym->hoffset = autolen;
	/*	} else { 			*/
	/*		dsym->hoffset = autolen;	*/
	/*		autolen =+ rlength(dsym);	*/
	/*	}			*/
	} else if (skw==STATIC) {
		dsym->hoffset = isn;
		outcode("BBNBNB", BSS, LABEL, isn++,
		    SSPACE, rlength(dsym), PROG);
	}
	dsym->hclass = skw;
syntax:
	return(elsize);
}

/*
 * Read a declarator and get the implied type
 */
getype(adimp)
struct tdim *adimp;
{
	int type;
	register int o;
	register struct hshtab *ds;
	register struct tdim *dimp;

	ds = defsym;
	dimp = adimp;
	switch(o=symbol()) {

	case TIMES:
		return(getype(dimp)<<TYLEN | PTR);

	case LPARN:
		type = getype(dimp);
		ds = defsym;
		if ((o=symbol()) != RPARN)
			goto syntax;
		goto getf;

	case NAME:
		defsym = ds = csym;
		type = 0;
	getf:
		switch(o=symbol()) {

		case LPARN:
			if (xdflg) {
				xdflg = 0;
				ds = defsym;
				declare(ARG1, 0, 0, 0);
				defsym = ds;
				xdflg++;
			} else
				if ((o=symbol()) != RPARN)
					goto syntax;
			type = type<<TYLEN | FUNC;
			goto getf;

		case LBRACK:
			if (dimp->rank>=5) {
				error("Rank too large");
				dimp->rank = 4;
			}
			if ((o=symbol()) != RBRACK) {
				peeksym = o;
				cval = conexp();
				if ((o=symbol())!=RBRACK)
					goto syntax;
			} else {
				if (dimp->rank!=0)
					error("Null dimension");
				cval = 0;
			}
			for (o=0; o < dimp->rank; o++)
				dimp->dimens[o] =* cval;
			dimp->dimens[dimp->rank++] = cval;
			type = type<<TYLEN | ARRAY;
			goto getf;
		}
		peeksym = o;
		return(type);
	}
syntax:
	decsyn(o);
	return(-1);
}

/*
 * Enforce alignment restrictions in structures,
 * including bit-field considerations.
 */
align(type, offset, aflen)
{
	register a, t, flen;
	char *ftl;

	flen = aflen;
	a = offset;
	t = type;
	ftl = "Field too long";
	if (flen==0) {
		a =+ (NBPC+bitoffs-1) / NBPC;
		bitoffs = 0;
	}
	while ((t&XTYPE)==ARRAY)
		t = decref(t);
	if (t!=CHAR) {
		a = (a+ALIGN) & ~ALIGN;
		if (a>offset)
			bitoffs = 0;
	}
	if (flen) {
		if (type==INT) {
			if (flen > NBPW)
				error(ftl);
			if (flen+bitoffs > NBPW) {
				bitoffs = 0;
				a =+ NCPW;
			}
		} else if (type==CHAR) {
			if (flen > NBPC)
				error(ftl);
			if (flen+bitoffs > NBPC) {
				bitoffs = 0;
				a =+ 1;
			}
		} else
			error("Bad type for field");
	}
	return(a-offset);
}

/*
 * Complain about syntax error in declaration
 */
decsyn(o)
{
	error("Declaration syntax");
	errflush(o);
}

/*
 * Complain about a redeclaration
 */
redec()
{
	error("%.8s redeclared", defsym->name);
}

/*
 * Determine if a variable is suitable for storage in
 * a register; if so return the register number
 */
goodreg(hp)
struct hshtab *hp;
{
	int type;

	type = hp->htype;
	if ((type&TYPE)>CHAR && (type&XTYPE)==0
	 || (type&XTYPE)>PTR || regvar<3)
		return(-1);
	return(--regvar);
}
