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

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

#include	"debug.h"

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

#include	"LLlex.h"
#include	"Lpars.h"
#include	"chk_expr.h"
#include	"const.h"
#include	"def.h"
#include	"idf.h"
#include	"main.h"
#include	"misc.h"
#include	"node.h"
#include	"required.h"
#include	"scope.h"
#include	"type.h"

extern char *symbol2str();
STATIC int ChkUnOper();

STATIC
Xerror(nd, mess)
	register struct node *nd;
	char *mess;
{
	if( nd->nd_class == Def && nd->nd_def )	{
		if( nd->nd_def->df_kind != D_ERROR )
			node_error(nd,"\"%s\": %s",
				    nd->nd_def->df_idf->id_text, mess);
	}
	else	node_error(nd, "%s", mess);
}

struct node *
ZeroParam()
{
	register struct node *nd;

	nd = MkLeaf(Value, &dot);
	nd->nd_type = int_type;
	nd->nd_symb = INTEGER;
	nd->nd_INT = (arith) 0;
	nd = MkNode(Link, nd, NULLNODE, &dot);
	nd->nd_symb = ',';

	return nd;
}

MarkUsed(nd)
	register struct node *nd;
{
	while( nd && nd->nd_class != Def ) {
		if( (nd->nd_class == Arrsel) || (nd->nd_class == LinkDef) )
			nd = nd->nd_left;
		else if( nd->nd_class == Arrow)
			nd = nd->nd_right;
		else break;
	}

	if( nd && nd->nd_class == Def ) {
		register struct def *df = nd->nd_def;

		if( df->df_kind != D_FIELD ) {
			if( !(df->df_flags & (D_SET|D_VARPAR)) &&
			    (df->df_scope == CurrentScope) )
				if( !is_anon_idf(df->df_idf) ) {
					warning("\"%s\" used before set",
						df->df_idf->id_text);
				}
			df->df_flags |= (D_USED | D_SET);
		}
	}
}

int
ChkConstant(expp)
	register struct node *expp;
{
	register struct node *nd;

	if( !(nd = expp->nd_right) ) nd = expp;

	if( nd->nd_class == Name &&  !ChkLinkOrName(nd) ) return 0;

	if( nd->nd_class != Value || expp->nd_left )	{
		Xerror(nd, "constant expected");
		return 0;
	}

	if( expp->nd_class == Uoper )
		return ChkUnOper(expp);
	else if( nd != expp )	{
		Xerror(expp, "constant expected");
		return 0;
	}
	return 1;
}

int
ChkVariable(expp)
	register struct node *expp;
{
	/* Check that "expp" indicates an item that can be accessed */

	if( !ChkLhs(expp) ) return 0;

	if( expp->nd_class == Def && expp->nd_def->df_kind == D_FUNCTION ) {
		Xerror(expp, "illegal use of function name");
		return 0;
	}
	return 1;
}

int
ChkLhs(expp)
	register struct node *expp;
{
	int class;

	/* Check that "expp" indicates an item that can be the lhs
	   of an assignment.
	*/
	if( !ChkVarAccess(expp) ) return 0;

	class = expp->nd_class;

	/* a constant is replaced by it's value in ChkLinkOrName, check here !,
	 * the remaining classes are checked by ChkVarAccess
	 */
	if( class == Value )	{
		node_error(expp, "can't access a value");
		return 0;
	}

	if( class == Def &&
	    !(expp->nd_def->df_kind & (D_FIELD | D_FUNCTION | D_VARIABLE)) ) {
		Xerror(expp, "variable expected");
		return 0;
	}

	/* assignment to function name */
	if( class == Def && expp->nd_def->df_kind == D_FUNCTION )
		if( expp->nd_def->prc_res )
			expp->nd_type = ResultType(expp->nd_def->df_type);
		else	{
			Xerror(expp, "illegal assignment to function-name");
			return 0;
		}

	return 1;
}

#ifdef DEBUG
STATIC int
ChkValue(expp)
	register struct node *expp;
{
	switch( expp->nd_symb )	{
		case INTEGER:
		case REAL:
		case STRING:
		case NIL:
			return 1;

		default:
			crash("(ChkValue)");
	}
	/*NOTREACHED*/
}
#endif

int
ChkLinkOrName(expp)
	register struct node *expp;
{
	register struct def *df;

	expp->nd_type = error_type;

	if( expp->nd_class == Name )	{
		expp->nd_def = lookfor(expp, CurrVis, 1);
		expp->nd_class = Def;
		expp->nd_type = expp->nd_def->df_type;
	}
	else if( expp->nd_class == Link )	{
		/* a selection from a record */
		register struct node *left = expp->nd_left;

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

		if( !ChkVariable(left) ) return 0;

		if( left->nd_type->tp_fund != T_RECORD )	{
			Xerror(left, "illegal selection");
			return 0;
		}

		if( !(df = lookup(expp->nd_IDF, left->nd_type->rec_scope, D_INUSE)) ) {
			id_not_declared(expp);
			return 0;
		}
		else	{
			expp->nd_def = df;
			expp->nd_type = df->df_type;
			expp->nd_class = LinkDef;
		}
		return 1;
	}
	assert(expp->nd_class == Def);

	df = expp->nd_def;

	if( df->df_kind & (D_ENUM | D_CONST) )	{
		MarkUsed(expp);
		/* Replace an enum-literal or a CONST identifier by its value.
		*/
		if( df->df_kind == D_ENUM )	{
			expp->nd_class = Value;
			expp->nd_INT = df->enm_val;
			expp->nd_symb = INTEGER;
		}
		else  {
			unsigned int ln = expp->nd_lineno;

			assert(df->df_kind == D_CONST);
			*expp = *(df->con_const);
			expp->nd_lineno = ln;
		}
	}
	return df->df_kind != D_ERROR;
}

STATIC int
ChkExLinkOrName(expp)
	register struct node *expp;
{
	if( !ChkLinkOrName(expp) ) return 0;
	if( expp->nd_class != Def ) return 1;

	if( !(expp->nd_def->df_kind & D_VALUE) ) {
		Xerror(expp, "value expected");
	}

	return 1;
}

STATIC int
ChkUnOper(expp)
	register struct node *expp;
{
	/*	Check an unary operation.
	*/
	register struct node *right = expp->nd_right;
	register struct type *tpr;

	if( !ChkExpression(right) ) return 0;

	MarkUsed(right);

	expp->nd_type = tpr = BaseType(right->nd_type);

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

	case '-':
		if( tpr->tp_fund == T_INTEGER || tpr->tp_fund == T_LONG ) {
			if( right->nd_class == Value )
				cstunary(expp);
			return 1;
		}
		if( tpr->tp_fund == T_REAL )	{
			if( right->nd_class == Value )	{
				expp->nd_token.tk_data.tk_real = right->nd_RIV;
				expp->nd_class = Value;
				expp->nd_symb = REAL;
				FreeNode(right);
				expp->nd_right = NULLNODE;
			}
			return 1;
		}
		break;

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

	case '(':
		/* Delete the brackets */
		*expp = *right;
		free_node(right);
		return 1;

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

STATIC struct type *
ResultOfOperation(operator, tpl, tpr)
	struct type *tpl, *tpr;
{
	/* Return the result type of the binary operation "operator",
	   with operand types "tpl" and "tpr".
	 */

	switch( operator )	{
		case '='	:
		case NOTEQUAL	:
		case '<'	:
		case '>'	:
		case LESSEQUAL	:
		case GREATEREQUAL:
		case IN		:
				return bool_type;
		case '+'	:
		case '-'	:
		case '*'	:
				if( tpl == real_type || tpr == real_type )
					return real_type;
				if( tpl == long_type || tpr == long_type)
					return long_type;
				return tpl;
		case '/'	:
				return real_type;
	}
	if (tpr == long_type && tpl == int_type) return tpr;
	return tpl;
}

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_NUMERIC;
		case DIV	:
		case MOD	:
				return T_INTEGER | T_LONG;
		case OR		:
		case AND	:
				return T_ENUMERATION;
		case '='	:
		case NOTEQUAL   :
				return T_ENUMERATION | T_CHAR | T_NUMERIC |
					T_SET | T_POINTER | T_STRINGCONST |
					T_STRING;
		case LESSEQUAL	:
		case GREATEREQUAL:
				return T_ENUMERATION | T_CHAR | T_NUMERIC |
					T_SET | T_STRINGCONST;
		case '<'	:
		case '>'	:
				return T_ENUMERATION | T_CHAR | T_NUMERIC |
					T_STRINGCONST;
		default		:
				crash("(AllowedTypes)");
	}
	/*NOTREACHED*/
}

STATIC int
Boolean(operator)
{
	return operator == OR || operator == AND;
}

STATIC int
ChkBinOper(expp)
	register struct node *expp;
{
	/*	Check a binary operation.
	 */
	register struct node *left, *right;
	struct type *tpl, *tpr;
	int retval, allowed;

	left = expp->nd_left;
	right = expp->nd_right;

	retval = ChkExpression(left);
	retval &= ChkExpression(right);

	MarkUsed(left);
	MarkUsed(right);

	tpl = BaseType(left->nd_type);
	tpr = BaseType(right->nd_type);

	expp->nd_type = ResultOfOperation(expp->nd_symb, tpl ,tpr);

	/* Check that the application of the operator is allowed on the type
	   of the operands.
	   There are some needles and pins:
	   - Boolean operators are only allowed on boolean operands, but the
	     "allowed-mask" of "AllowedTypes" can only indicate an enumeration
	     type.
	   - The IN-operator has as right-hand-side operand a set.
	   - Strings and packed arrays can be equivalent.
	   - In some cases, integers must be converted to reals.
	   - If one of the operands is the empty set then the result doesn't
	     have to be the empty set.
	*/

	if( expp->nd_symb == IN )	{
		if( tpr->tp_fund != T_SET )	{
			node_error(expp, "\"IN\": right operand must be a set");
			return 0;
		}
		if( !TstAssCompat(tpl, ElementType(tpr)) )	{
			node_error(expp, "\"IN\": incompatible types");
			return 0;
		}
		if( left->nd_class == Value && right->nd_class == Set )
			cstset(expp);
		return retval;
	}

	if( !retval ) return 0;

	allowed = AllowedTypes(expp->nd_symb);

	if( !(tpr->tp_fund & allowed) || !(tpl->tp_fund & allowed) )	{
		arith ub;
		extern arith IsString();

		if( allowed & T_STRINGCONST && (ub = IsString(tpl)) )	{
			if( ub == IsString(tpr) )
				return 1;
			else	{
				node_error(expp, "\"%s\": incompatible types",
						symbol2str(expp->nd_symb));
				return 0;
			}
		}
		else if( allowed & T_STRING && tpl->tp_fund == T_STRING )
				return 1;

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

	if( Boolean(expp->nd_symb) && tpl != bool_type )	{
		node_error(expp, "\"%s\": illegal operand type(s)",
						symbol2str(expp->nd_symb));
		return 0;
	}

	if( allowed & T_NUMERIC )	{
		if( (tpl == int_type || tpl == long_type) &&
		    (tpr == real_type || expp->nd_symb == '/') ) {
			expp->nd_left =
				MkNode(Cast, NULLNODE, expp->nd_left, &dot);
			expp->nd_left->nd_type = tpl = real_type;
		}
		if( tpl == real_type &&
				(tpr == int_type || tpr == long_type))	{
			expp->nd_right =
				MkNode(Cast, NULLNODE, expp->nd_right, &dot);
			expp->nd_right->nd_type = tpr = real_type;
		}
		if( tpl == int_type && tpr == long_type) {
			expp->nd_left =
				MkNode(IntCoerc, NULLNODE, expp->nd_left, &dot);
			expp->nd_left->nd_type = long_type;
		}
		else if( tpl == long_type && tpr == int_type) {
			expp->nd_right =
				MkNode(IntCoerc, NULLNODE, expp->nd_right, &dot);
			expp->nd_right->nd_type = long_type;
		}
	}

	/* Operands must be compatible */
	if( !TstCompat(tpl, tpr) )	{
		node_error(expp, "\"%s\": incompatible types",
						symbol2str(expp->nd_symb));
		return 0;
	}

	if( tpl->tp_fund & T_SET )	{
		if( tpl == emptyset_type )
			left->nd_type = tpr;
		else if( tpr == emptyset_type )
			right->nd_type = tpl;

		if( expp->nd_type == emptyset_type )
			expp->nd_type = tpr;
		if( left->nd_class == Set && right->nd_class == Set )
			cstset(expp);
	}
	else if( tpl->tp_fund != T_REAL &&
		left->nd_class == Value && right->nd_class == Value )
			cstbin(expp);

	return 1;
}

STATIC int
ChkElement(expp, tp, set, cnt)
	register struct node *expp;
	register struct type **tp;
	arith **set;
	unsigned *cnt;
{
	/*	Check elements of a set. This routine may call itself
		recursively. Also try to compute the set!
	*/
	register struct node *left = expp->nd_left;
	register struct node *right = expp->nd_right;
	register int i;
	extern char *Malloc();

	if( expp->nd_class == Link && expp->nd_symb == UPTO )	{
		/* [ ... , expr1 .. expr2,  ... ]
		   First check expr1 and expr2, and try to compute them.
		*/
		if( !ChkElement(left, tp, set, cnt) ||
					!ChkElement(right, tp, set, cnt) )
			return 0;

		if( left->nd_class == Value &&
				right->nd_class == Value && *set )	{

			if( left->nd_INT > right->nd_INT )	{
				/* Remove lower and upper bound of the range.
				*/
				*cnt -= 2;
				(*set)[left->nd_INT/wrd_bits] &=
						~(1 << (left->nd_INT%wrd_bits));
				(*set)[right->nd_INT/wrd_bits] &=
					       ~(1 << (right->nd_INT%wrd_bits));
			}
			else
				/* We have a constant range. Put all elements
				   in the set.
				*/
			    for( i = left->nd_INT + 1; i < right->nd_INT; i++ )
				(*set)[i/wrd_bits] |= ( 1 << (i%wrd_bits) );
		}
		return 1;
	}

	/* Here, a single element is checked
	*/
	if( !ChkExpression(expp) ) return 0;
	MarkUsed(expp);

	if( *tp == emptyset_type )	{
		/* first element in set determines the type of the set */
		unsigned size;

		*tp = set_type(expp->nd_type, 0);
		size = (*tp)->tp_size * (sizeof(arith) / word_size);
		*set = (arith *) Malloc(size);
		clear((char *) *set, size);
	}
	else if( !TstCompat(ElementType(*tp), expp->nd_type) )	{
		node_error(expp, "set element has incompatible type");
		return 0;
	}

	if( expp->nd_class == Value )	{
		/* a constant element
		*/
		i = expp->nd_INT;

		if( expp->nd_type == int_type )	{
			/* Check only integer base-types because they are not
			   equal to the integer host-type. The other base-types
			   are equal to their host-types.
			*/

	    		if( i < 0 || i > max_intset )	{
				node_error(expp, "set element out of range");
				return 0;
			}
		}

		if( *set ) (*set)[i/wrd_bits] |= ( 1 << (i%wrd_bits));
		(*cnt)++;
	}
	else if( *set )	{
		free((char *) *set);
		*set = (arith *) 0;
	}

	return 1;
}

STATIC int
ChkSet(expp)
	register struct node *expp;
{
	/*	Check the legality of a SET aggregate, and try to evaluate it
		compile time. Unfortunately this is all rather complicated.
	*/
	register struct node *nd = expp->nd_right;
	arith *set = (arith *) 0;
	unsigned cnt = 0;

	assert(expp->nd_symb == SET);

	expp->nd_type = emptyset_type;

	/* Now check the elements given, and try to compute a constant set.
	   First allocate room for the set, but only if it isn't empty.
	*/
	if( !nd )	{
		/* The resulting set IS empty, so we just return
		*/
		expp->nd_class = Set;
		expp->nd_set = (arith *) 0;
		return 1;
	}

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

		if( !ChkElement(nd->nd_left, &(expp->nd_type), &set, &cnt) )
			return 0;
		nd = nd->nd_right;
	}

	if( set )	{
		/* Yes, it was a constant set, and we managed to compute it!
		   Notice that at the moment there is no such thing as
		   partial evaluation. Either we evaluate the set, or we
		   don't (at all). Improvement not neccesary (???)
		   ??? sets have a contant part and a variable part ???
		*/
		expp->nd_class = Set;
		if( !cnt )	{
			/* after all the work we've done, the set turned out
			   out to be empty!
			*/
			free((char *) set);
			set = (arith *) 0;
		}
		expp->nd_set = set;
		FreeNode(expp->nd_right);
		expp->nd_right = NULLNODE;
	}

	return 1;
}

char *
ChkAllowedVar(nd, reading)		/* reading indicates read or readln */
	register struct node *nd;
{
	char *message = 0;

	switch( nd->nd_class )	{
	case Def:
		if( nd->nd_def->df_flags & D_INLOOP ) {
			message = "control variable";
			break;
		}
		if( nd->nd_def->df_kind != D_FIELD ) break;
		/* FALL THROUGH */

	case LinkDef:
		assert(nd->nd_def->df_kind == D_FIELD);

		if( nd->nd_def->fld_flags & F_PACKED )
			message = "field of packed record";
		else if( nd->nd_def->fld_flags & F_SELECTOR )
			message = "variant selector";
		break;

	case Arrsel:
		if( IsPacked(nd->nd_left->nd_type) )
			if( !reading ) message = "component of packed array";
		break;

	case Arrow:
		if( nd->nd_right->nd_type->tp_fund == T_FILE )
			message = "filebuffer variable";
		break;

	default:
		crash("(ChkAllowedVar)");
		/*NOTREACHED*/
	}
	MarkDef(nd, D_SET, 1);
	return message;
}

int
ChkVarPar(nd, name)
	register struct node *nd, *name;
{
	/* 	ISO 6.6.3.3 :
		An actual variable parameter shall not denote a field
		that is the selector of a variant-part or a component
		of a variable where that variable possesses a type
		that is designated packed.
	*/
	static char err_mes[80];
	char *message = (char *) 0;

	if( !ChkVariable(nd) ) return 0;

	message = ChkAllowedVar(nd, 0);

	if( message )	{
		sprint(err_mes, "%s can't be a variable parameter", message);
		Xerror(name, err_mes);
		return 0;
	}
	return 1;
}

STATIC struct node *
getarg(argp, bases, varaccess, name, paramtp)
	struct node **argp, *name;
	struct type *paramtp;
{
	/*	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 "varaccess" is a flag indicating
		that the address from this argument is taken, so that it
		must be a varaccess and may not be a register variable.
	*/
	register struct node *arg = (*argp)->nd_right;
	register struct node *left;

	if( !arg )	{
		Xerror(name, "too few arguments supplied");
		return 0;
	}

	left = arg->nd_left;
	*argp = arg;

	if( paramtp && paramtp->tp_fund & T_ROUTINE )	{
		/* From the context it appears that the occurrence of the
		   procedure/function-identifier is not a call.
		*/
		if( left->nd_class != NameOrCall )	{
			Xerror(name, "illegal proc/func parameter");
			return 0;
		}
		else if( ChkLinkOrName(left->nd_left) ) {
			left->nd_type = left->nd_left->nd_type;
			MarkUsed(left->nd_left);
		}
		else return 0;
	}
	else if( varaccess ) {
	    if( !ChkVarPar(left, name) ) {
		MarkUsed(left);
		return 0;
	    }
	}
	else if( !ChkExpression(left) ) {
		MarkUsed(left);
		return 0;
	}

	MarkUsed(left);

	if( !varaccess &&  bases == T_INTEGER &&
		    BaseType(left->nd_type)->tp_fund == T_LONG) {
		arg->nd_left = MkNode(IntReduc, NULLNODE, left, &dot);
		arg->nd_left->nd_type = int_type;
		left = arg->nd_left;
	}

	if( bases && !(BaseType(left->nd_type)->tp_fund & bases) )	{
		Xerror(name, "unexpected parameter type");
		return 0;
	}

	return left;
}

STATIC int
ChkProcCall(expp)
	struct node *expp;
{
	/*	Check a procedure call
	*/
	register struct node *left;
	struct node *name;
	register struct paramlist *param;
	char ebuf[80];
	int retval = 1;
	int cnt = 0;
	int new_par_section;
	struct type *lasttp = NULLTYPE;

	name = left = expp->nd_left;

	if( left->nd_type == error_type )	{
		/* Just check parameters as if they were value parameters
		*/
		expp->nd_type = error_type;
		while( expp->nd_right )
			(void) getarg(&expp, 0, 0, name, NULLTYPE);
		return 0;
	}

	expp->nd_type = ResultType(left->nd_type);

	/* Check parameter list
	*/
	for( param = ParamList(left->nd_type); param; param = param->next ) {
		if( !(left = getarg(&expp, 0, (int) IsVarParam(param), name,
							TypeOfParam(param))) )
			return 0;

		cnt++;
		new_par_section = lasttp != TypeOfParam(param);
		if( !TstParCompat(TypeOfParam(param), left->nd_type,
			    (int) IsVarParam(param), left, new_par_section) ) {
			sprint(ebuf, "type incompatibility in parameter %d",
					cnt);
			Xerror(name, ebuf);
			retval = 0;
		}

		/* Convert between integers and longs.
		 */
		if( !IsVarParam(param) && options['d'] )	{
			if( left->nd_type->tp_fund == T_INTEGER &&
					TypeOfParam(param)->tp_fund == T_LONG) {
				expp->nd_left =
					MkNode(IntCoerc, NULLNODE, left, &dot);
				expp->nd_left->nd_type = long_type;
				left = expp->nd_left;
			}
			else if( left->nd_type->tp_fund == T_LONG &&
				    TypeOfParam(param)->tp_fund == T_INTEGER) {
				expp->nd_left =
					MkNode(IntReduc, NULLNODE, left, &dot);
				expp->nd_left->nd_type = int_type;
				left = expp->nd_left;
			}
		}

		if( left->nd_type == emptyset_type )
			/* type of emptyset determined by the context */
			left->nd_type = TypeOfParam(param);

		lasttp = TypeOfParam(param);
	}

	if( expp->nd_right )	{
		Xerror(name, "too many arguments supplied");
		while( expp->nd_right )
			(void) getarg(&expp, 0, 0, name, NULLTYPE);
		return 0;
	}

	return retval;
}

STATIC int ChkStandard();

int
ChkCall(expp)
	register struct 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 standard procedure call.
	*/

	/* First, get the name of the function or procedure
	*/
	register struct node *left = expp->nd_left;

	expp->nd_type = error_type;

	if( ChkLinkOrName(left) )	{

		MarkUsed(left);
		if( IsProcCall(left) || left->nd_type == error_type )	{
			/* A call.
		   	   It may also be a call to a standard procedure
			*/

			if( left->nd_type == std_type )
				/* A standard procedure
				*/
				return ChkStandard(expp, left);

			/* Here, we have found a real procedure call. 
			*/
		}
		else	{
			node_error(left, "procedure or function expected");
			return 0;
		}
	}
	return ChkProcCall(expp);
}

STATIC int
ChkExCall(expp)
	register struct node *expp;
{
	if( !ChkCall(expp) ) return 0;

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

STATIC int
ChkNameOrCall(expp)
	register struct node *expp;
{
	/* From the context it appears that the occurrence of the function-
	   identifier is a call to that function
	*/
	assert(expp->nd_class == NameOrCall);
	expp->nd_class = Call;

	return ChkExCall(expp);
}

STATIC int
ChkStandard(expp,left)
	register struct node *expp, *left;
{
	/*	Check a call of a standard procedure or function
	*/

	struct node *arg = expp;
	struct node *name = left;
	int req;

	assert(left->nd_class == Def);

	req = left->nd_def->df_value.df_reqname;

	switch( req )	{
	    case R_ABS:
	    case R_SQR:
		if( !(left = getarg(&arg, T_NUMERIC, 0, name, NULLTYPE)) )
			return 0;
		expp->nd_type = left->nd_type;
		if( left->nd_class == Value &&
					expp->nd_type->tp_fund != T_REAL )
			cstcall(expp, req);
		break;

	    case R_SIN:
	    case R_COS:
	    case R_EXP:
	    case R_LN:
	    case R_SQRT:
	    case R_ARCTAN:
		if( !(left = getarg(&arg, T_NUMERIC, 0, name, NULLTYPE)) )
			return 0;
		expp->nd_type = real_type;
		if( BaseType(left->nd_type)->tp_fund == T_INTEGER ||
			    BaseType(left->nd_type)->tp_fund == T_LONG)	{
			arg->nd_left = MkNode(Cast,NULLNODE, arg->nd_left,&dot);
			arg->nd_left->nd_type = real_type;
		}
		break;

	    case R_TRUNC:
	    case R_ROUND:
		if( !(left = getarg(&arg, T_REAL, 0, name, NULLTYPE)) )
			return 0;
		expp->nd_type = int_type;
		break;

	    case R_ORD:
		if( !(left = getarg(&arg, T_ORDINAL, 0, name, NULLTYPE)) )
			return 0;
		if( BaseType(left->nd_type)->tp_fund == T_LONG )	{
			arg->nd_left = MkNode(IntReduc, NULLNODE, arg->nd_left, &dot);
			arg->nd_left->nd_type = int_type;
		}
		expp->nd_type = int_type;
		if( left->nd_class == Value )
			cstcall(expp, R_ORD);
		break;

	    case R_CHR:
		if( !(left = getarg(&arg, T_INTEGER, 0, name, NULLTYPE)) )
			return 0;
		expp->nd_type = char_type;
		if( left->nd_class == Value )
			cstcall(expp, R_CHR);
		break;

	    case R_SUCC:
	    case R_PRED:
		if( !(left = getarg(&arg, T_ORDINAL, 0, name, NULLTYPE)) )
			return 0;
		expp->nd_type = left->nd_type;
		if( left->nd_class == Value && options['R'] )
			cstcall(expp, req);
		break;

	    case R_ODD:
		if( !(left = getarg(&arg, T_INTEGER | T_LONG , 0, name, NULLTYPE)) )
			return 0;
		expp->nd_type = bool_type;
		if( left->nd_class == Value )
			cstcall(expp, R_ODD);
		break;

	    case R_EOF:
	    case R_EOLN:
	    case R_GET:
	    case R_PAGE:	{
		int st_out;

		if( req == R_PAGE )	{
			expp->nd_type = NULLTYPE;
			st_out = 1;
		}
		else	{
			st_out = 0;
			if (req == R_GET) {
				expp->nd_type = NULLTYPE;
			}
			else	expp->nd_type = bool_type;
		}
		if( !arg->nd_right )	{
			struct node *nd;

			if( !(nd = ChkStdInOut(name->nd_IDF->id_text, st_out)) )
				return 0;

			expp->nd_right = MkNode(Link, nd, NULLNODE, &dot);
			expp->nd_right->nd_symb = ',';
			arg = arg->nd_right;
		}
		else	{
			if( !(left = getarg(&arg, T_FILE, 1, name, NULLTYPE)) )
				return 0;
			if( (req == R_PAGE || req == R_EOLN)
			    && left->nd_type != text_type ) {
				Xerror(name, "textfile expected");
				return 0;
			}
		}
		break;

	    }
	    case R_REWRITE:
	    case R_PUT:
	    case R_RESET:
		if( !(left = getarg(&arg, T_FILE, 1, name, NULLTYPE)) )
			return 0;
		expp->nd_type = NULLTYPE;
		break;

	    case R_PACK:
	    case R_UNPACK:	{
		struct type *tp1, *tp2, *tp3;

		if( req == R_PACK )	{
			/* pack(a, i, z) */
			if( !(left = getarg(&arg, T_ARRAY, 1, name, NULLTYPE)) )
				return 0;
			tp1 = left->nd_type;		/* (a) */
			if( !(left = getarg(&arg, 0, 0, name, NULLTYPE)) )
				return 0;
			tp2 = left->nd_type;		/* (i) */
			if( !(left = getarg(&arg, T_ARRAY, 1, name, NULLTYPE)) )
				return 0;
			tp3 = left->nd_type;		/* (z) */
		}
		else	{
			/* unpack(z, a, i) */
			if( !(left = getarg(&arg, T_ARRAY, 1, name, NULLTYPE)) )
				return 0;
			tp3 = left->nd_type;		/* (z) */
			if( !(left = getarg(&arg, T_ARRAY, 1, name, NULLTYPE)) )
				return 0;
			tp1 = left->nd_type;		/* (a) */
			if( !(left = getarg(&arg, 0, 0, name, NULLTYPE)) )
				return 0;
			tp2 = left->nd_type;		/* (i) */
		}
		if( IsConformantArray(tp1) || IsPacked(tp1) )	{
			Xerror(name, "unpacked array expected");
			return 0;
		}
		if( !TstAssCompat(IndexType(tp1), tp2) )	{
			Xerror(name, "ordinal constant expected");
			return 0;
		}
		if( IsConformantArray(tp3) || !IsPacked(tp3) )	{
			Xerror(name, "packed array expected");
			return 0;
		}
		if( !TstTypeEquiv(tp1->arr_elem, tp3->arr_elem) )	{
			Xerror(name, "component types of arrays not equal");
			return 0;
		}
		expp->nd_type = NULLTYPE;
		break;
	    }

	    case R_NEW:
	    case R_DISPOSE:
		if( !(left = getarg(&arg, T_POINTER, 1, name, NULLTYPE)) )
			return 0;
		if( arg->nd_right )	{
			/* varargs new/dispose(p,c1,.....) */
			register struct selector *sel;
			register arith i;

			if( PointedtoType(left->nd_type)->tp_fund != T_RECORD )
				break;
			sel = PointedtoType(left->nd_type)->rec_sel;
			do	{
				if( !sel ) break;

				arg = arg->nd_right;
				left = arg->nd_left;

				/* ISO : COMPILETIME CONSTANTS NOT PERMITTED */
				if( !ChkConstant(left) ) return 0;

				if( !TstCompat(left->nd_type, sel->sel_type) ) {
					node_error(left,
					   "type incompatibility in caselabel");
					return 0;
				}

				i = left->nd_INT - sel->sel_lb;
				if( i < 0 || i >= sel->sel_ncst )	{
					node_error(left,
						"case constant: out of bounds");
					return 0;
				}

				sel = sel->sel_ptrs[i];
			} while( arg->nd_right );

			FreeNode(expp->nd_right->nd_right);
			expp->nd_right->nd_right = NULLNODE;
		}
		expp->nd_type = NULLTYPE;
		break;

	    case R_MARK:
	    case R_RELEASE:
		if( !(left = getarg(&arg, T_POINTER, 1, name, NULLTYPE)) )
			return 0;
		expp->nd_type = NULLTYPE;
		break;

	    case R_HALT:
		if( !arg->nd_right ) 		/* insert 0 parameter */
			arg->nd_right = ZeroParam();
		if( !(left = getarg(&arg, T_INTEGER, 0, name, NULLTYPE)) )
			return 0;
		expp->nd_type = NULLTYPE;
		break;

	    default:
		crash("(ChkStandard)");
	}
	
	if( arg->nd_right )	{
		Xerror(name, "too many arguments supplied");
		return 0;
	}

	return 1;
}

STATIC int
ChkArrow(expp)
	register struct node *expp;
{
	/*	Check an application of the '^' operator.
		The operand must be a variable of a pointer-type or a
		variable of a file-type.
	*/

	register struct type *tp;

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

	expp->nd_type = error_type;

	if( !ChkVariable(expp->nd_right) ) return 0;

	MarkUsed(expp->nd_right);

	tp = expp->nd_right->nd_type;

	if( !(tp->tp_fund & (T_POINTER | T_FILE)) )	{
		node_error(expp, "\"^\": illegal operand");
		return 0;
	}

	expp->nd_type = PointedtoType(tp);
	return 1;
}

STATIC int
ChkArr(expp)
	register struct 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 struct type *tpl, *tpr;
	int retval;

	assert(expp->nd_class == Arrsel);
	assert(expp->nd_symb == '[');

	expp->nd_type = error_type;

	/* Check the index first, so a[a[j]] is checked in order of
	 * evaluation. This to make sure that warnings are generated
	 * in the right order.
	 */
	retval = ChkExpression(expp->nd_right);
	MarkUsed(expp->nd_right);
	retval &= ChkVariable(expp->nd_left);

	tpl = expp->nd_left->nd_type;
	tpr = expp->nd_right->nd_type;
	if( tpl == error_type || tpr == error_type ) return 0;

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

	/* Type of the index must be assignment compatible with
	   the index type of the array.
	*/
	if( !TstCompat(IndexType(tpl), tpr) )	{
		node_error(expp, "incompatible index type");
		return 0;
	}

	if( tpr == long_type ) {
		expp->nd_right = MkNode(IntReduc, NULLNODE, expp->nd_right, &dot);
		expp->nd_right->nd_type = int_type;
	}

	expp->nd_type = tpl->arr_elem;
	return retval;
}

STATIC int
done_before()
{
	return 1;
}

STATIC int
no_var_access(expp)
	struct node *expp;
{
	node_error(expp, "variable-access expected");
	return 0;
}

extern int	NodeCrash();

int (*ExprChkTable[])() = {
#ifdef DEBUG
	ChkValue,
#else
	done_before,
#endif
	ChkExLinkOrName,
	ChkUnOper,
	ChkBinOper,
	ChkSet,
	NodeCrash,
	ChkExCall,
	ChkNameOrCall,
	ChkArrow,
	ChkArr,
	NodeCrash,
	ChkExLinkOrName,
	NodeCrash,
	NodeCrash,
	NodeCrash,
	NodeCrash
};

int (*VarAccChkTable[])() = {
	no_var_access,
	ChkLinkOrName,
	no_var_access,
	no_var_access,
	no_var_access,
	NodeCrash,
	no_var_access,
	no_var_access,
	ChkArrow,
	ChkArr,
	done_before,
	ChkLinkOrName,
	done_before,
	no_var_access,
	no_var_access,
	no_var_access
};
