/********************************************************/
/*							*/
/*		Q/C Compiler Version 3.2		*/
/*			(Part 5)			*/
/*							*/
/*     Copyright (c) 1984 Quality Computer Systems	*/
/*							*/
/*			11/19/84			*/
/********************************************************/

#include "stdio.h"
#include "cstddef.h"
#include "cglbdecl.c"


/* This section identifies tokens in declarations
 *	and manages all of the compiler tables
 */


/* Check next token to see if it is a keyword */
chkeyword()
{
    return (chks("break") || chks("case") || chks("continue")
	    || chks("default") || chks("do") || chks("double")
	    || chks("else") || chks("entry") || chks("enum")
	    || chks("float") || chks("for") || chks("goto")
	    || chks("if") || chks("return") || chks("sizeof")
	    || chks("struct") || chks("switch") || chks("union")
	    || chks("void") || chks("while") || chkdecl());
}

/* Check to see if we have a declaration */
chkdecl()
{
    return (chktype() || chksc());
}

/* Check for a valid C variable type */
chktype()
{
    blanks();
    if (chks("int"))
	return T_INT;
    else if (chks("char"))
	return T_CHAR;
    else if (chks("unsigned"))
	return T_UNSIGNED;
    else if (chks("struct"))
	return T_STRUCT;
    else if (chks("union"))
	return T_UNION;
    else if (chks("short"))
	return T_SHORT;
    else if (chks("long"))
	return T_LONG;
    else
	return T_NONE;
}

/* Check for a valid C variable storage class */
chksc()
{
    blanks();
    if (chks("register"))
	return SC_REG;
    else if (chks("static"))
	return SC_STATIC;
    else if (chks("extern"))
	return SC_EXTERN;
    else if (chks("typedef"))
	return SC_TYPE;
    else if (chks("auto"))
	return SC_AUTO;
    else
	return SC_NONE;
}

/* Get a valid C variable type if present */
struct typeinfo *
gettype()
{
    register        type, newtype;
    struct typeinfo *istypedef(), *decltag(), *p_type;

    type = T_NONE;		/* start with no type */
    for (;;)
    {				/* process all type specifiers */
	switch (newtype = chktype())
	{
	case T_LONG:
	    notavail("long integers");
	case T_SHORT:
	    if (type == T_NONE)
		type = T_INT;
	    else
		typerr();
	    break;
	case T_UNION:
	case T_STRUCT:
	case T_UNSIGNED:
	case T_CHAR:
	    if (type == T_NONE)
		type = newtype;
	    else
		typerr();
	    break;
	case T_INT:
	    if (type == T_CHAR)	/* type may be T_INT if we */
		typerr();	/* found long/short already */
	    else if (type == T_NONE)	/* leave unsigned alone */
		type = T_INT;
	    break;
	default:
	    switch (type)
	    {
	    case T_STRUCT:
	    case T_UNION:
		return decltag(type);
	    case T_UNSIGNED:
		return unsgntype;
	    case T_CHAR:
		return chartype;
	    case T_INT:
		return inttype;
	    case T_NONE:
		if (p_type = istypedef())
		    skip();	/* skip typedef name */
		return p_type;
	    }
	}
	skip();			/* skip the type keyword */
    }
}

/* See if next token is a typedef name */
struct typeinfo *
istypedef()
{
    register struct st *sym;
    struct st      *findglb(), *findloc();
    char           *chksym(), name[NAMESIZE];

    if (!chksym(name))
	return NULL;
    if (infunc && (sym = findloc(name))
	|| (sym = findglb(name)) && sym->st_info == DECL_GLB)
    {
	if (sym->st_sc == SC_TYPE)
	    return sym->st_type;
    }
    return NULL;
}

/* Get a valid C variable storage class if present */
getsc()
{
    int             sc;

    if (sc = chksc())
	skip();			/* skip over the keyword */
    return sc;
}

/* Parse an abstract declarator */
struct typeinfo *
abstdecl()
{
    register struct typeinfo *type;
    struct typeinfo *gettype(), *loadtype();
    struct parsestack ts;

    if (type = gettype())
    {
	ts.curr_ptr = ts.stack;	/* initialize local stack */
	dodecl(&ts, (char *) NULL, FALSE);	/* analyze the declarator */
	type = loadtype(type, &ts);
	if (type->t_code == T_ARRAY && type->t_size == -1)
	    sizerr();
	return type;
    } else
	return NULL;
}

/* Load a local type entry into the type table */
struct typeinfo *
loadtype(type, ts)
    register struct typeinfo *type;
    struct parsestack *ts;
{
    register struct typestack *p, *startstack;
    register        size;
    struct typeinfo *addtype(), *ptrto();
    union baseinfo  base;

    startstack = ts->stack;
    for (p = ts->curr_ptr; --p >= startstack;)
    {
	switch (p->t_code)
	{
	case T_PTR:
	    type = ptrto(type);
	    break;
	case T_FUNC:
	    if (type->t_code <= T_NONE || type->t_code > T_SIMPLE)
	    {
		error("Function can't return aggregate");
		type = inttype;
	    }
	    base.p_type = type;
	    type = addtype(T_FUNC, &base, S_FUNC);
	    break;
	case T_ARRAY:
	    if (type->t_code == T_FUNC)
	    {
		error("No arrays of functions");
		type = ptrto(type);
	    }
	    if (p->t_size != -1)
		size = p->t_size * type->t_size;
	    else
	    {
		if (p == startstack)
		    size = -1;	/* 1st dimension missing */
		else if (p == startstack + 1
			 && (p - 1)->t_code == T_PTR)
		    size = -1;	/* ptr-to-array */
		else
		{
		    sizerr();
		    size = type->t_size;
		}
	    }
	    base.p_type = type;
	    type = addtype(T_ARRAY, &base, size);
	    break;
	}
    }
    return type;
}

/* Process a declarator, and return the number of args found, if any. */
int
dodecl(ts, name, args)
    register struct parsestack *ts;
    register char  *name;
    int             args;	/* Whether or not function args are OK in
				 * this context. */
{
    int             argcnt;

    argcnt = 0;

    if (matchc('*'))
    {
	argcnt = dodecl(ts, name, args);
	pushtype(ts, T_PTR, 1);
	return (argcnt);
    }
    if (matchc('('))
    {
	argcnt = dodecl(ts, name, args);
	needpunc(')');
    } else if (name != NULL)	/* no name for abstract declarator */
	symname(name);
    if (matchc('('))
    {
	if (name == NULL	/* real declarations will do own chk */
	    || args == FALSE
	    || ts->curr_ptr > ts->stack)	/* pointer-to-func */
	    needpunc(')');
	else
	    argcnt = prarg1();

	pushtype(ts, T_FUNC, 1);
    } else
	while (matchc('['))
	    pushtype(ts, T_ARRAY, arraysize());

    return (argcnt);
}

/* Push a type onto a local type parsing stack */
pushtype(ts, code, itemct)
    register struct parsestack *ts;
{
    register struct typestack *tsptr;

    tsptr = ts->curr_ptr;
    if (tsptr < &ts->stack[TSSIZE])
    {
	tsptr->t_code = code;
	tsptr->t_size = itemct;
	++ts->curr_ptr;
    } else
	error("This type is too ornate");
}

/* Test for legal symbol name */
symname(name)
    char           *name;
{
    char           *chksym();

    if (!chksym(name))
    {
	skip();			/* pass by the name */
	illname();		/* illegal symbol name */
	return FALSE;
    } else if (chkeyword())
    {
	skip();
	keyerr(name);		/* illegal use of keyword */
	return FALSE;
    } else
    {				/* valid symbol name */
	skip();
	return TRUE;
    }
}

/* Check for a valid symbol */
char           *
chksym(name)
    register char  *name;
{
    register        i;
    register char  *p;

    blanks();
    if (!isletter(ch()))
    {				/* name must start with an alpha */
	*name = '\0';
	return FALSE;
    }
    i = 0;
    p = lptr;
    while (isletnum(*p))
    {
	if (i++ < NAMEMAX)
	    *name++ = *p;
	++p;
    }
    *name = '\0';
    return p;			/* return location of char after name */
}

/* Add a statement label to the local symbol table */
addlabel(labname, sc)
    register char  *labname;
    register        sc;
{
    register struct st *p;
    register        labno;
    struct st      *addloc(), *findloc(), *addvar();

    /* check if labname is already in local symbol table */
    if (p = findloc(labname))
    {
	if (p->st_type->t_code != T_LABEL)
	{
	    multidef(labname);	/* previously */
	    return 0;		/* defined as a variable */
	} else if (p->st_sc == SC_AUTO)
	{
	    if (sc == SC_AUTO)	/* 2nd definition */
		multidef(labname);
	} else			/* labname previously used in goto only */
	    p->st_sc = sc;	/* define it now */
	return p->st_info;	/* return label # assigned */
    }
    addvar(DECL_LOC + 1, labname, labeltype, sc, ID_VAR, labno = getlabel());
    return labno;
}

/* Return next available internal label number */
getlabel()
{
    static          nextlabel = FIRSTLABEL;

    return (++nextlabel);
}

/* Add an entry to the switch/loop queue */
addswq(p)
    struct swq     *p;
{
    if (pswq >= swq + nswq)
	error("Too many active switches or loops");
    else
    {
	pswq->loop = p->loop;
	pswq->exit = p->exit;
	++pswq;
    }
}

/* Delete an entry from the switch/loop queue */
delswq()
{
    struct swq     *getswq();

    if (getswq())
	--pswq;
    else
	error("No active switches or loops to delete");
}

/* Get the most recent entry in the switch/loop queue */
struct swq     *
getswq()
{
    return (pswq == swq) ? NULL : pswq - 1;
}

/* Add an entry to the switch case table */
addcase(first_case, new_case)
    struct case_table *first_case;
    register struct case_table *new_case;
{
    register struct case_table *p;

    for (p = first_case; p < pcasetab; ++p)
    {
	if (new_case->value == p->value)
	{
	    error("Duplicate switch case");
	    break;
	}
    }
    if (pcasetab >= casetab + ncases)
	error("Too many switch cases");
    else
    {
	pcasetab->value = new_case->value;
	pcasetab->label = new_case->label;
	++pcasetab;
    }
}

/* Delete an entry from the switch case table */
struct case_table *
delcase()
{
    if (pcasetab > casetab)
	return (--pcasetab);
    else
    {
	error("No entry in case table to delete");
	return NULL;
    }
}

/*	Evaluate a constant expression		*/
constexp(cexp)
    register struct operand *cexp;
{
    genflag = FALSE;		/* kill code generation */
    heir3(cexp);		/* no comma operators or assignment */
    genflag = TRUE;
    unreftype(cexp->op_type);
    return (cexp->op_sym) ?	/* if a symbol was found ... */
	    (cexp->op_load & CONSTADDR) :	/* chk constant address */
	    (cexp->op_load & CONSTANT);	/* else chk for constant */
}

/* If next token is a constant build an operand with its value */
constant(op)
    register struct operand *op;
{
    int             val;

    if (hex(&val) || octal(&val) || decimal(&val) || charconst(&val))
    {
	op->op_sym = NULL;
	reftype(op->op_type = inttype);
	op->op_load = (LOADVALUE | CONSTANT);
	op->op_val = val;
	return TRUE;
    } else
	return FALSE;
}

/* Check for hexadecimal constant */
hex(val)
    int            *val;
{
    if (match("0x") || match("0X"))
    {
	*val = gethex();
	return TRUE;
    } else
	return FALSE;
}

/* Convert string to hex value */
gethex()
{
    register        v, d;

    v = 0;
    for (;;)
    {
	d = chupper(ch());
	if (isdigit(d))
	    d -= '0';
	else if (d >= 'A' && d <= 'F')
	    d -= ('A' - 10);
	else
	    break;
	v = 16 * v + d;
	gch();
    }
    return v;
}

/* Check for octal constant */
octal(val)
    int            *val;
{
    register        k;

    if (!matchc('0'))
	return FALSE;
    k = 0;
    while (isdigit(ch()))
	k = k * 8 + gch() - '0';
    *val = k;
    return TRUE;
}

/* Check for decimal constant */
decimal(val)
    int            *val;
{
    register        c;

    blanks();
    if ((c = ch()) == '+' || c == '-')
	c = nch();		/* next character must be digit */
    if (!isdigit(c))
	return FALSE;
    *val = atoi(lptr);
    do
    {
	gch();
    } while (isdigit(ch()));
    return TRUE;
}

/* Check for character constant */
charconst(val)
    int            *val;
{
    register        c, k;

    if (!matchc('\''))
	return FALSE;
    k = 0;
    while ((c = gch()) != '\'' && c != 0)
    {
	if (c == '\\')
	    c = escape();
	k = (k << 8) + (c & 0xFF);
    }
    *val = k;
    if (c == 0)
	error("Unterminated character constant.");
    return TRUE;
}

/* Check for string, build an operand, and add it to the literal pool */
string(op)
    struct operand *op;
{
    register        c;
    static          lit_full = FALSE;

    if (!matchc('"'))
	return FALSE;
    initop(op, &lit_sym, lit_sym.st_type, (LOADADDR | CONSTADDR));
    op->op_val = plitpool - litpool;	/* offset from start of litpool */
    while ((c = getstring()) != -1)
    {
	if (plitpool - litpool < litpsize - 1)
	    *plitpool++ = c;
	else if (lit_full)
	    continue;
	else
	{
	    error("String space full");
	    lit_full = TRUE;
	}
    }
    if (plitpool - litpool < litpsize)
	*plitpool++ = '\0';
    return TRUE;
}

/* Get the next character in a literal string */
getstring()
{
    int             c;

    switch (c = gch())
    {
    case '\0':
	error("Unterminated string.");
    case '"':
	return -1;
    case '\\':
	return escape();
    default:
	return c;
    }
}

/* Evaluate an escape character constant */
escape()
{
    register        c, count, octal;

    switch (c = gch())
    {
    case 'f':
	return '\f';
    case 'r':
	return '\r';
    case 'b':
	return '\b';
    case 't':
	return '\t';
    case 'n':
	return '\n';
    case '\0':			/* Escaped Newline */
	inline();
	if ((c = gch()) == '\\')
	    return (escape());
	else
	    return (c);
    }
    if (isdigit(c))
    {				/* octal sequence */
	count = 0;
	octal = c - '0';
	while (isdigit(ch()) && ++count < 3)
	    octal = octal * 8 + gch() - '0';
	return octal;
    }
    return c;
}

/*
 *	Symbol table handling functions
 */


int
hash(name)
    char           *name;
{
    register int             sum;
    register int             j;

    sum = 0;
    j = 8;

    while (*name && j)
    {
	sum *= 2;
	sum += *name++;
	--j;
    }

    return (sum % nsym);
}




struct st      *
flvar(name, idset)
    char           *name;
    int             idset;	/* idset = ID_VAR or ID_STRUCT */
{
    struct st      *ptr;
    int             j;

    ptr = symtab + hash(name);

    ++nrefs;

    for (j = 0; j < nsym; ++j)
    {
	++nlooks;
	if (ptr->st_decl == DECL_VIRGIN)
	    return (NULL);
	else if (!(ptr->st_decl & HIDMASK) &&
		 (ptr->st_decl & LEVELMASK) > DECL_GLB &&
		 ptr->st_idset == idset &&
		 astreq(name, ptr->st_name, NAMEMAX))
	    return ptr;
	if (++ptr == symtab + nsym)
	    ptr = symtab;
    }
    return (NULL);		/* We have looked at every entry. */

}



struct st      *
fgvar(name, idset)
    char           *name;
    int             idset;	/* idset = ID_VAR or ID_STRUCT */
{
    struct st      *ptr;
    int             j;

    ptr = symtab + hash(name);
    ++nrefs;
    for (j = 0; j < nsym; ++j)
    {
	++nlooks;
	if (ptr->st_decl == DECL_VIRGIN)
	    return (NULL);
	else if ((ptr->st_decl & LEVELMASK) == DECL_GLB &&
		 ptr->st_idset == idset &&
		 astreq(name, ptr->st_name, NAMEMAX))
	    return ptr;
	if (++ptr == symtab + nsym)
	    ptr = symtab;
    }
    return (NULL);		/* We have looked at every entry. */

}

struct st      *
fanyvar(name, idset)
    char           *name;
    int             idset;	/* idset = ID_VAR or ID_STRUCT */
{
    struct st      *ptr;
    int             j;

    ptr = symtab + hash(name);

    ++nrefs;

    for (j = 0; j < nsym; ++j)
    {
	++nlooks;
	if (ptr->st_decl == DECL_VIRGIN)
	    return (NULL);	/* We have hit a never-used slot */
	else if (!(ptr->st_decl & HIDMASK) &&
		 (ptr->st_decl & LEVELMASK) > DECL_EMPTY &&
		 ptr->st_idset == idset &&
		 astreq(name, ptr->st_name, NAMEMAX))
	    return ptr;
	if (++ptr == symtab + nsym)
	    ptr = symtab;
    }
    return (NULL);		/* We have looked at every entry. */

}



struct st      *
addvar(decl, name, type, sc, idset, info)
    int             decl;
    char           *name;
    struct typeinfo *type;
    int             sc, idset, info;
{
    struct st      *ptr;
    int             j;

    ptr = symtab + hash(name);
    added = TRUE;

    for (j = 0; j < nsym; ++j)
    {
	if (ptr->st_decl == DECL_VIRGIN ||
	    ptr->st_decl == DECL_EMPTY)
	    goto space;
	if (++ptr == symtab + nsym)
	    ptr = symtab;
    }
    error("Symbol table full.");
    return (NULL);		/* We have looked at every entry. */

space:
    if (type != NULL)
    {
	reftype(type);
	if (type->t_code == T_STRUCT && idset == ID_VAR
	    && type->t_size == 0)
	    error("No template");
    }
    strcpy(ptr->st_name, name);
    ptr->st_type = type;
    ptr->st_sc = sc;
    ptr->st_idset = idset;
    ptr->st_info = info;
    ptr->st_decl = decl;
    return ptr;

}



struct st      *
findglb(name)
    char           *name;
{
    struct st      *fgvar();

    return fgvar(name, ID_VAR);
}
struct st      *
findloc(name)
    char           *name;
{
    struct st      *flvar();

    return flvar(name, ID_VAR);
}
struct st      *
findany(name)
    char           *name;
{
    struct st      *sym, *flvar();
    struct st      *addloc();

    if (sym = fanyvar(name, ID_VAR))
    {
	if (sym->st_decl == DECL_GLB)
	    if (sym->st_info == DECL_LOC)	/* declared locally? */
		sym = NULL;	/* not known here */

	return (sym);
    }
    return (NULL);
}


struct st      *
findtag(name)
    char           *name;
{

    return fanyvar(name, ID_STRUCT);
}


struct st      *
addglb(name, type, sc, idset, info)
    register char  *name;
    register struct typeinfo *type;
{
    struct st      *findglb(), *addvar();
    register struct st *p;
    register struct typeinfo *def_type, *decl_type;

    if (idset != ID_STRUCT && (p = findglb(name)))
    {
	/* check for consistency (struct info is checked when */
	/* it is declared since locals must be checked also) */
	if (sc == SC_EXTERN)
	{			/* is this a declaration? */
	    decl_type = type;	/* record declared type */
	    def_type = p->st_type;	/* and defined type */
	} else
	{			/* this is the definition */
	    if (p->st_sc == SC_GLOBAL || p->st_sc == SC_ST_GLB)
	    {
		multidef(name);	/* already defined */
		return NULL;
	    }
	    p->st_sc = sc;	/* say it's defined */
	    p->st_info = info;
	    decl_type = type;
	    def_type = p->st_type;
	}
	if (decl_type != def_type)
	{			/* declare/define type same? */
	    if (def_type->t_code == T_ARRAY	/* array size may */
		&& decl_type->t_code == T_ARRAY	/* be missing but */
		&& def_type->t_base.p_type ==	/* base types must */
		decl_type->t_base.p_type)
	    {			/* be the same */
		if (sc != SC_EXTERN)
		{		/* is this the def? */
		    reftype(def_type);
		    unreftype(decl_type);
		    p->st_type = def_type;	/* use defined size */
		}
	    } else
		errname("Inconsistent declaration", name);
	}
    } else
	p = addvar(DECL_GLB, name, type, sc, idset, info);

    return p;
}

struct st      *
addloc(name, type, sc, idset, info)
    char           *name;
    struct typeinfo *type;
{
    struct st      *sym, *addvar();

    /* See if a lower-level local var exists */
    if (sym = fanyvar(name, idset))
    {
	if (sym->st_decl > DECL_LOC + ncmp)
	    error("Panic!: declaration level failure.");
	else if (sym->st_decl == DECL_LOC + ncmp)
	    multidef(name);
	else
	{
	    sym->st_decl |= HIDMASK;	/* Hide it */
	}

    }
    return (addvar(DECL_LOC + ncmp, name, type, sc, idset, info));
}


/* Remove from the table all local vars at level. */
/* Also check things like undefined labels. */
freesym(level)
    int             level;
{
    register struct st *sym, *hidsym, *maxhid;
    int             maxlev;
    char           *name;
    int             sc;
    struct typeinfo *type;
    int             j;
    int		symspace;

    symspace = 0;
    for (sym = symtab; sym < symtab + nsym; ++sym)
    {
        if (sym->st_decl == DECL_VIRGIN ||
        	sym->st_decl == DECL_EMPTY)
	{
	    ++symspace;
	    continue;
	}

	if ((sym->st_decl & LEVELMASK) > level)
	    error("Panic: bad symbol table levels.");
	if (sym->st_decl != level)
	    continue;

	name = sym->st_name;
	sc = sym->st_sc;
	type = sym->st_type;
	if (type->t_code == T_LABEL && sc == SC_NONE)
	    errname("Undefined label", name);
	else if (type->t_code == T_STRUCT
		 && sc == SC_TYPE)
	{			/* local template? */
	    if (type->t_size == 0)
		tagerr(name);	/* undefined */
	    else		/* delete member list from member table */
		delmem(type->t_base.memlist);
	}
	unreftype(type);
	sym->st_decl = DECL_EMPTY;	/* Mark slot as unused. */

	/* Look for the highest level entry that was hidden by this one. */

	maxhid = NULL;
	maxlev = -1;
	hidsym = symtab + hash(name);
	++nrefs;
	for (j = 0; j < nsym; ++j)
	{
	    ++nlooks;
	    if (hidsym->st_decl == DECL_VIRGIN)
		break;

	    if ((hidsym->st_decl & HIDMASK) &&
		astreq(sym->st_name, hidsym->st_name, 8) &&
		(hidsym->st_decl & LEVELMASK) > maxlev)
	    {
		maxhid = hidsym;
		maxlev = hidsym->st_decl & LEVELMASK;
	    }
	    if (++hidsym == symtab + nsym)
		hidsym = symtab;
	}

	if (maxhid)
	    maxhid->st_decl &= ~HIDMASK;

    }
    minsym = imin(minsym, symspace);	/* remember symtab space */

}


/*
 *	Member table handling functions
 */
/* Add a member to the member table */
struct memtab  *
addmem(sym, type, prevmem)
    struct st      *sym;
    struct typeinfo *type;
    struct memtab  *prevmem;
{
    register struct memtab *currmem;

    if (freemem == NULL)
    {
	error("Member table full");
	return NULL;
    } else
    {
	currmem = freemem;	/* use next free member slot */
	freemem = freemem->nextmem;	/* update free list ptr */
	currmem->p_sym = sym;	/* record symbol table entry */
	currmem->nextmem = NULL;/* end of current list */
	if (prevmem == NULL)	/* first member in this struct */
	    type->t_base.memlist = currmem;
	else
	    prevmem->nextmem = currmem;	/* chain members */
	return (currmem);
    }
}

/* Delete an entire member list */
delmem(memlist)
    struct memtab  *memlist;
{
    register struct memtab *p;

    if (freemem != NULL)
    {				/* add memlist to front of free list */
	for (p = memlist; p->nextmem != NULL; p = p->nextmem)
	    ;			/* find last member in list */
	p->nextmem = freemem;	/* make it point to old free list */
    }
    freemem = memlist;
}

/*
 *	Type handling functions
 */
inittypes()
{
    register        i;
    struct typeinfo *addtype();

    for (i = 0; i < n_types; ++i)
    {
	typelist[i].t_code = T_NONE;
	typelist[i].t_next = &typelist[i + 1];
    }
    typelist[n_types - 1].t_next = NULL;
    for (i = 0; i < T_MAX; ++i)
	basetypes[i] = NULL;
    basetypes[T_NONE] = typelist;
    /* define some oft-used types, and mark them permanent */
    chartype = addtype(T_CHAR, (union baseinfo *) NULL, S_CHAR);
    reftype(chartype);
    inttype = addtype(T_INT, (union baseinfo *) NULL, S_INT);
    reftype(inttype);
    unsgntype = addtype(T_UNSIGNED, (union baseinfo *) NULL, S_UNSIGNED);
    reftype(unsgntype);
    labeltype = addtype(T_LABEL, (union baseinfo *) NULL, 0);
    reftype(labeltype);
    functype = addtype(T_FUNC, (union baseinfo *) & inttype, 0);
    reftype(functype);
}

/* Add a new type to table */
struct typeinfo *
addtype(typecode, base, size)
    union baseinfo *base;
{
    register struct typeinfo *p;
    struct typeinfo *findtype(), *uniquetype();

    if ((p = findtype(typecode, base, size)) == NULL)
	p = uniquetype(typecode, base, size);
    return p;
}
struct typeinfo *
uniquetype(typecode, base, size)
    union baseinfo *base;
{
    register struct typeinfo *p;
    struct typeinfo *alloctype();

    if ((p = alloctype(typecode, base, size)) == NULL)
	return inttype;
    else
    {
	p->t_next = basetypes[typecode];
	basetypes[typecode] = p;
	return p;
    }
}

reftype(p)
    struct typeinfo *p;
{
    ++p->t_refs;
}

unreftype(p)
    struct typeinfo *p;
{
    if (--p->t_refs == 0)
	scraptype(p);
}

/* Convert "type" to "ptr-to-type" */
struct typeinfo *
ptrto(type)
    struct typeinfo *type;
{
    struct typeinfo *addtype();

    return addtype(T_PTR, (union baseinfo *) & type, S_PTR);
}

/*  Find a type in the table */
struct typeinfo *
findtype(typecode, base, size)
    union baseinfo *base;
{
    register struct typeinfo *p;

    for (p = basetypes[typecode]; p != NULL; p = p->t_next)
    {
	if (p->t_size != size)
	    continue;
	if (typecode == T_STRUCT)
	{
	    if (p->t_base.memlist == base->memlist)
		break;
	} else if (p->t_base.p_type == base->p_type)
	    break;
    }
    return p;
}

/* Allocate an entry in the type table */
struct typeinfo *
alloctype(typecode, base, size)
    register union baseinfo *base;
{
    register struct typeinfo *p, *p_base;

    p_base = (base == NULL || typecode == T_STRUCT) ? NULL : base->p_type;
    if (p_base != NULL)
	reftype(p_base);
    if (basetypes[T_NONE] == NULL && !typecollect())
    {
	error("Too many different types in use");
	if (p_base != NULL)
	    unreftype(p_base);
	return NULL;
    } else
    {
	p = basetypes[T_NONE];
	basetypes[T_NONE] = p->t_next;
	p->t_code = typecode;
	p->t_size = size;
	p->t_refs = 0;
	p->t_next = NULL;
	if (typecode == T_STRUCT)
	    p->t_base.memlist = NULL;	/* no member list yet */
	else
	    p->t_base.p_type = p_base;
	return p;
    }
}

/* Remove a type from the type table */
scraptype(p)
    register struct typeinfo *p;
{
    register struct typeinfo *t, *prev;

    if (p->t_code != T_STRUCT && p->t_base.p_type != NULL)
	unreftype(p->t_base.p_type);
    prev = NULL;
    for (t = basetypes[p->t_code]; t != NULL; t = t->t_next)
    {
	if (t == p)
	{
	    freetype(prev, p);
	    break;
	}
	prev = t;
    }
}

/* Return a type node to the free list */
freetype(prev, type)
    struct typeinfo *prev;
    register struct typeinfo *type;
{
    if (prev == NULL)
	basetypes[type->t_code] = type->t_next;
    else
	prev->t_next = type->t_next;
    type->t_next = basetypes[T_NONE];
    basetypes[T_NONE] = type;
    type->t_code = T_NONE;
}

typecollect()
{
    register        code, freed;
    register struct typeinfo *p, *prev, *next;

    freed = 0;
    for (code = T_NONE + 1; code < T_MAX; ++code)
    {
	prev = NULL;
	for (p = basetypes[code]; p != NULL; p = next)
	{
	    next = p->t_next;
	    if (p->t_refs)
		prev = p;
	    else
	    {
		/* Unused node - free it after removing the reference to its
		 * base type. Unreftype() isn't used because a node somewhere
		 * in the chain from here to the real base type may have
		 * already been freed, and that will make unreftype() choke.
		 * Instead, we just decrement the use count, and let the next
		 * collection pick it up. */
		if (code != T_STRUCT && p->t_base.p_type != NULL)
		    p->t_base.p_type->t_refs--;
		freetype(prev, p);
		++freed;
	    }
	}
    }
    return freed;
}

/* Strip a layer off of op's type */
deref(op)
    struct operand *op;
{
    chtype(op, op->op_type->t_base.p_type);
}

/* Set type of expression to highest of either operand */
settype(lop, rop)
    struct operand *lop, *rop;
{
    register struct typeinfo *rtype;

    rtype = rop->op_type;
    if (rtype->t_code > lop->op_type->t_code)
	chtype(lop, rtype);
    unreftype(rtype);
    if (lop->op_type->t_code == T_ARRAY)	/* Change array to ptr */
	chtype(lop, ptrto(lop->op_type->t_base.p_type));
}

chtype(op, new)
    register struct operand *op;
    register struct typeinfo *new;
{
    reftype(new);
    unreftype(op->op_type);
    op->op_type = new;
    if (new->t_code == T_ARRAY)
	unarray(op);
}

basesize(op)
    struct operand *op;
{
    register struct typeinfo *p;

    p = op->op_type->t_base.p_type;
    if (p->t_size == 0)
	error("Size unknown");
    return p->t_size;
}

/* Check op to see if it is a pointer */
struct typeinfo *
isptr(op)
    struct operand *op;
{
    register struct typeinfo *type;
    struct typeinfo *ptrto();

    type = op->op_type;
    if (type->t_code == T_PTR)
	return type;
    else if (type->t_code == T_ARRAY)
	return ptrto(type->t_base.p_type);
    else
	return NULL;
}

islval(op)
    struct operand *op;
{
    return (op->op_load & LVALUE);
}

isconstant(op)
    struct operand *op;
{
    return (op->op_load & CONSTANT);
}

isloaded(op)
    struct operand *op;
{
    return (op->op_load & (LOADVALUE | LOADADDR)) == 0;
}

isvalue(op)
    struct operand *op;
{
    return (op->op_load == EXPRESSION);
}

isaddr(op)
    struct operand *op;
{
    return (islval(op) && op->op_sym == NULL);
}

/* end of CC5.C */
