| /* 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 (); |
| } |