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