/* 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    "parameters.h"
#include	"debug.h"

#include    <stdlib.h>
#include	<alloc.h>
#include	<assert.h>
#include	<em_arith.h>
#include	<em_label.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"
#include	"typequiv.h"
#include	"readwrite.h"
#include	"body.h"
#include	"cstoper.h"
#include	"error.h"

static int ChkValue(register struct node *);
static int ChkUnOper(register struct node *);
static int ChkStandard(register struct node *, register struct node *);


static void Xerror(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(void)
{
	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;
}

void MarkUsed(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(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(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(register struct node *expp)
{
	int class;


	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(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(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(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(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(int operator, struct type *tpl, struct type *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(int 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(int operator)
{
	return operator == OR || operator == AND;
}

static int ChkBinOper(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(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(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(register struct node *nd, int reading)
/* reading indicates read or readln */

{
	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;
}

static int ChkVarPar(register struct node *nd, register struct node *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(struct node **argp, int bases, int varaccess, struct node *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(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;
}

int ChkCall(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(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(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(register struct node *expp, register struct node *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_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(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(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(struct node *expp)
{
	return 1;
}

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

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

	int (*VarAccChkTable[])(struct node*) =
	{
		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
		};
