parser-brackets.c

     
   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-2023 J. Marcel van der Veer [algol68g@xs4all.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 b;
  39      ASSERT (snprintf (b, SNPRINTF_SIZE, "\"%s\" without matching \"%s\"", (n > 0 ? bra : ket), (n > 0 ? ket : bra)) >= 0);
  40      if (strlen (txt) > 0) {
  41        bufcat (txt, " and ", BUFFER_SIZE);
  42      }
  43      bufcat (txt, b, BUFFER_SIZE);
  44    }
  45  }
  46  
  47  //! @brief Diagnose brackets in local branch of the tree.
  48  
  49  char *bracket_check_diagnose (NODE_T * p)
  50  {
  51    int begins = 0, opens = 0, format_delims = 0, format_opens = 0, subs = 0, ifs = 0, cases = 0, dos = 0, accos = 0;
  52    for (; p != NO_NODE; FORWARD (p)) {
  53      switch (ATTRIBUTE (p)) {
  54      case BEGIN_SYMBOL:
  55        {
  56          begins++;
  57          break;
  58        }
  59      case END_SYMBOL:
  60        {
  61          begins--;
  62          break;
  63        }
  64      case OPEN_SYMBOL:
  65        {
  66          opens++;
  67          break;
  68        }
  69      case CLOSE_SYMBOL:
  70        {
  71          opens--;
  72          break;
  73        }
  74      case ACCO_SYMBOL:
  75        {
  76          accos++;
  77          break;
  78        }
  79      case OCCA_SYMBOL:
  80        {
  81          accos--;
  82          break;
  83        }
  84      case FORMAT_DELIMITER_SYMBOL:
  85        {
  86          if (format_delims == 0) {
  87            format_delims = 1;
  88          } else {
  89            format_delims = 0;
  90          }
  91          break;
  92        }
  93      case FORMAT_OPEN_SYMBOL:
  94        {
  95          format_opens++;
  96          break;
  97        }
  98      case FORMAT_CLOSE_SYMBOL:
  99        {
 100          format_opens--;
 101          break;
 102        }
 103      case SUB_SYMBOL:
 104        {
 105          subs++;
 106          break;
 107        }
 108      case BUS_SYMBOL:
 109        {
 110          subs--;
 111          break;
 112        }
 113      case IF_SYMBOL:
 114        {
 115          ifs++;
 116          break;
 117        }
 118      case FI_SYMBOL:
 119        {
 120          ifs--;
 121          break;
 122        }
 123      case CASE_SYMBOL:
 124        {
 125          cases++;
 126          break;
 127        }
 128      case ESAC_SYMBOL:
 129        {
 130          cases--;
 131          break;
 132        }
 133      case DO_SYMBOL:
 134        {
 135          dos++;
 136          break;
 137        }
 138      case OD_SYMBOL:
 139        {
 140          dos--;
 141          break;
 142        }
 143      }
 144    }
 145    A68 (edit_line)[0] = NULL_CHAR;
 146    bracket_check_error (A68 (edit_line), begins, "BEGIN", "END");
 147    bracket_check_error (A68 (edit_line), opens, "(", ")");
 148    bracket_check_error (A68 (edit_line), format_opens, "(", ")");
 149    bracket_check_error (A68 (edit_line), format_delims, "$", "$");
 150    bracket_check_error (A68 (edit_line), accos, "{", "}");
 151    bracket_check_error (A68 (edit_line), subs, "[", "]");
 152    bracket_check_error (A68 (edit_line), ifs, "IF", "FI");
 153    bracket_check_error (A68 (edit_line), cases, "CASE", "ESAC");
 154    bracket_check_error (A68 (edit_line), dos, "DO", "OD");
 155    return A68 (edit_line);
 156  }
 157  
 158  //! @brief Driver for locally diagnosing non-matching tokens.
 159  
 160  NODE_T *bracket_check_parse (NODE_T * top, NODE_T * p)
 161  {
 162    BOOL_T ignore_token;
 163    for (; p != NO_NODE; FORWARD (p)) {
 164      int ket = STOP;
 165      NODE_T *q = NO_NODE;
 166      ignore_token = A68_FALSE;
 167      switch (ATTRIBUTE (p)) {
 168      case BEGIN_SYMBOL:
 169        {
 170          ket = END_SYMBOL;
 171          q = bracket_check_parse (top, NEXT (p));
 172          break;
 173        }
 174      case OPEN_SYMBOL:
 175        {
 176          ket = CLOSE_SYMBOL;
 177          q = bracket_check_parse (top, NEXT (p));
 178          break;
 179        }
 180      case ACCO_SYMBOL:
 181        {
 182          ket = OCCA_SYMBOL;
 183          q = bracket_check_parse (top, NEXT (p));
 184          break;
 185        }
 186      case FORMAT_OPEN_SYMBOL:
 187        {
 188          ket = FORMAT_CLOSE_SYMBOL;
 189          q = bracket_check_parse (top, NEXT (p));
 190          break;
 191        }
 192      case SUB_SYMBOL:
 193        {
 194          ket = BUS_SYMBOL;
 195          q = bracket_check_parse (top, NEXT (p));
 196          break;
 197        }
 198      case IF_SYMBOL:
 199        {
 200          ket = FI_SYMBOL;
 201          q = bracket_check_parse (top, NEXT (p));
 202          break;
 203        }
 204      case CASE_SYMBOL:
 205        {
 206          ket = ESAC_SYMBOL;
 207          q = bracket_check_parse (top, NEXT (p));
 208          break;
 209        }
 210      case DO_SYMBOL:
 211        {
 212          ket = OD_SYMBOL;
 213          q = bracket_check_parse (top, NEXT (p));
 214          break;
 215        }
 216      case END_SYMBOL:
 217      case OCCA_SYMBOL:
 218      case CLOSE_SYMBOL:
 219      case FORMAT_CLOSE_SYMBOL:
 220      case BUS_SYMBOL:
 221      case FI_SYMBOL:
 222      case ESAC_SYMBOL:
 223      case OD_SYMBOL:
 224        {
 225          return p;
 226        }
 227      default:
 228        {
 229          ignore_token = A68_TRUE;
 230        }
 231      }
 232      if (ignore_token) {
 233        ;
 234      } else if (q != NO_NODE && IS (q, ket)) {
 235        p = q;
 236      } else if (q == NO_NODE) {
 237        char *diag = bracket_check_diagnose (top);
 238        diagnostic (A68_SYNTAX_ERROR, p, ERROR_PARENTHESIS, (strlen (diag) > 0 ? diag : INFO_MISSING_KEYWORDS));
 239        longjmp (A68_PARSER (top_down_crash_exit), 1);
 240      } else {
 241        char *diag = bracket_check_diagnose (top);
 242        diagnostic (A68_SYNTAX_ERROR, p, ERROR_PARENTHESIS_2, ATTRIBUTE (q), LINE (INFO (q)), ket, (strlen (diag) > 0 ? diag : INFO_MISSING_KEYWORDS));
 243        longjmp (A68_PARSER (top_down_crash_exit), 1);
 244      }
 245    }
 246    return NO_NODE;
 247  }
 248  
 249  //! @brief Driver for globally diagnosing non-matching tokens.
 250  
 251  void check_parenthesis (NODE_T * top)
 252  {
 253    if (!setjmp (A68_PARSER (top_down_crash_exit))) {
 254      if (bracket_check_parse (top, top) != NO_NODE) {
 255        diagnostic (A68_SYNTAX_ERROR, top, ERROR_PARENTHESIS, INFO_MISSING_KEYWORDS);
 256      }
 257    }
 258  }