#
/* C compiler
 *
 *
 */

#include "c0h.c"

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

	if(((o=symbol())==EOF) || o==SEMI)
		return;
	peeksym = o;
	type = INT;
	sclass = EXTERN;
	xdflg = FNDEL;
	if ((elsize = getkeywords(&sclass, &type)) == -1 && peeksym!=NAME)
		goto syntax;
	if (type==STRUCT)
		blkhed();
	do {
		defsym = 0;
		decl1(EXTERN, type, 0, elsize);
		if ((ds=defsym)==0)
			return;
		funcsym = ds;
		ds->hflag |= FNDEL;
		outcode("BS", SYMDEF, ds->name);
		xdflg = 0;
		if ((ds->type&XTYPE)==FUNC) {
			if ((peeksym=symbol())==LBRACE || peeksym==KEYW) {
				funcblk.type = decref(ds->type);
				cfunc(ds->name);
				return;
			}
		} else 
			cinit(ds);
	} 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(cs)
char *cs;
{
	register savdimp;

	savdimp = dimp;
	outcode("BBS", PROG, RLABEL, cs);
	declist(ARG);
	regvar = 5;
	retlab = isn++;
	if ((peeksym = symbol()) != LBRACE)
		error("Compound statement required");
	statement(1);
	outcode("BNB", LABEL, retlab, RETRN);
	dimp = savdimp;
}

/*
 * Process the initializers for an external definition.
 */
cinit(ds)
struct hshtab *ds;
{
	register basetype, nel, ninit;
	int o, width, realwidth;

	nel = 1;
	basetype = ds->type;
	/*
	 * If it's an array, find the number of elements.
	 * "basetype" is the type of thing it's an array of.
	 */
	while ((basetype&XTYPE)==ARRAY) {
		if ((nel = dimtab[ds->ssp&0377])==0)
			nel = 1;
		basetype = decref(basetype);
	}
	realwidth = width = length(ds) / nel;
	/*
	 * Pretend a structure is kind of an array of integers.
	 * This is a kludge.
	 */
	if (basetype==STRUCT) {
		nel *= realwidth/2;
		width = 2;
	}
	if ((peeksym=symbol())==COMMA || peeksym==SEMI) {
		outcode("BSN",CSPACE,ds->name,(nel*width+ALIGN)&~ALIGN);
		return;
	} 
	if (peeksym!=ASSIGN) {
		error("Declaration syntax");
        }
	
	peeksym = ninit = 0;
	outcode("BBS", DATA, NLABEL, ds->name);
	if ((o=symbol())==LBRACE) {
		do
			ninit = cinit1(ds, basetype, width, ninit, nel);
		while ((o=symbol())==COMMA);
		if (o!=RBRACE)
			peeksym = o;
	} else {
		peeksym = o;
		ninit = cinit1(ds, basetype, width, 0, nel);
	}
	/*
	 * Above we pretended that a structure was a bunch of integers.
	 * Readjust in accordance with reality.
	 * First round up partial initializations.
	 */
	if (basetype==STRUCT) {
		if (o = 2*ninit % realwidth)
			outcode("BN", SSPACE, realwidth-o);
		ninit = (2*ninit+realwidth-2) / realwidth;
		nel /= realwidth/2;
	}
	/*
	 * 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)*realwidth);
	else if (ninit>nel) {
		if ((ds->type&XTYPE)==ARRAY)
			dimtab[ds->ssp&0377] = ninit;
		nel = ninit;
	}
	/*
	 * If it's not an array, only one initializer is allowed.
	 */
	if (ninit>1 && (ds->type&XTYPE)!=ARRAY)
		error("Too many initializers");
	if (((nel&width)&ALIGN))
		outcode("B", EVEN);
}

/*
 * Process a single expression in a sequence of initializers
 * for an external. Mainly, it's for checking
 * type compatibility.
 */
cinit1(ds, type, awidth, aninit, nel)
struct hshtab *ds;
{
	float sf;
	register struct tnode *s;
	register width, ninit;

	width = awidth;
	ninit = aninit;
	if ((peeksym=symbol())==STRING && type==CHAR) {
		peeksym = -1;
		if (ninit)
			bxdec();
		putstr(0);
		return(nchstr);
	}
	if (peeksym==RBRACE)
		return(ninit);
	initflg++;
	s = tree();
	initflg = 0;
	switch(width) {

	case 1:
		if (s->op != CON)
			goto bad;
		outcode("B1N0", BDATA, s->value);
		break;

	case 2:
		if (s->op==CON) {
			outcode("B1N0", WDATA, s->value);
			break;
		}
		if (s->op==FCON || s->op==SFCON) {
			if (type==STRUCT) {
				ninit += 3;
				goto prflt;
			}
			goto bad;
		}
		rcexpr(block(1,INIT,0,0,s));
		break;

	case 4:
		sf = fcval;
		outcode("B1N1N0", WDATA, sf);
		goto flt;

	case 8:
	prflt:
		outcode("B1N1N1N1N0", WDATA, fcval);
	flt:
		if (s->op==FCON || s->op==SFCON)
			break;

	default:
	bad:
		bxdec();

	}
	return(++ninit);
}

bxdec()
{
	error("Inconsistent external initialization");
}

/*
 * Process one statement in a function.
 */
statement(d)
{
	register o, o1, o2;
	int o3, o4;
	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("BN", SAVE, 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(1,RFORCE,0,0,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;
		}

		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;
		rcexpr(block(1,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 = treespace;
	treespace = space;
	sline1 = line;
	line = sline;
	statement(0);
	label(contlab);
	rcexpr(st);
	line = sline1;
	treespace = ss;
	branch(l);
	return(0);
}

/*
 * A parenthesized expression,
 * as after "if".
 */
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 stateent, 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.
 */
blkhed()
{
	register pl;
	register struct hshtab *cs;

	autolen = 6;
	declist(0);
	pl = 4;
	while(paraml) {
		parame->hoffset = 0;
		cs = paraml;
		paraml = paraml->hoffset;
		if (cs->htype==FLOAT)
			cs->htype = DOUBLE;
		cs->hoffset = pl;
		cs->hclass = AUTO;
		if ((cs->htype&XTYPE) == ARRAY) {
			cs->htype -= (ARRAY-PTR);	/* set ptr */
			cs->ssp++;		/* pop dims */
		}
		pl += rlength(cs);
	}
	for (cs=hshtab; cs<hshtab+hshsiz; cs++) {
		if (cs->name[0] == '\0')
			continue;
		/* check tagged structure */
		if (cs->hclass>KEYWC && (cs->htype&TYPE)==RSTRUCT) {
			cs->lenp = dimtab[cs->lenp&0377]->lenp;
			cs->htype = cs->htype&~TYPE | STRUCT;
		}
		if (cs->hclass == STRTAG && dimtab[cs->lenp&0377]==0)
			error("Undefined structure: %.8s", cs->name);
		if (cs->hclass == ARG)
			error("Not an argument: %.8s", cs->name);
		if (stflg)
			prste(cs);
	}
	space = treespace;
	outcode("BN", SETREG, regvar);
	return(autolen);
}

/*
 * After a function definition, delete local
 * symbols.
 * Also complain about undefineds.
 */
blkend() {
	register struct hshtab *cs;

	for (cs=hshtab; cs<hshtab+hshsiz; cs++) {
		if (cs->name[0]) {
			if (cs->hclass==0 && (cs->hflag&FNUND)==0) {
				error("%.8s undefined", cs->name);
				cs->hflag |= FNUND;
			}
			if((cs->hflag&FNDEL)==0) {
				cs->name[0] = '\0';
				hshused--;
				cs->hflag &= ~(FNUND|FFIELD);
			}
		}
	}
}

/*
 * 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)
{
	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;
}
