(* mcError.mod provides an interface between the string handling modules. Copyright (C) 2015-2024 Free Software Foundation, Inc. Contributed by Gaius Mulley . This file is part of GNU Modula-2. GNU Modula-2 is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GNU Modula-2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Modula-2; see the file COPYING3. If not see . *) IMPLEMENTATION MODULE mcError ; FROM ASCII IMPORT nul, nl ; FROM DynamicStrings IMPORT String, InitString, InitStringCharStar, ConCat, ConCatChar, Mark, string, KillString, Dup ; FROM FIO IMPORT StdOut, WriteNBytes, Close, FlushBuffer ; FROM StrLib IMPORT StrLen, StrEqual ; FROM FormatStrings IMPORT Sprintf0, Sprintf1, Sprintf2, Sprintf3 ; FROM Storage IMPORT ALLOCATE, DEALLOCATE ; FROM M2RTS IMPORT ExitOnHalt ; FROM SYSTEM IMPORT ADDRESS ; IMPORT StdIO ; FROM nameKey IMPORT Name, keyToCharStar ; FROM mcLexBuf IMPORT findFileNameFromToken, tokenToLineNo, tokenToColumnNo, getTokenNo ; FROM mcPrintf IMPORT printf0, printf1, printf2 ; CONST Debugging = TRUE ; DebugTrace = FALSE ; Xcode = TRUE ; TYPE error = POINTER TO RECORD parent, child, next : error ; fatal : BOOLEAN ; s : String ; token : CARDINAL ; (* index of token causing the error *) END ; VAR head : error ; inInternal: BOOLEAN ; (* cast - casts a := b *) PROCEDURE cast (VAR a: ARRAY OF BYTE; b: ARRAY OF BYTE) ; VAR i: CARDINAL ; BEGIN IF HIGH(a)=HIGH(b) THEN FOR i := 0 TO HIGH(a) DO a[i] := b[i] END END END cast ; (* translateNameToString - takes a format specification string, a, and if they consist of of %a then this is translated into a String and %a is replaced by %s. *) PROCEDURE translateNameToCharStar (VAR a: ARRAY OF CHAR; n: CARDINAL) : BOOLEAN ; VAR argno, i, h : CARDINAL ; BEGIN argno := 1 ; i := 0 ; h := StrLen (a) ; WHILE in THEN (* all done *) RETURN FALSE END END ; INC (i) END ; RETURN FALSE END translateNameToCharStar ; (* outString - writes the contents of String to stdout. The string, s, is destroyed. *) PROCEDURE outString (file: String; line, col: CARDINAL; s: String) ; VAR leader : String ; p, q : POINTER TO CHAR ; space, newline: BOOLEAN ; BEGIN INC (col) ; IF Xcode THEN leader := Sprintf2(Mark(InitString('%s:%d:')), file, line) ELSE leader := Sprintf3(Mark(InitString('%s:%d:%d:')), file, line, col) END ; p := string(s) ; newline := TRUE ; space := FALSE ; WHILE (p#NIL) AND (p^#nul) DO IF newline THEN q := string (leader) ; WHILE (q#NIL) AND (q^#nul) DO StdIO.Write (q^) ; INC (q) END END ; newline := (p^=nl) ; space := (p^=' ') ; IF newline AND Xcode THEN printf1 ('(pos: %d)', col) END ; StdIO.Write (p^) ; INC (p) END ; IF NOT newline THEN IF Xcode THEN IF NOT space THEN StdIO.Write (' ') END ; printf1 ('(pos: %d)', col) END ; StdIO.Write (nl) END ; FlushBuffer (StdOut) ; IF NOT Debugging THEN s := KillString (s) ; leader := KillString (leader) END END outString ; (* internalError - displays an internal error message together with the compiler source file and line number. This function is not buffered and is used when the compiler is about to give up. *) PROCEDURE internalError (a: ARRAY OF CHAR; file: ARRAY OF CHAR; line: CARDINAL) ; BEGIN ExitOnHalt (1) ; IF NOT inInternal THEN inInternal := TRUE ; flushErrors ; outString (findFileNameFromToken (getTokenNo (), 0), tokenToLineNo (getTokenNo (), 0), tokenToColumnNo (getTokenNo (), 0), Mark(InitString ('*** fatal error ***'))) END ; outString (Mark (InitString (file)), line, 0, ConCat (Mark (InitString('*** internal error *** ')), Mark (InitString (a)))) ; HALT END internalError ; (* *************************************************************************** The following routines are used for normal syntax and semantic error reporting *************************************************************************** *) (* writeFormat0 - displays the source module and line together with the encapsulated format string. Used for simple error messages tied to the current token. *) PROCEDURE writeFormat0 (a: ARRAY OF CHAR) ; VAR e: error ; BEGIN e := newError (getTokenNo ()) ; WITH e^ DO s := Sprintf0 (Mark (InitString(a))) END END writeFormat0 ; (* WarnFormat0 - displays the source module and line together with the encapsulated format string. Used for simple warning messages tied to the current token. *) PROCEDURE warnFormat0 (a: ARRAY OF CHAR) ; VAR e: error ; BEGIN e := newWarning (getTokenNo()) ; WITH e^ DO s := Sprintf0 (Mark (InitString (a))) END END warnFormat0 ; (* DoFormat1 - *) PROCEDURE doFormat1 (a: ARRAY OF CHAR; w: ARRAY OF BYTE) : String ; VAR s: String ; n: Name ; BEGIN IF translateNameToCharStar(a, 1) THEN cast(n, w) ; s := Mark (InitStringCharStar (keyToCharStar (n))) ; s := Sprintf1 (Mark (InitString (a)), s) ELSE s := Sprintf1 (Mark (InitString (a)), w) END ; RETURN s END doFormat1 ; (* writeFormat1 - displays the source module and line together with the encapsulated format string. Used for simple error messages tied to the current token. *) PROCEDURE writeFormat1 (a: ARRAY OF CHAR; w: ARRAY OF BYTE) ; VAR e: error ; BEGIN e := newError (getTokenNo ()) ; e^.s := doFormat1 (a, w) END writeFormat1 ; (* warnFormat1 - displays the source module and line together with the encapsulated format string. Used for simple warning messages tied to the current token. *) PROCEDURE warnFormat1 (a: ARRAY OF CHAR; w: ARRAY OF BYTE) ; VAR e: error ; BEGIN e := newWarning (getTokenNo ()) ; e^.s := doFormat1 (a, w) END warnFormat1 ; (* doFormat2 - *) PROCEDURE doFormat2 (a: ARRAY OF CHAR; w1, w2: ARRAY OF BYTE) : String ; VAR n : Name ; s, s1, s2: String ; b : BITSET ; BEGIN b := {} ; IF translateNameToCharStar (a, 1) THEN cast (n, w1) ; s1 := Mark (InitStringCharStar (keyToCharStar (n))) ; INCL (b, 1) END ; IF translateNameToCharStar(a, 2) THEN cast (n, w2) ; s2 := Mark (InitStringCharStar (keyToCharStar(n))) ; INCL (b, 2) END ; CASE b OF {} : s := Sprintf2 (Mark (InitString (a)), w1, w2) | {1} : s := Sprintf2 (Mark (InitString (a)), s1, w2) | {2} : s := Sprintf2 (Mark (InitString (a)), w1, s2) | {1,2}: s := Sprintf2 (Mark (InitString (a)), s1, s2) ELSE HALT END ; RETURN s END doFormat2 ; (* writeFormat2 - displays the module and line together with the encapsulated format strings. Used for simple error messages tied to the current token. *) PROCEDURE writeFormat2 (a: ARRAY OF CHAR; w1, w2: ARRAY OF BYTE) ; VAR e: error ; BEGIN e := newError (getTokenNo()) ; e^.s := doFormat2 (a, w1, w2) END writeFormat2 ; PROCEDURE doFormat3 (a: ARRAY OF CHAR; w1, w2, w3: ARRAY OF BYTE) : String ; VAR n : Name ; s, s1, s2, s3: String ; b : BITSET ; BEGIN b := {} ; IF translateNameToCharStar (a, 1) THEN cast (n, w1) ; s1 := Mark (InitStringCharStar (keyToCharStar (n))) ; INCL(b, 1) END ; IF translateNameToCharStar (a, 2) THEN cast (n, w2) ; s2 := Mark (InitStringCharStar (keyToCharStar (n))) ; INCL (b, 2) END ; IF translateNameToCharStar (a, 3) THEN cast(n, w3) ; s3 := Mark (InitStringCharStar (keyToCharStar (n))) ; INCL (b, 3) END ; CASE b OF {} : s := Sprintf3 (Mark (InitString (a)), w1, w2, w3) | {1} : s := Sprintf3 (Mark (InitString (a)), s1, w2, w3) | {2} : s := Sprintf3 (Mark (InitString (a)), w1, s2, w3) | {1,2} : s := Sprintf3 (Mark (InitString (a)), s1, s2, w3) | {3} : s := Sprintf3 (Mark (InitString (a)), w1, w2, s3) | {1,3} : s := Sprintf3 (Mark (InitString (a)), s1, w2, s3) | {2,3} : s := Sprintf3 (Mark (InitString (a)), w1, s2, s3) | {1,2,3}: s := Sprintf3 (Mark (InitString (a)), s1, s2, s3) ELSE HALT END ; RETURN s END doFormat3 ; (* writeFormat3 - displays the module and line together with the encapsulated format strings. Used for simple error messages tied to the current token. *) PROCEDURE writeFormat3 (a: ARRAY OF CHAR; w1, w2, w3: ARRAY OF BYTE) ; VAR e: error ; BEGIN e := newError (getTokenNo ()) ; e^.s := doFormat3 (a, w1, w2, w3) END writeFormat3 ; (* newError - creates and returns a new error handle. *) PROCEDURE newError (atTokenNo: CARDINAL) : error ; VAR e, f: error ; BEGIN NEW (e) ; WITH e^ DO s := NIL ; token := atTokenNo ; next := NIL ; parent := NIL ; child := NIL ; fatal := TRUE END ; IF (head=NIL) OR (head^.token>atTokenNo) THEN e^.next := head ; head := e ELSE f := head ; WHILE (f^.next#NIL) AND (f^.next^.token