ack/lang/m2/comp/chk_expr.c

1278 lines
27 KiB
C
Raw Normal View History

/*
* (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
*/
1986-04-08 18:15:46 +00:00
/* E X P R E S S I O N C H E C K I N G */
/* $Header$ */
1986-04-08 18:15:46 +00:00
/* Check expressions, and try to evaluate them as far as possible.
*/
1986-05-01 19:06:53 +00:00
#include "debug.h"
1986-04-08 18:15:46 +00:00
#include <em_arith.h>
#include <em_label.h>
#include <assert.h>
1986-04-09 18:14:49 +00:00
#include <alloc.h>
1986-04-18 17:53:47 +00:00
1986-04-10 01:08:49 +00:00
#include "Lpars.h"
1986-04-08 18:15:46 +00:00
#include "idf.h"
#include "type.h"
#include "def.h"
#include "LLlex.h"
#include "node.h"
#include "scope.h"
1986-04-09 18:14:49 +00:00
#include "const.h"
#include "standards.h"
1986-06-17 12:04:05 +00:00
#include "chk_expr.h"
1986-10-22 15:38:24 +00:00
#include "misc.h"
1986-11-05 14:33:00 +00:00
#include "warning.h"
1986-04-18 17:53:47 +00:00
1986-04-23 22:12:22 +00:00
extern char *symbol2str();
1987-05-18 15:57:33 +00:00
extern char *sprint();
1986-04-23 22:12:22 +00:00
STATIC
Xerror(nd, mess, edf)
struct node *nd;
char *mess;
1986-11-17 13:08:18 +00:00
register struct def *edf;
{
if (edf) {
if (edf->df_kind != D_ERROR) {
1986-11-17 13:08:18 +00:00
node_error(nd,"\"%s\": %s", edf->df_idf->id_text, mess);
}
}
else node_error(nd, "%s", mess);
}
1986-06-26 09:39:36 +00:00
int
ChkVariable(expp)
1986-06-26 09:39:36 +00:00
register struct node *expp;
{
1986-10-06 20:36:30 +00:00
/* Check that "expp" indicates an item that can be
assigned to.
*/
1986-06-26 09:39:36 +00:00
if (! ChkDesignator(expp)) return 0;
1986-06-26 09:39:36 +00:00
1987-07-13 10:30:37 +00:00
if ((expp->nd_class == Def || expp->nd_class == LinkDef) &&
!(expp->nd_def->df_kind & (D_FIELD|D_VARIABLE))) {
Xerror(expp, "variable expected", expp->nd_def);
1986-06-26 09:39:36 +00:00
return 0;
}
return 1;
}
STATIC int
ChkArrow(expp)
1986-06-26 09:39:36 +00:00
register struct node *expp;
{
1986-10-06 20:36:30 +00:00
/* Check an application of the '^' operator.
The operand must be a variable of a pointer type.
*/
1986-06-26 09:39:36 +00:00
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;
1986-06-26 09:39:36 +00:00
tp = expp->nd_right->nd_type;
if (tp->tp_fund != T_POINTER) {
node_error(expp, "\"^\": illegal operand");
1986-06-26 09:39:36 +00:00
return 0;
}
1986-09-25 19:39:06 +00:00
expp->nd_type = RemoveEqual(PointedtoType(tp));
1986-06-26 09:39:36 +00:00
return 1;
}
1986-06-17 12:04:05 +00:00
STATIC int
ChkArr(expp)
1986-06-26 09:39:36 +00:00
register struct node *expp;
1986-04-08 18:15:46 +00:00
{
1986-10-06 20:36:30 +00:00
/* 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.
*/
1986-06-26 09:39:36 +00:00
register struct type *tpl, *tpr;
int retval;
1986-06-26 09:39:36 +00:00
assert(expp->nd_class == Arrsel);
assert(expp->nd_symb == '[');
expp->nd_type = error_type;
retval = ChkVariable(expp->nd_left) & ChkExpression(expp->nd_right);
1986-06-26 09:39:36 +00:00
tpl = expp->nd_left->nd_type;
tpr = expp->nd_right->nd_type;
if (tpl == error_type || tpr == error_type) return 0;
1986-06-26 09:39:36 +00:00
if (tpl->tp_fund != T_ARRAY) {
1986-10-06 20:36:30 +00:00
node_error(expp, "not indexing an ARRAY type");
1986-06-26 09:39:36 +00:00
return 0;
}
/* 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.
Either INTEGER or CARDINAL seems reasonable.
*/
if (IsConformantArray(tpl) ? !TstAssCompat(card_type, tpr)
: !TstAssCompat(IndexType(tpl), tpr)) {
node_error(expp, "incompatible index type");
return 0;
}
1986-09-25 19:39:06 +00:00
expp->nd_type = RemoveEqual(tpl->arr_elem);
return retval;
1986-06-17 12:04:05 +00:00
}
1986-04-18 17:53:47 +00:00
1986-10-06 20:36:30 +00:00
#ifdef DEBUG
1986-06-17 12:04:05 +00:00
STATIC int
ChkValue(expp)
1986-06-17 12:04:05 +00:00
struct node *expp;
{
switch(expp->nd_symb) {
case REAL:
case STRING:
case INTEGER:
return 1;
1986-04-18 17:53:47 +00:00
1986-04-08 23:34:10 +00:00
default:
crash("(ChkValue)");
1986-04-08 18:15:46 +00:00
}
/*NOTREACHED*/
}
1986-10-06 20:36:30 +00:00
#endif
1986-04-08 18:15:46 +00:00
1986-06-17 12:04:05 +00:00
STATIC int
ChkLinkOrName(expp)
1986-04-08 18:15:46 +00:00
register struct node *expp;
{
1986-10-06 20:36:30 +00:00
/* Check either an ID or a construction of the form
ID.ID [ .ID ]*
*/
1986-06-26 09:39:36 +00:00
register struct def *df;
1986-08-26 14:33:24 +00:00
expp->nd_type = error_type;
1986-06-26 09:39:36 +00:00
if (expp->nd_class == Name) {
expp->nd_def = lookfor(expp, CurrVis, 1);
expp->nd_class = Def;
1986-09-25 19:39:06 +00:00
expp->nd_type = RemoveEqual(expp->nd_def->df_type);
1986-06-26 09:39:36 +00:00
}
else if (expp->nd_class == Link) {
1986-10-06 20:36:30 +00:00
/* A selection from a record or a module.
Modules also have a record type.
*/
1986-06-26 09:39:36 +00:00
register struct node *left = expp->nd_left;
assert(expp->nd_symb == '.');
if (! ChkDesignator(left)) return 0;
1986-06-26 09:39:36 +00:00
1987-07-13 11:49:32 +00:00
if ((left->nd_class==Def || left->nd_class==LinkDef) &&
(left->nd_type->tp_fund != T_RECORD ||
!(left->nd_def->df_kind & (D_MODULE|D_VARIABLE|D_FIELD))
1986-06-26 09:39:36 +00:00
)
) {
Xerror(left, "illegal selection", left->nd_def);
1986-06-26 09:39:36 +00:00
return 0;
}
if (left->nd_type->tp_fund != T_RECORD) {
node_error(left, "illegal selection");
return 0;
}
1986-06-26 09:39:36 +00:00
if (!(df = lookup(expp->nd_IDF, left->nd_type->rec_scope, 1))) {
1986-06-26 09:39:36 +00:00
id_not_declared(expp);
return 0;
}
else {
expp->nd_def = df;
1986-09-25 19:39:06 +00:00
expp->nd_type = RemoveEqual(df->df_type);
1986-06-26 09:39:36 +00:00
expp->nd_class = LinkDef;
if (!(df->df_flags & (D_EXPORTED|D_QEXPORTED))) {
/* Fields of a record are always D_QEXPORTED,
so ...
1986-06-17 12:04:05 +00:00
*/
Xerror(expp, "not exported from qualifying module", df);
1986-06-17 12:04:05 +00:00
}
1986-04-08 23:34:10 +00:00
}
1986-06-26 09:39:36 +00:00
1987-07-13 11:49:32 +00:00
if ((left->nd_class == Def || left->nd_class == LinkDef) &&
left->nd_def->df_kind == D_MODULE) {
1986-06-26 09:39:36 +00:00
expp->nd_class = Def;
FreeNode(left);
expp->nd_left = 0;
}
else return 1;
1986-05-28 18:36:51 +00:00
}
1986-06-26 09:39:36 +00:00
assert(expp->nd_class == Def);
1987-07-13 11:49:32 +00:00
return expp->nd_def->df_kind != D_ERROR;
1987-07-13 10:30:37 +00:00
}
STATIC int
ChkExLinkOrName(expp)
register struct node *expp;
{
/* Check either an ID or an ID.ID [.ID]* occurring in an
expression.
*/
register struct def *df;
if (! ChkLinkOrName(expp)) return 0;
1986-06-26 09:39:36 +00:00
df = expp->nd_def;
if (df->df_kind & (D_ENUM | D_CONST)) {
1986-10-06 20:36:30 +00:00
/* Replace an enum-literal or a CONST identifier by its value.
*/
1986-06-26 09:39:36 +00:00
if (df->df_kind == D_ENUM) {
expp->nd_class = Value;
expp->nd_INT = df->enm_val;
expp->nd_symb = INTEGER;
}
else {
1986-10-06 20:36:30 +00:00
unsigned int ln = expp->nd_lineno;
1986-06-26 09:39:36 +00:00
assert(df->df_kind == D_CONST);
*expp = *(df->con_const);
expp->nd_lineno = ln;
}
}
1986-10-06 20:36:30 +00:00
if (!(df->df_kind & D_VALUE)) {
Xerror(expp, "value expected", df);
1986-06-26 09:39:36 +00:00
}
if (df->df_kind == D_PROCEDURE) {
1986-10-06 20:36:30 +00:00
/* Check that this procedure is one that we may take the
address from.
1986-06-26 09:39:36 +00:00
*/
if (df->df_type == std_type || df->df_scope->sc_level > 0) {
/* Address of standard or nested procedure
taken.
*/
1986-10-06 20:36:30 +00:00
node_error(expp, "standard or local procedures may not be assigned");
1986-06-26 09:39:36 +00:00
return 0;
}
}
return 1;
1986-06-17 12:04:05 +00:00
}
1986-04-22 22:36:16 +00:00
1986-06-17 12:04:05 +00:00
STATIC int
ChkElement(expp, tp, set, level)
struct node **expp;
1986-06-04 09:01:48 +00:00
register struct type *tp;
1986-04-09 18:14:49 +00:00
arith **set;
1986-04-08 23:34:10 +00:00
{
/* Check elements of a set. This routine may call itself
1986-04-09 18:14:49 +00:00
recursively.
Also try to compute the set!
1986-04-08 23:34:10 +00:00
*/
register struct node *expr = *expp;
register struct node *left = expr->nd_left;
register struct node *right = expr->nd_right;
1987-05-18 15:57:33 +00:00
register arith i;
1986-04-18 17:53:47 +00:00
if (expr->nd_class == Link && expr->nd_symb == UPTO) {
1986-04-09 18:14:49 +00:00
/* { ... , expr1 .. expr2, ... }
First check expr1 and expr2, and try to compute them.
*/
if (!ChkElement(&(expr->nd_left), tp, set, 1) ||
!ChkElement(&(expr->nd_right), tp, set, 1)) {
1986-04-08 23:34:10 +00:00
return 0;
}
1986-04-22 22:36:16 +00:00
if (left->nd_class == Value && right->nd_class == Value) {
1986-04-09 18:14:49 +00:00
/* We have a constant range. Put all elements in the
set
*/
1986-04-22 22:36:16 +00:00
if (left->nd_INT > right->nd_INT) {
1987-05-18 15:57:33 +00:00
node_error(expr, "lower bound exceeds upper bound in range");
1986-10-06 20:36:30 +00:00
return 0;
1986-04-08 23:34:10 +00:00
}
1986-04-22 22:36:16 +00:00
for (i=left->nd_INT; i<=right->nd_INT; i++) {
(*set)[i/wrd_bits] |= (1<<(i%wrd_bits));
1986-04-09 18:14:49 +00:00
}
FreeNode(expr);
*expp = 0;
1986-04-08 23:34:10 +00:00
}
1986-04-22 22:36:16 +00:00
1986-04-08 23:34:10 +00:00
return 1;
}
1986-04-09 18:14:49 +00:00
/* Here, a single element is checked
*/
if (!ChkExpression(expr)) return 0;
1986-04-22 22:36:16 +00:00
if (!TstCompat(tp, expr->nd_type)) {
node_error(expr, "set element has incompatible type");
1986-10-06 20:36:30 +00:00
return 0;
1986-04-08 23:34:10 +00:00
}
1986-04-22 22:36:16 +00:00
if (expr->nd_class == Value) {
1986-04-22 22:36:16 +00:00
/* a constant element
*/
1986-10-06 20:36:30 +00:00
arith low, high;
i = expr->nd_INT;
1986-10-06 20:36:30 +00:00
getbounds(tp, &low, &high);
1986-04-22 22:36:16 +00:00
1986-10-06 20:36:30 +00:00
if (i < low || i > high) {
node_error(expr, "set element out of range");
1986-10-06 20:36:30 +00:00
return 0;
1986-04-08 23:34:10 +00:00
}
1986-04-22 22:36:16 +00:00
if (! level) {
(*set)[i/wrd_bits] |= (1 << (i%wrd_bits));
FreeNode(expr);
*expp = 0;
}
1986-04-08 23:34:10 +00:00
}
1986-04-22 22:36:16 +00:00
1986-04-08 18:15:46 +00:00
return 1;
}
1986-06-17 12:04:05 +00:00
STATIC int
ChkSet(expp)
1986-06-17 12:04:05 +00:00
register struct node *expp;
1986-04-09 18:14:49 +00:00
{
1986-06-17 12:04:05 +00:00
/* Check the legality of a SET aggregate, and try to evaluate it
compile time. Unfortunately this is all rather complicated.
1986-04-09 18:14:49 +00:00
*/
1986-06-17 12:04:05 +00:00
register struct type *tp;
register struct node *nd;
register struct def *df;
unsigned size;
int retval = 1;
1986-06-17 12:04:05 +00:00
assert(expp->nd_symb == SET);
expp->nd_class = Set;
1986-06-17 12:04:05 +00:00
/* First determine the type of the set
*/
if (nd = expp->nd_left) {
/* A type was given. Check it out
*/
if (! ChkDesignator(nd)) return 0;
1987-07-13 10:30:37 +00:00
assert(nd->nd_class == Def || nd->nd_class == LinkDef);
1986-06-17 12:04:05 +00:00
df = nd->nd_def;
1986-10-06 20:36:30 +00:00
if (!is_type(df) ||
(df->df_type->tp_fund != T_SET)) {
1986-10-06 20:36:30 +00:00
if (df->df_kind != D_ERROR) {
1987-07-13 10:30:37 +00:00
Xerror(nd, "not a SET type", df);
1986-10-06 20:36:30 +00:00
}
1986-06-17 12:04:05 +00:00
return 0;
}
tp = df->df_type;
FreeNode(nd);
1986-06-17 12:04:05 +00:00
expp->nd_left = 0;
1986-04-09 18:14:49 +00:00
}
1986-06-17 12:04:05 +00:00
else tp = bitset_type;
expp->nd_type = tp;
nd = expp->nd_right;
/* Now check the elements given, and try to compute a constant set.
1987-05-13 13:04:28 +00:00
First allocate room for the set.
1986-06-17 12:04:05 +00:00
*/
size = tp->tp_size * (sizeof(arith) / word_size);
expp->nd_set = (arith *) Malloc(size);
clear((char *) (expp->nd_set) , size);
1986-06-17 12:04:05 +00:00
/* Now check the elements, one by one
*/
while (nd) {
assert(nd->nd_class == Link && nd->nd_symb == ',');
if (!ChkElement(&(nd->nd_left), ElementType(tp),
&(expp->nd_set), 0)) {
retval = 0;
}
if (nd->nd_left) expp->nd_class = Xset;
1986-06-17 12:04:05 +00:00
nd = nd->nd_right;
}
if (expp->nd_class == Set) {
1986-06-17 12:04:05 +00:00
FreeNode(expp->nd_right);
expp->nd_right = 0;
}
return retval;
1986-04-09 18:14:49 +00:00
}
1986-06-17 12:04:05 +00:00
STATIC struct node *
getarg(argp, bases, designator, edf)
1986-05-30 18:48:00 +00:00
struct node **argp;
struct def *edf;
1986-04-10 01:08:49 +00:00
{
1986-06-17 12:04:05 +00:00
/* 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.
*/
1986-09-25 19:39:06 +00:00
register struct node *arg = (*argp)->nd_right;
1986-06-17 12:04:05 +00:00
register struct node *left;
1986-04-10 01:08:49 +00:00
1986-09-25 19:39:06 +00:00
if (! arg) {
Xerror(*argp, "too few arguments supplied", edf);
1986-04-10 01:08:49 +00:00
return 0;
}
1986-06-17 12:04:05 +00:00
left = arg->nd_left;
1986-11-26 16:40:45 +00:00
*argp = arg;
1986-06-17 12:04:05 +00:00
1986-09-25 19:39:06 +00:00
if (designator ? !ChkVariable(left) : !ChkExpression(left)) {
1986-05-23 09:46:31 +00:00
return 0;
}
1986-06-17 12:04:05 +00:00
1987-07-13 10:30:37 +00:00
if (designator && (left->nd_class==Def || left->nd_class==LinkDef)) {
1986-07-14 15:00:08 +00:00
left->nd_def->df_flags |= D_NOREG;
}
1986-09-25 19:39:06 +00:00
if (bases) {
1986-10-06 20:36:30 +00:00
if (!(BaseType(left->nd_type)->tp_fund & bases)) {
Xerror(arg, "unexpected parameter type", edf);
1986-09-25 19:39:06 +00:00
return 0;
}
1986-04-10 01:08:49 +00:00
}
1986-05-30 18:48:00 +00:00
1986-06-17 12:04:05 +00:00
return left;
1986-04-10 01:08:49 +00:00
}
1986-06-17 12:04:05 +00:00
STATIC struct node *
getname(argp, kinds, bases, edf)
1986-05-30 18:48:00 +00:00
struct node **argp;
struct def *edf;
1986-04-10 01:08:49 +00:00
{
1986-10-06 20:36:30 +00:00
/* Get the next argument from argument list "argp".
The argument must indicate a definition, and the
definition kind must be one of "kinds".
*/
1986-05-30 18:48:00 +00:00
register struct node *arg = *argp;
1986-10-06 20:36:30 +00:00
register struct node *left;
1986-05-30 18:48:00 +00:00
1986-11-26 16:40:45 +00:00
*argp = arg->nd_right;
1986-05-30 18:48:00 +00:00
if (!arg->nd_right) {
Xerror(arg, "too few arguments supplied", edf);
1986-04-10 01:08:49 +00:00
return 0;
}
1986-06-17 12:04:05 +00:00
1986-05-30 18:48:00 +00:00
arg = arg->nd_right;
1986-10-06 20:36:30 +00:00
left = arg->nd_left;
if (! ChkDesignator(left)) return 0;
1986-04-25 10:14:08 +00:00
1986-10-06 20:36:30 +00:00
if (left->nd_class != Def && left->nd_class != LinkDef) {
Xerror(arg, "identifier expected", edf);
1986-06-20 14:36:49 +00:00
return 0;
}
1986-04-25 10:14:08 +00:00
1986-10-06 20:36:30 +00:00
if (!(left->nd_def->df_kind & kinds)) {
Xerror(arg, "unexpected parameter type", edf);
1986-04-10 01:08:49 +00:00
return 0;
}
1986-04-25 10:14:08 +00:00
if (bases) {
if (!(left->nd_type->tp_fund & bases)) {
Xerror(arg, "unexpected parameter type", edf);
return 0;
}
}
1986-10-06 20:36:30 +00:00
return left;
1986-04-10 01:08:49 +00:00
}
1986-06-17 12:04:05 +00:00
STATIC int
ChkProcCall(expp)
1986-10-06 20:36:30 +00:00
struct node *expp;
1986-06-17 12:04:05 +00:00
{
/* Check a procedure call
*/
register struct node *left;
struct def *edf = 0;
1986-06-17 12:04:05 +00:00
register struct paramlist *param;
char ebuf[256];
int retval = 1;
int cnt = 0;
1986-06-17 12:04:05 +00:00
left = expp->nd_left;
if (left->nd_class == Def || left->nd_class == LinkDef) {
edf = left->nd_def;
}
1986-11-17 13:08:18 +00:00
if (left->nd_type == error_type) {
/* Just check parameters as if they were value parameters
*/
expp->nd_type = error_type;
while (expp->nd_right) {
getarg(&expp, 0, 0, edf);
}
return 0;
}
1986-09-25 19:39:06 +00:00
expp->nd_type = RemoveEqual(ResultType(left->nd_type));
1986-06-17 12:04:05 +00:00
1986-10-06 20:36:30 +00:00
/* Check parameter list
*/
1986-06-20 14:36:49 +00:00
for (param = ParamList(left->nd_type); param; param = param->next) {
if (!(left = getarg(&expp, 0, IsVarParam(param), edf))) {
return 0;
}
cnt++;
1986-06-17 12:04:05 +00:00
if (left->nd_symb == STRING) {
TryToString(left, TypeOfParam(param));
}
if (! TstParCompat(RemoveEqual(TypeOfParam(param)),
1986-06-17 12:04:05 +00:00
left->nd_type,
IsVarParam(param),
left)) {
sprint(ebuf, "type incompatibility in parameter %d",
cnt);
Xerror(left, ebuf, edf);
retval = 0;
1986-06-17 12:04:05 +00:00
}
}
1986-10-06 20:36:30 +00:00
if (expp->nd_right) {
Xerror(expp->nd_right, "too many parameters supplied", edf);
1986-11-26 16:40:45 +00:00
while (expp->nd_right) {
getarg(&expp, 0, 0, edf);
}
1986-06-17 12:04:05 +00:00
return 0;
}
return retval;
1986-06-17 12:04:05 +00:00
}
1987-05-18 15:57:33 +00:00
int
ChkFunCall(expp)
register struct node *expp;
{
/* Check a call that must have a result
*/
int retval = 1;
if (!ChkCall(expp)) retval = 0;
if (expp->nd_type == 0) {
node_error(expp, "function call expected");
expp->nd_type = error_type;
retval = 0;
}
return retval;
}
1986-04-08 18:15:46 +00:00
int
ChkCall(expp)
1986-04-08 18:15:46 +00:00
register struct node *expp;
{
1986-04-11 11:57:19 +00:00
/* Check something that looks like a procedure or function call.
1986-10-06 20:36:30 +00:00
Of course this does not have to be a call at all,
1986-04-11 11:57:19 +00:00
it may also be a cast or a standard procedure call.
*/
1986-04-09 18:14:49 +00:00
register struct node *left;
STATIC int ChkStandard();
STATIC int ChkCast();
1986-04-09 18:14:49 +00:00
1986-04-18 17:53:47 +00:00
/* First, get the name of the function or procedure
*/
1986-04-09 18:14:49 +00:00
expp->nd_type = error_type;
left = expp->nd_left;
1986-11-17 13:08:18 +00:00
if (ChkDesignator(left)) {
if (IsCast(left)) {
/* It was a type cast.
*/
return ChkCast(expp, left);
}
1986-04-09 18:14:49 +00:00
1986-11-26 16:40:45 +00:00
if (IsProcCall(left) || left->nd_type == error_type) {
1986-11-17 13:08:18 +00:00
/* 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, left);
}
/* Here, we have found a real procedure call.
The left hand side may also represent a procedure
variable.
1986-04-11 11:57:19 +00:00
*/
1986-04-09 18:14:49 +00:00
}
1986-11-17 13:08:18 +00:00
else node_error(left, "procedure, type, or function expected");
1986-04-09 18:14:49 +00:00
}
1986-11-17 13:08:18 +00:00
return ChkProcCall(expp);
1986-04-08 18:15:46 +00:00
}
1986-06-17 12:04:05 +00:00
STATIC struct type *
1986-06-06 02:22:09 +00:00
ResultOfOperation(operator, tp)
struct type *tp;
{
1986-10-06 20:36:30 +00:00
/* Return the result type of the binary operation "operator",
with operand type "tp".
*/
1986-06-06 02:22:09 +00:00
switch(operator) {
case '=':
case '#':
case GREATEREQUAL:
case LESSEQUAL:
case '<':
case '>':
case IN:
return bool_type;
}
return tp;
}
1986-06-17 12:04:05 +00:00
STATIC int
1986-06-06 02:22:09 +00:00
Boolean(operator)
{
return operator == OR || operator == AND || operator == '&';
}
1986-06-17 12:04:05 +00:00
STATIC int
1986-06-06 02:22:09 +00:00
AllowedTypes(operator)
{
1986-10-06 20:36:30 +00:00
/* Return a bit mask indicating the allowed operand types
for binary operator "operator".
*/
1986-06-06 02:22:09 +00:00
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:
case '&':
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*/
}
1986-06-17 12:04:05 +00:00
STATIC int
ChkAddress(tpl, tpr)
1986-06-17 12:04:05 +00:00
register struct type *tpl, *tpr;
{
1986-10-06 20:36:30 +00:00
/* Check that either "tpl" or "tpr" are both of type
address_type, or that one of them is, but the other is
of type cardinal.
*/
1986-06-17 12:04:05 +00:00
if (tpl == address_type) {
1986-10-06 20:36:30 +00:00
return tpr == address_type || (tpr->tp_fund & T_CARDINAL);
1986-06-17 12:04:05 +00:00
}
if (tpr == address_type) {
1986-10-06 20:36:30 +00:00
return (tpl->tp_fund & T_CARDINAL);
1986-06-17 12:04:05 +00:00
}
return 0;
}
STATIC int
ChkBinOper(expp)
1986-04-08 18:15:46 +00:00
register struct node *expp;
{
1986-04-10 01:08:49 +00:00
/* Check a binary operation.
1986-04-08 18:15:46 +00:00
*/
1986-06-10 13:18:52 +00:00
register struct node *left, *right;
struct type *tpl, *tpr;
1986-06-06 02:22:09 +00:00
int allowed;
int retval;
1986-06-06 02:22:09 +00:00
1986-06-10 13:18:52 +00:00
left = expp->nd_left;
right = expp->nd_right;
retval = ChkExpression(left) & ChkExpression(right);
1986-06-10 13:18:52 +00:00
1986-06-26 09:39:36 +00:00
tpl = BaseType(left->nd_type);
tpr = BaseType(right->nd_type);
1986-06-06 02:22:09 +00:00
1986-04-08 18:15:46 +00:00
if (tpl == intorcard_type) {
if (tpr == int_type || tpr == card_type) {
1986-04-23 22:12:22 +00:00
left->nd_type = tpl = tpr;
1986-04-08 18:15:46 +00:00
}
}
if (tpr == intorcard_type) {
if (tpl == int_type || tpl == card_type) {
1986-04-23 22:12:22 +00:00
right->nd_type = tpr = tpl;
1986-04-08 18:15:46 +00:00
}
}
1986-06-06 02:22:09 +00:00
1986-10-06 20:36:30 +00:00
expp->nd_type = ResultOfOperation(expp->nd_symb, tpr);
1986-04-08 18:15:46 +00:00
1986-10-06 20:36:30 +00:00
/* 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.
*/
1986-04-08 18:15:46 +00:00
if (expp->nd_symb == IN) {
if (tpr->tp_fund != T_SET) {
node_error(expp, "\"IN\": right operand must be a set");
return 0;
}
1986-06-26 09:39:36 +00:00
if (!TstAssCompat(tpl, ElementType(tpr))) {
1986-04-23 22:12:22 +00:00
/* Assignment compatible ???
1986-04-28 18:06:58 +00:00
I don't know! Should we be allowed to check
if a INTEGER is a member of a BITSET???
1986-04-23 22:12:22 +00:00
*/
node_error(expp, "\"IN\": incompatible types");
1986-04-08 18:15:46 +00:00
return 0;
}
1986-04-23 22:12:22 +00:00
if (left->nd_class == Value && right->nd_class == Set) {
1986-04-11 11:57:19 +00:00
cstset(expp);
}
return retval;
1986-04-08 18:15:46 +00:00
}
if (!retval) return 0;
1986-06-06 02:22:09 +00:00
allowed = AllowedTypes(expp->nd_symb);
1986-09-25 19:39:06 +00:00
1986-10-06 20:36:30 +00:00
if (!(tpr->tp_fund & allowed) || !(tpl->tp_fund & allowed)) {
if (!((T_CARDINAL & allowed) &&
ChkAddress(tpl, tpr))) {
node_error(expp, "\"%s\": illegal operand type(s)",
symbol2str(expp->nd_symb));
1986-10-06 20:36:30 +00:00
return 0;
}
if (expp->nd_type->tp_fund & T_CARDINAL) {
expp->nd_type = address_type;
}
}
1986-09-25 19:39:06 +00:00
if (Boolean(expp->nd_symb) && tpl != bool_type) {
node_error(expp, "\"%s\": illegal operand type(s)",
symbol2str(expp->nd_symb));
1986-09-25 19:39:06 +00:00
return 0;
}
1986-10-06 20:36:30 +00:00
/* Operands must be compatible (distilled from Def 8.2)
*/
if (!TstCompat(tpl, tpr)) {
node_error(expp, "\"%s\": incompatible types", symbol2str(expp->nd_symb));
1986-10-06 20:36:30 +00:00
return 0;
1986-06-06 02:22:09 +00:00
}
1986-04-18 17:53:47 +00:00
1986-06-06 02:22:09 +00:00
if (tpl->tp_fund == T_SET) {
if (left->nd_class == Set && right->nd_class == Set) {
cstset(expp);
1986-04-08 18:15:46 +00:00
}
}
1986-06-06 02:22:09 +00:00
else if ( tpl->tp_fund != T_REAL &&
left->nd_class == Value && right->nd_class == Value) {
cstbin(expp);
1986-04-08 18:15:46 +00:00
}
1986-06-06 02:22:09 +00:00
return 1;
1986-04-08 18:15:46 +00:00
}
1986-06-17 12:04:05 +00:00
STATIC int
ChkUnOper(expp)
1986-04-08 18:15:46 +00:00
register struct node *expp;
{
1986-04-10 01:08:49 +00:00
/* Check an unary operation.
1986-04-08 18:15:46 +00:00
*/
1986-04-28 18:06:58 +00:00
register struct node *right = expp->nd_right;
1986-06-10 13:18:52 +00:00
register struct type *tpr;
if (! ChkExpression(right)) return 0;
1986-04-08 18:15:46 +00:00
1986-10-06 20:36:30 +00:00
expp->nd_type = tpr = BaseType(right->nd_type);
if (tpr == address_type) tpr = card_type;
1986-04-08 18:15:46 +00:00
switch(expp->nd_symb) {
case '+':
if (!(tpr->tp_fund & T_NUMERIC)) break;
/* fall through */
case '(':
*expp = *right;
free_node(right);
return 1;
1986-04-18 17:53:47 +00:00
1986-04-08 18:15:46 +00:00
case '-':
1986-04-11 11:57:19 +00:00
if (tpr->tp_fund & T_INTORCARD) {
if (tpr == intorcard_type || tpr == card_type) {
1986-05-14 09:03:51 +00:00
expp->nd_type = int_type;
}
1986-04-28 18:06:58 +00:00
if (right->nd_class == Value) {
1986-04-08 18:15:46 +00:00
cstunary(expp);
}
return 1;
1986-04-11 11:57:19 +00:00
}
else if (tpr->tp_fund == T_REAL) {
1986-04-28 18:06:58 +00:00
if (right->nd_class == Value) {
1986-06-04 09:01:48 +00:00
if (*(right->nd_REL) == '-') (right->nd_REL)++;
else (right->nd_REL)--;
1986-05-28 18:36:51 +00:00
expp->nd_class = Value;
1986-06-04 09:01:48 +00:00
expp->nd_symb = REAL;
expp->nd_REL = right->nd_REL;
1986-04-28 18:06:58 +00:00
FreeNode(right);
1986-04-08 18:15:46 +00:00
expp->nd_right = 0;
}
return 1;
}
break;
1986-04-18 17:53:47 +00:00
1986-04-08 18:15:46 +00:00
case NOT:
1986-04-22 22:36:16 +00:00
case '~':
1986-04-08 18:15:46 +00:00
if (tpr == bool_type) {
1986-04-28 18:06:58 +00:00
if (right->nd_class == Value) {
1986-04-08 18:15:46 +00:00
cstunary(expp);
}
return 1;
}
break;
1986-04-18 17:53:47 +00:00
1986-04-08 18:15:46 +00:00
default:
crash("ChkUnOper");
1986-04-08 18:15:46 +00:00
}
node_error(expp, "\"%s\": illegal operand", symbol2str(expp->nd_symb));
1986-04-08 18:15:46 +00:00
return 0;
}
1986-04-23 22:12:22 +00:00
1986-06-17 12:04:05 +00:00
STATIC struct node *
getvariable(argp, edf)
1986-05-30 18:48:00 +00:00
struct node **argp;
struct def *edf;
1986-04-23 22:12:22 +00:00
{
1986-10-06 20:36:30 +00:00
/* Get the next argument from argument list "argp".
It must obey the rules of "ChkVariable".
*/
1986-05-30 18:48:00 +00:00
register struct node *arg = *argp;
1986-04-28 18:06:58 +00:00
1986-04-23 22:12:22 +00:00
arg = arg->nd_right;
if (!arg) {
Xerror(arg, "too few parameters supplied", edf);
1986-04-23 22:12:22 +00:00
return 0;
}
1986-05-30 18:48:00 +00:00
*argp = arg;
1986-10-06 20:36:30 +00:00
arg = arg->nd_left;
if (! ChkVariable(arg)) return 0;
return arg;
1986-04-23 22:12:22 +00:00
}
1986-06-26 09:39:36 +00:00
STATIC int
ChkStandard(expp, left)
1986-05-30 18:48:00 +00:00
register struct node *expp, *left;
1986-04-23 22:12:22 +00:00
{
/* Check a call of a standard procedure or function
*/
1986-05-30 18:48:00 +00:00
struct node *arg = expp;
register struct def *edf;
1986-05-30 18:48:00 +00:00
int std;
1986-04-23 22:12:22 +00:00
1987-07-13 10:30:37 +00:00
assert(left->nd_class == Def || left->nd_class == LinkDef);
edf = left->nd_def;
1987-07-13 10:30:37 +00:00
std = edf->df_value.df_stdname;
1986-05-30 18:48:00 +00:00
switch(std) {
1986-04-23 22:12:22 +00:00
case S_ABS:
if (!(left = getarg(&arg, T_NUMERIC, 0, edf))) return 0;
1986-04-23 22:12:22 +00:00
expp->nd_type = left->nd_type;
1986-06-04 09:01:48 +00:00
if (left->nd_class == Value &&
expp->nd_type->tp_fund != T_REAL) {
cstcall(expp, S_ABS);
}
1986-04-23 22:12:22 +00:00
break;
case S_CAP:
expp->nd_type = char_type;
if (!(left = getarg(&arg, T_CHAR, 0, edf))) return 0;
1986-04-23 22:12:22 +00:00
if (left->nd_class == Value) cstcall(expp, S_CAP);
break;
case S_CHR:
expp->nd_type = char_type;
if (!(left = getarg(&arg, T_INTORCARD, 0, edf))) return 0;
1986-04-23 22:12:22 +00:00
if (left->nd_class == Value) cstcall(expp, S_CHR);
break;
case S_FLOATD:
1986-04-23 22:12:22 +00:00
case S_FLOAT:
expp->nd_type = real_type;
if (std == S_FLOATD) expp->nd_type = longreal_type;
if (!(left = getarg(&arg, T_INTORCARD, 0, edf))) return 0;
1986-04-23 22:12:22 +00:00
break;
case S_LONG: {
struct type *tp;
if (!(left = getarg(&arg, 0, 0, edf))) {
return 0;
}
tp = BaseType(left->nd_type);
if (tp == int_type) expp->nd_type = longint_type;
else if (tp == real_type) expp->nd_type = longreal_type;
else {
expp->nd_type = error_type;
Xerror(left, "unexpected parameter type", edf);
}
if (left->nd_class == Value) cstcall(expp, S_LONG);
break;
}
case S_SHORT: {
struct type *tp;
if (!(left = getarg(&arg, 0, 0, edf))) {
return 0;
}
tp = BaseType(left->nd_type);
if (tp == longint_type) expp->nd_type = int_type;
else if (tp == longreal_type) expp->nd_type = real_type;
else {
expp->nd_type = error_type;
Xerror(left, "unexpected parameter type", edf);
}
if (left->nd_class == Value) cstcall(expp, S_SHORT);
break;
}
1986-04-23 22:12:22 +00:00
case S_HIGH:
if (!(left = getarg(&arg, T_ARRAY|T_STRING|T_CHAR, 0, edf))) {
return 0;
}
1986-06-26 09:39:36 +00:00
if (IsConformantArray(left->nd_type)) {
/* A conformant array has no explicit index type,
but it is a subrange with lower bound 0, so
it is of type CARDINAL !!!
1986-04-23 22:12:22 +00:00
*/
expp->nd_type = card_type;
1986-10-22 15:38:24 +00:00
break;
1986-06-26 09:39:36 +00:00
}
1986-10-22 15:38:24 +00:00
if (left->nd_type->tp_fund == T_ARRAY) {
1986-06-26 09:39:36 +00:00
expp->nd_type = IndexType(left->nd_type);
cstcall(expp, S_MAX);
1986-10-22 15:38:24 +00:00
break;
}
if (left->nd_symb != STRING) {
Xerror(left,"array parameter expected", edf);
return 0;
1986-04-23 22:12:22 +00:00
}
expp->nd_type = card_type;
1986-10-22 15:38:24 +00:00
expp->nd_class = Value;
/* Notice that we could disallow HIGH("") here by checking
that left->nd_type->tp_fund != T_CHAR || left->nd_INT != 0.
??? For the time being, we don't. !!!
Maybe the empty string should not be allowed at all.
*/
1986-10-22 15:38:24 +00:00
expp->nd_INT = left->nd_type->tp_fund == T_CHAR ? 0 :
left->nd_SLE - 1;
expp->nd_symb = INTEGER;
1986-04-23 22:12:22 +00:00
break;
case S_MAX:
case S_MIN:
if (!(left = getname(&arg, D_ISTYPE, T_DISCRETE, edf))) {
1986-06-17 12:04:05 +00:00
return 0;
}
1986-05-30 18:48:00 +00:00
expp->nd_type = left->nd_type;
cstcall(expp,std);
1986-04-23 22:12:22 +00:00
break;
case S_ODD:
if (!(left = getarg(&arg, T_INTORCARD, 0, edf))) return 0;
1986-04-23 22:12:22 +00:00
expp->nd_type = bool_type;
1986-05-30 18:48:00 +00:00
if (left->nd_class == Value) cstcall(expp, S_ODD);
1986-04-23 22:12:22 +00:00
break;
case S_ORD:
if (!(left = getarg(&arg, T_DISCRETE, 0, edf))) return 0;
1986-04-23 22:12:22 +00:00
expp->nd_type = card_type;
1986-05-30 18:48:00 +00:00
if (left->nd_class == Value) cstcall(expp, S_ORD);
1986-04-23 22:12:22 +00:00
break;
1986-06-20 14:36:49 +00:00
case S_NEW:
case S_DISPOSE:
{
static int warning_given = 0;
if (!warning_given) {
warning_given = 1;
node_warning(expp, W_OLDFASHIONED, "NEW and DISPOSE are obsolete");
1986-06-20 14:36:49 +00:00
}
}
if (! (left = getvariable(&arg, edf))) return 0;
1986-06-20 14:36:49 +00:00
if (! (left->nd_type->tp_fund == T_POINTER)) {
Xerror(left, "pointer variable expected", edf);
1986-06-20 14:36:49 +00:00
return 0;
}
/* Now, make it look like a call to ALLOCATE or DEALLOCATE */
{
struct token dt;
struct node *nd;
1987-07-13 10:30:37 +00:00
dt.TOK_INT = PointedtoType(left->nd_type)->tp_size;
dt.tk_symb = INTEGER;
dt.tk_lineno = left->nd_lineno;
nd = MkLeaf(Value, &dt);
1986-06-20 14:36:49 +00:00
nd->nd_type = card_type;
1987-07-13 10:30:37 +00:00
dt.tk_symb = ',';
arg->nd_right = MkNode(Link, nd, NULLNODE, &dt);
1986-06-20 14:36:49 +00:00
/* Ignore other arguments to NEW and/or DISPOSE ??? */
FreeNode(expp->nd_left);
1987-07-13 10:30:37 +00:00
dt.tk_symb = IDENT;
dt.tk_lineno = expp->nd_left->nd_lineno;
dt.TOK_IDF = str2idf(std == S_NEW ?
1986-06-20 14:36:49 +00:00
"ALLOCATE" : "DEALLOCATE", 0);
1987-07-13 10:30:37 +00:00
expp->nd_left = MkLeaf(Name, &dt);
1986-06-20 14:36:49 +00:00
}
return ChkCall(expp);
1986-06-20 14:36:49 +00:00
1986-04-23 22:12:22 +00:00
case S_TSIZE: /* ??? */
case S_SIZE:
expp->nd_type = intorcard_type;
if (!(left = getname(&arg,D_FIELD|D_VARIABLE|D_ISTYPE,0,edf))) {
return 0;
}
if (! IsConformantArray(left->nd_type)) cstcall(expp, S_SIZE);
else node_warning(expp,
W_STRICT,
"%s on conformant array",
expp->nd_left->nd_def->df_idf->id_text);
1986-04-23 22:12:22 +00:00
break;
case S_TRUNCD:
1986-04-23 22:12:22 +00:00
case S_TRUNC:
expp->nd_type = card_type;
if (std == S_TRUNCD) expp->nd_type = longint_type;
if (!(left = getarg(&arg, T_REAL, 0, edf))) return 0;
1986-04-23 22:12:22 +00:00
break;
case S_VAL:
if (!(left = getname(&arg, D_ISTYPE, T_DISCRETE, edf))) {
1986-04-23 22:12:22 +00:00
return 0;
}
1986-05-30 18:48:00 +00:00
expp->nd_type = left->nd_def->df_type;
1986-04-23 22:12:22 +00:00
expp->nd_right = arg->nd_right;
arg->nd_right = 0;
FreeNode(arg);
1986-05-30 18:48:00 +00:00
arg = expp;
if (!(left = getarg(&arg, T_INTORCARD, 0, edf))) return 0;
1986-05-30 18:48:00 +00:00
if (left->nd_class == Value) cstcall(expp, S_VAL);
1986-04-23 22:12:22 +00:00
break;
case S_ADR:
expp->nd_type = address_type;
if (!(left = getarg(&arg, 0, 1, edf))) return 0;
1986-04-23 22:12:22 +00:00
break;
case S_DEC:
case S_INC:
expp->nd_type = 0;
if (! (left = getvariable(&arg, edf))) return 0;
1986-05-30 18:48:00 +00:00
if (! (left->nd_type->tp_fund & T_DISCRETE)) {
Xerror(left,"illegal parameter type", edf);
1986-05-30 18:48:00 +00:00
return 0;
}
1986-04-23 22:12:22 +00:00
if (arg->nd_right) {
if (! getarg(&arg, T_INTORCARD, 0, edf)) return 0;
1986-04-23 22:12:22 +00:00
}
break;
case S_HALT:
expp->nd_type = 0;
break;
case S_EXCL:
case S_INCL:
{
struct type *tp;
expp->nd_type = 0;
if (!(left = getvariable(&arg, edf))) return 0;
1986-05-30 18:48:00 +00:00
tp = left->nd_type;
1986-04-23 22:12:22 +00:00
if (tp->tp_fund != T_SET) {
Xerror(arg, "SET parameter expected", edf);
1986-04-23 22:12:22 +00:00
return 0;
}
if (!(left = getarg(&arg, T_DISCRETE, 0, edf))) return 0;
1986-06-26 09:39:36 +00:00
if (!TstAssCompat(ElementType(tp), left->nd_type)) {
1986-04-28 18:06:58 +00:00
/* What type of compatibility do we want here?
apparently assignment compatibility! ??? ???
*/
Xerror(arg, "unexpected parameter type", edf);
1986-04-23 22:12:22 +00:00
return 0;
}
break;
}
default:
crash("(ChkStandard)");
1986-04-23 22:12:22 +00:00
}
if (arg->nd_right) {
Xerror(arg->nd_right, "too many parameters supplied", edf);
1986-04-23 22:12:22 +00:00
return 0;
}
return 1;
}
1986-05-30 18:48:00 +00:00
1986-06-26 09:39:36 +00:00
STATIC int
ChkCast(expp, left)
1986-05-30 18:48:00 +00:00
register struct node *expp, *left;
{
/* 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 struct node *arg = expp->nd_right;
if ((! arg) || arg->nd_right) {
Xerror(expp, "too many parameters in type cast", left->nd_def);
1986-05-30 18:48:00 +00:00
return 0;
}
arg = arg->nd_left;
if (! ChkExpression(arg)) return 0;
1986-05-30 18:48:00 +00:00
if (arg->nd_type->tp_size != left->nd_type->tp_size &&
(arg->nd_type->tp_size > word_size ||
left->nd_type->tp_size > word_size)) {
Xerror(expp, "unequal sizes in type cast", left->nd_def);
1986-05-30 18:48:00 +00:00
}
if (arg->nd_class == Value) {
struct type *tp = left->nd_type;
FreeNode(left);
expp->nd_right->nd_left = 0;
FreeNode(expp->nd_right);
expp->nd_left = expp->nd_right = 0;
*expp = *arg;
expp->nd_type = tp;
}
else expp->nd_type = left->nd_type;
return 1;
}
1986-06-04 09:01:48 +00:00
TryToString(nd, tp)
1986-12-01 10:06:53 +00:00
register struct node *nd;
1986-06-04 09:01:48 +00:00
struct type *tp;
{
1986-06-17 12:04:05 +00:00
/* Try a coercion from character constant to string.
*/
1986-09-25 19:39:06 +00:00
assert(nd->nd_symb == STRING);
1986-06-04 09:01:48 +00:00
if (tp->tp_fund == T_ARRAY && nd->nd_type == char_type) {
int ch = nd->nd_INT;
nd->nd_type = standard_type(T_STRING, 1, (arith) 2);
nd->nd_token.tk_data.tk_str =
(struct string *) Malloc(sizeof(struct string));
nd->nd_STR = Salloc("X", 2);
*(nd->nd_STR) = ch;
nd->nd_SLE = 1;
}
}
1986-06-17 12:04:05 +00:00
1986-06-26 09:39:36 +00:00
STATIC int
no_desig(expp)
struct node *expp;
{
node_error(expp, "designator expected");
return 0;
}
STATIC int
done_before()
1986-06-26 09:39:36 +00:00
{
return 1;
}
1986-06-17 12:04:05 +00:00
extern int NodeCrash();
1986-06-26 09:39:36 +00:00
int (*ExprChkTable[])() = {
1986-10-06 20:36:30 +00:00
#ifdef DEBUG
ChkValue,
1986-10-06 20:36:30 +00:00
#else
done_before,
#endif
ChkArr,
ChkBinOper,
ChkUnOper,
ChkArrow,
1987-05-18 15:57:33 +00:00
ChkFunCall,
ChkExLinkOrName,
1986-06-17 12:04:05 +00:00
NodeCrash,
ChkSet,
1986-06-17 12:04:05 +00:00
NodeCrash,
NodeCrash,
ChkExLinkOrName,
1986-06-20 14:36:49 +00:00
NodeCrash
1986-06-17 12:04:05 +00:00
};
1986-06-26 09:39:36 +00:00
int (*DesigChkTable[])() = {
no_desig,
ChkArr,
1986-06-26 09:39:36 +00:00
no_desig,
no_desig,
ChkArrow,
1986-06-26 09:39:36 +00:00
no_desig,
ChkLinkOrName,
1986-06-26 09:39:36 +00:00
NodeCrash,
no_desig,
done_before,
NodeCrash,
ChkLinkOrName,
1986-06-26 09:39:36 +00:00
done_before
};