--- /dev/null
+/* Recursive-descent parenthesis checker.
+ Copyright (C) 2001-2023 J. Marcel van der Veer.
+ Copyright (C) 2025 Jose E. Marchesi.
+
+ Original implementation by J. Marcel van der Veer.
+ Adapted for GCC by Jose E. Marchesi.
+
+ GCC is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GCC is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GCC; see the file COPYING3. If not see
+ <http://www.gnu.org/licenses/>. */
+
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+
+#include "a68.h"
+
+/* After this checker, we know that at least brackets are matched. This
+ stabilises later parser phases.
+
+ Note that this checker operates on a linear list of nodes.
+
+ Error diagnostics are placed near offending lines. */
+
+/* Intelligible diagnostics for the bracket checker. */
+
+static void
+bracket_check_error (char *txt, int n, const char *bra, const char *ket)
+{
+ BUFFER buf;
+
+ if (n == 0)
+ return;
+
+ const char *strop_ket = a68_strop_keyword (ket);
+ const char *strop_bra = a68_strop_keyword (bra);
+
+ BUFCLR (buf);
+ if (snprintf (buf, SNPRINTF_SIZE, "missing matching %%<%s%%>",
+ (n > 0 ? strop_ket : strop_bra)) < 0)
+ gcc_unreachable ();
+
+ if (strlen (txt) > 0)
+ a68_bufcat (txt, " or ", BUFFER_SIZE);
+ a68_bufcat (txt, buf, BUFFER_SIZE);
+}
+
+/* Diagnose brackets in local branch of the tree. */
+
+static char *
+bracket_check_diagnose (NODE_T *p)
+{
+ int begins = 0, opens = 0, format_delims = 0, format_opens = 0;
+ int subs = 0, ifs = 0, cases = 0, dos = 0;
+
+ for (; p != NO_NODE; FORWARD (p))
+ {
+ switch (ATTRIBUTE (p))
+ {
+ case BEGIN_SYMBOL:
+ begins++;
+ break;
+ case END_SYMBOL:
+ begins--;
+ break;
+ case OPEN_SYMBOL:
+ opens++;
+ break;
+ case CLOSE_SYMBOL:
+ opens--;
+ break;
+ case FORMAT_DELIMITER_SYMBOL:
+ if (format_delims == 0)
+ format_delims = 1;
+ else
+ format_delims = 0;
+ break;
+ case FORMAT_OPEN_SYMBOL:
+ format_opens++;
+ break;
+ case FORMAT_CLOSE_SYMBOL:
+ format_opens--;
+ break;
+ case SUB_SYMBOL:
+ subs++;
+ break;
+ case BUS_SYMBOL:
+ subs--;
+ break;
+ case IF_SYMBOL:
+ ifs++;
+ break;
+ case FI_SYMBOL:
+ ifs--;
+ break;
+ case CASE_SYMBOL:
+ cases++;
+ break;
+ case ESAC_SYMBOL:
+ cases--;
+ break;
+ case DO_SYMBOL:
+ dos++;
+ break;
+ case OD_SYMBOL:
+ dos--;
+ break;
+ default:
+ break;
+ }
+ }
+
+ A68 (edit_line)[0] = '\0';
+ bracket_check_error (A68 (edit_line), begins, "BEGIN", "END");
+ bracket_check_error (A68 (edit_line), opens, "(", ")");
+ bracket_check_error (A68 (edit_line), format_opens, "(", ")");
+ bracket_check_error (A68 (edit_line), format_delims, "$", "$");
+ bracket_check_error (A68 (edit_line), subs, "[", "]");
+ bracket_check_error (A68 (edit_line), ifs, "IF", "FI");
+ bracket_check_error (A68 (edit_line), cases, "CASE", "ESAC");
+ bracket_check_error (A68 (edit_line), dos, "DO", "OD");
+ return A68 (edit_line);
+}
+
+/* Driver for locally diagnosing non-matching tokens. */
+
+static NODE_T *
+bracket_check_parse (NODE_T *top, NODE_T *p)
+{
+ for (; p != NO_NODE; FORWARD (p))
+ {
+ int ket = STOP;
+ NODE_T *q = NO_NODE;
+ bool ignore_token = false;
+
+ switch (ATTRIBUTE (p))
+ {
+ case BEGIN_SYMBOL:
+ ket = END_SYMBOL;
+ q = bracket_check_parse (top, NEXT (p));
+ break;
+ case OPEN_SYMBOL:
+ ket = CLOSE_SYMBOL;
+ q = bracket_check_parse (top, NEXT (p));
+ break;
+ case FORMAT_OPEN_SYMBOL:
+ ket = FORMAT_CLOSE_SYMBOL;
+ q = bracket_check_parse (top, NEXT (p));
+ break;
+ case SUB_SYMBOL:
+ ket = BUS_SYMBOL;
+ q = bracket_check_parse (top, NEXT (p));
+ break;
+ case IF_SYMBOL:
+ ket = FI_SYMBOL;
+ q = bracket_check_parse (top, NEXT (p));
+ break;
+ case CASE_SYMBOL:
+ ket = ESAC_SYMBOL;
+ q = bracket_check_parse (top, NEXT (p));
+ break;
+ case DO_SYMBOL:
+ ket = OD_SYMBOL;
+ q = bracket_check_parse (top, NEXT (p));
+ break;
+ case END_SYMBOL:
+ case CLOSE_SYMBOL:
+ case FORMAT_CLOSE_SYMBOL:
+ case BUS_SYMBOL:
+ case FI_SYMBOL:
+ case ESAC_SYMBOL:
+ case OD_SYMBOL:
+ return p;
+ default:
+ ignore_token = true;
+ }
+
+ if (ignore_token)
+ ;
+ else if (q != NO_NODE && IS (q, ket))
+ p = q;
+ else if (q == NO_NODE)
+ {
+ char *diag = bracket_check_diagnose (top);
+ a68_error (p, "incorrect nesting, check for Y",
+ (strlen (diag) > 0 ? diag : "missing or unmatched keyword"));
+ longjmp (A68_PARSER (top_down_crash_exit), 1);
+ }
+ else
+ {
+ char *diag = bracket_check_diagnose (top);
+ a68_error (q, "unexpected X, check for Y",
+ ATTRIBUTE (q),
+ (strlen (diag) > 0 ? diag : "missing or unmatched keyword"));
+ longjmp (A68_PARSER (top_down_crash_exit), 1);
+ }
+ }
+ return NO_NODE;
+}
+
+/* Driver for globally diagnosing non-matching tokens. */
+
+void
+a68_check_parenthesis (NODE_T *top)
+{
+ if (!setjmp (A68_PARSER (top_down_crash_exit)))
+ {
+ if (bracket_check_parse (top, top) != NO_NODE)
+ a68_error (top, "incorrect nesting, check for Y",
+ "missing or unmatched keyword");
+ }
+}