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-2024 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 buf;
  39      BUFCLR (buf);
  40      ASSERT (a68_bufprt (buf, SNPRINTF_SIZE, "\"%s\" without matching \"%s\"", (n > 0 ? bra : ket), (n > 0 ? ket : bra)) >= 0);
  41      if (strlen (txt) > 0) {
  42        a68_bufcat (txt, " or ", BUFFER_SIZE);
  43      }
  44      a68_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    A68 (edit_line)[0] = NULL_CHAR;
 130    bracket_check_error (A68 (edit_line), begins, "BEGIN", "END");
 131    bracket_check_error (A68 (edit_line), opens, "(", ")");
 132    bracket_check_error (A68 (edit_line), format_opens, "(", ")");
 133    bracket_check_error (A68 (edit_line), format_delims, "$", "$");
 134    bracket_check_error (A68 (edit_line), accos, "{", "}");
 135    bracket_check_error (A68 (edit_line), subs, "[", "]");
 136    bracket_check_error (A68 (edit_line), ifs, "IF", "FI");
 137    bracket_check_error (A68 (edit_line), cases, "CASE", "ESAC");
 138    bracket_check_error (A68 (edit_line), dos, "DO", "OD");
 139    return A68 (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 = A68_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 = A68_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 (A68_SYNTAX_ERROR, p, ERROR_PARENTHESIS, (strlen (diag) > 0 ? diag : INFO_MISSING_KEYWORDS));
 212        longjmp (A68_PARSER (top_down_crash_exit), 1);
 213      } else {
 214        char *diag = bracket_check_diagnose (top);
 215        diagnostic (A68_SYNTAX_ERROR, p, ERROR_PARENTHESIS_2, ATTRIBUTE (q), LINE (INFO (q)), ket, (strlen (diag) > 0 ? diag : INFO_MISSING_KEYWORDS));
 216        longjmp (A68_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 (A68_PARSER (top_down_crash_exit))) {
 227      if (bracket_check_parse (top, top) != NO_NODE) {
 228        diagnostic (A68_SYNTAX_ERROR, top, ERROR_PARENTHESIS, INFO_MISSING_KEYWORDS);
 229      }
 230    }
 231  }