710 lines
11 KiB
C
710 lines
11 KiB
C
#
|
|
/* C compiler
|
|
*
|
|
*
|
|
*
|
|
* Called from cc:
|
|
* c0 source temp1 temp2 [ profileflag ]
|
|
* temp1 contains some ascii text and the binary expression
|
|
* trees. Each tree is introduced by the # character.
|
|
* Strings are put on temp2, which cc tacks onto
|
|
* temp1 for assembly.
|
|
*/
|
|
|
|
#include "c0h.c"
|
|
|
|
int isn 1;
|
|
int stflg 1;
|
|
int peeksym -1;
|
|
int line 1;
|
|
int debug 0;
|
|
int dimp 0;
|
|
struct tname funcblk { NAME, 0, 0, REG, 0, 0 };
|
|
int *treespace { osspace };
|
|
|
|
struct kwtab {
|
|
char *kwname;
|
|
int kwval;
|
|
} kwtab[]
|
|
{
|
|
"int", INT,
|
|
"char", CHAR,
|
|
"float", FLOAT,
|
|
"double", DOUBLE,
|
|
"struct", STRUCT,
|
|
"long", LONG,
|
|
"auto", AUTO,
|
|
"extern", EXTERN,
|
|
"static", STATIC,
|
|
"register", REG,
|
|
"goto", GOTO,
|
|
"return", RETURN,
|
|
"if", IF,
|
|
"while", WHILE,
|
|
"else", ELSE,
|
|
"switch", SWITCH,
|
|
"case", CASE,
|
|
"break", BREAK,
|
|
"continue", CONTIN,
|
|
"do", DO,
|
|
"default", DEFAULT,
|
|
"for", FOR,
|
|
"sizeof", SIZEOF,
|
|
0, 0,
|
|
};
|
|
|
|
main(argc, argv)
|
|
char *argv[];
|
|
{
|
|
extern fin;
|
|
register char *sp;
|
|
register i;
|
|
register struct kwtab *ip;
|
|
|
|
if(argc<3) {
|
|
error("Arg count");
|
|
exit(1);
|
|
}
|
|
if((fin=open(argv[1],0))<0) {
|
|
error("Can't find %s", argv[1]);
|
|
exit(1);
|
|
}
|
|
if (fcreat(argv[2], obuf)<0 || fcreat(argv[3], sbuf)<0) {
|
|
error("Can't create temp");
|
|
exit(1);
|
|
}
|
|
if (argc>4)
|
|
proflg++;
|
|
/*
|
|
* The hash table locations of the keywords
|
|
* are marked; if an identifier hashes to one of
|
|
* these locations, it is looked up in in the keyword
|
|
* table first.
|
|
*/
|
|
for (ip=kwtab; (sp = ip->kwname); ip++) {
|
|
i = 0;
|
|
while (*sp)
|
|
i =+ *sp++;
|
|
hshtab[i%hshsiz].hflag = FKEYW;
|
|
}
|
|
while(!eof) {
|
|
extdef();
|
|
blkend();
|
|
}
|
|
outcode("B", EOF);
|
|
strflg++;
|
|
outcode("B", EOF);
|
|
fflush(obuf);
|
|
fflush(sbuf);
|
|
exit(nerror!=0);
|
|
}
|
|
|
|
/*
|
|
* Look up the identifier in symbuf in the symbol table.
|
|
* If it hashes to the same spot as a keyword, try the keyword table
|
|
* first. An initial "." is ignored in the hash.
|
|
* Return is a ptr to the symbol table entry.
|
|
*/
|
|
lookup()
|
|
{
|
|
int ihash;
|
|
register struct hshtab *rp;
|
|
register char *sp, *np;
|
|
|
|
ihash = 0;
|
|
sp = symbuf;
|
|
if (*sp=='.')
|
|
sp++;
|
|
while (sp<symbuf+ncps)
|
|
ihash =+ *sp++;
|
|
rp = &hshtab[ihash%hshsiz];
|
|
if (rp->hflag&FKEYW)
|
|
if (findkw())
|
|
return(KEYW);
|
|
while (*(np = rp->name)) {
|
|
for (sp=symbuf; sp<symbuf+ncps;)
|
|
if (*np++ != *sp++)
|
|
goto no;
|
|
csym = rp;
|
|
return(NAME);
|
|
no:
|
|
if (++rp >= &hshtab[hshsiz])
|
|
rp = hshtab;
|
|
}
|
|
if(++hshused >= hshsiz) {
|
|
error("Symbol table overflow");
|
|
exit(1);
|
|
}
|
|
rp->hclass = 0;
|
|
rp->htype = 0;
|
|
rp->hoffset = 0;
|
|
rp->dimp = 0;
|
|
rp->hflag =| xdflg;
|
|
sp = symbuf;
|
|
for (np=rp->name; sp<symbuf+ncps;)
|
|
*np++ = *sp++;
|
|
csym = rp;
|
|
return(NAME);
|
|
}
|
|
|
|
/*
|
|
* Search the keyword table.
|
|
* Ignore initial "." to avoid member-of-structure
|
|
* problems.
|
|
*/
|
|
findkw()
|
|
{
|
|
register struct kwtab *kp;
|
|
register char *p1, *p2;
|
|
char *wp;
|
|
|
|
wp = symbuf;
|
|
if (*wp=='.')
|
|
wp++;
|
|
for (kp=kwtab; (p2 = kp->kwname); kp++) {
|
|
p1 = wp;
|
|
while (*p1 == *p2++)
|
|
if (*p1++ == '\0') {
|
|
cval = kp->kwval;
|
|
return(1);
|
|
}
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
|
|
/*
|
|
* Return the next symbol from the input.
|
|
* peeksym is a pushed-back symbol, peekc is a pushed-back
|
|
* character (after peeksym).
|
|
* mosflg means that the next symbol, if an identifier,
|
|
* is a member of structure or a structure tag, and it
|
|
* gets a "." prepended to it to distinguish
|
|
* it from other identifiers.
|
|
*/
|
|
symbol() {
|
|
register c;
|
|
register char *sp;
|
|
|
|
if (peeksym>=0) {
|
|
c = peeksym;
|
|
peeksym = -1;
|
|
if (c==NAME)
|
|
mosflg = 0;
|
|
return(c);
|
|
}
|
|
if (peekc) {
|
|
c = peekc;
|
|
peekc = 0;
|
|
} else
|
|
if (eof)
|
|
return(EOF);
|
|
else
|
|
c = getchar();
|
|
loop:
|
|
switch(ctab[c]) {
|
|
|
|
case INSERT: /* ignore newlines */
|
|
inhdr = 1;
|
|
c = getchar();
|
|
goto loop;
|
|
|
|
case NEWLN:
|
|
if (!inhdr)
|
|
line++;
|
|
inhdr = 0;
|
|
|
|
case SPACE:
|
|
c = getchar();
|
|
goto loop;
|
|
|
|
case EOF:
|
|
eof++;
|
|
return(0);
|
|
|
|
case PLUS:
|
|
return(subseq(c,PLUS,INCBEF));
|
|
|
|
case MINUS:
|
|
return(subseq(c,subseq('>',MINUS,ARROW),DECBEF));
|
|
|
|
case ASSIGN:
|
|
if (subseq(' ',0,1)) return(ASSIGN);
|
|
c = symbol();
|
|
if (c>=PLUS && c<=EXOR) {
|
|
if (spnextchar() != ' '
|
|
&& (c==MINUS || c==AND || c==TIMES)) {
|
|
error("Warning: assignment operator assumed");
|
|
nerror--;
|
|
}
|
|
return(c+ASPLUS-PLUS);
|
|
}
|
|
if (c==ASSIGN)
|
|
return(EQUAL);
|
|
peeksym = c;
|
|
return(ASSIGN);
|
|
|
|
case LESS:
|
|
if (subseq(c,0,1)) return(LSHIFT);
|
|
return(subseq('=',LESS,LESSEQ));
|
|
|
|
case GREAT:
|
|
if (subseq(c,0,1)) return(RSHIFT);
|
|
return(subseq('=',GREAT,GREATEQ));
|
|
|
|
case EXCLA:
|
|
return(subseq('=',EXCLA,NEQUAL));
|
|
|
|
case DIVIDE:
|
|
if (subseq('*',1,0))
|
|
return(DIVIDE);
|
|
while ((c = spnextchar()) != EOF) {
|
|
peekc = 0;
|
|
if (c=='*') {
|
|
if (spnextchar() == '/') {
|
|
peekc = 0;
|
|
c = getchar();
|
|
goto loop;
|
|
}
|
|
}
|
|
}
|
|
eof++;
|
|
error("Nonterminated comment");
|
|
return(0);
|
|
|
|
case PERIOD:
|
|
case DIGIT:
|
|
peekc = c;
|
|
if ((c=getnum(c=='0'?8:10)) == FCON)
|
|
cval = isn++;
|
|
return(c);
|
|
|
|
case DQUOTE:
|
|
return(getstr());
|
|
|
|
case SQUOTE:
|
|
return(getcc());
|
|
|
|
case LETTER:
|
|
sp = symbuf;
|
|
if (mosflg) {
|
|
*sp++ = '.';
|
|
mosflg = 0;
|
|
}
|
|
while(ctab[c]==LETTER || ctab[c]==DIGIT) {
|
|
if (sp<symbuf+ncps) *sp++ = c;
|
|
c = getchar();
|
|
}
|
|
while(sp<symbuf+ncps)
|
|
*sp++ = '\0';
|
|
peekc = c;
|
|
if ((c=lookup())==KEYW && cval==SIZEOF)
|
|
c = SIZEOF;
|
|
return(c);
|
|
|
|
case AND:
|
|
return(subseq('&', AND, LOGAND));
|
|
|
|
case OR:
|
|
return(subseq('|', OR, LOGOR));
|
|
|
|
case UNKN:
|
|
error("Unknown character");
|
|
c = getchar();
|
|
goto loop;
|
|
|
|
}
|
|
return(ctab[c]);
|
|
}
|
|
|
|
/*
|
|
* If the next input character is c, return a and advance.
|
|
* Otherwise push back the character and return a.
|
|
*/
|
|
subseq(c,a,b)
|
|
{
|
|
if (spnextchar() != c)
|
|
return(a);
|
|
peekc = 0;
|
|
return(b);
|
|
}
|
|
|
|
/*
|
|
* Read a double-quoted string, placing it on the
|
|
* string buffer.
|
|
*/
|
|
getstr()
|
|
{
|
|
register int c;
|
|
register char *sp;
|
|
|
|
nchstr = 1;
|
|
sp = savstr;
|
|
while((c=mapch('"')) >= 0) {
|
|
nchstr++;
|
|
if (sp >= &savstr[STRSIZ]) {
|
|
sp = savstr;
|
|
error("String too long");
|
|
}
|
|
*sp++ = c;
|
|
}
|
|
strptr = sp;
|
|
cval = isn++;
|
|
return(STRING);
|
|
}
|
|
|
|
/*
|
|
* Write out a string, either in-line
|
|
* or in the string temp file labelled by
|
|
* lab.
|
|
*/
|
|
putstr(lab)
|
|
{
|
|
register char *sp;
|
|
|
|
if (lab) {
|
|
strflg++;
|
|
outcode("BNB", LABEL, lab, BDATA);
|
|
} else
|
|
outcode("B", BDATA);
|
|
for (sp = savstr; sp<strptr; )
|
|
outcode("1N", *sp++ & 0377);
|
|
outcode("100");
|
|
strflg = 0;
|
|
}
|
|
|
|
/*
|
|
* read a single-quoted character constant.
|
|
* The routine is sensitive to the layout of
|
|
* characters in a word.
|
|
*/
|
|
getcc()
|
|
{
|
|
register int c, cc;
|
|
register char *ccp;
|
|
|
|
cval = 0;
|
|
ccp = &cval;
|
|
cc = 0;
|
|
while((c=mapch('\'')) >= 0)
|
|
if(cc++ < NCPW)
|
|
*ccp++ = c;
|
|
if(cc>NCPW)
|
|
error("Long character constant");
|
|
return(CON);
|
|
}
|
|
|
|
/*
|
|
* Read a character in a string or character constant,
|
|
* detecting the end of the string.
|
|
* It implements the escape sequences.
|
|
*/
|
|
mapch(ac)
|
|
{
|
|
register int a, c, n;
|
|
static mpeek;
|
|
|
|
c = ac;
|
|
if (a = mpeek)
|
|
mpeek = 0;
|
|
else
|
|
a = getchar();
|
|
loop:
|
|
if (a==c)
|
|
return(-1);
|
|
switch(a) {
|
|
|
|
case '\n':
|
|
case '\0':
|
|
error("Nonterminated string");
|
|
peekc = a;
|
|
return(-1);
|
|
|
|
case '\\':
|
|
switch (a=getchar()) {
|
|
|
|
case 't':
|
|
return('\t');
|
|
|
|
case 'n':
|
|
return('\n');
|
|
|
|
case 'b':
|
|
return('\b');
|
|
|
|
case '0': case '1': case '2': case '3':
|
|
case '4': case '5': case '6': case '7':
|
|
n = 0;
|
|
c = 0;
|
|
while (++c<=3 && '0'<=a && a<='7') {
|
|
n =<< 3;
|
|
n =+ a-'0';
|
|
a = getchar();
|
|
}
|
|
mpeek = a;
|
|
return(n);
|
|
|
|
case 'r':
|
|
return('\r');
|
|
|
|
case '\n':
|
|
if (!inhdr)
|
|
line++;
|
|
inhdr = 0;
|
|
a = getchar();
|
|
goto loop;
|
|
}
|
|
}
|
|
return(a);
|
|
}
|
|
|
|
/*
|
|
* Read an expression and return a pointer to its tree.
|
|
* It's the classical bottom-up, priority-driven scheme.
|
|
* The initflg prevents the parse from going past
|
|
* "," or ":" because those delimitesrs are special
|
|
* in initializer (and some other) expressions.
|
|
*/
|
|
tree()
|
|
{
|
|
#define SEOF 200
|
|
#define SSIZE 20
|
|
int *op, opst[SSIZE], *pp, prst[SSIZE];
|
|
register int andflg, o;
|
|
register struct hshtab *cs;
|
|
int p, ps, os;
|
|
|
|
space = treespace;
|
|
op = opst;
|
|
pp = prst;
|
|
cp = cmst;
|
|
*op = SEOF;
|
|
*pp = 06;
|
|
andflg = 0;
|
|
|
|
advanc:
|
|
switch (o=symbol()) {
|
|
|
|
case NAME:
|
|
cs = csym;
|
|
if (cs->hclass==0 && cs->htype==0)
|
|
if(nextchar()=='(') {
|
|
/* set function */
|
|
cs->hclass = EXTERN;
|
|
cs->htype = FUNC;
|
|
} else if (initflg)
|
|
cs->hclass = EXTERN;
|
|
else {
|
|
/* set label */
|
|
cs->htype = ARRAY;
|
|
if (cs->hoffset==0)
|
|
cs->hoffset = isn++;
|
|
}
|
|
*cp++ = copname(cs);
|
|
goto tand;
|
|
|
|
case FCON:
|
|
if (!initflg)
|
|
outcode("BBNB1N1N1N1N0B", DATA,LABEL,
|
|
cval, WDATA, fcval, PROG);
|
|
|
|
case CON:
|
|
case SFCON:
|
|
*cp++ = block(1,o,(o==CON?INT:DOUBLE),0,cval);
|
|
goto tand;
|
|
|
|
/* fake a static char array */
|
|
case STRING:
|
|
putstr(cval);
|
|
*cp++ = block(3, NAME, ARRAY+CHAR,0,STATIC,0,cval);
|
|
|
|
tand:
|
|
if(cp>=cmst+cmsiz) {
|
|
error("Expression overflow");
|
|
exit(1);
|
|
}
|
|
if (andflg)
|
|
goto syntax;
|
|
andflg = 1;
|
|
goto advanc;
|
|
|
|
case INCBEF:
|
|
case DECBEF:
|
|
if (andflg)
|
|
o =+ 2;
|
|
goto oponst;
|
|
|
|
case COMPL:
|
|
case EXCLA:
|
|
case SIZEOF:
|
|
if (andflg)
|
|
goto syntax;
|
|
goto oponst;
|
|
|
|
case MINUS:
|
|
if (!andflg) {
|
|
if ((peeksym=symbol())==FCON) {
|
|
fcval = - fcval;
|
|
goto advanc;
|
|
}
|
|
if (peeksym==SFCON) {
|
|
fcval = - fcval;
|
|
cval =^ 0100000;
|
|
goto advanc;
|
|
}
|
|
o = NEG;
|
|
}
|
|
andflg = 0;
|
|
goto oponst;
|
|
|
|
case AND:
|
|
case TIMES:
|
|
if (andflg)
|
|
andflg = 0; else
|
|
if(o==AND)
|
|
o = AMPER;
|
|
else
|
|
o = STAR;
|
|
goto oponst;
|
|
|
|
case LPARN:
|
|
if (andflg) {
|
|
o = symbol();
|
|
if (o==RPARN)
|
|
o = MCALL;
|
|
else {
|
|
peeksym = o;
|
|
o = CALL;
|
|
andflg = 0;
|
|
}
|
|
}
|
|
goto oponst;
|
|
|
|
case RBRACK:
|
|
case RPARN:
|
|
if (!andflg)
|
|
goto syntax;
|
|
goto oponst;
|
|
|
|
case DOT:
|
|
case ARROW:
|
|
mosflg++;
|
|
break;
|
|
|
|
}
|
|
/* binaries */
|
|
if (!andflg)
|
|
goto syntax;
|
|
andflg = 0;
|
|
|
|
oponst:
|
|
p = (opdope[o]>>9) & 077;
|
|
if ((o==COMMA || o==COLON) && initflg)
|
|
p = 05;
|
|
opon1:
|
|
ps = *pp;
|
|
if (p>ps || p==ps && (opdope[o]&RASSOC)!=0) {
|
|
switch (o) {
|
|
|
|
case INCAFT:
|
|
case DECAFT:
|
|
p = 37;
|
|
break;
|
|
case LPARN:
|
|
case LBRACK:
|
|
case CALL:
|
|
p = 04;
|
|
}
|
|
if (op >= &opst[SSIZE-1]) {
|
|
error("expression overflow");
|
|
exit(1);
|
|
}
|
|
*++op = o;
|
|
*++pp = p;
|
|
goto advanc;
|
|
}
|
|
--pp;
|
|
switch (os = *op--) {
|
|
|
|
case SEOF:
|
|
peeksym = o;
|
|
build(0); /* flush conversions */
|
|
return(*--cp);
|
|
|
|
case CALL:
|
|
if (o!=RPARN)
|
|
goto syntax;
|
|
build(os);
|
|
goto advanc;
|
|
|
|
case MCALL:
|
|
*cp++ = block(0,0,0,0); /* 0 arg call */
|
|
os = CALL;
|
|
break;
|
|
|
|
case INCBEF:
|
|
case INCAFT:
|
|
case DECBEF:
|
|
case DECAFT:
|
|
*cp++ = block(1, CON, INT, 0, 1);
|
|
break;
|
|
|
|
case LPARN:
|
|
if (o!=RPARN)
|
|
goto syntax;
|
|
goto advanc;
|
|
|
|
case LBRACK:
|
|
if (o!=RBRACK)
|
|
goto syntax;
|
|
build(LBRACK);
|
|
goto advanc;
|
|
}
|
|
build(os);
|
|
goto opon1;
|
|
|
|
syntax:
|
|
error("Expression syntax");
|
|
errflush(o);
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* Generate a tree node for a name.
|
|
* All the relevant info from the symbol table is
|
|
* copied out, including the name if it's an external.
|
|
* This is because the symbol table is gone in the next
|
|
* pass, so a ptr isn't sufficient.
|
|
*/
|
|
copname(acs)
|
|
struct hshtab *acs;
|
|
{
|
|
register struct hshtab *cs;
|
|
register struct tname *tp;
|
|
register char *cp1;
|
|
int i;
|
|
char *cp2;
|
|
|
|
cs = acs;
|
|
tp = gblock(sizeof(*tp)/NCPW);
|
|
tp->op = NAME;
|
|
tp->type = cs->htype;
|
|
tp->dimp = cs->hdimp;
|
|
if ((tp->class = cs->hclass)==0)
|
|
tp->class = STATIC;
|
|
tp->offset = 0;
|
|
tp->nloc = cs->hoffset;
|
|
if (cs->hclass==EXTERN) {
|
|
gblock((ncps-NCPW)/NCPW);
|
|
cp1 = tp->nname;
|
|
cp2 = cs->name;
|
|
i = ncps;
|
|
do {
|
|
*cp1++ = *cp2++;
|
|
} while (--i);
|
|
}
|
|
if (cs->hflag&FFIELD)
|
|
tp->class = FMOS;
|
|
return(tp);
|
|
}
|