+#
+/* C compiler
+ *
+ *
+ */
+
+#include "c0.h"
+
+/*
+ * Process a single external definition
+ */
+extdef()
+{
+ register o;
+ int sclass, scflag, *cb;
+ struct hshtab typer;
+ register struct hshtab *ds;
+
+ if(((o=symbol())==EOFC) || o==SEMI)
+ return;
+ peeksym = o;
+ sclass = 0;
+ blklev = 0;
+ if (getkeywords(&sclass, &typer)==0) {
+ sclass = EXTERN;
+ if (peeksym!=NAME)
+ goto syntax;
+ }
+ scflag = 0;
+ if (sclass==DEFXTRN) {
+ scflag++;
+ sclass = EXTERN;
+ }
+ if (sclass!=EXTERN && sclass!=STATIC && sclass!=TYPEDEF)
+ error("Illegal storage class");
+ do {
+ defsym = 0;
+ paraml = 0;
+ parame = 0;
+ if (sclass==TYPEDEF) {
+ decl1(TYPEDEF, &typer, 0, NULL);
+ continue;
+ }
+ decl1(EXTERN, &typer, 0, NULL);
+ if ((ds=defsym)==0)
+ return;
+ funcsym = ds;
+ if ((ds->type&XTYPE)==FUNC) {
+ if ((peeksym=symbol())==LBRACE || peeksym==KEYW
+ || (peeksym==NAME && csym->hclass==TYPEDEF)) {
+ funcblk.type = decref(ds->type);
+ funcblk.strp = ds->strp;
+ setinit(ds);
+ outcode("BS", SYMDEF, sclass==EXTERN?ds->name:"");
+ cfunc();
+ return;
+ }
+ if (paraml)
+ error("Inappropriate parameters");
+ } else if ((o=symbol())==COMMA || o==SEMI) {
+ peeksym = o;
+ o = (length(ds)+ALIGN) & ~ALIGN;
+ if (sclass==STATIC) {
+ setinit(ds);
+ outcode("BSBBSBN", SYMDEF, "", BSS, NLABEL, ds->name, SSPACE, o);
+ } else if (scflag)
+ outcode("BSN", CSPACE, ds->name, o);
+ } else {
+ if (o!=ASSIGN)
+ peeksym = o;
+ setinit(ds);
+ if (sclass==EXTERN)
+ outcode("BS", SYMDEF, ds->name);
+ outcode("BBS", DATA, NLABEL, ds->name);
+ cb = funcbase;
+ if (cinit(ds, 1, sclass) & ALIGN)
+ outcode("B", EVEN);
+ if (maxdecl > cb)
+ cb = maxdecl;
+ 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();
+}
+
+/*
+ * Process a function definition.
+ */
+cfunc()
+{
+ register int *cb;
+ register sloc;
+
+ sloc = isn;
+ isn =+ 2;
+ outcode("BBS", PROG, RLABEL, funcsym->name);
+ if (proflg)
+ outcode("BN", PROFIL, isn++);
+ cb = curbase;
+ regvar = 5;
+ autolen = STAUTO;
+ maxauto = STAUTO;
+ blklev = 1;
+ declist(ARG);
+ outcode("B", SAVE);
+ funchead();
+ branch(sloc);
+ label(sloc+1);
+ retlab = isn++;
+ blklev = 0;
+ if ((peeksym = symbol()) != LBRACE)
+ error("Compound statement required");
+ statement();
+ outcode("BNB", LABEL, retlab, RETRN);
+ label(sloc);
+ outcode("BN", SETSTK, -maxauto);
+ branch(sloc+1);
+ if (cb < maxdecl)
+ cb = maxdecl;
+ curbase = funcbase = cb;
+}
+
+/*
+ * Process the initializers for an external definition.
+ */
+cinit(anp, flex, sclass)
+struct hshtab *anp;
+{
+ register struct phshtab *np;
+ register nel, ninit;
+ int width, isarray, o, brace, realtype, *cb;
+ struct tnode *s;
+
+ cb = funcbase;
+ np = gblock(sizeof(*np));
+ funcbase = curbase;
+ cpysymb(np, anp);
+ realtype = np->type;
+ isarray = 0;
+ if ((realtype&XTYPE) == ARRAY)
+ isarray++;
+ else
+ flex = 0;
+ width = length(np);
+ nel = 1;
+ /*
+ * If it's an array, find the number of elements.
+ * temporarily modify to look like kind of thing it's
+ * an array of.
+ */
+ if (sclass==AUTO)
+ if (isarray || realtype==STRUCT)
+ error("No auto. aggregate initialization");
+ if (isarray) {
+ np->type = decref(realtype);
+ np->subsp++;
+ if (width==0 && flex==0)
+ error("0-length row: %.8s", anp->name);
+ o = length(np);
+ /* nel = ldiv(0, width, o); */
+ nel = (unsigned)width/o;
+ width = o;
+ }
+ 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) {
+ if (sclass==AUTO)
+ error("No strings in automatic");
+ peeksym = -1;
+ putstr(0, flex?10000:nel);
+ ninit =+ nchstr;
+ o = symbol();
+ break;
+ } else if (np->type==STRUCT) {
+ strinit(np, sclass);
+ } else if ((np->type&ARRAY)==ARRAY || peeksym==LBRACE)
+ cinit(np, 0, sclass);
+ else {
+ initflg++;
+ s = tree();
+ initflg = 0;
+ if (np->hflag&FFIELD)
+ error("No field initialization");
+ *cp++ = nblock(np);
+ *cp++ = s;
+ build(ASSIGN);
+ if (sclass==AUTO||sclass==REG)
+ rcexpr(*--cp);
+ else if (sclass==ENUMCON) {
+ if (s->op!=CON)
+ error("Illegal enum constant for %.8s", anp->name);
+ anp->hoffset = s->value;
+ } else
+ rcexpr(block(INIT,np->type,NULL,NULL,(*--cp)->tr2));
+ }
+ ninit++;
+ if ((ninit&077)==0 && sclass==EXTERN)
+ outcode("BS", SYMDEF, "");
+ } 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 && sclass!=AUTO)
+ outcode("BN", SSPACE, (nel-ninit)*width);
+ else if (ninit>nel) {
+ if (flex && nel==0) {
+ np->subsp[-1] = ninit;
+ } else
+ error("Too many initializers: %.8s", anp->name);
+ nel = ninit;
+ }
+ curbase = funcbase = cb;
+ return(nel*width);
+}
+
+/*
+ * Initialize a structure
+ */
+strinit(np, sclass)
+struct tnode *np;
+{
+ register struct hshtab **mlp;
+ static zerloc;
+ register int o, brace;
+
+ if ((mlp = np->strp->memlist)==NULL) {
+ mlp = &zerloc;
+ error("Undefined structure initialization");
+ }
+ 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(&funcblk, 0, sclass);
+ } else
+ cinit(*mlp++, 0, sclass);
+ if (*mlp == &structhole) {
+ outcode("B", EVEN);
+ mlp++;
+ }
+ } while ((o=symbol())==COMMA && (*mlp || brace));
+ if (sclass!=AUTO && sclass!=REG) {
+ 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()
+{
+ register o, o1, o2;
+ int o3;
+ struct tnode *np;
+ int sauto, sreg;
+
+stmt:
+ switch(o=symbol()) {
+
+ case EOFC:
+ error("Unexpected EOF");
+ case SEMI:
+ return;
+
+ case LBRACE:
+ sauto = autolen;
+ sreg = regvar;
+ blockhead();
+ while (!eof) {
+ if ((o=symbol())==RBRACE) {
+ autolen = sauto;
+ if (sreg!=regvar)
+ outcode("BN", SETREG, sreg);
+ regvar = sreg;
+ blkend();
+ return;
+ }
+ peeksym = o;
+ statement();
+ }
+ 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();
+ if ((o=symbol())==KEYW && cval==ELSE) {
+ o2 = isn++;
+ branch(o2);
+ label(o1);
+ statement();
+ label(o2);
+ return;
+ }
+ peeksym = o;
+ label(o1);
+ return;
+
+ case WHILE:
+ o1 = contlab;
+ o2 = brklab;
+ label(contlab = isn++);
+ cbranch(pexpr(), brklab=isn++, 0);
+ statement();
+ 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();
+ 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 (deflab)
+ error("More than 1 'default'");
+ 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();
+ return;
+ }
+ error("Unknown keyword");
+ goto syntax;
+
+ case NAME:
+ if (nextchar()==':') {
+ peekc = 0;
+ o1 = csym;
+ if (o1->hclass>0) {
+ if (o1->hblklev==0) {
+ pushdecl(o1);
+ o1->hoffset = 0;
+ } else {
+ defsym = o1;
+ redec();
+ goto stmt;
+ }
+ }
+ o1->hclass = STATIC;
+ o1->htype = ARRAY;
+ o1->hflag =| FLABL;
+ 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();
+ branch(contlab);
+ return(0);
+ }
+ l = contlab;
+ contlab = isn++;
+ st = tree();
+ sline = line;
+ if ((o=symbol()) != RPARN)
+ return(o);
+ ss = funcbase;
+ funcbase = curbase;
+ statement();
+ sline1 = line;
+ line = sline;
+ label(contlab);
+ rcexpr(st);
+ line = sline1;
+ if (ss < maxdecl)
+ ss = maxdecl;
+ 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();
+ 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;
+}
+
+/*
+ * funchead is called at the start of each function
+ * to process the arguments, which have been linked in a list.
+ * This list is necessary because in
+ * f(a, b) float b; int a; ...
+ * the names are seen before the types.
+ */
+/*
+ * 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};
+funchead()
+{
+ register pl;
+ register struct hshtab *cs;
+ struct tnode *bstack[2];
+
+ 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==AREG && (hreg.hoffset=goodreg(cs))>=0) {
+ bstack[0] = &areg;
+ bstack[1] = nblock(cs);
+ cp = &bstack[2];
+ areg.type = cs->htype;
+ cs->hclass = AUTO;
+ build(ASSIGN);
+ rcexpr(bstack[0]);
+ cs->hoffset = hreg.hoffset;
+ cs->hclass = REG;
+ } else
+ cs->hclass = AUTO;
+ prste(cs);
+ }
+ for (cs=hshtab; cs<hshtab+HSHSIZ; cs++) {
+ if (cs->name[0] == '\0')
+ continue;
+ if (cs->hclass == ARG || cs->hclass==AREG)
+ error("Not an argument: %.8s", cs->name);
+ }
+ outcode("BN", SETREG, regvar);
+}
+
+blockhead()
+{
+ register r;
+
+ r = regvar;
+ blklev++;
+ declist(0);
+ if (r != regvar)
+ outcode("BN", SETREG, regvar);
+}
+
+/*
+ * After the end of a block, delete local
+ * symbols; save those that are external.
+ * Also complain about undefined labels.
+ */
+blkend()
+{
+ register struct hshtab *cs, *ncs;
+ struct hshtab *endcs;
+ register i;
+
+ blklev--;
+ for (cs=hshtab; cs->name[0] && cs<hshtab+HSHSIZ-1; ++cs)
+ ;
+ endcs = cs;
+ do if (cs->name[0]) {
+ if (cs->hblklev <= blklev)
+ continue;
+ if ((cs->hclass!=EXTERN || blklev!=0)
+ && ((cs->hflag&FLABL)==0 || blklev==0)) {
+ if (cs->hclass==0)
+ error("%.8s undefined", cs->name);
+ if ((ncs = cs->hpdown)==NULL) {
+ cs->name[0] = '\0';
+ hshused--;
+ cs->hflag =& FKEYW;
+ } else {
+ cpysymb(cs, ncs);
+ }
+ continue;
+ }
+ /*
+ * Retained name; must rehash.
+ */
+ for (i=0; i<NCPS; i++)
+ symbuf[i] = cs->name[i];
+ mossym = cs->hflag&FMOS;
+ lookup();
+ if ((ncs=csym) != cs) {
+ cs->name[0] = '\0';
+ hshused--;
+ i = ncs->hflag;
+ cpysymb(ncs, cs);
+ ncs->hflag =| i&FKEYW;
+ cs->hflag =& FKEYW;
+ }
+ if (ncs->hblklev>1 || (ncs->hblklev>0 && ncs->hclass==EXTERN))
+ ncs->hblklev--;
+ } while ((cs = (cs<&hshtab[HSHSIZ-1])? ++cs: hshtab) != endcs);
+}
+
+/*
+ * 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;
+}