/*
 * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
 * See the copyright notice in the ACK home directory, in the file "Copyright".
 *
 * Author: Ceriel J.H. Jacobs
 */

/* E X P R E S S I O N   C H E C K I N G */

/* $Header$ */

/*	Check expressions, and try to evaluate them as far as possible.
*/

#include	"debug.h"

#include	<em_arith.h>
#include	<em_label.h>
#include	<assert.h>
#include	<alloc.h>

#include	"strict3rd.h"
#include	"Lpars.h"
#include	"idf.h"
#include	"type.h"
#include	"LLlex.h"
#include	"def.h"
#include	"node.h"
#include	"scope.h"
#include	"standards.h"
#include	"chk_expr.h"
#include	"misc.h"
#include	"warning.h"
#include	"main.h"
#include	"nostrict.h"

extern char *symbol2str();
extern char *sprint();
extern arith flt_flt2arith();

STATIC
df_error(nd, mess, edf)
	t_node		*nd;		/* node on which error occurred */
	char		*mess;		/* error message */
	register t_def	*edf;		/* do we have a name? */
{
	if (edf) {
		if (edf->df_kind != D_ERROR)  {
			node_error(nd,"\"%s\": %s", edf->df_idf->id_text, mess);
		}
	}
	else node_error(nd, mess);
}

MkCoercion(pnd, tp)
	t_node		**pnd;
	register t_type	*tp;
{
	/*	Make a coercion from the node indicated by *pnd to the
		type indicated by tp. If the node indicated by *pnd
		is constant, try to do the coercion compile-time.
		Coercions are inserted in the tree when
		- the expression is not constant or
		- we are in the second pass and the coercion might cause
		  an error
	*/
	register t_node	*nd = *pnd;
	register t_type	*nd_tp = nd->nd_type;
	extern int	pass_1;
	char		*wmess = 0;
	arith		op;

	if (nd_tp == tp || nd_tp->tp_fund == T_STRING /* Why ??? */) return;
	nd_tp = BaseType(nd_tp);
	if (nd->nd_class == Value) {
		if (nd_tp->tp_fund == T_REAL) {
			switch(tp->tp_fund) {
			case T_REAL:
				nd->nd_type = tp;
				return;
			case T_CARDINAL:
				op = flt_flt2arith(&nd->nd_RVAL, 1);
				break;
			case T_INTEGER:
				op = flt_flt2arith(&nd->nd_RVAL, 0);
				break;
			default:
				crash("MkCoercion");
				/*NOTREACHED*/
			}
			if (flt_status == FLT_OVFL) {
				wmess = "conversion";
			}
			if (!wmess || pass_1) {
				if (nd->nd_RSTR) free(nd->nd_RSTR);
				free_real(nd->nd_REAL);
				nd->nd_INT = op;
				nd->nd_symb = INTEGER;
			}
		}
		switch(tp->tp_fund) {
		case T_REAL: {
			struct real *p = new_real();
			switch(BaseType(nd_tp)->tp_fund) {
			case T_CARDINAL:
			case T_INTORCARD:
				flt_arith2flt(nd->nd_INT, &p->r_val, 1);
				break;
			case T_INTEGER:
				flt_arith2flt(nd->nd_INT, &p->r_val, 0);
				break;
			default:
				crash("MkCoercion");
			}
			nd->nd_REAL = p;
			nd->nd_symb = REAL;
			}
			break;
		case T_SUBRANGE:
		case T_ENUMERATION:
		case T_CHAR:
			if (! in_range(nd->nd_INT, tp)) {
				wmess = "range bound";
			}
			break;
		case T_INTORCARD:
		case T_CARDINAL:
		case T_POINTER:
			if ((nd_tp->tp_fund == T_INTEGER && nd->nd_INT < 0) ||
			    (nd->nd_INT & ~full_mask[(int)(tp->tp_size)])) {
				wmess = "conversion";
			}
			break;
		case T_INTEGER:
			if (! chk_bounds(nd->nd_INT,
					 max_int[(int)(tp->tp_size)],
					 nd_tp->tp_fund) ||
			    ! chk_bounds(min_int[(int)(tp->tp_size)],
					 nd->nd_INT,
					 T_INTEGER)) {
				wmess = "conversion";
			}
			break;
		}
		if (wmess) {
		   node_warning(nd, W_ORDINARY, "might cause %s error", wmess);
		}
		if (!wmess || pass_1) {
			nd->nd_type = tp;
			return;
		}
	}
	*pnd = nd;
	nd = getnode(Uoper);
	nd->nd_symb = COERCION;
	nd->nd_type = tp;
	nd->nd_LEFT = NULLNODE;
	nd->nd_RIGHT = *pnd;
	nd->nd_lineno = (*pnd)->nd_lineno;
	*pnd = nd;
}

int
ChkVariable(expp, flags)
	register t_node **expp;
{
	/*	Check that "expp" indicates an item that can be
		assigned to.
	*/
	register t_node *exp;

	if (! ChkDesig(expp, flags)) return 0;

	exp = *expp;
	if (exp->nd_class == Def &&
	    ! (exp->nd_def->df_kind & (D_FIELD|D_VARIABLE))) {
		df_error(exp, "variable expected", exp->nd_def);
		return 0;
	}
	return 1;
}

STATIC int
ChkArrow(expp)
	t_node **expp;
{
	/*	Check an application of the '^' operator.
		The operand must be a variable of a pointer type.
	*/
	register t_type *tp;
	register t_node *exp = *expp;

	assert(exp->nd_class == Arrow);
	assert(exp->nd_symb == '^');

	exp->nd_type = error_type;

	if (! ChkVariable(&(exp->nd_RIGHT), D_USED)) return 0;

	tp = exp->nd_RIGHT->nd_type;

	if (tp->tp_fund != T_POINTER) {
		node_error(exp, "\"^\": illegal operand type");
		return 0;
	}

	if ((tp = RemoveEqual(PointedtoType(tp))) == 0) tp = error_type;
	exp->nd_type = tp;
	return 1;
}

STATIC int
ChkArr(expp, flags)
	t_node **expp;
{
	/*	Check an array selection.
		The left hand side must be a variable of an array type,
		and the right hand side must be an expression that is
		assignment compatible with the array-index.
	*/

	register t_type *tpl;
	register t_node *exp = *expp;

	assert(exp->nd_class == Arrsel);
	assert(exp->nd_symb == '[' || exp->nd_symb == ',');

	exp->nd_type = error_type;

	if (! (ChkVariable(&(exp->nd_LEFT), flags) &
	       ChkExpression(&(exp->nd_RIGHT)))) {
		/* Bitwise and, because we want them both evaluated.
		*/
		return 0;
	}

	tpl = exp->nd_LEFT->nd_type;

	if (tpl->tp_fund != T_ARRAY) {
		node_error(exp, "not indexing an ARRAY type");
		return 0;
	}
	exp->nd_type = RemoveEqual(tpl->arr_elem);

	/* Type of the index must be assignment compatible with
	   the index type of the array (Def 8.1).
	   However, the index type of a conformant array is not specified.
	   In our implementation it is CARDINAL.
	*/
	return ChkAssCompat(&(exp->nd_RIGHT),
			    BaseType(IndexType(tpl)),
			    "index type");
}

/*ARGSUSED*/
STATIC int
ChkValue(expp)
	t_node **expp;
{
#ifdef DEBUG
	switch((*expp)->nd_symb) {
	case REAL:
	case STRING:
	case INTEGER:
		break;

	default:
		crash("(ChkValue)");
	}
#endif
	return 1;
}

STATIC int
ChkSelOrName(expp, flags)
	t_node **expp;
{
	/*	Check either an ID or a construction of the form
		ID.ID [ .ID ]*
	*/
	register t_def *df;
	register t_node *exp = *expp;

	exp->nd_type = error_type;

	if (exp->nd_class == Name) {
		df = lookfor(exp, CurrVis, 1, flags);
		exp = getnode(Def);
		exp->nd_def = df;
		exp->nd_lineno = (*expp)->nd_lineno;
		exp->nd_type = RemoveEqual(df->df_type);
		FreeNode(*expp);
		*expp = exp;
	}
	else if (exp->nd_class == Select) {
		/*	A selection from a record or a module.
			Modules also have a record type.
		*/
		register t_node *left;

		assert(exp->nd_symb == '.');

		if (! ChkDesig(&(exp->nd_NEXT), flags)) return 0;

		left = exp->nd_NEXT;
		if (left->nd_class==Def &&
		    (left->nd_type->tp_fund != T_RECORD ||
		    !(left->nd_def->df_kind & (D_MODULE|D_VARIABLE|D_FIELD))
		    )
		   ) {
			df_error(left, "illegal selection", left->nd_def);
			return 0;
		}
		if (left->nd_type->tp_fund != T_RECORD) {
			node_error(left, "illegal selection");
			return 0;
		}

		if (!(df = lookup(exp->nd_IDF, left->nd_type->rec_scope, D_IMPORTED, flags))) {
			id_not_declared(exp);
			return 0;
		}
		exp = getnode(Def);
		exp->nd_def = df;
		exp->nd_type = RemoveEqual(df->df_type);
		exp->nd_lineno = (*expp)->nd_lineno;
		free_node(*expp);
		*expp = exp;
		if (!(df->df_flags & (D_EXPORTED|D_QEXPORTED))) {
			/* Fields of a record are always D_QEXPORTED,
			   so ...
			*/
			df_error(exp, "not exported from qualifying module", df);
		}

		if (!(left->nd_class == Def &&
		      left->nd_def->df_kind == D_MODULE)) {
			exp->nd_NEXT = left;
			return 1;
		}
		FreeNode(left);
	}

	assert(exp->nd_class == Def);

	return exp->nd_def->df_kind != D_ERROR;
}

STATIC int
ChkExSelOrName(expp)
	t_node **expp;
{
	/*	Check either an ID or an ID.ID [.ID]* occurring in an
		expression.
	*/
	register t_def *df;
	register t_node *exp;

	if (! ChkSelOrName(expp, D_USED)) return 0;

	exp = *expp;

	df = exp->nd_def;

	if (df->df_kind & (D_ENUM | D_CONST)) {
		/* Replace an enum-literal or a CONST identifier by its value.
		*/
		exp = getnode(Value);
		exp->nd_type = df->df_type;
		if (df->df_kind == D_ENUM) {
			exp->nd_INT = df->enm_val;
			exp->nd_symb = INTEGER;
		}
		else  {
			assert(df->df_kind == D_CONST);
			exp->nd_token = df->con_const;
		}
		exp->nd_lineno = (*expp)->nd_lineno;
		if (df->df_type->tp_fund == T_SET) {
			exp->nd_class = Set;
			inc_refcount(exp->nd_set);
		}
		else if (df->df_type->tp_fund == T_PROCEDURE) {
			/* for procedure constants */
			exp->nd_class = Def;
		}
		if (df->df_type->tp_fund == T_REAL) {
			struct real *p = exp->nd_REAL;

			exp->nd_REAL = new_real();
			*(exp->nd_REAL) = *p;
			if (p->r_real) {
				p->r_real = Salloc(p->r_real,
					   (unsigned)(strlen(p->r_real)+1));
			}
		}
		FreeNode(*expp);
		*expp = exp;
	}

	if (!(df->df_kind & D_VALUE)) {
		df_error(exp, "value expected", df);
		return 0;
	}

	if (df->df_kind == D_PROCEDURE) {
		/* Check that this procedure is one that we may take the
		   address from.
		*/
		if (df->df_type == std_type || df->df_scope->sc_level > 0) {
			/* Address of standard or nested procedure
			   taken.
			*/
			node_error(exp,
			   "standard or local procedures may not be assigned");
			return 0;
		}
	}

	return 1;
}

STATIC int
ChkEl(expp, tp)
	register t_node **expp;
	t_type *tp;
{

	return ChkExpression(expp) && ChkCompat(expp, tp, "set element");
}

STATIC int
ChkElement(expp, tp, set)
	t_node **expp;
	t_type *tp;
	arith *set;
{
	/*	Check elements of a set. This routine may call itself
		recursively.
		Also try to compute the set!
	*/
	register t_node *expr = *expp;
	t_type *el_type = ElementType(tp);
	register unsigned int i;
	arith low, high;

	if (expr->nd_class == Link && expr->nd_symb == UPTO) {
		/* { ... , expr1 .. expr2,  ... }
		   First check expr1 and expr2, and try to compute them.
		*/
		if (! (ChkEl(&(expr->nd_LEFT), el_type) & 
		       ChkEl(&(expr->nd_RIGHT), el_type))) {
			return 0;
		}

		if (!(expr->nd_LEFT->nd_class == Value &&
		      expr->nd_RIGHT->nd_class == Value)) {
			return 1;
		}
		/* We have a constant range. Put all elements in the
		  set
		*/

		low = expr->nd_LEFT->nd_INT;
		high = expr->nd_RIGHT->nd_INT;
	}
	else {
		if (! ChkEl(expp, el_type)) return 0;
		expr = *expp;
		if (expr->nd_class != Value) {
			return 1;
		}
		low = high = expr->nd_INT;
	}
	if (! chk_bounds(low, high, BaseType(el_type)->tp_fund)) {
		node_error(expr, "lower bound exceeds upper bound in range");
		return 0;
	}

	if (! in_range(low, el_type) || ! in_range(high, el_type)) {
		node_error(expr, "set element out of range");
		return 0;
	}

	low -= tp->set_low;
	high -= tp->set_low;
	for (i=(unsigned)low; i<= (unsigned)high; i++) {
		set[i/wrd_bits] |= (1<<(i%wrd_bits));
	}
	FreeNode(expr);
	*expp = 0;
	return 1;
}

arith *
MkSet(size)
	unsigned size;
{
	register arith	*s, *t;

	s = t = (arith *) Malloc(size);
	s++;
	size /= sizeof(arith);
	while (size--) *t++ = 0;
	inc_refcount(s);
	return s;
}

FreeSet(s)
	register arith *s;
{
	dec_refcount(s);
	if (refcount(s) <= 0) {
		assert(refcount(s) == 0);
		free((char *) (s-1));
	}
}

STATIC int
ChkSet(expp)
	t_node **expp;
{
	/*	Check the legality of a SET aggregate, and try to evaluate it
		compile time. Unfortunately this is all rather complicated.
	*/
	register t_type *tp;
	register t_node *exp = *expp;
	register t_node *nd;
	register t_def *df;
	int retval = 1;
	int SetIsConstant = 1;

	assert(exp->nd_symb == SET);

	*expp = getnode(Set);
	(*expp)->nd_type = error_type;
	(*expp)->nd_lineno = exp->nd_lineno;

	/* First determine the type of the set
	*/
	if (exp->nd_LEFT) {
		/* A type was given. Check it out
		*/
		if (! ChkDesig(&(exp->nd_LEFT), D_USED)) return 0;
		nd = exp->nd_LEFT;
		assert(nd->nd_class == Def);
		df = nd->nd_def;

		if (!is_type(df) ||
	   	    (df->df_type->tp_fund != T_SET)) {
			df_error(nd, "not a SET type", df);
			return 0;
		}
		tp = df->df_type;
	}
	else	tp = bitset_type;
	(*expp)->nd_type = tp;

	nd = exp->nd_RIGHT;

	/* Now check the elements given, and try to compute a constant set.
	   First allocate room for the set.
	*/

	(*expp)->nd_set = MkSet(tp->set_sz);

	/* Now check the elements, one by one
	*/
	while (nd) {
		assert(nd->nd_class == Link && nd->nd_symb == ',');

		if (!ChkElement(&(nd->nd_LEFT), tp, (*expp)->nd_set)) {
			retval = 0;
		}
		if (nd->nd_LEFT) SetIsConstant = 0;
		nd = nd->nd_RIGHT;
	}

	if (! SetIsConstant) {
		(*expp)->nd_NEXT = exp->nd_RIGHT;
		exp->nd_RIGHT = 0;
	}
	FreeNode(exp);
	return retval;
}

STATIC t_node *
nextarg(argp, edf)
	t_node **argp;
	t_def *edf;
{
	register t_node *arg = (*argp)->nd_RIGHT;

	if (! arg) {
		df_error(*argp, "too few arguments supplied", edf);
		return 0;
	}

	*argp = arg;
	return arg;
}

STATIC t_node *
getarg(argp, bases, designator, edf)
	t_node **argp;
	t_def *edf;
{
	/*	This routine is used to fetch the next argument from an
		argument list. The argument list is indicated by "argp".
		The parameter "bases" is a bitset indicating which types
		are allowed at this point, and "designator" is a flag
		indicating that the address from this argument is taken, so
		that it must be a designator and may not be a register
		variable.
	*/
	register t_node *arg = nextarg(argp, edf);
	register t_node *left;

	if (! arg ||
	    ! arg->nd_LEFT ||
	    ! (designator ? ChkVariable(&(arg->nd_LEFT), D_USED|D_DEFINED) : ChkExpression(&(arg->nd_LEFT)))) {
		return 0;
	}
	left = arg->nd_LEFT;

	if (designator && left->nd_class==Def) {
		left->nd_def->df_flags |= D_NOREG;
	}

	if (bases) {
		t_type *tp = BaseType(left->nd_type);

		if (! designator) MkCoercion(&(arg->nd_LEFT), tp);
		left = arg->nd_LEFT;
		if (!(tp->tp_fund & bases)) {
			df_error(left, "unexpected parameter type", edf);
			return 0;
		}
	}

	return left;
}

STATIC t_node *
getname(argp, kinds, bases, edf)
	t_node **argp;
	t_def *edf;
{
	/*	Get the next argument from argument list "argp".
		The argument must indicate a definition, and the
		definition kind must be one of "kinds".
	*/
	register t_node *arg = nextarg(argp, edf);
	register t_node *left;

	if (!arg || !arg->nd_LEFT || ! ChkDesig(&(arg->nd_LEFT), D_USED)) return 0;

	left = arg->nd_LEFT;
	if (left->nd_class != Def) {
		df_error(left, "identifier expected", edf);
		return 0;
	}

	if (!(left->nd_def->df_kind & kinds) ||
	    (bases && !(left->nd_type->tp_fund & bases))) {
		df_error(left, "unexpected parameter type", edf);
		return 0;
	}

	return left;
}

STATIC int
ChkProcCall(exp)
	register t_node *exp;
{
	/*	Check a procedure call
	*/
	register t_node *left;
	t_node *argp;
	t_def *edf = 0;
	register t_param *param;
	int retval = 1;
	int cnt = 0;

	left = exp->nd_LEFT;
	if (left->nd_class == Def) {
		edf = left->nd_def;
	}
	if (left->nd_type == error_type) {
		/* Just check parameters as if they were value parameters
		*/
		argp = exp;
		while (argp->nd_RIGHT) {
			if (getarg(&argp, 0, 0, edf)) { }
		}
		return 0;
	}

	exp->nd_type = RemoveEqual(ResultType(left->nd_type));

	/* Check parameter list
	*/
	argp = exp;
	for (param = ParamList(left->nd_type); param; param = param->par_next) {
		if (!(left = getarg(&argp, 0, IsVarParam(param), edf))) {
			retval = 0;
			cnt++;
			continue;
		}
		cnt++;
		if (left->nd_symb == STRING) {
			TryToString(left, TypeOfParam(param));
		}
		if (! TstParCompat(cnt,
				   RemoveEqual(TypeOfParam(param)),
				   IsVarParam(param),
				   &(argp->nd_LEFT),
				   edf)) {
			retval = 0;
		}
	}

	exp = argp;
	if (exp->nd_RIGHT) {
		df_error(exp->nd_RIGHT,"too many parameters supplied",edf);
		while (argp->nd_RIGHT) {
			if (getarg(&argp, 0, 0, edf)) { }
		}
		return 0;
	}

	return retval;
}

STATIC int
ChkFunCall(expp)
	register t_node **expp;
{
	/*	Check a call that must have a result
	*/

	if (ChkCall(expp)) {
		if ((*expp)->nd_type != 0) return 1;
		node_error(*expp, "function call expected");
	}
	(*expp)->nd_type = error_type;
	return 0;
}

STATIC int ChkStandard();
STATIC int ChkCast();

int
ChkCall(expp)
	t_node **expp;
{
	/*	Check something that looks like a procedure or function call.
		Of course this does not have to be a call at all,
		it may also be a cast or a standard procedure call.
	*/

	/* First, get the name of the function or procedure
	*/
	if (ChkDesig(&((*expp)->nd_LEFT), D_USED)) {
		register t_node *left = (*expp)->nd_LEFT;
		
		if (IsCast(left)) {
			/* It was a type cast.
			*/
			return ChkCast(expp);
		}

		if (IsProc(left) || left->nd_type == error_type) {
			/* A procedure call.
		   	   It may also be a call to a standard procedure
			*/
			if (left->nd_type == std_type) {
				/* A standard procedure
				*/
				return ChkStandard(expp);
			}
			/* Here, we have found a real procedure call. 
			   The left hand side may also represent a procedure
			   variable.
			*/
		}
		else {
			node_error(left, "procedure, type, or function expected");
			left->nd_type = error_type;
		}
	}
	return ChkProcCall(*expp);
}

STATIC t_type *
ResultOfOperation(operator, tp)
	t_type *tp;
{
	/*	Return the result type of the binary operation "operator",
		with operand type "tp".
	*/

	switch(operator) {
	case '=':
	case '#':
	case GREATEREQUAL:
	case LESSEQUAL:
	case '<':
	case '>':
	case IN:
		return bool_type;
	}

	return tp;
}

#define Boolean(operator) (operator == OR || operator == AND)

STATIC int
AllowedTypes(operator)
{
	/*	Return a bit mask indicating the allowed operand types
		for binary operator "operator".
	*/

	switch(operator) {
	case '+':
	case '-':
	case '*':
		return T_NUMERIC|T_SET;
	case '/':
		return T_REAL|T_SET;
	case DIV:
	case MOD:
		return T_INTORCARD;
	case OR:
	case AND:
		return T_ENUMERATION;
	case '=':
	case '#':
		return T_POINTER|T_HIDDEN|T_SET|T_NUMERIC|T_ENUMERATION|T_CHAR;
	case GREATEREQUAL:
	case LESSEQUAL:
		return T_SET|T_NUMERIC|T_CHAR|T_ENUMERATION;
	case '<':
	case '>':
		return T_NUMERIC|T_CHAR|T_ENUMERATION;
	default:
		crash("(AllowedTypes)");
	}
	/*NOTREACHED*/
}

STATIC int
ChkAddressOper(tpl, tpr, expp)
	register t_type *tpl, *tpr;
	register t_node *expp;
{
	/*	Check that either "tpl" or "tpr" are both of type
		address_type, or that one of them is, but the other is
		of a cardinal type.
		Also insert proper coercions, making sure that the EM pointer
		arithmetic instructions can be generated whenever possible
	*/

	if (tpr == address_type && expp->nd_symb == '+') {
		/* use the fact that '+' is a commutative operator */
		t_type *tmptype = tpr;
		t_node *tmpnode = expp->nd_RIGHT;

		tpr = tpl;
		expp->nd_RIGHT = expp->nd_LEFT;
		tpl = tmptype;
		expp->nd_LEFT = tmpnode;
	}
	
	if (tpl == address_type) {
		expp->nd_type = address_type;
		if (tpr == address_type) {
			return 1;
		}
		if (tpr->tp_fund & T_CARDINAL) {
			MkCoercion(&(expp->nd_RIGHT),
				   expp->nd_symb=='+' || expp->nd_symb=='-' ?
					tpr :
				  	address_type);
			return 1;
		}
		return 0;
	}

	if (tpr == address_type && tpl->tp_fund & T_CARDINAL) {
		expp->nd_type = address_type;
		MkCoercion(&(expp->nd_LEFT), address_type);
		return 1;
	}

	return 0;
}

STATIC int
ChkBinOper(expp)
	t_node **expp;
{
	/*	Check a binary operation.
	*/
	register t_node *exp = *expp;
	register t_type *tpl, *tpr;
	t_type *result_type;
	int allowed;
	int retval;
	char *symb;

	/* First, check BOTH operands */

	retval = ChkExpression(&(exp->nd_LEFT));
	retval &= ChkExpression(&(exp->nd_RIGHT));

	tpl = BaseType(exp->nd_LEFT->nd_type);
	tpr = BaseType(exp->nd_RIGHT->nd_type);

	if (intorcard(tpl, tpr) != 0) {
		if (tpl == intorcard_type) {
			 exp->nd_LEFT->nd_type = tpl = tpr;
		}
		if (tpr == intorcard_type) {
			exp->nd_RIGHT->nd_type = tpr = tpl;
		}
	}

	exp->nd_type = result_type = ResultOfOperation(exp->nd_symb, tpr);

	/* Check that the application of the operator is allowed on the type
	   of the operands.
	   There are three tricky parts:
	   - Boolean operators are only allowed on boolean operands, but
	     the "allowed-mask" of "AllowedTypes" can only indicate
	     an enumeration type.
	   - All operations that are allowed on CARDINALS are also allowed
	     on ADDRESS.
	   - The IN-operator has as right-hand-size operand a set.
	*/
	if (exp->nd_symb == IN) {
		if (tpr->tp_fund != T_SET) {
			node_error(exp, "\"IN\": right operand must be a set");
			return 0;
		}
		if (!TstAssCompat(ElementType(tpr), tpl)) {
			/* Assignment compatible ???
			   I don't know! Should we be allowed to check
			   if a INTEGER is a member of a BITSET???
			*/
			node_error(exp->nd_LEFT, "type incompatibility in IN");
			return 0;
		}
		MkCoercion(&(exp->nd_LEFT), word_type);
		if (exp->nd_LEFT->nd_class == Value &&
		    exp->nd_RIGHT->nd_class == Set &&
		    ! exp->nd_RIGHT->nd_NEXT) {
			cstset(expp);
		}
		return retval;
	}

	if (!retval) return 0;

	allowed = AllowedTypes(exp->nd_symb);

	symb = symbol2str(exp->nd_symb);
	if (!(tpr->tp_fund & allowed) || !(tpl->tp_fund & allowed)) {
	    	if (!((T_CARDINAL & allowed) &&
	             ChkAddressOper(tpl, tpr, exp))) {
			node_error(exp, "\"%s\": illegal operand type(s)", symb);
			return 0;
		}
		if (result_type == bool_type) exp->nd_type = bool_type;
	}
	else {
		if (Boolean(exp->nd_symb) && tpl != bool_type) {
			node_error(exp, "\"%s\": illegal operand type(s)", symb);
			return 0;
		}

		/* Operands must be compatible (distilled from Def 8.2)
		*/
		if (!TstCompat(tpr, tpl)) {
			extern char *incompat();
			node_error(exp, "\"%s\": %s in operands", symb, incompat(tpl, tpr));
			return 0;
		}

		MkCoercion(&(exp->nd_LEFT), tpl);
		MkCoercion(&(exp->nd_RIGHT), tpr);
	}

	if (tpl->tp_fund == T_SET) {
	    	if (exp->nd_LEFT->nd_class == Set &&
		    ! exp->nd_LEFT->nd_NEXT &&
		    exp->nd_RIGHT->nd_class == Set &&
			! exp->nd_RIGHT->nd_NEXT) {
			cstset(expp);
		}
	}
	else if ( exp->nd_LEFT->nd_class == Value &&
		  exp->nd_RIGHT->nd_class == Value) {
		if (tpl->tp_fund == T_INTEGER) {
			cstibin(expp);
		}
		else if (tpl->tp_fund == T_REAL) {
			cstfbin(expp);
		}
		else	cstubin(expp);
	}

	return 1;
}

STATIC int
ChkUnOper(expp)
	t_node **expp;
{
	/*	Check an unary operation.
	*/
	register t_node *exp = *expp;
	register t_node *right = exp->nd_RIGHT;
	register t_type *tpr;

	if (exp->nd_symb == COERCION) return 1;
	if (exp->nd_symb == '(') {
		*expp = right;
		free_node(exp);
		return ChkExpression(expp);
	}
	exp->nd_type = error_type;
	if (! ChkExpression(&(exp->nd_RIGHT))) return 0;
	exp->nd_type = tpr = BaseType(exp->nd_RIGHT->nd_type);
	MkCoercion(&(exp->nd_RIGHT), tpr);
	right = exp->nd_RIGHT;

	if (tpr == address_type) tpr = card_type;

	switch(exp->nd_symb) {
	case '+':
		if (!(tpr->tp_fund & T_NUMERIC)) break;
		*expp = right;
		free_node(exp);
		return 1;

	case '-':
		if (tpr->tp_fund == T_INTORCARD || tpr->tp_fund == T_INTEGER) {
			if (tpr == intorcard_type) {
				exp->nd_type = int_type;
			}
			if (right->nd_class == Value) {
				cstunary(expp);
			}
			return 1;
		}
		else if (tpr->tp_fund == T_REAL) {
			if (right->nd_class == Value) {
				*expp = right;
				flt_umin(&(right->nd_RVAL));
				if (right->nd_RSTR) {
					free(right->nd_RSTR);
					right->nd_RSTR = 0;
				}
				free_node(exp);
			}
			return 1;
		}
		break;

	case NOT:
	case '~':
		if (tpr == bool_type) {
			if (right->nd_class == Value) {
				cstunary(expp);
			}
			return 1;
		}
		break;

	default:
		crash("ChkUnOper");
	}
	node_error(exp, "\"%s\": illegal operand type", symbol2str(exp->nd_symb));
	return 0;
}

STATIC t_node *
getvariable(argp, edf, flags)
	t_node **argp;
	t_def *edf;
{
	/*	Get the next argument from argument list "argp".
		It must obey the rules of "ChkVariable".
	*/
	register t_node *arg = nextarg(argp, edf);

	if (! arg ||
	    ! arg->nd_LEFT ||
	    ! ChkVariable(&(arg->nd_LEFT), flags)) return 0;

	return arg->nd_LEFT;
}

STATIC int
ChkStandard(expp)
	t_node **expp;
{
	/*	Check a call of a standard procedure or function
	*/
	register t_node *exp = *expp;
	t_node *arglink = exp;
	register t_node *arg;
	register t_def *edf = exp->nd_LEFT->nd_def;
	int free_it = 0;
	int isconstant = 0;

	assert(exp->nd_LEFT->nd_class == Def);

	exp->nd_type = error_type;
	switch(edf->df_value.df_stdname) {
	case S_ABS:
		if (!(arg = getarg(&arglink, T_NUMERIC, 0, edf))) return 0;
		exp->nd_type = BaseType(arg->nd_type);
		MkCoercion(&(arglink->nd_LEFT), exp->nd_type);
		arg = arglink->nd_LEFT;
		if (! (exp->nd_type->tp_fund & (T_INTEGER|T_REAL))) {
			free_it = 1;
		}
		if (arg->nd_class == Value) {
			switch(exp->nd_type->tp_fund) {
			case T_REAL:
				arg->nd_RVAL.flt_sign = 0;
				free_it = 1;
				break;
			case T_INTEGER:
				isconstant = 1;
				break;
			}
		}
		break;

	case S_CAP:
		exp->nd_type = char_type;
		if (!(arg = getarg(&arglink, T_CHAR, 0, edf))) return 0;
		if (arg->nd_class == Value) isconstant = 1;
		break;

	case S_FLOATD:
	case S_FLOAT:
		if (! getarg(&arglink, T_INTORCARD, 0, edf)) return 0;
		arg = arglink;
		if (edf->df_value.df_stdname == S_FLOAT) {
			MkCoercion(&(arg->nd_LEFT), card_type);
		}
		MkCoercion(&(arg->nd_LEFT),
			   edf->df_value.df_stdname == S_FLOATD ?
				longreal_type :
				real_type);
		free_it = 1;
		break;

	case S_SHORT:
	case S_LONG: {
		t_type *tp;
		t_type *s1, *s2, *d1, *d2;

		if (!(arg = getarg(&arglink, 0, 0, edf))) {
			return 0;
		}
		tp = BaseType(arg->nd_type);

		if (edf->df_value.df_stdname == S_SHORT) {
			s1 = longint_type;
			d1 = int_type;
			s2 = longreal_type;
			d2 = real_type;
		}
		else {
			d1 = longint_type;
			s1 = int_type;
			d2 = longreal_type;
			s2 = real_type;
		}

		if (tp == s1) {
			MkCoercion(&(arglink->nd_LEFT), d1);
		}
		else if (tp == s2) {
			MkCoercion(&(arglink->nd_LEFT), d2);
		}
		else {
			df_error(arg, "unexpected parameter type", edf);
			break;
		}
		free_it = 1;
		break;
		}

	case S_HIGH:
		if (!(arg = getarg(&arglink, T_ARRAY|T_STRING|T_CHAR, 0, edf))) {
			return 0;
		}
		if (arg->nd_type->tp_fund == T_ARRAY) {
			exp->nd_type = IndexType(arg->nd_type);
			if (! IsConformantArray(arg->nd_type)) {
				arg->nd_type = exp->nd_type;
				isconstant = 1;
			}
			break;
		}
		if (arg->nd_symb != STRING) {
			df_error(arg,"array parameter expected", edf);
			return 0;
		}
		exp = getnode(Value);
		exp->nd_type = card_type;
		/* Notice that we could disallow HIGH("") here by checking
		   that arg->nd_type->tp_fund != T_CHAR || arg->nd_INT != 0.
		   ??? For the time being, we don't. !!!
		   Maybe the empty string should not be allowed at all.
		*/
		exp->nd_INT = arg->nd_type->tp_fund == T_CHAR ? 0 :
					arg->nd_SLE - 1;
		exp->nd_symb = INTEGER;
		exp->nd_lineno = (*expp)->nd_lineno;
		(*expp)->nd_RIGHT = 0;
		FreeNode(*expp);
		*expp = exp;
		break;

	case S_MAX:
	case S_MIN:
		if (!(arg = getname(&arglink, D_ISTYPE, T_DISCRETE, edf))) {
			return 0;
		}
		exp->nd_type = arg->nd_type;
		isconstant = 1;
		break;

	case S_ODD:
		if (! (arg = getarg(&arglink, T_INTORCARD, 0, edf))) return 0;
		MkCoercion(&(arglink->nd_LEFT), BaseType(arg->nd_type));
		exp->nd_type = bool_type;
		if (arglink->nd_LEFT->nd_class == Value) isconstant = 1;
		break;

	case S_ORD:
		if (! (arg = getarg(&arglink, T_NOSUB, 0, edf))) return 0;
		exp->nd_type = card_type;
		if (arg->nd_class == Value) {
			arg->nd_type = card_type;
			free_it = 1;
		}
		break;

#ifndef STRICT_3RD_ED
	case S_NEW:
	case S_DISPOSE:
		{
			static int warning_given = 0;

			if (!warning_given) {
				warning_given = 1;
				if (! options['3'])
	node_warning(exp, W_OLDFASHIONED, "NEW and DISPOSE are obsolete");
				else
	node_error(exp, "NEW and DISPOSE are obsolete");
			}
		}
		exp->nd_type = 0;
		arg = getvariable(&arglink, edf, D_USED|D_DEFINED);
		if (! arg) return 0;
		if (! (arg->nd_type->tp_fund == T_POINTER)) {
			df_error(arg, "pointer variable expected", edf);
			return 0;
		}
		/* Now, make it look like a call to ALLOCATE or DEALLOCATE */
		arglink->nd_RIGHT = arg = getnode(Link);
		arg->nd_lineno = exp->nd_lineno;
		arg->nd_symb = ',';
		arg->nd_LEFT = getnode(Value);
		arg = arg->nd_LEFT;
		arg->nd_INT = PointedtoType(arglink->nd_LEFT->nd_type)->tp_size;
		arg->nd_symb = INTEGER;
		arg->nd_lineno = exp->nd_lineno;
		arg->nd_type = card_type;
		/* Ignore other arguments to NEW and/or DISPOSE ??? */

		FreeNode(exp->nd_LEFT);
		exp->nd_LEFT = arg = getnode(Name);
		arg->nd_symb = IDENT;
		arg->nd_lineno = exp->nd_lineno;
		arg->nd_IDF = str2idf(edf->df_value.df_stdname==S_NEW ?
					"ALLOCATE" : "DEALLOCATE", 0);
		return ChkCall(expp);
#endif

	case S_TSIZE:	/* ??? */
	case S_SIZE:
		exp->nd_type = intorcard_type;
		if (!(arg = getname(&arglink,D_FIELD|D_VARIABLE|D_ISTYPE,0,edf))) {
			return 0;
		}
		if (! IsConformantArray(arg->nd_type)) isconstant = 1;
#ifndef NOSTRICT
		else node_warning(exp,
				  W_STRICT,
				  "%s on conformant array",
				  edf->df_idf->id_text);
#endif
#ifndef STRICT_3RD_ED
		if (! options['3'] && edf->df_value.df_stdname == S_TSIZE) {
			if (arg = arglink->nd_RIGHT) {
				node_warning(arg,
					     W_OLDFASHIONED,
					     "TSIZE with multiple parameters, only first parameter used");
				FreeNode(arg);
				arglink->nd_RIGHT = 0;
			}
		}
#endif
		break;

	case S_TRUNCD:
	case S_TRUNC:
		if (! getarg(&arglink, T_REAL, 0, edf)) return 0;
		MkCoercion(&(arglink->nd_LEFT),
			   edf->df_value.df_stdname == S_TRUNCD ?
				longint_type : card_type);
		free_it = 1;
		break;

	case S_VAL:
		if (!(arg = getname(&arglink, D_ISTYPE, T_NOSUB, edf))) {
			return 0;
		}
		exp->nd_type = arg->nd_def->df_type;
		exp->nd_RIGHT = arglink->nd_RIGHT;
		arglink->nd_RIGHT = 0;
		FreeNode(arglink);
		arglink = exp;
		/* fall through */
	case S_CHR:
		if (! getarg(&arglink, T_CARDINAL, 0, edf)) return 0;
		if (edf->df_value.df_stdname == S_CHR) {
			exp->nd_type = char_type;
		}
		if (exp->nd_type != int_type) {
			MkCoercion(&(arglink->nd_LEFT), exp->nd_type);
			free_it = 1;
		}
		break;

	case S_ADR:
		exp->nd_type = address_type;
		if (! getarg(&arglink, 0, 1, edf)) return 0;
		break;

	case S_DEC:
	case S_INC:
		exp->nd_type = 0;
		if (! (arg = getvariable(&arglink, edf, D_USED|D_DEFINED))) return 0;
		if (! (arg->nd_type->tp_fund & T_DISCRETE)) {
			df_error(arg,"illegal parameter type", edf);
			return 0;
		}
		if (arglink->nd_RIGHT) {
			if (! getarg(&arglink, T_INTORCARD, 0, edf)) return 0;
		}
		break;

	case S_HALT:
		exp->nd_type = 0;
		break;

	case S_EXCL:
	case S_INCL:
		{
		register t_type *tp;
		t_node *dummy;

		exp->nd_type = 0;
		if (!(arg = getvariable(&arglink, edf, D_USED|D_DEFINED))) return 0;
		tp = arg->nd_type;
		if (tp->tp_fund != T_SET) {
			df_error(arg, "SET parameter expected", edf);
			return 0;
		}
		if (!(dummy = getarg(&arglink, 0, 0, edf))) return 0;
		if (!ChkAssCompat(&dummy, ElementType(tp), "EXCL/INCL")) {
			/* What type of compatibility do we want here?
			   apparently assignment compatibility! ??? ???
			   But we don't want the coercion in the tree, because
			   we don't want a range check here. We want a SET
			   error.
			*/
			return 0;
		}
		MkCoercion(&(arglink->nd_LEFT), word_type);
		break;
		}

	default:
		crash("(ChkStandard)");
	}

	arg = arglink;

	if (arg->nd_RIGHT) {
		df_error(arg->nd_RIGHT, "too many parameters supplied", edf);
		return 0;
	}

	if (isconstant) {
		cstcall(expp, edf->df_value.df_stdname);
		return 1;
	}
	if (free_it) {
		*expp = arg->nd_LEFT;
		exp->nd_RIGHT = arg;
		arg->nd_LEFT = 0;
		FreeNode(exp);
	}

	return 1;
}

STATIC int
ChkCast(expp)
	t_node **expp;
{
	/*	Check a cast and perform it if the argument is constant.
		If the sizes don't match, only complain if at least one of them
		has a size larger than the word size.
		If both sizes are equal to or smaller than the word size, there
		is no problem as such values take a word on the EM stack
		anyway.
	*/
	register t_node *exp = *expp;
	register t_node *arg = exp->nd_RIGHT;
	register t_type *lefttype = exp->nd_LEFT->nd_type;
	t_def		*df = exp->nd_LEFT->nd_def;

	if ((! arg) || arg->nd_RIGHT) {
		df_error(exp, "type cast must have 1 parameter", df);
		return 0;
	}

	if (! ChkExpression(&(arg->nd_LEFT))) return 0;

	MkCoercion(&(arg->nd_LEFT), BaseType(arg->nd_LEFT->nd_type));

	arg = arg->nd_LEFT;
	if (arg->nd_type->tp_size != lefttype->tp_size &&
	    (arg->nd_type->tp_size > word_size ||
	     lefttype->tp_size > word_size)) {
		df_error(exp, "unequal sizes in type cast", df);
		return 0;
	}

	if (IsConformantArray(arg->nd_type)) {
		df_error(exp,
		  "type transfer function on conformant array not supported",
		  df);
		return 0;
	}

	exp->nd_RIGHT->nd_LEFT = 0;
	FreeNode(exp);
	if (arg->nd_class == Value) {
		exp = arg;
		if (lefttype->tp_fund == T_SET) {
			/* User deserves what he gets here ... */
			exp = getnode(Set);
			exp->nd_set = MkSet((unsigned)(lefttype->set_sz));
			exp->nd_set[0] = arg->nd_INT;
			exp->nd_lineno = arg->nd_lineno;
			FreeNode(arg);
		}
	}
	else {
		exp = getnode(Uoper);
		exp->nd_symb = CAST;
		exp->nd_lineno = arg->nd_lineno;
		exp->nd_RIGHT = arg;
	}
	*expp = exp;
	exp->nd_type = lefttype;

	return 1;
}

TryToString(nd, tp)
	register t_node *nd;
	t_type *tp;
{
	/*	Try a coercion from character constant to string.
	*/
	static char buf[8];

	assert(nd->nd_symb == STRING);

	if (tp->tp_fund == T_ARRAY && nd->nd_type == char_type) {
		buf[0] = nd->nd_INT;
		nd->nd_type = standard_type(T_STRING, 1, (arith) 2);
		nd->nd_SSTR = 
			(struct string *) Malloc(sizeof(struct string));
		nd->nd_STR = Salloc(buf, (unsigned) word_size);
		nd->nd_SLE = 1;
	}
}

STATIC int
no_desig(expp)
	t_node **expp;
{
	node_error(*expp, "designator expected");
	return 0;
}

STATIC int
add_flags(expp, flags)
	t_node **expp;
{
	(*expp)->nd_def->df_flags |= flags;
	return 1;
}

extern int	PNodeCrash();

int (*ExprChkTable[])() = {
	ChkValue,
	ChkArr,
	ChkBinOper,
	ChkUnOper,
	ChkArrow,
	ChkFunCall,
	ChkExSelOrName,
	PNodeCrash,
	ChkSet,
	add_flags,
	PNodeCrash,
	ChkExSelOrName,
	PNodeCrash,
};

int (*DesigChkTable[])() = {
	no_desig,
	ChkArr,
	no_desig,
	no_desig,
	ChkArrow,
	no_desig,
	ChkSelOrName,
	PNodeCrash,
	no_desig,
	add_flags,
	PNodeCrash,
	ChkSelOrName,
	PNodeCrash,
};
