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

Reply via email to