|
|
1 //! @file parser-brackets.c 2 //! @author J. Marcel van der Veer 3 4 //! @section Copyright 5 //! 6 //! This file is part of Algol68G - an Algol 68 compiler-interpreter. 7 //! Copyright 2001-2026 J. Marcel van der Veer [algol68g@algol68genie.nl]. 8 9 //! @section License 10 //! 11 //! This program is free software; you can redistribute it and/or modify it 12 //! under the terms of the GNU General Public License as published by the 13 //! Free Software Foundation; either version 3 of the License, or 14 //! (at your option) any later version. 15 //! 16 //! This program is distributed in the hope that it will be useful, but 17 //! WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 18 //! or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 19 //! more details. You should have received a copy of the GNU General Public 20 //! License along with this program. If not, see [http://www.gnu.org/licenses/]. 21 22 //! @section Synopsis 23 //! 24 //! Recursive-descent parenthesis checker. 25 26 #include "a68g.h" 27 #include "a68g-parser.h" 28 29 // After this checker, we know that at least brackets are matched. 30 // This stabilises later parser phases. 31 // Top-down parsing is done to place error diagnostics near offending lines. 32 33 //! @brief Intelligible diagnostics for the bracket checker. 34 35 void bracket_check_error (char *txt, int n, char *bra, char *ket) 36 { 37 if (n != 0) { 38 BUFFER buf; 39 BUFCLR (buf); 40 ASSERT (a68g_bufprt (buf, SNPRINTF_SIZE, "\"%s\" without matching \"%s\"", (n > 0 ? bra : ket), (n > 0 ? ket : bra)) >= 0); 41 if (strlen (txt) > 0) { 42 a68g_bufcat (txt, " or ", BUFFER_SIZE); 43 } 44 a68g_bufcat (txt, buf, BUFFER_SIZE); 45 } 46 } 47 48 //! @brief Diagnose brackets in local branch of the tree. 49 50 char *bracket_check_diagnose (NODE_T * p) 51 { 52 int begins = 0, opens = 0, format_delims = 0, format_opens = 0, subs = 0, ifs = 0, cases = 0, dos = 0, accos = 0; 53 for (; p != NO_NODE; FORWARD (p)) { 54 switch (ATTRIBUTE (p)) { 55 case BEGIN_SYMBOL: { 56 begins++; 57 break; 58 } 59 case END_SYMBOL: { 60 begins--; 61 break; 62 } 63 case OPEN_SYMBOL: { 64 opens++; 65 break; 66 } 67 case CLOSE_SYMBOL: { 68 opens--; 69 break; 70 } 71 case ACCO_SYMBOL: { 72 accos++; 73 break; 74 } 75 case OCCA_SYMBOL: { 76 accos--; 77 break; 78 } 79 case FORMAT_DELIMITER_SYMBOL: { 80 if (format_delims == 0) { 81 format_delims = 1; 82 } else { 83 format_delims = 0; 84 } 85 break; 86 } 87 case FORMAT_OPEN_SYMBOL: { 88 format_opens++; 89 break; 90 } 91 case FORMAT_CLOSE_SYMBOL: { 92 format_opens--; 93 break; 94 } 95 case SUB_SYMBOL: { 96 subs++; 97 break; 98 } 99 case BUS_SYMBOL: { 100 subs--; 101 break; 102 } 103 case IF_SYMBOL: { 104 ifs++; 105 break; 106 } 107 case FI_SYMBOL: { 108 ifs--; 109 break; 110 } 111 case CASE_SYMBOL: { 112 cases++; 113 break; 114 } 115 case ESAC_SYMBOL: { 116 cases--; 117 break; 118 } 119 case DO_SYMBOL: { 120 dos++; 121 break; 122 } 123 case OD_SYMBOL: { 124 dos--; 125 break; 126 } 127 } 128 } 129 A68G (edit_line)[0] = NULL_CHAR; 130 bracket_check_error (A68G (edit_line), begins, "BEGIN", "END"); 131 bracket_check_error (A68G (edit_line), opens, "(", ")"); 132 bracket_check_error (A68G (edit_line), format_opens, "(", ")"); 133 bracket_check_error (A68G (edit_line), format_delims, "$", "$"); 134 bracket_check_error (A68G (edit_line), accos, "{", "}"); 135 bracket_check_error (A68G (edit_line), subs, "[", "]"); 136 bracket_check_error (A68G (edit_line), ifs, "IF", "FI"); 137 bracket_check_error (A68G (edit_line), cases, "CASE", "ESAC"); 138 bracket_check_error (A68G (edit_line), dos, "DO", "OD"); 139 return A68G (edit_line); 140 } 141 142 //! @brief Driver for locally diagnosing non-matching tokens. 143 144 NODE_T *bracket_check_parse (NODE_T * top, NODE_T * p) 145 { 146 for (; p != NO_NODE; FORWARD (p)) { 147 int ket = STOP; 148 NODE_T *q = NO_NODE; 149 BOOL_T ignore_token = A68G_FALSE; 150 switch (ATTRIBUTE (p)) { 151 case BEGIN_SYMBOL: { 152 ket = END_SYMBOL; 153 q = bracket_check_parse (top, NEXT (p)); 154 break; 155 } 156 case OPEN_SYMBOL: { 157 ket = CLOSE_SYMBOL; 158 q = bracket_check_parse (top, NEXT (p)); 159 break; 160 } 161 case ACCO_SYMBOL: { 162 ket = OCCA_SYMBOL; 163 q = bracket_check_parse (top, NEXT (p)); 164 break; 165 } 166 case FORMAT_OPEN_SYMBOL: { 167 ket = FORMAT_CLOSE_SYMBOL; 168 q = bracket_check_parse (top, NEXT (p)); 169 break; 170 } 171 case SUB_SYMBOL: { 172 ket = BUS_SYMBOL; 173 q = bracket_check_parse (top, NEXT (p)); 174 break; 175 } 176 case IF_SYMBOL: { 177 ket = FI_SYMBOL; 178 q = bracket_check_parse (top, NEXT (p)); 179 break; 180 } 181 case CASE_SYMBOL: { 182 ket = ESAC_SYMBOL; 183 q = bracket_check_parse (top, NEXT (p)); 184 break; 185 } 186 case DO_SYMBOL: { 187 ket = OD_SYMBOL; 188 q = bracket_check_parse (top, NEXT (p)); 189 break; 190 } 191 case END_SYMBOL: 192 case OCCA_SYMBOL: 193 case CLOSE_SYMBOL: 194 case FORMAT_CLOSE_SYMBOL: 195 case BUS_SYMBOL: 196 case FI_SYMBOL: 197 case ESAC_SYMBOL: 198 case OD_SYMBOL: { 199 return p; 200 } 201 default: { 202 ignore_token = A68G_TRUE; 203 } 204 } 205 if (ignore_token) { 206 ; 207 } else if (q != NO_NODE && IS (q, ket)) { 208 p = q; 209 } else if (q == NO_NODE) { 210 char *diag = bracket_check_diagnose (top); 211 diagnostic (A68G_SYNTAX_ERROR, p, ERROR_PARENTHESIS, (strlen (diag) > 0 ? diag : INFO_MISSING_KEYWORDS)); 212 longjmp (A68G_PARSER (top_down_crash_exit), 1); 213 } else { 214 char *diag = bracket_check_diagnose (top); 215 diagnostic (A68G_SYNTAX_ERROR, p, ERROR_PARENTHESIS_2, ATTRIBUTE (q), LINE (INFO (q)), ket, (strlen (diag) > 0 ? diag : INFO_MISSING_KEYWORDS)); 216 longjmp (A68G_PARSER (top_down_crash_exit), 1); 217 } 218 } 219 return NO_NODE; 220 } 221 222 //! @brief Driver for globally diagnosing non-matching tokens. 223 224 void check_parenthesis (NODE_T * top) 225 { 226 if (!setjmp (A68G_PARSER (top_down_crash_exit))) { 227 if (bracket_check_parse (top, top) != NO_NODE) { 228 diagnostic (A68G_SYNTAX_ERROR, top, ERROR_PARENTHESIS, INFO_MISSING_KEYWORDS); 229 } 230 } 231 }
© 2001-2026 J.M. van der Veer
jmvdveer@algol68genie.nl