/*
 * C compiler
 * ==========
 *
 * Copyright 1989, 1990, 1991 Christoph van Wuellen.
 * Credits to Matthew Brandt.
 * All commercial rights reserved.
 *
 * This compiler may be redistributed as long there is no
 * commercial interest. The compiler must not be redistributed
 * without its full sources. This notice must stay intact.
 *
 * History:
 *
 * 1989   starting an 68000 C compiler, starting with material
 *        originally by M. Brandt
 * 1990   68000 C compiler further bug fixes
 *        started i386 port (December)
 * 1991   i386 port finished (January)
 *        further corrections in the front end and in the 68000
 *        code generator.
 *        The next port will be a SPARC port
 */

#include "chdr.h"
#include "expr.h"
#include "cglbdec.h"
#include "proto.h"
#include "outproto.h"

#if defined(__STDC__) || defined(__cplusplus)
#define P_(s) s
#else
#define P_(s) ()
#endif

static	BLOCK *	get_parameters			P_((SYM *));
static	BOOL	is_local_typedef		P_((CHAR *));
static	BOOL	is_representable		P_((IVAL,  TYP *));
static	BOOL	is_typedef			P_((CHAR *));
static	MSGNUM	check_prototype			P_((SYM *));
static	QUALIFIER type_qualifier		P_((QUALIFIER));
static	QUALIFIER type_qualifier_list		P_((void));
static	SIZE	declaration			P_((STORAGE, STORAGE, SIZE));
static	SIZE	struct_declaration_list		P_((TYP *));
static	SIZE	struct_declarator_list		P_((TYP *, SIZE, BOOL));
static	STORAGE	storage_class_specifier		P_((STORAGE,  BOOL));
static	SYM *	declarator			P_((STORAGE, TYP *));
static	SYM *	declare				P_((SYM *, STORAGE));
static	SYM *	direct_declarator		P_((STORAGE, TYP *));
static	TYP *	declaration_specifiers		P_((STORAGE *, STORAGE));
static	TYP *	direct_declarator_tail		P_((SYM *, TYP *));
static	TYP *	enum_specifier			P_((STORAGE));
static	TYP *	enumeration_list		P_((void));
static	TYP *	struct_or_union_specifier	P_((STORAGE));
static	void	check_stdarg			P_((SYM *));
static	void	check_tag			P_((SYM *,  BTYPE));
static	void	identifier_list			P_((void));
static	void	parameter_type_list		P_((void));

#undef	P_
#undef	P_

/* type specifiers */
#define	T_VOID		((unsigned) 1)
#define	T_CHAR		((unsigned) 2)
#define	T_SHORT		((unsigned) 4)
#define	T_INT		((unsigned) 8)
#define	T_LONG		((unsigned) 16)
#define	T_FLOAT		((unsigned) 32)
#define	T_DOUBLE	((unsigned) 64)
#define	T_SIGNED	((unsigned) 128)
#define	T_UNSIGNED	((unsigned) 256)
#define	T_ALL		(T_VOID|T_CHAR|T_SHORT|T_INT|T_LONG|T_FLOAT|T_DOUBLE|T_SIGNED|T_UNSIGNED)

/*
**	Returns true if the token is one which starts a type
**	specifier, otherwise it returns false.
*/

static BOOL
is_type_specifier P1(TOKEN, st)
{
    switch (st) {
      case tk_id:
	return is_typedef(lastsym);
      case kw_void:
      case kw_char:
      case kw_short:
      case kw_unsigned:
      case kw_signed:
      case kw_long:
      case kw_struct:
      case kw_union:
      case kw_enum:
      case kw_float:
      case kw_double:
      case kw_int:
	return TRUE;
     default:
	break;
    }
    return FALSE;
}


/*
**	Returns true if the token is one which starts a storage
**	class specifier.
*/

static BOOL
is_storage_class P1( TOKEN, st)
{
    switch (st) {
      case kw_typedef:
      case kw_static:
      case kw_auto:
      case kw_register:
      case kw_extern:
	return TRUE;
      default:
	break;
    }
    return FALSE;
}


/*
**	Returns true if the token is one which starts a type
**	qualifier.
*/

static BOOL
is_type_qualifier P1( TOKEN, st)
{
    switch (st) {
      case kw_const:
      case kw_volatile:
#ifdef TOPSPEED
      case kw_cdecl:
#endif /* TOPSPEED */
#ifdef EXTENSION
      case kw_restrict:
#endif /* EXTENSION */
	return TRUE;
      default:
	break;
    }
    return FALSE;
}


/*
**	Return true if the token is one which starts a declaration
**	specifier, otherwise it returns false.
*/

static BOOL
is_declaration_specifier P1( TOKEN, st)
{
    return is_type_specifier(st) ||
	   is_storage_class(st) ||
	   is_type_qualifier(st);
}


/*
**	Returns true if the token is one which starts a specifier
**	qualifier list, otherwise it returns false.
*/

static BOOL
is_specifier_qualifier P1( TOKEN, st)
{
    return is_type_specifier(st) ||
	   is_type_qualifier(st);
}


/*
**	Returns trus if the token is one which starts a type
**	name, otherwise it returns false.
*/

BOOL
is_type_name P1( TOKEN, st)
{
    return is_specifier_qualifier(st);
}


/*
**	Returns true if the integer constant can be represented by
**	the specified type, otherwise it returns false.
*/

static BOOL
is_representable P2( IVAL, i,  TYP *, tp)
{
    switch (tp->type) {
      case bt_char:
      case bt_schar:
	return (i >= -128L && i < 128L);
      case bt_uchar:
      case bt_charu:
	return (i >= 0L && i < 256L);
      case bt_short:
      case bt_int16:
	return (i >= -32768L && i < 32768L);
      case bt_ushort:
      case bt_uint16:
	return (i >= 0L && i < 65536L);
      case bt_int32:
      case bt_long:
	return TRUE;
      case bt_uint32:
      case bt_ulong:
	return TRUE;
      default:
	break;
    }
    return FALSE;
}


/*
**	Returns true if the current token is a typedef name in
**	the current scope, otherwise it returns false.
*/

static BOOL
is_local_typedef P1(CHAR *, str)
{
    SYM *sp;
    assert (lastst == tk_id);
    sp = sym_search(str);
    return (sp && sp->storage_class == sc_typedef && is_local_scope(sp));
}


/*
**	Returns true if the current token is a typedef name,
**	otherwise it returns false.
*/

static BOOL
is_typedef P1(CHAR *, str)
{
    SYM *sp;
    assert (lastst == tk_id);
    sp = sym_search(str);
    return (sp && sp->storage_class == sc_typedef);
}


/*
**	If the parameter parmN is declared with the register storage
**	class, with a function or array type, or with a type that is not
**	compatible with the type that results after application of the
**	default promotions, the behavious is undefined.
*/

static void
check_stdarg P1( SYM *, sp)
{
     TYP *	tp = sp->tp;
    if ((sp->storage_class == sc_register) || is_array_type(tp) || is_function_type(tp))
	message (WARN_STDARG);
    else {
	switch (tp->type) {
	  case bt_char:
	  case bt_uchar:
	  case bt_schar:
	  case bt_charu:
	  case bt_short:
	  case bt_ushort:
	  case bt_float:
	    message (WARN_STDARG);
	    break;
	  default:
	    break;
	}
    }
}

static void
check_tag P2( SYM *, sp,  BTYPE, bt)
{
    switch (sp->tp->type) {
      case bt_struct:
      case bt_union:
	if (sp->tp->type == bt)
	    return;
	break;
      case bt_schar:
      case bt_uchar:
      case bt_short:
      case bt_ushort:
      case bt_int16:
      case bt_uint16:
      case bt_int32:
      case bt_uint32:
      case bt_long:
      case bt_ulong:
	if (tp_int->type == bt)
	    return;
	break;
      default:
	break;
    }
    message (ERR_TAG, sp->name);
}

static void
check_function_declaration P1( SYM *, sp)
{
    /*
    **	The storage-specifier, if any, in the declaration specifiers
    **	shall be either extern or static.
    */

    switch (sp->storage_class) {
      case sc_global:
      case sc_external:
      case sc_static:
	break;
      default:
	message(ERR_ILLCLASS);
    }
    if (returned_type(sp->tp)->type != bt_void)
	check_complete (returned_type(sp->tp));
}

static void
check_function_definition P1( SYM *, sp)
{
    /*
    **	The return type of a function shall be void or an object type
    **	other than array.
    */

     TYP * tp = returned_type(sp->tp);
    switch (tp->type) {
      case bt_func:
	message(ERR_FUNC);
	break;
      case bt_pointer16:
      case bt_pointer32:
	if (is_array_type(tp))
	    message (ERR_ARRAYRETURN);
	break;
      case bt_void:
	if (is_qualified_type(tp))
	    message (WARN_QUALIFIER);
	break;
      default:
	break;
    }
}


/*
**	Checks that i has a value representable as a 'tp'
*/
static void
check_representable P2( IVAL, i,  TYP *, tp)
{
    if (!is_representable(i, tp))
	message(ERR_REPRESENT, i);
}

static MSGNUM
check_prototype P1( SYM *, sp)
{
    for (;sp != NIL_SYM; sp = sp->next) {
	switch (sp->tp->type) {
	  case bt_char:
	  case bt_charu:
	  case bt_uchar:
	  case bt_schar:
	  case bt_short:
	  case bt_ushort:
	  case bt_float:
	  case bt_ellipsis:
	    return ERR_PROTODEF;
	  default:
	    break;
	}
    }
    return (MSGNUM)0;
}

MSGNUM
check_proto P3( SYM*, sp1,  SYM*, sp2,  BOOL, KandR)
{
    if (sp1 == sp2)
	return (MSGNUM)0;	/* same or no prototypes */
    if (KandR) {
	if (sp1 == NIL_SYM || sp2 == NIL_SYM)
	    return (MSGNUM)0;	/* no prototype */
    } else {
	if (sp1 == NIL_SYM)
	    return check_prototype(sp2);
	if (sp2 == NIL_SYM)
	    return check_prototype(sp1);
    }

    while (sp1 != NIL_SYM && sp2 != NIL_SYM) {
	if (is_same_type (sp1->tp, sp2->tp, KandR) != (MSGNUM)0)
	    return ERR_PROTO;
	sp1 = sp1->next;
	sp2 = sp2->next;
    }
    if (KandR) {
	if (sp2 != NIL_SYM && sp2->tp->type == bt_void)
	    return (MSGNUM)0;
    }
    return (MSGNUM)(sp1 == sp2 ? 0 : ERR_PROTO);
}

/*
 * Adds the symbol to the symbol table.
 */
static SYM*
declare P2(SYM*, sp,  STORAGE, sc)
{
    BOOL	func_body;
    switch (sp->tp->type) {
      case bt_func:
	func_body = (lastst == tk_begin || lastst == kw_register ||
		     lastst == kw_auto || is_type_name(lastst));
	switch(sp->storage_class) {
	  case sc_external:
	    if (func_body)
		sp->storage_class = sc_global;
	    break;
	  case sc_global:
	    if (!func_body)
		sp->storage_class = sc_external;
	    break;
	  case sc_auto:
	    sp->storage_class = sc_external;
	    break;
	  case sc_static:
	  case sc_typedef:
	    break;
	  default:
	    CANNOT_REACH_HERE();
	    break;
	}
	if (sc == sc_auto) {
	    switch (sp->storage_class) {
	      default:
		message (ERR_ILLCLASS);
		/*FALLTHRU*/
	      case sc_external:
	      case sc_typedef:
		break;
	    }
	}
	break;
      default:
	break;
    }
    sym_append(&sp);
    return sp;
}


SYM *
identifier P0(void)
{
    SYM	*sp = sym_search(lastsym);
    if (sp != NULL) {
	if (is_symbol_outscope(sp))
	    message(WARN_OUTSCOPE, sp->name);
	getsym();
	return sp;
    }

    sp = mk_sym(lastsym, sc_external, tp_long);
    getsym();
    switch (lastst) {
      case tk_openpa:
	sp->tp = tp_func;
	message(WARN_IMPLICITFN,sp->name);
        break;
      default:
	message(ERR_UNDEFINED, sp->name);
	break;
    }
    sym_append(&sp);
    return sp;
}


/*ANSI 3.6.2
 *	declaration-list:
 *		declaration
 *		declaration-list declaration
 */
SIZE
declaration_list P2( STORAGE, def_sc, SIZE, offset)
{
    while(is_declaration_specifier(lastst)) {
	if ((def_sc != sc_member) && is_label(lastst))
	    return offset;
	offset = declaration(def_sc, def_sc, offset);
    }
    return offset;
}


/*ANSI 3.5.4
 *	identifier-list:
 *		identifier
 *		identifier-list , identifier
 */
static void
identifier_list P0(void)
{
    SYM	*sp;
    global_flag--;
    assert(lastst == tk_id);
    for (;;) {
	switch (lastst) {
	  case tk_id:
	    sp = mk_sym(lastsym, sc_parms, NIL_TYP);
	    getsym();
	    sym_append(&sp);
	    break;
	  default:
	    message (ERR_PARMS);
	    global_flag++;
	    return;
	}
	if (lastst != tk_comma) {
	    global_flag++;
	    return;
	}
	getsym();
    }
}


/*ANSI 3.5.5
 *	type-name:
 *		specifier-qualifier-list abstract-declarator[opt]
 */
TYP *
type_name P0(void)
{
    TYP		*tp = NIL_TYP;
    SYM		*sp = NIL_SYM;
    STORAGE	 sc;

    if(is_type_name(lastst)) {
	sc = sc_tag;
	tp = declaration_specifiers(&sc, sc_global);
	switch(lastst) {
	  case tk_star:
	  case tk_openbr:
	  case tk_openpa:
	    sp = declarator(sc, tp);
	    tp = sp->tp;
	    break;
	  default:
	    break;
	}
    }
    return tp;
}


/*ANSI 3.5.4
 *	parameter-type-list:
 *		parameter-list
 *		parameter-list , ...
 *
 *	parameter-list:
 *		parameter-declaration
 *		parameter-list , parameter-declaration
 *
 *	parameter-declaration:
 *		declaration-specifiers declarator
 *		declaration-specifiers abstract-declarator[opt]
 */
static void
parameter_type_list P0(void)
{
    TYP		*tp;
    SYM	*sp;
    STORAGE	sc;

    global_flag++;	/* allocate into global heap */
    while (is_declaration_specifier(lastst)) {
	sc = sc_parms;
	tp = declaration_specifiers(&sc, sc_parms);
	if (sc ==  sc_auto)
	    sc = sc_parms;
	sp = declarator(sc, tp);
	if (sp->tp->type == bt_func) {
	    /*
	     * Functions which are passed as a parameter are really
	     * pointers to a function.
	     */
	    sp->tp = mk_type (tp_pointer, sp->tp);
	}
	sym_append (&sp);
	if (lastst != tk_comma || is_void(sp->tp))
	    break;
	getsym();
	switch (lastst) {
	  case tk_ellipsis:
	    getsym();
	    check_stdarg(sp);
	    tp = mk_type(tp_ellipsis, NIL_TYP);
	    sp = mk_sym((CHAR *)"", sc_tag, tp);
	    sym_append(&sp);
	    global_flag--;
	    return;
	  case tk_closepa:
	    if (lattice_option) {
		tp = mk_type(tp_ellipsis, NIL_TYP);
		sp = mk_sym((CHAR *)"", sc_tag, tp);
		sym_append(&sp);
		global_flag--;
		return;
	    }
	    break;
	  default:
	    break;
	}
    }
    global_flag--;
    return;
}

/*ANSI 3.5.2.2
 *	enumeration-list:
 *		enumerator
 *		enumerator-list , enumerator
 *
 *	enumerator:
 *		enumeration-constant
 *		enumeration-constant = constant-expression
 *
 *	enumeration-constant:
 *		identifier
 */
static TYP *
enumeration_list P0(void)
{
    IVAL evalue, minval, maxval;
    int  count;
    TYP *tp = mk_type (tp_enum, NIL_TYP);
    TYP *tp2 = mk_type (tp_int, NIL_TYP);
    set_derived(tp2);
    set_enum(tp2);
    set_enumtype(tp2, tp);
    for (count = 0, minval = maxval = 0L, evalue = -1L; lastst == tk_id; ) {
	SYM *sp = mk_sym(lastsym, sc_const, tp2);
	getsym();
	if (lastst == tk_assign) {
	    getsym();
	    evalue = intexpr();
	} else {
	    if (evalue == (tp_int->size == 2L ? 32767L : 2147483647L))
	        message(ERR_ENUMWRAP);
	    evalue++;
	}
	/*
	 *Constraint: The expression that defines the value of an enumeration
	 *	      constant shall be an integral constant expression that
	 *	      has a value representable as an int.
	 */
	check_representable(evalue, tp_int);
	if (evalue < minval)
	    minval = evalue;
	if (evalue > maxval)
	    maxval = evalue;
	sp->value.i = evalue;
	sym_append(&sp);
	count++;
	if (lastst != tk_comma)
	    break;
	getsym();
    }
    if (count == 0)
	message(ERR_INCOMPLETE, "enum");

    /* now determine the best integer type to hold the enumeration */
#ifdef PACKENUM
    if (packenum_option) {
	if (minval >= -128L && maxval <= 127L)
	    tp2 = tp_schar;
	else if (minval >= 0L && maxval <= 256L)
	    tp2 = tp_uchar;
	else if (minval >= -32768L && maxval <= 32767L)
	    tp2 = tp_short;
	else if (minval >= 0L && maxval <= 65535L)
	    tp2 = tp_ushort;
	else
	    tp2 = tp_long;
    } else
#endif /* PACKENUM */
	tp2 = tp_int;
    tp->type = tp2->type;
    tp->size = tp2->size;
    set_enum(tp);
    set_enumtype(tp, tp);
    return tp;
}



/*ANSI 3.5.2.2
 *	enum-specifier:
 *		"enum" identifier[opt] { enumeration-list }
 *		"enum" identifier
 */
static TYP *
enum_specifier P1( STORAGE, sc)
{
    TYP		*tp;
    SYM	*sp;
    assert (lastst == kw_enum);
    getsym();
    switch (lastst) {
      case tk_begin:
	getsym();
	tp = enumeration_list();
	needpunc(tk_end);
	break;
      case tk_id:
	if ((sp = tag_search(lastsym)) == NIL_SYM) {
	    sp = mk_sym(lastsym, sc_tag, tp_int);
	    tag_append(&sp);
	    if (sc == sc_parms)
		message (WARN_DUBIOUS, "enum");
	    getsym();
	    if (lastst == tk_begin) {
		getsym();
		sp->tp = tp = enumeration_list();
		tp->sname = sp->name;
		needpunc(tk_end);
	    } else {
		tp = tp_int;
		message(ERR_INCOMPLETE, "enum");
		break;
	    }
	} else {
	    getsym();
	    if (is_local_scope(sp) || lastst != tk_begin) {
		check_tag(sp, tp_int->type);
		tp = sp->tp;
		if (lastst == tk_semicolon)
		    message(ERR_INCOMPLETE, "enum");
	    } else {
		getsym();
		sp->tp = tp = enumeration_list();
		tp->sname = sp->name;
		needpunc(tk_end);
	    }
	}
	break;
      default:
	tp = tp_int ;
	message(ERR_INCOMPLETE, "enum");
	break;
    }
    return tp;
}


static int	bit_width, bit_offset, bit_next;

/*ANSI 3.5.2.1
 *	struct-declarator-list:
 *		struct-declarator
 *		struct-declarator-list , struct-declarator
 *ANSI 3.5.2.1
 *	struct-declarator:
 *		declarator
 *		declarator[opt] : constant-expression
 */
static SIZE
struct_declarator_list P3( TYP*, tp, SIZE, offset,  BOOL, is_struct)
{
    SIZE	size, al;

    for (size = 0L;;) {
	SYM *	sp = NIL_SYM;
	switch (lastst) {
	  case tk_id:
	  case tk_star:
	  case tk_openpa:
	    sp = declarator(sc_member, tp);
	    if (sp->tp->size == UNKNOWN_SIZE)
		message(ERR_SIZE);
	    if (sp->tp->type == bt_func)
		message(ERR_FUNC);
	    if (lastst != tk_colon) {
		if (is_struct) {
		    al = align(sp->tp,offset + size);
		    sp->value.i = offset + size + al;
		    field_append(&sp);
		    size += sp->tp->size + al;
		} else {
		    al = align(sp->tp, offset);
		    sp->value.i = offset + al;
		    field_append(&sp);
		    size = sp->tp->size + al;
		}
		bit_next = 0;
		break;
	    }
	    tp = sp->tp;
	    /*FALLTHRU*/
	  case tk_colon:
	    getsym();
	    /*
	     *Semantic:   A bit-field shall have type int, unsigned int, or
	     *	          signed int.
	     */
	    if (tp->type != tp_int->type && tp->type != tp_uint->type)
		message(WARN_FLDTYPE);

	    bit_width = (int)intexpr();
	    bit_offset = bit_next;

	    /*
	     *Constraint: The expression that specified the width of a bit-field
	     *	          shall be an integral constant expression that has
	     *	          nonnegative value that shall not exceed the number
	     *	          of bits in an ordinary object of compatible type.
	     */
	    if (bit_width <0 || bit_width > int_bits) {
		message(ERR_WIDTH);
		bit_width=1;
	    }

	    /*ANSI 3.5.2.1
	     *Constraint: If the value is zero, the declaration shall have
	     *		  no declarator.
	     *
	     *Semantic:	  A bit-field structure member with a width of 0
	     *		  indicates that no further bit-field is to be
	     *		  packed into the unit in which the previous
	     *		  bit-field, if any, was placed.
	     */
	    if (bit_width == 0 || bit_offset + bit_width > int_bits)
		bit_offset = 0;
	    bit_next = bit_offset + bit_width;
	    if (sp != NULL) {
		if (bit_width == 0)
		    message(ERR_WIDTH);
		sp->tp = copy_type(tp);
		sp->tp->type = 
		  (BTYPE)(tp->type == tp_int->type ? bt_bitfield : bt_ubitfield);
		sp->tp->size = tp_int->size;
		set_bit_width(sp->tp, (unsigned char)bit_width);
		if (bitfield_option)
		    set_bit_offset(sp->tp, (unsigned char)(int_bits-bit_offset-bit_width));
		else
		    set_bit_offset(sp->tp, (unsigned char)bit_offset);
		if (bit_offset > 0)
		    offset -= tp_int->size;	/* shares space with previous field */
		al = align(sp->tp, offset + size);
		sp->value.i = offset + size + al;
		field_append(&sp);
		size += sp->tp->size + al;
		if (bit_offset > 0)
		    size -= tp_int->size;
	    } else {
		if (bit_offset > 0)
		    offset -= tp_int->size;	/* shares space with previous field */
		al = align(tp_int, offset + size);
		size += tp_int->size + al;
		if (bit_offset > 0)
		    size -= tp_int->size;
	    }

	    break;
	  case tk_comma:
	    needpunc((TOKEN)(lastst+1));		/* force error */
	    break;
	  default:
	    break;
	}
	if (lastst != tk_comma)
	    break;
	getsym();
    }
    return size;
}

static BOOL
is_struct_declarator P1( TOKEN, st)
{
    return (is_specifier_qualifier(st) || st == tk_colon);
}

/*ANSI 3.5.2.1
 *	struct-declaration-list:
 *		struct-declaration
 *		struct-declaration-list struct-declaration
 *
 *ANSI .5.2.1
 *	struct-declaration:
 *		specifier-qualifier-list struct-declarator-list ;
 */
static SIZE
struct_declaration_list P1(TYP *, tp)
{
    STORAGE	sc;
    TYP		*tp1;
    SIZE	size;
    SIZE	offset = 0L;
    SIZE	max_size = UNKNOWN_SIZE;
    int		count;
    BOOL	is_struct = tp->type == bt_struct;

    beginstructblock(members(tp));
    bit_next = 0; bit_offset = 0;
    for (count = 0 ; is_struct_declarator(lastst) ; count++) {
	sc = sc_member;
	tp1 = declaration_specifiers(&sc, sc_member);
	size = struct_declarator_list(tp1, offset, is_struct);
	if (offset + size > max_size)
	    max_size = offset + size;
	if (is_struct)
	    offset += size;
	needpunc(tk_semicolon);
    }
    if (count == 0)
	message(ERR_INCOMPLETE, (is_struct ? "struct" : "union"));
    set_members(tp, endstructblock());
    return max_size;
}



/*ANSI3.5.2.1
 *	struct-or_union-specifier:
 *		struct-or-union identifier[opt] { struct-declaration-list }
 *		struct-or-union identifier
 *
 *	struct-or-union:
 *		"struct"
 *		"union"
 */
static TYP *
struct_or_union_specifier P1( STORAGE, sc)
{
    TYP	       *tp;
    SYM *sp;
    SIZE	size;

    switch (lastst) {
      case kw_struct:
	tp = tp_struct;
	break;
      case kw_union:
	tp = tp_union;
	break;
      default:
	CANNOT_REACH_HERE();
	break;
    }
    getsym();

    switch (lastst) {
      case tk_id:
	if ((sp = tag_search(lastsym)) == NIL_SYM) {
	    tp = mk_type (tp, NIL_TYP);
	    sp = mk_sym(lastsym, sc_tag, tp);
	    getsym();
	    tp->sname = sp->name;
	    tag_append(&sp);
	    if (sc == sc_parms)
		message (WARN_DUBIOUS, (tp->type == bt_struct ? "struct" : "union"));
	    beginstructblock(members(tp));
	    set_members(tp, endstructblock());
	} else {
	    getsym();
	    if (is_local_scope(sp)) {
		check_tag (sp, tp->type);
	    } else {
		switch (lastst) {
		  case tk_semicolon:
		    /* ANSI 3.5.2.3
		     *	A declaration of the form
		     *		struct-or-union identifier ;
		     *	specifies a structure or union and declares a tag
		     *	both visible only within the scope in which the
		     *	declaration occurs.  It declares a new type distinct
		     *	from any type with the same tag in an enclosing scope
		     *	(if any).
		     */
		    tp = mk_type (tp, NIL_TYP);
		    sp = mk_sym(sp->name, sc_tag, tp);
		    tp->sname = sp->name;
		    tag_append(&sp);
		    beginstructblock(NIL_BLOCK);
		    set_members(tp, endstructblock());
		    return tp;
		  case tk_begin:
		    /*
		     * Defining a new structure/union which will hide the one
		     * at an outer scope.
		     */
		    tp = mk_type (tp, NIL_TYP);
		    sp = mk_sym(sp->name, sc_tag, tp);
		    tp->sname = sp->name;
		    tag_append(&sp);
		    if (sc == sc_parms)
			message (WARN_DUBIOUS, (tp->type == bt_struct ? "struct" : "union"));
		    beginstructblock(NIL_BLOCK);
		    set_members(tp, endstructblock());
		    break;
		  default:
		    break;
		}
	    }
	}
	tp = sp->tp;
	size = tp->size;
	if (lastst == tk_begin) {
	    if (is_symbol_defined(sp))
		message (ERR_REDECL, sp->name);
	    getsym();
	    size = struct_declaration_list(tp);
	    needpunc(tk_end);
	    symbol_defined(sp);
	}
	break;
      case tk_begin:
	if (sc == sc_parms)
	    message (WARN_DUBIOUS, (tp->type == bt_struct ? "struct" : "union"));
	getsym();
	tp = mk_type (tp, NIL_TYP);
	size = struct_declaration_list(tp);
	needpunc(tk_end);
	break;
      default:
	message(ERR_INCOMPLETE, (tp->type == bt_struct ? "struct": "union"));
	tp = mk_type (tp, NIL_TYP);
	return tp;
    }
    if (size != UNKNOWN_SIZE)
	size += align (tp, size);
    tp->size = size;
    return tp;
}


/*ANSI 3.5.1
 *	storage-class-specifier:
 *		"typedef"
 *		"extern"
 *		"static"
 *		"auto"
 *		"register"
 *
 * Only one storage-class is allowed on a declaration.
 */

static STORAGE
storage_class_specifier P2(STORAGE, sc,  BOOL, sc_set)
{
    /*
     *Constraint: At most one storage-class specifier may be given in the
     *		  declaration specifiers in a declaration.
     */
    if (sc_set)
	message(ERR_ILLCLASS);
    switch (lastst) {
      case kw_typedef:
	sc = sc_typedef;
	break;
      case kw_extern:
	sc = sc_external;
	break;
      case kw_static:
	sc = sc_static;
	break;
      case kw_auto:
	sc = sc_auto;
	break;
      case kw_register:
	sc = sc_register;
	break;
      default:
	CANNOT_REACH_HERE();
	break;
    }
    getsym();
    return sc;
}


/*ANSI 3.5.3
 *	type_qualifier:
 *		"const"
 *		"volatile"
 */

static QUALIFIER
type_qualifier P1( QUALIFIER, qual)
{
    QUALIFIER q;
    switch (lastst) {
      case kw_const:
	q = QUAL_CONST;
	break;
      case kw_volatile:
	q = QUAL_VOLATILE;
	break;
#ifdef TOPSPEED
      case kw_cdecl:
	q = QUAL_NONE;
	break;
#endif /* TOPSPEED */
#ifdef EXTENSION
      case kw_restrict:
	q = QUAL_RESTRICT;
	break;
#endif /* EXTENSION */
      default:
	CANNOT_REACH_HERE();
	break;
    }
    /*
     *Constraint: The same type qualifier shall not appear more than once in
     *		  the same specifier list or qualifier list.
     */
    if (qual & q)
	message (ERR_QUALIFIER);
    getsym();
    return qual|q;
}


/*ANSI 3.5
 *	declaration-specifiers:
 *		storage-class-specifier declaration-specifiers[opt]
 *		type-specifier declaration-specifiers[opt]
 *		type-qualifier declaration-specifiers[opt]
 *ANSI 3.5.2.1
 *	specifier-qualifier-list:
 *		type-specifier specifier-qualifier-list[opt]
 *		type-qualifier specifier-qualifier-list[opt]
 *
 * These definitions can be combined because the "storage" for the
 * specifier-qualifier-list is "sc_member" which ensures that no
 * storage-class-specifier can be selected for this definition.
 *
 *ANSI 3.5.2
 *	type-specifier:
 *		"void"
 *		"char"
 *		"short"
 *		"int"
 *		"long"
 *		"float"
 *		"double"
 *		"signed"
 *		"unsigned"
 *		struct-or-union-specifier
 *		enum-specifier
 *		typedef-name
 */
static TYP *
declaration_specifiers P2(STORAGE*, storage,  STORAGE, def_sc)
{
    QUALIFIER	 qualifier = QUAL_NONE;
    unsigned	 ts, type = 0;
    int		 count;
    TYP		*tp = NIL_TYP;
    SYM		*sp;
    BOOL	 done, sc_set = FALSE;

    for (count = 0, done = FALSE; !done; count++) {
	switch(lastst) {
	  /* storage class specifiers */
	  case kw_extern:
	  case kw_static:
	  case kw_auto:
	  case kw_typedef:
	    if (def_sc == sc_parms)
		message(ERR_ILLCLASS);
	    /*FALLTHRU*/
	  case kw_register:
	    if ((count != 0) && obsolete_option)
		message (WARN_STORAGE);
	    *storage = storage_class_specifier(*storage, sc_set);
	    sc_set = TRUE;
	    continue;

	  /* type specifiers */
	  case tk_id:
	    if (type && (*storage == sc_member)) {
		done = TRUE;
		continue;
	    }
	    sp = sym_search(lastsym);
	    if (sp == NIL_SYM || sp->storage_class != sc_typedef) {
		done = TRUE;
		continue;
	    }
	    if (type) {
		if (*storage == sc_parms) {
		    done = TRUE;
		    continue;
		} else if (is_local_scope(sp)) {
		    /*
		     * the typedef matches a symbol in the same scope
		     */
		    message(ERR_REDECL,sp->name);
		} else {
		    /*
		     * the typedef symbol that we found isn't in the local
		     * scope and we already have a type defined - we must
		     * be defining a symbol with the same name as the
		     * typedef.
		     */
		    done = TRUE;
		    continue;
		}
	    }
	    tp = sp->tp;
	    if (tp->qual & qualifier)
		message (ERR_QUALIFIER);
	    qualifier |= tp->qual;

	    /*
	    **	Typdef'd arrays of incomplete types must be copied as
	    **	their use must not complete the typedef type.
	    */
	    if (is_array_type(tp) && is_incomplete_type(tp))
		tp = copy_type(tp);

	    getsym();
	    type = T_ALL;
	    continue;
	  case kw_void:
	    ts = T_VOID;
	    break;
	  case kw_char:
	    ts = T_CHAR;
	    break;
	  case kw_short:
	    ts = T_SHORT;
	    break;
	  case kw_int:
	    ts = T_INT;
	    break;
	  case kw_long:
	    ts = T_LONG;
	    break;
	  case kw_float:
	    ts = T_FLOAT;
	    break;
	  case kw_double:
	    ts = T_DOUBLE;
	    break;
	  case kw_signed:
	    ts = T_SIGNED;
	    break;
	  case kw_unsigned:
	    ts = T_UNSIGNED;
	    break;
	  case kw_struct:
	  case kw_union:
	    if (type)
		message(ERR_ILLTYPE);
	    tp =  struct_or_union_specifier(def_sc);
	    type = T_ALL;
	    continue;
	  case kw_enum:
	    if (type)
		message(ERR_ILLTYPE);
	    tp = enum_specifier(def_sc);
	    type = T_ALL;
	    continue;

	  /* type qualifiers */
#ifdef EXTENSION
	  case kw_restrict:
	    message(ERR_RESTRICT);
	    /*FALLTHRU*/
#endif /* EXTENSION */
	  case kw_const:
	  case kw_volatile:
#ifdef TOPSPEED
	  case kw_cdecl:
#endif /* TOPSPEED */
	    qualifier = type_qualifier(qualifier);
	    continue;
	  default:
	    done = TRUE;
	    continue;
	}
	if (type == T_ALL)
	    break;
	if (ts & type)
	    message(ERR_TYPE);
	type |= ts;
	getsym();
    }
    switch (type) {
      case T_VOID:
	tp=  tp_void;
	break;
      case 0:
      case T_INT:
      case T_SIGNED:
      case T_INT|T_SIGNED:
	tp = tp_int;
	break;
      case T_UNSIGNED:
      case T_INT|T_UNSIGNED:
	tp = tp_uint;
	break;
      case T_CHAR:
	tp = tp_char;
	break;
      case T_CHAR|T_UNSIGNED:
	tp = tp_uchar;
	break;
      case T_CHAR|T_SIGNED:
	tp = tp_schar;
	break;
      case T_SHORT:
      case T_SHORT|T_SIGNED:
      case T_SHORT|T_INT:
      case T_SHORT|T_INT|T_SIGNED:
	tp = tp_short;
	break;
      case T_SHORT|T_UNSIGNED:
      case T_SHORT|T_INT|T_UNSIGNED:
	tp = tp_ushort;
	break;
      case T_LONG:
      case T_LONG|T_INT:
      case T_LONG|T_SIGNED:
      case T_LONG|T_INT|T_SIGNED:
	tp = tp_long;
	break;
      case T_LONG|T_UNSIGNED:
      case T_LONG|T_INT|T_UNSIGNED:
	tp = tp_ulong;
	break;
      case T_FLOAT:
	tp = tp_float;
	break;
      case T_DOUBLE:
	tp = tp_double;
	break;
      case T_DOUBLE|T_LONG:
	tp = tp_longdouble;
	break;
      case T_ALL:
	break;
      default:
	message (ERR_ILLTYPE);
	tp = tp_int;
	break;
    }
    return qualify_type (tp, qualifier);
}


/*ANSI 3.5.4
 *	direct-declarator:
 *		identifier
 *		( declarator )
 *		direct-declarator [ constant-expression[opt] ]
 *		direct-declarator ( parameter-type-list )
 *		direct-declarator ( identifier-list[opt] )
 *
 *	direct-abstract-declarator:
 *		( abstract-declarator )
 *		direct-abstract-declarator[opt] [ constant-expression[opt] ]
 *		direct-abstract-declarator[opt] ( parameter-type-list[opt] )
 */
static TYP *
direct_declarator_tail P2(SYM *, sp, TYP*, tp)
{
    SIZE	size, ind;
    switch(lastst) {
      case tk_openbr:
	getsym();
	if (lastst != tk_closebr) {
	    ind = intexpr();
	    /*
	     *Constraint: The expression delimited by [ and ] (which
	     *	      specifies the size of an array) shall be an
	     *	      integral constant expression that has a value
	     *	      greater than zero.
	     */
	    if (ind <= 0L)
		message(ERR_SIZE);
	} else {
	    ind = UNKNOWN_SIZE;
	}
	needpunc(tk_closebr);
	tp = direct_declarator_tail (sp, tp);
	size = tp ? tp->size : (SIZE)1;
	tp = mk_type (tp_array, tp);
	set_array_index(tp,ind);
	set_derived(tp);
	if (size == UNKNOWN_SIZE || ind == UNKNOWN_SIZE) {
	    tp = copy_type(tp);
	    tp->size = UNKNOWN_SIZE;
	} else {
	    tp->size = size * ind;
	}
	return tp;

      case tk_openpa:
	getsym();
	tp = mk_type (tp_func, tp);
	set_derived(tp);

	switch (lastst) {
	  case tk_id:
	    if (!is_typedef(lastsym)) {
#ifdef FACIST
		message(WARN_OLDDEF);
#endif /* FACIST */
		beginparamblock();
		identifier_list();
		set_parameters(tp, endparamblock());
		if (obsolete_option)
		    message(WARN_PROTOTYPE, sp->name);
		break;
	    }
	    /*FALLTHRU*/
	  default:
	    if (!trad_option) {
		beginparamblock();
		parameter_type_list();
		set_parameters(tp, endparamblock());
		set_ansi(tp);
	    }
	    break;
	  case tk_closepa:
#ifdef FACIST
	    message(WARN_OLDDEF);
#endif /* FACIST */
	    set_parameters(tp, &init_block);
	    if (obsolete_option)
		message(WARN_PROTOTYPE, sp->name);
	    break;
	}
	needpunc(tk_closepa);
	return direct_declarator_tail (sp, tp);
      default:
	return tp;
    }
}

static SYM*
direct_declarator P2( STORAGE, sc, TYP*, tp)
{
    SYM	*sp;

    switch (lastst) {
      case tk_id:
	if ((sc != sc_member) && (sc != sc_parms) && is_local_typedef(lastsym)){
	    sp = mk_sym ((CHAR *)"", sc, tp);
	    break;
	}
	sp = mk_sym(lastsym, sc, tp);
	getsym();
	break;
      case tk_openpa:
	getsym();
	if (lastst == tk_closepa) {
	    /*
	     * empty parentheses in a type-name are interpreted as "function
	     * with no parameter specification", rather than redundant
	     * parentheses around the omitted identifier.
	     */
	    getsym();
	    tp = mk_type(tp_func, tp);
	    sp = mk_sym ((CHAR *)"", sc, tp);
	} else {
	    sp = declarator(sc, (TYP *)NULL);
	    needpunc(tk_closepa);
	    tp =  direct_declarator_tail (sp, tp) ;
	    if (sp->tp) {
		TYP *tp1;
		for (tp1 = sp->tp; tp1->btp; tp1 = tp1->btp)
			;
		tp1->btp = tp;
	    } else
		sp->tp = tp;
	    size_type(sp->tp);
	}
	return sp;
      default:
	/* abstract */
	sp = mk_sym ((CHAR *)"", sc, tp);
	break;
    }
    sp->tp =  direct_declarator_tail (sp, sp->tp) ;
    return sp;
}


/*ANSI 3.5.4
 *	type-qualifier-list:
 *		type-qualifier
 *		type_qualifier-list type-qualifier
 */
static QUALIFIER
type_qualifier_list P0(void)
{
    QUALIFIER	qual = QUAL_NONE;
    while (is_type_qualifier(lastst)) {
	qual = type_qualifier(qual);
    }
    return qual;
}


/*ANSI 3.5.4
 *	declarator:
 *		pointer[opt] direct-declarator
 *
 *	abstract-declarator:
 *		pointer
 *		pointer[opt] direct-abstract-declarator
 *
 *	pointer:
 *		* type-qualifier-list[opt]
 *		* type-qualifier-list[opt] pointer
 */
static SYM*
declarator P2( STORAGE, sc, TYP*, tp)
{
    SYM		*sp;
    switch (lastst) {
      case tk_star:
	getsym();
	tp = mk_type (tp_pointer, tp);
	if (tp->btp == NULL) {
	    /*
	    **	The type of pointer isn't known .... it will only be
	    **	known later.    We must ensure that the same pointer is
	    **	therefore not used more than once.
	    */
	    tp = copy_type(tp);
	}
	tp = qualify_type(tp, type_qualifier_list());
	sp = declarator (sc, tp);
	break;
      case tk_id:
	if ((sc != sc_member) && (sc != sc_parms) && is_local_typedef(lastsym)) {
	    sp = mk_sym ((CHAR *)"", sc, tp);
	    break;
	}
	/*FALLTHRU*/
      case tk_openpa:
      case tk_openbr:
	sp = direct_declarator(sc, tp);
	break;
      default:
	sp = mk_sym ((CHAR *)"", sc, tp);
    }
    return sp;
}

static BLOCK *
get_parameters P1(SYM *, sp)
{
    BLOCK	*block, *block2;
    SYM		*sp1, *sp2;

    block = parameters(sp->tp);
    if (is_ansi(sp->tp)) {
	if (lastst == tk_begin) {
	    /*
	     * Parameters to function definitions must have a name
	     */
	    for (sp2 = block->symbols.head; sp2 != NULL; sp2 = sp2->next) {
		if (sp2->tp->type == bt_void ||
		    sp2->tp->type == bt_ellipsis)
		    break;
		if (sp2->name == NULL || sp2->name[0] == 0) {
		    message(ERR_PARMS);
		    break;
		}
		symbol_set(sp2);
	    }
	}
	return block;
    }

    beginparamblock();
    VOIDCAST declaration_list(sc_parms, 0L);
    block2= endparamblock();
    set_parameters(sp->tp, &init_block);

    /*
     * an id list on a function declaration is illegal
     */
    if ((lastst != tk_begin) && (block->symbols.head != NULL))
	message(ERR_IDLIST);

    /*
     * now match up the identifier list with the declarations
     */
    for (sp1 = block->symbols.head; sp1 != NULL; sp1 = sp1->next) {
	sp2 = search(sp1->name, &block2->symbols);
	if (sp2 == NULL) {
	    message (WARN_IMPLICIT, sp1->name);
	    sp1->tp = tp_int;
	} else {
	    sp1->storage_class = sp2->storage_class;
	    sp1->tp = sp2->tp;
	    sp1->value.i = sp2->value.i;
	    symbol_set(sp1);
	}
    }
    /*
     * now check for declared items which aren't in the
     * identifier list for the function.
     */
    for (sp2 = block2->symbols.head; sp2 != NULL; sp2 = sp2->next) {
	if (sp2->storage_class == sc_const)
	    continue;
	if (search(sp2->name, &block->symbols) == NULL)
	    message (ERR_ARG, sp2->name);
	symbol_set(sp2);
    }
    if ((sp2 = sym_search(sp->name)) != NIL_SYM && sp2->tp->type == bt_func) {
	 MSGNUM ret = check_proto(block->symbols.head,
			      parameters(sp2->tp)->symbols.head, TRUE);
	if (ret != (MSGNUM)0)
	    message(ret, sp->name);
    }
    return block;
}

/*ANSI 3.7
 *	external-declaration:
 *		function-declaration
 *		declaration
 *
 *ANSI 3.5
 *	declaration:
 *		declaration-specifiers init-declaration-list[opt] ;
 *
 *	init-declaration-list:
 *		init-declarator
 *		init-declarator-list , init-declarator
 *
 *	init-declarator:
 *		declarator
 *		declarator = initializer
 *
 *	function-definition:
 *		declaration-specifiers[opt] declarator declaration-list[opt]
 *							compound-statement
 */
static SIZE
declaration P3(STORAGE, sc,  STORAGE, def_sc, SIZE, offset)
{
    TYP		*tp;
    SYM		*sp;
    BLOCK	*block;

    tp = declaration_specifiers(&sc, def_sc);
    switch (def_sc) {
      case  sc_global:
	if (sc == sc_register || sc == sc_auto)
	    message(ERR_ILLCLASS);
	break;
      case sc_parms:
	if (sc != sc_register && sc != sc_parms)
	    message(ERR_ILLCLASS);
	break;
      default:
	break;
    }
    for (sp=NIL_SYM;;) {
	switch (lastst) {
	  case tk_id:
	    if (is_local_typedef(lastsym))
		break;
	    /*FALLTHRU*/
	  case tk_star:
	  case tk_openpa:
	    if (sc != sc_global && tp == NIL_TYP) {
		/*ANSI 3.5
		 * If an identifier for an object is declared with no linkage,
		 * the type for the object shall be complete by the end of its
		 * declarator, or by the end of its identifiers if it has an
		 * initializer.
		 *ANSI 3.1.2.2
		 * The following identifiers have no linkage: an identifier
		 * declared to be anything other than an object or a function;
		 * an identifier declared to be a function parameter; a block
		 * scope identifier for an object declared without the
		 * storage-class specifier extern.
		 */
		return offset;
	    }
	    sp = declarator(sc, tp);
	    switch (sp->tp->type) {
	      case bt_func:
		check_function_definition(sp);
		block = get_parameters(sp);
		if (def_sc == sc_parms) {
		    /*
		     * Functions which are passed as a parameter are really
		     * pointers to a function.
		     */
		    sp->tp = mk_type (tp_pointer, sp->tp);
		}
		break;
	      default:
		break;
	    }
	    sp = declare(sp, def_sc);
	    switch (sp->tp->type) {
	      case bt_func:
		if (lastst == tk_begin) {
		    /* function declaration */
		    check_function_declaration(sp);
		    if (def_sc != sc_global) {
			message(ERR_NESTED, sp->name);
			break;
		    }
		    if (is_symbol_defined(sp))
			message (ERR_REDECL, sp->name);
		    symbol_defined(sp);
#ifdef EXTERNAL
		    funclist(sp, block->symbols.head);
#endif /* EXTERNAL */
		    funcbody(sp, block);
		    return offset;
		}
		/*FALLTHRU*/
	      default:
		offset = doinit(sp, offset);
		if (def_sc == sc_auto) {
		    switch (sp->storage_class) {
		      default:
			check_complete(sp->tp);
#ifdef CPU_DEFINED
			addoptinfo (sp, def_sc);
#endif /* CPU_DEFINED */
			/*FALLTHRU*/
		      case sc_external:
		      case sc_typedef:
			break;
		    }
		}
	    }
	    break;
	  case tk_eof:
	    return offset;
	  case tk_comma:
	    needpunc((TOKEN)(lastst+1));	/* force error */
	    break;
	  default:
	    break;
	}
	if (lastst != tk_comma)
	    break;
	getsym();
    }
    if (lastst == tk_semicolon)
	needpunc(tk_semicolon);
    else {
	needpunc(tk_semicolon);
	getsym();
    }
    return offset;
}


/*
 * main compiler routine. this routine parses all of the declarations using
 * declare which will call funcbody as functions are encountered.
 *
 *ANSI 3.7
 *	translation-unit:
 *		external-declaration
 *		translation-unit external-declaration
 */
void
translation_unit P0(void)
{
    SIZE		offset = 0L;
#ifdef VERBOSE
     clock_t	ltime = clock();
#endif

    global_flag = 1;
    strtab = 0;
    beginblock();
    nextlabel = (LABEL)1;
#ifdef CPU_DEFINED
    if (code_option)
	put_start();
#endif /* CPU_DEFINED */
    while (lastst != tk_eof) {
	if (lastst == tk_semicolon)
	    message(WARN_EMPTY);
	offset += declaration(sc_global, sc_global, offset);
    }
    endblock();
#ifdef CPU_DEFINED
    if (code_option) {
	put_literals();
	put_finish();
    }
#endif /* CPU_DEFINED */

#ifdef VERBOSE
    decl_time += clock() - ltime;
#endif
}

