/*
 * 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
 */

/*
 * expression evaluation
 *
 * this set of routines builds a parse tree for an expression. no code is
 * generated for the expressions during the build, this is the job of the
 * codegen module. for most purposes expression() is the routine to call. it
 * will allow all of the C operators. for the case where the comma operator
 * is not valid (function parameters for instance) call exprnc().
 *
 * each of the routines returns a pointer to a describing type structure. each
 * routine also takes one parameter which is a pointer to an expression node
 * by reference (address of pointer). the completed expression is returned in
 * this pointer. all routines return either a pointer to a valid type or NULL
 * if the hierarchy of the next operator is too low or the next symbol is not
 * part of an expression.
 */

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

#include <ctype.h>

typedef struct _stree STREE;
struct _stree {
	LABEL		label;
	const CHAR *	sptr;
	size_t		len;
	STREE *		less;
	STREE *		more;
};

static BOOL	sizeof_flag = FALSE;
static BOOL	address_flag = FALSE;
#ifdef SEQUENCE
static SEQNUM	sequence_number = (SEQNUM)1;
#else
#define	check_sequence(ep,mnum)
#define	check_sequence_modified(ep)
#define	check_sequence_accessed(ep)
#endif

enum fpos { format_start, format_precision, format_optional };


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

/* expr.c */
static BOOL 	is_null_pointer		P_((const EXPR *));
static void 	check_object		P_((const EXPR *));
static void 	check_cast		P_((IVAL, IVAL, const TYP *));
static void 	check_scalar		P_((const EXPR *));
static void 	check_integral		P_((const EXPR *));
static void 	check_arithmetic	P_((const EXPR *));
static void 	check_modifiable_lvalue	P_((EXPR *));
static EXPR *	condition		P_((EXPR *));
static EXPR *	integral_promotion	P_((EXPR *));
static EXPR *	mk_enode		P_((EXPRTYPE, TYP *));
#ifdef FLOAT_SUPPORT
#ifndef TMS320C30
static EXPR *	mk_fcon			P_((RVAL *, TYP *));
#endif /* TMS320C30 */
#endif /* FLOAT_SUPPORT */
static EXPR *	deref			P_((EXPR *, TYP *));
static EXPR *	cond_deref		P_((EXPR *, TYP *));
static EXPR *	nameref			P_((void));
#ifdef FORMAT_CHECK
static void 	check_parameter		P_((const CHAR *, int, const TYP *, const TYP *));
static void 	check_pointer_parameter	P_((const CHAR *, int, const TYP *, const TYP *));
static void 	check_array_parameter	P_((const CHAR *, int, const TYP *));
static const CHAR *	check_printf		P_((const CHAR *, int, const CHAR *, enum fpos *, const TYP *));
static const CHAR *	check_scanf		P_((const CHAR *, int, const CHAR *, enum fpos *, const TYP *));
#endif /* FORMAT_CHECK */
static EXPR *	addops			P_((void));
static EXPR *	andop			P_((void));
static EXPR *	asnop			P_((void));
static EXPR *	binlog			P_((EXPR *(*)(void), EXPRTYPE, TOKEN));
static EXPR *	binop			P_((EXPR *(*)(void), EXPRTYPE, TOKEN));
static EXPR *	bitandop		P_((void));
static EXPR *	bitorop			P_((void));
static EXPR *	bitxor			P_((void));
static EXPR *	commaop			P_((void));
static EXPR *	conditional		P_((void));
static EXPR *	equalops		P_((void));
static EXPR *	multops			P_((void));
static EXPR *	orop			P_((void));
static EXPR *	parmlist		P_((EXPR *, const BLOCK *));
static EXPR *	primary			P_((void));
static EXPR *	relation		P_((void));
static EXPR *	shiftop			P_((void));
static EXPR *	unary			P_((void));
static TYP * 	arithmetic_conversion	P_((EXPR **, EXPR **));
static TYP * 	arithmetic_conversion2	P_((EXPR **, EXPR **));
#ifdef FORMAT_CHECK
static const CHAR *findstr P_((const STREE *,  LABEL));
#endif /* FORMAT_CHECK */
static EXPR *	explicit_castop		P_((EXPR *, TYP *));

#define	is_ellipsis(tp)	((tp)->type == bt_ellipsis)
#define	is_float(tp)	((tp)->type == bt_float)

static STREE *strtree = NULL;	/* Tree for string constants */

/*
 * returns true if the expression is a constant integral expression node,
 * otherwise returns false.
 */
static BOOL
is_constexpr P1(const EXPR *, ep)
{
    return (ep->nodetype == en_icon);
}

/*
 * returns true if the expression is a constant which can be represented
 * within the range of the type, otherwise it returns false.
 */
static BOOL
is_in_range P2( EXPR *, ep, TYP *, tp)
{
    if (is_constexpr(ep)) {
	 IVAL	val = ep->v.i;
	switch (tp->type) {
	  case bt_char:
	  case bt_schar:
	    return (-128L <= val && val <= 127L);
	  case bt_uchar:
	  case bt_charu:
	    return (0L <= val && val <= 255L);
	  case bt_short:
	  case bt_int16:
	    return (-32768L <= val && val <= 32767L);
	  case bt_ushort:
	  case bt_uint16:
	    return (0L <= val && val <= 65535L);
	  case bt_long:
	  case bt_int32:
	  case bt_ulong:
	  case bt_uint32:
	    return FALSE;
	  case bt_pointer16:
	  case bt_pointer32:
	    return (val == 0L);
	  default:
	    break;
	}
    }
    return FALSE;
}

/*
 * returns true if the expression is a null pointer constant, otherwise
 * returns false.
 */
static BOOL
is_null_pointer P1(const EXPR *, ep)
{
    return (is_constexpr(ep) && ep->v.i == 0L);
}
static BOOL
is_negative P1(const  EXPR *, ep)
{
    return (is_constexpr(ep) && ep->v.i < 0L);
}

static BOOL
is_negative_or_zero P1(const EXPR *, ep)
{
    return (is_constexpr(ep) && ep->v.i <= 0L);
}

/*
 * returns true if ep is an expression suitable as an lvalue
 */
BOOL
is_lvalue P1(const EXPR *, ep)
{
    switch (ep->nodetype) {
      case en_ref:
	/*
	 * a function returning a structure (which cannot be used as an
	 * lvalue) will create an en_deref node
	 */
	return (ep->v.p[0]->nodetype != en_add ||
		ep->v.p[0]->v.p[0]->nodetype != en_deref);
      case en_fieldref:
	return (ep->v.p[0]->nodetype != en_add ||
		ep->v.p[0]->v.p[0]->nodetype != en_deref);
      default:
	break;
    }
    return FALSE;
}

#ifdef SEQUENCE
static void
check_sequence P2(const EXPR *, ep,  MSGNUM, mnum)
{
    SYM	*sp ;
    switch (ep->nodetype) {
      case en_ref:
	check_sequence(ep->v.p[0], mnum);
	break;
      case en_sym:
	sp = ep->v.sp;
	if (sp->sequence == sequence_number) {
	    /* already modified at this sequence point */
	    message (mnum, sp->name);
	}
	if (mnum == WARN_MODIFIED)
	    sp->sequence = sequence_number;
	break;
      default:
	break;
    }
}

static void
check_sequence_modified P1(const EXPR *, ep)
{
    check_sequence(ep, WARN_MODIFIED);
}

static void
check_sequence_accessed P1(const EXPR *, ep)
{
    check_sequence(ep, WARN_ACCESS);
}

/*
 * a sequence-point has been encountered
 */
void
sequence_point P0(void)
{
    sequence_number++;
}
#endif

static void
check_object P1(const EXPR *, ep)
{
    if (!is_object_type(referenced_type(ep->etp)))
	message(ERR_OBJECT);
}

static void
check_scalar P1(const EXPR *, ep)
{
    if (!is_scalar_type(ep->etp))
	message(ERR_SCALAR);
}

static void
check_integral P1(const EXPR *, ep)
{
    if (!is_integral_type(ep->etp))
	message(ERR_INTEGER);
}

static void
check_arithmetic P1(const EXPR *, ep)
{
    if (!is_arithmetic_type(ep->etp))
	message(ERR_ARITHMETIC);
}

static void
check_zero P1(const EXPR * , ep)
{
    if (!is_constexpr(ep))
	return;
    if (is_integral_type(ep->etp) && (ep->v.i == 0))
	message(WARN_ZERO);
#ifdef FLOAT_SUPPORT
#ifndef FLOAT_BOOTSTRAP
    else if (is_floating_type(ep->etp) && FEQ(((EXPR *)ep)->v.f, F_zero))
	message(WARN_ZERO);
#endif /* FLOAT_BOOTSTRAP */
#endif /* FLOAT_SUPPORT */
}

static void
check_shift P2(const EXPR *, ep, const TYP *, tp)
{
    if (!is_constexpr(ep) || !is_integral_type(ep->etp))
	return;
    if (ep->v.i < 0L || ep->v.i >= (tp->size*8L))
	message(WARN_SHIFT, ep->v.i);
}

void
check_discard P1(const EXPR *, ep)
{
    if (ep == NIL_EXPR)
	return;
    switch (ep->nodetype) {
      case en_fcall:
      case en_call:
	if (ep->etp->type != bt_void)
	    message (WARN_IGNORE,
		ep->v.p[0]->nodetype == en_nacon ? (char *)ep->v.p[0]->v.str:"");
	break;
      case en_assign:
      case en_asadd:
      case en_assub:
      case en_asmul:
      case en_asmul2:
      case en_asdiv:
      case en_asdiv2:
      case en_asmod:
      case en_asrsh:
      case en_asxor:
      case en_aslsh:
      case en_asand:
      case en_asor:
      case en_ainc:
      case en_adec:
	break;
      case en_comma:
	check_discard(ep->v.p[1]);
	break;
      default:
	if (ep->etp->type != bt_void)
	    message (WARN_DISCARD);
	break;
    }
}

static void
check_unsigned P3(const EXPR *, ep1, const EXPR *, ep2, BOOL, check_for_zero)
{
    if (is_signed_type(ep2->etp)) {
	if (is_unsigned_type(ep1->etp)) {
	    if (check_for_zero ? is_negative_or_zero(ep2) : is_negative(ep2))
		message(WARN_UNSIGNED);
	} else if (ep1->etp->type == bt_char && ep2->etp->type != bt_char) {
	    if (check_for_zero ? is_negative_or_zero(ep2) : is_negative(ep2))
		message(WARN_CHAR);
	}
    }
}

static void
check_relational P2(const EXPR *, ep1, const EXPR *, ep2)
{
    check_unsigned (ep1, ep2, TRUE);
    check_unsigned (ep2, ep1, TRUE);
}

static void
check_equality P2(const EXPR *, ep1, const EXPR *, ep2)
{
    check_unsigned (ep1, ep2, FALSE);
    check_unsigned (ep2, ep1, FALSE);
}

static void
check_negative P2(const EXPR *, ep1, const EXPR *, ep2)
{
    if (is_unsigned_type(ep1->etp) &&
        is_signed_type(ep2->etp) && is_negative(ep2))
	message(WARN_NEGATIVE);
}

/*
**	A modifiable lvalue is an lvalue that does not have array
**	type, does not have an incomplete type, does not have a
**	const-qualified type, and if it is a structure or union,
**	does not have any member (including, recursively, any member
**	of all contained structures or unions) with const-qualified
**	type.
*/

static void
check_modifiable_lvalue P1(EXPR *, ep)
{
    if (!is_lvalue(ep))
	message(ERR_LVALUE);
    if (is_const_qualified(ep->etp))
	message(ERR_CONST);
}

/*
**	Check to see if a cast of a constant value has altered the
**	value.
*/

static void
check_cast P3(IVAL, i, IVAL, j, const TYP *, tp)
{
    if (i == j)
	return;
    switch (tp->type) {
      case bt_charu:
      case bt_uchar:
      case bt_char:
      case bt_schar:
	if ((i & (IVAL) ~0xffL) == (IVAL) ~0xffL)
	    return;
	break;
      case bt_ushort:
      case bt_uint16:
      case bt_short:
      case bt_int16:
	if ((i & (IVAL) ~0xffffL) == (IVAL) ~0xffffL)
	    return;
	break;
      default:
	break;
    }
    message (WARN_CONSTCAST, i, j);
}


/*
**	Check that the sizeof hasn't been applied to a bitfield, void
**	or a function.
*/

static void
check_sizeof P2(const EXPR *, ep, SIZE, size)
{
    if (size == UNKNOWN_SIZE ||
	(ep != NIL_EXPR &&
	 ((ep->nodetype == en_fieldref) ||
	  (!trad_option && (ep->nodetype==en_sym) && is_function_type(ep->etp)))))
	message (ERR_ILLSIZEOF);	

    if (size == 0L) {
	message(WARN_SIZEOF0);
    }
    /* a ">>" is undefined if the RHS equals number of bits of LHS! */
    if ((size>>(tp_size->size*4L))>>(tp_size->size*4L) != 0L) {
	message(WARN_SIZEOFBIG, size);
    }
}


/*
**	If the expression is a symbol then mark the symbol as set
*/

static void
set_used P1(EXPR *, ep)
{
    switch (ep->nodetype) {
      case en_ref:
	set_used(ep->v.p[0]);
	break;
      case en_sym:
	symbol_set(ep->v.sp);
	break;
      default:
	break;
    }
}


/*
**	Check to see if the symbol has been set before it is about
**	to be used.
*/

static void
check_set P1(const EXPR *, ep)
{
    SYM	*sp ;
    switch (ep->nodetype) {
      case en_ref:
	check_set(ep->v.p[0]);
	break;
      case en_sym:
	sp = ep->v.sp;
	switch (sp->storage_class) {
	  case sc_auto:
	  case sc_register:
	    if (!is_symbol_set(sp)) {
		message(WARN_NOTSET, sp->name);
		symbol_set(sp);
	    }
	    break;
	  default:
	    break;
	}
	break;
      default:
	break;
    }
}


/*
**	This function handles the adjustment of integer constants upon
**	casts. It forces the constant into the range acceptable for
**	the given type.
*/

IVAL
strip_icon P2(IVAL, i, const TYP *, tp)
{
    switch (tp->type) {
      case bt_charu:
      case bt_uchar:		/* 0 .. 255 */
	i &= (IVAL) 0xffL;
	break;
      case bt_char:		/* -128 .. 127 */
      case bt_schar:
	i &= (IVAL) 0xffL;
	if (i >= (IVAL) 128L)
	    i -= (IVAL) 256L;
	break;
      case bt_ushort:		/* 0 .. 65535 */
      case bt_uint16:
	i &= (IVAL) 0xffffL;
	break;
      case bt_short:		/* -32768 .. 32767 */
      case bt_int16:
	i &= (IVAL) 0xffffL;
	if (i >= (IVAL) 32768L)
	    i -= (IVAL) 65536L;
	break;
      default:
	break;
    }
    return i;
}

#ifdef FORMAT_CHECK
/*
 * Search the tree looking for the string associated with the supplied label
 */
static const CHAR *
findstr P2(const STREE *, tree,  LABEL, lab)
{
    const CHAR *str;
    if (tree == (STREE *)0)
	return (CHAR *) 0;
    if (tree->label == lab)
	return tree->sptr;
    str = findstr(tree->less, lab);
    return str ? str : findstr(tree->more, lab);
}

static const CHAR *
get_stringlit P1(LABEL, lab)
{
    return findstr(strtree, lab);
}
#endif /* FORMAT_CHECK */

/*
 * mk_ s a string literal and return it's label number.
 *
 *	Strict K&R requires that all strings even when
 *	written identically are distinct.   This
 *	behaviour is enforced if the 'trad_option' flag
 *	is set.
 */
static LABEL
stringlit P2(const CHAR*, s, size_t, len)
{
    struct slit	*lp;
    int	local_global = global_flag;
    STREE	*p, *q;
    int  	result = 0;

    if (!trad_option) {
	/*
	 * Not in tradional mode, so shared strings allowed.
	 * Search our tree of existing strings to see if
	 * an identical one has already been generated.
	 * (this is allowed by ANSI).
	 * If so we can merely return its label.
	 */
	for (q = p = strtree; p ;  p = result<0 ? p->less : p->more) {
	    result = (int)(len - p->len);
	    if (result == 0) {
		result=memcmp((void *)s,(void *)p->sptr,(size_t)len);
		if (result == 0)
		    return p->label;
	    }
	    q = p;
	}
     }
    /*
     *  If we reach this point, either the string does not exist
     *  or we are operating in 'traditional' mode.   We must therefore
     *  create a new string.   The actual string text is allocated
     *  in local space if in traditional mode, and global otherwise
     *  (so we can check for strings already generated).
     */
    global_flag = 0;		/* always allocate from local space. */
    lp = (struct slit *) xalloc((size_t) sizeof(struct slit));
    lp->label = nextlabel++;
    global_flag = !trad_option;
    lp->str = s;
    lp->len = len;
    lp->next = strtab;
    strtab = lp;
    if (!trad_option) {
	/*
	 *  traditional mode not set, so create a new entry for
	 *  our tree of already generated strings.
	 */
	p = (STREE *) xalloc ((size_t)sizeof(STREE));
	p->label = lp->label;
	p->sptr = lp->str;
	p->len = lp->len;
	p->less = p->more = (STREE *) NULL;
	if (q == NULL)
	    strtree = p;
	else if (result < 0)
	    q->less = p;
	else
	    q->more = p;
    }
    global_flag = local_global;
    return lp->label;
}

  
/*
 * generates the cast node if required for an integral promotion
 */
static EXPR *
integral_promotion P1(EXPR *, ep)
{
    TYP * tp = unary_conversion(ep->etp);
    if (tp != ep->etp) {
	return implicit_castop(ep, tp);
    }
    return ep;
}

static EXPR *
mk_enode P2(EXPRTYPE, nt, TYP *, tp)
{
    EXPR *  ep = (EXPR *) xalloc((size_t) sizeof(EXPR));
    ep->nodetype = nt;
    ep->etp = tp;
    return ep;
}

/*
 * build an expression node with a node type of nt and values v1 and v2.
 */
EXPR   *
mk_node P4(EXPRTYPE, nt, EXPR *, v1, EXPR *, v2, TYP *, tp)
{
    EXPR *  ep = mk_enode(nt, tp);
    ep->v.p[0] = v1;
    ep->v.p[1] = v2;
    return ep;
}

/*
 * build an expression node forming an integer constant
 */
EXPR   *
mk_icon P2(IVAL, i, TYP *, tp)
{
    EXPR *  ep = mk_enode (en_icon, tp);
    ep->v.i = i;
    return ep;
}

#ifdef FLOAT_SUPPORT
/*
 * build an expression node forming a floating point constant
 */
#ifndef TMS320C30
static
#endif /* TMS320C30 */
EXPR *
mk_fcon P2(RVAL *, fp, TYP *, tp)
{
    EXPR * ep = mk_enode (en_fcon, tp);
    FASSIGN(ep->v.f, *fp);
    return ep;
}
#endif

/*
 * build an expression node forming a label
 */
EXPR *
mk_lcon P1(LABEL, lab)
{
    EXPR * ep = mk_enode (en_labcon, tp_pointer);
    ep->v.l = lab;
    return ep;
}

EXPR *
mk_ref P2(EXPR *, ep, TYP *, tp)
{
    return mk_node(en_ref, ep, NIL_EXPR, tp);
}

EXPR *
mk_symnode P1(SYM *, sp)
{
    EXPR *  ep = mk_enode (en_sym, sp->tp);
    ep->v.sp = sp;
    return ep;
}

EXPR *
mk_autocon P1( SIZE, offset)
{
    EXPR *  ep = mk_node(en_autocon, NIL_EXPR, NIL_EXPR, tp_pointer);
    ep->v.i = offset;
    return ep;
}

/*
 * build the proper dereference operation for a node using the type pointer
 * tp.
 */
static EXPR *
deref P2(EXPR*, ep,  TYP *, tp)
{
    switch (tp->type) {
      case bt_func:	/* ANSI - functions automatically dereferenced */
	if (trad_option)
	    message(ERR_DEREF);
	/* FALLTHRU */
      case bt_char:
      case bt_charu:
      case bt_uchar:
      case bt_schar:
      case bt_short:
      case bt_ushort:
      case bt_int16:
      case bt_uint16:
      case bt_pointer16:
      case bt_int32:
      case bt_uint32:
      case bt_long:
      case bt_pointer32:
      case bt_ulong:
      case bt_struct:
      case bt_union:
      case bt_float:
      case bt_double:
      case bt_longdouble:
	ep = mk_ref(ep, tp);
	break;
      case bt_ubitfield:
	ep = mk_node(en_fieldref, ep, NIL_EXPR, tp_uint);
	ep->v.bit.width = bitfield_width(tp);
	ep->v.bit.offset = bitfield_offset(tp);
	break;
      case bt_bitfield:
	ep = mk_node(en_fieldref, ep, NIL_EXPR, tp_int);
	ep->v.bit.width = bitfield_width(tp);
	ep->v.bit.offset = bitfield_offset(tp);
	break;
      case bt_void:
	if (is_qualified_type(tp)) {
	    ep = mk_ref(ep, tp);
	    break;
	}
	/*FALLTHRU*/
      default:
	message(ERR_DEREF);
	break;
    }
    return ep;
}

/*
 * dereference the node if state is not STATE_DERIVED
 * If state is STATE_DERIVED and tp is a bt_pointer (array reference)
 *     set the size field to the pointer size
 */
static EXPR *
cond_deref P2(EXPR*, ep, TYP*, tp)
{
    if (is_derived_type(tp)) {
	/*
	 * Here, the information on array sizes is lost.
	 */
	if (is_pointer_type(tp)) {
	    if (sizeof_flag) {
		ep->nodetype = en_size;
		ep->v.i = tp->size;
	    }
	    tp = mk_type(tp_array, referenced_type(tp));
	    tp->size = tp_pointer->size;
	    set_derived(tp);
	} else if (is_function_type(tp)) {
	    tp = mk_type(tp_pointer, tp);
	    set_derived(tp);
	}
	ep->etp = tp;
    } else {
	ep = deref(ep, tp);
    }
    return ep;
}


/*
 *	nameref() will build an expression tree that references an identifier.
**
**	If the identifier is not in the global or local symbol table then a
**	look-ahead to the next character is done and if it indicates a function
**	call the identifier is coerced to an external function name.
**
**	Non-value references generate an additional level of indirection.
*/
static EXPR *
nameref P0(void)
{
    EXPR	*ep;
    SYM		*sp;
    TYP		*tp;

    sp = identifier();
    tp = sp->tp;
    symbol_used(sp);
    switch (sp->storage_class) {
      case sc_register:
	if (address_flag) {
	    if (is_array_type(tp) || is_structure(tp))
		message(ERR_ADDREGVAR, sp->name);
	} else if (!sizeof_flag && is_array_type(tp))
	    message(ERR_IMPLICITADDR, sp->name);
	/* FALLTHRU */
      case sc_static:
      case sc_global:
      case sc_external:
      case sc_auto:
      case sc_parms:
	if (address_flag || is_array_type(tp) || is_structure(tp))
	    symbol_set(sp);
	ep = mk_symnode(sp);
	break;
      case sc_const:
	ep = mk_icon(sp->value.i, tp);
	break;
      default:
	message(ERR_ILLCLASS);
	ep = mk_autocon(sp->value.i);
	break;
    }
    address_flag = FALSE;
    return cond_deref(ep, tp);
}


#ifdef FORMAT_CHECK
static void
check_parameter P4(const CHAR *, fname, int, num, const TYP *, tp1, const TYP *, tp2)
{
    if (tp1 == NIL_TYP)
	message (ERR_COUNTPARAM, fname);
    else if (tp1->type != tp2->type)
	message (WARN_FORMAT, num, fname);
}

static void
check_pointer_parameter P4(const CHAR *, fname, int, num, const TYP *, tp1, const TYP *, tp2)
{
    if (tp1 == NIL_TYP)
	message (ERR_COUNTPARAM, fname);
    else if (!is_pointer_type(tp1) || (referenced_type(tp1)->type != tp2->type))
	message (WARN_FORMAT, num, fname);
}

static void
check_array_parameter P3(const CHAR *, fname, int, num, const TYP *, tp1)
{
    if (tp1 == NIL_TYP)
	message (ERR_COUNTPARAM, fname);
    else if (!is_pointer_type(tp1))
	message (WARN_FORMAT, num, fname);
    else {
	switch (referenced_type(tp1)->type) {
	  case bt_char:
	  case bt_uchar:
	  case bt_schar:
	  case bt_charu:
	    break;
	  default:
	    message (WARN_FORMAT, num, fname);
	    break;
	}
    }
}

static const CHAR *
check_printf P5(const CHAR *, fname, int, num, const CHAR*, fstr, enum fpos*, pos, const TYP *, ptp)
{
    CHAR	 optional;
    TYP		*tp;
    if (fstr == NULL || *fstr == 0)
	return NULL;

    switch (*pos) {
      case format_start:
	for ( ; *fstr ; fstr++) {
	    if (*fstr == (CHAR)'%') {
		fstr++;
		if (*fstr == (CHAR)'%')
		    continue;
		break;
	    }
	}
	    
	/* flags */
	for (optional = (CHAR)1; optional && *fstr;) {
	    switch (*fstr) {
	      case '-':
	      case '+':
	      case ' ':
	      case '#':
	      case '0':
		fstr++;
		break;
	      default:
		optional = (CHAR)0;
		break;
	    }
	}

	/* field width */
	for (;*fstr; fstr++) {
	    if (isdigit( ((int)(*fstr)) ))
		continue;
	    if (*fstr == (CHAR)'*') {
		*pos = format_precision;
		check_parameter(fname, num, ptp, tp_int);
		return fstr+1;
	    }
	    break;
	}
	/*FALLTHRU*/
      case format_precision:
	/* precision */
	if (*fstr == (CHAR)'.') {
	    for (fstr++;*fstr;fstr++) {
		if (isdigit( ((int)(*fstr)) ))
		    continue;
		if (*fstr == (CHAR)'*') {
		    *pos = format_optional;
		    check_parameter(fname, num, ptp, tp_int);
		    return fstr+1;
		}
		break;
	    }
	}
	/*FALLTHRU*/
      case format_optional:
	/* optional */
	*pos = format_start;
	switch (*fstr) {
	  case 'h':
	  case 'l':
	  case 'L':
	    optional = *fstr++;
	    break;
	  default:
	    optional = 0;
	    break;
	}

	/* type */
	switch (*fstr) {
	  case 'c':
	  case 'd':
	  case 'i':
	    switch (optional) {
	      case 'l':
		tp = tp_long;
		break;
	      case 'h':
		tp = tp_short;
		break;
	      default:
		tp = tp_int;
		break;
	    }
	    check_parameter(fname, num, ptp, promote(tp));
	    return fstr+1;
	  case 'o':
	  case 'u':
	  case 'x':
	  case 'X':
	    switch (optional) {
	      case 'l':
		tp = tp_ulong;
		break;
	      case 'h':
		tp = tp_ushort;
		break;
	      default:
		tp = tp_uint;
		break;
	    }
	    check_parameter(fname, num, ptp, promote(tp));
	    return fstr+1;
	  case 'e':
	  case 'E': 
	  case 'f': 
	  case 'g':
	  case 'G':
	    switch (optional) {
	      case 'L':
		tp = tp_longdouble;
		break;
	      default:
		tp = tp_double;
		break;
	    }
	    check_parameter(fname, num, ptp, tp);
	    return fstr+1;
	  case 's':
	    tp = tp_string;
	    check_parameter(fname, num, ptp, tp);
	    return fstr+1;
	  case 'p':
	    tp = tp_pointer;
	    check_parameter(fname, num, ptp, tp);
	    return fstr+1;
	  case 'n':
	    switch (optional) {
	      case 'l':
		tp = tp_long;
		break;
	      case 'h':
		tp = tp_short;
		break;
	      default:
		tp = tp_int;
		break;
	    }
	    check_pointer_parameter(fname, num, ptp, tp);
	    return fstr+1;
	  default:
#if 0
	    message();
#endif
	    break;
	}
	break;
     default:
	CANNOT_REACH_HERE();
	break;
    }
    return fstr;
}

static const CHAR *
check_scanf P5(const CHAR *, fname, int, num, const CHAR*, fstr, enum fpos*, pos, const TYP *, ptp)
{
    int		 suppress;
    CHAR	 optional;
    TYP		*tp;
    if (fstr == 0 || *fstr == 0)
	return 0;
    pos = pos;	/* stops compilers generating warning about pos not being used */

    for ( ; *fstr ; fstr++) {
	suppress = 0;
	switch (*fstr) {
	  case '%':
	    fstr++;
	    /* optional assignment-suppressing character */
	    if (*fstr == (CHAR)'*') {
		suppress++;
		fstr++;
	    }

	    /* optional decimal integer that specifies maximum field width */
	    while (isdigit( ((int)(*fstr)) ))
		fstr++;

	    /* optional size of object */
	    switch (*fstr) {
	      case 'h':
	      case 'l':
	      case 'L':
		optional = *fstr++;
		break;
	      default:
		optional = 0;
		break;
	    }

	    /* type of conversion */
	    switch (*fstr) {
	      case 'd':
	      case 'i':
	      case 'n':
		switch (optional) {
		  case 'l':
		    tp = tp_long;
		    break;
		  case 'h':
		    tp = tp_short;
		    break;
		  default:
		    tp = tp_int;
		    break;
		}
		if (suppress)
		    continue;
		check_pointer_parameter(fname, num, ptp, tp);
		return fstr+1;
	      case 'o':
	      case 'u':
	      case 'x':
		switch (optional) {
		  case 'l':
		    tp = tp_ulong;
		    break;
		  case 'h':
		    tp = tp_ushort;
		    break;
		  default:
		    tp = tp_uint;
		    break;
		}
		if (suppress)
		    continue;
		check_pointer_parameter(fname, num, ptp, tp);
		return fstr+1;
	      case 'e':
	      case 'f':
	      case 'g':
		if (suppress)
		    continue;
		switch (optional) {
		  case 'L':
		    tp = tp_longdouble;
		    break;
		  case 'l':
		    tp = tp_double;
		    break;
		  default:
		    tp = tp_float;
		    break;
		}
		check_pointer_parameter(fname, num, ptp, tp);
		return fstr+1;
	      case '[':
		fstr++;
		if (*fstr == (CHAR)'^')
		    fstr++;
		if (*fstr == (CHAR)']')
		    fstr++;
		while (*fstr && *fstr != (CHAR)']')
		    fstr++;
		/*FALLTHRU*/
	      case 'c':
	      case 's':
		if (suppress)
		    continue;
		check_array_parameter (fname, num, ptp);
		return fstr+1;
	      case 'P':
		if (suppress)
		    continue;
		tp = tp_pointer;
		check_pointer_parameter(fname, num, ptp, tp);
		return fstr+1;
	      default:
		break;
	    }
	    break;
	  default:
	    break;
	}
    }
    return 0;
}
#endif /* FORMAT_CHECK */

/*
 * parmlist will build a list of parameter expressions in a function call and
 * return a pointer to the last expression parsed. since parameters are
 * generally pushed from right to left we get just what we asked for...
 */
static EXPR *
parmlist P2(EXPR *, ep, const BLOCK *, block)
{
    SYM	*	sp = block ? block->symbols.head : NULL;
    SYM		*sp1 = sp;
    EXPR	*ep1, *ep2;
    TYP		*tp, *tp2;
    int		 pnum = 0;
    const CHAR *fname = ep->nodetype == en_nacon ? ep->v.str : (CHAR *)"";
#ifdef FORMAT_CHECK
    enum fpos	 format_position;
    const CHAR	*fstr = NULL;
    struct pflike {
	    CHAR **		name;
	    int			num;
	    const CHAR *	(*func) P_((const CHAR *, int, const CHAR *, enum fpos *, const TYP *));
	    };
    struct pflike *pf;
    static  struct pflike printflike[] = {
		{ &fprintf_name, 2, check_printf },
		{ &printf_name,  1, check_printf },
		{ &sprintf_name, 2, check_printf },
		{ &fscanf_name,  2, check_scanf },
		{ &scanf_name,   1, check_scanf },
		{ &sscanf_name,  2, check_scanf },
		{ 0,		 0, 0 }
	    };


    if (format_option) {
	for (pf = printflike; pf->name; pf++) {
	    if (fname == *(pf->name))
		break;
	}
    }
#endif /*FORMAT_CHECK*/
    if (!trad_option && sp == NIL_SYM)
	message(WARN_NOPROTO, fname);

    ep1 = NIL_EXPR;
    while (lastst != tk_closepa) {
	pnum++;
	ep2 = exprnc();	/* evaluate a parameter */
        if (ep2 == NIL_EXPR)
            return NIL_EXPR;
	check_set(ep2);
	check_sequence_accessed(ep2);
	tp = ep2->etp;
	if (is_void(tp)) {
	    message(ERR_VOIDPARM, fname);
	    break;
	}
	if (sp != NIL_SYM) {		/* do prototype checking */
	    /* skip enumeration constants etc. */
	    while ((sp1 != NIL_SYM) && ((sp1->storage_class == sc_const) ||
				  (sp1->storage_class == sc_member)))
		sp1 = sp1->next;

	    if (sp1 == NIL_SYM || is_void(sp1->tp))
		message(ERR_COUNTPARAM, fname);
	    else if (!is_ellipsis(sp1->tp)) {
		tp2 = tp;
		if (is_array_type(sp1->tp))
		    ep2 = implicit_castop(ep2, tp_pointer);
		else
		    ep2 = implicit_castop(ep2, sp1->tp);
	    }
	}

	/*
	 * do the default promotions
	 */
	switch (tp->type) {
	  case bt_char:
	  case bt_charu:
	  case bt_uchar:
	  case bt_schar:
	  case bt_short:
	  case bt_ushort:
	    if (sp1 == NIL_SYM || is_ellipsis(sp1->tp)) {
		if (!trad_option && (sp1 == NIL_SYM) && (tp->size < tp_int->size))
		    message (WARN_PROMOTE, pnum, fname, "int");
		ep2 = implicit_castop(ep2, promote(tp));
	    }
	    break;
	  case bt_float:
	    if (sp1 == NIL_SYM || is_ellipsis(sp1->tp)) {
		if (!trad_option && sp1 == NIL_SYM)
		    message (WARN_PROMOTE, pnum, fname, "double");
		ep2 = implicit_castop(ep2, tp_double);
	    }
	    /*FALLTHRU*/
	  case bt_double:
	  case bt_longdouble:
	  case bt_struct:
	  case bt_union:
            /* trap struct assigns */
	    uses_structassign=1;
	    break;
	  default:
	    break;
	}

#ifdef FORMAT_CHECK
	if (format_option) {
	    if ((pf->num == pnum) && (ep2->nodetype == en_labcon)) {
		fstr = get_stringlit(ep2->v.l);
		format_position = format_start;
	    } else if (fstr) {
		fstr = (pf->func)(fname, pnum, fstr, &format_position, ep2->etp);
	    }
	}
#endif /*FORMAT_CHECK*/

	if (sp1 != NIL_SYM && !is_ellipsis(sp1->tp)) {
	    /*
	     * Check to see if this would cause a problem if compiled by a
	     * K&R compiler.
	     */
	    tp2 = promote(tp2);
	    tp = promote(sp1->tp);
	    if (tp2->size != tp->size)
		message(WARN_PARAMSIZE, pnum, fname);
	    sp1 = sp1->next;
	}
	ep1 = mk_node(en_list, ep2, ep1, tp_void);
	if (lastst != tk_comma)
	    break;
	getsym();
    }
    /* skip enumeration constants etc. */
    while ((sp1 != NIL_SYM) && ((sp1->storage_class == sc_const) ||
			  (sp1->storage_class == sc_member)))
	sp1 = sp1->next;
    if (sp1 != NIL_SYM && !is_ellipsis(sp1->tp) && !is_void(sp1->tp))
	message(ERR_COUNTPARAM, fname);
#ifdef FORMAT_CHECK
    else if (format_option && (fstr != 0))
	VOIDCAST (pf->func)(fname, pnum, fstr, &format_position, NIL_TYP);
#endif /* FORMAT_CHECK */
    return ep1;
}

/*
 * primary will parse a primary expression and set the node pointer returning
 * the type of the expression parsed. primary expressions are any of:
 * id
 * constant
 * string
 * ( expression )
 * primary[ expression ]
 * primary.id
 * primary->id
 * primary( parameter list )
 * primary++
 * primary--
 * -- or just a semicolon, yields empty expression --
 *
 */
static EXPR *
primary P0(void)
{
    EXPR	*ep, *ep1, *ep2;
    SYM		*sp;
    TYP		*tp, *tp1;

    switch (lastst) {
      case tk_id:
	ep = nameref();
        if (ep == NULL)
            return NIL_EXPR;
        /*
         * function names alone are pointers to functions.
         * If followed by '(', the reference is stripped off
         * later.
         */
        if (ep->etp->type == bt_func) {
	   ep->etp = mk_type(tp_pointer, ep->etp);
	}
	break;
      case tk_iconst:
	tp = tp_int;
	goto const1;
      case tk_uconst:
	tp = tp_uint;
	goto const1;
      case tk_lconst:
	tp = tp_long;
	goto const1;
      case tk_ulconst:
	tp = tp_ulong;
	goto const1;
      case tk_wconst:
	tp = tp_wchar;
const1:
	ep = mk_icon((IVAL)ival, tp);
	getsym();
	break;
#ifdef FLOAT_SUPPORT
      case tk_fconst:
        tp = tp_float;
	goto const2;
      case tk_rconst:
        tp = tp_double;
	goto const2;
      case tk_lrconst:
        tp = tp_longdouble;
const2:
	ep = mk_fcon(&rval, tp);
	getsym();
	break;
#endif /* FLOAT_SUPPORT */
      case tk_wsconst:
	tp = tp_wstring;
	goto string;
      case tk_sconst:
	tp = tp_string;
string:
        if (sizeof_flag) {
	    ep = mk_node(en_size, NIL_EXPR, NIL_EXPR, tp);
            ep->v.i  = (IVAL) lastsymlen + 1L;
        } else {
	    ep = mk_lcon(stringlit(lastsym, lastsymlen));
	    ep->etp = tp;
	}
	getsym();
	break;
      case tk_openpa:
	getsym();
	if (is_type_name(lastst)) {
	    tp = type_name();
	    needpunc(tk_closepa);
	    ep = unary();
	    if (ep == NULL) {
		message(ERR_IDEXPECT);
		return NIL_EXPR;
	    }
	    /* do the cast */
	    ep = explicit_castop(ep, tp);
	} else {
	    ep = expression();
	    needpunc(tk_closepa);
	    if (ep == NULL)
		return NIL_EXPR;
	}
	break;
      default:
	return NIL_EXPR;
    }
    for (;;) {
	switch (lastst) {
	  case tk_openbr:		/* build a subscript reference */
	    getsym();
	    /*
	     * a[b] is defined as *(a+b), such exactly one of (a,b) must be a
	     * pointer and one of (a,b) must be an integer expression
	     */
	    if (is_pointer_type(ep->etp)) {
	        ep2 = expression();
		ep1 = ep;
	    } else {
		ep2 = ep;
		ep1 = expression();
		ep  = ep1;
	    }

	    /*
	     * now, ep1 describes the pointer,
	     *      ep2 describes the integral value
	     */
	    check_integral(ep2);

	    tp = ep1->etp;
	    if (is_pointer_type(tp)) {
		tp = referenced_type(tp);
		check_complete(tp);
	    } else
		message (ERR_NOPOINTER);

	    if (tp_pointer->size == tp_ulong->size)
		tp1 = tp_ulong;
	    else
		tp1 = tp_ushort;

	    ep = mk_icon((IVAL) tp->size, tp1);
	    /*
	     * ep is the size of the referenced object
	     */
	    ep2 = explicit_castop(ep2, tp1);
	    ep = mk_node(en_mul, ep, ep2, tp1);
	    ep = explicit_castop(ep, tp1);
	    ep = mk_node(en_add, ep, ep1, ep1->etp);
	    ep = cond_deref(ep, tp);
	    needpunc(tk_closebr);
	    break;
	  case tk_pointsto:
	    check_set(ep);
	    check_sequence_accessed(ep);
	    tp = ep->etp;
	    if (is_pointer_type(tp))
		tp = referenced_type(tp);
	    else
		message(ERR_NOPOINTER);
	    /*
	     * tp->type should be bt_struct or bt_union 
	     * the ref node will be stripped off in a minute
	     */
	    ep = cond_deref(ep, tp);
	    /*FALLTHRU*/
	  case tk_dot:
	    getsym();		/* past -> or . */
	    tp = ep->etp;
	    if (lastst == tk_id) {
		if ((members(tp) == NIL_BLOCK) ||
		    ((sp = search(lastsym, &(members(tp)->symbols))) == NIL_SYM))
		    message(ERR_NOMEMBER, lastsym);
		else {
		    /* strip off the en_ref node on top */
		    if (ep->nodetype == en_ref) {
			ep = ep->v.p[0];
			ep->etp = tp_pointer;
		    } else {
			ep = mk_node(en_deref, ep, NIL_EXPR, tp_pointer);
		    }
		    tp = qualify_type(sp->tp, tp->qual | sp->tp->qual);
		    ep1 = mk_icon(sp->value.i, tp_long);
		    ep = mk_node(en_add, ep, ep1,
					 mk_type(tp_pointer, tp));
		    ep = cond_deref(ep, tp);
		}
		getsym();	/* past id */
	    } else
		message(ERR_IDEXPECT);
	    break;
	  case tk_openpa:		/* function reference */
	    getsym();
	    tp = ep->etp;
	    if (!is_function_type(tp))
		message(ERR_NOFUNC);
	    /*
	     *  the '*' may be ommitted with pointers to functions
	     *  we have included another indirection (see above, case id:)
	     */
	    if (is_pointer_type(tp))
		tp = referenced_type(tp);
            /*
             * This hack lets us remember that this function itself calls
             * other functions.
             * The code generator might use this information to generate
             * safer register-pop-off code.
             */
            is_leaf_function = FALSE;

	    ep2 = parmlist(ep, parameters(tp));
	    sequence_point();
	    tp = returned_type(tp);
	    if (tp == NULL)
		tp = tp_void;	/* this happens on an error path! */
	    if (is_structure(tp))
		check_complete(tp);
	    ep = mk_node(en_fcall, ep, ep2, tp);
	    needpunc(tk_closepa);
	    break;
	  case tk_autodec:
	  case tk_autoinc:
	    check_set(ep);
	    check_sequence_modified(ep);
	    check_modifiable_lvalue(ep);
	    check_scalar(ep);
	    tp = ep->etp;
	    check_complete(tp);
#ifdef FLOAT_SUPPORT
	    if (is_floating_type(tp))
		ep1 = mk_fcon(&F_one, tp);
	    else
#endif
	    {
		SIZE size;
		if (is_pointer_type(tp)) {
		    check_object(ep);
		    size = referenced_type(tp)->size;
		} else
		    size = 1L;
		ep1 = mk_icon((IVAL) size, tp_long);
	    }
	    if (lastst == tk_autodec)
		ep = mk_node(en_adec, ep, ep1, tp);
	    else
		ep = mk_node(en_ainc, ep, ep1, tp);
	    getsym();
	    if (trad_option && lastst == tk_pointsto)
		needpunc((TOKEN)(lastst+1));	/* force an error as K&R doesn't allow this*/
	    break;
	  default:
	    return ep;
	}
    }
}

/*
 * unary evaluates unary expressions and returns the type of the expression
 * evaluated. unary expressions are any of:
 *
 * primary
 * !unary
 * ~unary
 * ++unary
 * --unary
 * +unary
 * -unary
 * *unary
 * &unary
 * (typecast)unary
 * sizeof(typecast)
 * sizeof unary
 *
 */
static EXPR *
unary P0(void)
{
    TYP		*tp;
    EXPR	*ep, *ep1;
    BOOL	 flag = FALSE;
    SIZE	 size;
    switch (lastst) {
      case tk_autodec:
	flag = TRUE;
	/*FALLTHRU*/
      case tk_autoinc:
	getsym();
	ep = unary();
	if (ep == NIL_EXPR) {
	    message(ERR_IDEXPECT);
	    return NIL_EXPR;
	}
	check_modifiable_lvalue(ep);
	check_scalar(ep);
	check_sequence_modified(ep);
	tp = ep->etp;
	check_complete(tp);
#ifdef FLOAT_SUPPORT
	if (is_floating_type(tp))
	    ep1 = mk_fcon(&F_one, tp);
	else
#endif
	{
	    if (is_pointer_type(tp)) {
		check_object(ep);
		size = referenced_type(tp)->size;
	    } else
		size = 1L;
	    ep1 = mk_icon(size, tp_long);
	}
	ep = mk_node((EXPRTYPE)(flag ? en_assub : en_asadd), ep, ep1, tp);
	break;
      case tk_plus:
	if (trad_option) {
	    /* only ANSI C allows the unary + operator */
	    ep = primary();
	    break;
	}
	flag = TRUE;
	/*FALLTHRU*/
      case tk_minus:
	getsym();
	ep = unary();
	if (ep == NIL_EXPR) {
	    message(ERR_IDEXPECT);
	    return NIL_EXPR;
	}
	check_arithmetic(ep);
	check_set(ep);
	check_sequence_accessed(ep);
	if (!flag && is_unsigned_type(ep->etp))
	    message(WARN_MINUS);
	ep = integral_promotion(ep);

	if (!flag)	/* don't bother about unary plus */
	    ep = mk_node(en_uminus, ep, NIL_EXPR, ep->etp);
	break;
      case tk_not:
	getsym();
	ep = unary();
	if (ep == NIL_EXPR) {
	    message(ERR_IDEXPECT);
	    return NIL_EXPR;
	}
	check_scalar(ep);
	check_set(ep);
	check_sequence_accessed(ep);
	ep = mk_node(en_not, ep, NIL_EXPR, tp_int);
	break;
      case tk_compl:
	getsym();
	ep = unary();
	if (ep == NIL_EXPR) {
	    message(ERR_IDEXPECT);
	    return NIL_EXPR;
	}
	check_integral(ep);
	check_set(ep);
	check_sequence_accessed(ep);
	ep = integral_promotion(ep);
	ep = mk_node(en_compl, ep, NIL_EXPR, ep->etp);
	break;
      case tk_star:
	getsym();
	ep = unary();
	if (ep == NIL_EXPR) {
	    message(ERR_IDEXPECT);
	    return NIL_EXPR;
	}
	check_set(ep);
	check_sequence_accessed(ep);
	tp = ep->etp;
	if (referenced_type(tp)) {
	    /*
	     * ANSI C specifies that the function identifer will be converted to
	     * a pointer to function if necessary.
	     */
	    if (tp->type != bt_func)
		tp = referenced_type(tp);
	} else
	    message(ERR_DEREF);
	ep = cond_deref(ep, tp);
	break;
      case tk_and:
	getsym();
	address_flag = TRUE;
	ep = unary();
	address_flag = FALSE;
	if (ep == NIL_EXPR) {
	    message(ERR_IDEXPECT);
	    return NIL_EXPR;
	}
	if (is_function_type(ep->etp) && is_derived_type(ep->etp)) {
	    if (ep->etp->type == bt_func) {
		/* functions are implicitly converted to pointers to functions */
		ep->etp = mk_type(tp_pointer, ep->etp);
	    }
	} else if (is_lvalue(ep)) {
	    if (ep->nodetype == en_fieldref) {
		message(ERR_BITFIELD);
		break;
	    }
	    tp = ep->etp;
	    ep = ep->v.p[0];
	    ep->etp = mk_type(tp_pointer, tp);
	    if (ep->nodetype == en_size)
		ep->v.i = ep->etp->size;
#ifdef CPU_DEFINED
	    /*
	     * possibly remove a variable which is a candidate for a
	     * register.  This is done in the analyze module normally,
	     * but the use of the address operator may be hidden due
	     * to optimizatons.
	     * imagine '(char *)(&len) + 2'
	     */
           if (ep->nodetype == en_sym &&
	       (ep->v.sp->storage_class == sc_auto ||
		ep->v.sp->storage_class == sc_parms ||
		ep->v.sp->storage_class == sc_register)) {
                deloptinfo (ep) ;
           }
#endif /* CPU_DEFINED */
	} else if (is_pointer_type(ep->etp)) {
	    if (referenced_type(ep->etp)->type == bt_func)
		message(WARN_ADDFUNC);
	    else if (is_array_type(ep->etp)) {
		ep->etp = mk_type(tp_pointer, ep->etp);
	    }
	} else
	    message(ERR_LVALUE);
	break;
      case kw_sizeof:
	getsym();
	/*
	 * This is a mess.
	 * Normally, we treat array names just as pointers, but with sizeof,
	 * we want to get the real array size. So, the 'real' size is stored
	 * in an en_size node in v.i
	 */
	if (lastst == tk_openpa) {
	    getsym();
	    if (is_type_name(lastst)) {
		tp = type_name();
		if (tp->type == bt_func)
		    message(ERR_ILLSIZEOF);
		size = tp->size;
		ep = NIL_EXPR;
	    } else {
		/* '(' met -- so any expression allowed */
                sizeof_flag++;
		ep = expression();
		sizeof_flag--;
		if (ep == NIL_EXPR) {
		    message(ERR_ILLSIZEOF);
		    size = (SIZE) 1;
                } else if (ep->nodetype == en_size) {
		    size = ep->v.i;
		} else {
		    size = ep->etp->size;
		}
	    }
	    needpunc(tk_closepa);
	} else {
            sizeof_flag++;
	    ep = unary();
            sizeof_flag--;
	    if (ep == NIL_EXPR) {
		message(ERR_ILLSIZEOF);
		size = (SIZE) 1;
            } else if (ep->nodetype == en_size) {
	 	size = ep->v.i;
	    } else {
		size = ep->etp->size;
	    }
	}
	check_sizeof(ep, size);
	ep = mk_icon(size, tp_size);
	break;
      default:
	ep = primary();
	break;
    }
    ep = opt0(ep);
    return ep;
}

/*
 * arithmetic_conversion will coerce the nodes passed into compatible
 * types and return the type of the resulting expression.
 */
static TYP*
arithmetic_conversion P2(EXPR**, node1, EXPR**, node2)
{
    EXPR	*ep1 = *node1;
    EXPR	*ep2 = *node2;
    TYP		*tp1 = ep1->etp;
    TYP		*tp2 = ep2->etp;
    TYP		*tp;
    /* pointers may be combined with integer constant 0 */
    ep1 = opt0(ep1);
    ep2 = opt0(ep2);
    if (is_pointer_type(tp1) && is_null_pointer(ep2))
	return tp1;
    if (is_pointer_type(tp2) && is_null_pointer(ep1))
	return tp2;

    if (is_pointer_type(tp1) && is_pointer_type(tp2))
	return tp1;

    /*
     * if either operand has type long double, the other operand is
     * converted to long double.
     */
    tp = tp_longdouble;
    if (tp1->type == tp->type || tp2->type == tp->type) {
	*node2 = implicit_castop(ep2, tp);
	*node1 = implicit_castop(ep1, tp);
	return tp;
    }

    /*
     * if either operand has type double the other operand is converted
     * to double.
     */
    tp = tp_double;
    if (tp1->type == tp->type || tp2->type == tp->type) {
	*node2 = implicit_castop(ep2, tp);
	*node1 = implicit_castop(ep1, tp);
	return tp;
    }

    /*
     * if either operand has type float the other operand is converted
     * to float.
     *
     * However K&R mandates that all floating point arithmetic be done as
     * doubles.
     */
    tp = tp_float;
    if (tp1->type == tp->type || tp2->type == tp->type) {
	if (trad_option)
	    tp = tp_double;
	*node2 = implicit_castop(ep2, tp);
	*node1 = implicit_castop(ep1, tp);
	return tp;
    }

    /*
     * The integral promotions are performed on both operands.
     * If an int can represent all values of the original type, the value is
     * converted to an int; otherwise it is converted to an unsigned int.
     */
    ep1 = integral_promotion(ep1);
    tp1 = ep1->etp;
    ep2 = integral_promotion(ep2);
    tp2 = ep2->etp;

    /*
     * if either operand has type unsigned long int, the other operand is
     * converted to unsigned long int.
     */
    tp = tp_ulong;
    if (tp1->type == tp->type || tp2->type == tp->type) {
	*node2 = implicit_castop(ep2, tp);
	*node1 = implicit_castop(ep1, tp);
	return tp;
    }

    /*
     * if one operand has type long int and the other has type unsigned int,
     * if a long int can represent all values of an unsigned int, the operand
     * of type unsigned int is converted to long int;  if a long int cannot
     * represent all the values of an unsigned int, both operands are converted
     * to unsigned long int.
     */
    if ((tp1->type == bt_long && tp2->type == tp_uint->type ) ||
	(tp2->type == bt_long && tp1->type == tp_uint->type)) {
	tp = (tp_long->size > tp_uint->size) ? tp_long : tp_ulong;
	*node2 = implicit_castop(ep2, tp);
	*node1 = implicit_castop(ep1, tp);
	return tp;
    }

    /*
     * if either operand has type long int, the other operand is
     * converted to long int.
     */
    tp = tp_long;
    if (tp1->type == tp->type || tp2->type == tp->type) {
	*node2 = implicit_castop(ep2, tp);
	*node1 = implicit_castop(ep1, tp);
	return tp;
    }

    /*
     * if either operand has type unsigned int, the other operand is
     * converted to unsigned int.
     */
    tp = tp_uint;
    if (tp1->type == tp->type || tp2->type == tp->type) {
	*node2 = implicit_castop(ep2, tp);
	*node1 = implicit_castop(ep1, tp);
	return tp;
    }

    /*
     * both operands have type int
     */
    tp = tp_int;
    *node2 = implicit_castop(ep2, tp);
    *node1 = implicit_castop(ep1, tp);
    return tp;
}

/*
 * ,,arithmetic_conversion'' for comparisons:
 * When comparing two char's, it is not necessary to cast
 * both of them to long in advance
 *
 * Perhaps not strictly K&R, but more efficient.
 * If you don't like it, use arithmetic_conversion in ALL cases
 */
static TYP *
arithmetic_conversion2 P2(EXPR**, node1, EXPR**, node2)
{
    TYP		*tp1 = (*node1)->etp;
    TYP		*tp2 = (*node2)->etp;
    TYP		*tp;

    /* short cut: */
    if (tp1->type == tp2->type)
	return tp1;

    /* comparison with integer constant */
    *node1 = opt0(*node1);
    *node2 = opt0(*node2);
    if (is_constexpr(*node1)) {
	EXPR  ** node = node1;
	tp = tp1;
	node1 = node2;
	tp1 = tp2;
	node2 = node;
	tp2 = tp;
    }
    if (is_constexpr(*node2)) {
	if (is_pointer_type(tp2) && !is_pointer_type(tp1))
	    message(ERR_MISMATCH);
	if (is_in_range(*node2, tp1)) {
	    tp = tp1;
	    *node2 = implicit_castop(*node2, tp);
	    return tp;
	}
    }

    switch (tp1->type) {
	/* Type of first operand */
      case bt_char:
      case bt_schar:
	switch (tp2->type) {
	  case bt_char:
	  case bt_schar:
	    return tp2;
	  case bt_charu:
	  case bt_uchar:
	    tp = tp_short;
	    *node1 = implicit_castop(*node1, tp);
	    *node2 = implicit_castop(*node2, tp);
	    return tp;
	  case bt_short:
	  case bt_ushort:
	  case bt_int16:
	  case bt_uint16:
	  case bt_int32:
	  case bt_uint32:
	  case bt_long:
	  case bt_ulong:
	  case bt_float:
	  case bt_double:
	  case bt_longdouble:
	    tp = tp2;
	    *node1 = implicit_castop(*node1, tp);
	    return tp;
	  default:
	    break;
	}
	break;
      case bt_charu:
      case bt_uchar:
	switch (tp2->type) {
	  case bt_charu:
	  case bt_uchar:
	    return tp2;
	  case bt_char:
	  case bt_schar:
	    tp = tp_short;
	    *node1 = implicit_castop(*node1, tp);
	    *node2 = implicit_castop(*node2, tp);
	    return tp;
	  case bt_short:
	  case bt_ushort:
	  case bt_int16:
	  case bt_uint16:
	  case bt_int32:
	  case bt_uint32:
	  case bt_long:
	  case bt_ulong:
	  case bt_float:
	  case bt_double:
	  case bt_longdouble:
	    tp = tp2;
	    *node1 = implicit_castop(*node1, tp);
	    return tp;
	  default:
	    break;
	}
	break;
      case bt_short:
      case bt_ushort:
      case bt_int16:
      case bt_uint16:
	switch (tp2->type) {
	  case bt_char:
	  case bt_charu:
	  case bt_uchar:
	  case bt_schar:
	    tp = tp1;
	    *node2 = implicit_castop(*node2, tp);
	    return tp;
	  case bt_ushort:
	  case bt_uint16:
            if (short_option) {
		tp = tp2;
		*node1 = implicit_castop (*node1, tp);
            } else {
		tp = tp_long;
		*node1 = implicit_castop (*node1, tp);
		*node2 = implicit_castop (*node2, tp);
            }
	    return tp;
	  case bt_short:
	  case bt_int16:
	    if (short_option) {
		tp = tp_ushort;
		*node2 = implicit_castop(*node2, tp);
	    } else {
		tp = tp_long;
		*node1 = implicit_castop (*node1, tp);
		*node2 = implicit_castop (*node2, tp);
            }
	    return tp;
	  case bt_int32:
	  case bt_uint32:
	  case bt_long:
	  case bt_ulong:
	  case bt_float:
	  case bt_double:
	  case bt_longdouble:
	    tp = tp2;
	    *node1 = implicit_castop(*node1, tp);
	    return tp;
	  default:
	    break;
	}
	break;
	/*
	 * pointers are equivalent to function names
	 */
      case bt_pointer16:
      case bt_pointer32:
	if (tp2->type == bt_func) {
	    tp = tp1;
	    *node2 = implicit_castop(*node2, tp);
	    return tp;
	}
	/*FALLTHRU*/
      case bt_func:
	if (is_pointer_type(tp2)) {
	    *node1 = implicit_castop(*node1, tp2);
	}
	/*FALLTHRU*/
      case bt_int32:
      case bt_uint32:
      case bt_long:
      case bt_ulong:
	switch (tp2->type) {
	  case bt_char:
	  case bt_charu:
	  case bt_uchar:
	  case bt_schar:
	  case bt_short:
	  case bt_ushort:
	  case bt_int16:
	  case bt_uint16:
	  case bt_int32:
          case bt_long:
	  case bt_pointer16:
	  case bt_pointer32:
	    tp = tp1;
            *node2 = implicit_castop(*node2, tp);
	    return tp;
	  case bt_uint32:
	  case bt_ulong:
	  case bt_float:
	  case bt_double:
	  case bt_longdouble:
	    tp = tp2;
	    *node1 = implicit_castop(*node1, tp);
	    return tp;
	  default:
	    break;
	}
	break;
      case bt_float:
      case bt_double:
	switch (tp2->type) {
	  case bt_char:
	  case bt_charu:
	  case bt_uchar:
	  case bt_schar:
	  case bt_short:
	  case bt_ushort:
	  case bt_int16:
	  case bt_uint16:
	  case bt_int32:
	  case bt_uint32:
	  case bt_long:
	  case bt_ulong:
	  case bt_float:
	    tp = tp1;
            *node2 = implicit_castop(*node2, tp);
	    return tp;
          case bt_double:
	    tp = tp2;
	    *node1 = implicit_castop(*node1, tp);
	    return tp;
	  default:
	    break;
	}
	break;
      case bt_longdouble:
	switch (tp2->type) {
	  case bt_char:
	  case bt_charu:
	  case bt_uchar:
	  case bt_schar:
	  case bt_short:
	  case bt_ushort:
	  case bt_int16:
	  case bt_uint16:
	  case bt_int32:
	  case bt_uint32:
	  case bt_long:
	  case bt_ulong:
	  case bt_float:
          case bt_double:
	    tp = tp2;
	    *node1 = implicit_castop(*node1, tp);
	    return tp;
	  default:
	    break;
	}
	break;
      default:
	break;
    }
    message(ERR_MISMATCH);
    return tp1;
}


/*
 * multops parses the multiply priority operators. the syntax of this group
 * is:
 *
 * unary multop * unary multop / unary multop % unary
 */
static EXPR *
multops P0(void)
{
    EXPR	*ep1, *ep2;
    EXPRTYPE	 nt;
    TYP		*tp;

    ep1 = unary();
    if (ep1 == NIL_EXPR)
	return NIL_EXPR;
    for (;;) {
	switch (lastst) {
	  case tk_star:
	    nt = en_mul;
	    break;
	  case tk_divide:
	    nt = en_div;
	    break;
	  case tk_mod:
	    nt = en_mod;
	    break;
	  default:
	    ep1 = opt0(ep1);
	    return ep1;
	}
	getsym();		/* move on to next unary op */
	ep2 = unary();
	if (ep2) {
	    switch (nt) {
	      case en_div:
		check_zero(ep2);
		/*FALLTHRU*/
	      case en_mul:
		check_arithmetic(ep1);
		check_arithmetic(ep2);
		break;
	      case en_mod:
		check_zero(ep2);
		check_integral(ep1);
		check_integral(ep2);
		break;
	      default:
		CANNOT_REACH_HERE();
		break;
	    }
	    check_set(ep1);
	    check_set(ep2);
	    check_sequence_accessed(ep1);
	    check_sequence_accessed(ep2);
	    tp = arithmetic_conversion(&ep1, &ep2);
	    ep1 = mk_node(nt, ep1, ep2, tp);
	} else
	    message(ERR_IDEXPECT);
    }
}

/*
 * addops handles the addition and subtraction operators.
 */
static EXPR *
addops P0(void)
{
    EXPR	*ep1, *ep2, *ep3;
    TYP		*tp;
    TYP		*tp1 = small_option ? tp_ushort : tp_ulong;
    EXPRTYPE	 nt;

    ep1 = multops();
    if (ep1 == NIL_EXPR)
	return NIL_EXPR;
    for (;;) {
	switch (lastst) {
	  case tk_plus:
	    nt = en_add;
	    break;
	  case tk_minus:
	    nt = en_sub;
	    break;
	  default:
	    ep1 = opt0(ep1);
	    return ep1;
	}
	getsym();
	ep2 = multops();
	if (ep2 == NIL_EXPR) {
	    message(ERR_IDEXPECT);
	    return ep2;
	}
	check_set(ep1);
	check_set(ep2);
	check_sequence_accessed(ep1);
	check_sequence_accessed(ep2);
	if (is_pointer_type(ep1->etp)) {
	    if ((nt == en_sub) && is_pointer_type(ep2->etp)) {
		/* pointer subtraction */
		if (!is_compatible(ep1->etp, ep2->etp))
		    message(WARN_POINTER);
		check_object(ep1);
		check_complete(ep1->etp);
		check_object(ep2);
		check_complete(ep2->etp);
		ep1 = mk_node(nt, ep1, ep2, tp_ptrdiff);
		ep1 = explicit_castop(ep1, tp1);
		/* divide the result by the size */
		ep2 = mk_icon((IVAL) referenced_type(ep2->etp)->size, tp1);
		ep1 = mk_node(en_div, ep1, ep2, tp_ptrdiff);
		ep1 = (tp_ptrdiff->size == 2L) ? explicit_castop(ep1, tp_ptrdiff) :
						implicit_castop(ep1, tp_ptrdiff);
		continue;
	    }
	    /* pointer +/- integer */
	    check_integral(ep2);
	    check_object(ep1);
	    check_complete(ep1->etp);
	    ep2 = explicit_castop(ep2, tp1);
	    ep3 = mk_icon((IVAL) referenced_type(ep1->etp)->size, tp1);
	    ep2 = mk_node(en_mul, ep3, ep2, tp_long);
	    ep1 = mk_node(nt, ep1, ep2, ep1->etp);
	    continue;
	}
	if (is_pointer_type(ep2->etp)) {
	    /* integer + pointer */
	    if (nt == en_add)
		check_integral(ep1);
	    else
		message(ERR_MINUS);
	    check_object(ep2);
	    check_complete(ep2->etp);
	    ep1 = explicit_castop(ep1, tp1);
	    ep3 = mk_icon((IVAL) referenced_type(ep2->etp)->size, tp1);
	    ep1 = mk_node(en_mul, ep3, ep1, tp1);
	    ep1 = mk_node(nt, ep1, ep2, ep2->etp);
	    continue;
	}
	tp = arithmetic_conversion(&ep1, &ep2);
	ep1 = mk_node(nt, ep1, ep2, tp);
    }
}

/*
 * shiftop handles the shift operators << and >>.
 */
static EXPR *
shiftop P0(void)
{
    EXPR	*ep1, *ep2;
    EXPRTYPE	 nt;

    ep1 = addops();
    if (ep1 == NIL_EXPR)
	return NIL_EXPR;
    for (;;) {
	switch (lastst) {
	  case tk_lshift:
	    nt = en_lsh;
	    break;
	  case tk_rshift:
	    nt = en_rsh;
	    break;
	  default:
	    ep1 = opt0(ep1);
	    return ep1;
	}
	getsym();
	ep2 = addops();
	if (ep2) {
	    check_integral(ep1);
	    check_integral(ep2);
	    check_set(ep1);
	    check_set(ep2);
	    check_sequence_accessed(ep1);
	    check_sequence_accessed(ep2);
	    ep1 = integral_promotion(ep1);
	    check_shift(ep2, ep1->etp);
	    ep2 = integral_promotion(ep2);
	    ep1 = mk_node(nt, ep1, ep2, ep1->etp);
	} else
	    message(ERR_IDEXPECT);
    }
}

/*
 * relation handles the relational operators < <= > and >=.
 */
static EXPR *
relation P0(void)
{
    EXPR	*ep1, *ep2;
    EXPRTYPE	 nt;

    ep1 = shiftop();
    if (ep1 == NIL_EXPR)
	return NIL_EXPR;
    for (;;) {
	switch (lastst) {

	  case tk_lt:
	    nt = en_lt;
	    break;
	  case tk_gt:
	    nt = en_gt;
	    break;
	  case tk_leq:
	    nt = en_le;
	    break;
	  case tk_geq:
	    nt = en_ge;
	    break;
	  default:
	    ep1 = opt0(ep1);
	    return ep1;
	}
	getsym();
	ep2 = shiftop();
	if (ep2) {
	    check_set(ep1);
	    check_set(ep2);
	    check_sequence_accessed(ep1);
	    check_sequence_accessed(ep2);
	    if (nt == en_lt)
		check_relational (ep1, ep2) ;
	    VOIDCAST arithmetic_conversion2(&ep1, &ep2);
	    if (is_pointer_type(ep1->etp) && is_pointer_type(ep2->etp) &&
	        !is_compatible(ep1->etp, ep2->etp))
		message(WARN_PTRCAST);
	    ep1 = mk_node(nt, ep1, ep2, tp_int);
	} else
	    message(ERR_IDEXPECT);
    }
}

/*
 * equalops handles the equality and inequality operators.
 */
static EXPR *
equalops P0(void)
{
    EXPR	*ep1, *ep2;
    EXPRTYPE	 nt;

    ep1 = relation();
    if (ep1 == NIL_EXPR)
	return NIL_EXPR;
    for (;;) {
	switch (lastst) {
	  case tk_eq:
	    nt = en_eq;
	    break;
	  case tk_neq:
	    nt = en_ne;
	    break;
	  default:
	    ep1 = opt0(ep1);
	    return ep1;
	}
	getsym();
	ep2 = relation();
	if (ep2) {
	    check_set(ep1);
	    check_set(ep2);
	    check_sequence_accessed(ep1);
	    check_sequence_accessed(ep2);
	    check_equality (ep1, ep2) ;
	    VOIDCAST arithmetic_conversion2(&ep1, &ep2);
	    if (is_pointer_type(ep1->etp) && is_pointer_type(ep2->etp) &&
	        !is_compatible(ep1->etp, ep2->etp))
		message(WARN_PTRCAST);
	    ep1 = mk_node(nt, ep1, ep2, tp_int);
	} else
	    message(ERR_IDEXPECT);
    }
}

/*
 * binop is a common routine to handle all of the legwork and error checking
 * for bitandop, bitorop, bitxor
 */
#ifdef __STDC__
static EXPR *
binop(EXPR *(*xfunc) (void),  EXPRTYPE nt,  TOKEN sy)
#else
static EXPR *
binop(xfunc, nt, sy)
    EXPR	*(*xfunc) P_((void));
     EXPRTYPE	 nt;
     TOKEN	 sy;
#endif
{
    EXPR	*ep1, *ep2;
    TYP		*tp;

    ep1 = (*xfunc) ();
    if (ep1 == NIL_EXPR)
	return NIL_EXPR;
    while (lastst == sy) {
	getsym();
	ep2 = (*xfunc) ();
	if (ep2) {
	    check_integral(ep1);
	    check_integral(ep2);
	    check_set(ep1);
	    check_set(ep2);
	    check_sequence_accessed(ep1);
	    check_sequence_accessed(ep2);
	    tp = arithmetic_conversion2(&ep1, &ep2);
	    ep1 = mk_node(nt, ep1, ep2, tp);
	} else
	    message(ERR_IDEXPECT);
    }
    ep1 = opt0(ep1);
    return ep1;
}

/*
 * binlog is a common routine to handle all of the legwork and error checking
 * for logical and, or
 */
#ifdef __STDC__
static EXPR *
binlog(EXPR *(*xfunc) (void),  EXPRTYPE nt,  TOKEN sy)
#else
static EXPR *
binlog(xfunc, nt, sy)
    EXPR	*(*xfunc) P_((void));
     EXPRTYPE	 nt;
     TOKEN	 sy;
#endif
{
    EXPR	*ep1, *ep2;

    ep1 = (*xfunc) ();
    if (ep1 == NIL_EXPR)
	return NIL_EXPR;
    while (lastst == sy) {
	sequence_point();
	getsym();
	ep2 = (*xfunc) ();
	if (ep2) {
	    check_scalar(ep1);
	    check_scalar(ep2);
	    check_set(ep1);
	    check_set(ep2);
	    ep1 = condition(ep1);
	    ep2 = condition(ep2);
	    ep1 = mk_node(nt, ep1, ep2, tp_int);
	} else
	    message(ERR_IDEXPECT);
    }
    ep1 = opt0(ep1);
    return ep1;
}

/*
 * the bitwise and operator...
 */
static EXPR *
bitandop P0(void)
{
    return binop(equalops, en_and, tk_and);
}

static EXPR *
bitxor P0(void)
{
    return binop(bitandop, en_xor, tk_uparrow);
}

static EXPR *
bitorop P0(void)
{
    return binop(bitxor, en_or, tk_or);
}

static EXPR *
andop P0(void)
{
    return binlog(bitorop, en_land, tk_land);
}

static EXPR *
orop P0(void)
{
    return binlog(andop, en_lor, tk_lor);
}

/*
 * this routine processes the hook operator.
 */
static EXPR *
conditional P0(void)
{
    TYP		*tp1, *tp2, *tp3;
    EXPR	*ep1, *ep2, *ep3;

    ep1 = orop();		/* get condition */
    if (ep1 == NIL_EXPR)
	return NIL_EXPR;
    if (lastst == tk_hook) {
	check_scalar(ep1);
	check_set(ep1);
	check_sequence_accessed(ep1);
	ep1 = condition(ep1);
	sequence_point();
	getsym();
	if ((ep2 = expression()) == NIL_EXPR) {
	    message(ERR_IDEXPECT);
	    return NIL_EXPR;
	}
	check_set(ep2);
	needpunc(tk_colon);
	if ((ep3 = exprnc()) == NIL_EXPR) {
	    message(ERR_IDEXPECT);
	    return NIL_EXPR;
	}
	check_set(ep3);

	ep2 = opt0(ep2);
	ep3 = opt0(ep3);
	tp2 = ep2->etp;
	tp3 = ep3->etp;
	if (is_arithmetic_type(tp2) && is_arithmetic_type(tp3)) {
	    /*
	     * if both the second and third operands have arithmetic type,
	     * the usual arithmetic conversions are performed to bring them
	     * to a common type and the result has that type
	     */
	    tp1 = arithmetic_conversion2(&ep2, &ep3);
	} else if (is_structure(tp2) && is_structure(tp3) &&
		   is_compatible(tp2,tp3)) {
	    /*
	     * If both the operands have structure or union type, the
	     * result has that type
	     */
	    tp1 = tp2;
	} else if (is_void(tp2) && is_void(tp3)) {
	    /*
	     * If both operands have void type, the result has void type
	     */
	    tp1 = tp2;
	} else if (is_pointer_type(tp2) && is_pointer_type(tp3)) {
	    /*
	     * If both the second and third operands are pointers or one is a
	     * null pointer constant and the other is a pointer, the result
	     * type is a pointer to a type qualified with all the type
	     * qualifiers of the types pointed-to by both operands.
	     *
	     * Furthermore, if both operands are pointers to compatible
	     * types or differently qualified versions of a compatible type,
	     * the result has the composite type;  if one operand is a null
	     * pointer constant, the result has the type of the other operand;
	     * otherwise, one operand is a pointer to void or a qualified
	     * version of void, in which case the other operand is converted
	     * to type pointer to void, and the result has that type.
	     */
	    tp1 = arithmetic_conversion2(&ep2, &ep3);
	    if (is_compatible(tp2, tp3)) {
		set_referenced_type( tp1, qualify_type(referenced_type(tp1),
			referenced_type(tp2)->qual|referenced_type(tp3)->qual));
	    } else
		message(WARN_PTRCAST);
	} else if ((is_pointer_type(tp2) && is_null_pointer(ep3)) ||
		   (is_pointer_type(tp3) && is_null_pointer(ep2))) {
	    tp1 = arithmetic_conversion2(&ep2, &ep3);
	} else {
	    /*
	     * If either type is void and the other is not, cast the other
	     * one to void.  I dare not do this in arithmetic_conversion2.
	     * Strict ANSI does not allow that only one part of the sentence
	     * is void, that is what gcc -pedantic tells me.
	     * But since such conditionals occur even in GNU Software (look at
	     * obstack.h), I allow such constructs here.
	     */
	    if (is_void(tp2) && !is_void(tp3)) {
		ep3 = implicit_castop(ep3, tp2);
		message(WARN_CONDVOID, 3);
	    } else if (is_void(tp3) && !is_void(tp2)) {
		ep2 = implicit_castop(ep2, tp3);
		message(WARN_CONDVOID, 2);
	    } else
		message(ERR_ILLTYPE);
	    tp1 = arithmetic_conversion2(&ep2, &ep3);
	}

        if (tp1 == NIL_TYP)
            return NIL_EXPR;
	ep2 = mk_node(en_list, ep2, ep3, tp_void);
	ep1 = mk_node(en_cond, ep1, ep2, tp1);
    }
    return ep1;
}

/*
 * asnop handles the assignment operators.
 */
static EXPR *
asnop P0(void)
{
    EXPR	*ep1, *ep2, *ep3;
    TYP		*tp;
    EXPRTYPE	 op;
    BOOL	 implicit_cast;

    ep1 = conditional();
    if (ep1 == NIL_EXPR)
	return NIL_EXPR;
    for (;;) {
	switch (lastst) {
	  case tk_asrshift:
	    op = en_asrsh;
	    break;
	  case tk_asand:
	    op = en_asand;
	    break;
	  case tk_asor:
	    op = en_asor;
	    break;
	  case tk_asuparrow:
	    op = en_asxor;
	    break;
	  case tk_aslshift:
	    op = en_aslsh;
	    break;
	  case tk_astimes:
	    op = en_asmul;
	    break;
	  case tk_asdivide:
	    op = en_asdiv;
	    break;
	  case tk_asmod:
	    op = en_asmod;
	    break;
	  case tk_assign:
	    op = en_assign;
	    break;
	  case tk_asminus:
	    op = en_assub;
	    break;
	  case tk_asplus:
	    op = en_asadd;
	    break;
	  default:
	    return ep1;
	}
	getsym();
	ep2 = asnop();
	if (ep2 == NIL_EXPR)
	    continue;
	implicit_cast = TRUE;
	switch (op) {
	  case en_aslsh:
	  case en_asrsh:
	    implicit_cast = FALSE;
	    check_shift(ep2, ep1->etp);	/* should really be promoted type */
	    /*FALLTHRU*/
	  case en_asand:
	  case en_asor:
	  case en_asxor:
	  case en_asmod:
	    /* For operators which must have integral operands */
	    check_integral(ep1);
	    check_integral(ep2);
	    check_set(ep1);
	    check_sequence_modified(ep1);
	    break;

	  case en_asmul:
	  case en_asdiv:
	    check_arithmetic(ep1);
	    check_arithmetic(ep2);
	    check_set(ep1);
	    check_sequence_modified(ep1);
	    break;

	  case en_assign:
	    check_qualifiers(ep1->etp, ep2->etp);
	    set_used(ep1);
	    check_negative(ep1, ep2);
	    break;

	  case en_assub:
	  case en_asadd:
	    check_set(ep1);
	    check_sequence_modified(ep1);
	    tp = ep1->etp;
	    if (is_pointer_type(tp) && is_integral_type(ep2->etp)) {
		check_object(ep1);
		check_complete(ep1->etp);
		ep2 = explicit_castop(ep2, tp_long);
		ep3 = mk_icon((IVAL) referenced_type(tp)->size, tp_long);
		ep2 = mk_node(en_mul, ep2, ep3, tp_long);
		ep2 = explicit_castop(ep2, tp);
	    }
	    break;

	  default:
	    CANNOT_REACH_HERE();
	    break;
	}
	check_set(ep2);
	check_sequence_accessed(ep2);

	if (ep2->etp == NIL_TYP)
	    continue;
	check_modifiable_lvalue(ep1);
	if (implicit_cast) {
	    switch (op) {
#ifdef FLOAT_SUPPORT
	      case en_asmul:
	      case en_asdiv:
		if (is_integral_type(ep1->etp) && is_floating_type(ep2->etp)) {
		    op = (EXPRTYPE)(op+1);
		    break;
		}
		/*FALLTHRU*/
#endif /* FLOAT_SUPPORT */
	     default:
		ep2 = implicit_castop(ep2, ep1->etp);
		break;
	    }
	} else {
	    ep2 = explicit_castop(ep2, ep1->etp);
	}
	ep1->etp = qualify_type(ep1->etp, QUAL_NONE);
	ep1 = mk_node(op, ep1, ep2, ep1->etp);
	/* trap struct assigns */
	switch (ep1->etp->type) {
	  case bt_struct:
	  case bt_union:
	    check_complete(ep1->etp);
#ifdef INTEL_386
	    /*FALLTHRU*/
	  case bt_float:
	  case bt_double:
	  case bt_longdouble:
	    uses_structassign = TRUE;
#endif
	    break;
	  default:
	    break;
	}
    }
}

/*
 * evaluate an expression where the comma operator is not legal.
 */
EXPR *
exprnc P0(void)
{
    return asnop();
}

/*
 * evaluate the comma operator.
 */
static EXPR *
commaop P0(void)
{
    EXPR	*ep1, *ep2;
    ep1 = asnop();
    if (ep1 == NIL_EXPR)
	return NIL_EXPR;
    if (lastst == tk_comma) {
	sequence_point();
	getsym();
	ep2 = commaop();
	if (ep2) {
	    check_discard(ep1);
	    ep1 = mk_node(en_comma, ep1, ep2, ep2->etp);
	} else
	    message(ERR_IDEXPECT);
    }
#ifdef FLOAT_CHECK
    if (fcheck_option && is_floating_type(ep1->etp))
	message(WARN_FLOAT);
#endif /* FLOAT_CHECK */
    return ep1;
}

/*
 * evaluate an expression where all operators are legal.
 */
EXPR *
expression P0(void)
{
    return commaop();
}

static EXPR *
condition P1(EXPR *, ep)
{
    switch (ep->nodetype) {
      case en_land:
      case en_lor:
	ep->v.p[0] = condition(ep->v.p[0]);
	ep->v.p[1] = condition(ep->v.p[1]);
	/*FALLTHRU*/
      case en_eq:
      case en_ne:
      case en_lt:
      case en_le:
      case en_gt:
      case en_ge:
      case en_not:
      case en_test:
	return ep;
      case en_assign:
	message(WARN_ASSIGN);
	/*FALLTHRU*/
      default:
	ep = mk_node(en_test, ep, NIL_EXPR, tp_int);
	return ep;
    }
}

/*
**	Evaluates a conditional expression.   All conditional expression
**	must be scalars.
*/
EXPR *
condition_expression P0(void)
{
    EXPR *ep;
    ep = commaop();
    if (ep == NIL_EXPR) {
	message (ERR_EXPREXPECT) ;
	return ep;
    }
    check_scalar(ep);
    sequence_point();
    return condition(ep);
}

/*
 * evaluate an expression where all operators are legal but result must be
 * an integral value.
 */
EXPR *
integral_expression P0(void)
{
    EXPR	*ep;
    ep = commaop();
    if (ep == NIL_EXPR) {
	message(ERR_EXPREXPECT);
    } else
	check_integral(ep);
    return ep;
}

#ifdef ASM
/*
 * evaluate an ASM expression
 */
EXPR *
asm_expression P0(void)
{
    EXPR	*ep = NIL_EXPR;
    if (lastst == tk_sconst) {
	ep = mk_enode (en_str, tp_pointer);
	ep->v.str = lastsym;
	getsym();
    }
    return ep;
}
#endif /* ASM */

/*
**	Cast the expression 'ep' to type 'tp2'.   The flag parameters
**	is used to specify whether this case was implicit or has been
**	explictly requested in the C source.
*/
EXPR *
castop P3(EXPR*, ep, TYP *, tp2, CAST, flag)
{
    TYP		*tp1;

    if (ep == NIL_EXPR || tp2 == NIL_TYP) {
	message(ERR_CAST);
	return NIL_EXPR;
    }
    ep = opt0(ep);		/* to make a constant really a constant */
    tp1 = ep->etp;

    if (tp1->type == tp2->type) {
	if (is_structure(tp1)) {
	    if (tp1->size != tp2->size)
		message(ERR_CAST);
	}
	if (flag == implicit) {
	    if (is_pointer_type(tp1) &&
		(referenced_type(tp1)->qual > referenced_type(tp2)->qual))
		message (WARN_CASTCONST);
	    if (!is_compatible(tp1, tp2)) {
		if (is_pointer_type(tp1)) {
		    if (!is_null_pointer(ep))
			message (WARN_PTRCAST);
		} else
		    message (WARN_TYPECAST);
	    }
	    if (is_enum(tp2) &&
		(!is_enum(tp1) || (enumtype(tp1) != enumtype(tp2))))
		message (WARN_ENUM);
	} else {
	    /*
	     * An explicit cast may be syntactically relevant:
	     * sizeof("long string") is something else than
	     * sizeof((char *) "long string")!
	     * So no shortcut in this case.
	     * Explicit introduction of en_cast nodes is not
	     * good since this may disturb tst_const etc.,
	     * consider the following statement:
	     *
	     * static char *p = (char *) (void *) 0;
	     *
	     */
	    if (ep->nodetype == en_size)
		ep->v.i = tp2->size;
	    if (is_void(tp2))
		message(WARN_VOID);
	}
	ep->etp = tp2;
	return ep;
    }

    switch (tp1->type) {
      case bt_void:
	/* A cast from void to anything other than void must be an error */
	message (ERR_ILLCAST);
	break;
      case bt_pointer16:
      case bt_pointer32:
	if (is_integral_type(tp2) || is_pointer_type(tp2)  || is_void(tp2))
	    break;
	message (ERR_ILLCAST);
	/*FALLTHRU*/
      default:
	break;
    }

    if (flag == implicit) {
	if (is_pointer_type(tp1)) {
	    message(WARN_TYPECAST);
	} else {
	    if (is_pointer_type(tp2)) {
		if (is_null_pointer(ep)) {
		    message(WARN_NULLCAST);
		} else {
		    message(WARN_TYPECAST);
		}
#ifdef FACIST
	    } else {
		message(WARN_NOIMPLICIT);
#endif /* FACIST */
	    }
	}
    }

    switch (ep->nodetype) {
      case en_icon:
	if (is_integral_type(tp2) || is_pointer_type(tp2) || is_void(tp2)) {
	    IVAL	j = ep->v.i;
	    ep->v.i = strip_icon(j, tp2);
	    if (flag == implicit)
		check_cast(j, ep->v.i, tp2);
	    ep->etp = tp2;
	    return ep;
#ifdef FLOAT_SUPPORT
#ifndef FLOAT_BOOTSTRAP
	} else if (is_floating_type(tp2)) {
	    if (is_unsigned_type (tp1)) {
		UTOF(ep->v.f, ep->v.u);
	    } else {
		LTOF(ep->v.f, ep->v.i);
	    }
	    ep->nodetype = en_fcon;
	    ep->etp = tp2;
	    return ep;
#endif /* FLOAT_BOOTSTRAP */
#endif /* FLOAT_SUPPORT */
	} else {
	    message(ERR_CASTCON);
	    return NIL_EXPR;
	}
#ifdef FLOAT_SUPPORT
#ifndef FLOAT_BOOTSTRAP
      case en_fcon:
	if (is_integral_type(tp2)) {
	    IVAL	j;
	    FTOL(j, ep->v.f);
	    ep->v.i = strip_icon(j, tp2);
	    ep->nodetype = en_icon;
	    ep->etp = tp2;
	    return ep;
	} else if (is_floating_type(tp2)) {
	    ep->etp = tp2;
	    return ep;
	} else {
	    message(ERR_CASTCON);
	    return NIL_EXPR;
	}
#endif /* FLOAT_BOOTSTRAP */
#endif /* FLOAT_SUPPORT */
      default:
	break;
    }
    if(!is_void(tp2) &&
       (is_structure(tp1) || is_structure(tp2))) {
	message(ERR_ILLCAST);
	return ep;
    }
    if (flag == implicit) {
	if (is_pointer_type(tp2) && (tp1->size < tp_pointer->size))
	    message(WARN_SHORTPTR);
	if (!is_void(tp2) && !is_float(tp2) && (tp1->size > tp2->size))
	    message(WARN_NARROWER);
    }
    ep = mk_node(en_cast, ep, NIL_EXPR, tp2);

    return ep;
}

static EXPR *
explicit_castop P2(EXPR *,ep, TYP *, tp2)
{
    return castop(ep, tp2, explicit);
}

EXPR *
implicit_castop P2(EXPR *,ep, TYP *, tp2)
{
    return castop(ep, tp2, implicit);
}

#ifdef TRACE
EXPR *
traceexpr P0(void)
{
    EXPR	*ep1, *ep2;
    SYM         *sp;

    /* create line number parameter */
    ep2 = mk_icon((IVAL)act_line, tp_int);
    ep1 = mk_node(en_list, ep2, NIL_EXPR, tp_void);

    /* create file name parameter */
    ep2 = mk_lcon(stringlit((CHAR *)act_file, strlen(act_file)));
    ep1 = mk_node(en_list, ep2, ep1, tp_void);

    /* function call name */
    sp = internal_symbol ((CHAR *)SUP_TRACE, tp_func);
    ep2 = mk_symnode (sp);

    /* do function call */
    ep1 = mk_node(en_fcall, ep2, ep1, tp_void);
    return ep1;
}
#endif /* TRACE */


/******************************************************************************
**
**	The following routines are used by the code generators to change
**	operator nodes into run-time support function calls.   Previously
**	these calls were performed directly within the code generation
**	routines themselve.   By changing the expression trees themselves
**	it simplifies the code generation routines and also allows the
**	global optimiser to also work on the parameters passed to the
**	support routines.
*/


/*
**	This routine takes a unary operator name 'ep' and changes it
**	to call the function 'name' with the operand as the parameter
**	to the function 
*/
EXPR *
transform_unary P2(EXPR *, ep, CHAR *, name)
{
    ep->v.p[1] = mk_node(en_list, ep->v.p[0], NIL_EXPR, tp_void);
    ep->v.p[0] = mk_symnode( internal_symbol(name, ep->etp));
    ep->nodetype = en_call;
    return ep;
}

/*
**	This routine takes a unary operator 'ep' and changes it
**	to call the function 'name' with the reference to the operand
**	as the parameter to the function 
*/
EXPR *
transform_unary_ref P2(EXPR *, ep, CHAR *, name)
{
    ep->v.p[1] = mk_node(en_list, ep->v.p[0]->v.p[0], NIL_EXPR, tp_void);
    ep->v.p[0] = mk_symnode( internal_symbol(name, ep->etp));
    ep->nodetype = en_call;
    return ep;
}

/*
**	This routine takes a binary operator 'ep' and changes it
**	to call the function 'name' with the operands as the parameters
**	to the function 
*/
EXPR *
transform_binary P2(EXPR *, ep, CHAR *, name)
{
    EXPR *ep1;
    ep1 = mk_node(en_list, ep->v.p[0], NIL_EXPR, tp_void);
    ep1 = mk_node(en_list, ep->v.p[1], ep1, tp_void);
    ep->v.p[1] = ep1;
    ep->v.p[0] = mk_symnode( internal_symbol(name, ep->etp));
    ep->nodetype = en_call;
    return ep;
}

/*
**	This routine takes a binary operator 'ep' and changes it
**	to call the function 'name' with the operands as the parameters
**	to the function - the 1st parameter being a reference to the
**	operand.
*/
EXPR *
transform_binary_ref P2(EXPR *, ep, CHAR *, name)
{
    EXPR *ep1;
    ep1 = mk_node(en_list, ep->v.p[0]->v.p[0], NIL_EXPR, tp_void);
    ep1 = mk_node(en_list, ep->v.p[1], ep1, tp_void);
    ep->v.p[1] = ep1;
    ep->v.p[0] = mk_symnode( internal_symbol(name, ep->etp));
    ep->nodetype = en_call;
    return ep;
}

/*
**	This routine takes a binary assign operator 'ep' and changes it
**	to call the function 'name1' if it is an ordinary assignment
**	and 'name2' if it is to a bitfield, with the operands as the
**	parameters to the function - the 1st parameter being a reference
**	to the operand.
*/
EXPR *
transform_assign P4(EXPR *, ep, CHAR *, name1, CHAR *, name2, CHAR *, name3)
{
    EXPR *ep1;
    EXPR *ep0 = ep->v.p[0];
    TYP *tp = ep->etp;
    SIZE size;
    switch (ep0->nodetype) {
      case en_fieldref:
	/*
	**	The encoding information passed to the support routine
	**	as follows:
	**
	**		F E D C B A 9 8 | 7 6 5 4 3 2 1 0
	**		----------------+----------------
	**		S 0 0 W W W W W | 0 0 0 F F F F F 
	**
	**	S - sign
	**	W - width of the LHS
	**	f - offset of the LHS
	*/
	size = ((((SIZE)ep0->v.bit.width) << 8) |
		((tp_long->size*8L) -
		 (SIZE)(ep0->v.bit.offset) - (SIZE)(ep0->v.bit.width)) |
		 ((SIZE)is_unsigned_type(tp) << 15)) ;
	ep1 = mk_node(en_list, ep0->v.p[0], NIL_EXPR, tp_void);
	ep1 = mk_node(en_list, mk_icon(size, tp_short), ep1, tp_void);
	ep1 = mk_node(en_list, mk_symnode(internal_symbol(name2, tp)), ep1, tp_void);
	ep1 = mk_node(en_list, ep->v.p[1], ep1, tp_void);
	ep->v.p[1] = ep1;
	ep->v.p[0] = mk_symnode( internal_symbol(name3, tp));
	ep->nodetype = en_call;
	break;
      case en_ref:
	ep1 = mk_node(en_list, ep0->v.p[0], NIL_EXPR, tp_void);
	ep1 = mk_node(en_list, ep->v.p[1], ep1, tp_void);
	ep->v.p[1] = ep1;
	ep->v.p[0] = mk_symnode( internal_symbol(name1, tp));
	ep->nodetype = en_call;
	break;
      default:
	CANNOT_REACH_HERE();
    }
    return ep;
}

/*
**	This routine takes a binary assign operator 'ep' and changes it
**	to call the function 'name'.
*/
EXPR *
transform_assign2 P2(EXPR *, ep, CHAR *, name)
{
    EXPR *ep1;
    EXPR *ep0 = ep->v.p[0];
    TYP *tp = ep->etp;
    SIZE size = (tp->size * 8L) << 8;;
    switch (ep0->nodetype) {
      case en_fieldref:
	size = ((((SIZE)ep0->v.bit.width) << 8) |
		((tp_long->size*8L) -
		 (SIZE)(ep0->v.bit.offset) - (SIZE)(ep0->v.bit.width)));
	/*FALLTHRU*/
      case en_ref:
	size |= ((SIZE)is_unsigned_type(tp) << 15);
	/*
	**	The encoding information passed to the support routine
	**	as follows:
	**
	**		F E D C B A 9 8 | 7 6 5 4 3 2 1 0
	**		----------------+----------------
	**		S 0 0 W W W W W | 0 0 0 F F F F F 
	**
	**	S - sign
	**	W - width in bits of the LHS
	**	f - offset in bits of the LHS
	*/
	ep1 = mk_node(en_list, ep0->v.p[0], NIL_EXPR, tp_void);
	ep1 = mk_node(en_list, mk_icon(size, tp_short), ep1, tp_void);
	ep1 = mk_node(en_list, ep->v.p[1], ep1, tp_void);
	ep->v.p[1] = ep1;
	ep->v.p[0] = mk_symnode( internal_symbol(name, tp));
	ep->nodetype = en_call;
	break;
      default:
	CANNOT_REACH_HERE();
    }
    return ep;
}
