/* 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 . */ #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"); } }