]> git.ipfire.org Git - thirdparty/gcc.git/blame - gcc/m2/mc/mcError.mod
Update copyright years.
[thirdparty/gcc.git] / gcc / m2 / mc / mcError.mod
CommitLineData
1eee94d3
GM
1(* mcError.mod provides an interface between the string handling modules.
2
a945c346 3Copyright (C) 2015-2024 Free Software Foundation, Inc.
1eee94d3
GM
4Contributed by Gaius Mulley <gaius.mulley@southwales.ac.uk>.
5
6This file is part of GNU Modula-2.
7
8GNU Modula-2 is free software; you can redistribute it and/or modify
9it under the terms of the GNU General Public License as published by
10the Free Software Foundation; either version 3, or (at your option)
11any later version.
12
13GNU Modula-2 is distributed in the hope that it will be useful, but
14WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16General Public License for more details.
17
18You should have received a copy of the GNU General Public License
19along with GNU Modula-2; see the file COPYING3. If not see
20<http://www.gnu.org/licenses/>. *)
21
22IMPLEMENTATION MODULE mcError ;
23
24FROM ASCII IMPORT nul, nl ;
25FROM DynamicStrings IMPORT String, InitString, InitStringCharStar, ConCat, ConCatChar, Mark, string, KillString, Dup ;
26FROM FIO IMPORT StdOut, WriteNBytes, Close, FlushBuffer ;
27FROM StrLib IMPORT StrLen, StrEqual ;
28FROM FormatStrings IMPORT Sprintf0, Sprintf1, Sprintf2, Sprintf3 ;
29FROM Storage IMPORT ALLOCATE, DEALLOCATE ;
30FROM M2RTS IMPORT ExitOnHalt ;
31FROM SYSTEM IMPORT ADDRESS ;
32IMPORT StdIO ;
33
34FROM nameKey IMPORT Name, keyToCharStar ;
35FROM mcLexBuf IMPORT findFileNameFromToken, tokenToLineNo, tokenToColumnNo, getTokenNo ;
36FROM mcPrintf IMPORT printf0, printf1, printf2 ;
37
38
39CONST
40 Debugging = TRUE ;
41 DebugTrace = FALSE ;
42 Xcode = TRUE ;
43
44TYPE
45 error = POINTER TO RECORD
46 parent,
47 child,
48 next : error ;
49 fatal : BOOLEAN ;
50 s : String ;
51 token : CARDINAL ; (* index of token causing the error *)
52 END ;
53
54VAR
55 head : error ;
56 inInternal: BOOLEAN ;
57
58
59(*
60 cast - casts a := b
61*)
62
63PROCEDURE cast (VAR a: ARRAY OF BYTE; b: ARRAY OF BYTE) ;
64VAR
65 i: CARDINAL ;
66BEGIN
67 IF HIGH(a)=HIGH(b)
68 THEN
69 FOR i := 0 TO HIGH(a) DO
70 a[i] := b[i]
71 END
72 END
73END cast ;
74
75
76(*
77 translateNameToString - takes a format specification string, a, and
78 if they consist of of %a then this is translated
79 into a String and %a is replaced by %s.
80*)
81
82PROCEDURE translateNameToCharStar (VAR a: ARRAY OF CHAR;
83 n: CARDINAL) : BOOLEAN ;
84VAR
85 argno,
86 i, h : CARDINAL ;
87BEGIN
88 argno := 1 ;
89 i := 0 ;
90 h := StrLen (a) ;
91 WHILE i<h DO
92 IF (a[i]='%') AND (i+1<h)
93 THEN
94 IF (a[i+1]='a') AND (argno=n)
95 THEN
96 a[i+1] := 's' ;
97 RETURN TRUE
98 END ;
99 INC (argno) ;
100 IF argno>n
101 THEN
102 (* all done *)
103 RETURN FALSE
104 END
105 END ;
106 INC (i)
107 END ;
108 RETURN FALSE
109END translateNameToCharStar ;
110
111
112(*
113 outString - writes the contents of String to stdout.
114 The string, s, is destroyed.
115*)
116
117PROCEDURE outString (file: String; line, col: CARDINAL; s: String) ;
118VAR
119 leader : String ;
120 p, q : POINTER TO CHAR ;
121 space,
122 newline: BOOLEAN ;
123BEGIN
124 INC (col) ;
125 IF Xcode
126 THEN
127 leader := Sprintf2(Mark(InitString('%s:%d:')), file, line)
128 ELSE
129 leader := Sprintf3(Mark(InitString('%s:%d:%d:')), file, line, col)
130 END ;
131 p := string(s) ;
132 newline := TRUE ;
133 space := FALSE ;
134 WHILE (p#NIL) AND (p^#nul) DO
135 IF newline
136 THEN
137 q := string (leader) ;
138 WHILE (q#NIL) AND (q^#nul) DO
139 StdIO.Write (q^) ;
140 INC (q)
141 END
142 END ;
143 newline := (p^=nl) ;
144 space := (p^=' ') ;
145 IF newline AND Xcode
146 THEN
147 printf1 ('(pos: %d)', col)
148 END ;
149 StdIO.Write (p^) ;
150 INC (p)
151 END ;
152 IF NOT newline
153 THEN
154 IF Xcode
155 THEN
156 IF NOT space
157 THEN
158 StdIO.Write (' ')
159 END ;
160 printf1 ('(pos: %d)', col)
161 END ;
162 StdIO.Write (nl)
163 END ;
164 FlushBuffer (StdOut) ;
165 IF NOT Debugging
166 THEN
167 s := KillString (s) ;
168 leader := KillString (leader)
169 END
170END outString ;
171
172
173(*
174 internalError - displays an internal error message together with the compiler source
175 file and line number.
176 This function is not buffered and is used when the compiler is about
177 to give up.
178*)
179
180PROCEDURE internalError (a: ARRAY OF CHAR; file: ARRAY OF CHAR; line: CARDINAL) ;
181BEGIN
182 ExitOnHalt (1) ;
183 IF NOT inInternal
184 THEN
185 inInternal := TRUE ;
186 flushErrors ;
187 outString (findFileNameFromToken (getTokenNo (), 0),
188 tokenToLineNo (getTokenNo (), 0),
189 tokenToColumnNo (getTokenNo (), 0),
190 Mark(InitString ('*** fatal error ***')))
191 END ;
192 outString (Mark (InitString (file)), line, 0,
193 ConCat (Mark (InitString('*** internal error *** ')), Mark (InitString (a)))) ;
194 HALT
195END internalError ;
196
197
198(* ***************************************************************************
199 The following routines are used for normal syntax and semantic error reporting
200 *************************************************************************** *)
201
202
203(*
204 writeFormat0 - displays the source module and line together
205 with the encapsulated format string.
206 Used for simple error messages tied to the current token.
207*)
208
209PROCEDURE writeFormat0 (a: ARRAY OF CHAR) ;
210VAR
211 e: error ;
212BEGIN
213 e := newError (getTokenNo ()) ;
214 WITH e^ DO
215 s := Sprintf0 (Mark (InitString(a)))
216 END
217END writeFormat0 ;
218
219
220(*
221 WarnFormat0 - displays the source module and line together
222 with the encapsulated format string.
223 Used for simple warning messages tied to the current token.
224*)
225
226PROCEDURE warnFormat0 (a: ARRAY OF CHAR) ;
227VAR
228 e: error ;
229BEGIN
230 e := newWarning (getTokenNo()) ;
231 WITH e^ DO
232 s := Sprintf0 (Mark (InitString (a)))
233 END
234END warnFormat0 ;
235
236
237(*
238 DoFormat1 -
239*)
240
241PROCEDURE doFormat1 (a: ARRAY OF CHAR; w: ARRAY OF BYTE) : String ;
242VAR
243 s: String ;
244 n: Name ;
245BEGIN
246 IF translateNameToCharStar(a, 1)
247 THEN
248 cast(n, w) ;
249 s := Mark (InitStringCharStar (keyToCharStar (n))) ;
250 s := Sprintf1 (Mark (InitString (a)), s)
251 ELSE
252 s := Sprintf1 (Mark (InitString (a)), w)
253 END ;
254 RETURN s
255END doFormat1 ;
256
257
258(*
259 writeFormat1 - displays the source module and line together
260 with the encapsulated format string.
261 Used for simple error messages tied to the current token.
262*)
263
264PROCEDURE writeFormat1 (a: ARRAY OF CHAR; w: ARRAY OF BYTE) ;
265VAR
266 e: error ;
267BEGIN
268 e := newError (getTokenNo ()) ;
269 e^.s := doFormat1 (a, w)
270END writeFormat1 ;
271
272
273(*
274 warnFormat1 - displays the source module and line together
275 with the encapsulated format string.
276 Used for simple warning messages tied to the current token.
277*)
278
279PROCEDURE warnFormat1 (a: ARRAY OF CHAR; w: ARRAY OF BYTE) ;
280VAR
281 e: error ;
282BEGIN
283 e := newWarning (getTokenNo ()) ;
284 e^.s := doFormat1 (a, w)
285END warnFormat1 ;
286
287
288(*
289 doFormat2 -
290*)
291
292PROCEDURE doFormat2 (a: ARRAY OF CHAR; w1, w2: ARRAY OF BYTE) : String ;
293VAR
294 n : Name ;
295 s,
296 s1, s2: String ;
297 b : BITSET ;
298BEGIN
299 b := {} ;
300 IF translateNameToCharStar (a, 1)
301 THEN
302 cast (n, w1) ;
303 s1 := Mark (InitStringCharStar (keyToCharStar (n))) ;
304 INCL (b, 1)
305 END ;
306 IF translateNameToCharStar(a, 2)
307 THEN
308 cast (n, w2) ;
309 s2 := Mark (InitStringCharStar (keyToCharStar(n))) ;
310 INCL (b, 2)
311 END ;
312 CASE b OF
313
314 {} : s := Sprintf2 (Mark (InitString (a)), w1, w2) |
315 {1} : s := Sprintf2 (Mark (InitString (a)), s1, w2) |
316 {2} : s := Sprintf2 (Mark (InitString (a)), w1, s2) |
317 {1,2}: s := Sprintf2 (Mark (InitString (a)), s1, s2)
318
319 ELSE
320 HALT
321 END ;
322 RETURN s
323END doFormat2 ;
324
325
326(*
327 writeFormat2 - displays the module and line together with the encapsulated
328 format strings.
329 Used for simple error messages tied to the current token.
330*)
331
332PROCEDURE writeFormat2 (a: ARRAY OF CHAR; w1, w2: ARRAY OF BYTE) ;
333VAR
334 e: error ;
335BEGIN
336 e := newError (getTokenNo()) ;
337 e^.s := doFormat2 (a, w1, w2)
338END writeFormat2 ;
339
340
341PROCEDURE doFormat3 (a: ARRAY OF CHAR; w1, w2, w3: ARRAY OF BYTE) : String ;
342VAR
343 n : Name ;
344 s, s1, s2, s3: String ;
345 b : BITSET ;
346BEGIN
347 b := {} ;
348 IF translateNameToCharStar (a, 1)
349 THEN
350 cast (n, w1) ;
351 s1 := Mark (InitStringCharStar (keyToCharStar (n))) ;
352 INCL(b, 1)
353 END ;
354 IF translateNameToCharStar (a, 2)
355 THEN
356 cast (n, w2) ;
357 s2 := Mark (InitStringCharStar (keyToCharStar (n))) ;
358 INCL (b, 2)
359 END ;
360 IF translateNameToCharStar (a, 3)
361 THEN
362 cast(n, w3) ;
363 s3 := Mark (InitStringCharStar (keyToCharStar (n))) ;
364 INCL (b, 3)
365 END ;
366 CASE b OF
367
368 {} : s := Sprintf3 (Mark (InitString (a)), w1, w2, w3) |
369 {1} : s := Sprintf3 (Mark (InitString (a)), s1, w2, w3) |
370 {2} : s := Sprintf3 (Mark (InitString (a)), w1, s2, w3) |
371 {1,2} : s := Sprintf3 (Mark (InitString (a)), s1, s2, w3) |
372 {3} : s := Sprintf3 (Mark (InitString (a)), w1, w2, s3) |
373 {1,3} : s := Sprintf3 (Mark (InitString (a)), s1, w2, s3) |
374 {2,3} : s := Sprintf3 (Mark (InitString (a)), w1, s2, s3) |
375 {1,2,3}: s := Sprintf3 (Mark (InitString (a)), s1, s2, s3)
376
377 ELSE
378 HALT
379 END ;
380 RETURN s
381END doFormat3 ;
382
383
384(*
385 writeFormat3 - displays the module and line together with the encapsulated
386 format strings.
387 Used for simple error messages tied to the current token.
388*)
389
390PROCEDURE writeFormat3 (a: ARRAY OF CHAR; w1, w2, w3: ARRAY OF BYTE) ;
391VAR
392 e: error ;
393BEGIN
394 e := newError (getTokenNo ()) ;
395 e^.s := doFormat3 (a, w1, w2, w3)
396END writeFormat3 ;
397
398
399(*
400 newError - creates and returns a new error handle.
401*)
402
403PROCEDURE newError (atTokenNo: CARDINAL) : error ;
404VAR
405 e, f: error ;
406BEGIN
407 NEW (e) ;
408 WITH e^ DO
409 s := NIL ;
410 token := atTokenNo ;
411 next := NIL ;
412 parent := NIL ;
413 child := NIL ;
414 fatal := TRUE
415 END ;
416 IF (head=NIL) OR (head^.token>atTokenNo)
417 THEN
418 e^.next := head ;
419 head := e
420 ELSE
421 f := head ;
422 WHILE (f^.next#NIL) AND (f^.next^.token<atTokenNo) DO
423 f := f^.next
424 END ;
425 e^.next := f^.next ;
426 f^.next := e
427 END ;
428 RETURN e
429END newError ;
430
431
432(*
433 newWarning - creates and returns a new error handle suitable for a warning.
434 A warning will not stop compilation.
435*)
436
437PROCEDURE newWarning (atTokenNo: CARDINAL) : error ;
438VAR
439 e: error ;
440BEGIN
441 e := newError (atTokenNo) ;
442 e^.fatal := FALSE ;
443 RETURN e
444END newWarning ;
445
446
447(*
448 chainError - creates and returns a new error handle, this new error
449 is associated with, e, and is chained onto the end of, e.
450 If, e, is NIL then the result to NewError is returned.
451*)
452
453PROCEDURE chainError (atTokenNo: CARDINAL; e: error) : error ;
454VAR
455 f: error ;
456BEGIN
457 IF e=NIL
458 THEN
459 RETURN newError (atTokenNo)
460 ELSE
461 NEW (f) ;
462 WITH f^ DO
463 s := NIL ;
464 token := atTokenNo ;
465 next := e^.child ;
466 parent := e ;
467 child := NIL ;
468 fatal := e^.fatal
469 END ;
470 e^.child := f
471 END ;
472 RETURN f
473END chainError ;
474
475
476(*
477 errorFormat routines provide a printf capability for the error handle.
478*)
479
480PROCEDURE errorFormat0 (e: error; a: ARRAY OF CHAR) ;
481BEGIN
482 WITH e^ DO
483 IF s=NIL
484 THEN
485 s := Sprintf0 (Mark (InitString (a)))
486 ELSE
487 s := ConCat(s, Mark(Sprintf0 (Mark (InitString (a)))))
488 END
489 END
490END errorFormat0 ;
491
492
493PROCEDURE errorFormat1 (e: error; a: ARRAY OF CHAR; w: ARRAY OF BYTE) ;
494VAR
495 s1: String ;
496BEGIN
497 s1 := doFormat1 (a, w) ;
498 WITH e^ DO
499 IF s=NIL
500 THEN
501 s := s1
502 ELSE
503 s := ConCat (s, Mark (s1))
504 END
505 END
506END errorFormat1 ;
507
508
509PROCEDURE errorFormat2 (e: error; a: ARRAY OF CHAR; w1, w2: ARRAY OF BYTE) ;
510VAR
511 s1: String ;
512BEGIN
513 s1 := doFormat2 (a, w1, w2) ;
514 WITH e^ DO
515 IF s=NIL
516 THEN
517 s := s1
518 ELSE
519 s := ConCat (s, Mark (s1))
520 END
521 END
522END errorFormat2 ;
523
524
525PROCEDURE errorFormat3 (e: error; a: ARRAY OF CHAR;
526 w1, w2, w3: ARRAY OF BYTE) ;
527VAR
528 s1: String ;
529BEGIN
530 s1 := doFormat3 (a, w1, w2, w3) ;
531 WITH e^ DO
532 IF s=NIL
533 THEN
534 s := s1
535 ELSE
536 s := ConCat (s, Mark (s1))
537 END
538 END
539END errorFormat3 ;
540
541
542PROCEDURE errorString (e: error; str: String) ;
543BEGIN
544 WITH e^ DO
545 s := str
546 END
547END errorString ;
548
549
550(*
551 init - initializes the error list.
552*)
553
554PROCEDURE init ;
555BEGIN
556 head := NIL ;
557 inInternal := FALSE
558END init ;
559
560
561(*
562 checkIncludes - generates a sequence of error messages which determine the relevant
563 included file and line number.
564 For example:
565
566 gcc a.c
567 In file included from b.h:1,
568 from a.c:1:
569 c.h:1: parse error before `and'
570
571 where a.c is: #include "b.h"
572 b.h is: #include "c.h"
573 c.h is: and this and that
574
575 we attempt to follow the error messages that gcc issues.
576*)
577
578PROCEDURE checkIncludes (token: CARDINAL; depth: CARDINAL) ;
579VAR
580 included: String ;
581 lineno : CARDINAL ;
582BEGIN
583 included := findFileNameFromToken (token, depth+1) ;
584 IF included#NIL
585 THEN
586 lineno := tokenToLineNo (token, depth+1) ;
587 IF depth=0
588 THEN
589 printf2('In file included from %s:%d', included, lineno)
590 ELSE
591 printf2(' from %s:%d', included, lineno)
592 END ;
593 IF findFileNameFromToken (token, depth+2)=NIL
594 THEN
595 printf0(':\n')
596 ELSE
597 printf0(',\n')
598 END ;
599 checkIncludes (token, depth+1)
600 END
601END checkIncludes ;
602
603
604(*
605 flushAll - flushes all errors in list, e.
606*)
607
608PROCEDURE flushAll (e: error; FatalStatus: BOOLEAN) : BOOLEAN ;
609VAR
610 f : error ;
611 written: BOOLEAN ;
612BEGIN
613 written := FALSE ;
614 IF e#NIL
615 THEN
616 REPEAT
617 WITH e^ DO
618 IF (FatalStatus=fatal) AND (s#NIL)
619 THEN
620 checkIncludes (token, 0) ;
621 IF fatal
622 THEN
623 s := ConCat (InitString (' error: '), Mark (s))
624 ELSE
625 s := ConCat (InitString(' warning: '), Mark (s))
626 END ;
627 outString (findFileNameFromToken (token, 0),
628 tokenToLineNo (token, 0), tokenToColumnNo (token, 0), s) ;
629 IF (child#NIL) AND flushAll (child, FatalStatus)
630 THEN
631 END ;
632 s := NIL ;
633 written := TRUE
634 END
635 END ;
636 f := e ;
637 e := e^.next ;
638 IF NOT Debugging
639 THEN
640 WITH f^ DO
641 s := KillString(s)
642 END ;
643 DISPOSE (f)
644 END ;
645 UNTIL e=NIL
646 END ;
647 RETURN written
648END flushAll ;
649
650
651(*
652 flushErrors - switches the output channel to the error channel
653 and then writes out all errors.
654*)
655
656PROCEDURE flushErrors ;
657BEGIN
658 IF DebugTrace
659 THEN
660 printf0 ('\nFlushing all errors\n') ;
661 printf0 ('===================\n')
662 END ;
663 IF flushAll (head, TRUE)
664 THEN
665 ExitOnHalt (1) ;
666 HALT
667 END
668END flushErrors ;
669
670
671(*
672 flushWarnings - switches the output channel to the error channel
673 and then writes out all warnings.
674 If an error is present the compilation is terminated,
675 if warnings only were emitted then compilation will
676 continue.
677*)
678
679PROCEDURE flushWarnings ;
680BEGIN
681 IF flushAll (head, FALSE)
682 THEN
683 END
684END flushWarnings ;
685
686
687(*
688 errorStringsAt2 - given error strings, s1, and, s2, it places these
689 strings at token positions, tok1 and tok2, respectively.
690 Both strings are consumed.
691*)
692
693PROCEDURE errorStringsAt2 (s1, s2: String; tok1, tok2: CARDINAL) ;
694VAR
695 e: error ;
696BEGIN
697 IF s1=s2
698 THEN
699 s2 := Dup (s1)
700 END ;
701 e := newError (tok1) ;
702 errorString (e, s1) ;
703 errorString (chainError (tok2, e), s2)
704END errorStringsAt2 ;
705
706
707(*
708 errorStringAt2 - given an error string, s, it places this
709 string at token positions, tok1 and tok2, respectively.
710 The string is consumed.
711*)
712
713PROCEDURE errorStringAt2 (s: String; tok1, tok2: CARDINAL) ;
714BEGIN
715 errorStringsAt2 (s, s, tok1, tok2)
716END errorStringAt2 ;
717
718
719(*
720 errorStringAt - given an error string, s, it places this
721 string at token position, tok.
722 The string is consumed.
723*)
724
725PROCEDURE errorStringAt (s: String; tok: CARDINAL) ;
726VAR
727 e: error ;
728BEGIN
729 e := newError (tok) ;
730 errorString (e, s)
731END errorStringAt ;
732
733
734(*
735 warnStringsAt2 - given warning strings, s1, and, s2, it places these
736 strings at token positions, tok1 and tok2, respectively.
737 Both strings are consumed.
738*)
739
740PROCEDURE warnStringsAt2 (s1, s2: String; tok1, tok2: CARDINAL) ;
741VAR
742 e: error ;
743BEGIN
744 IF s1=s2
745 THEN
746 s2 := Dup (s1)
747 END ;
748 e := newWarning (tok1) ;
749 errorString (e, s1) ;
750 errorString (chainError (tok2, e), s2)
751END warnStringsAt2 ;
752
753
754(*
755 warnStringAt2 - given an warning string, s, it places this
756 string at token positions, tok1 and tok2, respectively.
757 The string is consumed.
758*)
759
760PROCEDURE warnStringAt2 (s: String; tok1, tok2: CARDINAL) ;
761BEGIN
762 warnStringsAt2 (s, s, tok1, tok2)
763END warnStringAt2 ;
764
765
766(*
767 warnStringAt - given an error string, s, it places this
768 string at token position, tok.
769 The string is consumed.
770*)
771
772PROCEDURE warnStringAt (s: String; tok: CARDINAL) ;
773VAR
774 e: error ;
775BEGIN
776 e := newWarning (tok) ;
777 errorString (e, s)
778END warnStringAt ;
779
780
781(*
782 errorAbort0 - aborts compiling, it flushes all warnings and errors before aborting.
783*)
784
785PROCEDURE errorAbort0 (a: ARRAY OF CHAR) ;
786BEGIN
787 flushWarnings ;
788 IF NOT StrEqual (a, '')
789 THEN
790 writeFormat0(a)
791 END ;
792 IF NOT flushAll(head, TRUE)
793 THEN
794 writeFormat0 ('unidentified error') ;
795 IF flushAll (head, TRUE)
796 THEN
797 END
798 END ;
799 ExitOnHalt (1) ;
800 HALT
801END errorAbort0 ;
802
803
804BEGIN
805 init
806END mcError.