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-2026 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 int k;
90 // Count the number of characters in token to print.
91 if (IS_GRAPH (p[0])) {
92 char *q;
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 *severity = get_severity (sev);
481 if (severity == NO_TEXT) {
482 ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "%s: %s.", A68G (a68g_cmd_name), b) >= 0);
483 } else {
484 char txt[SMALL_BUFFER_SIZE];
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 *severity = get_severity (sev);
501 int k = 1;
502 if (line == NO_LINE && p == NO_NODE) {
503 return;
504 }
505 if (A68G (in_monitor)) {
506 monitor_error (b, NO_TEXT);
507 return;
508 }
509 nst[0] = NULL_CHAR;
510 if (line == NO_LINE && p != NO_NODE) {
511 line = LINE (INFO (p));
512 }
513 while (line != NO_LINE && NUMBER (line) == 0) {
514 FORWARD (line);
515 }
516 if (line == NO_LINE) {
517 return;
518 }
519 ref_msg = &(DIAGNOSTICS (line));
520 while (*ref_msg != NO_DIAGNOSTIC) {
521 ref_msg = &(NEXT (*ref_msg));
522 k++;
523 }
524 if (p != NO_NODE) {
525 NODE_T *n = NEST (p);
526 if (n != NO_NODE && NSYMBOL (n) != NO_TEXT) {
527 char *nt = non_terminal_string (A68G (edit_line), ATTRIBUTE (n));
528 if (nt != NO_TEXT) {
529 if (LINE_NUMBER (n) == 0) {
530 ASSERT (a68g_bufprt (nst, SNPRINTF_SIZE, ", in %s", nt) >= 0);
531 } else {
532 if (MOID (n) != NO_MOID) {
533 if (LINE_NUMBER (n) == NUMBER (line)) {
534 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);
535 } else {
536 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);
537 }
538 } else {
539 if (LINE_NUMBER (n) == NUMBER (line)) {
540 ASSERT (a68g_bufprt (nst, SNPRINTF_SIZE, ", in %s starting at \"%.64s\" in this line", nt, NSYMBOL (n)) >= 0);
541 } else {
542 ASSERT (a68g_bufprt (nst, SNPRINTF_SIZE, ", in %s starting at \"%.64s\" in line %d", nt, NSYMBOL (n), LINE_NUMBER (n)) >= 0);
543 }
544 }
545 }
546 }
547 }
548 }
549 if (severity == NO_TEXT) {
550 if (FILENAME (line) != NO_TEXT && strcmp (FILE_SOURCE_NAME (&A68G_JOB), FILENAME (line)) == 0) {
551 ASSERT (a68g_bufprt (a, SNPRINTF_SIZE, "%s: %x: %s", A68G (a68g_cmd_name), (unt) k, b) >= 0);
552 } else if (FILENAME (line) != NO_TEXT) {
553 ASSERT (a68g_bufprt (a, SNPRINTF_SIZE, "%s: %s: %x: %s", A68G (a68g_cmd_name), FILENAME (line), (unt) k, b) >= 0);
554 } else {
555 ASSERT (a68g_bufprt (a, SNPRINTF_SIZE, "%s: %x: %s", A68G (a68g_cmd_name), (unt) k, b) >= 0);
556 }
557 } else {
558 char st[SMALL_BUFFER_SIZE];
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 char *t = loc_str, b[BUFFER_SIZE];
589 BOOL_T compose = A68G_TRUE, issue = A68G_TRUE;
590 va_start (args, loc_str);
591 b[0] = NULL_CHAR;
592 // Node or line?
593 LINE_T *line = NO_LINE;
594 char *pos = NO_TEXT;
595 if (p == NO_NODE) {
596 line = va_arg (args, LINE_T *);
597 pos = va_arg (args, char *);
598 }
599 // No warnings or notices?
600 if (sev == A68G_NOTICE && OPTION_NO_NOTICES (&A68G_JOB)) {
601 va_end (args);
602 return;
603 }
604 if (sev == A68G_WARNING && OPTION_NO_WARNINGS (&A68G_JOB)) {
605 va_end (args);
606 return;
607 }
608 if (sev == A68G_MATH_WARNING && OPTION_NO_WARNINGS (&A68G_JOB)) {
609 va_end (args);
610 return;
611 }
612 if (sev == A68G_NOTICE && OPTION_QUIET (&A68G_JOB)) {
613 va_end (args);
614 return;
615 }
616 if (sev == A68G_WARNING && OPTION_QUIET (&A68G_JOB)) {
617 va_end (args);
618 return;
619 }
620 if (sev == A68G_MATH_WARNING && OPTION_QUIET (&A68G_JOB)) {
621 va_end (args);
622 return;
623 }
624 // Suppressed?.
625 if (sev == A68G_ERROR || sev == A68G_SYNTAX_ERROR) {
626 if (ERROR_COUNT (&A68G_JOB) == MAX_ERRORS) {
627 a68g_bufcpy (b, "further diagnostics suppressed", BUFFER_SIZE);
628 compose = A68G_FALSE;
629 sev = A68G_ERROR;
630 } else if (ERROR_COUNT (&A68G_JOB) > MAX_ERRORS) {
631 ERROR_COUNT (&A68G_JOB)++;
632 compose = issue = A68G_FALSE;
633 }
634 } else if (sev == A68G_NOTICE || sev == A68G_WARNING || sev == A68G_MATH_WARNING) {
635 if (WARNING_COUNT (&A68G_JOB) == MAX_ERRORS) {
636 a68g_bufcpy (b, "further diagnostics suppressed", BUFFER_SIZE);
637 compose = A68G_FALSE;
638 } else if (WARNING_COUNT (&A68G_JOB) > MAX_ERRORS) {
639 WARNING_COUNT (&A68G_JOB)++;
640 compose = issue = A68G_FALSE;
641 }
642 }
643 if (compose) {
644 // Synthesize diagnostic message.
645 if ((sev & A68G_NO_SYNTHESIS) != NULL_MASK) {
646 sev &= ~A68G_NO_SYNTHESIS;
647 a68g_bufcat (b, t, BUFFER_SIZE);
648 } else {
649 // Legend for special symbols:
650 // * as first character, copy rest of string literally
651 // # skip extra syntactical information
652 // @ non terminal
653 // A non terminal
654 // B keyword
655 // C context
656 // D argument in decimal
657 // H char argument
658 // K 'LONG'
659 // L line number
660 // M moid - if error mode return without giving a message
661 // N mode - M_NIL
662 // O moid - operand
663 // S quoted symbol, when possible with typographical display features
664 // X expected attribute
665 // Y string literal.
666 // Z quoted string literal.
667 if (t[0] == '*') {
668 a68g_bufcat (b, &t[1], BUFFER_SIZE);
669 } else
670 while (t[0] != NULL_CHAR) {
671 if (t[0] == '#') {
672 ;
673 } else if (t[0] == '@') {
674 if (p == NO_NODE) {
675 a68g_bufcat (b, "construct", BUFFER_SIZE);
676 } else {
677 char *nt = non_terminal_string (A68G (edit_line), ATTRIBUTE (p));
678 if (t != NO_TEXT) {
679 a68g_bufcat (b, nt, BUFFER_SIZE);
680 } else {
681 a68g_bufcat (b, "construct", BUFFER_SIZE);
682 }
683 }
684 } else if (t[0] == 'A') {
685 int att = va_arg (args, int);
686 char *nt = non_terminal_string (A68G (edit_line), att);
687 if (nt != NO_TEXT) {
688 a68g_bufcat (b, nt, BUFFER_SIZE);
689 } else {
690 a68g_bufcat (b, "construct", BUFFER_SIZE);
691 }
692 } else if (t[0] == 'B') {
693 int att = va_arg (args, int);
694 KEYWORD_T *nt = find_keyword_from_attribute (A68G (top_keyword), att);
695 if (nt != NO_KEYWORD) {
696 a68g_bufcat (b, "\"", BUFFER_SIZE);
697 a68g_bufcat (b, TEXT (nt), BUFFER_SIZE);
698 a68g_bufcat (b, "\"", BUFFER_SIZE);
699 } else {
700 a68g_bufcat (b, "keyword", BUFFER_SIZE);
701 }
702 } else if (t[0] == 'C') {
703 int att = va_arg (args, int);
704 if (att == NO_SORT) {
705 a68g_bufcat (b, "this", BUFFER_SIZE);
706 }
707 if (att == SOFT) {
708 a68g_bufcat (b, "a soft", BUFFER_SIZE);
709 } else if (att == WEAK) {
710 a68g_bufcat (b, "a weak", BUFFER_SIZE);
711 } else if (att == MEEK) {
712 a68g_bufcat (b, "a meek", BUFFER_SIZE);
713 } else if (att == FIRM) {
714 a68g_bufcat (b, "a firm", BUFFER_SIZE);
715 } else if (att == STRONG) {
716 a68g_bufcat (b, "a strong", BUFFER_SIZE);
717 }
718 } else if (t[0] == 'D') {
719 int a = va_arg (args, int);
720 BUFFER d;
721 BUFCLR (d);
722 ASSERT (a68g_bufprt (d, SNPRINTF_SIZE, "%d", a) >= 0);
723 a68g_bufcat (b, d, BUFFER_SIZE);
724 } else if (t[0] == 'H') {
725 char *a = va_arg (args, char *);
726 char d[SMALL_BUFFER_SIZE];
727 ASSERT (a68g_bufprt (d, (size_t) SMALL_BUFFER_SIZE, "\"%c\"", a[0]) >= 0);
728 a68g_bufcat (b, d, BUFFER_SIZE);
729 } else if (t[0] == 'K') {
730 a68g_bufcat (b, "LONG", BUFFER_SIZE);
731 } else if (t[0] == 'L') {
732 LINE_T *a = va_arg (args, LINE_T *);
733 ABEND (a == NO_LINE, ERROR_INTERNAL_CONSISTENCY, NO_TEXT);
734 if (NUMBER (a) == 0) {
735 a68g_bufcat (b, "in standard environment", BUFFER_SIZE);
736 } else {
737 char d[SMALL_BUFFER_SIZE];
738 if (p != NO_NODE && NUMBER (a) == LINE_NUMBER (p)) {
739 ASSERT (a68g_bufprt (d, (size_t) SMALL_BUFFER_SIZE, "in this line") >= 0);
740 } else {
741 ASSERT (a68g_bufprt (d, (size_t) SMALL_BUFFER_SIZE, "in line %d", NUMBER (a)) >= 0);
742 }
743 a68g_bufcat (b, d, BUFFER_SIZE);
744 }
745 } else if (t[0] == 'M') {
746 MOID_T *moid = va_arg (args, MOID_T *);
747 if (moid == NO_MOID || moid == M_ERROR) {
748 moid = M_UNDEFINED;
749 }
750 if (IS (moid, SERIES_MODE)) {
751 if (PACK (moid) != NO_PACK && NEXT (PACK (moid)) == NO_PACK) {
752 a68g_bufcat (b, moid_to_string (MOID (PACK (moid)), MOID_ERROR_WIDTH, p), BUFFER_SIZE);
753 } else {
754 a68g_bufcat (b, moid_to_string (moid, MOID_ERROR_WIDTH, p), BUFFER_SIZE);
755 }
756 } else {
757 a68g_bufcat (b, moid_to_string (moid, MOID_ERROR_WIDTH, p), BUFFER_SIZE);
758 }
759 } else if (t[0] == 'N') {
760 a68g_bufcat (b, "NIL name of mode ", BUFFER_SIZE);
761 MOID_T *moid = va_arg (args, MOID_T *);
762 if (moid != NO_MOID) {
763 a68g_bufcat (b, moid_to_string (moid, MOID_ERROR_WIDTH, p), BUFFER_SIZE);
764 }
765 } else if (t[0] == 'O') {
766 MOID_T *moid = va_arg (args, MOID_T *);
767 if (moid == NO_MOID || moid == M_ERROR) {
768 moid = M_UNDEFINED;
769 }
770 if (moid == M_VOID) {
771 a68g_bufcat (b, "UNION (VOID, ..)", BUFFER_SIZE);
772 } else if (IS (moid, SERIES_MODE)) {
773 if (PACK (moid) != NO_PACK && NEXT (PACK (moid)) == NO_PACK) {
774 a68g_bufcat (b, moid_to_string (MOID (PACK (moid)), MOID_ERROR_WIDTH, p), BUFFER_SIZE);
775 } else {
776 a68g_bufcat (b, moid_to_string (moid, MOID_ERROR_WIDTH, p), BUFFER_SIZE);
777 }
778 } else {
779 a68g_bufcat (b, moid_to_string (moid, MOID_ERROR_WIDTH, p), BUFFER_SIZE);
780 }
781 } else if (t[0] == 'S') {
782 if (p != NO_NODE && NSYMBOL (p) != NO_TEXT) {
783 char *txt = NSYMBOL (p);
784 char *sym = NCHAR_IN_LINE (p);
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 int n = 0;
791 while (n < size) {
792 if (IS_PRINT (sym[0])) {
793 char str[2];
794 str[0] = sym[0];
795 str[1] = NULL_CHAR;
796 a68g_bufcat (b, str, BUFFER_SIZE);
797 }
798 if (TO_LOWER (txt[0]) == TO_LOWER (sym[0])) {
799 txt++;
800 n++;
801 }
802 sym++;
803 }
804 }
805 a68g_bufcat (b, "\"", BUFFER_SIZE);
806 } else {
807 a68g_bufcat (b, "symbol", BUFFER_SIZE);
808 }
809 } else if (t[0] == 'V') {
810 a68g_bufcat (b, PACKAGE_STRING, BUFFER_SIZE);
811 } else if (t[0] == 'X') {
812 int att = va_arg (args, int);
813 BUFFER z;
814 (void) non_terminal_string (z, att);
815 a68g_bufcat (b, new_string (z, NO_TEXT), BUFFER_SIZE);
816 } else if (t[0] == 'Y') {
817 char *loc_string = va_arg (args, char *);
818 if (loc_string != NO_TEXT) {
819 a68g_bufcat (b, loc_string, BUFFER_SIZE);
820 }
821 } else if (t[0] == 'Z') {
822 char *loc_string = va_arg (args, char *);
823 a68g_bufcat (b, "\"", BUFFER_SIZE);
824 if (loc_string != NO_TEXT) {
825 a68g_bufcat (b, loc_string, BUFFER_SIZE);
826 }
827 a68g_bufcat (b, "\"", BUFFER_SIZE);
828 } else {
829 char q[2];
830 q[0] = t[0];
831 q[1] = NULL_CHAR;
832 a68g_bufcat (b, q, BUFFER_SIZE);
833 }
834 t++;
835 }
836 // Add information from errno, if any.
837 if (errno != 0) {
838 char *loc_str2 = new_string (error_specification (), NO_TEXT);
839 if (loc_str2 != NO_TEXT) {
840 char *stu;
841 a68g_bufcat (b, ", ", BUFFER_SIZE);
842 for (stu = loc_str2; stu[0] != NULL_CHAR; stu++) {
843 stu[0] = (char) TO_LOWER (stu[0]);
844 }
845 a68g_bufcat (b, loc_str2, BUFFER_SIZE);
846 }
847 }
848 }
849 }
850 // Construct a diagnostic message.
851 if (issue) {
852 if (sev == A68G_NOTICE || sev == A68G_WARNING) {
853 WARNING_COUNT (&A68G_JOB)++;
854 } else {
855 ERROR_COUNT (&A68G_JOB)++;
856 }
857 if (p == NO_NODE) {
858 if (line == NO_LINE) {
859 write_diagnostic (sev, b);
860 } else {
861 add_diagnostic (line, pos, NO_NODE, sev, b);
862 }
863 } else {
864 add_diagnostic (NO_LINE, NO_TEXT, p, sev, b);
865 if (sev == A68G_MATH_WARNING && p != NO_NODE && LINE (INFO (p)) != NO_LINE) {
866 write_source_line (A68G_STDERR, LINE (INFO (p)), p, A68G_TRUE);
867 WRITE (A68G_STDERR, NEWLINE_STRING);
868 }
869 }
870 }
871 va_end (args);
872 }
|
© 2002-2026 J.M. van der Veer (jmvdveer@xs4all.nl)
|