Signed-off-by: Jose E. Marchesi <[email protected]> Co-authored-by: Marcel van der Veer <[email protected]> --- gcc/algol68/a68-parser-taxes.cc | 1648 +++++++++++++++++++++++++++++++ 1 file changed, 1648 insertions(+) create mode 100644 gcc/algol68/a68-parser-taxes.cc
diff --git a/gcc/algol68/a68-parser-taxes.cc b/gcc/algol68/a68-parser-taxes.cc new file mode 100644 index 00000000000..23317a4b9f9 --- /dev/null +++ b/gcc/algol68/a68-parser-taxes.cc @@ -0,0 +1,1648 @@ +/* Symbol table management. + 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/>. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "options.h" + +#include "a68.h" + +/* + * Symbol table handling, managing TAGS. + */ + +/* Forward declarations for several functions defined below. */ + +static TAG_T *find_tag_local (TABLE_T *table, int a, const char *name); + +/* Set level for procedures. */ + +void +a68_set_proc_level (NODE_T *p, int n) +{ + for (; p != NO_NODE; FORWARD (p)) + { + PROCEDURE_LEVEL (INFO (p)) = n; + if (IS (p, ROUTINE_TEXT)) + a68_set_proc_level (SUB (p), n + 1); + else + a68_set_proc_level (SUB (p), n); + } +} + +/* Set nests for diagnostics. */ + +void +a68_set_nest (NODE_T *p, NODE_T *s) +{ + for (; p != NO_NODE; FORWARD (p)) + { + NEST (p) = s; + if (IS (p, PARTICULAR_PROGRAM)) + a68_set_nest (SUB (p), p); + else if (IS (p, CLOSED_CLAUSE) && LINE_NUMBER (p) != 0) + a68_set_nest (SUB (p), p); + else if (IS (p, COLLATERAL_CLAUSE) && LINE_NUMBER (p) != 0) + a68_set_nest (SUB (p), p); + else if (IS (p, CONDITIONAL_CLAUSE) && LINE_NUMBER (p) != 0) + a68_set_nest (SUB (p), p); + else if (IS (p, CASE_CLAUSE) && LINE_NUMBER (p) != 0) + a68_set_nest (SUB (p), p); + else if (IS (p, CONFORMITY_CLAUSE) && LINE_NUMBER (p) != 0) + a68_set_nest (SUB (p), p); + else if (IS (p, LOOP_CLAUSE) && LINE_NUMBER (p) != 0) + a68_set_nest (SUB (p), p); + else + a68_set_nest (SUB (p), s); + } +} + +/* + * Routines that work with tags and symbol tables. + */ + +static void tax_tags (NODE_T *); +static void tax_specifier_list (NODE_T *); +static void tax_parameter_list (NODE_T *); +static void tax_format_texts (NODE_T *); + +/* Find a tag, searching symbol tables towards the root. */ + +int +a68_first_tag_global (TABLE_T * table, const char *name) +{ + if (table != NO_TABLE) + { + for (TAG_T *s = IDENTIFIERS (table); s != NO_TAG; FORWARD (s)) + { + if (NSYMBOL (NODE (s)) == name) + return IDENTIFIER; + } + for (TAG_T *s = INDICANTS (table); s != NO_TAG; FORWARD (s)) + { + if (NSYMBOL (NODE (s)) == name) + return INDICANT; + } + for (TAG_T *s = LABELS (table); s != NO_TAG; FORWARD (s)) + { + if (NSYMBOL (NODE (s)) == name) + return LABEL; + } + for (TAG_T *s = OPERATORS (table); s != NO_TAG; FORWARD (s)) + { + if (NSYMBOL (NODE (s)) == name) + return OP_SYMBOL; + } + for (TAG_T *s = PRIO (table); s != NO_TAG; FORWARD (s)) + { + if (NSYMBOL (NODE (s)) == name) + return PRIO_SYMBOL; + } + return a68_first_tag_global (PREVIOUS (table), name); + } + else + return STOP; +} + +/* Whether routine can be "lengthety-mapped". */ + +static bool +is_mappable_routine (char *z) +{ +#define ACCEPT(u, v) {\ + if (strlen (u) >= strlen (v)) {\ + if (strcmp (&u[strlen (u) - strlen (v)], v) == 0) {\ + return true;\ + }}} + + /* Math routines. */ + ACCEPT (z, "arccos"); + ACCEPT (z, "arccosdg"); + ACCEPT (z, "arccot"); + ACCEPT (z, "arccotdg"); + ACCEPT (z, "arcsin"); + ACCEPT (z, "arcsindg"); + ACCEPT (z, "arctan"); + ACCEPT (z, "arctandg"); + ACCEPT (z, "beta"); + ACCEPT (z, "betainc"); + ACCEPT (z, "cbrt"); + ACCEPT (z, "cos"); + ACCEPT (z, "cosdg"); + ACCEPT (z, "cospi"); + ACCEPT (z, "cot"); + ACCEPT (z, "cot"); + ACCEPT (z, "cotdg"); + ACCEPT (z, "cotpi"); + ACCEPT (z, "curt"); + ACCEPT (z, "erf"); + ACCEPT (z, "erfc"); + ACCEPT (z, "exp"); + ACCEPT (z, "gamma"); + ACCEPT (z, "gammainc"); + ACCEPT (z, "gammaincg"); + ACCEPT (z, "gammaincgf"); + ACCEPT (z, "ln"); + ACCEPT (z, "log"); + ACCEPT (z, "pi"); + ACCEPT (z, "sin"); + ACCEPT (z, "sindg"); + ACCEPT (z, "sinpi"); + ACCEPT (z, "sqrt"); + ACCEPT (z, "tan"); + ACCEPT (z, "tandg"); + ACCEPT (z, "tanpi"); + /* Random generator. */ + ACCEPT (z, "nextrandom"); + ACCEPT (z, "random"); + /* BITS. */ + ACCEPT (z, "bitspack"); + /* Enquiries. */ + ACCEPT (z, "maxint"); + ACCEPT (z, "intwidth"); + ACCEPT (z, "maxreal"); + ACCEPT (z, "realwidth"); + ACCEPT (z, "expwidth"); + ACCEPT (z, "maxbits"); + ACCEPT (z, "bitswidth"); + ACCEPT (z, "byteswidth"); + ACCEPT (z, "smallreal"); + return false; +#undef ACCEPT +} + +/* Map "short sqrt" onto "sqrt" etcetera. */ + +static TAG_T * +bind_lengthety_identifier (const char *u) +{ +#define CAR(u, v) (strncmp (u, v, strlen(v)) == 0) + /* We can only map routines blessed by "is_mappable_routine", so there is no + "short print" or "long char in string". */ + if (CAR (u, "short")) + { + do + { + u = &u[strlen ("short")]; + char *v = TEXT (a68_add_token (&A68 (top_token), u)); + TAG_T *w = find_tag_local (A68_STANDENV, IDENTIFIER, v); + if (w != NO_TAG && is_mappable_routine (v)) + return w; + } + while (CAR (u, "short")); + } + else if (CAR (u, "long")) + { + do + { + u = &u[strlen ("long")]; + char *v = TEXT (a68_add_token (&A68 (top_token), u)); + TAG_T *w = find_tag_local (A68_STANDENV, IDENTIFIER, v); + if (w != NO_TAG && is_mappable_routine (v)) + return w; + } + while (CAR (u, "long")); + } + + return NO_TAG; +#undef CAR +} + +/* Bind identifier tags to the symbol table. */ + +static void +bind_identifier_tag_to_symbol_table (NODE_T * p) +{ + for (; p != NO_NODE; FORWARD (p)) + { + bind_identifier_tag_to_symbol_table (SUB (p)); + + if (a68_is_one_of (p, IDENTIFIER, DEFINING_IDENTIFIER, STOP)) + { + int att = a68_first_tag_global (TABLE (p), NSYMBOL (p)); + + if (att == STOP) + { + TAG_T *z = bind_lengthety_identifier (NSYMBOL (p)); + + if (z != NO_TAG) + MOID (p) = MOID (z); + TAX (p) = z; + } + else + { + TAG_T *z = a68_find_tag_global (TABLE (p), att, NSYMBOL (p)); + + if (att == IDENTIFIER && z != NO_TAG) + MOID (p) = MOID (z); + else if (att == LABEL && z != NO_TAG) + ; + else if ((z = bind_lengthety_identifier (NSYMBOL (p))) != NO_TAG) + MOID (p) = MOID (z); + else + { + a68_error (p, "tag S has not been declared properly"); + z = a68_add_tag (TABLE (p), IDENTIFIER, p, M_ERROR, NORMAL_IDENTIFIER); + MOID (p) = M_ERROR; + } + TAX (p) = z; + if (IS (p, DEFINING_IDENTIFIER)) + NODE (z) = p; + } + } + } +} + +/* Tell whether the given tree refers to the applied indicant INDICANT in an + actual declarer. */ + +static bool +declarer_contains_indicant (NODE_T *p, NODE_T *indicant) +{ + for (NODE_T *q = p; q != NO_NODE; FORWARD (q)) + { + if (IS (q, DECLARER) + && IS (SUB (q), INDICANT) + && ((TAX (SUB (q)) && IS_RECURSIVE (TAX (SUB (q)))) + || IS_LITERALLY (SUB (q), NSYMBOL (indicant)))) + { + return true; + } + + if (declarer_contains_indicant (SUB (q), indicant)) + return true; + } + + return false; +} + +/* Bind indicant tags to the symbol table. */ + +static void +bind_indicant_tag_to_symbol_table (NODE_T *p) +{ + for (; p != NO_NODE; FORWARD (p)) + { + bind_indicant_tag_to_symbol_table (SUB (p)); + + if (a68_is_one_of (p, INDICANT, DEFINING_INDICANT, STOP)) + { + TAG_T *z = a68_find_tag_global (TABLE (p), INDICANT, NSYMBOL (p)); + + if (z != NO_TAG) + { + MOID (p) = MOID (z); + TAX (p) = z; + if (IS (p, DEFINING_INDICANT)) + { + NODE (z) = p; + IS_RECURSIVE (z) = declarer_contains_indicant (NEXT_NEXT (p), p); + } + } + } + } +} + +/* Enter specifier identifiers in the symbol table. */ + +static void +tax_specifiers (NODE_T *p) +{ + for (; p != NO_NODE; FORWARD (p)) + { + tax_specifiers (SUB (p)); + + if (SUB (p) != NO_NODE && IS (p, SPECIFIER)) + tax_specifier_list (SUB (p)); + } +} + +/* Enter specifier identifiers in the symbol table. */ + +static void +tax_specifier_list (NODE_T *p) +{ + if (p != NO_NODE) + { + if (IS (p, OPEN_SYMBOL)) + tax_specifier_list (NEXT (p)); + else if (a68_is_one_of (p, CLOSE_SYMBOL, VOID_SYMBOL, STOP)) + ; + else if (IS (p, IDENTIFIER)) + { + TAG_T *z = a68_add_tag (TABLE (p), IDENTIFIER, p, NO_MOID, SPECIFIER_IDENTIFIER); + HEAP (z) = LOC_SYMBOL; + } + else if (IS (p, DECLARER)) + { + tax_specifiers (SUB (p)); + tax_specifier_list (NEXT (p)); + /* last identifier entry is identifier with this declarer. */ + if (IDENTIFIERS (TABLE (p)) != NO_TAG + && PRIO (IDENTIFIERS (TABLE (p))) == SPECIFIER_IDENTIFIER) + MOID (IDENTIFIERS (TABLE (p))) = MOID (p); + } + } +} + +/* Enter parameter identifiers in the symbol table. */ + +static void +tax_parameters (NODE_T *p) +{ + for (; p != NO_NODE; FORWARD (p)) + { + if (SUB (p) != NO_NODE) + { + tax_parameters (SUB (p)); + if (IS (p, PARAMETER_PACK)) + tax_parameter_list (SUB (p)); + } + } +} + +/* Enter parameter identifiers in the symbol table. */ + +static void +tax_parameter_list (NODE_T *p) +{ + if (p != NO_NODE) + { + if (a68_is_one_of (p, OPEN_SYMBOL, COMMA_SYMBOL, STOP)) + tax_parameter_list (NEXT (p)); + else if (IS (p, CLOSE_SYMBOL)) + ; + else if (a68_is_one_of (p, PARAMETER_LIST, PARAMETER, STOP)) + { + tax_parameter_list (NEXT (p)); + tax_parameter_list (SUB (p)); + } + else if (IS (p, IDENTIFIER)) + { + /* parameters are always local. */ + HEAP (a68_add_tag (TABLE (p), IDENTIFIER, p, NO_MOID, PARAMETER_IDENTIFIER)) = LOC_SYMBOL; + } + else if (IS (p, DECLARER)) + { + tax_parameter_list (NEXT (p)); + /* last identifier entries are identifiers with this declarer. */ + for (TAG_T *s = IDENTIFIERS (TABLE (p)); s != NO_TAG && MOID (s) == NO_MOID; FORWARD (s)) + MOID (s) = MOID (p); + tax_parameters (SUB (p)); + } + } +} + +/* Enter FOR identifiers in the symbol table. */ + +static void +tax_for_identifiers (NODE_T *p) +{ + for (; p != NO_NODE; FORWARD (p)) + { + tax_for_identifiers (SUB (p)); + + if (IS (p, FOR_SYMBOL)) + { + if ((FORWARD (p)) != NO_NODE) + (void) a68_add_tag (TABLE (p), IDENTIFIER, p, M_INT, LOOP_IDENTIFIER); + } + } +} + +/* Enter routine texts in the symbol table. */ + +static void +tax_routine_texts (NODE_T *p) +{ + for (; p != NO_NODE; FORWARD (p)) + { + tax_routine_texts (SUB (p)); + + if (IS (p, ROUTINE_TEXT)) + { + TAG_T *z = a68_add_tag (TABLE (p), ANONYMOUS, p, MOID (p), ROUTINE_TEXT); + TAX (p) = z; + HEAP (z) = LOC_SYMBOL; + USE (z) = true; + } + } +} + +/* Enter format texts in the symbol table. */ + +static void +tax_format_texts (NODE_T *p) +{ + for (; p != NO_NODE; FORWARD (p)) + { + tax_format_texts (SUB (p)); + + if (IS (p, FORMAT_TEXT)) + { + TAG_T *z = a68_add_tag (TABLE (p), ANONYMOUS, p, M_FORMAT, FORMAT_TEXT); + TAX (p) = z; + USE (z) = true; + } + else if (IS (p, FORMAT_DELIMITER_SYMBOL) && NEXT (p) != NO_NODE) + { + TAG_T *z = a68_add_tag (TABLE (p), ANONYMOUS, p, M_FORMAT, FORMAT_IDENTIFIER); + TAX (p) = z; + USE (z) = true; + } + } +} + +/* Enter FORMAT pictures in the symbol table. */ + +static void +tax_pictures (NODE_T * p) +{ + for (; p != NO_NODE; FORWARD (p)) + { + tax_pictures (SUB (p)); + + if (IS (p, PICTURE)) + TAX (p) = a68_add_tag (TABLE (p), ANONYMOUS, p, M_COLLITEM, FORMAT_IDENTIFIER); + } +} + +/* Enter generators in the symbol table. */ + +static void +tax_generators (NODE_T *p) +{ + for (; p != NO_NODE; FORWARD (p)) + { + tax_generators (SUB (p)); + + if (IS (p, GENERATOR)) + { + if (IS (SUB (p), LOC_SYMBOL)) + { + TAG_T *z = a68_add_tag (TABLE (p), ANONYMOUS, p, SUB_MOID (SUB (p)), GENERATOR); + HEAP (z) = LOC_SYMBOL; + USE (z) = true; + TAX (p) = z; + } + } + } +} + +/* Find a firmly related operator for operands. */ + +static TAG_T * +find_firmly_related_op (TABLE_T *c, const char *n, MOID_T *l, MOID_T *r, TAG_T *self) +{ + if (c != NO_TABLE) + { + TAG_T *s = OPERATORS (c); + + for (; s != NO_TAG; FORWARD (s)) + { + if (s != self && NSYMBOL (NODE (s)) == n) + { + PACK_T *t = PACK (MOID (s)); + if (t != NO_PACK && a68_is_firm (MOID (t), l)) + { + /* catch monadic operator. */ + if ((FORWARD (t)) == NO_PACK) + { + if (r == NO_MOID) + return s; + } + else + { + /* catch dyadic operator. */ + if (r != NO_MOID && a68_is_firm (MOID (t), r)) + return s; + } + } + } + } + } + return NO_TAG; +} + +/* Check for firmly related operators in this range. */ + +static void +test_firmly_related_ops_local (NODE_T *p, TAG_T *s) +{ + if (s != NO_TAG) + { + PACK_T *u = PACK (MOID (s)); + + if (u != NO_PACK) + { + MOID_T *l = MOID (u); + MOID_T *r = (NEXT (u) != NO_PACK ? MOID (NEXT (u)) : NO_MOID); + TAG_T *t = find_firmly_related_op (TAG_TABLE (s), NSYMBOL (NODE (s)), l, r, s); + + if (t != NO_TAG) + { + a68_error (p, "M Z is firmly related to M Z", + MOID (s), NSYMBOL (NODE (s)), MOID (t), + NSYMBOL (NODE (t))); + } + + /* Warn for hidden firmly related operators defined in outer ranges, + if requested. */ + for (TABLE_T *prev = PREVIOUS (TAG_TABLE (s)); + prev != NO_TABLE; + prev = PREVIOUS (prev)) + { + TAG_T *t = find_firmly_related_op (prev, NSYMBOL (NODE (s)), l, r, + NO_TAG /* self */); + if (t != NO_TAG) + { + if (TAG_TABLE (t) == A68_STANDENV) + { + if (a68_warning (p, OPT_Whidden_declarations, + "'M Z' hides a firmly related operator in a larger reach", + MOID (s), NSYMBOL (NODE (s)))) + { + a68_inform (NO_NODE, + "operator 'M Z' defined in the standard prelude", + MOID (t), NSYMBOL (NODE (t))); + } + } + else + { + if (a68_warning (p, OPT_Whidden_declarations, + "'M Z' hides a firmly related operator in a larger reach", + MOID (s), NSYMBOL (NODE (s)))) + { + a68_inform (NODE (t), + "previous hidden declaration of S declared here", + NSYMBOL (NODE (s))); + } + } + + /* Report only one level of hidding or it gets messy. */ + break; + } + } + } + if (NEXT (s) != NO_TAG) + test_firmly_related_ops_local ((p == NO_NODE ? NO_NODE : NODE (NEXT (s))), NEXT (s)); + } +} + +/* Find firmly related operators in this program. */ + +static void +test_firmly_related_ops (NODE_T *p) +{ + for (; p != NO_NODE; FORWARD (p)) + { + if (SUB (p) != NO_NODE && a68_is_new_lexical_level (p)) + { + TAG_T *oops = OPERATORS (TABLE (SUB (p))); + + if (oops != NO_TAG) + test_firmly_related_ops_local (NODE (oops), oops); + } + test_firmly_related_ops (SUB (p)); + } +} + +/* Driver for the processing of TAXes. */ + +void +a68_collect_taxes (NODE_T *p) +{ + tax_tags (p); + tax_specifiers (p); + tax_parameters (p); + tax_for_identifiers (p); + tax_routine_texts (p); + tax_pictures (p); + tax_format_texts (p); + tax_generators (p); + bind_identifier_tag_to_symbol_table (p); + bind_indicant_tag_to_symbol_table (p); + test_firmly_related_ops (p); + test_firmly_related_ops_local (NO_NODE, OPERATORS (A68_STANDENV)); +} + +/* Whether tag has already been declared in this range. */ + +static void +already_declared (NODE_T *n, int a) +{ + if (find_tag_local (TABLE (n), a, NSYMBOL (n)) != NO_TAG) + a68_error (n, "multiple declaration of tag S"); +} + +/* Whether tag has already been declared in this range. */ + +static void +already_declared_hidden (NODE_T *n, int a) +{ + if (find_tag_local (TABLE (n), a, NSYMBOL (n)) != NO_TAG) + a68_error (n, "multiple declaration of tag S"); + + TAG_T *s = a68_find_tag_global (PREVIOUS (TABLE (n)), a, NSYMBOL (n)); + if (s != NO_TAG) + { + if (TAG_TABLE (s) == A68_STANDENV) + { + a68_warning (n, OPT_Whidden_declarations, + "declaration hides prelude declaration of M S", + MOID (s), NSYMBOL (n)); + } + else + { + if (a68_warning (n, OPT_Whidden_declarations, + "declaration hides a declaration of S with larger reach", + NSYMBOL (n))) + { + a68_inform (NO_NODE, + "previous hidden declaration of S declared here", + NSYMBOL (n)); + } + } + } +} + +/* Add tag to local symbol table. */ + +TAG_T * +a68_add_tag (TABLE_T *s, int a, NODE_T *n, MOID_T *m, int p) +{ +#define INSERT_TAG(l, n) {NEXT (n) = *(l); *(l) = (n);} + if (s != NO_TABLE) + { + TAG_T *z = a68_new_tag (); + + TAG_TABLE (z) = s; + PRIO (z) = p; + MOID (z) = m; + NODE (z) = n; + /* TAX(n) = z;. */ + switch (a) + { + case IDENTIFIER: + already_declared_hidden (n, IDENTIFIER); + already_declared_hidden (n, LABEL); + INSERT_TAG (&IDENTIFIERS (s), z); + break; + case INDICANT: + already_declared_hidden (n, INDICANT); + already_declared (n, OP_SYMBOL); + already_declared (n, PRIO_SYMBOL); + INSERT_TAG (&INDICANTS (s), z); + break; + case LABEL: + already_declared_hidden (n, LABEL); + already_declared_hidden (n, IDENTIFIER); + INSERT_TAG (&LABELS (s), z); + break; + case OP_SYMBOL: + already_declared (n, INDICANT); + INSERT_TAG (&OPERATORS (s), z); + break; + case PRIO_SYMBOL: + already_declared (n, PRIO_SYMBOL); + already_declared (n, INDICANT); + INSERT_TAG (&PRIO (s), z); + break; + case ANONYMOUS: + INSERT_TAG (&ANONYMOUS (s), z); + break; + default: + gcc_unreachable (); + } + return z; + } + else + return NO_TAG; +} + +/* Find a tag, searching symbol tables towards the root. */ + +TAG_T * +a68_find_tag_global (TABLE_T *table, int a, const char *name) +{ + if (table != NO_TABLE) + { + TAG_T *s = NO_TAG; + switch (a) + { + case IDENTIFIER: + s = IDENTIFIERS (table); + break; + case INDICANT: + s = INDICANTS (table); + break; + case LABEL: + s = LABELS (table); + break; + case OP_SYMBOL: + s = OPERATORS (table); + break; + case PRIO_SYMBOL: + s = PRIO (table); + break; + default: + gcc_unreachable (); + break; + } + + for (; s != NO_TAG; FORWARD (s)) + { + if (NSYMBOL (NODE (s)) == name) + return s; + } + return a68_find_tag_global (PREVIOUS (table), a, name); + } + else + return NO_TAG; +} + +/* Whether identifier or label global. */ + +int +a68_is_identifier_or_label_global (TABLE_T *table, const char *name) +{ + if (table != NO_TABLE) + { + for (TAG_T *s = IDENTIFIERS (table); s != NO_TAG; FORWARD (s)) + { + if (NSYMBOL (NODE (s)) == name) + return IDENTIFIER; + } + for (TAG_T *s = LABELS (table); s != NO_TAG; FORWARD (s)) + { + if (NSYMBOL (NODE (s)) == name) + return LABEL; + } + return a68_is_identifier_or_label_global (PREVIOUS (table), name); + } + else + return 0; +} + +/* Find a tag, searching only local symbol table. */ + +static TAG_T * +find_tag_local (TABLE_T *table, int a, const char *name) +{ + if (table != NO_TABLE) + { + TAG_T *s = NO_TAG; + + if (a == OP_SYMBOL) + s = OPERATORS (table); + else if (a == PRIO_SYMBOL) + s = PRIO (table); + else if (a == IDENTIFIER) + s = IDENTIFIERS (table); + else if (a == INDICANT) + s = INDICANTS (table); + else if (a == LABEL) + s = LABELS (table); + else + gcc_unreachable (); + + for (; s != NO_TAG; FORWARD (s)) + { + if (NSYMBOL (NODE (s)) == name) + return s; + } + } + return NO_TAG; +} + +/* Whether context specifies HEAP or LOC for an identifier. */ + +static int +tab_qualifier (NODE_T *p) +{ + if (p != NO_NODE) + { + if (a68_is_one_of (p, UNIT, ASSIGNATION, TERTIARY, SECONDARY, GENERATOR, STOP)) + return tab_qualifier (SUB (p)); + else if (a68_is_one_of (p, LOC_SYMBOL, HEAP_SYMBOL, STOP)) + return ATTRIBUTE (p) == LOC_SYMBOL ? LOC_SYMBOL : HEAP_SYMBOL; + else + return LOC_SYMBOL; + } + else + return LOC_SYMBOL; +} + +/* Enter identity declarations in the symbol table. */ + +static void +tax_identity_dec (NODE_T *p, MOID_T **m) +{ + if (p != NO_NODE) + { + if (IS (p, IDENTITY_DECLARATION)) + { + tax_identity_dec (SUB (p), m); + tax_identity_dec (NEXT (p), m); + } + else if (IS (p, DECLARER)) + { + tax_tags (SUB (p)); + *m = MOID (p); + tax_identity_dec (NEXT (p), m); + } + else if (IS (p, COMMA_SYMBOL)) + { + tax_identity_dec (NEXT (p), m); + } + else if (IS (p, DEFINING_IDENTIFIER)) + { + TAG_T *entry = find_tag_local (TABLE (p), IDENTIFIER, NSYMBOL (p)); + + MOID (p) = *m; + HEAP (entry) = LOC_SYMBOL; + TAX (p) = entry; + MOID (entry) = *m; + if (ATTRIBUTE (*m) == REF_SYMBOL) + HEAP (entry) = tab_qualifier (NEXT_NEXT (p)); + tax_identity_dec (NEXT_NEXT (p), m); + } + else + tax_tags (p); + } +} + +/* Enter variable declarations in the symbol table. */ + +static void +tax_variable_dec (NODE_T *p, int *q, MOID_T **m) +{ + if (p != NO_NODE) + { + if (IS (p, VARIABLE_DECLARATION)) + { + tax_variable_dec (SUB (p), q, m); + tax_variable_dec (NEXT (p), q, m); + } + else if (IS (p, DECLARER)) + { + tax_tags (SUB (p)); + *m = MOID (p); + tax_variable_dec (NEXT (p), q, m); + } + else if (IS (p, QUALIFIER)) + { + *q = ATTRIBUTE (SUB (p)); + tax_variable_dec (NEXT (p), q, m); + } + else if (IS (p, COMMA_SYMBOL)) + { + tax_variable_dec (NEXT (p), q, m); + } + else if (IS (p, DEFINING_IDENTIFIER)) + { + TAG_T *entry = find_tag_local (TABLE (p), IDENTIFIER, NSYMBOL (p)); + + MOID (p) = *m; + TAX (p) = entry; + HEAP (entry) = *q; + if (*q == LOC_SYMBOL) + { + TAG_T *z = a68_add_tag (TABLE (p), ANONYMOUS, p, SUB (*m), GENERATOR); + HEAP (z) = LOC_SYMBOL; + USE (z) = true; + BODY (entry) = z; + } + else + { + BODY (entry) = NO_TAG; + } + MOID (entry) = *m; + tax_variable_dec (NEXT (p), q, m); + } + else + tax_tags (p); + } +} + +/* Enter procedure variable declarations in the symbol table. */ + +static void +tax_proc_variable_dec (NODE_T *p, int *q) +{ + if (p != NO_NODE) + { + if (IS (p, PROCEDURE_VARIABLE_DECLARATION)) + { + tax_proc_variable_dec (SUB (p), q); + tax_proc_variable_dec (NEXT (p), q); + } + else if (IS (p, QUALIFIER)) + { + *q = ATTRIBUTE (SUB (p)); + tax_proc_variable_dec (NEXT (p), q); + } + else if (a68_is_one_of (p, PROC_SYMBOL, COMMA_SYMBOL, STOP)) + { + tax_proc_variable_dec (NEXT (p), q); + } + else if (IS (p, DEFINING_IDENTIFIER)) + { + TAG_T *entry = find_tag_local (TABLE (p), IDENTIFIER, NSYMBOL (p)); + + TAX (p) = entry; + HEAP (entry) = *q; + MOID (entry) = MOID (p); + if (*q == LOC_SYMBOL) + { + TAG_T *z = a68_add_tag (TABLE (p), ANONYMOUS, p, SUB_MOID (p), GENERATOR); + HEAP (z) = LOC_SYMBOL; + USE (z) = true; + BODY (entry) = z; + } + else + { + BODY (entry) = NO_TAG; + } + tax_proc_variable_dec (NEXT (p), q); + } + else + tax_tags (p); + } +} + +/* Enter procedure declarations in the symbol table. */ + +static void +tax_proc_dec (NODE_T *p) +{ + if (p != NO_NODE) + { + if (IS (p, PROCEDURE_DECLARATION)) + { + tax_proc_dec (SUB (p)); + tax_proc_dec (NEXT (p)); + } + else if (a68_is_one_of (p, PROC_SYMBOL, COMMA_SYMBOL, STOP)) + { + tax_proc_dec (NEXT (p)); + } + else if (IS (p, DEFINING_IDENTIFIER)) + { + TAG_T *entry = find_tag_local (TABLE (p), IDENTIFIER, NSYMBOL (p)); + + MOID_T *m = MOID (NEXT_NEXT (p)); + MOID (p) = m; + TAX (p) = entry; + HEAP (entry) = LOC_SYMBOL; + MOID (entry) = m; + tax_proc_dec (NEXT (p)); + } + else + tax_tags (p); + } +} + +/* Check validity of operator declaration. */ + +static void +check_operator_dec (NODE_T *p, MOID_T *u) +{ + int k = 0; + + if (u == NO_MOID) + { + NODE_T *pack = SUB_SUB (NEXT_NEXT (p)); /* Where the parameter pack + is. */ + if (ATTRIBUTE (NEXT_NEXT (p)) != ROUTINE_TEXT) + pack = SUB (pack); + k = 1 + a68_count_operands (pack); + } + else + k = a68_count_pack_members (PACK (u)); + + if (k < 1 || k > 2) + { + a68_error (p, "incorrect number of operands for S"); + k = 0; + } + + if (k == 1 && strchr (NOMADS, NSYMBOL (p)[0]) != NO_TEXT) + { + a68_error (p, "monadic S cannot start with a character from Z", NOMADS); + } + else if (k == 2 && !a68_find_tag_global (TABLE (p), PRIO_SYMBOL, NSYMBOL (p))) + { + a68_error (p, "dyadic S has no priority declaration"); + } +} + +/* Enter operator declarations in the symbol table. */ + +static void +tax_op_dec (NODE_T *p, MOID_T **m) +{ + if (p != NO_NODE) + { + if (IS (p, OPERATOR_DECLARATION)) + { + tax_op_dec (SUB (p), m); + tax_op_dec (NEXT (p), m); + } + else if (IS (p, OPERATOR_PLAN)) + { + tax_tags (SUB (p)); + *m = MOID (p); + tax_op_dec (NEXT (p), m); + } + else if (IS (p, OP_SYMBOL)) + { + tax_op_dec (NEXT (p), m); + } + else if (IS (p, COMMA_SYMBOL)) + { + tax_op_dec (NEXT (p), m); + } + else if (IS (p, DEFINING_OPERATOR)) + { + TAG_T *entry = OPERATORS (TABLE (p)); + check_operator_dec (p, *m); + while (entry != NO_TAG && NODE (entry) != p) + FORWARD (entry); + MOID (p) = *m; + TAX (p) = entry; + HEAP (entry) = LOC_SYMBOL; + MOID (entry) = *m; + tax_op_dec (NEXT (p), m); + } + else + { + tax_tags (p); + } + } +} + +/* Enter brief operator declarations in the symbol table. */ + +static void +tax_brief_op_dec (NODE_T *p) +{ + if (p != NO_NODE) + { + if (IS (p, BRIEF_OPERATOR_DECLARATION)) + { + tax_brief_op_dec (SUB (p)); + tax_brief_op_dec (NEXT (p)); + } + else if (a68_is_one_of (p, OP_SYMBOL, COMMA_SYMBOL, STOP)) + { + tax_brief_op_dec (NEXT (p)); + } + else if (IS (p, DEFINING_OPERATOR)) + { + TAG_T *entry = OPERATORS (TABLE (p)); + MOID_T *m = MOID (NEXT_NEXT (p)); + check_operator_dec (p, NO_MOID); + while (entry != NO_TAG && NODE (entry) != p) + FORWARD (entry); + MOID (p) = m; + TAX (p) = entry; + HEAP (entry) = LOC_SYMBOL; + MOID (entry) = m; + tax_brief_op_dec (NEXT (p)); + } + else + { + tax_tags (p); + } + } +} + +/* Enter priority declarations in the symbol table. */ + +static void tax_prio_dec (NODE_T *p) +{ + if (p != NO_NODE) + { + if (IS (p, PRIORITY_DECLARATION)) + { + tax_prio_dec (SUB (p)); + tax_prio_dec (NEXT (p)); + } + else if (a68_is_one_of (p, PRIO_SYMBOL, COMMA_SYMBOL, STOP)) + { + tax_prio_dec (NEXT (p)); + } + else if (IS (p, DEFINING_OPERATOR)) + { + TAG_T *entry = PRIO (TABLE (p)); + while (entry != NO_TAG && NODE (entry) != p) + FORWARD (entry); + MOID (p) = NO_MOID; + TAX (p) = entry; + HEAP (entry) = LOC_SYMBOL; + tax_prio_dec (NEXT (p)); + } + else + { + tax_tags (p); + } + } +} + +/* Enter TAXes in the symbol table. */ + +static void +tax_tags (NODE_T *p) +{ + for (; p != NO_NODE; FORWARD (p)) + { + int heap = LOC_SYMBOL; + MOID_T *m = NO_MOID; + + if (IS (p, IDENTITY_DECLARATION)) + tax_identity_dec (p, &m); + else if (IS (p, VARIABLE_DECLARATION)) + tax_variable_dec (p, &heap, &m); + else if (IS (p, PROCEDURE_DECLARATION)) + tax_proc_dec (p); + else if (IS (p, PROCEDURE_VARIABLE_DECLARATION)) + tax_proc_variable_dec (p, &heap); + else if (IS (p, OPERATOR_DECLARATION)) + tax_op_dec (p, &m); + else if (IS (p, BRIEF_OPERATOR_DECLARATION)) + tax_brief_op_dec (p); + else if (IS (p, PRIORITY_DECLARATION)) + tax_prio_dec (p); + else + tax_tags (SUB (p)); + } +} + +/* Reset symbol table nest count. */ + +void +a68_reset_symbol_table_nest_count (NODE_T *p) +{ + for (; p != NO_NODE; FORWARD (p)) + { + if (SUB (p) != NO_NODE && a68_is_new_lexical_level (p)) + NEST (TABLE (SUB (p))) = A68 (symbol_table_count)++; + a68_reset_symbol_table_nest_count (SUB (p)); + } +} + +//! @brief Bind routines in symbol table to the tree. + +void +a68_bind_routine_tags_to_tree (NODE_T *p) +{ + /* By inserting coercions etc. some may have shifted. */ + for (; p != NO_NODE; FORWARD (p)) + { + if (IS (p, ROUTINE_TEXT) && TAX (p) != NO_TAG) + NODE (TAX (p)) = p; + a68_bind_routine_tags_to_tree (SUB (p)); + } +} + +/* Bind formats in symbol table to tree. */ + +static void +bind_format_tags_to_tree (NODE_T *p) +{ + /* By inserting coercions etc. some may have shifted. */ + for (; p != NO_NODE; FORWARD (p)) + { + if (IS (p, FORMAT_TEXT) && TAX (p) != NO_TAG) + NODE (TAX (p)) = p; + else if (IS (p, FORMAT_DELIMITER_SYMBOL) && NEXT (p) != NO_NODE && TAX (p) != NO_TAG) + NODE (TAX (p)) = p; + + bind_format_tags_to_tree (SUB (p)); + } +} + +/* Fill outer level of symbol table. */ + +void +a68_fill_symbol_table_outer (NODE_T *p, TABLE_T *s) +{ + for (; p != NO_NODE; FORWARD (p)) + { + if (TABLE (p) != NO_TABLE) + OUTER (TABLE (p)) = s; + + if (SUB (p) != NO_NODE && IS (p, ROUTINE_TEXT)) + a68_fill_symbol_table_outer (SUB (p), TABLE (SUB (p))); + else if (SUB (p) != NO_NODE && IS (p, FORMAT_TEXT)) + a68_fill_symbol_table_outer (SUB (p), TABLE (SUB (p))); + else + a68_fill_symbol_table_outer (SUB (p), s); + } +} + +/* Flood branch in tree with local symbol table S. */ + +static void +flood_with_symbol_table_restricted (NODE_T *p, TABLE_T *s) +{ + for (; p != NO_NODE; FORWARD (p)) + { + TABLE (p) = s; + if (ATTRIBUTE (p) != ROUTINE_TEXT && ATTRIBUTE (p) != SPECIFIED_UNIT) + { + if (a68_is_new_lexical_level (p)) + PREVIOUS (TABLE (SUB (p))) = s; + else + flood_with_symbol_table_restricted (SUB (p), s); + } + } +} + +/* Final structure of symbol table after parsing. */ + +void +a68_finalise_symbol_table_setup (NODE_T *p, int l) +{ + TABLE_T *s = TABLE (p); + NODE_T *q = p; + + while (q != NO_NODE) + { + /* routine texts are ranges. */ + if (IS (q, ROUTINE_TEXT)) + flood_with_symbol_table_restricted (SUB (q), a68_new_symbol_table (s)); + + /* specifiers are ranges. */ + else if (IS (q, SPECIFIED_UNIT)) + flood_with_symbol_table_restricted (SUB (q), a68_new_symbol_table (s)); + + /* level count and recursion. */ + if (SUB (q) != NO_NODE) + { + if (a68_is_new_lexical_level (q)) + { + LEX_LEVEL (SUB (q)) = l + 1; + PREVIOUS (TABLE (SUB (q))) = s; + a68_finalise_symbol_table_setup (SUB (q), l + 1); + if (IS (q, WHILE_PART)) + { + /* This was a bug that went unnoticed for 15 years!. */ + TABLE_T *s2 = TABLE (SUB (q)); + if ((FORWARD (q)) == NO_NODE) + return; + if (IS (q, ALT_DO_PART)) + { + PREVIOUS (TABLE (SUB (q))) = s2; + LEX_LEVEL (SUB (q)) = l + 2; + a68_finalise_symbol_table_setup (SUB (q), l + 2); + } + } + } + else + { + TABLE (SUB (q)) = s; + a68_finalise_symbol_table_setup (SUB (q), l); + } + } + TABLE (q) = s; + + if (IS (q, FOR_SYMBOL)) + FORWARD (q); + FORWARD (q); + } + + /* FOR identifiers are in the DO ... OD range. */ + for (q = p; q != NO_NODE; FORWARD (q)) + { + if (IS (q, FOR_SYMBOL)) + TABLE (NEXT (q)) = TABLE (SEQUENCE (NEXT (q))); + } +} + +/* First structure of symbol table for parsing. */ + +void +a68_preliminary_symbol_table_setup (NODE_T *p) +{ + TABLE_T *s = TABLE (p); + bool not_a_for_range = false; + + /* Let the tree point to the current symbol table. */ + for (NODE_T *q = p; q != NO_NODE; FORWARD (q)) + TABLE (q) = s; + + /* insert new tables when required. */ + for (NODE_T *q = p; q != NO_NODE && !not_a_for_range; FORWARD (q)) + { + if (SUB (q) != NO_NODE) + { + /* BEGIN ... END, CODE ... EDOC, DEF ... FED, DO ... OD, $ ... $, + { ... } are ranges. */ + if (a68_is_one_of (q, BEGIN_SYMBOL, DO_SYMBOL, ALT_DO_SYMBOL, + FORMAT_DELIMITER_SYMBOL, STOP)) + { + TABLE (SUB (q)) = a68_new_symbol_table (s); + a68_preliminary_symbol_table_setup (SUB (q)); + } + /* ( ... ) is a range. */ + else if (IS (q, OPEN_SYMBOL)) + { + if (a68_whether (q, OPEN_SYMBOL, THEN_BAR_SYMBOL, STOP)) + { + TABLE (SUB (q)) = s; + a68_preliminary_symbol_table_setup (SUB (q)); + FORWARD (q); + TABLE (SUB (q)) = a68_new_symbol_table (s); + a68_preliminary_symbol_table_setup (SUB (q)); + if ((FORWARD (q)) == NO_NODE) + not_a_for_range = true; + else + { + if (IS (q, THEN_BAR_SYMBOL)) + { + TABLE (SUB (q)) = a68_new_symbol_table (s); + a68_preliminary_symbol_table_setup (SUB (q)); + } + if (IS (q, OPEN_SYMBOL)) + { + TABLE (SUB (q)) = a68_new_symbol_table (s); + a68_preliminary_symbol_table_setup (SUB (q)); + } + } + } + else + { + /* Don't worry about STRUCT (...), UNION (...), PROC (...) + yet. */ + TABLE (SUB (q)) = a68_new_symbol_table (s); + a68_preliminary_symbol_table_setup (SUB (q)); + } + } + /* IF ... THEN ... ELSE ... FI are ranges. */ + else if (IS (q, IF_SYMBOL)) + { + if (a68_whether (q, IF_SYMBOL, THEN_SYMBOL, STOP)) + { + TABLE (SUB (q)) = s; + a68_preliminary_symbol_table_setup (SUB (q)); + FORWARD (q); + TABLE (SUB (q)) = a68_new_symbol_table (s); + a68_preliminary_symbol_table_setup (SUB (q)); + if ((FORWARD (q)) == NO_NODE) + not_a_for_range = true; + else + if (IS (q, ELSE_SYMBOL)) + { + TABLE (SUB (q)) = a68_new_symbol_table (s); + a68_preliminary_symbol_table_setup (SUB (q)); + } + if (IS (q, IF_SYMBOL)) + { + TABLE (SUB (q)) = a68_new_symbol_table (s); + a68_preliminary_symbol_table_setup (SUB (q)); + } + } + else + { + TABLE (SUB (q)) = a68_new_symbol_table (s); + a68_preliminary_symbol_table_setup (SUB (q)); + } + } + /* CASE ... IN ... OUT ... ESAC are ranges. */ + else if (IS (q, CASE_SYMBOL)) + { + if (a68_whether (q, CASE_SYMBOL, IN_SYMBOL, STOP)) + { + TABLE (SUB (q)) = s; + a68_preliminary_symbol_table_setup (SUB (q)); + FORWARD (q); + TABLE (SUB (q)) = a68_new_symbol_table (s); + a68_preliminary_symbol_table_setup (SUB (q)); + if ((FORWARD (q)) == NO_NODE) + not_a_for_range = true; + else + { + if (IS (q, OUT_SYMBOL)) + { + TABLE (SUB (q)) = a68_new_symbol_table (s); + a68_preliminary_symbol_table_setup (SUB (q)); + } + if (IS (q, CASE_SYMBOL)) + { + TABLE (SUB (q)) = a68_new_symbol_table (s); + a68_preliminary_symbol_table_setup (SUB (q)); + } + } + } + else + { + TABLE (SUB (q)) = a68_new_symbol_table (s); + a68_preliminary_symbol_table_setup (SUB (q)); + } + } + /* WHILE ... DO ... OD are ranges. */ + else if (IS (q, WHILE_SYMBOL)) + { + TABLE_T *u = a68_new_symbol_table (s); + TABLE (SUB (q)) = u; + a68_preliminary_symbol_table_setup (SUB (q)); + if ((FORWARD (q)) == NO_NODE) + not_a_for_range = true; + else if (IS (q, ALT_DO_SYMBOL)) + { + TABLE (SUB (q)) = a68_new_symbol_table (u); + a68_preliminary_symbol_table_setup (SUB (q)); + } + } + else + { + TABLE (SUB (q)) = s; + a68_preliminary_symbol_table_setup (SUB (q)); + } + } + } + /* FOR identifiers will go to the DO ... OD range. */ + if (!not_a_for_range) + { + for (NODE_T *q = p; q != NO_NODE; FORWARD (q)) + { + if (IS (q, FOR_SYMBOL)) + { + NODE_T *r = q; + TABLE (NEXT (q)) = NO_TABLE; + for (; r != NO_NODE && TABLE (NEXT (q)) == NO_TABLE; FORWARD (r)) + { + if ((a68_is_one_of (r, WHILE_SYMBOL, ALT_DO_SYMBOL, STOP)) + && (NEXT (q) != NO_NODE && SUB (r) != NO_NODE)) + { + TABLE (NEXT (q)) = TABLE (SUB (r)); + SEQUENCE (NEXT (q)) = SUB (r); + } + } + } + } + } +} + +/* Mark a mode as in use. */ + +static void +mark_mode (MOID_T *m) +{ + if (m != NO_MOID && USE (m) == false) + { + PACK_T *p = PACK (m); + USE (m) = true; + for (; p != NO_PACK; FORWARD (p)) + { + mark_mode (MOID (p)); + mark_mode (SUB (m)); + mark_mode (SLICE (m)); + } + } +} + +//! @brief Traverse tree and mark modes as used. + +void +a68_mark_moids (NODE_T *p) +{ + for (; p != NO_NODE; FORWARD (p)) + { + a68_mark_moids (SUB (p)); + if (MOID (p) != NO_MOID) + mark_mode (MOID (p)); + } +} + +/* Mark various tags as used. */ + +void +a68_mark_auxilliary (NODE_T *p) +{ + for (; p != NO_NODE; FORWARD (p)) + { + if (SUB (p) != NO_NODE) + { + /* You get no warnings on unused PROC parameters. That is ok since + A68 has some parameters that you may not use at all - think of + PROC (REF FILE) BOOL event routines in transput. */ + a68_mark_auxilliary (SUB (p)); + } + else if (IS (p, OPERATOR)) + { + TAG_T *z; + + if (TAX (p) != NO_TAG) + USE (TAX (p)) = true; + + if ((z = a68_find_tag_global (TABLE (p), PRIO_SYMBOL, NSYMBOL (p))) != NO_TAG) + USE (z) = true; + } + else if (IS (p, INDICANT)) + { + TAG_T *z = a68_find_tag_global (TABLE (p), INDICANT, NSYMBOL (p)); + + if (z != NO_TAG) + { + TAX (p) = z; + USE (z) = true; + } + } + else if (IS (p, IDENTIFIER)) + { + if (TAX (p) != NO_TAG) + USE (TAX (p)) = true; + } + } +} + +/* Check a single tag. */ + +static void +unused (TAG_T *s) +{ + for (; s != NO_TAG; FORWARD (s)) + { + if (LINE_NUMBER (NODE (s)) > 0 && !USE (s)) + a68_warning (NODE (s), OPT_Wunused, "tag S is not used", NODE (s)); + } +} + +/* Driver for traversing tree and warn for unused tags. */ + +void +a68_warn_for_unused_tags (NODE_T *p) +{ + for (; p != NO_NODE; FORWARD (p)) + { + if (SUB (p) != NO_NODE) + { + if (a68_is_new_lexical_level (p)) + { + unused (OPERATORS (TABLE (SUB (p)))); + unused (PRIO (TABLE (SUB (p)))); + unused (IDENTIFIERS (TABLE (SUB (p)))); + unused (LABELS (TABLE (SUB (p)))); + unused (INDICANTS (TABLE (SUB (p)))); + } + } + a68_warn_for_unused_tags (SUB (p)); + } +} + +/* Mark jumps and procedured jumps. */ + +void +a68_jumps_from_procs (NODE_T *p) +{ + for (; p != NO_NODE; FORWARD (p)) + { + if (IS (p, PROCEDURING)) + { + NODE_T *u = SUB_SUB (p); + + if (IS (u, GOTO_SYMBOL)) + FORWARD (u); + USE (TAX (u)) = true; + } + else if (IS (p, JUMP)) + { + NODE_T *u = SUB (p); + + if (IS (u, GOTO_SYMBOL)) + FORWARD (u); + if ((TAX (u) == NO_TAG) && (MOID (u) == NO_MOID) + && (a68_find_tag_global (TABLE (u), LABEL, NSYMBOL (u)) == NO_TAG)) + { + (void) a68_add_tag (TABLE (u), LABEL, u, NO_MOID, LOCAL_LABEL); + a68_error (u, "tag S has not been declared properly"); + } + else + USE (TAX (u)) = true; + } + else + a68_jumps_from_procs (SUB (p)); + } +} -- 2.30.2
