Google
 

Trailing-Edge - PDP-10 Archives - SRI_NIC_PERM_FS_1_19910112 - kcc-4/kcc/ccdecl.c
There are 8 other files named ccdecl.c in the archive. Click here to see a list.
/*	CCDECL.C - Declaration Parser
**
**	All changes after version 154 (8/8/85), unless otherwise specified, are
**	Copyright 1985, 1986 by Ken Harrenstien, SRI International.
*/
/* [SRI-NIC]SS:<C.KCC.CC>CCDECL.C.290,  5-Mar-86 19:57:00, Edit by KLH */
/*  **MAJOR** overhaul and rewrite of just about everything.  Now
** conforms to H&S except for initializers (deferred work on pizer()
** for later).
*/
/* [SRI-NIC]SS:<C.KCC.CC>CCDECL.C.162, 17-Dec-85 08:01:25, Edit by KLH */
/*  Rationalized names of constants and structures */
/* <KCC.CC>CCDECL.C.151, 20-Jul-85 15:52:07, Edit by KRONJ */
/*  Make normal tsize of pointer zero so can have nonzero tsize */
/*  for array coerced to pointer so  int x[]; sizeof x;  can work */
/* <KCC.CC>CCDECL.C.149, 27-Jun-85 13:22:52, Edit by KRONJ */
/*  Don't ignore static in function declaration */
/* <KCC.CC>CCDECL.C.148, 19-Jun-85 10:54:43, Edit by KRONJ */
/*  Improve entry statement for #asm in middle, funny ident chars */

/*
** ccdecl - Declaration parser for KCC
** (C) 1981  K.Chen
*/

#include "cc.h"

/* functions from outside used herein */
extern SYMBOL *findsym(), *makesym(), *uniqsym();	/* CCSYM */
extern SYMBOL *lsymhead;	/* CCSYM - var indicating loc sym blk */
extern SYMBOL *newlabel();	/* CCSYM */
extern SYMBOL *findsmem();	/* CCSYM */
extern int isdupsym();		/* CCSYM */
extern SYMBOL *beglsym();	/* CCSYM */
extern void endlsym(), funlsym();			/* CCSYM */
extern TYPE *findctype(), *findtype(), *findsztype();	/* CCSYM */
extern int sizetype();					/* CCSYM */
extern NODE *funstmt(), *exprdecl(), *defnode();	/* From CCSTMT */
extern int pconst();			/* CCSTMT */
extern NODE *convasgn();		/* CCTYPE */
extern TYPE *convfparam();		/* CCTYPE */
extern int nextoken(), expect();
extern void warn(), error(), outid(), outnl();

/* functions herein exported to the outside */
void initpar();		/* Called by CC mainline */
void entdefs();		/* Ditto, someday this will be flushed */
NODE *extdef();		/* Called by CC mainline */
NODE *ldecllist();	/* Called by CCSTMT */
TYPE *typename();	/* Called by CCSTMT */
SYMBOL *defauto();	/* CCSTMT */

/* functions used entirely internally */
static NODE *funcdef(), *datadef();
static void paramlist(), funchk(), sdeclenum(), decllist();
static int sdecllist(), sdeclunion(), sdeclstruct(), fldsize(),
	tmismatch(), pstoclass(), isdecl(), pbase();
static NODE *dodecl();
static SYMBOL *declarator(), *decl0();
static TYPE *addpp(), *pushtype(), *pushsztype(), *tagspec();
static TYPE *typespec();
static SYMBOL *sdeclaration();

static NODE *pizer(), *npizer();
static int newpiz = 1;	/* Patch 0 to restore old pizer */

/* Internal data */

static int paramok;	/* True if OK to parse parameter list for a
			** function-type declaration.  Only true when at top
			** level and parsing the first declarator.
			*/
static SYMBOL *arglist;	/* At top level, points to list of any parameters
			** furnished with a function type declaration.
			*/
static NODE *statdecls,	/* Pointer to list of static decls within current fn */
	*stattail;	/* Pointer to tail of statdecls list */
static int nsdefs;	/* # of enum/struct/union side effect definitions seen.
			** The exact number doesn't matter; this is only used
			** to tell when a type-specifier has had the side
			** effect of defining a tag or enum constant.
			*/
static int itags;	/* # of internal tags defined.  This is used only
			** to create unique names for internal tags. 
			*/

/* Flags returned from pbase(). */
/* These aren't really used yet, maybe should be flushed. */
#define BASEF_SC 01	/* Storage class parsed */
#define BASEF_TS 02	/* Type-specifier parsed */
#define BASEF_TAG 04	/* enum/struct/union tag was defined as side effect */
/* ---------------------------------------- */
/* INITPAR - Initialize parser		    */
/*	Called once for each file compiled. */
/* ---------------------------------------- */
void
initpar()
{
    curfn = NULL;		/* Not in any function yet */
    maxnode = 0;		/* No parse-tree nodes used */
    itags = 0;			/* Reset internal tag count for gensyms */
    nsdefs = 0;			/* Reset side-effect def count for neatness */
}

/* ----------------------------------------------------------
** ENTDEFS - Process "entry" statements at start of file
**	This is a hack routine in several respects, and should vanish
** someday after all library routines have been fixed to remove their
** existing "entry" statements.  The hacks, among other things, are that:
** (1) the char flag table is munged to allow otherwise invalid ident chars.
** (2) this does both parsing and generation and does not return a NODE *.
** (3) output to the assembler file is performed here (should not happen
**	until the code generation phase).  This is necessary to ensure that
**	the assembler ENTRY statements precede all other code.
*/

#include "ccchar.h"		/* To access setcsym and clrcsym */
void
entdefs()
{
    SYMBOL *s;

    /* Verify it's ok to do this stuff at all */
    if (!clevkcc) return;	/* Return if not using KCC extensions. */

    /* Check out first token to see if it's an entry statement. */
    if (token != Q_IDENT || strcmp(csymbol->Sname, "entry"))
	return;			/* 1st token not "entry", can just return. */

    /* Process possible ENTRY statement */
    setcsym('$');		/* Allow $ and % as identifier chars */
    setcsym('%');		/* (note '_' becomes '.', so needs no hack) */
    s = csymbol;		/* Save symbol */
    if(nextoken() != Q_IDENT) {	/* must be followed by ident */
	tokpush (Q_IDENT, s);	/* Nope, put "entry" back on stack */
	clrcsym('$');		/* No longer allow $ or % as ident chars */
	clrcsym('%');
	return;			/* And return */
    }
    freesym(s);		/* OK, can flush "entry" identifier sym */

    /* Process an ENTRY statement with at least one valid identifier arg */
    while (1) {
	outstr ("\tENTRY\t");		/* make ENTRY statement */
	outid (csymbol->Sname);		/* before we call expect() */
	outnl();			/* so #asm in middle won't screw us */
	expect (Q_IDENT);		/* it better have been a sym */
	if (token != T_COMMA) break;	/* repeat while we have more */
	nextoken();			/* skip COMMA */
    }
    clrcsym('$');		/* No longer allow $ or % as ident chars */
    clrcsym('%');
    expect(T_SCOLON);	/* Must end with ';', flush til get one */
			/* Note error recovery will not gobble improper
			** identifiers due to above clrcsym calls.
			*/
    /* Now invoke self again, until no more entry stmts.
    ** Tail recursion makes this efficient, though it hardly matters.
    */
    entdefs();
}
/* -------------------------------------- */
/* EXTDEF - Parse an external definition  */
/*	Main entry to input parser	  */
/* -------------------------------------- */

NODE *
extdef()
{
    SYMBOL *s, tempsym, base;
    TYPE *t;

    /* Do top level initializations */
    paramok = 1;		/* OK to parse a function parameter list */
    arglist = NULL;		/* No parameter list yet */

    curfnnew = tline;		/* Remember where line started */

    pbase(&base);		/* Parse base (storage class & type) */

    if (token == T_SCOLON) {	/* just the type (struct)? */
	nextoken();		/* yes, skip over final semi */
	if ((base.Sflags&SF_SIDEFF) == 0)	/* If no side effs, */
	    warn(EGEN,"Null declaration");	/* warn user */
	if (base.Sclass)	/* Useless to specify a storage class */
	    warn(EGEN,"Useless storage class");
	return NULL;		/* and stop doing this def */
    }

    copysym(&tempsym, &base);		/* Copy base storage class & type */
    if (tempsym.Sclass == SC_UNDEF)	/* Set up for defaults */
	tempsym.Sclass = SC_AEXTERN;	/* Default is assumed-extern */
    if (tempsym.Stype == NULL)
	tempsym.Stype = deftype;
    for(;;) {
	if (s = declarator(&tempsym))	/* Get decl sym if any */
	    break;
	if (arglist) ridlsym();		/* Just in case, flush. */
	warn(EGEN, "Null declarator");
	if (token == T_SCOLON) {
	    nextoken();
	    return NULL;
	}
	if (token == T_COMMA) {
	    nextoken();
	    continue;
	}
	error(EGEN,"Bad top-level declaration token");
	errflush();		/* Flush to probable end of statement */
	return NULL;		/* No identifier, so give up on it. */
    }

    paramok = 0;		/* No longer OK to parse function param list */

    /* If function type, need to examine more closely to see whether this
    ** is a definition or just a forward-reference declaration.
    ** It is a definition only if the current token is one of:
    **		Left brace (start of function body)
    **		A type-specifier keyword (a function parameter declaration).
    **		The "register" keyword (only permissible storage class for
    **			function parameter declarations).
    ** We permit any storage class here, for better error diagnostics later.
    */
    if (tempsym.Stype->Tspec == TS_FUNCT) {
	if (token == T_LBRACE || isdecl())
	    return funcdef(&base, &tempsym, s, arglist);  /* Parse funct def */
    }
    if (arglist) {
	error(EGEN,"Bad syntax - function parameters without body");
	ridlsym();		/* Flush them */
    }

    /* Not a function definition, so is either a function reference, or a
    ** data definition/reference.
    */
    return datadef(&base, &tempsym, s);	/* data def/ref or function ref */
}
/* -------------------------------------------- */
/*	function-definition  Ref[1] A.18.4      */
/* -------------------------------------------- */
/* Only called from extdef().  
** The symbol pointer argument always points to a temporary symbol
** structure not in the symbol table.  However, the argument list
** symbols ARE in the table, chained as local symbols.
*/

static NODE *
funcdef (b, d, syment, args)
SYMBOL *b, *d, *syment, *args;
{
    static int nsetjmps();
    int n, siz, nsjmps;
    TYPE *t;
    NODE *nnode, *header;
    SYMBOL *s1, argbase, stemp;

    /* Function name symtab entry is pointed to by syment.
    ** Lexer will have created the symtab entry with
    ** class SC_UNDEF if it didn't already exist.
    */
    curfn = syment;	/* remember context for error messages */
    curfnloc = curfnnew;

    d->Svalue = 1;		/* This is a defining declaration */
    funchk(b, d, syment); /* Check out storage class and type, fix up sym */

    /* parse declarations of arguments */
    funlsym();			/* Restore function parameter symbols, */
				/* and set up a local sym block for function */

    while (token != T_LBRACE && token != T_EOF) {	/* type-decl-list */

	pbase(&argbase);	/* Parse storage class and type specifier */
	copysym(&stemp, &argbase);
	switch (stemp.Sclass) {	/* Check storage class */
	    case SC_RAUTO:		/* If "register" seen, */
		stemp.Sclass = SC_RARG;	/* use right symbol class */
	    case SC_TYPEDEF:		/* Only other one OK is typedef. */
		break;
	    default:
		error(EGEN, "Illegal storage class for function parameter");
	    case SC_UNDEF:		/* Default becomes this. */
		stemp.Sclass = SC_ARG;
		break;
	}
	if ((t = stemp.Stype) == NULL) {	/* Check type-specifier */
	    if (argbase.Sclass == SC_UNDEF)
		error(EGEN,"No type-specifier for parameter decl, assuming int");
	    t = deftype;
	}
	while (1) {
	    stemp.Sname[0] = '\0';	/* no symbol given yet */
	    stemp.Stype = t;		/* Reset base type */
	    for(;;) {
		if (s1 = declarator(&stemp))	/* Get sym and rest of type */
		    break;
		if (token == T_COMMA) {
		    warn(EGEN, "Null parameter declarator");
		    nextoken();
		    continue;
		}
		if (token == T_SCOLON) {
		    nextoken();
		    break;
		}
		error(EGEN,"Bad parameter declaration token");
		errflush();	/* Flush to probable end of statement */
		break;		/* Will get two err msgs, but so what */
	    }

	    if (s1 == NULL)
		warn(EGEN, "Null parameter declaration");
	    else if (s1->Sclass == SC_TYPEDEF) {
		dodecl(&stemp, &argbase, s1);	/* Do the typedef */
	    } else if (s1->Sclass != SC_ARG) {
		error(EGEN, "Identifier \"%s\" not in function parameter list",
			s1->Sname);		/* not an arg to this fn */
		if (s1->Sclass == SC_UNDEF)
		    freesym(s1);		/* Clean up if boo-boo */
	    } else if (s1->Sflags & SF_PARAMDECL) {
		error(EGEN,"Duplicate parameter declaration: \"%s\"",s1->Sname);
	    } else {		/* Is arg, set type to what we parsed */
		s1->Sclass = stemp.Sclass;	/* Maybe indicate register */
		s1->Sflags |= SF_PARAMDECL;	/* Say decl seen for arg */
		s1->Stype = convfparam(stemp.Stype);	/* Get right type */
	    }
	    if (token != T_COMMA) break;	/* repeat through list */
	    nextoken();			/* skipping over commas */
	}
	expect(T_SCOLON);		/* decl line ends with a semicolon */
    }

    /* now that types are set, make sizes of local vars */
    n = 0;				/* set up for first arg */
    siz = sizetype(syment->Stype->Tsubt);	/* get size of return val */
    if (siz > 2)
#if 1
	n = 1;			/* Allow space for struct-return pointer */
#else
	n = siz;		/* allow space for struct return */
#endif
    while (args != NULL) {
	s1 = args;			/* get arg symbol */
	args = args->Sargp;		/* Move on before zapping it! */
	n += sizetype(s1->Stype);	/* count off by size */
	s1->Svalue = n;			/* set offset */
    }

    maxauto = 0;			/* no local variables yet */
    stackrefs = 0;			/* and therefore no refs to them */
    nsjmps = nsetjmps();		/* Remember # of setjmp refs */
    statdecls = stattail = NULL;	/* No static declarations yet */
    nnode = funstmt();			/* Parse function statement */
    endlsym((SYMBOL *)NULL);		/* End the local sym blk for params */
    nextoken();		/* Now safe to flush the right brace
			** and set up new current token.
			** See CCSTMT's compound() for discussion of this.
			*/
    stkgoto = (nsjmps != nsetjmps());	/* Say whether any setjmps in funct */
    header = defnode(N1, N_NODE);	/* Put together the function header */
    header->Nright = statdecls;		/* Point to any static decls found */
    header->Nleft = defnode(N2, Q_IDENT, syment->Stype, 0, (NODE *)NULL);
    header->Nleft->Nid = syment;

    /* Return completed parse tree */
    return defnode(N3, N_FUNCTION, (TYPE *)NULL, 0, header, nnode);
}

/* NSETJMPS - Auxiliary to find current # of references to the "setjmp"
**	function.  Any functions which contain calls to setjmp have to
**	avoid using tail recursion.
*/
static int
nsetjmps()
{
    SYMBOL *s;
    if (   ((s = findsym("setjmp")) != NULL)
	&& (s->Sclass == SC_AEXTERN || s->Sclass == SC_EXTERN)
	&& (s->Stype->Tspec == TS_FUNCT))
	return s->Srefs;
    return 0;
}
/* FUNCHK - Check out a function definition or reference for
**	proper use of storage class and type specifier.
** Called from funcdef() for a definition and dodecl() for a reference.
** d->Svalue will be 1 for a definition and 0 for a reference.
** Returns with the symbol table entry completely set up.
*/
static void
funchk(base, d, s)
SYMBOL *base,	/* Parsed base (storage class and type specifier) */
	*d,	/* Parsed declarator (Sclass and Stype) */
	*s;	/* Symtab entry for parsed identifier */
{
    
    switch (base->Sclass) {	/* Not all storage classes are allowed */
	case SC_UNDEF:		/* Default is OK, make it assumed-extern */
	    d->Sclass = SC_AEXTERN;
	    if (s->Sclass == SC_EXTERN) d->Sclass = SC_EXTERN;
	    break;
	case SC_EXTERN:		/* Explicit "extern" and "static" also OK */
	case SC_STATIC:
	    if (s->Sclass == SC_AEXTERN) s->Sclass = d->Sclass;
	    break;
	default:
	    error (EGEN,"Illegal storage class for function");
	    d->Sclass = SC_STATIC;	/* Assume static and carry on */
	    break;
    }

    if (s->Sclass == SC_UNDEF) {	/* Symbol not defined yet? */
	s->Sclass = d->Sclass;	/* No, copy the parsed class */
	s->Stype = d->Stype;	/* and the type specification */
	s->Svalue = d->Svalue;	/* mark as a def or ref */
	s->Srefs = 0;		/* and reset usage count in case a ref. */
	return;
    }

    /* Symbol already defined, check for match or multiple decl */

    /* Make sure basic type is also "function returning ..." */
    if (s->Stype->Tspec != TS_FUNCT) {
	error(ETWOTYP, s->Sname);	/* Complain */
	if (d->Svalue == 0) return;	/* If only a ref, ignore it */
	/* else def, go on to overlay the sym, hope nothing vital clobbered */
	s->Svalue = 0;		/* Pretend existing sym is just a ref */
    } else {
	if (s->Sclass != d->Sclass) {		/* Verify storage class OK */
	    warn (EGEN, "Storage class conflict for \"%s\"; assuming static",
		s->Sname);
	    d->Sclass = SC_STATIC;
	}
	if (tmismatch (s->Stype, d->Stype))	/* Verify type OK */
	    warn (ETWOTYP, s->Sname);
	if (d->Svalue && s->Svalue)		/* Check for duplicate defs */
	    error (EDSYMB, s->Sname);
    }
    /* Force the symbol table entry to match current declaration */
    s->Sclass = d->Sclass;	/* Force the storage class */
    s->Stype = d->Stype;	/* and the type specification */
    if (d->Svalue)
	s->Svalue = d->Svalue;	/* Force definition if defining */
    if (s->Svalue == 0)		/* and if not yet defined, */
	s->Srefs = 0;		/* reset usage count. */
}
/* ----------------------------------------- */
/*	sc-type, type-specifier, symbol      */
/*	      Ref[1] A.18.4		     */
/* ----------------------------------------- */
/* Only called from extdef().
** This parses a top-level data declaration or function reference.
*/
static NODE *
datadef (base, s, syment)
SYMBOL *base, *s, *syment;
{
    SYMBOL defbase;
    NODE *root = NULL, *tail = NULL;

    /* Check out the storage class and type
    ** specifications.  At top level, some storage class or type specifier
    ** must have been given.
    */
    defbase.Scontents = base->Scontents;	/* Copy contents of base */
    switch (defbase.Sclass) {
	case SC_UNDEF:
	    defbase.Sclass = s->Sclass;		/* Copy whatever default was */
	    if (defbase.Stype == NULL) {
		error(EGEN,"Declaration without storage class or type-spec");
	    }
	    break;		
	case SC_EXTERN:		/* One of these three is okay */
	case SC_STATIC:
	case SC_TYPEDEF:
	    break;
	default:
	    error(EGEN,"Illegal top-level storage class");
	    s->Sclass = defbase.Sclass = SC_AEXTERN;	/* Use default */
	    break;
    }
    if (defbase.Stype == NULL)
	defbase.Stype = s->Stype;	/* Copy whatever default was */

    decllist(base, &defbase, s, syment, &root, &tail);

    if (!expect(T_SCOLON) && token == T_RBRACE) nextoken();
    return (root);
}
/* ----------------------------------------- */
/*	declaration-list  Ref.[1] A.18.3     */
/* ----------------------------------------- */
/* Only called by CCSTMT's compound() to parse the declarations
** at the start of a compound statement.  Returns a node pointer
** to a list of initializations that must be done.
*/

NODE *
ldecllist()
{
    SYMBOL base, defbase;
    NODE *autodecls, *autotail;	/* Pointers to list of inits for decls
				** within a block */

    if (!isdecl()) return NULL;		/* Most common case -- no decls */

    autodecls = autotail = NULL;
    do {
	/* If current token is start of a declaration, handle it. */
	pbase(&base);		/* Parse base storage-class and type */
				/* Note all classes are OK */
	copysym(&defbase, &base);
	if (defbase.Sclass == SC_UNDEF)
	    defbase.Sclass = SC_AUTO;	/* Default class is AUTO */
	if (defbase.Stype == NULL)
	    defbase.Stype = deftype;

	/* Handle the local declaration, adding defs to the right list. */
	if (defbase.Sclass == SC_STATIC)
	     decllist(&base, &defbase, (SYMBOL *)NULL, (SYMBOL *)NULL,
			&statdecls, &stattail);
	else decllist(&base, &defbase, (SYMBOL *)NULL, (SYMBOL *)NULL,
			&autodecls, &autotail);

	expect(T_SCOLON);
    } while (isdecl());

    return(autodecls);
}
/* -------------------------------------------- */
/*	DECLLIST - parse a list of declarators	*/
/* -------------------------------------------- */
static void
decllist(base, defbase, d, s, root, tail)
SYMBOL *base,		/* Base storage class & type-spec furnished */
	*defbase,	/* Same but completely defaulted as necessary */
	*d,		/* First parsed declarator (NULL if none) */
	*s;		/* Symbol table entry for d's ident */
NODE **root,		/* Addr of Root of declaration parse tree, if any */
	**tail;		/* Addr of Tail of parse tree, if any */
{
    SYMBOL tempsym;
    NODE *n, *z;
  
    if (d == NULL) {		/* Already parsed first declarator? */
	d = &tempsym;		/* No, so do first one here. */
	copysym(d, defbase);
	s = declarator(d);
	if (s == NULL && token == T_SCOLON && (base->Sflags&SF_SIDEFF))
	    return;		/* No declarators but have side-effect */
    }
    while (1) {
	if (s == NULL)
	    warn(EGEN, "Null declarator");
	else if ((n = dodecl(d, base, s)) != NULL) {
	    z = defnode(N2, N_DATA, (TYPE *)NULL, 0, n);
	    if (*tail == NULL)		/* Add parse result onto tree */
		*root = *tail = z;		/* Either as 1st node */
	    else *tail = (*tail)->Nright = z;	/* or at end of current tree */
	}
	if (token != T_COMMA) break;
	nextoken();
	copysym(d, defbase);
	s = declarator(d);
    }
    return;
}
/* ------------------------------------------------------------ */
/*	PBASE - parse base of declaration:			*/
/*		{storage-class-specifier} {type-specifier}	*/
/* ------------------------------------------------------------ */

static int
pbase(bsym)
SYMBOL *bsym;
{
    int savnsdefs = nsdefs;		/* Remember # side-eff defs so far */

    bsym->Sname[0] = '\0';		/* init symbol */
    bsym->Svalue = 0;			/* no val yet */

    bsym->Sclass = pstoclass();		/* parse storage class if any */
    bsym->Stype = typespec();		/* parse type if any */

    /* Set flag if any side effects (tag or enum defined) during parse */
    bsym->Sflags = (savnsdefs == nsdefs ? 0 : SF_SIDEFF);
}

/* ISDECL - returns true if current token is either a
**	storage-class-specifier or a type-specifier,
**	i.e. token is the start of a declaration.
*/
static int
isdecl()
{
    return (csymbol != NULL && (
	(tok[token].tktype == TKTY_RWSC || tok[token].tktype == TKTY_RWTYPE)
	|| (csymbol->Sclass == SC_TYPEDEF)
	));
}

/* --------------------------------------- */
/*      parse storage class specifier      */
/* --------------------------------------- */

static int
pstoclass()
{
    switch (token) {
    case T_AUTO:
	nextoken();
	return SC_AUTO;
    case T_STATIC:
	nextoken();
	return SC_STATIC;
    case T_EXTERN:
	nextoken();
	return SC_EXTERN;
    case T_REGISTER:
	nextoken();
	return SC_RAUTO;		/* Assume "auto" */
    case T_TYPEDEF:
	nextoken();
	return SC_TYPEDEF;
    }
    return SC_UNDEF;			/* no storage class, return nothing */
}
/* --------------------------------------- */
/*	type-specifier  Ref[1] A.18.2      */
/* --------------------------------------- */
static TYPE *findbtype();	/* Auxiliary for TYPESPEC */

static TYPE *
typespec()
{
    TYPE *t;
    int sign;

    if (tok[token].tktype == TKTY_RWTYPE) {
	/* Check out a reserved-word type specifier */
	switch (token) {
	    case T_CONST:		/* "const" */
		if (sign = TF_CONST) ;	/* Hack to help share code */
		else {
	    case T_VOLATILE:		/* "volatile" */
		    sign = TF_VOLATILE;
		}
		nextoken();
		if ((t = typespec()) == NULL) {	/* Then get real type. */
		    error(EGEN, "Type-specifier must follow \"%s\"",
				(sign==TF_CONST ? "const" : "volatile"));
		    return deftype;
		}
		return findctype(t->Tspec, sign|t->Tflag, t->Tsize, t->Tsubt);

	    case T_STRUCT: return tagspec(TS_STRUCT);
	    case T_UNION:  return tagspec(TS_UNION);

	    /* Enums are handled specially.  Until the code generation knows
	    ** about them, we always substitute "int" as the type for an enum.
	    */
	    case T_ENUM:   (void) tagspec(TS_ENUM);	/* Ignore return val */
			   return inttype;		/* and pretend int */

	    case T_VOID:	t = voidtype; break;
	    case T_FLOAT:	t = flttype; break;
	    case T_DOUBLE:	t = dbltype; break;
	    case T_CHAR:	t = chartype; break;
	    case T_INT:		t = inttype; break;

	    case T_SHORT:			/* "short" */
		if (nextoken() == T_INT)
		    nextoken();			/* "short int" */
		return shrttype;

	    case T_LONG:			/* "long" */
		if (nextoken() == T_INT)
		    nextoken();			/* "long int" */
		else if (token == T_FLOAT) {
		    nextoken();			/* "long float" */
		    return dbltype;
		} else if (token == T_DOUBLE) {	/* "long double" */
		    nextoken();
		    return lngdbltype;
		}
		return longtype;

	    /* Special KCC extended char types */
	    case T_CHAR6: t = findbtype(6, 0); break;
	    case T_CHAR7: t = findbtype(7, 0); break;
	    case T_CHAR8: t = findbtype(8, 0); break;
	    case T_CHAR9: t = findbtype(9, 0); break;
	    case T_CHAR18:t = findbtype(18,0); break;

	    case T_SIGNED:			/* "signed" */
		if (sign = 1) ;	/* silly crock to skip next stmt */
		else {
	    case T_UNSIGNED:			/* "unsigned" */
		    sign = 0;
		}
		if (tok[nextoken()].tktype != TKTY_RWTYPE)
		    return (sign ? inttype : uinttype);
		switch (token) {
		    case T_CHAR:		/* "un/signed char" */
			t = (sign ? schartype : uchartype);
			break;
		    case T_INT:			/* "un/signed int" */
			t = (sign ? inttype : uinttype);
			break;
		    case T_SHORT:		/* "un/signed short" */
			if (nextoken() == T_INT)
			    nextoken();		/* "un/signed short int"*/
			return (sign ? shrttype : ushrttype);
		    case T_LONG:		/* "un/signed long" */
			if (nextoken() == T_INT)
			    nextoken();		/* "un/signed long int" */
			return (sign ? longtype : ulongtype);
		    default:
			error(ETYPCOMB);	/* Default "un/signed" */
			return (sign ? inttype : uinttype);
		    /* Special KCC extended char types */
		    case T_CHAR6: t = findbtype(6, sign); break;
		    case T_CHAR7: t = findbtype(7, sign); break;
		    case T_CHAR8: t = findbtype(8, sign); break;
		    case T_CHAR9: t = findbtype(9, sign); break;
		    case T_CHAR18:t = findbtype(18,sign); break;
		}
		break;

	    default:
		error(EGEN,"Internal error - Illegal RWTYPE token");
		return deftype;
	}
	nextoken();			/* move to next token */
	return t;
    }

    /* Token wasn't a reserved-word type-specifier; check for typedef names. */
    if (csymbol && csymbol->Sclass == SC_TYPEDEF) {
	t = csymbol->Stype;	/* get the type */
	nextoken();		/* skip over it */
	return t;
    }

    return NULL;
}

/* Auxiliary for above routine to handle KCC extended char types */
static TYPE *
findbtype(bsiz, sign)
{
    return findctype((sign ? TS_CHAR : TS_UCHAR), bsiz, 1, (TYPE *)NULL);
}
/* ------------------------------------ */
/*	declarator  Ref[1]  A.18.2      */
/*					*/
/* ------------------------------------ */
/* The symbol pointer "s" always points to a temporary symbol structure
** which is NOT in the symbol table.
** All declaration statements eventually call this routine.
**	normal = extdef (toplevel) & decllist (toplevel & local)
**	smems = sdeclaration (decl within structure definition)
**	params = funcdef (declaration of function parameters)
**	abstract = typename (abstract declaration for casts & sizeof)
*/

static SYMBOL *
declarator (s)
SYMBOL *s;
{
    SYMBOL *syment;

    syment = decl0(s);
    if (s->Stype->Tspec == TS_VOID && (s->Sclass != SC_TYPEDEF))
	    error (EVOID, syment->Sname);
    return syment;
}
/* ---------------------------------------------------------- */
/*      parse a declarator's identifier and type information  */
/*	[H&S 4.5]					      */
/* ---------------------------------------------------------- */
/*
**	A DECLARATOR specifies the identifier being declared and may also
** supply additional type information.  The resulting type is one of:
**	( decl )	- parens used to establish precedence
**	ident		- Simple declarator
**	* decl		- Pointer to
**	decl [...]	- Array of
**	decl (...)	- Function (definition or reference)
**
** Only called by declarator() and itself recursively.
** The argument symbol pointer "s" must always point to a temporary symbol
** structure which is NOT in the symbol table.  The parsed type is
** returned in this symbol.
** If a non-null symbol pointer is returned, this is the parsed identifier
** which IS in the symbol table.
*/
static SYMBOL *
decl0 (s)
SYMBOL *s;
{
    TYPE *pp;		/* Holds "paren part" of type */
    SYMBOL *idsym;	/* Symtab ptr to parsed identifier */
    int saveidsc;	/* Temp while parsing function params */

    /* parse stars before ident part */
    while (token == Q_MPLY) {
	s->Stype = pushtype(TS_PTR, s->Stype);
	nextoken();
    }

    /* now parse main part (normally the ident) */
    switch (token) {
    case T_LPAREN:
	nextoken();			/* move over paren */
	{
	    TYPE *savt = s->Stype;	/* Save type thus far */
	    s->Stype = NULL;		/* and pretend it's null */
	    idsym = decl0(s);		/* Now parse stuff in parens */
	    pp = s->Stype;
	    s->Stype = savt;		/* Then can restore saved type */
	}
	expect(T_RPAREN);
	break;

    case Q_IDENT:
	idsym = csymbol;		/* Remember sym ptr for this ident */
	nextoken();
	pp = NULL;			/* No paren part */
	break;

    default:
	idsym = NULL;			/* No identifier */
	pp = NULL;			/* No paren part */
	break;
    }

    /* Check for function or array specifiers */
    while(1) {
	switch (token) {
	case T_LPAREN:		/* Function definition or reference */
	    /* Parse param list of function.  If idsym is a new symbol,
	    ** we set it temporarily so as to avoid bashing our global
	    ** function-name symbol if it turns out to have a parameter
	    ** name the same as the function name!
	    */
	    if (idsym && ((saveidsc = idsym->Sclass) == SC_UNDEF))
		idsym->Sclass = SC_AEXTERN;	/* Fake out paramlist */
	    paramlist();		/* Parse the parameter list if any */
	    if (idsym)			/* Restore real class after fakeout */
		idsym->Sclass = saveidsc;
	    pp = addpp(pp, pushtype(TS_FUNCT, (TYPE *)NULL));
	    break;

	case T_LBRACK:		/* Array of something */
	    nextoken();
	    pp = addpp(pp, pushsztype(TS_ARRAY,
				    (token == T_RBRACK)? 0 : pconst(),
				    (TYPE *)NULL));
	    expect(T_RBRACK);
	    break;
	    
	default:
	    /* go back and add paren part, return the result */
	    if (pp) s->Stype = addpp(pp, s->Stype);
	    return idsym;
	}
    }
}
/* PARAMLIST - Parse optional parameter list
**	Only called by decl0() to handle a function type declaration.
** Current token will be the left paren; on return, current token is
** first thing after the right paren.  (if error, current token is whatever
** halted the parse).
*/
static void
paramlist()
{
    SYMBOL *lastarg, *arg, *savehead;

    switch (nextoken()) {	/* Flush left paren, get next token */
	case Q_IDENT:
	    break;
	case T_RPAREN:
	    nextoken();
	    return;		/* No param list, nothing to do */

	default:
	    error(EGEN,"Bad syntax in function parameter list");
		/* Here, maybe should flush input until a ')', ';', or
		** '}' is seen.  Currently we pretend a ')' is seen.
		*/
	    return;
    }

    /* Parse parameter list.
    ** If not in proper context, we complain but parse the params anyway
    ** without remembering them.
    */
    if (!paramok)	/* Complain if wrong context for params */
	error(EGEN,"Bad context for function parameters");
    if (arglist) {
	error(EGEN,"Already parsed function parameters");
	paramok = 0;		/* Don't store any more params */
    }
    if (paramok) savehead = beglsym();	/* Begin a local symbol block */

    while (token == Q_IDENT) {		/* Token must be parameter */
	if (csymbol->Sclass == SC_ARG)	/* Already a parameter? */
	    error(EGEN,"Duplicate parameter: \"%s\"", csymbol->Sname);
	else if (paramok) {		/* If OK to remember param list, */
	    arg = uniqsym(csymbol);	/* make local sym for param */
	    if (arglist == NULL)	/* Save pointer to this param */
		arglist = arg;		/* either in head of list */
	    else lastarg->Sargp = arg;	/* or in last param so far */
	    lastarg = arg;		/* Move on to end of chain */
	    arg->Sargp = NULL;		/* This one is now the last */
	    arg->Sclass = SC_ARG;	/* Say it's a function parameter */
	    arg->Stype = deftype;	/* and (int) unless declared later */
	} else if (csymbol->Sclass == SC_UNDEF)
	    freesym(csymbol);		/* Not using sym, flush it if can. */

        nextoken();
        if (token != T_COMMA) break;
        nextoken();
    }
    if (paramok) endlsym(savehead);	/* End the local symbol block */

    expect(T_RPAREN);
}
/* --------------------------------------- */
/*      add type to inside of nesting      */
/* --------------------------------------- */

static TYPE *
addpp (pp, t)
TYPE *pp, *t;
{
    /*
    ** This takes a base-less type structure in pp, and returns the
    ** result of replacing the NULL where the base should be with t.
    ** Thus it is the inverse of pushtype(), adding the new type
    ** at the base of the structure rather than at the top.
    **
    ** I realize the recursive definition below may look messy,
    ** but an iterative definition of this function would be worse...
    */

    return (pp != NULL)?
	     pushsztype(pp->Tspec, pp->Tsize, addpp(pp->Tsubt, t)) :
	     t;
}

/* ------------------------------------------------------------------- */
/*	PUSHTYPE - Check for valid type combinations and return type   */
/* ------------------------------------------------------------------- */
/* As per [H&S 4.5.5] checks for the following illegal type
** combinations:
**	(1) Any type with "void" except "... function returning void"
**	(2) "Array of function ..."
**	(3) "Function returning array of ..."
**	(4) "Function returning function ..."
** There is also one other special case (5) that this list omits.
** As per [H&S 4.5.3 and 5.5.3] a N-dimensional array must always have
** all of the last N-1 sizes specified; only the first dimension's size
** can be omitted.
**	If the type combination is illegal, some plausible type is
** substituted, not so that something useful will be compiled but so
** that no bizarre "types" will unduly interfere with scanning the rest of the
** source file for further errors.
**	Note that "void" is OK all by itself as a type.  The checks here
** only look for illegal COMBINATIONS of types.
*/
static TYPE *
pushtype(typ, ptr)
TYPE *ptr;
{	return pushsztype(typ, typsiztab[typ], ptr);
}
static TYPE *
pushsztype(typ, siz, ptr)
TYPE *ptr;
{
    if (ptr != NULL) switch (typ) {	/* If making a combination, check it */
	default:		/* Check for (1) */
	    if (typ == TS_VOID) {	/* Nothing can be a subtype of void */
		error(ETYPCOMB);
		ptr = NULL;	/* Just make it "void" */
	    }
	    break;

	case TS_VOID:		/* Another check for (1) */
	    if (typ != TS_FUNCT) {	/* only functs can have void subtype */
		error(ETYPCOMB);
		ptr = inttype;	/* Substitute "int" for "void" */
	    }
	    break;

	case TS_ARRAY:		/* Check for (2) and (5) */
	    switch (ptr->Tspec) {
		case TS_ARRAY:		/* Array can have arrays, if... */
		    if (ptr->Tsize != 0)
			break;		/* array[][x] ok, array[x][] not */
		case TS_FUNCT:		/* Array cannot have functions */
		    error(ETYPCOMB);
		    ptr = inttype;	/* Lose, substitute "int" */
		}
		break;

	case TS_FUNCT:		/* Check for (3) and (4) */
	    switch (ptr->Tspec) {
		case TS_ARRAY:		/* Function cannot return array */
		case TS_FUNCT:		/* Function cannot return function */
		    error(ETYPCOMB);
		    ptr = voidtype;	/* Lose, substitute void */
	    }
    }

    /* now hash up the actual type and return the canonicalized version */
    return findsztype (typ, siz, ptr);
}
/* -------------------------------------------------- */
/* TAGSPEC - struct/union/enum tag handling */
/* -------------------------------------------------- */

static TYPE *
tagspec(typ)
int typ;		/* TS_STRUCT, TS_UNION, or TS_ENUM */
{
    SYMBOL s, *tagsym;
    TYPE *t;
    int siz;

    tagsym = NULL;
    switch (nextoken()) {
    case Q_IDENT:	/* Have tag ident */
	/* Get right sym for tagged entity by adding special prefix. */
	idpfcpy(s.Sname, SPC_TAG, csymbol->Sname);
	if (csymbol->Sclass == SC_UNDEF) freesym(csymbol);
	tagsym = findsym(s.Sname);
	if (nextoken() == T_LBRACE) nsdefs++;
	break;

    case T_LBRACE:
	/* No tag given, so we make up an internal one. */
	/* Note assumption here that symbol identifier is big enough */
	sprintf(s.Sname, "%c%d", SPC_TAG, ++itags);
	tagsym = NULL;
	break;

    default:
	error(EGEN,"struct/union/enum not followed by tag or definition");
	while (1)  switch (nextoken()) {
	    case T_EOF: case T_RBRACE: case T_SCOLON:
		return NULL;
	}
    }

    /* If a tag symbol already exists, check it to see whether we can use
    ** that one or need to create a new one.  This also checks for
    ** duplicate definitions.
    ** Note that if this is a reference (not a definition) then we always
    ** just use whatever tagsym is.
    */
    if (tagsym && token == T_LBRACE) {	/* If this will be a definition */
	if (tagsym->Sclass == SC_TAG) {	/* Tag already defined? */
	    if (isdupsym(tagsym))	/* Dup def? */
		error(EGEN, "Duplicate tag definition: \"%s\"", s.Sname + 1);
	    tagsym = NULL;		/* Force a new definition */
	} else if (tagsym->Sclass == SC_UTAG) {	/* A ref already exists? */
	    if (!isdupsym(tagsym))	/* If not in same block, */
		tagsym = NULL;		/* don't satisfy the ref! */
	}
    }
    if (tagsym && tagsym->Stype->Tspec != typ) {
	error(EGEN,"Tag redefined with different type: \"%s\"", s.Sname+1);
	tagsym = NULL;
    }

    /* If no tag (specified or internal) exists, make one and pretend it
    ** was seen as a forward reference (which it may well be).
    */
    if (tagsym == NULL) { 		/* Need tag symbol? */
	tagsym = makesym(s.Sname);	/* make a new tag symbol */
	tagsym->Sclass = SC_UTAG;	/* but with no defined body */
	tagsym->Ssmnext = NULL;		/* No members yet */
	tagsym->Srefs++;		/* This creation is a reference too */
					/* Create new type for it */
	tagsym->Stype = findtype(typ, (TYPE *)tagsym);
	tagsym->Stype->Tsmtag = tagsym;	/* Make sure type points to tag */
    }
    if (token != T_LBRACE)		/* If no definition, just return */
	return tagsym->Stype;

    /* Define the structure. */
    nextoken();				/* Flush the left brace */
    if (typ == TS_ENUM) sdeclenum(tagsym);
    else tagsym->Stype->Tsize =
	(typ == TS_STRUCT) ? sdeclstruct(tagsym) : sdeclunion(tagsym);

    tagsym->Sclass = SC_TAG;		/* Struct is now defined */
    return tagsym->Stype;
}
/* ------------------------- */
/*      enumeration type definition      */
/* ------------------------- */

static void
sdeclenum(tag)
SYMBOL *tag;
{
    int val;
    SYMBOL *s, *last;

    s = NULL;
    val = 0;			/* start at zero */
    last = tag;
    while (1) {
	if (token == T_RBRACE)	/* Allow ", }" without complaint */
	    break;
	if (token != Q_IDENT) {
	    error(EGEN,"Identifier expected as enum constant");
	    break;
	}
	nsdefs++;		/* Enum constant def as "side effect" */
	s = csymbol;		/* get identifier */
	if (isdupsym(s)) error (EDSYMB, s->Sname);
	s = uniqsym(s);		/* Create or re-use symbol as needed */
	s->Sclass = SC_ENUM;	/* this is an enum constant */
	s->Stype = inttype;	/* acting like an int */
	s->Ssmtag = tag;	/* Remember tag defined within */
	s->Ssmnext = NULL;
	last->Ssmnext = s;	/* Link onto list of enum members */

	if (nextoken() == Q_ASGN) {
	    nextoken();		/* want specific value for this one */
	    val = pconst();	/* so set it to given constant */
	}
	s->Svalue = val++;		/* with this value */
	if (token != T_COMMA)
	    break;
	nextoken();
    }
    expect(T_RBRACE);		/* done with def, end with right brace */
    if (s == NULL)
	warn(EGEN,"Empty enum definition list");
}


/*	Structure type definition */

static int
sdeclstruct(tag)
SYMBOL *tag;
{
    SYMBOL *lastmem;
    int offset, boffset, inbitf;

    lastmem = tag;
    offset = boffset = 0;		/* structure starts at zero */
    inbitf = 0;
    while (token != T_RBRACE) {
	if (eof) { error(EEOF,"in struct decl"); break; }
	lastmem = sdeclaration(tag, lastmem, &offset, &boffset, &inbitf);
    }
    nextoken();				/* skip over close brace */
    if (boffset > 0) offset++;		/* round offset out to full word */
    return offset;			/* return as size of struct */
}

/*      Union type definition      */
static int
sdeclunion(tag)
SYMBOL *tag;
{
    SYMBOL *lastmem;
    int maxsize, offset, boffset, inbitf;

    lastmem = tag;
    maxsize = 0;
    while (token != T_RBRACE) {
	if (eof) { error(EEOF,"in union decl"); break; }
	offset = boffset = 0;		/* each member starts at zero */
	inbitf = 0;
	lastmem = sdeclaration(tag, lastmem, &offset, &boffset, &inbitf);
	if (boffset > 0) offset++;	/* round out to full word */
	if (offset > maxsize) maxsize = offset;	/* find max with old size */
    }
    nextoken();				/* skip over close brace */
    return maxsize;			/* largest elt size is union size */
}
/* -------------------------------------------- */
/*	struct-declaration  Ref.[1]  A.8.5      */
/* -------------------------------------------- */

static SYMBOL *
sdeclaration (tag, prevsmem, offset, boffset, inbitf)
SYMBOL *tag, *prevsmem;
int *offset, *boffset, *inbitf;
{
    SYMBOL base, tempsym, *u;
    int offcode, bsiz, ts;

    pbase(&base);		/* Parse base storage-class and type */
    if (base.Stype == NULL) {
	error(EGEN,"No type-specifier for struct member, assuming int");
	base.Stype = deftype;
    }
    if (base.Sclass != SC_UNDEF)
	error(EGEN,"Storage class illegal for struct member");
    base.Sclass = SC_MEMBER;

    while (1) {
	if (token == T_COLON) {

	    /*
	    ** Colon without declarator before it - specifies space
	    ** left for alignment.  Constant expression following
	    ** colon is how much space, or zero to align to a word.
	    */
	    if (tag->Stype->Tspec == TS_UNION)
		error(EGEN,"Bit fields not allowed in unions");
	    nextoken();			/* skip over colon */
		/* If not on word boundary and previous member was not a
		** bitfield, force alignment.  There can be non-bitfield
		** objects smaller than a word (eg chars).
		*/
		if (*boffset && !(*inbitf)) {
		    *boffset = 0;	/* Force to word boundary */
		    (*offset)++;
		}
	    fldsize(pconst(), offset, boffset); /* parse & handle size */
	    (*inbitf)++;			/* Say in bitfield now */
	} else {

	    /*
	    ** Normal declarator.  Parse it, then check if there is
	    ** a colon expression after it making it a bit field, or
	    ** if it is a whole word expression.
	    **
	    ** For bitfields, the offset is encoded as follows:
	    **  offcode % 07777 - high 12 bits of byte pointer to the field
	    **  offcode >> 12   - word offset in struct
	    ** and then the whole thing is negated.
	    **
	    ** Note that we let the bit offset remain at 36 rather
	    ** than folding it to zero - the calculations are easier.
	    */

	    copysym(&tempsym, &base);
	    u = declarator(&tempsym);
	    if (u == NULL) {		/* Check for case of no identifier */
		warn(EGEN,"Null declarator");
		if (token == T_COLON) continue;	/* Pretend no declarator */
		if (token == T_COMMA) {
		    nextoken();
		    continue;
		}
		break;			/* Something bad, stop loop */
	    }

	    if (token == T_COLON) {

		/* Handle bitfield */
		if (tag->Stype->Tspec == TS_UNION)
		    error(EGEN,"Bit fields not allowed in unions");
		switch (tempsym.Stype->Tspec) {
		    case TS_INT:
			ts = TS_BITF;  break;
		    default:
			error(EGEN,"Bit field must be int or unsigned int");
		    case TS_UINT:		/* Above error drops thru */
			ts = TS_UBITF; break;
		}
		/* If not on word boundary and previous member was not a
		** bitfield, force alignment.  There can be non-bitfield
		** objects smaller than a word (eg chars).
		*/
		if (*boffset && !(*inbitf)) {
		    *boffset = 0;	/* Force to word boundary */
		    (*offset)++;
		}
		(*inbitf)++;			/* Say now in bitfield */

		nextoken();			/* move over colon */
		bsiz = pconst();		/* Parse size */
		offcode = fldsize(bsiz, offset, boffset);	/* Handle it */
		tempsym.Stype = findctype(ts,		/* Make bitfld type */
			bsiz | (tempsym.Stype->Tflag&(TF_CONST|TF_VOLATILE)),
			1, (TYPE *)NULL);
	    } else {			/* not bitfield */

		/* Handle normal non-bitfield member */

		/* Leave byte mode if necessary.
		** If new member is not a byte object, or if byte mode is
		** due to previous bitfield, always leave it, to force
		** word alignment.
		*/
		bsiz = tisscalar(tempsym.Stype) ?	/* Get object size */
			tbitsize(tempsym.Stype)		/* in bits if can */
			: TGSIZ_WORD;		/* else force wd mode */
		if (*boffset > 0		/* If in byte mode */
		  && ((bsiz >= TGSIZ_WORD)	/* and obj not byte */
		      || (*inbitf))) {		/* or prev obj was bitfield */
		    *boffset = 0;	/* Then force word alignment */
		    (*offset)++;	/* and move up */
		}
		*inbitf = 0;		/* Say no longer in bitfield */

		/* Now see if this type should be in byte mode or not.
		** Sizes smaller than a word will either enter or remain
		** in byte mode.  If the size is >= to a word, then the
		** above code will have already taken us out of byte mode.
		*/
		if (bsiz < TGSIZ_WORD) {
		    if (*boffset % bsiz)	/* Align to byte bndry */
			*boffset += bsiz - (*boffset % bsiz);
		    offcode = fldsize(bsiz, offset, boffset);

		} else {			/* One or more words */
		    offcode = *offset;		/* starts at offset */
		    *offset += sizetype(tempsym.Stype); /* remember size */
		}
	    }

	    /*
	    ** Now we have parsed the declarator, and the encoded
	    ** offset is in offcode.  Always make a new symbol for each
	    ** structure member.
	    */
	    idpfcpy(tempsym.Sname, SPC_SMEM, u->Sname);	/* Make smem ident */
	    if (u->Sclass == SC_UNDEF) freesym(u);	/* Clean up */
	    if ((u = findsmem(tempsym.Sname, tag)) != NULL) {
		error(EGEN,"Duplicate struct member declaration: \"%s\"",
			tempsym.Sname+1);
		/* If a dup, just ignore current declaration. */
	    } else {
		u = makesym(tempsym.Sname);	/* Make smem symbol entry */
		u->Sclass = SC_MEMBER;		/* Say it is a struct mem */
		u->Ssmoff = offcode;		/* with the given offset */
		u->Stype = tempsym.Stype;	/* Store type of member */
		u->Ssmtag = tag;		/* Point to parent structure */
		u->Ssmnext = NULL;		/* This is last mem so far */
		prevsmem->Ssmnext = u;	/* Point prev smem to this one */
		prevsmem = u;		/* This is new current smem */
	    }
	}
	if (token != T_COMMA) break;
	nextoken();
    }
    expect(T_SCOLON);
    return prevsmem;			/* return with latest pointer */
}
/* ----------------------------- */
/*      parse bitfield size      */
/* ----------------------------- */

static int
fldsize(bsiz, offset, boffset)
int bsiz, *offset, *boffset;
{
    if (bsiz > TGSIZ_WORD || bsiz < 0)	/* range check */
	error(EGEN, "Bit field longer than word (%d bits)", TGSIZ_WORD);
    if (bsiz == 0 && *boffset > 0)	/* Zero size means round to wd bdry */
	*boffset = TGSIZ_WORD+1;	/* Hack so roundup is forced */

    *boffset += bsiz;			/* advance by that many bits */
    if (*boffset > TGSIZ_WORD) {	/* If not enough room */
	*boffset = bsiz;		/* move bit offset to word bdy */
	(*offset)++;			/* in next word */
    }
    /* Return encoded offset */
    return -(((*offset * 64) + TGSIZ_WORD - *boffset) * 64 + bsiz);
}
/* --------------------------------------------- */
/*	Do processing for a parsed declarator    */
/*      Ref.[1] A.8.6				 */
/* --------------------------------------------- */
/* Called by decllist() to process the results of parsing a declarator.
** This IS called for:
**	toplevel declarations
**	local (at head of block) declarations
** This is NOT called for:
**	abstract declarators - handled by typename().
**	function parameter declarations - handled by funcdef().
**	structure/union declarations - handled by sdeclaration().
** This also checks for an initializer, and handles it if one
** exists.  Note both args refer to temporary symbol structures which are
** NOT in the symbol table.
*/

static NODE *
dodecl (d, base, s)
SYMBOL *d,	/* Declarator identifier, with sc and type all set up */
	*base,	/* Whatever was actually parsed as sc and type */
	*s;	/* Symbol table entry for the identifier */
{
    TYPE nt, *tp;
    NODE *y, *z;
    int cnt;
    char *id;

    /* Symbol table entry will always exist, because the
    ** lexer will have created it if necessary as a global symbol with
    ** symbol class SC_UNDEF.  If the new symbol actually should be a local one
    ** then it needs to be flushed and then created again in the right place.
    */

    /* Check for a function reference, which needs special handling */
    if (d->Stype->Tspec == TS_FUNCT
	&& d->Sclass != SC_TYPEDEF) {	/* A function reference? */
	d->Svalue = 0;		/* Indicate this is a reference */
	funchk(base, d, s);
	return NULL;		/* function decl doesn't use storage */
    }

    /* Real variable or typedef, do things depending on class */
    id = d->Sname;		/* Get convenient pointer to temp sym name */
    idcpy(id, s->Sname);	/* Copy identifier there from real symbol */

    switch (d->Sclass) {
    case SC_TYPEDEF:			/* type definition */
	if (isdupsym(s)) {	/* This symbol already def'd in same block? */
		error(EDSYMB, id);
		return NULL;
	}
	s = uniqsym(s);		/* Create or re-use symbol as needed */
	copysym(s, d);		/* fill in rest of symbol */
	return NULL;		/* no initialization or storage */

    case SC_AUTO:		/* local extent variable, in function */
    case SC_RAUTO:
	if (isdupsym(s))
	    error(EGEN,"Duplicate definition: \"%s\"", id);	/* Barf */
	s = uniqsym(s);			/* Always make local cell */
	d->Svalue = maxauto;		/* remember its stack offset */
	maxauto += sizetype(d->Stype);	/* and count it in to frame size */
	break;			/* Go copy symbol and check for init */

    case SC_EXTERN:		/* extern - assume not a definition */
	d->Svalue = 0;
	if (s->Sclass == SC_AEXTERN)
	    s->Sclass = SC_EXTERN;
	else if (s->Sclass == SC_UNDEF) {
	    copysym(s, d);		/* fill in rest of symbol */
	    s->Srefs = 0;		/* Reset # references */
	} else if (s->Sclass != d->Sclass) error (EDSYMB, id);
	else if (tmismatch (s->Stype, d->Stype)) warn (ETWOTYP, id);
	if (token == Q_ASGN) {
	    warn(EGEN, "extern shouldn't have initializer");
	    d->Svalue = 1;	/* Make it a definition instead */
	    break;		/* and go handle as for assumed-extern */
	}
	return NULL;		/* no initialization or storage */

    case SC_AEXTERN:		/* Assumed-extern, assume defined here. */
	d->Svalue = 1;			/* mark as definition, not ref */
	if ((s->Sclass == SC_EXTERN || s->Sclass == SC_AEXTERN)
	  && s->Svalue == 0) {
	    if (tmismatch (s->Stype, d->Stype)) warn (ETWOTYP, id);
	    s->Sclass = SC_UNDEF;
	}
	if (s->Sclass != SC_UNDEF)	/* Better not be defined yet */
	    error (EDSYMB, id);
	break;

    case SC_STATIC:		/* static, not visible as external */
	if (isdupsym(s))		/* Check for duplicate def */
	    error(EDSYMB, id);
	s = uniqsym(s);			/* Then always make unique cell */
	if (lsymhead) {		/* If within a local block */
	    d->Sclass = SC_ISTATIC;	/* internal static */
	    d->Ssym = newlabel (1);	/* create internal handle on object */
	}
	break;

    default:
	error(EGEN, "Internal error - illegal symbol class %d for %s",
	    d->Sclass, id);
	return NULL;
    }

    copysym(s, d);			/* fill out the symbol */

    /* Parse initializer.
    ** At this point the symbol is guaranteed to have one of these classes:
    **	SC_EXTERN, SC_AEXTERN, SC_STATIC, SC_ISTATIC, SC_AUTO, SC_RAUTO.
    */
    if (token == Q_ASGN) {
	nextoken();			/* skip equal sign */
	if (newpiz)
	    z = npizer(s);		/* New initializer parsing */
	else {	/* Old code */
	    cnt = 0;			/* 0 initializers seen */
	    z = pizer(&cnt);		/* read initializer */

	    /* If initializing an automatic variable, apply the 
	    ** usual assignment conversions.  Stopgap for now.
	    */
	    if (z && cnt==0 && (s->Sclass == SC_AUTO || s->Sclass == SC_RAUTO))
		z = convasgn(s->Stype, z);
	}
	if (z == NULL)
	    error(EGEN,"Null initializer for \"%s\"", s->Sname);
    } else z = NULL;

    /* Make a node giving its name */
    y = defnode(N2, Q_IDENT, s->Stype, 0, (NODE *)NULL);
    y->Nid = s;
    return defnode(N3, N_IZ, (TYPE *)NULL, 0, y, z);	/* and its izer */
}

/* DEFAUTO - Define an automatic variable of the given type.
**	This routine is for the benefit of CCSTMT's function call
**	parsing, which sometimes needs to create temporary internal
**	variables to hold return values.
**	The id is assumed to be unique.
*/
SYMBOL *
defauto(id, typ)
char *id;
TYPE *typ;
{
    SYMBOL *s;
    s = makesym(id);		/* Make symbol in current scope */
    s->Sclass = SC_AUTO;
    s->Stype = typ;
    s->Svalue = maxauto;	/* Remember stack offset for it */
    maxauto += sizetype(typ);
    return s;
}
/* --------------------------- */
/*      parse initializer      */
/* --------------------------- */
/* Called by dodecl() and by itself recursively.
** NEEDS WORK!!  NEEDS LOTS OF WORK!!  Needs to check parsed expression
** result for legality in context, and apply the usual assignment
** conversions on the type.
*/

static NODE *
pizer(cnt)
int *cnt;
{
    NODE *root, *n;

    if (token != T_LBRACE) {
	return exprdecl(); /* not list, simple init.  Get expression. */
    }
    nextoken();				/* skip open brace */
    root = n = defnode(N3, N_IZLIST, (TYPE *)NULL, 0, pizer(0), (NODE *)NULL);
    if (cnt)
	(*cnt)++;
    while (token == T_COMMA) {
	nextoken();			/* skip comma */
	if (token == T_RBRACE) break;	/* allow comma at end of initializer */
	n = n->Nright = defnode(N3, N_IZLIST, (TYPE *)NULL, 0, pizer(0), (NODE *)NULL);
	if (cnt)
	    (*cnt)++;
    }
    expect(T_RBRACE);			/* finish with close brace */
    return root;			/* return whole thing */
}
/* NPIZER - Parse initializer.
**	Argument is pointer to symbol identifier being initialized.
**	Current token should be first thing after the '='.
*/
#if 1
static NODE *npizer();
static NODE *piztype(), *chkinteg(), *pizstruct(), *pizarray(),
	*pexizer(), *pizlist();
static void pizflush();
static int isauto(), nisconst();
			/* These are set by npizer and refed by its subrs */
static int izautof;	/* True if symbol being initialized is automatic */
static SYMBOL *izsym;	/* Symbol for var being initialized */

static NODE *
npizer(s)
SYMBOL *s;
{
    NODE *n;
    int saveopt = optpar;	/* Save value of parser optimization flag */

    izautof = isauto(izsym = s);	/* Set "globals" for subroutines */
    if (!izautof)		/* If we'll want a constant expression, */
	optpar = 1;		/* parse with full optimization. */

    n = piztype(s->Stype, 0);	/* Do outermost-level parse for this type */
    optpar = saveopt;		/* Restore original optimization flag */
    return n;
}
/* PIZTYPE - Parse initializer for a given type.
**	Recursive; makes use of izautof/izsym globals.
*/
static NODE *
piztype(t,lev)
TYPE *t;
int lev;			/* Level being parsed.  0 - outermost */
{
    NODE *e, *n;
    extern NODE *fold();

    switch (t->Tspec) {
	case TS_BITF:	case TS_UBITF:
	case TS_CHAR:	case TS_UCHAR:
	case TS_SHORT:	case TS_USHORT:
	case TS_INT:	case TS_UINT:
	case TS_LONG:	case TS_ULONG:
	    /* Parse a single expression */
	    if ((e = pexizer(lev)) == NULL)
		break;
	    if (!tisarith(e->Ntype)) {
		error(EGEN,"Initializer must be of arithmetic type");
		break;
	    }
	    return chkinteg(t, e);

	case TS_FLOAT:	case TS_DOUBLE: case TS_LNGDBL:
	    /* Parse a single expression */
	    if ((e = pexizer(lev)) == NULL)
		break;
	    if (!tisarith(e->Ntype)) {
		error(EGEN,"Initializer must be of arithmetic type");
		break;
	    }
	    if ((n = convasgn(t, e)) != e)	/* Apply assignment convs */
		e = optpar ? fold(n) : n;	/* and optimize if needed */

	    if (!izautof && e->Nop != N_FCONST) {
		error(EGEN,"Floating-point constant required as initializer");
		break;
	    }
	    return e;

	/* Enums are treated as integers, thus any integer type is allowed */
	case TS_ENUM:
	    /* Parse a single expression */
	    if ((e = pexizer(lev)) == NULL)
		break;
	    if (!tisinteg(e->Ntype)) {
		error(EGEN,"Initializer must be of integral type");
		break;
	    }
	    if ((n = convasgn(t, e)) != e)	/* Apply assignment convs */
		e = optpar ? fold(n) : n;	/* and optimize if needed */

	    if (!izautof && e->Nop != N_ICONST) {
		error(EGEN,"Enum or integer constant required as initializer");
		break;
	    }
	    return e;


	case TS_PTR:
	    /* Parse a single expression */
	    if ((e = pexizer(lev)) == NULL)
		break;
	    if ((n = convasgn(t, e)) != e)	/* Apply assignment convs */
		e = optpar ? fold(n) : n;	/* and optimize if needed */
	    if (t != e->Ntype) {		/* Types must match */
		error(EGEN,"Pointer initializer has wrong type");
		break;
	    }

	    /* A constant expression for a pointer requires hairy checks. */
	    if (!izautof		/* If must be a constant expr */
	      && !nisconst(e)) {	/* then check it out */
		error(EGEN,"Pointer initializer not constant");
		break;
	    }
	    return e;

	case TS_ARRAY:
	    return pizarray(t, lev);

	case TS_STRUCT:
	    return pizstruct(t, lev);

	case TS_UNION:		/* Forget about unions for now */
	    error(EGEN,"Initialization of unions is not permitted");
	    pizlist();		/* Parse and ignore it */
	    break;

	case TS_VOID:
	    error(EGEN, "Cannot initialize void type");
	    pizlist();		/* Parse and ignore it */
	    break;

	case TS_FUNCT:
	    error(EGEN, "Cannot initialize function type");
	    pizlist();		/* Parse and ignore it */
	    break;

	default:
	    error(EINT, "Unknown Tspec = %d", t->Tspec);
	    break;
    }
    return NULL;
}

/* CHKINTEG - Check initializer for integral type.
**	This is a subroutine for code sharing purposes (see pizarray).
*/
static NODE *
chkinteg(t, e)
TYPE *t;
NODE *e;
{
    NODE *n;

    if ((n = convasgn(t, e)) != e)	/* Apply assignment convs */
	e = optpar ? fold(n) : n;	/* and optimize if needed */

    if (!izautof && e->Nop != N_ICONST)
	error(EGEN,"Integer constant required as initializer");
    return e;
}
/* PIZSTRUCT - Parse initializer for a structure.
*/
static NODE *
pizstruct(t, lev)
TYPE *t;
int lev;
{
    register SYMBOL *smem;
    NODE *e, *n, *root;
    int braces;

    if (izautof)			/* Complain now, fail later */
	error(EGEN,"Initialization of automatic structures is not allowed");

    braces = (token == T_LBRACE);	/* Remember if have braces */
    if (!braces && lev == 0) {		/* If top level, must have them */
	error(EGEN,"Structure initializer must be enclosed in braces");
	pizflush(0);			/* Flush entire izer */
	return NULL;
    }
    if ((smem = t->Tsmtag->Ssmnext) == NULL) {
	error(EGEN,"Attempting to initialize an undefined structure");
	pizflush(0);			/* Flush entire izer */
	return NULL;
    }

    /* Now loop through structure members, reading an initializer for each */
    if (braces) nextoken();			/* Skip over left-brace */
    if (smem->Sclass != SC_MEMBER)		/* Paranoia check */
	error(EINT,"bad smem class");
    root = n = defnode(N3, N_IZLIST, (TYPE *)NULL, 0,	/* Parse 1st member */
		piztype(smem->Stype, 1), (NODE *)NULL);

    if (!braces) {
	while (smem = smem->Ssmnext) {
	    if (token != T_COMMA) {
		error(EGEN,"Too few elements in unbraced struct initializer list");
		return NULL;
	    }
	    nextoken();				/* Skip comma */
	    if (smem->Sclass != SC_MEMBER)	/* Paranoia check */
		error(EINT,"bad smem class");
	    n = n->Nright = defnode(N3, N_IZLIST, (TYPE *)NULL, 0,
		piztype(smem->Stype, 1), (NODE *)NULL);	/* Parse element */
	}
	/* When loop ends, fall thru to successful return */

    } else {	/* Have braces, life is more complicated */
	while (token == T_COMMA) {
	    if (nextoken() == T_RBRACE) break;	/* Skip comma, allow ",}" */
	    if ((smem = smem->Ssmnext) == NULL) {
		error(EGEN,"Too many components in struct initializer list");
		pizflush(1);			/* Flush rest of list */
		if (token == T_RBRACE) nextoken();
		return NULL;
	    }
	    if (smem->Sclass != SC_MEMBER)	/* Paranoia check */
		error(EINT,"bad smem class");
	    n = n->Nright = defnode(N3, N_IZLIST, (TYPE *)NULL, 0,
		piztype(smem->Stype, 1), (NODE *)NULL);	/* Parse element */
	}
	/* Check for proper ending */
	if (token != T_RBRACE) {
	    error(EGEN,"Bad initializer list syntax");
	    pizflush(1);		/* Flush inside of list */
	    if (token == T_RBRACE)
		nextoken();		/* Flush end close-brace if one */
	    return NULL;
	}
	nextoken();			/* Skip close-brace */
    }

    return (izautof ? NULL : root);
}
/* PIZARRAY - Parse initializer for an array.
*/
static NODE *
pizarray(t, lev)
TYPE *t;
int lev;
{
    register TYPE *subt;
    NODE *e, *n, *root;
    int cnt, braces, size;

    if (izautof)		/* Complain now, fail later */
	error(EGEN,"Initialization of automatic arrays is not allowed");

    if ((subt = t->Tsubt) == NULL) {	/* Paranoia */
	error(EINT,"undefined array");
	pizflush(0);			/* Flush entire izer */
	return NULL;
    }
    braces = (token == T_LBRACE);	/* Remember whether have braces */
    if (braces) nextoken();		/* Skip over left-brace */

    /* Now handle first array element.  Requires a bunch of special hackery
    ** for initializing array of char with a string literal.
    ** This is only allowed if there are no braces.
    ** We have to do special hackery here if the initializer is NOT a
    ** string literal, because we've already parsed a complete expression
    ** and it's too late to pass it on to piztype for normal handling.
    ** So we duplicate some code and call a common integral-type handler.
    */
    if (!braces && tischar(subt)) {
	if (!(e = exprdecl()))		/* Get expression, will use or flush */
	    return NULL;
	if (e->Nop == N_SCONST) {	/* Is expr a string constant? */
	    if (t->Tsize == 0)		/* If size undefined, make new type */
		izsym->Stype = t = findsztype(TS_ARRAY, e->Nsclen, subt);
	    else if (t->Tsize < e->Nsclen)
		error(EGEN,"String exceeds char array bounds");
	    return (izautof ? NULL : e);	/* Last-minute fail if auto */
	}
	
	/* This code duplicates that found in piztype() for integral types */
	if (!tisarith(e->Ntype))
	    error(EGEN,"Char array initializer must be string literal or of arith type");
	n = chkinteg(subt, e);	/* This is 1st element of unbraced array */

    } else n = NULL;		/* Say no 1st element parsed yet */

    if (!braces && lev == 0) {
	error(EGEN,"Array initializer must be enclosed in braces");
	return n;
    }

    /* Check array size.  If unspecified, we must be handling a braced list
    ** at outermost level.  This is just a paranoid internal consistency check.
    */
    if (((size = t->Tsize) == 0) && (!braces || lev)) {
	error(EINT, "inner array size unspecified");
	pizflush(1);		/* Flush to end of inner list */
	return NULL;
    }

    /* Now loop through list, reading array elements */
    if (!n)			/* Unless already set by char array check */
	n = piztype(subt, 1);	/* Parse 1st element */
    root = n = defnode(N3, N_IZLIST, (TYPE *)NULL, 0,	/* Put into list */
		n, (NODE *)NULL);
    cnt = 1;
    if (!braces) {
	while (++cnt <= size) {			/* Stop when array filled */
	    if (token != T_COMMA) {		/* Fail if bad separator */
		error(EGEN,"Too few elements in unbraced array initializer list");
		return NULL;
	    }
	    nextoken();				/* Skip comma */
	    n = n->Nright = defnode(N3, N_IZLIST, (TYPE *)NULL, 0,
		piztype(subt, 1), (NODE *)NULL);	/* Parse element */
	}
	/* When loop ends, fall thru to successful return */

    } else {	/* Have braces, life is more complicated */
	for (; token == T_COMMA; ++cnt) {
	    if (nextoken() == T_RBRACE) break;	/* Skip comma, allow ",}" */
	    n = n->Nright = defnode(N3, N_IZLIST, (TYPE *)NULL, 0,
		piztype(subt, 1), (NODE *)NULL);	/* Parse element */
	}
	if (token != T_RBRACE) {
	    error(EGEN,"Bad initializer list syntax");
	    pizflush(1);			/* Flush inner list */
	    if (token == T_RBRACE)
		nextoken();
	    return NULL;
	}
	nextoken();			/* Skip close-brace */

	if (t->Tsize == 0)		/* If setting size, cnt always OK */
	    izsym->Stype = t = findsztype(TS_ARRAY, cnt, subt);
	else if (cnt > size)		/* Complain if too many */
		error(EGEN,"Too many elements in array initializer list");
    } /* End of brace handling */

    return (izautof ? NULL : root);	/* Last-minute fail if auto */
}
/* PEXIZER - Parse single initializer expression.
**	Should be only one expression; outermost braces are allowed.
**	Never returns a list; complains and fixes up as necessary.
*/
static NODE *
pexizer(lev)
int lev;			/* 0 if outermost level */
{
    NODE *n;
    if (token != T_LBRACE)
	return exprdecl();	/* All's well */

    /* We have a list of some sort.  This is only legal if at outermost
    ** level AND there is only one thing in the list.
    */
    if (lev)
	error(EGEN,"Inner initializer for this object cannot be a list");
    n = pizlist();		/* Parse the list, always returns N_IZLIST */
    if (n->Nright && lev == 0)	/* More than one thing in list? */
				/* Complain unless already complained above */
	error(EGEN, "Initializer cannot be a list of more than one element");
    if ((n = n->Nleft) == NULL) {
	error(EGEN, "Null initializer");
	return NULL;
    }
    if (n->Nop == N_IZLIST) {	/* Sublist? */
	error(EGEN, "Initializer cannot have sublist");
	while (n->Nop == N_IZLIST && (n = n->Nleft));
    }	
    return n;
}

/* PIZLIST - Parse initializer list
**	Mainly for error recovery, when contents aren't analyzed closely.
*/
static NODE *
pizlist()
{
    NODE *n, *root;

    if (token != T_LBRACE)	/* If not a list */
	return exprdecl();	/* Get expression and return simple izer */

    nextoken();				/* Skip open brace */
    root = n = defnode(N3, N_IZLIST, (TYPE *)NULL,0, pizlist(0), (NODE *)NULL);
    while (token == T_COMMA) {
	nextoken();			/* Skip comma */
	if (token == T_RBRACE) break;	/* allow comma at end of initializer */
	n = n->Nright = defnode(N3, N_IZLIST, (TYPE *)NULL, 0,
		pizlist(), (NODE *)NULL);
    }
    expect(T_RBRACE);			/* finish with close brace */
    return root;			/* return whole thing */
}

/* PIZFLUSH - Flush initializer tokens
**	Used during error recovery.  Type says what situation we're in.
**	0 - at start of izer, flush entire izer.
**		(stop if top-level comma, gobble end close-brace if one seen)
**		This also works to flush only a single list, if at its start.
**	1 - inside a list, flush to higher-level close-brace and don't
**		gobble it, so caller can handle close-brace.
*/
static void
pizflush(typ)
int typ;
{
    int lev = 0;
    if (typ) lev = 1;		/* Pretend already in a list */
    for(;; nextoken()) switch(token) {
	case T_COMMA:
	    if (typ == 0 && lev <= 0) return;
	    break;
	case T_LBRACE:		/* Start of list */
	    lev++;
	    break;
	case T_RBRACE:		/* End of list */
	    if (--lev <= 0) {
		if (typ == 0) nextoken();	/* Maybe gobble close-brace */
		return;
	    }
	    break;

	case T_EOF:		/* Input EOF */
	case T_SCOLON:		/* End of declaration statement */
	    return;
    }
}
/* NISCONST - Returns true if expression is an allowable initializer constant.
**	Return value indicates something about the type of constant:
**	0 - not a constant
**	1 - definitely a constant (arithmetic, or a cast pointer)
**	2 - address of some kind
**	3 - function address (cannot add or subtract from this)
*/
static int
nisconst(e)
NODE *e;
{
    int res;

    switch(e->Nop) {
	case N_CAST:		/* Assume generator will be able to handle */
	    return nisconst(e->Nleft);

	case N_ICONST:
	case N_FCONST:
	case N_PCONST:
	    return 1;		/* Simple constant */
	case N_SCONST:
	    return 2;		/* Address */

	case Q_IDENT:
		/* Identifier.  See documentation for Q_IDENT in cctoks.h
		** for explanation of this method of testing.
		*/
	    switch (e->Nid->Stype->Tspec) {
		case TS_FUNCT:	return 3;	/* Function address */
		case TS_ARRAY:	return 2;	/* Array address */
	    }
	    break;

	case N_ADDR:
	    switch (e->Nleft->Nop) {
		case N_PTR:
		    return nisconst(e->Nleft->Nleft);

		/* Structure hair.
		** For MEMBER (->) the Nleft must be a constant address.
		**	Can just apply nisconst to this.
		** For DOT (.) the Nleft can be anything that evaluates into
		**	a static structure.  We assume this is only possible
		**	with either Q_IDENT, or N_PTR of a struct addr.
		*/
		case Q_DOT:
		    if (tisbitf(e->Nleft->Ntype))	/* No bitfield ptrs */
			return 0;
		    switch (e->Nleft->Nleft->Nop) {
			case Q_IDENT:
			    switch (e->Nleft->Nleft->Nid->Sclass) {
				case SC_AEXTERN: case SC_EXTERN:
				case SC_STATIC: case SC_ISTATIC:
				    return 2;	/* Good address of object */
			    }
			    break;
			case N_PTR:
			    if (nisconst(e->Nleft->Nleft->Nleft)==2)
				return 2;
			    break;
		    }
		    break;			/* Otherwise fail. */

		case Q_MEMBER:
		    if (!tisbitf(e->Nleft->Ntype)	/* No bitfield ptrs */
		      && nisconst(e->Nleft->Nleft)==2)	/* If struct addr is */
			return 2;		/*  OK, then we're OK */
		    break;			/* Otherwise fail. */

		case Q_IDENT:	/* Addr OK if of external or static */
			/* Needn't test type since parser checks it while
			** parsing "&" to verify not function or array.
			*/
		    switch (e->Nleft->Nid->Sclass) {
			case SC_AEXTERN: case SC_EXTERN:
			case SC_STATIC: case SC_ISTATIC:
			    return 2;		/* Good address of object */
		    }
		    break;
	    }
	    break;

	/* Non-atomic expression checks, for plus and minus. */
	case Q_PLUS:
	    if (e->Nleft->Nop == N_ICONST	/* Integ constant */
		&& nisconst(e->Nright) == 2)	/* plus address */
		    return 2;
	    /* Fall through into Q_MINUS code */
	case Q_MINUS:
	    if (nisconst(e->Nleft) == 2		/* Address */
	      && e->Nright->Nop == N_ICONST)	/* plus/minus integ constant */
		return 2;
	    break;
    }
    return 0;
}

#if 0
/* CHKPTRCON - utility routine to check out a pointer initializer.
**	Allowed initializers (per CARM 4.6.3) must be an integer or
**	a static/external address plus/minus an integer.
*/
static NODE *
chkptrcon(e)
NODE *e;
{
    NODE *n, *addr, *con;

    n = (e->Nop == N_CAST) ? e->Nleft : e;	/* Allow casts */
    if (n->Nop == N_ICONST) return e;	/* Integer const OK (case 1,6) */
    if (n->Nop == Q_IDENT) return e;	/* Funct or array name OK (case 2,3) */
					/* (type cked by convasgn) */

    if (n->Nop == Q_PLUS || n->Nop == Q_MINUS) {
	addr = n->Nleft;
	con = n->Nright;
	if (n->Nop == Q_PLUS && con->Nop != N_ICONST)
	    addr = n->Nright, con = n->Nleft;
	if (con->Nop != N_ICONST) {
	    error(EGEN,"Pointer initializer constant has bad expression");
	    return NULL;
	}
    } else addr = n;

    /* Check out address */
    n = (addr->Nop == N_CAST) ? addr->Nleft : addr;	/* Allow casts */
    if (n->Nop == N_ICONST) return e;	/* Integer const OK (case 6) */
    if (n->Nop == N_SCONST) return e;	/* String const OK (case 7) */
    if (n->Nop == N_ADDR) {		/* Addr of something (case 4) */
	addr = n->Nleft;
	if (addr->Nop != Q_IDENT
	  || isauto(addr->Nid)) {
	    error(EGEN,"Constant pointer initializer has non-constant addr");
	    return NULL;
	}

    }
    return e;
}
#endif /* commented-out code */

/* ISAUTO - Returns true if symbol has automatic extent
*/
static int
isauto(s)
SYMBOL *s;
{
    switch(s->Sclass) {
	case SC_ARG:  case SC_RARG:
	case SC_AUTO: case SC_RAUTO:
		return 1;
	default:
		return 0;
    }
}
#endif
/* ----------------------------------- */
/*	type-name  Ref.[1]  A.8.7      */
/* ----------------------------------- */
/*	This parses a "type name" built from an abstract declarator.
** Used only for cast expressions and the sizeof operator.
*/

TYPE *
typename()
{
    SYMBOL s, *t;

    pbase(&s);			/* Parse base */
    if (s.Sclass != SC_UNDEF) {
	error(EGEN,"Storage class not allowed for type-name");
	s.Sclass = SC_UNDEF;
    }
    if (s.Stype == NULL) {
	error(EGEN,"No type-specifier for type-name, assuming int");
	s.Stype = deftype;
    }
    if ((t = decl0(&s)) != NULL) {
	error(EGEN, "Identifier not allowed in type-name");
	if (t->Sclass == SC_UNDEF)
	    freesym(t);			/* Clean up */
    }
    return s.Stype;
}
/* ------------------------------------------- */
/*      check two types for compatibility      */
/* ------------------------------------------- */

static int
tmismatch (t1, t2)
TYPE *t1, *t2;
{
    if (t1 == t2) return 0;
    if (t1->Tspec != TS_ARRAY || t2->Tspec != TS_ARRAY) return 1;
    if (t1->Tsubt != t2->Tsubt) return 1;
    return (t1->Tsize != 0 && t2->Tsize != 0);
}