[gcc r16-5746] a68: parser: symbol table management

Jose E. Marchesi jemarch@gcc.gnu.org
Sun Nov 30 01:01:12 GMT 2025


https://gcc.gnu.org/g:95b025f3a187def451a92ff107fd64e08d1773e1

commit r16-5746-g95b025f3a187def451a92ff107fd64e08d1773e1
Author: Jose E. Marchesi <jose.marchesi@oracle.com>
Date:   Sat Oct 11 19:49:38 2025 +0200

    a68: parser: symbol table management
    
    Signed-off-by: Jose E. Marchesi <jemarch@gnu.org>
    Co-authored-by: Marcel van der Veer <algol68g@xs4all.nl>

Diff:
---
 gcc/algol68/a68-parser-taxes.cc | 1806 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 1806 insertions(+)

diff --git a/gcc/algol68/a68-parser-taxes.cc b/gcc/algol68/a68-parser-taxes.cc
new file mode 100644
index 000000000000..fa9f9471d983
--- /dev/null
+++ b/gcc/algol68/a68-parser-taxes.cc
@@ -0,0 +1,1806 @@
+/* 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 || strcmp (NSYMBOL (NODE (s)), name) == 0)
+	    return IDENTIFIER;
+	}
+      for (TAG_T *s = INDICANTS (table); s != NO_TAG; FORWARD (s))
+	{
+	  if (NSYMBOL (NODE (s)) == name || strcmp (NSYMBOL (NODE (s)), name) == 0)
+	    return INDICANT;
+	}
+      for (TAG_T *s = LABELS (table); s != NO_TAG; FORWARD (s))
+	{
+	  if (NSYMBOL (NODE (s)) == name || strcmp (NSYMBOL (NODE (s)), name) == 0)
+	    return LABEL;
+	}
+      for (TAG_T *s = OPERATORS (table); s != NO_TAG; FORWARD (s))
+	{
+	  if (NSYMBOL (NODE (s)) == name || strcmp (NSYMBOL (NODE (s)), name) == 0)
+	    return OP_SYMBOL;
+	}
+      for (TAG_T *s = MODULES (table); s != NO_TAG; FORWARD (s))
+	{
+	  if (NSYMBOL (NODE (s)) == name || strcmp (NSYMBOL (NODE (s)), name) == 0)
+	    return MODULE_SYMBOL;
+	}
+      for (TAG_T *s = PRIO (table); s != NO_TAG; FORWARD (s))
+	{
+	  if (NSYMBOL (NODE (s)) == name || strcmp (NSYMBOL (NODE (s)), name) == 0)
+	    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 (const 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")];
+	  const 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")];
+	  const 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)));
+	    }
+	  else
+	    {
+	      /* 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
+			  && warn_algol68_hidden_declarations > 0)
+			{
+			  if (a68_warning (p, OPT_Whidden_declarations_,
+					   "Z hides a firmly related operator in a larger reach",
+					   NSYMBOL (NODE (s))))
+			    {
+			      a68_inform (NO_NODE,
+					  "operator M Z defined in the standard prelude",
+					  MOID (t), NSYMBOL (NODE (t)));
+			    }
+			}
+		      else if (warn_algol68_hidden_declarations > 1)
+			{
+			  if (a68_warning (p, OPT_Whidden_declarations_,
+					   "Z hides a firmly related operator in a larger reach",
+					   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
+      && ((TAG_TABLE (s) == A68_STANDENV && warn_algol68_hidden_declarations > 0)
+	  || (TAG_TABLE (s) != A68_STANDENV && warn_algol68_hidden_declarations > 1)))
+    {
+      if (a68_warning (n, OPT_Whidden_declarations_,
+		       "Z hides a declaration with larger reach",
+		       NSYMBOL (n)))
+	{
+	  if (TAG_TABLE (s) == A68_STANDENV)
+	    a68_inform (NO_NODE,
+			"M Z defined in the standard prelude",
+			MOID (s), NSYMBOL (NODE (s)));
+	  else
+	    a68_inform (NODE (s),
+			"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 MODULE_SYMBOL:
+	  already_declared (n, INDICANT);
+	  INSERT_TAG (&MODULES (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 MODULE_SYMBOL:
+	  s = MODULES (table);
+	  break;
+	case PRIO_SYMBOL:
+	  s = PRIO (table);
+	  break;
+	default:
+	  gcc_unreachable ();
+	  break;
+	}
+
+      for (; s != NO_TAG; FORWARD (s))
+	{
+	  if (NSYMBOL (NODE (s)) == name || strcmp (NSYMBOL (NODE (s)), name) == 0)
+	    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 || strcmp (NSYMBOL (NODE (s)), name) == 0)
+	    return IDENTIFIER;
+	}
+      for (TAG_T *s = LABELS (table); s != NO_TAG; FORWARD (s))
+	{
+	  if (NSYMBOL (NODE (s)) == name || strcmp (NSYMBOL (NODE (s)), name) == 0)
+	    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 == MODULE_SYMBOL)
+	s = MODULES (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 || strcmp (NSYMBOL (NODE (s)), name) == 0)
+	  return s;
+      }
+    }
+  return NO_TAG;
+}
+
+/* Whether context specifies HEAP or LOC for an identifier.
+
+   The boolean *E is set to true if an explicit qualifier was found, false
+   otherwise.  */
+
+static int
+tab_qualifier (NODE_T *p, bool *e)
+{
+  *e = false;
+  if (p != NO_NODE)
+    {
+      if (a68_is_one_of (p, UNIT, ASSIGNATION, TERTIARY, SECONDARY, GENERATOR, STOP))
+	return tab_qualifier (SUB (p), e);
+      else if (a68_is_one_of (p, LOC_SYMBOL, HEAP_SYMBOL, STOP))
+	{
+	  *e = true;
+	  return ATTRIBUTE (p) == LOC_SYMBOL ? LOC_SYMBOL : HEAP_SYMBOL;
+	}
+      else
+	return LOC_SYMBOL;
+    }
+  else
+    return LOC_SYMBOL;
+}
+
+/* Enter identity declarations in the symbol table.
+
+   E is true if HEAP or LOC got specified explicitly in the identity
+   declaration's formal parameter.  */
+
+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));
+	  gcc_assert (entry != NO_TAG);
+
+	  MOID (p) = *m;
+	  HEAP (entry) = LOC_SYMBOL;
+	  TAX (p) = entry;
+	  MOID (entry) = *m;
+	  PUBLICIZED (entry) = PUBLICIZED (p);
+	  bool e;
+	  if (ATTRIBUTE (*m) == REF_SYMBOL)
+	    {
+	      HEAP (entry) = tab_qualifier (NEXT_NEXT (p), &e);
+	      if (e && HEAP (entry) == LOC_SYMBOL && PUBLICIZED (entry))
+		a68_warning (p, 0, "value of local generator will be out of scope");
+	    }
+	  tax_identity_dec (NEXT_NEXT (p), m);
+	}
+      else
+	tax_tags (p);
+    }
+}
+
+/* Enter variable declarations in the symbol table.
+
+   E is true if an explicit sample generator was specified in the variable
+   declaration.  */
+
+static void
+tax_variable_dec (NODE_T *p, int *q, MOID_T **m, bool e)
+{
+  if (p != NO_NODE)
+    {
+      if (IS (p, VARIABLE_DECLARATION))
+	{
+	  tax_variable_dec (SUB (p), q, m, e);
+	  tax_variable_dec (NEXT (p), q, m, e);
+	}
+      else if (IS (p, DECLARER))
+	{
+	  tax_tags (SUB (p));
+	  *m = MOID (p);
+	  tax_variable_dec (NEXT (p), q, m, e);
+	}
+      else if (IS (p, QUALIFIER))
+	{
+	  *q = ATTRIBUTE (SUB (p));
+	  tax_variable_dec (NEXT (p), q, m, true);
+	}
+      else if (IS (p, COMMA_SYMBOL))
+	{
+	  tax_variable_dec (NEXT (p), q, m, e);
+	}
+      else if (IS (p, DEFINING_IDENTIFIER))
+	{
+	  TAG_T *entry = find_tag_local (TABLE (p), IDENTIFIER, NSYMBOL (p));
+
+	  MOID (p) = *m;
+	  TAX (p) = entry;
+	  PUBLICIZED (entry) = PUBLICIZED (p);
+
+	  if (PUBLICIZED (p) && e && *q == LOC_SYMBOL)
+	    a68_error (p, "publicized variable should not be allocated on the stack");
+
+	  if (PUBLICIZED (p))
+	    HEAP (entry) = STATIC_SYMBOL;
+	  else
+	    HEAP (entry) = *q;
+	  
+	  if (HEAP (entry) == 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, e);
+	}
+      else
+	tax_tags (p);
+    }
+}
+
+/* Enter procedure variable declarations in the symbol table.
+
+   E is true if an explicit sample generator was specified in the procedure
+   variable declaration.  */
+
+static void
+tax_proc_variable_dec (NODE_T *p, int *q, bool e)
+{
+  if (p != NO_NODE)
+    {
+      if (IS (p, PROCEDURE_VARIABLE_DECLARATION))
+	{
+	  tax_proc_variable_dec (SUB (p), q, e);
+	  tax_proc_variable_dec (NEXT (p), q, e);
+	}
+      else if (IS (p, QUALIFIER))
+	{
+	  *q = ATTRIBUTE (SUB (p));
+	  tax_proc_variable_dec (NEXT (p), q, true);
+	}
+      else if (a68_is_one_of (p, PROC_SYMBOL, COMMA_SYMBOL, STOP))
+	{
+	  tax_proc_variable_dec (NEXT (p), q, e);
+	}
+      else if (IS (p, DEFINING_IDENTIFIER))
+	{
+	  TAG_T *entry = find_tag_local (TABLE (p), IDENTIFIER, NSYMBOL (p));
+
+	  TAX (p) = entry;
+	  MOID (entry) = MOID (p);
+	  PUBLICIZED (entry) = PUBLICIZED (p);
+
+	  if (PUBLICIZED (p) && e && *q == LOC_SYMBOL)
+	    a68_error (p, "publicized variable should not be allocated on the stack");
+
+	  if (PUBLICIZED (p))
+	    HEAP (entry) = STATIC_SYMBOL;
+	  else
+	    HEAP (entry) = *q;
+
+	  if (HEAP (entry) == 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, e);
+	}
+      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;
+	  PUBLICIZED (entry) = PUBLICIZED (p);
+	  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;
+	  PUBLICIZED (entry) = PUBLICIZED (p);
+	  tax_op_dec (NEXT (p), m);
+	}
+      else
+	{
+	  tax_tags (p);
+	}
+    }
+}
+
+/* Enter module declarations in the symbol table.  */
+
+static void
+tax_module_dec (NODE_T *p)
+{
+  if (p != NO_NODE)
+    {
+      if (IS (p, MODULE_DECLARATION))
+	{
+	  tax_module_dec (SUB (p));
+	  tax_module_dec (NEXT (p));
+	}
+      else if (IS (p, MODULE_SYMBOL))
+	{
+	  tax_module_dec (NEXT (p));
+	}
+      else if (IS (p, COMMA_SYMBOL))
+	{
+	  tax_module_dec (NEXT (p));
+	}
+      else if (IS (p, DEFINING_MODULE_INDICANT))
+	{
+	  TAG_T *entry = MODULES (TABLE (p));
+	  while (entry != NO_TAG && NODE (entry) != p)
+	    FORWARD (entry);
+	  MOID (p) = NO_MOID;
+	  TAX (p) = entry;
+	  HEAP (entry) = LOC_SYMBOL;
+	  MOID (entry) = NO_MOID;
+	  PUBLICIZED (entry) = PUBLICIZED (p);
+	  tax_module_dec (NEXT (p));
+	}
+      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;
+	  PUBLICIZED (entry) = PUBLICIZED (p);
+	  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;
+	  PUBLICIZED (entry) = PUBLICIZED (p);
+	  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, false);
+      else if (IS (p, PROCEDURE_DECLARATION))
+	tax_proc_dec (p);
+      else if (IS (p, PROCEDURE_VARIABLE_DECLARATION))
+	tax_proc_variable_dec (p, &heap, false);
+      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 if (IS (p, MODULE_DECLARATION))
+	tax_module_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 if (SUB (p) != NO_NODE && IS (p, MODULE_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;
+	      gcc_assert (TABLE (SUB (q)) != s);
+	      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);
+		    }
+		}
+	      if (IS (q, DEF_PART))
+		{
+		  TABLE_T *s2 = TABLE (SUB (q));
+		  if ((FORWARD (q)) == NO_NODE)
+		    return;
+		  if (IS (q, POSTLUDE_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, 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));
+		}
+	    }
+	  /* ACCESS ... DEF ...POSTLUDE ...FED are ranges.  */
+	  else if (IS (q, ALT_ACCESS_SYMBOL))
+	    {
+	      TABLE_T *u = a68_new_symbol_table (s);
+	      TABLE (SUB (q)) = u;
+	      a68_preliminary_symbol_table_setup (SUB (q));
+	      if (NEXT (q) == NO_NODE)
+		not_a_for_range = true;
+	      else if (IS ((FORWARD (q)), DEF_SYMBOL))
+		{
+		  TABLE_T *v = a68_new_symbol_table (u);
+		  TABLE (SUB (q)) = v;
+		  a68_preliminary_symbol_table_setup (SUB (q));
+		}
+	    }
+	  /* DEF ... POSTLUDE ... FED are ranges.  */
+	  else if (IS (q, DEF_SYMBOL))
+	    {
+	      TABLE_T *u = a68_new_symbol_table (s);
+	      PUBLIC_RANGE (u) = true;
+	      TABLE (SUB (q)) = u;
+	      a68_preliminary_symbol_table_setup (SUB (q));
+	      if (NEXT (q) == NO_NODE)
+		  not_a_for_range = true;
+	      else if (IS ((FORWARD (q)), POSTLUDE_SYMBOL))
+		{
+		  TABLE_T *v = a68_new_symbol_table (u);
+		  TABLE (SUB (q)) = v;
+		  a68_preliminary_symbol_table_setup (SUB (q));
+		}
+	    }
+	  /* ACCESS ... CONTROLLED_CLAUSE  are ranges.  */
+	  else if (IS (q, ACCESS_SYMBOL))
+	    {
+	      TABLE_T *u = a68_new_symbol_table (s);
+	      TABLE (SUB (q)) = u;
+	      a68_preliminary_symbol_table_setup (SUB (q));
+	      if (NEXT (q) == NO_NODE)
+		not_a_for_range = true;
+	    }
+	  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 (MODULES (TABLE (SUB (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));
+    }
+}


More information about the Gcc-cvs mailing list