1405 lines
28 KiB
C
1405 lines
28 KiB
C
/* 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[])() =
|
|
{
|
|
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
|
|
};
|