a68g-diagnostics.c

     
   1  //! @file a68g-diagnostics.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-2025 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  //! Error and warning routines.
  25  
  26  #include "a68g.h"
  27  #include "a68g-prelude.h"
  28  #include "a68g-transput.h"
  29  #include "a68g-parser.h"
  30  
  31  // Error handling routines.
  32  
  33  #define TABULATE(n) (8 * (n / 8 + 1) - n)
  34  
  35  //! @brief Return error test from errno.
  36  
  37  char *error_specification (void)
  38  {
  39    static BUFFER txt;
  40    if (errno == 0) {
  41      ASSERT (a68g_bufprt (txt, SNPRINTF_SIZE, "no information") >= 0);
  42    } else {
  43      ASSERT (a68g_bufprt (txt, SNPRINTF_SIZE, "%s", strerror (errno)) >= 0);
  44    }
  45    if (strlen (txt) > 0) {
  46      txt[0] = TO_LOWER (txt[0]);
  47    }
  48    return txt;
  49  }
  50  
  51  //! @brief Whether unprintable control character.
  52  
  53  BOOL_T unprintable (char ch)
  54  {
  55    return (BOOL_T) (!IS_PRINT (ch) && ch != TAB_CHAR);
  56  }
  57  
  58  //! @brief Format for printing control character.
  59  
  60  char *ctrl_char (int ch)
  61  {
  62    static char txt[SMALL_BUFFER_SIZE];
  63    ch = TO_UCHAR (ch);
  64    if (IS_CNTRL (ch) && IS_LOWER (ch + 96)) {
  65      ASSERT (a68g_bufprt (txt, (size_t) SMALL_BUFFER_SIZE, "\\^%c", ch + 96) >= 0);
  66    } else {
  67      ASSERT (a68g_bufprt (txt, (size_t) SMALL_BUFFER_SIZE, "\\%02x", (unt) ch) >= 0);
  68    }
  69    return txt;
  70  }
  71  
  72  //! @brief Widen single char to string.
  73  
  74  char *char_to_str (char ch)
  75  {
  76    static char txt[2];
  77    txt[0] = ch;
  78    txt[1] = NULL_CHAR;
  79    return txt;
  80  }
  81  
  82  //! @brief Pretty-print diagnostic.
  83  
  84  void pretty_diag (FILE_T f, char *p)
  85  {
  86    int line_width = (f == A68G_STDERR || f == A68G_STDOUT ? A68G (term_width) : MAX_TERM_WIDTH);
  87    int pos = 1;
  88    while (p[0] != NULL_CHAR) {
  89      char *q;
  90      int k;
  91  // Count the number of characters in token to print.
  92      if (IS_GRAPH (p[0])) {
  93        for (k = 0, q = p; q[0] != BLANK_CHAR && q[0] != NULL_CHAR && k <= line_width; q++, k++) {
  94          ;
  95        }
  96      } else {
  97        k = 1;
  98      }
  99  // Now see if there is space for the token.
 100      if (k > line_width) {
 101        k = 1;
 102      }
 103      if ((pos + k) >= line_width) {
 104        WRITE (f, NEWLINE_STRING);
 105        pos = 1;
 106      }
 107      for (; k > 0; k--, p++, pos++) {
 108        WRITE (f, char_to_str (p[0]));
 109      }
 110    }
 111    for (; p[0] == BLANK_CHAR; p++, pos++) {
 112      WRITE (f, char_to_str (p[0]));
 113    }
 114  }
 115  
 116  //! @brief Abnormal end.
 117  
 118  void abend (char *reason, char *info, char *file, int line)
 119  {
 120  #if defined (BUILD_WIN32) || defined (BUILD_WIN64)
 121    printf_s ("%s: abend: %s.\n", A68G (a68g_cmd_name), reason);
 122    exit (EXIT_FAILURE);
 123  #else
 124    ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "%s: exiting: %s (%s: %d", A68G (a68g_cmd_name), reason, basename (file), line) >= 0);
 125    if (info != NO_TEXT) {
 126      a68g_bufcat (A68G (output_line), ", ", BUFFER_SIZE);
 127      a68g_bufcat (A68G (output_line), info, BUFFER_SIZE);
 128    }
 129    if (errno != 0) {
 130      a68g_bufcat (A68G (output_line), ", ", BUFFER_SIZE);
 131      a68g_bufcat (A68G (output_line), error_specification (), BUFFER_SIZE);
 132    }
 133    a68g_bufcat (A68G (output_line), ")\n", BUFFER_SIZE);
 134    io_close_tty_line ();
 135    pretty_diag (A68G_STDERR, A68G (output_line));
 136    a68g_exit (EXIT_FAILURE);
 137  #endif
 138  }
 139  
 140  //! @brief Position in line .
 141  
 142  char *where_pos (LINE_T * p, NODE_T * q)
 143  {
 144    char *pos;
 145    if (q != NO_NODE && p == LINE (INFO (q))) {
 146      pos = CHAR_IN_LINE (INFO (q));
 147    } else {
 148      pos = STRING (p);
 149    }
 150    if (pos == NO_TEXT) {
 151      pos = STRING (p);
 152    }
 153    for (; IS_SPACE (pos[0]) && pos[0] != NULL_CHAR; pos++) {
 154      ;
 155    }
 156    if (pos[0] == NULL_CHAR) {
 157      pos = STRING (p);
 158    }
 159    return pos;
 160  }
 161  
 162  //! @brief Position in line where diagnostic points at.
 163  
 164  char *diag_pos (LINE_T * p, DIAGNOSTIC_T * d)
 165  {
 166    char *pos;
 167    if (WHERE (d) != NO_NODE && p == LINE (INFO (WHERE (d)))) {
 168      pos = CHAR_IN_LINE (INFO (WHERE (d)));
 169    } else {
 170      pos = STRING (p);
 171    }
 172    if (pos == NO_TEXT) {
 173      pos = STRING (p);
 174    }
 175    for (; IS_SPACE (pos[0]) && pos[0] != NULL_CHAR; pos++) {
 176      ;
 177    }
 178    if (pos[0] == NULL_CHAR) {
 179      pos = STRING (p);
 180    }
 181    return pos;
 182  }
 183  
 184  //! @brief Write source line to file with diagnostics.
 185  
 186  void write_source_line (FILE_T f, LINE_T * p, NODE_T * nwhere, int mask)
 187  {
 188    int line_width = (f == A68G_STDERR || f == A68G_STDOUT ? A68G (term_width) : MAX_TERM_WIDTH);
 189  // Terminate properly.
 190    size_t len = strlen (STRING (p));
 191    if (len > 0 && (STRING (p))[len - 1] == NEWLINE_CHAR) {
 192      (STRING (p))[len - 1] = NULL_CHAR;
 193    }
 194    len = strlen (STRING (p));
 195    if (len > 0 && (STRING (p))[len - 1] == CR_CHAR) {
 196      (STRING (p))[len - 1] = NULL_CHAR;
 197    }
 198  // Print line number.
 199    if (f == A68G_STDERR || f == A68G_STDOUT) {
 200      io_close_tty_line ();
 201    } else {
 202      WRITE (f, NEWLINE_STRING);
 203    }
 204    if (NUMBER (p) == 0) {
 205      ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "      ") >= 0);
 206    } else {
 207      ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "%-5d ", NUMBER (p) % 100000) >= 0);
 208    }
 209    WRITE (f, A68G (output_line));
 210  // Pretty print line.
 211    char *c, *c0;
 212    c = c0 = STRING (p);
 213    int pos = 5, col = 1;
 214    int continuations = 0;
 215    BOOL_T line_ended = A68G_FALSE;
 216    while (!line_ended) {
 217      len = 0;
 218      char *new_pos = NO_TEXT;
 219      if (c[0] == NULL_CHAR) {
 220        a68g_bufcpy (A68G (output_line), "", BUFFER_SIZE);
 221        line_ended = A68G_TRUE;
 222      } else {
 223        if (IS_GRAPH (c[0])) {
 224          char *c1;
 225          a68g_bufcpy (A68G (output_line), "", BUFFER_SIZE);
 226          for (c1 = c; IS_GRAPH (c1[0]) && len <= line_width - 5; c1++, len++) {
 227            a68g_bufcat (A68G (output_line), char_to_str (c1[0]), BUFFER_SIZE);
 228          }
 229          if (len > line_width - 5) {
 230            a68g_bufcpy (A68G (output_line), char_to_str (c[0]), BUFFER_SIZE);
 231            len = 1;
 232          }
 233          new_pos = &c[len];
 234          col += len;
 235        } else if (c[0] == TAB_CHAR) {
 236          int n = TABULATE (col);
 237          len = n;
 238          col += n;
 239          a68g_bufcpy (A68G (output_line), "", BUFFER_SIZE);
 240          while (n--) {
 241            a68g_bufcat (A68G (output_line), " ", BUFFER_SIZE);
 242          }
 243          new_pos = &c[1];
 244        } else if (unprintable (c[0])) {
 245          a68g_bufcpy (A68G (output_line), ctrl_char ((int) c[0]), BUFFER_SIZE);
 246          len = strlen (A68G (output_line));
 247          new_pos = &c[1];
 248          col++;
 249        } else {
 250          a68g_bufcpy (A68G (output_line), char_to_str (c[0]), BUFFER_SIZE);
 251          len = 1;
 252          new_pos = &c[1];
 253          col++;
 254        }
 255      }
 256      if (!line_ended && (pos + len) <= line_width) {
 257  // Still room - print a character.
 258        WRITE (f, A68G (output_line));
 259        pos += len;
 260        c = new_pos;
 261      } else {
 262  // First see if there are diagnostics to be printed.
 263        BOOL_T y = A68G_FALSE, z = A68G_FALSE;
 264        DIAGNOSTIC_T *d = DIAGNOSTICS (p);
 265        if (d != NO_DIAGNOSTIC || nwhere != NO_NODE) {
 266          char *c1;
 267          for (c1 = c0; c1 != c; c1++) {
 268            y |= (BOOL_T) (nwhere != NO_NODE && p == LINE (INFO (nwhere)) ? c1 == where_pos (p, nwhere) : A68G_FALSE);
 269            if (mask != A68G_NO_DIAGNOSTICS) {
 270              for (d = DIAGNOSTICS (p); d != NO_DIAGNOSTIC; FORWARD (d)) {
 271                z = (BOOL_T) (z | (c1 == diag_pos (p, d)));
 272              }
 273            }
 274          }
 275        }
 276  // If diagnostics are to be printed then print marks.
 277        if (y || z) {
 278          DIAGNOSTIC_T *d2;
 279          char *c1;
 280          int col_2 = 1;
 281          WRITE (f, "\n      ");
 282          for (c1 = c0; c1 != c; c1++) {
 283            int k = 0, diags_at_this_pos = 0;
 284            for (d2 = DIAGNOSTICS (p); d2 != NO_DIAGNOSTIC; FORWARD (d2)) {
 285              if (c1 == diag_pos (p, d2)) {
 286                diags_at_this_pos++;
 287                k = NUMBER (d2);
 288              }
 289            }
 290            if (y == A68G_TRUE && c1 == where_pos (p, nwhere)) {
 291              a68g_bufcpy (A68G (output_line), "-", BUFFER_SIZE);
 292            } else if (diags_at_this_pos != 0) {
 293              if (mask == A68G_NO_DIAGNOSTICS) {
 294                a68g_bufcpy (A68G (output_line), " ", BUFFER_SIZE);
 295              } else if (diags_at_this_pos == 1) {
 296                ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "%c", digchar (k)) >= 0);
 297              } else {
 298                a68g_bufcpy (A68G (output_line), "*", BUFFER_SIZE);
 299              }
 300            } else {
 301              if (unprintable (c1[0])) {
 302                size_t n = strlen (ctrl_char (c1[0]));
 303                col_2 += 1;
 304                a68g_bufcpy (A68G (output_line), "", BUFFER_SIZE);
 305                while (n--) {
 306                  a68g_bufcat (A68G (output_line), " ", BUFFER_SIZE);
 307                }
 308              } else if (c1[0] == TAB_CHAR) {
 309                int n = TABULATE (col_2);
 310                col_2 += n;
 311                a68g_bufcpy (A68G (output_line), "", BUFFER_SIZE);
 312                while (n--) {
 313                  a68g_bufcat (A68G (output_line), " ", BUFFER_SIZE);
 314                }
 315              } else {
 316                a68g_bufcpy (A68G (output_line), " ", BUFFER_SIZE);
 317                col_2++;
 318              }
 319            }
 320            WRITE (f, A68G (output_line));
 321          }
 322        }
 323  // Resume pretty printing of line.
 324        if (!line_ended) {
 325          continuations++;
 326          ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "\n.%1d   ", continuations) >= 0);
 327          WRITE (f, A68G (output_line));
 328          if (continuations >= 9) {
 329            WRITE (f, "...");
 330            line_ended = A68G_TRUE;
 331          } else {
 332            c0 = c;
 333            pos = 5;
 334            col = 1;
 335          }
 336        }
 337      }
 338    }
 339  // Print the diagnostics.
 340    if (mask) {
 341      if (DIAGNOSTICS (p) != NO_DIAGNOSTIC) {
 342        DIAGNOSTIC_T *d;
 343        for (d = DIAGNOSTICS (p); d != NO_DIAGNOSTIC; FORWARD (d)) {
 344          if (mask == A68G_RUNTIME_ERROR) {
 345            if (IS (d, A68G_RUNTIME_ERROR) || IS (d, A68G_MATH_ERROR) || (IS (d, A68G_MATH_WARNING))) {
 346              WRITE (f, NEWLINE_STRING);
 347              pretty_diag (f, TEXT (d));
 348            }
 349          } else {
 350            WRITE (f, NEWLINE_STRING);
 351            pretty_diag (f, TEXT (d));
 352          }
 353        }
 354      }
 355    }
 356  }
 357  
 358  //! @brief Write diagnostics to STDERR.
 359  
 360  void diagnostics_to_terminal (LINE_T * p, int sev)
 361  {
 362    for (; p != NO_LINE; FORWARD (p)) {
 363      if (DIAGNOSTICS (p) != NO_DIAGNOSTIC) {
 364        int N = 0;
 365        for (DIAGNOSTIC_T *d = DIAGNOSTICS (p); d != NO_DIAGNOSTIC; FORWARD (d)) {
 366          if (sev == A68G_ALL_DIAGNOSTICS) {
 367            if (
 368              IS (d, A68G_ERROR)         || 
 369              IS (d, A68G_NOTICE)        || 
 370              IS (d, A68G_SCANNER_ERROR) ||
 371              IS (d, A68G_SYNTAX_ERROR)  || 
 372              IS (d, A68G_WARNING)
 373            ) {
 374              N++;
 375            }
 376          } 
 377          if (sev == A68G_ALL_DIAGNOSTICS || sev == A68G_RUNTIME_ERROR) {
 378            if (
 379              IS (d, A68G_MATH_ERROR)   ||
 380              IS (d, A68G_MATH_WARNING) ||
 381              IS (d, A68G_RUNTIME_ERROR)
 382            ) {
 383              N++;
 384            }
 385          }
 386        }
 387        if (N > 0) {
 388          write_source_line (A68G_STDERR, p, NO_NODE, sev);
 389        }
 390      }
 391    }
 392  }
 393  
 394  //! @brief Give an intelligible error and exit.
 395  
 396  void scan_error (LINE_T * u, char *v, char *txt)
 397  {
 398    if (errno != 0) {
 399      diagnostic (A68G_SCANNER_ERROR, NO_NODE, txt, u, v, error_specification ());
 400    } else {
 401      diagnostic (A68G_SCANNER_ERROR, NO_NODE, txt, u, v, ERROR_UNSPECIFIED);
 402    }
 403    longjmp (RENDEZ_VOUS (&A68G_JOB), 1);
 404  }
 405  
 406  void scan_error_info (LINE_T * u, char *v, char *txt, char *info)
 407  {
 408    if (info != NO_TEXT) {
 409      size_t len = strlen (txt) + 3;
 410      char *txti = a68g_alloc(len, __FILE__, __LINE__);
 411      ABEND (txti == NO_TEXT, ERROR_MEMORY_FULL, __func__);
 412      a68g_bufcpy (txti, txt, len);
 413      a68g_bufcat (txti, " Z", len);
 414      if (errno != 0) {
 415        diagnostic (A68G_SCANNER_ERROR, NO_NODE, txti, u, v, info, error_specification ());
 416      } else {
 417        diagnostic (A68G_SCANNER_ERROR, NO_NODE, txti, u, v, info, ERROR_UNSPECIFIED);
 418      }
 419      longjmp (RENDEZ_VOUS (&A68G_JOB), 1);
 420    } else {
 421      scan_error(u, v, txt);
 422    }
 423  }
 424  
 425  //! @brief Give an intelligible warning.
 426  
 427  void scan_warning (LINE_T * u, char *v, char *txt)
 428  {
 429    if (errno != 0) {
 430      diagnostic (A68G_SCANNER_WARNING, NO_NODE, txt, u, v, error_specification ());
 431    } else {
 432      diagnostic (A68G_SCANNER_WARNING, NO_NODE, txt, u, v, ERROR_UNSPECIFIED);
 433    }
 434  }
 435  
 436  //! @brief Get severity text.
 437  
 438  char *get_severity (int sev)
 439  {
 440    switch (sev) {
 441    case A68G_ERROR: {
 442        return "error";
 443      }
 444    case A68G_SYNTAX_ERROR: {
 445        return "syntax error";
 446      }
 447    case A68G_RUNTIME_ERROR: {
 448        return "runtime error";
 449      }
 450    case A68G_MATH_ERROR: {
 451        return "math error";
 452      }
 453    case A68G_MATH_WARNING: {
 454        return "math warning";
 455      }
 456    case A68G_NOTICE: {
 457        return "notice";
 458      }
 459    case A68G_WARNING: {
 460        return "warning";
 461      }
 462    case A68G_SCANNER_ERROR: {
 463        return "scanner error";
 464      }
 465    case A68G_SCANNER_WARNING: {
 466        return "scanner warning";
 467      }
 468    default: {
 469        return NO_TEXT;
 470      }
 471    }
 472  }
 473  
 474  //! @brief Print diagnostic.
 475  
 476  void write_diagnostic (int sev, char *b)
 477  {
 478    char txt[SMALL_BUFFER_SIZE];
 479    char *severity = get_severity (sev);
 480    if (severity == NO_TEXT) {
 481      ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "%s: %s.", A68G (a68g_cmd_name), b) >= 0);
 482    } else {
 483      a68g_bufcpy (txt, get_severity (sev), SMALL_BUFFER_SIZE);
 484      ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "%s: %s: %s.", A68G (a68g_cmd_name), txt, b) >= 0);
 485    }
 486    io_close_tty_line ();
 487    pretty_diag (A68G_STDERR, A68G (output_line));
 488  }
 489  
 490  //! @brief Add diagnostic to source line.
 491  
 492  void add_diagnostic (LINE_T * line, char *pos, NODE_T * p, int sev, char *b)
 493  {
 494  // Add diagnostic and choose GNU style or non-GNU style.
 495    DIAGNOSTIC_T *msg = (DIAGNOSTIC_T *) get_heap_space (SIZE_ALIGNED (DIAGNOSTIC_T));
 496    DIAGNOSTIC_T **ref_msg;
 497    BUFFER a, nst;
 498    char st[SMALL_BUFFER_SIZE];
 499    char *severity = get_severity (sev);
 500    int k = 1;
 501    if (line == NO_LINE && p == NO_NODE) {
 502      return;
 503    }
 504    if (A68G (in_monitor)) {
 505      monitor_error (b, NO_TEXT);
 506      return;
 507    }
 508    nst[0] = NULL_CHAR;
 509    if (line == NO_LINE && p != NO_NODE) {
 510      line = LINE (INFO (p));
 511    }
 512    while (line != NO_LINE && NUMBER (line) == 0) {
 513      FORWARD (line);
 514    }
 515    if (line == NO_LINE) {
 516      return;
 517    }
 518    ref_msg = &(DIAGNOSTICS (line));
 519    while (*ref_msg != NO_DIAGNOSTIC) {
 520      ref_msg = &(NEXT (*ref_msg));
 521      k++;
 522    }
 523    if (p != NO_NODE) {
 524      NODE_T *n = NEST (p);
 525      if (n != NO_NODE && NSYMBOL (n) != NO_TEXT) {
 526        char *nt = non_terminal_string (A68G (edit_line), ATTRIBUTE (n));
 527        if (nt != NO_TEXT) {
 528          if (LINE_NUMBER (n) == 0) {
 529            ASSERT (a68g_bufprt (nst, SNPRINTF_SIZE, ", in %s", nt) >= 0);
 530          } else {
 531            if (MOID (n) != NO_MOID) {
 532              if (LINE_NUMBER (n) == NUMBER (line)) {
 533                ASSERT (a68g_bufprt (nst, SNPRINTF_SIZE, ", in %s %s starting at \"%.64s\" in this line", moid_to_string (MOID (n), MOID_ERROR_WIDTH, p), nt, NSYMBOL (n)) >= 0);
 534              } else {
 535                ASSERT (a68g_bufprt (nst, SNPRINTF_SIZE, ", in %s %s starting at \"%.64s\" in line %d", moid_to_string (MOID (n), MOID_ERROR_WIDTH, p), nt, NSYMBOL (n), LINE_NUMBER (n)) >= 0);
 536              }
 537            } else {
 538              if (LINE_NUMBER (n) == NUMBER (line)) {
 539                ASSERT (a68g_bufprt (nst, SNPRINTF_SIZE, ", in %s starting at \"%.64s\" in this line", nt, NSYMBOL (n)) >= 0);
 540              } else {
 541                ASSERT (a68g_bufprt (nst, SNPRINTF_SIZE, ", in %s starting at \"%.64s\" in line %d", nt, NSYMBOL (n), LINE_NUMBER (n)) >= 0);
 542              }
 543            }
 544          }
 545        }
 546      }
 547    }
 548    if (severity == NO_TEXT) {
 549      if (FILENAME (line) != NO_TEXT && strcmp (FILE_SOURCE_NAME (&A68G_JOB), FILENAME (line)) == 0) {
 550        ASSERT (a68g_bufprt (a, SNPRINTF_SIZE, "%s: %x: %s", A68G (a68g_cmd_name), (unt) k, b) >= 0);
 551      } else if (FILENAME (line) != NO_TEXT) {
 552        ASSERT (a68g_bufprt (a, SNPRINTF_SIZE, "%s: %s: %x: %s", A68G (a68g_cmd_name), FILENAME (line), (unt) k, b) >= 0);
 553      } else {
 554        ASSERT (a68g_bufprt (a, SNPRINTF_SIZE, "%s: %x: %s", A68G (a68g_cmd_name), (unt) k, b) >= 0);
 555      }
 556    } else {
 557      a68g_bufcpy (st, get_severity (sev), SMALL_BUFFER_SIZE);
 558      if (FILENAME (line) != NO_TEXT && strcmp (FILE_SOURCE_NAME (&A68G_JOB), FILENAME (line)) == 0) {
 559        ASSERT (a68g_bufprt (a, SNPRINTF_SIZE, "%s: %s: %x: %s", A68G (a68g_cmd_name), st, (unt) k, b) >= 0);
 560      } else if (FILENAME (line) != NO_TEXT) {
 561        ASSERT (a68g_bufprt (a, SNPRINTF_SIZE, "%s: %s: %s: %x: %s", A68G (a68g_cmd_name), FILENAME (line), st, (unt) k, b) >= 0);
 562      } else {
 563        ASSERT (a68g_bufprt (a, SNPRINTF_SIZE, "%s: %s: %x: %s", A68G (a68g_cmd_name), st, (unt) k, b) >= 0);
 564      }
 565    }
 566  // cppcheck might complain here but this memory is not returned, for obvious reasons.
 567    *ref_msg = msg;
 568    ATTRIBUTE (msg) = sev;
 569    if (nst[0] != NULL_CHAR) {
 570      a68g_bufcat (a, nst, BUFFER_SIZE);
 571    }
 572    a68g_bufcat (a, ".", BUFFER_SIZE);
 573    TEXT (msg) = new_string (a, NO_TEXT);
 574    WHERE (msg) = p;
 575    LINE (msg) = line;
 576    SYMBOL (msg) = pos;
 577    NUMBER (msg) = k;
 578    NEXT (msg) = NO_DIAGNOSTIC;
 579  }
 580  
 581  //! @brief Give a diagnostic message.
 582  
 583  void diagnostic (STATUS_MASK_T sev, NODE_T * p, char *loc_str, ...)
 584  {
 585    va_list args;
 586    MOID_T *moid = NO_MOID;
 587    char *t = loc_str, b[BUFFER_SIZE];
 588    BOOL_T compose = A68G_TRUE, issue = A68G_TRUE;
 589    va_start (args, loc_str);
 590    b[0] = NULL_CHAR;
 591  // Node or line?
 592    LINE_T *line = NO_LINE;
 593    char *pos = NO_TEXT;
 594    if (p == NO_NODE) {
 595      line = va_arg (args, LINE_T *);
 596      pos = va_arg (args, char *);
 597    }
 598  // No warnings or notices?
 599    if (sev == A68G_NOTICE && OPTION_NO_NOTICES (&A68G_JOB)) {
 600      va_end (args);
 601      return;
 602    }
 603    if (sev == A68G_WARNING && OPTION_NO_WARNINGS (&A68G_JOB)) {
 604      va_end (args);
 605      return;
 606    }
 607    if (sev == A68G_MATH_WARNING && OPTION_NO_WARNINGS (&A68G_JOB)) {
 608      va_end (args);
 609      return;
 610    }
 611    if (sev == A68G_NOTICE && OPTION_QUIET (&A68G_JOB)) {
 612      va_end (args);
 613      return;
 614    }
 615    if (sev == A68G_WARNING && OPTION_QUIET (&A68G_JOB)) {
 616      va_end (args);
 617      return;
 618    }
 619    if (sev == A68G_MATH_WARNING && OPTION_QUIET (&A68G_JOB)) {
 620      va_end (args);
 621      return;
 622    }
 623  // Suppressed?.
 624    if (sev == A68G_ERROR || sev == A68G_SYNTAX_ERROR) {
 625      if (ERROR_COUNT (&A68G_JOB) == MAX_ERRORS) {
 626        a68g_bufcpy (b, "further diagnostics suppressed", BUFFER_SIZE);
 627        compose = A68G_FALSE;
 628        sev = A68G_ERROR;
 629      } else if (ERROR_COUNT (&A68G_JOB) > MAX_ERRORS) {
 630        ERROR_COUNT (&A68G_JOB)++;
 631        compose = issue = A68G_FALSE;
 632      }
 633    } else if (sev == A68G_NOTICE || sev == A68G_WARNING || sev == A68G_MATH_WARNING) {
 634      if (WARNING_COUNT (&A68G_JOB) == MAX_ERRORS) {
 635        a68g_bufcpy (b, "further diagnostics suppressed", BUFFER_SIZE);
 636        compose = A68G_FALSE;
 637      } else if (WARNING_COUNT (&A68G_JOB) > MAX_ERRORS) {
 638        WARNING_COUNT (&A68G_JOB)++;
 639        compose = issue = A68G_FALSE;
 640      }
 641    }
 642    if (compose) {
 643  // Synthesize diagnostic message.
 644      if ((sev & A68G_NO_SYNTHESIS) != NULL_MASK) {
 645        sev &= ~A68G_NO_SYNTHESIS;
 646        a68g_bufcat (b, t, BUFFER_SIZE);
 647      } else {
 648  // Legend for special symbols:
 649  // * as first character, copy rest of string literally
 650  // # skip extra syntactical information
 651  // @ non terminal
 652  // A non terminal
 653  // B keyword
 654  // C context
 655  // D argument in decimal
 656  // H char argument
 657  // K 'LONG'
 658  // L line number
 659  // M moid - if error mode return without giving a message
 660  // N mode - M_NIL
 661  // O moid - operand
 662  // S quoted symbol, when possible with typographical display features
 663  // X expected attribute
 664  // Y string literal. 
 665  // Z quoted string literal. 
 666        if (t[0] == '*') {
 667          a68g_bufcat (b, &t[1], BUFFER_SIZE);
 668        } else
 669          while (t[0] != NULL_CHAR) {
 670            if (t[0] == '#') {
 671              ;
 672            } else if (t[0] == '@') {
 673              if (p == NO_NODE) {
 674                a68g_bufcat (b, "construct", BUFFER_SIZE);
 675              } else {
 676                char *nt = non_terminal_string (A68G (edit_line), ATTRIBUTE (p));
 677                if (t != NO_TEXT) {
 678                  a68g_bufcat (b, nt, BUFFER_SIZE);
 679                } else {
 680                  a68g_bufcat (b, "construct", BUFFER_SIZE);
 681                }
 682              }
 683            } else if (t[0] == 'A') {
 684              int att = va_arg (args, int);
 685              char *nt = non_terminal_string (A68G (edit_line), att);
 686              if (nt != NO_TEXT) {
 687                a68g_bufcat (b, nt, BUFFER_SIZE);
 688              } else {
 689                a68g_bufcat (b, "construct", BUFFER_SIZE);
 690              }
 691            } else if (t[0] == 'B') {
 692              int att = va_arg (args, int);
 693              KEYWORD_T *nt = find_keyword_from_attribute (A68G (top_keyword), att);
 694              if (nt != NO_KEYWORD) {
 695                a68g_bufcat (b, "\"", BUFFER_SIZE);
 696                a68g_bufcat (b, TEXT (nt), BUFFER_SIZE);
 697                a68g_bufcat (b, "\"", BUFFER_SIZE);
 698              } else {
 699                a68g_bufcat (b, "keyword", BUFFER_SIZE);
 700              }
 701            } else if (t[0] == 'C') {
 702              int att = va_arg (args, int);
 703              if (att == NO_SORT) {
 704                a68g_bufcat (b, "this", BUFFER_SIZE);
 705              }
 706              if (att == SOFT) {
 707                a68g_bufcat (b, "a soft", BUFFER_SIZE);
 708              } else if (att == WEAK) {
 709                a68g_bufcat (b, "a weak", BUFFER_SIZE);
 710              } else if (att == MEEK) {
 711                a68g_bufcat (b, "a meek", BUFFER_SIZE);
 712              } else if (att == FIRM) {
 713                a68g_bufcat (b, "a firm", BUFFER_SIZE);
 714              } else if (att == STRONG) {
 715                a68g_bufcat (b, "a strong", BUFFER_SIZE);
 716              }
 717            } else if (t[0] == 'D') {
 718              int a = va_arg (args, int);
 719              BUFFER d;
 720              BUFCLR (d);
 721              ASSERT (a68g_bufprt (d, SNPRINTF_SIZE, "%d", a) >= 0);
 722              a68g_bufcat (b, d, BUFFER_SIZE);
 723            } else if (t[0] == 'H') {
 724              char *a = va_arg (args, char *);
 725              char d[SMALL_BUFFER_SIZE];
 726              ASSERT (a68g_bufprt (d, (size_t) SMALL_BUFFER_SIZE, "\"%c\"", a[0]) >= 0);
 727              a68g_bufcat (b, d, BUFFER_SIZE);
 728            } else if (t[0] == 'K') {
 729              a68g_bufcat (b, "LONG", BUFFER_SIZE);
 730            } else if (t[0] == 'L') {
 731              LINE_T *a = va_arg (args, LINE_T *);
 732              char d[SMALL_BUFFER_SIZE];
 733              ABEND (a == NO_LINE, ERROR_INTERNAL_CONSISTENCY, __func__);
 734              if (NUMBER (a) == 0) {
 735                a68g_bufcat (b, "in standard environment", BUFFER_SIZE);
 736              } else {
 737                if (p != NO_NODE && NUMBER (a) == LINE_NUMBER (p)) {
 738                  ASSERT (a68g_bufprt (d, (size_t) SMALL_BUFFER_SIZE, "in this line") >= 0);
 739                } else {
 740                  ASSERT (a68g_bufprt (d, (size_t) SMALL_BUFFER_SIZE, "in line %d", NUMBER (a)) >= 0);
 741                }
 742                a68g_bufcat (b, d, BUFFER_SIZE);
 743              }
 744            } else if (t[0] == 'M') {
 745              moid = va_arg (args, MOID_T *);
 746              if (moid == NO_MOID || moid == M_ERROR) {
 747                moid = M_UNDEFINED;
 748              }
 749              if (IS (moid, SERIES_MODE)) {
 750                if (PACK (moid) != NO_PACK && NEXT (PACK (moid)) == NO_PACK) {
 751                  a68g_bufcat (b, moid_to_string (MOID (PACK (moid)), MOID_ERROR_WIDTH, p), BUFFER_SIZE);
 752                } else {
 753                  a68g_bufcat (b, moid_to_string (moid, MOID_ERROR_WIDTH, p), BUFFER_SIZE);
 754                }
 755              } else {
 756                a68g_bufcat (b, moid_to_string (moid, MOID_ERROR_WIDTH, p), BUFFER_SIZE);
 757              }
 758            } else if (t[0] == 'N') {
 759              a68g_bufcat (b, "NIL name of mode ", BUFFER_SIZE);
 760              moid = va_arg (args, MOID_T *);
 761              if (moid != NO_MOID) {
 762                a68g_bufcat (b, moid_to_string (moid, MOID_ERROR_WIDTH, p), BUFFER_SIZE);
 763              }
 764            } else if (t[0] == 'O') {
 765              moid = va_arg (args, MOID_T *);
 766              if (moid == NO_MOID || moid == M_ERROR) {
 767                moid = M_UNDEFINED;
 768              }
 769              if (moid == M_VOID) {
 770                a68g_bufcat (b, "UNION (VOID, ..)", BUFFER_SIZE);
 771              } else if (IS (moid, SERIES_MODE)) {
 772                if (PACK (moid) != NO_PACK && NEXT (PACK (moid)) == NO_PACK) {
 773                  a68g_bufcat (b, moid_to_string (MOID (PACK (moid)), MOID_ERROR_WIDTH, p), BUFFER_SIZE);
 774                } else {
 775                  a68g_bufcat (b, moid_to_string (moid, MOID_ERROR_WIDTH, p), BUFFER_SIZE);
 776                }
 777              } else {
 778                a68g_bufcat (b, moid_to_string (moid, MOID_ERROR_WIDTH, p), BUFFER_SIZE);
 779              }
 780            } else if (t[0] == 'S') {
 781              if (p != NO_NODE && NSYMBOL (p) != NO_TEXT) {
 782                char *txt = NSYMBOL (p);
 783                char *sym = NCHAR_IN_LINE (p);
 784                int n = 0;
 785                size_t size = strlen (txt);
 786                a68g_bufcat (b, "\"", BUFFER_SIZE);
 787                if (txt[0] != sym[0] || strlen (sym) < size) {
 788                  a68g_bufcat (b, txt, BUFFER_SIZE);
 789                } else {
 790                  while (n < size) {
 791                    if (IS_PRINT (sym[0])) {
 792                      char str[2];
 793                      str[0] = sym[0];
 794                      str[1] = NULL_CHAR;
 795                      a68g_bufcat (b, str, BUFFER_SIZE);
 796                    }
 797                    if (TO_LOWER (txt[0]) == TO_LOWER (sym[0])) {
 798                      txt++;
 799                      n++;
 800                    }
 801                    sym++;
 802                  }
 803                }
 804                a68g_bufcat (b, "\"", BUFFER_SIZE);
 805              } else {
 806                a68g_bufcat (b, "symbol", BUFFER_SIZE);
 807              }
 808            } else if (t[0] == 'V') {
 809              a68g_bufcat (b, PACKAGE_STRING, BUFFER_SIZE);
 810            } else if (t[0] == 'X') {
 811              int att = va_arg (args, int);
 812              BUFFER z;
 813              (void) non_terminal_string (z, att);
 814              a68g_bufcat (b, new_string (z, NO_TEXT), BUFFER_SIZE);
 815            } else if (t[0] == 'Y') {
 816              char *loc_string = va_arg (args, char *);
 817              if (loc_string != NO_TEXT) {
 818                a68g_bufcat (b, loc_string, BUFFER_SIZE);
 819              }
 820            } else if (t[0] == 'Z') {
 821              char *loc_string = va_arg (args, char *);
 822              a68g_bufcat (b, "\"", BUFFER_SIZE);
 823              if (loc_string != NO_TEXT) {
 824                a68g_bufcat (b, loc_string, BUFFER_SIZE);
 825              }
 826              a68g_bufcat (b, "\"", BUFFER_SIZE);
 827            } else {
 828              char q[2];
 829              q[0] = t[0];
 830              q[1] = NULL_CHAR;
 831              a68g_bufcat (b, q, BUFFER_SIZE);
 832            }
 833            t++;
 834          }
 835  // Add information from errno, if any.
 836        if (errno != 0) {
 837          char *loc_str2 = new_string (error_specification (), NO_TEXT);
 838          if (loc_str2 != NO_TEXT) {
 839            char *stu;
 840            a68g_bufcat (b, ", ", BUFFER_SIZE);
 841            for (stu = loc_str2; stu[0] != NULL_CHAR; stu++) {
 842              stu[0] = (char) TO_LOWER (stu[0]);
 843            }
 844            a68g_bufcat (b, loc_str2, BUFFER_SIZE);
 845          }
 846        }
 847      }
 848    }
 849  // Construct a diagnostic message.
 850    if (issue) {
 851      if (sev == A68G_NOTICE || sev == A68G_WARNING) {
 852        WARNING_COUNT (&A68G_JOB)++;
 853      } else {
 854        ERROR_COUNT (&A68G_JOB)++;
 855      }
 856      if (p == NO_NODE) {
 857        if (line == NO_LINE) {
 858          write_diagnostic (sev, b);
 859        } else {
 860          add_diagnostic (line, pos, NO_NODE, sev, b);
 861        }
 862      } else {
 863        add_diagnostic (NO_LINE, NO_TEXT, p, sev, b);
 864        if (sev == A68G_MATH_WARNING && p != NO_NODE && LINE (INFO (p)) != NO_LINE) {
 865          write_source_line (A68G_STDERR, LINE (INFO (p)), p, A68G_TRUE);
 866          WRITE (A68G_STDERR, NEWLINE_STRING);
 867        }
 868      }
 869    }
 870    va_end (args);
 871  }
     


© 2002-2025 J.M. van der Veer (jmvdveer@xs4all.nl)