(* M2Error.mod error reporting interface. Copyright (C) 2001-2023 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 M2Error ; FROM NameKey IMPORT NulName, Name, KeyToCharStar ; 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 M2LexBuf IMPORT FindFileNameFromToken, TokenToLineNo, TokenToColumnNo, GetTokenNo ; FROM Storage IMPORT ALLOCATE, DEALLOCATE ; FROM M2Printf IMPORT printf0, printf1, printf2, printf3 ; FROM M2Options IMPORT Xcode ; FROM M2RTS IMPORT ExitOnHalt ; FROM SYSTEM IMPORT ADDRESS ; FROM M2Emit IMPORT EmitError ; FROM M2LexBuf IMPORT UnknownTokenNo ; FROM M2StackAddress IMPORT StackOfAddress, InitStackAddress, PushAddress, PopAddress, NoOfItemsInStackAddress ; FROM Indexing IMPORT Index, HighIndice, InitIndex, GetIndice, PutIndice ; FROM M2Debug IMPORT Assert ; FROM M2Pass IMPORT IsPass0, IsPass1 ; FROM SymbolTable IMPORT NulSym ; FROM M2ColorString IMPORT filenameColor, endColor, errorColor, warningColor, noteColor, range1Color, range2Color, quoteOpen, quoteClose ; IMPORT M2Emit ; CONST Debugging = TRUE ; DebugTrace = FALSE ; DebugError = FALSE ; TYPE Error = POINTER TO RECORD parent, child, next : Error ; note, fatal : BOOLEAN ; s : String ; (* index of token causing the error *) token : CARDINAL ; color : BOOLEAN ; scope : ErrorScope ; END ; KindScope = (noscope, definition, implementation, program, module, procedure) ; ErrorScope = POINTER TO RECORD scopeKind: KindScope ; scopeName: Name ; symbol : CARDINAL ; (* symbol table entry. *) END ; VAR head : Error ; InInternal : BOOLEAN ; lastScope : ErrorScope ; scopeIndex : CARDINAL ; scopeArray : Index ; currentScope: ErrorScope ; scopeStack : StackOfAddress ; (* SetColor - informs the error module that this error will have had colors assigned to it. If an error is issued without colors assigned then the default colors will be assigned to the legacy error messages. *) PROCEDURE SetColor (e: Error) : Error ; BEGIN e^.color := TRUE ; RETURN e END SetColor ; (* 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 ; (* 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 (message: ARRAY OF CHAR) <* noreturn *> ; BEGIN IF NOT InInternal THEN InInternal := TRUE ; FlushErrors END ; M2Emit.InternalError (message) ; 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 n := NulName ; 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 := {} ; n := NulName ; 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 := {} ; n := NulName ; 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 ; (* MoveError - repositions an error, e, to token, AtTokenNo, and returns, e. *) PROCEDURE MoveError (e: Error; AtTokenNo: CARDINAL) : Error ; BEGIN IF e # NIL THEN e^.token := AtTokenNo END ; RETURN e END MoveError ; (* NewError - creates and returns a new error handle. *) PROCEDURE NewError (AtTokenNo: CARDINAL) : Error ; VAR e, f: Error ; BEGIN IF AtTokenNo = UnknownTokenNo THEN (* this could be used as a useful debugging hook as the front end has forgotten the token no. This can occur if a complex record structure or array is used for example. *) AtTokenNo := GetTokenNo () END ; NEW(e) ; WITH e^ DO s := NIL ; token := AtTokenNo ; next := NIL ; parent := NIL ; child := NIL ; note := FALSE ; fatal := TRUE ; color := FALSE ; END ; (* Assert (scopeKind # noscope) ; *) e^.scope := currentScope ; IF (head=NIL) OR (head^.token>AtTokenNo) THEN e^.next := head ; head := e ELSE f := head ; WHILE (f^.next#NIL) AND (f^.next^.token