mirror of
https://forge.sourceware.org/marek/gcc.git
synced 2026-02-22 12:00:11 -05:00
This commit adapts the static scope checker to prelude packets. Signed-off-by: Jose E. Marchesi <jemarch@gnu.org> gcc/algol68/ChangeLog * a68-parser-scope.cc (scope_module_text): New function. (scope_module_declaration): Likewise. (scope_particular_program): Likewise. (scope_prelude_packet): Likewise. (a68_scope_checker): Call scope_particular_program and scope_prelude_packet. gcc/testsuite/ChangeLog * algol68/compile/warning-scope-module-1.a68: New test. * algol68/compile/warning-scope-module-2.a68: Likewise.
1047 lines
25 KiB
C++
1047 lines
25 KiB
C++
/* Static scope checker.
|
|
Copyright (C) 2001-2023 J. Marcel van der Veer.
|
|
Copyright (C) 2025 Jose E. Marchesi.
|
|
|
|
Original implementation by J. Marcel van der Veer.
|
|
Adapted for GCC by Jose E. Marchesi.
|
|
|
|
GCC is free software; you can redistribute it and/or modify it
|
|
under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3, or (at your option)
|
|
any later version.
|
|
|
|
GCC is distributed in the hope that it will be useful, but WITHOUT
|
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
|
License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with GCC; see the file COPYING3. If not see
|
|
<http://www.gnu.org/licenses/>. */
|
|
|
|
/* A static scope checker inspects the source. Note that ALGOL 68 also needs
|
|
dynamic scope checking. This phase concludes the parser. */
|
|
|
|
#include "config.h"
|
|
#include "system.h"
|
|
#include "coretypes.h"
|
|
#include "options.h"
|
|
|
|
#include "a68.h"
|
|
|
|
struct TUPLE_T
|
|
{
|
|
int level;
|
|
bool transient;
|
|
};
|
|
|
|
struct SCOPE_T
|
|
{
|
|
NODE_T *where;
|
|
TUPLE_T tuple;
|
|
SCOPE_T *next;
|
|
};
|
|
|
|
constexpr TUPLE_T *NO_TUPLE = nullptr;
|
|
constexpr SCOPE_T *NO_SCOPE = nullptr;
|
|
|
|
#define TUPLE(p) ((p)->tuple)
|
|
|
|
enum { NOT_TRANSIENT = 0, TRANSIENT };
|
|
|
|
static void gather_scopes_for_youngest (NODE_T *, SCOPE_T **);
|
|
static void scope_statement (NODE_T *, SCOPE_T **);
|
|
static void scope_enclosed_clause (NODE_T *, SCOPE_T **);
|
|
static void scope_formula (NODE_T *, SCOPE_T **);
|
|
static void scope_routine_text (NODE_T *, SCOPE_T **);
|
|
static void scope_access_clause (NODE_T *, SCOPE_T **);
|
|
|
|
/*
|
|
* Static scope checker.
|
|
*/
|
|
|
|
/* Scope_make_tuple. */
|
|
|
|
static TUPLE_T
|
|
scope_make_tuple (int e, int t)
|
|
{
|
|
static TUPLE_T z;
|
|
LEVEL (&z) = e;
|
|
TRANSIENT (&z) = t;
|
|
return z;
|
|
}
|
|
|
|
/* Link scope information into the list. */
|
|
|
|
static void
|
|
scope_add (SCOPE_T **sl, NODE_T *p, TUPLE_T tup)
|
|
{
|
|
if (sl != NO_VAR)
|
|
{
|
|
SCOPE_T *ns = (SCOPE_T *) xmalloc (sizeof (SCOPE_T));
|
|
WHERE (ns) = p;
|
|
TUPLE (ns) = tup;
|
|
NEXT (ns) = *sl;
|
|
*sl = ns;
|
|
}
|
|
}
|
|
|
|
/* Scope_check. */
|
|
|
|
static bool
|
|
scope_check (SCOPE_T *top, int mask, int dest)
|
|
{
|
|
int errors = 0;
|
|
|
|
/* Transient names cannot be stored. */
|
|
if (mask & TRANSIENT)
|
|
{
|
|
for (SCOPE_T *s = top; s != NO_SCOPE; FORWARD (s))
|
|
{
|
|
if (TRANSIENT (&TUPLE (s)) & TRANSIENT)
|
|
{
|
|
a68_error (WHERE (s), "attempt at storing a transient name");
|
|
STATUS_SET (WHERE (s), SCOPE_ERROR_MASK);
|
|
errors++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Potential scope violations. */
|
|
for (SCOPE_T *s = top; s != NO_SCOPE; FORWARD (s))
|
|
{
|
|
if (dest < LEVEL (&TUPLE (s)) && !STATUS_TEST (WHERE (s), SCOPE_ERROR_MASK))
|
|
{
|
|
MOID_T *ws = MOID (WHERE (s));
|
|
|
|
if (ws != NO_MOID)
|
|
{
|
|
if (IS_REF (ws) || IS (ws, PROC_SYMBOL) || IS (ws, FORMAT_SYMBOL) || IS (ws, UNION_SYMBOL))
|
|
a68_warning (WHERE (s), OPT_Wscope, "M A is a potential scope violation",
|
|
MOID (WHERE (s)), ATTRIBUTE (WHERE (s)));
|
|
}
|
|
STATUS_SET (WHERE (s), SCOPE_ERROR_MASK);
|
|
errors++;
|
|
}
|
|
}
|
|
return (errors == 0);
|
|
}
|
|
|
|
/* Scope_check_multiple. */
|
|
|
|
static bool
|
|
scope_check_multiple (SCOPE_T *top, int mask, SCOPE_T *dest)
|
|
{
|
|
bool no_err = true;
|
|
|
|
for (; dest != NO_SCOPE; FORWARD (dest))
|
|
no_err = no_err && scope_check (top, mask, LEVEL (&TUPLE (dest)));
|
|
return no_err;
|
|
}
|
|
|
|
/* Check_identifier_usage. */
|
|
|
|
static void
|
|
check_identifier_usage (TAG_T *t, NODE_T *p)
|
|
{
|
|
for (; p != NO_NODE; FORWARD (p))
|
|
{
|
|
if (IS (p, IDENTIFIER) && TAX (p) == t && ATTRIBUTE (MOID (t)) != PROC_SYMBOL)
|
|
a68_warning (p, OPT_Wuninitialized, "identifier S might be used uninitialised");
|
|
check_identifier_usage (t, SUB (p));
|
|
}
|
|
}
|
|
|
|
/* Scope_find_youngest_outside. */
|
|
|
|
static TUPLE_T
|
|
scope_find_youngest_outside (SCOPE_T *s, int treshold)
|
|
{
|
|
TUPLE_T z = scope_make_tuple (PRIMAL_SCOPE, NOT_TRANSIENT);
|
|
|
|
for (; s != NO_SCOPE; FORWARD (s))
|
|
{
|
|
if (LEVEL (&TUPLE (s)) > LEVEL (&z) && LEVEL (&TUPLE (s)) <= treshold)
|
|
z = TUPLE (s);
|
|
}
|
|
return z;
|
|
}
|
|
|
|
/* Scope_find_youngest. */
|
|
|
|
static TUPLE_T
|
|
scope_find_youngest (SCOPE_T *s)
|
|
{
|
|
return scope_find_youngest_outside (s, INT_MAX);
|
|
}
|
|
|
|
/*
|
|
* Routines for determining scope of ROUTINE TEXT or FORMAT TEXT.
|
|
*/
|
|
|
|
/* Get_declarer_elements. */
|
|
|
|
static void
|
|
get_declarer_elements (NODE_T *p, SCOPE_T **r, bool no_ref)
|
|
{
|
|
if (p != NO_NODE)
|
|
{
|
|
if (IS (p, BOUNDS))
|
|
gather_scopes_for_youngest (SUB (p), r);
|
|
else if (IS (p, INDICANT))
|
|
{
|
|
if (MOID (p) != NO_MOID && TAX (p) != NO_TAG && HAS_ROWS (MOID (p)) && no_ref)
|
|
scope_add (r, p, scope_make_tuple (TAG_LEX_LEVEL (TAX (p)), NOT_TRANSIENT));
|
|
}
|
|
else if (IS_REF (p))
|
|
get_declarer_elements (NEXT (p), r, false);
|
|
else if (a68_is_one_of (p, PROC_SYMBOL, UNION_SYMBOL, STOP))
|
|
;
|
|
else
|
|
{
|
|
get_declarer_elements (SUB (p), r, no_ref);
|
|
get_declarer_elements (NEXT (p), r, no_ref);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Gather_scopes_for_youngest. */
|
|
|
|
static void
|
|
gather_scopes_for_youngest (NODE_T *p, SCOPE_T **s)
|
|
{
|
|
for (; p != NO_NODE; FORWARD (p))
|
|
{
|
|
if ((a68_is_one_of (p, ROUTINE_TEXT, FORMAT_TEXT, STOP))
|
|
&& (YOUNGEST_ENVIRON (TAX (p)) == PRIMAL_SCOPE))
|
|
{
|
|
SCOPE_T *t = NO_SCOPE;
|
|
TUPLE_T tup;
|
|
|
|
gather_scopes_for_youngest (SUB (p), &t);
|
|
tup = scope_find_youngest_outside (t, LEX_LEVEL (p));
|
|
YOUNGEST_ENVIRON (TAX (p)) = LEVEL (&tup);
|
|
/* Direct link into list iso "gather_scopes_for_youngest (SUB (p),
|
|
s);". */
|
|
if (t != NO_SCOPE)
|
|
{
|
|
SCOPE_T *u = t;
|
|
while (NEXT (u) != NO_SCOPE) {
|
|
FORWARD (u);
|
|
}
|
|
NEXT (u) = *s;
|
|
(*s) = t;
|
|
}
|
|
}
|
|
else if (a68_is_one_of (p, IDENTIFIER, OPERATOR, STOP))
|
|
{
|
|
if (TAX (p) != NO_TAG && TAG_LEX_LEVEL (TAX (p)) != PRIMAL_SCOPE)
|
|
scope_add (s, p, scope_make_tuple (TAG_LEX_LEVEL (TAX (p)), NOT_TRANSIENT));
|
|
}
|
|
else if (IS (p, DECLARER))
|
|
get_declarer_elements (p, s, true);
|
|
else
|
|
gather_scopes_for_youngest (SUB (p), s);
|
|
}
|
|
}
|
|
|
|
/* Get_youngest_environs. */
|
|
|
|
static void
|
|
get_youngest_environs (NODE_T *p)
|
|
{
|
|
for (; p != NO_NODE; FORWARD (p))
|
|
{
|
|
if (a68_is_one_of (p, ROUTINE_TEXT, FORMAT_TEXT, STOP))
|
|
{
|
|
SCOPE_T *s = NO_SCOPE;
|
|
TUPLE_T tup;
|
|
gather_scopes_for_youngest (SUB (p), &s);
|
|
tup = scope_find_youngest_outside (s, LEX_LEVEL (p));
|
|
YOUNGEST_ENVIRON (TAX (p)) = LEVEL (&tup);
|
|
}
|
|
else
|
|
get_youngest_environs (SUB (p));
|
|
}
|
|
}
|
|
|
|
/* Bind_scope_to_tag. */
|
|
|
|
static void
|
|
bind_scope_to_tag (NODE_T *p)
|
|
{
|
|
for (; p != NO_NODE; FORWARD (p))
|
|
{
|
|
if (IS (p, DEFINING_IDENTIFIER) && MOID (p) == M_FORMAT)
|
|
{
|
|
if (IS (NEXT_NEXT (p), FORMAT_TEXT))
|
|
{
|
|
SCOPE (TAX (p)) = YOUNGEST_ENVIRON (TAX (NEXT_NEXT (p)));
|
|
SCOPE_ASSIGNED (TAX (p)) = true;
|
|
}
|
|
return;
|
|
}
|
|
else if (IS (p, DEFINING_IDENTIFIER))
|
|
{
|
|
if (IS (NEXT_NEXT (p), ROUTINE_TEXT))
|
|
{
|
|
SCOPE (TAX (p)) = YOUNGEST_ENVIRON (TAX (NEXT_NEXT (p)));
|
|
SCOPE_ASSIGNED (TAX (p)) = true;
|
|
}
|
|
return;
|
|
}
|
|
else
|
|
bind_scope_to_tag (SUB (p));
|
|
}
|
|
}
|
|
|
|
/* Bind_scope_to_tags. */
|
|
|
|
static void
|
|
bind_scope_to_tags (NODE_T *p)
|
|
{
|
|
for (; p != NO_NODE; FORWARD (p))
|
|
{
|
|
if (a68_is_one_of (p, PROCEDURE_DECLARATION, IDENTITY_DECLARATION, STOP))
|
|
bind_scope_to_tag (SUB (p));
|
|
else
|
|
bind_scope_to_tags (SUB (p));
|
|
}
|
|
}
|
|
|
|
/* Scope_bounds. */
|
|
|
|
static void
|
|
scope_bounds (NODE_T *p)
|
|
{
|
|
for (; p != NO_NODE; FORWARD (p))
|
|
{
|
|
if (IS (p, UNIT))
|
|
scope_statement (p, NO_VAR);
|
|
else
|
|
scope_bounds (SUB (p));
|
|
}
|
|
}
|
|
|
|
/* Scope_declarer. */
|
|
|
|
static void
|
|
scope_declarer (NODE_T *p)
|
|
{
|
|
if (p != NO_NODE)
|
|
{
|
|
if (IS (p, BOUNDS))
|
|
scope_bounds (SUB (p));
|
|
else if (IS (p, INDICANT))
|
|
;
|
|
else if (IS_REF (p))
|
|
scope_declarer (NEXT (p));
|
|
else if (a68_is_one_of (p, PROC_SYMBOL, UNION_SYMBOL, STOP))
|
|
;
|
|
else
|
|
{
|
|
scope_declarer (SUB (p));
|
|
scope_declarer (NEXT (p));
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Scope_identity_declaration. */
|
|
|
|
static void
|
|
scope_identity_declaration (NODE_T *p)
|
|
{
|
|
for (; p != NO_NODE; FORWARD (p))
|
|
{
|
|
scope_identity_declaration (SUB (p));
|
|
|
|
if (IS (p, DEFINING_IDENTIFIER))
|
|
{
|
|
NODE_T *unit = NEXT_NEXT (p);
|
|
SCOPE_T *s = NO_SCOPE;
|
|
TUPLE_T tup;
|
|
int z = PRIMAL_SCOPE;
|
|
|
|
if (ATTRIBUTE (MOID (TAX (p))) != PROC_SYMBOL)
|
|
check_identifier_usage (TAX (p), unit);
|
|
scope_statement (unit, &s);
|
|
(void) scope_check (s, TRANSIENT, LEX_LEVEL (p));
|
|
tup = scope_find_youngest (s);
|
|
z = LEVEL (&tup);
|
|
if (z < LEX_LEVEL (p))
|
|
{
|
|
SCOPE (TAX (p)) = z;
|
|
SCOPE_ASSIGNED (TAX (p)) = true;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Scope_variable_declaration. */
|
|
|
|
static void
|
|
scope_variable_declaration (NODE_T *p)
|
|
{
|
|
for (; p != NO_NODE; FORWARD (p))
|
|
{
|
|
scope_variable_declaration (SUB (p));
|
|
if (IS (p, DECLARER))
|
|
scope_declarer (SUB (p));
|
|
else if (IS (p, DEFINING_IDENTIFIER))
|
|
{
|
|
if (a68_whether (p, DEFINING_IDENTIFIER, ASSIGN_SYMBOL, UNIT, STOP))
|
|
{
|
|
NODE_T *unit = NEXT_NEXT (p);
|
|
SCOPE_T *s = NO_SCOPE;
|
|
check_identifier_usage (TAX (p), unit);
|
|
scope_statement (unit, &s);
|
|
(void) scope_check (s, TRANSIENT, LEX_LEVEL (p));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Scope_procedure_declaration. */
|
|
|
|
static void
|
|
scope_procedure_declaration (NODE_T *p)
|
|
{
|
|
for (; p != NO_NODE; FORWARD (p))
|
|
{
|
|
scope_procedure_declaration (SUB (p));
|
|
|
|
if (a68_is_one_of (p, DEFINING_IDENTIFIER, DEFINING_OPERATOR, STOP))
|
|
{
|
|
NODE_T *unit = NEXT_NEXT (p);
|
|
SCOPE_T *s = NO_SCOPE;
|
|
|
|
scope_statement (unit, &s);
|
|
(void) scope_check (s, NOT_TRANSIENT, LEX_LEVEL (p));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Scope_declaration_list. */
|
|
|
|
static void
|
|
scope_declaration_list (NODE_T *p)
|
|
{
|
|
if (p != NO_NODE)
|
|
{
|
|
if (IS (p, IDENTITY_DECLARATION))
|
|
scope_identity_declaration (SUB (p));
|
|
else if (IS (p, VARIABLE_DECLARATION))
|
|
scope_variable_declaration (SUB (p));
|
|
else if (IS (p, MODE_DECLARATION))
|
|
scope_declarer (SUB (p));
|
|
else if (IS (p, PRIORITY_DECLARATION))
|
|
;
|
|
else if (IS (p, PROCEDURE_DECLARATION))
|
|
scope_procedure_declaration (SUB (p));
|
|
else if (IS (p, PROCEDURE_VARIABLE_DECLARATION))
|
|
scope_procedure_declaration (SUB (p));
|
|
else if (a68_is_one_of (p, BRIEF_OPERATOR_DECLARATION, OPERATOR_DECLARATION, STOP))
|
|
scope_procedure_declaration (SUB (p));
|
|
else
|
|
{
|
|
scope_declaration_list (SUB (p));
|
|
scope_declaration_list (NEXT (p));
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Scope_arguments. */
|
|
|
|
static void
|
|
scope_arguments (NODE_T *p)
|
|
{
|
|
for (; p != NO_NODE; FORWARD (p))
|
|
{
|
|
if (IS (p, UNIT))
|
|
{
|
|
SCOPE_T *s = NO_SCOPE;
|
|
scope_statement (p, &s);
|
|
(void) scope_check (s, TRANSIENT, LEX_LEVEL (p));
|
|
}
|
|
else
|
|
scope_arguments (SUB (p));
|
|
}
|
|
}
|
|
|
|
/* Is_coercion. */
|
|
|
|
static bool
|
|
is_coercion (NODE_T *p)
|
|
{
|
|
if (p != NO_NODE)
|
|
{
|
|
switch (ATTRIBUTE (p))
|
|
{
|
|
case DEPROCEDURING:
|
|
case DEREFERENCING:
|
|
case UNITING:
|
|
case ROWING:
|
|
case WIDENING:
|
|
case VOIDING:
|
|
case PROCEDURING:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
/* Scope_coercion. */
|
|
|
|
static void
|
|
scope_coercion (NODE_T *p, SCOPE_T **s)
|
|
{
|
|
if (is_coercion (p))
|
|
{
|
|
if (IS (p, VOIDING))
|
|
scope_coercion (SUB (p), NO_VAR);
|
|
else if (IS (p, DEREFERENCING))
|
|
/* Leave this to the dynamic scope checker. */
|
|
scope_coercion (SUB (p), NO_VAR);
|
|
else if (IS (p, DEPROCEDURING))
|
|
scope_coercion (SUB (p), NO_VAR);
|
|
else if (IS (p, ROWING))
|
|
{
|
|
SCOPE_T *z = NO_SCOPE;
|
|
|
|
scope_coercion (SUB (p), &z);
|
|
(void) scope_check (z, TRANSIENT, LEX_LEVEL (p));
|
|
if (IS_REF_FLEX (MOID (SUB (p))))
|
|
scope_add (s, p, scope_make_tuple (LEX_LEVEL (p), TRANSIENT));
|
|
else
|
|
scope_add (s, p, scope_make_tuple (LEX_LEVEL (p), NOT_TRANSIENT));
|
|
}
|
|
else if (IS (p, PROCEDURING))
|
|
{
|
|
/* Can only be a JUMP. */
|
|
NODE_T *q = SUB_SUB (p);
|
|
if (IS (q, GOTO_SYMBOL))
|
|
FORWARD (q);
|
|
|
|
scope_add (s, q, scope_make_tuple (TAG_LEX_LEVEL (TAX (q)), NOT_TRANSIENT));
|
|
}
|
|
else if (IS (p, UNITING))
|
|
{
|
|
SCOPE_T *z = NO_SCOPE;
|
|
|
|
scope_coercion (SUB (p), &z);
|
|
if (z != NO_SCOPE)
|
|
{
|
|
(void) scope_check (z, TRANSIENT, LEX_LEVEL (p));
|
|
scope_add (s, p, scope_find_youngest (z));
|
|
}
|
|
}
|
|
else
|
|
scope_coercion (SUB (p), s);
|
|
}
|
|
else
|
|
scope_statement (p, s);
|
|
}
|
|
|
|
/* Scope_format_text. */
|
|
|
|
static void
|
|
scope_format_text (NODE_T *p, SCOPE_T **s)
|
|
{
|
|
for (; p != NO_NODE; FORWARD (p))
|
|
{
|
|
if (IS (p, FORMAT_PATTERN))
|
|
scope_enclosed_clause (SUB (NEXT_SUB (p)), s);
|
|
else if (IS (p, FORMAT_ITEM_G) && NEXT (p) != NO_NODE)
|
|
scope_enclosed_clause (SUB_NEXT (p), s);
|
|
else if (IS (p, DYNAMIC_REPLICATOR))
|
|
scope_enclosed_clause (SUB (NEXT_SUB (p)), s);
|
|
else
|
|
scope_format_text (SUB (p), s);
|
|
}
|
|
}
|
|
|
|
/* Scope_operand. */
|
|
|
|
static void
|
|
scope_operand (NODE_T *p, SCOPE_T **s)
|
|
{
|
|
if (IS (p, MONADIC_FORMULA))
|
|
scope_operand (NEXT_SUB (p), s);
|
|
else if (IS (p, FORMULA))
|
|
scope_formula (p, s);
|
|
else if (IS (p, SECONDARY))
|
|
scope_statement (SUB (p), s);
|
|
}
|
|
|
|
/* Scope_formula. */
|
|
|
|
static void
|
|
scope_formula (NODE_T *p, SCOPE_T **s)
|
|
{
|
|
NODE_T *q = SUB (p);
|
|
SCOPE_T *s2 = NO_SCOPE;
|
|
|
|
scope_operand (q, &s2);
|
|
(void) scope_check (s2, TRANSIENT, LEX_LEVEL (p));
|
|
if (NEXT (q) != NO_NODE)
|
|
{
|
|
SCOPE_T *s3 = NO_SCOPE;
|
|
scope_operand (NEXT_NEXT (q), &s3);
|
|
(void) scope_check (s3, TRANSIENT, LEX_LEVEL (p));
|
|
}
|
|
(void) s;
|
|
}
|
|
|
|
/* Scope_routine_text. */
|
|
|
|
static void
|
|
scope_routine_text (NODE_T *p, SCOPE_T **s)
|
|
{
|
|
NODE_T *q = SUB (p);
|
|
NODE_T *routine = (IS (q, PARAMETER_PACK) ? NEXT (q) : q);
|
|
SCOPE_T *x = NO_SCOPE;
|
|
|
|
scope_statement (NEXT_NEXT (routine), &x);
|
|
(void) scope_check (x, TRANSIENT, LEX_LEVEL (p));
|
|
TUPLE_T routine_tuple = scope_make_tuple (YOUNGEST_ENVIRON (TAX (p)), NOT_TRANSIENT);
|
|
scope_add (s, p, routine_tuple);
|
|
}
|
|
|
|
/* Scope_statement. */
|
|
|
|
static void
|
|
scope_statement (NODE_T *p, SCOPE_T **s)
|
|
{
|
|
if (is_coercion (p))
|
|
scope_coercion (p, s);
|
|
else if (a68_is_one_of (p, PRIMARY, SECONDARY, TERTIARY, UNIT, STOP))
|
|
scope_statement (SUB (p), s);
|
|
else if (a68_is_one_of (p, NIHIL, STOP))
|
|
scope_add (s, p, scope_make_tuple (PRIMAL_SCOPE, NOT_TRANSIENT));
|
|
else if (IS (p, DENOTATION))
|
|
;
|
|
else if (IS (p, IDENTIFIER))
|
|
{
|
|
if (IS_REF (MOID (p)))
|
|
{
|
|
if (PRIO (TAX (p)) == PARAMETER_IDENTIFIER)
|
|
scope_add (s, p, scope_make_tuple (TAG_LEX_LEVEL (TAX (p)) - 1, NOT_TRANSIENT));
|
|
else
|
|
{
|
|
if (HEAP (TAX (p)) == HEAP_SYMBOL)
|
|
scope_add (s, p, scope_make_tuple (PRIMAL_SCOPE, NOT_TRANSIENT));
|
|
else if (SCOPE_ASSIGNED (TAX (p)))
|
|
scope_add (s, p, scope_make_tuple (SCOPE (TAX (p)), NOT_TRANSIENT));
|
|
else
|
|
scope_add (s, p, scope_make_tuple (TAG_LEX_LEVEL (TAX (p)), NOT_TRANSIENT));
|
|
}
|
|
}
|
|
else if (ATTRIBUTE (MOID (p)) == PROC_SYMBOL && SCOPE_ASSIGNED (TAX (p)) == true)
|
|
scope_add (s, p, scope_make_tuple (SCOPE (TAX (p)), NOT_TRANSIENT));
|
|
else if (MOID (p) == M_FORMAT && SCOPE_ASSIGNED (TAX (p)) == true)
|
|
scope_add (s, p, scope_make_tuple (SCOPE (TAX (p)), NOT_TRANSIENT));
|
|
}
|
|
else if (IS (p, ENCLOSED_CLAUSE))
|
|
scope_enclosed_clause (SUB (p), s);
|
|
else if (IS (p, CALL))
|
|
{
|
|
SCOPE_T *x = NO_SCOPE;
|
|
|
|
scope_statement (SUB (p), &x);
|
|
(void) scope_check (x, NOT_TRANSIENT, LEX_LEVEL (p));
|
|
scope_arguments (NEXT_SUB (p));
|
|
}
|
|
else if (IS (p, SLICE))
|
|
{
|
|
SCOPE_T *x = NO_SCOPE;
|
|
MOID_T *m = MOID (SUB (p));
|
|
|
|
if (IS_REF (m))
|
|
{
|
|
if (ATTRIBUTE (SUB (p)) == PRIMARY && ATTRIBUTE (SUB_SUB (p)) == SLICE)
|
|
scope_statement (SUB (p), s);
|
|
else
|
|
{
|
|
scope_statement (SUB (p), &x);
|
|
(void) scope_check (x, NOT_TRANSIENT, LEX_LEVEL (p));
|
|
}
|
|
if (IS_FLEX (SUB (m)))
|
|
scope_add (s, SUB (p), scope_make_tuple (LEX_LEVEL (p), TRANSIENT));
|
|
scope_bounds (SUB (NEXT_SUB (p)));
|
|
}
|
|
if (IS_REF (MOID (p)))
|
|
scope_add (s, p, scope_find_youngest (x));
|
|
}
|
|
else if (IS (p, FORMAT_TEXT))
|
|
{
|
|
SCOPE_T *x = NO_SCOPE;
|
|
scope_format_text (SUB (p), &x);
|
|
scope_add (s, p, scope_find_youngest (x));
|
|
}
|
|
else if (IS (p, CAST))
|
|
{
|
|
SCOPE_T *x = NO_SCOPE;
|
|
scope_enclosed_clause (SUB (NEXT_SUB (p)), &x);
|
|
(void) scope_check (x, NOT_TRANSIENT, LEX_LEVEL (p));
|
|
scope_add (s, p, scope_find_youngest (x));
|
|
}
|
|
else if (IS (p, SELECTION))
|
|
{
|
|
SCOPE_T *ns = NO_SCOPE;
|
|
scope_statement (NEXT_SUB (p), &ns);
|
|
(void) scope_check (ns, NOT_TRANSIENT, LEX_LEVEL (p));
|
|
if (a68_is_ref_refety_flex (MOID (NEXT_SUB (p))))
|
|
scope_add (s, p, scope_make_tuple (LEX_LEVEL (p), TRANSIENT));
|
|
scope_add (s, p, scope_find_youngest (ns));
|
|
}
|
|
else if (IS (p, GENERATOR))
|
|
{
|
|
if (IS (SUB (p), LOC_SYMBOL))
|
|
{
|
|
if (NON_LOCAL (p) != NO_TABLE)
|
|
scope_add (s, p, scope_make_tuple (LEVEL (NON_LOCAL (p)), NOT_TRANSIENT));
|
|
else
|
|
scope_add (s, p, scope_make_tuple (LEX_LEVEL (p), NOT_TRANSIENT));
|
|
}
|
|
else
|
|
scope_add (s, p, scope_make_tuple (PRIMAL_SCOPE, NOT_TRANSIENT));
|
|
scope_declarer (SUB (NEXT_SUB (p)));
|
|
}
|
|
else if (IS (p, FORMULA))
|
|
scope_formula (p, s);
|
|
else if (IS (p, ASSIGNATION))
|
|
{
|
|
NODE_T *unit = NEXT (NEXT_SUB (p));
|
|
SCOPE_T *ns = NO_SCOPE, *nd = NO_SCOPE;
|
|
TUPLE_T tup;
|
|
scope_statement (SUB_SUB (p), &nd);
|
|
scope_statement (unit, &ns);
|
|
(void) scope_check_multiple (ns, TRANSIENT, nd);
|
|
tup = scope_find_youngest (nd);
|
|
scope_add (s, p, scope_make_tuple (LEVEL (&tup), NOT_TRANSIENT));
|
|
}
|
|
else if (IS (p, ROUTINE_TEXT))
|
|
scope_routine_text (p, s);
|
|
else if (a68_is_one_of (p, IDENTITY_RELATION, AND_FUNCTION, OR_FUNCTION, STOP))
|
|
{
|
|
SCOPE_T *n = NO_SCOPE;
|
|
scope_statement (SUB (p), &n);
|
|
scope_statement (NEXT (NEXT_SUB (p)), &n);
|
|
(void) scope_check (n, NOT_TRANSIENT, LEX_LEVEL (p));
|
|
}
|
|
else if (IS (p, ASSERTION))
|
|
{
|
|
SCOPE_T *n = NO_SCOPE;
|
|
scope_enclosed_clause (SUB (NEXT_SUB (p)), &n);
|
|
(void) scope_check (n, NOT_TRANSIENT, LEX_LEVEL (p));
|
|
}
|
|
else if (a68_is_one_of (p, JUMP, SKIP, STOP))
|
|
{
|
|
;
|
|
}
|
|
}
|
|
|
|
/* Scope_statement_list. */
|
|
|
|
static void
|
|
scope_statement_list (NODE_T *p, SCOPE_T **s)
|
|
{
|
|
for (; p != NO_NODE; FORWARD (p))
|
|
{
|
|
if (IS (p, UNIT))
|
|
scope_statement (p, s);
|
|
else
|
|
scope_statement_list (SUB (p), s);
|
|
}
|
|
}
|
|
|
|
/* Scope_serial_clause. */
|
|
|
|
static void
|
|
scope_serial_clause (NODE_T *p, SCOPE_T **s, bool terminator)
|
|
{
|
|
if (p != NO_NODE)
|
|
{
|
|
if (IS (p, INITIALISER_SERIES))
|
|
{
|
|
scope_serial_clause (SUB (p), s, false);
|
|
scope_serial_clause (NEXT (p), s, terminator);
|
|
}
|
|
else if (IS (p, DECLARATION_LIST))
|
|
scope_declaration_list (SUB (p));
|
|
else if (a68_is_one_of (p, LABEL, SEMI_SYMBOL, EXIT_SYMBOL, STOP))
|
|
scope_serial_clause (NEXT (p), s, terminator);
|
|
else if (a68_is_one_of (p, SERIAL_CLAUSE, ENQUIRY_CLAUSE, STOP))
|
|
{
|
|
if (NEXT (p) != NO_NODE)
|
|
{
|
|
int j = ATTRIBUTE (NEXT (p));
|
|
if (j == EXIT_SYMBOL || j == END_SYMBOL || j == CLOSE_SYMBOL)
|
|
scope_serial_clause (SUB (p), s, true);
|
|
else
|
|
scope_serial_clause (SUB (p), s, false);
|
|
}
|
|
else
|
|
scope_serial_clause (SUB (p), s, true);
|
|
scope_serial_clause (NEXT (p), s, terminator);
|
|
}
|
|
else if (IS (p, LABELED_UNIT))
|
|
scope_serial_clause (SUB (p), s, terminator);
|
|
else if (IS (p, UNIT))
|
|
{
|
|
if (terminator)
|
|
scope_statement (p, s);
|
|
else
|
|
scope_statement (p, NO_VAR);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Scope_closed_clause. */
|
|
|
|
static void
|
|
scope_closed_clause (NODE_T *p, SCOPE_T **s)
|
|
{
|
|
if (p != NO_NODE)
|
|
{
|
|
if (IS (p, SERIAL_CLAUSE))
|
|
scope_serial_clause (p, s, true);
|
|
else if (a68_is_one_of (p, OPEN_SYMBOL, BEGIN_SYMBOL, STOP))
|
|
scope_closed_clause (NEXT (p), s);
|
|
}
|
|
}
|
|
|
|
/* Scope_collateral_clause. */
|
|
|
|
static void
|
|
scope_collateral_clause (NODE_T *p, SCOPE_T **s)
|
|
{
|
|
if (p != NO_NODE)
|
|
{
|
|
if (!(a68_whether (p, BEGIN_SYMBOL, END_SYMBOL, STOP)
|
|
|| a68_whether (p, OPEN_SYMBOL, CLOSE_SYMBOL, STOP)))
|
|
{
|
|
scope_statement_list (p, s);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Scope_conditional_clause. */
|
|
|
|
static void
|
|
scope_conditional_clause (NODE_T *p, SCOPE_T **s)
|
|
{
|
|
scope_serial_clause (NEXT_SUB (p), NO_VAR, true);
|
|
FORWARD (p);
|
|
scope_serial_clause (NEXT_SUB (p), s, true);
|
|
if ((FORWARD (p)) != NO_NODE)
|
|
{
|
|
if (a68_is_one_of (p, ELSE_PART, CHOICE, STOP))
|
|
scope_serial_clause (NEXT_SUB (p), s, true);
|
|
else if (a68_is_one_of (p, ELIF_PART, BRIEF_ELIF_PART, STOP))
|
|
scope_conditional_clause (SUB (p), s);
|
|
}
|
|
}
|
|
|
|
/* Scope_case_clause. */
|
|
|
|
static void
|
|
scope_case_clause (NODE_T *p, SCOPE_T **s)
|
|
{
|
|
SCOPE_T *n = NO_SCOPE;
|
|
scope_serial_clause (NEXT_SUB (p), &n, true);
|
|
(void) scope_check (n, NOT_TRANSIENT, LEX_LEVEL (p));
|
|
FORWARD (p);
|
|
scope_statement_list (NEXT_SUB (p), s);
|
|
if ((FORWARD (p)) != NO_NODE)
|
|
{
|
|
if (a68_is_one_of (p, OUT_PART, CHOICE, STOP))
|
|
scope_serial_clause (NEXT_SUB (p), s, true);
|
|
else if (a68_is_one_of (p, CASE_OUSE_PART, BRIEF_OUSE_PART, STOP))
|
|
scope_case_clause (SUB (p), s);
|
|
else if (a68_is_one_of (p, CONFORMITY_OUSE_PART, BRIEF_CONFORMITY_OUSE_PART, STOP))
|
|
scope_case_clause (SUB (p), s);
|
|
}
|
|
}
|
|
|
|
/* Scope_loop_clause. */
|
|
|
|
static void
|
|
scope_loop_clause (NODE_T *p)
|
|
{
|
|
if (p != NO_NODE)
|
|
{
|
|
if (IS (p, FOR_PART))
|
|
scope_loop_clause (NEXT (p));
|
|
else if (a68_is_one_of (p, FROM_PART, BY_PART, TO_PART, STOP))
|
|
{
|
|
scope_statement (NEXT_SUB (p), NO_VAR);
|
|
scope_loop_clause (NEXT (p));
|
|
}
|
|
else if (IS (p, WHILE_PART))
|
|
{
|
|
scope_serial_clause (NEXT_SUB (p), NO_VAR, true);
|
|
scope_loop_clause (NEXT (p));
|
|
}
|
|
else if (a68_is_one_of (p, DO_PART, ALT_DO_PART, STOP))
|
|
{
|
|
NODE_T *do_p = NEXT_SUB (p);
|
|
|
|
if (IS (do_p, SERIAL_CLAUSE))
|
|
scope_serial_clause (do_p, NO_VAR, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Scope and access-clause. */
|
|
|
|
static void
|
|
scope_access_clause (NODE_T *p, SCOPE_T **s)
|
|
{
|
|
for (; p != NO_NODE; FORWARD (p))
|
|
{
|
|
if (IS (p, ENCLOSED_CLAUSE))
|
|
scope_enclosed_clause (SUB (p), s);
|
|
}
|
|
}
|
|
|
|
/* Scope_enclosed_clause. */
|
|
|
|
static void
|
|
scope_enclosed_clause (NODE_T *p, SCOPE_T **s)
|
|
{
|
|
if (IS (p, ENCLOSED_CLAUSE))
|
|
scope_enclosed_clause (SUB (p), s);
|
|
else if (IS (p, CLOSED_CLAUSE))
|
|
scope_closed_clause (SUB (p), s);
|
|
else if (a68_is_one_of (p, COLLATERAL_CLAUSE, PARALLEL_CLAUSE, STOP))
|
|
scope_collateral_clause (SUB (p), s);
|
|
else if (IS (p, CONDITIONAL_CLAUSE))
|
|
scope_conditional_clause (SUB (p), s);
|
|
else if (a68_is_one_of (p, CASE_CLAUSE, CONFORMITY_CLAUSE, STOP))
|
|
scope_case_clause (SUB (p), s);
|
|
else if (IS (p, LOOP_CLAUSE))
|
|
scope_loop_clause (SUB (p));
|
|
else if (IS (p, ACCESS_CLAUSE))
|
|
scope_access_clause (SUB (p), s);
|
|
}
|
|
|
|
/* Whether a symbol table contains no (anonymous) definition. */
|
|
|
|
static bool
|
|
empty_table (TABLE_T * t)
|
|
{
|
|
if (IDENTIFIERS (t) == NO_TAG)
|
|
return (OPERATORS (t) == NO_TAG && INDICANTS (t) == NO_TAG);
|
|
else if (PRIO (IDENTIFIERS (t)) == LOOP_IDENTIFIER && NEXT (IDENTIFIERS (t)) == NO_TAG)
|
|
return (OPERATORS (t) == NO_TAG && INDICANTS (t) == NO_TAG);
|
|
else if (PRIO (IDENTIFIERS (t)) == SPECIFIER_IDENTIFIER && NEXT (IDENTIFIERS (t)) == NO_TAG)
|
|
return (OPERATORS (t) == NO_TAG && INDICANTS (t) == NO_TAG);
|
|
else
|
|
return false;
|
|
}
|
|
|
|
/* Indicate non-local environs. */
|
|
|
|
static void
|
|
get_non_local_environs (NODE_T *p, int max)
|
|
{
|
|
for (; p != NO_NODE; FORWARD (p))
|
|
{
|
|
if (IS (p, ROUTINE_TEXT))
|
|
get_non_local_environs (SUB (p), LEX_LEVEL (SUB (p)));
|
|
else if (IS (p, FORMAT_TEXT))
|
|
get_non_local_environs (SUB (p), LEX_LEVEL (SUB (p)));
|
|
else
|
|
{
|
|
get_non_local_environs (SUB (p), max);
|
|
NON_LOCAL (p) = NO_TABLE;
|
|
if (TABLE (p) != NO_TABLE)
|
|
{
|
|
TABLE_T *q = TABLE (p);
|
|
while (q != NO_TABLE && empty_table (q)
|
|
&& PREVIOUS (q) != NO_TABLE && LEVEL (PREVIOUS (q)) >= max)
|
|
{
|
|
NON_LOCAL (p) = PREVIOUS (q);
|
|
q = PREVIOUS (q);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Scope a module text. */
|
|
|
|
static void
|
|
scope_module_text (NODE_T *p)
|
|
{
|
|
for (; p != NO_NODE; FORWARD (p))
|
|
{
|
|
if (IS (p, DEF_PART) || IS (p, POSTLUDE_PART))
|
|
{
|
|
NODE_T *clause = NEXT (SUB (p));
|
|
gcc_assert (IS (clause, ENQUIRY_CLAUSE) || IS (clause, SERIAL_CLAUSE));
|
|
scope_serial_clause (clause, NO_VAR, true /* terminator */);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Scope a module declaration. */
|
|
|
|
static void
|
|
scope_module_declaration (NODE_T *p)
|
|
{
|
|
for (; p != NO_NODE; FORWARD (p))
|
|
{
|
|
if (IS (p, MODULE_TEXT))
|
|
scope_module_text (SUB (p));
|
|
else
|
|
scope_module_declaration (SUB (p));
|
|
}
|
|
}
|
|
|
|
/* Scope a particular program. */
|
|
|
|
static void
|
|
scope_particular_program (NODE_T *p)
|
|
{
|
|
scope_enclosed_clause (SUB (SUB (p)), NO_VAR);
|
|
}
|
|
|
|
/* Scope a prelude packet. */
|
|
|
|
static void
|
|
scope_prelude_packet (NODE_T *p)
|
|
{
|
|
gcc_assert (IS (SUB (p), MODULE_DECLARATION));
|
|
scope_module_declaration (SUB (p));
|
|
}
|
|
|
|
/* The static scope checker. */
|
|
|
|
void
|
|
a68_scope_checker (NODE_T *p)
|
|
{
|
|
/* Establish scopes of routine texts and format texts. */
|
|
get_youngest_environs (p);
|
|
/* Find non-local environs. */
|
|
get_non_local_environs (p, PRIMAL_SCOPE);
|
|
/* PROC and FORMAT identities can now be assigned a scope. */
|
|
bind_scope_to_tags (p);
|
|
|
|
/* Now check evertyhing else. */
|
|
gcc_assert (IS (p, PACKET));
|
|
if (IS (SUB (p), PARTICULAR_PROGRAM))
|
|
scope_particular_program (SUB (p));
|
|
else if (IS (SUB (p), PRELUDE_PACKET))
|
|
scope_prelude_packet (SUB (p));
|
|
else
|
|
gcc_unreachable ();
|
|
}
|