blob: 3aa7543231d744644c368fbed626c51fd3435d42 [file] [log] [blame]
(* M2MetaError.mod provides a set of high level error routines.
Copyright (C) 2008-2025 Free Software Foundation, Inc.
Contributed by Gaius Mulley <gaius.mulley@southwales.ac.uk>.
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
<http://www.gnu.org/licenses/>. *)
IMPLEMENTATION MODULE M2MetaError ;
FROM M2Base IMPORT ZType, RType, IsPseudoBaseFunction, IsPseudoBaseProcedure ;
FROM NameKey IMPORT Name, KeyToCharStar, NulName ;
FROM StrLib IMPORT StrLen ;
FROM M2LexBuf IMPORT GetTokenNo, UnknownTokenNo ;
FROM M2Error IMPORT Error, NewError, NewWarning, NewNote, ErrorString, InternalError, ChainError, SetColor, FlushErrors, FlushWarnings ;
FROM FIO IMPORT StdOut, WriteLine ;
FROM SFIO IMPORT WriteS ;
FROM StringConvert IMPORT ctos ;
FROM M2Printf IMPORT printf1, printf0 ;
FROM M2Options IMPORT LowerCaseKeywords ;
FROM StrCase IMPORT Lower ;
FROM libc IMPORT printf ;
FROM SYSTEM IMPORT ADDRESS ;
FROM M2Error IMPORT MoveError ;
FROM M2Debug IMPORT Assert ;
FROM Storage IMPORT ALLOCATE ;
FROM Indexing IMPORT Index, InitIndex, KillIndex, GetIndice, PutIndice,
DeleteIndice, HighIndice ;
FROM DynamicStrings IMPORT String, InitString, InitStringCharStar,
ConCat, ConCatChar, Mark, string, KillString,
Dup, char, Length, Mult, EqualArray, Equal ;
FROM SymbolTable IMPORT NulSym,
IsDefImp, IsModule, IsInnerModule,
IsUnknown, IsType, IsProcedure, IsParameter,
IsParameterUnbounded, IsParameterVar, IsVarParam,
IsUnboundedParamAny, IsPointer, IsRecord, IsVarient,
IsFieldVarient, IsEnumeration, IsFieldEnumeration,
IsUnbounded, IsArray, IsRecordField, IsProcType,
IsVar, IsConst, IsConstString, IsConstLit, IsConstSet,
IsConstructor, IsDummy, IsTemporary, IsVarAParam,
IsSubscript, IsSubrange, IsSet, IsHiddenType,
IsError, GetSymName, GetScope, IsExported,
GetType, SkipType, GetDeclaredDef, GetDeclaredMod,
GetDeclaredFor, GetDeclaredModule,
GetDeclaredDefinition, GetScope,
GetFirstUsed, IsNameAnonymous, GetErrorScope,
GetVarDeclTok, GetVarDeclTypeTok, GetVarDeclFullTok ;
IMPORT M2ColorString ;
IMPORT M2Error ;
CONST
MaxStack = 10 ;
Debugging = FALSE ;
ColorDebug = FALSE ;
TYPE
GetTokProcedure = PROCEDURE (CARDINAL) : CARDINAL ;
errorType = (none, error, warning, note, chained, aborta) ;
colorType = (unsetColor, noColor, quoteColor, filenameColor, errorColor,
warningColor, noteColor, keywordColor, locusColor,
insertColor, deleteColor, typeColor, range1Color, range2Color) ;
errorBlock = RECORD
useError : BOOLEAN ;
e : Error ;
type : errorType ;
out, in : String ;
highplus1 : CARDINAL ;
len,
ini : INTEGER ;
glyph,
chain,
root,
quotes,
positive : BOOLEAN ;
currentCol,
beginCol, (* the color at the start of the string. *)
endCol : colorType ; (* the color at the end of the text before. *)
colorStack: ARRAY [0..MaxStack] OF colorType ;
stackPtr : CARDINAL ;
END ;
dictionaryEntry = POINTER TO RECORD
key,
value: String ;
next : dictionaryEntry ;
END ;
VAR
lastRoot : Error ;
lastColor : colorType ;
seenAbort : BOOLEAN ;
dictionary : Index ;
outputStack: Index ;
freeEntry : dictionaryEntry ;
(*
pushOutput -
*)
PROCEDURE pushOutput (VAR eb: errorBlock) ;
BEGIN
PutIndice (outputStack, HighIndice (outputStack)+1, eb.out) ;
eb.out := InitString ('') ;
eb.glyph := FALSE
END pushOutput ;
(*
readWord - reads and returns a word delimited by '}' it uses '%' as
the escape character.
*)
PROCEDURE readWord (VAR eb: errorBlock) : String ;
VAR
word: String ;
BEGIN
word := InitString ('') ;
WHILE (eb.ini < eb.len) AND (char (eb.in, eb.ini) # "}") DO
IF char (eb.in, eb.ini) = "%"
THEN
INC (eb.ini)
END ;
word := ConCatChar (word, char (eb.in, eb.ini)) ;
INC (eb.ini)
END ;
RETURN word
END readWord ;
(*
addEntry -
*)
PROCEDURE addEntry (key, value: String) ;
VAR
e: dictionaryEntry ;
s: String ;
i: CARDINAL ;
BEGIN
s := lookupString (key) ;
IF s = NIL
THEN
e := newEntry () ;
e^.key := key ;
e^.value := value ;
PutIndice (dictionary, HighIndice (dictionary)+1, e)
ELSE
i := 1 ;
WHILE i <= HighIndice (dictionary) DO
e := GetIndice (dictionary, i) ;
IF Equal (e^.key, key)
THEN
e^.value := KillString (e^.value) ;
e^.value := value ;
RETURN
END ;
INC (i)
END
END
END addEntry ;
(*
popOutput -
*)
PROCEDURE popOutput (VAR eb: errorBlock) ;
VAR
key,
previous: String ;
BEGIN
IF HighIndice (outputStack) >= 1
THEN
previous := GetIndice (outputStack, HighIndice (outputStack)) ;
DeleteIndice (outputStack, HighIndice (outputStack)) ;
key := readWord (eb) ;
addEntry (key, eb.out) ;
eb.out := previous
END
END popOutput ;
(*
newEntry -
*)
PROCEDURE newEntry () : dictionaryEntry ;
VAR
e: dictionaryEntry ;
BEGIN
IF freeEntry = NIL
THEN
NEW (e)
ELSE
e := freeEntry ;
freeEntry := freeEntry^.next
END ;
WITH e^ DO
key := NIL ;
value := NIL ;
next := NIL
END ;
RETURN e
END newEntry ;
(*
killEntry - dispose e and delete any strings.
*)
PROCEDURE killEntry (e: dictionaryEntry) ;
BEGIN
e^.next := freeEntry ;
freeEntry := e ;
IF e^.key # NIL
THEN
e^.key := KillString (e^.key)
END ;
IF e^.value # NIL
THEN
e^.value := KillString (e^.value)
END
END killEntry ;
(*
resetDictionary - remove all entries in the dictionary.
*)
PROCEDURE resetDictionary ;
VAR
i: CARDINAL ;
e: dictionaryEntry ;
BEGIN
i := 1 ;
WHILE i <= HighIndice (dictionary) DO
e := GetIndice (dictionary, i) ;
killEntry (e) ;
INC (i)
END ;
dictionary := KillIndex (dictionary) ;
dictionary := InitIndex (1)
END resetDictionary ;
(*
lookupString - lookup and return a duplicate of the string value for key s.
NIL is returned if the key s is unknown.
*)
PROCEDURE lookupString (s: String) : String ;
VAR
i: CARDINAL ;
e: dictionaryEntry ;
BEGIN
i := 1 ;
WHILE i <= HighIndice (dictionary) DO
e := GetIndice (dictionary, i) ;
IF Equal (e^.key, s)
THEN
RETURN Dup (e^.value)
END ;
INC (i)
END ;
RETURN NIL
END lookupString ;
(*
lookupDefine - looks up the word in the input string (ending with '}').
It uses this word as a key into the dictionary and returns
the entry.
*)
PROCEDURE lookupDefine (VAR eb: errorBlock) : String ;
VAR
s: String ;
BEGIN
s := InitString ('') ;
WHILE (eb.ini < eb.len) AND (char (eb.in, eb.ini) # "}") DO
IF char (eb.in, eb.ini) = "%"
THEN
INC (eb.ini)
END ;
s := ConCatChar (s, char (eb.in, eb.ini)) ;
INC (eb.ini)
END ;
s := lookupString (s) ;
IF s = NIL
THEN
s := InitString ('')
END ;
RETURN s
END lookupDefine ;
(*
processDefine - place contents of dictionary entry name onto the output string.
*)
PROCEDURE processDefine (VAR eb: errorBlock) ;
BEGIN
eb.out := ConCat (eb.out, lookupDefine (eb))
END processDefine ;
(*
lookupColor - looks up the color enum from the string.
*)
PROCEDURE lookupColor (s: String) : colorType ;
BEGIN
IF EqualArray (s, "filename")
THEN
RETURN filenameColor
ELSIF EqualArray (s, "quote")
THEN
RETURN quoteColor
ELSIF EqualArray (s, "error")
THEN
RETURN errorColor
ELSIF EqualArray (s, "warning")
THEN
RETURN warningColor ;
ELSIF EqualArray (s, "note")
THEN
RETURN warningColor ;
ELSIF EqualArray (s, "locus")
THEN
RETURN locusColor
ELSIF EqualArray (s, "insert")
THEN
RETURN insertColor
ELSIF EqualArray (s, "delete")
THEN
RETURN deleteColor
ELSIF EqualArray (s, "type")
THEN
RETURN typeColor
ELSIF EqualArray (s, "range1")
THEN
RETURN range1Color
ELSIF EqualArray (s, "range2")
THEN
RETURN range2Color
END ;
RETURN noColor
END lookupColor ;
(*
readColor -
*)
PROCEDURE readColor (VAR eb: errorBlock) : colorType ;
VAR
s: String ;
c: colorType ;
BEGIN
s := InitString ('') ;
WHILE (eb.ini < eb.len) AND (char (eb.in, eb.ini) # "}") DO
IF char (eb.in, eb.ini) = "%"
THEN
INC (eb.ini)
END ;
s := ConCatChar (s, char (eb.in, eb.ini)) ;
INC (eb.ini)
END ;
c := lookupColor (s) ;
s := KillString (s) ;
RETURN c
END readColor ;
(*
keyword - copy characters until the '}' in the input string and convert them to
the keyword color/font.
*)
PROCEDURE keyword (VAR eb: errorBlock) ;
BEGIN
IF CAP (char (eb.in, eb.ini)) = 'K'
THEN
INC (eb.ini) ;
pushColor (eb) ;
changeColor (eb, keywordColor) ;
WHILE (eb.ini < eb.len) AND (char (eb.in, eb.ini) # "}") DO
IF Debugging
THEN
dump (eb)
END ;
IF char (eb.in, eb.ini) = "%"
THEN
INC (eb.ini)
END ;
copyKeywordChar (eb) ;
INC (eb.ini)
END ;
popColor (eb)
ELSE
InternalError ('expecting index to be on the K for keyword')
END
END keyword ;
(*
filename - copy characters until the '}' in the input string and convert them to
the filename color/font.
*)
PROCEDURE filename (VAR eb: errorBlock) ;
BEGIN
IF CAP (char (eb.in, eb.ini)) = 'F'
THEN
INC (eb.ini) ;
pushColor (eb) ;
changeColor (eb, filenameColor) ;
WHILE (eb.ini < eb.len) AND (char (eb.in, eb.ini) # "}") DO
IF Debugging
THEN
dump (eb)
END ;
IF char (eb.in, eb.ini) = "%"
THEN
INC (eb.ini)
END ;
copyChar (eb) ;
INC (eb.ini)
END ;
popColor (eb)
ELSE
InternalError ('expecting index to be on the F for filename')
END
END filename ;
(*
pushColor -
*)
PROCEDURE pushColor (VAR eb: errorBlock) ;
BEGIN
WITH eb DO
IF stackPtr > MaxStack
THEN
HALT
ELSE
colorStack[stackPtr] := currentCol ;
INC (stackPtr)
END
END
END pushColor ;
(*
popColor -
*)
PROCEDURE popColor (VAR eb: errorBlock) ;
BEGIN
WITH eb DO
IF stackPtr > 0
THEN
DEC (stackPtr)
ELSE
HALT
END ;
currentCol := colorStack[stackPtr] ;
IF currentCol = unsetColor
THEN
currentCol := noColor
END
END
END popColor ;
(*
initErrorBlock - initialise an error block with the, input, string.
*)
PROCEDURE initErrorBlock (VAR eb: errorBlock; input: String; sym: ARRAY OF CARDINAL) ;
BEGIN
WITH eb DO
useError := TRUE ;
e := NIL ;
type := error ; (* default to the error color. *)
out := InitString ('') ;
in := input ;
highplus1 := HIGH (sym) + 1 ;
len := Length (input) ;
ini := 0 ;
glyph := FALSE ; (* nothing to output yet. *)
quotes := TRUE ;
positive := TRUE ;
root := FALSE ;
chain := FALSE ;
currentCol := findColorType (input) ;
beginCol := unsetColor ;
endCol := unsetColor ;
stackPtr := 0
END
END initErrorBlock ;
(*
push - performs a push from the oldblock to the newblock.
It copies all fields except the output string.
*)
PROCEDURE push (VAR newblock: errorBlock; oldblock: errorBlock) ;
BEGIN
pushColor (oldblock) ; (* save the current color. *)
newblock := oldblock ; (* copy all the fields. *)
newblock.out := NIL ; (* must do this before a clear as we have copied the address. *)
clear (newblock) ;
newblock.quotes := TRUE
END push ;
(*
pop - copies contents of oldblock into newblock. It only copies the error
handle if the toblock.e is NIL.
*)
PROCEDURE pop (VAR toblock, fromblock: errorBlock) ;
VAR
c: colorType ;
BEGIN
IF empty (fromblock)
THEN
toblock.stackPtr := fromblock.stackPtr ;
toblock.colorStack := fromblock.colorStack ;
popColor (toblock) (* and restore the color from the push start. *)
ELSE
IF fromblock.quotes
THEN
(* string needs to be quoted. *)
IF toblock.currentCol = unsetColor
THEN
(* caller has not yet assigned a color, so use the callee color at the end. *)
OutOpenQuote (toblock) ;
OutGlyphS (toblock, fromblock.out) ;
OutCloseQuote (toblock) ;
changeColor (toblock, fromblock.currentCol)
ELSE
shutdownColor (fromblock) ;
(* caller has assigned a color, so use it after the new string. *)
c := toblock.currentCol ;
OutOpenQuote (toblock) ;
OutGlyphS (toblock, fromblock.out) ;
OutCloseQuote (toblock) ;
toblock.currentCol := c
END
ELSE
IF toblock.currentCol = unsetColor
THEN
OutGlyphS (toblock, fromblock.out) ;
toblock.endCol := fromblock.endCol ;
changeColor (toblock, fromblock.endCol)
ELSE
pushColor (toblock) ;
OutGlyphS (toblock, fromblock.out) ;
toblock.endCol := fromblock.endCol ;
popColor (toblock)
END
END
END ;
IF toblock.e = NIL
THEN
toblock.e := fromblock.e
END ;
toblock.chain := fromblock.chain ;
toblock.root := fromblock.root ;
toblock.ini := fromblock.ini ;
toblock.type := fromblock.type (* might have been changed by the callee. *)
END pop ;
(*
OutOpenQuote -
*)
PROCEDURE OutOpenQuote (VAR eb: errorBlock) ;
BEGIN
eb.currentCol := noColor ;
flushColor (eb) ;
eb.out := ConCat (eb.out, openQuote (InitString ('')))
END OutOpenQuote ;
(*
OutCloseQuote -
*)
PROCEDURE OutCloseQuote (VAR eb: errorBlock) ;
BEGIN
eb.out := ConCat (eb.out, closeQuote (InitString (''))) ;
eb.currentCol := noColor ;
eb.endCol := noColor
END OutCloseQuote ;
(*
findColorType - return the color of the string. This is determined by the first
occurrance of an error, warning or note marker. An error message
is assumed to either be: a keyword category, error category, note
category, warning category or to be chained from a previous error.
*)
PROCEDURE findColorType (s: String) : colorType ;
VAR
i: CARDINAL ;
BEGIN
i := 0 ;
WHILE i < Length (s) DO
IF char (s, i) = "{"
THEN
INC (i) ;
IF char (s, i) = "%"
THEN
INC (i) ;
WHILE (i < Length (s)) AND (char (s, i) # "}") DO
IF char (s, i) = "%"
THEN
INC (i)
END ;
CASE char (s, i) OF
"K": RETURN errorColor | (* keyword errors start with the fatal error color. *)
"E": RETURN errorColor |
"A": RETURN errorColor |
"O": RETURN noteColor |
"W": RETURN warningColor |
"C": RETURN lastColor
ELSE
END ;
INC (i)
END
END
END ;
INC (i)
END ;
RETURN errorColor (* default to the error color. *)
END findColorType ;
(*
killErrorBlock - deallocates the dynamic strings associated with the error block.
*)
PROCEDURE killErrorBlock (VAR eb: errorBlock) ;
BEGIN
WITH eb DO
out := KillString (out) ;
in := KillString (in)
END
END killErrorBlock ;
(*
ebnf := { percent
| lbra
| any % copy ch %
}
=:
percent := '%' ( "<" | % open quote
">" | % close quote
anych ) % copy anych %
=:
lbra := '{' [ '!' ] percenttoken '}' =:
percenttoken := '%' (
'1' % doOperand(1) %
op
| '2' % doOperand(2) %
op
| '3' % doOperand(3) %
op
| '4' % doOperand(4) %
op
)
=:
op := {'a'|'q'|'t'|'d'|'n'|'s'|'B'|'D'|'F'|'G'|'H'|'M'|'U'|'E'|'V'|'W'|'A'} then =:
then := [ ':' ebnf ] =:
*)
(*
{%1V} set the error message location to the name of the symbol declared.
For example foo: bar
^^^ some error message.
{%1H} set the error message location to the whole declaration of the symbol.
For example foo: bar
^^^^^^^^ some error message.
{%1B} set the error message location to the type declaration of the symbol.
For example foo: bar
^^^ some error message.
*)
(*
InternalFormat - produces an informative internal error.
*)
PROCEDURE InternalFormat (eb: errorBlock; m: ARRAY OF CHAR; line: CARDINAL) ;
BEGIN
printf1 ("M2MetaError.mod:%d:internalformat error detected\n", line) ;
dump (eb) ;
InternalError (m)
END InternalFormat ;
(*
x - checks to see that a=b.
*)
PROCEDURE x (a, b: String) : String ;
BEGIN
IF a # b
THEN
InternalError ('different string returned')
END ;
RETURN a
END x ;
(*
IsWhite - returns TRUE if, ch, is a space.
*)
PROCEDURE IsWhite (ch: CHAR) : BOOLEAN ;
BEGIN
RETURN ch = ' '
END IsWhite ;
(*
skip - skips over this level input until the next '}'.
*)
PROCEDURE skip (VAR sb: errorBlock) ;
VAR
level: INTEGER ;
BEGIN
level := 0 ;
WHILE sb.ini < sb.len DO
IF (level = 0) AND (char (sb.in, sb.ini) = "}")
THEN
RETURN
END ;
IF char (sb.in, sb.ini) = "}"
THEN
DEC (level)
ELSIF char (sb.in, sb.ini) = "{"
THEN
INC (level)
END ;
INC (sb.ini)
END
END skip ;
(*
ifNonNulThen := [ ':' ebnf ] =:
*)
PROCEDURE ifNonNulThen (VAR eb: errorBlock;
sym: ARRAY OF CARDINAL) ;
BEGIN
IF char (eb.in, eb.ini) = ':'
THEN
INC (eb.ini) ;
IF eb.positive
THEN
IF empty (eb) AND (Length (eb.out) # 0)
THEN
printf0 ("inconsistency found\n") ;
dump (eb)
END ;
IF empty (eb)
THEN
IF Debugging
THEN
printf0 ("empty expression, skip\n")
END ;
clear (eb) ;
(* skip over this level of input text. *)
skip (eb)
ELSE
IF Debugging
THEN
dump (eb) ;
printf0 ("non empty expression, clear and continue\n") ;
END ;
clear (eb) ;
IF Debugging
THEN
dump (eb) ;
printf0 ("cleared, continue\n") ;
dump (eb)
END ;
(* carry on processing input text. *)
ebnf (eb, sym) ;
IF Debugging
THEN
printf0 ("evaluated\n") ;
dump (eb)
END
END
ELSE
IF empty (eb)
THEN
clear (eb) ;
(* carry on processing input text. *)
ebnf (eb, sym)
ELSE
clear (eb) ;
(* skip over this level of input text. *)
skip (eb)
END
END ;
IF (eb.ini < eb.len) AND (char (eb.in, eb.ini) # '}')
THEN
InternalFormat (eb, 'expecting to see }', __LINE__)
END
END
END ifNonNulThen ;
(*
doNumber -
*)
PROCEDURE doNumber (VAR eb: errorBlock;
sym: ARRAY OF CARDINAL; bol: CARDINAL) ;
BEGIN
IF empty (eb)
THEN
eb.quotes := FALSE ;
OutGlyphS (eb, ctos (sym[bol], 0, ' '))
END
END doNumber ;
(*
doCount -
*)
PROCEDURE doCount (VAR eb: errorBlock;
sym: ARRAY OF CARDINAL; bol: CARDINAL) ;
BEGIN
IF empty (eb)
THEN
eb.quotes := FALSE ;
OutGlyphS (eb, ctos(sym[bol], 0, ' ')) ;
CASE sym[bol] MOD 100 OF
11..13: OutGlyphS (eb, Mark (InitString ('th')))
ELSE
CASE sym[bol] MOD 10 OF
1: OutGlyphS (eb, Mark (InitString ('st'))) |
2: OutGlyphS (eb, Mark (InitString ('nd'))) |
3: OutGlyphS (eb, Mark (InitString ('rd')))
ELSE
OutGlyphS (eb, Mark (InitString ('th')))
END
END
END
END doCount ;
PROCEDURE doAscii (VAR eb: errorBlock; sym: ARRAY OF CARDINAL; bol: CARDINAL) ;
BEGIN
IF (sym[bol] = NulSym) OR (NOT empty (eb)) OR IsTemporary (sym[bol]) OR IsNameAnonymous (sym[bol])
THEN
RETURN
ELSE
OutGlyphS (eb, InitStringCharStar (KeyToCharStar (GetSymName (sym[bol]))))
END
END doAscii ;
(*
unquotedKeyword -
*)
PROCEDURE unquotedKeyword (VAR eb: errorBlock) ;
BEGIN
eb.quotes := FALSE ;
keyword (eb)
END unquotedKeyword ;
(*
OutArray -
*)
PROCEDURE OutArray (VAR eb: errorBlock; a: ARRAY OF CHAR) ;
BEGIN
OutGlyphS (eb, Mark (InitString (a)))
END OutArray ;
(*
OutGlyphS - outputs a string of glyphs.
*)
PROCEDURE OutGlyphS (VAR eb: errorBlock; s: String) ;
BEGIN
IF Length (s) > 0
THEN
flushColor (eb) ;
checkMe ;
eb.glyph := TRUE ;
eb.out := ConCat (eb.out, s)
END
END OutGlyphS ;
(*
OutColorS - outputs a string of color requests.
*)
(*
PROCEDURE OutColorS (VAR eb: errorBlock; s: String) ;
BEGIN
flushColor (eb) ;
eb.out := ConCat (eb.out, s)
END OutColorS ;
*)
(*
empty - returns TRUE if the output string is empty.
It ignores color changes.
*)
PROCEDURE empty (VAR eb: errorBlock) : BOOLEAN ;
BEGIN
RETURN NOT eb.glyph
END empty ;
(*
clear - remove the output string.
*)
PROCEDURE clear (VAR eb: errorBlock) ;
BEGIN
eb.out := KillString (eb.out) ;
eb.out := InitString ('') ;
eb.glyph := FALSE ;
eb.beginCol := unsetColor ;
eb.quotes := FALSE
END clear ;
PROCEDURE doName (VAR eb: errorBlock;
sym: ARRAY OF CARDINAL; bol: CARDINAL) ;
BEGIN
IF (NOT empty (eb)) OR (sym[bol] = NulSym) OR IsTemporary (sym[bol]) OR IsNameAnonymous (sym[bol])
THEN
RETURN
ELSE
IF sym[bol] = ZType
THEN
eb.quotes := FALSE ;
OutArray (eb, 'the ZType')
ELSIF sym[bol] = RType
THEN
eb.quotes := FALSE ;
OutArray (eb, 'the RType')
ELSE
doAscii (eb, sym, bol)
END
END
END doName ;
PROCEDURE doQualified (VAR eb: errorBlock; sym: ARRAY OF CARDINAL; bol: CARDINAL) ;
VAR
mod: ARRAY [0..1] OF CARDINAL ;
BEGIN
IF (NOT empty (eb)) OR (sym[bol] = NulSym) OR IsTemporary (sym[bol]) OR IsNameAnonymous (sym[bol])
THEN
RETURN
ELSE
mod[0] := GetScope (sym[bol]) ;
IF IsDefImp (mod[0]) AND IsExported (mod[0], sym[bol])
THEN
doAscii (eb, mod, 0) ;
OutArray (eb, '.') ;
OutGlyphS (eb, Mark (InitStringCharStar (KeyToCharStar (GetSymName (sym[bol])))))
ELSE
doAscii (eb, sym, bol)
END
END
END doQualified ;
(*
doType - returns a string containing the type name of
sym.
*)
PROCEDURE doType (VAR eb: errorBlock;
sym: ARRAY OF CARDINAL; bol: CARDINAL) ;
BEGIN
IF (NOT empty (eb)) OR (sym[bol] = NulSym)
THEN
RETURN
ELSE
sym[bol] := GetType (sym[bol]) ;
doAscii (eb, sym, bol)
END
END doType ;
(*
doSkipType - will skip all pseudonym types. It also
returns the type symbol found and name.
*)
PROCEDURE doSkipType (eb: errorBlock; sym: ARRAY OF CARDINAL; bol: CARDINAL) ;
BEGIN
IF (NOT empty (eb)) OR (sym[bol] = NulSym)
THEN
RETURN
ELSE
sym[bol] := SkipType(sym[bol]) ;
WHILE IsType(sym[bol]) AND ((GetSymName (sym[bol]) = NulName) OR
IsNameAnonymous (sym[bol])) DO
sym[bol] := GetType (sym[bol])
END ;
doAscii (eb, sym, bol)
END
END doSkipType ;
(*
doGetType - attempts to get the type of sym[bol].
*)
PROCEDURE doGetType (VAR eb: errorBlock;
VAR sym: ARRAY OF CARDINAL; bol: CARDINAL) ;
BEGIN
IF (bol > HIGH (sym)) OR (NOT empty (eb)) OR (sym[bol] = NulSym)
THEN
RETURN
ELSE
sym[bol] := GetType (sym[bol])
END
END doGetType ;
(*
doGetSkipType - will skip all pseudonym types. It also
returns the type symbol found and name.
*)
PROCEDURE doGetSkipType (VAR eb: errorBlock; VAR sym: ARRAY OF CARDINAL; bol: CARDINAL) ;
VAR
prev: CARDINAL ;
BEGIN
IF (bol > HIGH (sym)) OR (NOT empty (eb)) OR (sym[bol] = NulSym)
THEN
RETURN
ELSE
REPEAT
prev := sym[bol] ;
sym[bol] := SkipType (sym[bol]) ;
IF IsType(sym[bol]) AND ((GetSymName (sym[bol]) = NulName) OR
IsNameAnonymous (sym[bol])) AND
(GetType(sym[bol]) # NulSym)
THEN
sym[bol] := GetType (sym[bol])
END
UNTIL sym[bol] = prev
END
END doGetSkipType ;
(*
doChain -
*)
PROCEDURE doChain (VAR eb: errorBlock; tok: CARDINAL) ;
BEGIN
IF lastRoot=NIL
THEN
InternalError ('should not be chaining an error onto an empty error note')
ELSE
eb.e := ChainError (tok, lastRoot)
END
END doChain ;
(*
doError - creates and returns an error note.
*)
PROCEDURE doError (VAR eb: errorBlock; tok: CARDINAL) ;
BEGIN
IF eb.useError
THEN
chooseError (eb, tok)
END
END doError ;
(*
defaultError - adds the default error location to, tok, if one has not already been
assigned.
*)
PROCEDURE defaultError (VAR eb: errorBlock; tok: CARDINAL) ;
BEGIN
IF eb.e = NIL
THEN
doError (eb, tok)
END
END defaultError ;
(*
chooseError - choose the error kind dependant upon type.
Either an error, warning or note will be generated.
*)
PROCEDURE chooseError (VAR eb: errorBlock; tok: CARDINAL) ;
BEGIN
IF eb.chain
THEN
doChain (eb, tok)
ELSE
CASE eb.type OF
chained: doChain (eb, tok) |
none,
aborta,
error : IF eb.e=NIL
THEN
eb.e := NewError (tok)
ELSE
eb.e := MoveError (eb.e, tok)
END |
warning: IF eb.e=NIL
THEN
eb.e := NewWarning (tok)
ELSE
eb.e := MoveError (eb.e, tok)
END |
note : IF eb.e=NIL
THEN
eb.e := NewNote (tok)
ELSE
eb.e := MoveError (eb.e, tok)
END
ELSE
InternalError ('unexpected enumeration value')
END
END ;
IF eb.root
THEN
lastRoot := eb.e ;
lastColor := findColorType (eb.in)
END ;
eb.e := SetColor (eb.e)
END chooseError ;
(*
doErrorScopeModule -
*)
PROCEDURE doErrorScopeModule (VAR eb: errorBlock; sym: CARDINAL) ;
VAR
scope: CARDINAL ;
BEGIN
scope := GetScope (sym) ;
IF IsModule (scope)
THEN
IF IsInnerModule (scope)
THEN
doError (eb, GetDeclaredMod (sym))
ELSE
doError (eb, GetDeclaredMod (sym))
END
ELSE
Assert (IsDefImp (scope)) ;
(* if this fails then we need to skip to the outer scope.
REPEAT
OuterModule := GetScope(OuterModule)
UNTIL GetScope(OuterModule)=NulSym. *)
IF GetDeclaredModule (sym) = UnknownTokenNo
THEN
doError (eb, GetDeclaredDef (sym))
ELSE
doError (eb, GetDeclaredMod (sym))
END
END
END doErrorScopeModule ;
(*
doErrorScopeForward -
*)
PROCEDURE doErrorScopeForward (VAR eb: errorBlock; sym: CARDINAL) ;
VAR
scope: CARDINAL ;
BEGIN
scope := GetScope (sym) ;
IF IsModule (scope)
THEN
IF IsInnerModule (scope)
THEN
doError (eb, GetDeclaredFor (sym))
ELSE
doError (eb, GetDeclaredFor (sym))
END
ELSE
Assert (IsDefImp (scope)) ;
(* if this fails then we need to skip to the outer scope.
REPEAT
OuterModule := GetScope(OuterModule)
UNTIL GetScope(OuterModule)=NulSym. *)
IF GetDeclaredModule (sym) = UnknownTokenNo
THEN
doError (eb, GetDeclaredDef (sym))
ELSE
doError (eb, GetDeclaredFor (sym))
END
END
END doErrorScopeForward ;
(*
doErrorScopeMod - potentially create an error referring to the definition
module, fall back to the implementation or program module if
there is no declaration in the definition module.
*)
PROCEDURE doErrorScopeMod (VAR eb: errorBlock; sym: CARDINAL) ;
VAR
scope: CARDINAL ;
BEGIN
scope := GetScope (sym) ;
IF scope = NulSym
THEN
M2Error.EnterErrorScope (NIL) ;
doError (eb, GetDeclaredMod (sym))
ELSE
M2Error.EnterErrorScope (GetErrorScope (scope)) ;
IF IsProcedure (scope)
THEN
doError (eb, GetDeclaredMod (sym))
ELSE
doErrorScopeModule (eb, sym)
END
END ;
M2Error.LeaveErrorScope
END doErrorScopeMod ;
(*
doErrorScopeFor - potentially create an error referring to the
forward declaration, definition module, fall back
to the implementation or program module if
there is no declaration in the definition module.
*)
PROCEDURE doErrorScopeFor (VAR eb: errorBlock; sym: CARDINAL) ;
VAR
scope: CARDINAL ;
BEGIN
scope := GetScope (sym) ;
IF scope = NulSym
THEN
M2Error.EnterErrorScope (NIL) ;
doError (eb, GetDeclaredFor (sym))
ELSE
M2Error.EnterErrorScope (GetErrorScope (scope)) ;
IF IsProcedure (scope)
THEN
doError (eb, GetDeclaredFor (sym))
ELSE
doErrorScopeForward (eb, sym)
END
END ;
M2Error.LeaveErrorScope
END doErrorScopeFor ;
(*
doDeclaredMod - creates an error note where sym[bol] was declared.
*)
PROCEDURE declaredMod (VAR eb: errorBlock; sym: ARRAY OF CARDINAL; bol: CARDINAL) ;
BEGIN
IF bol <= HIGH (sym)
THEN
doErrorScopeMod (eb, sym[bol])
END
END declaredMod ;
(*
doErrorScopeDefinition - use the declaration in the definitio module if one is available.
*)
PROCEDURE doErrorScopeDefinition (VAR eb: errorBlock; sym: CARDINAL) ;
VAR
scope: CARDINAL ;
BEGIN
scope := GetScope (sym) ;
IF IsModule (scope)
THEN
(* No definition module for a program module. *)
doError (eb, GetDeclaredMod (sym))
ELSE
Assert (IsDefImp (scope)) ;
IF GetDeclaredDefinition (sym) = UnknownTokenNo
THEN
(* Fall back to the implementation module if no declaration exists
in the definition module. *)
doError (eb, GetDeclaredMod (sym))
ELSE
doError (eb, GetDeclaredDef (sym))
END
END
END doErrorScopeDefinition ;
(*
doErrorScopeDef - potentially create an error referring to the definition
module, fall back to the implementation or program module if
there is no declaration in the definition module.
*)
PROCEDURE doErrorScopeDef (VAR eb: errorBlock; sym: CARDINAL) ;
VAR
scope: CARDINAL ;
BEGIN
scope := GetScope (sym) ;
IF scope = NulSym
THEN
M2Error.EnterErrorScope (NIL) ;
doError (eb, GetDeclaredFor (sym))
ELSE
M2Error.EnterErrorScope (GetErrorScope (scope)) ;
IF IsProcedure (scope)
THEN
doError (eb, GetDeclaredDef (sym))
ELSE
doErrorScopeDefinition (eb, sym)
END
END ;
M2Error.LeaveErrorScope
END doErrorScopeDef ;
(*
doDeclaredDef - creates an error note where sym[bol] was declared.
*)
PROCEDURE declaredDef (VAR eb: errorBlock; sym: ARRAY OF CARDINAL; bol: CARDINAL) ;
BEGIN
IF bol <= HIGH (sym)
THEN
doErrorScopeDef (eb, sym[bol])
END
END declaredDef ;
(*
doDeclaredFor - creates an error note where sym[bol] was declared.
*)
PROCEDURE declaredFor (VAR eb: errorBlock; sym: ARRAY OF CARDINAL; bol: CARDINAL) ;
BEGIN
IF bol <= HIGH (sym)
THEN
doErrorScopeFor (eb, sym[bol])
END
END declaredFor ;
(*
doErrorScopeProc - determine the location for the error or warning from
the default declaration. For example parameters can be
declared in definition, forward or in modules (proper procedure).
Use GetVarParamTok to obtain a variable or parameter location.
*)
PROCEDURE doErrorScopeProc (VAR eb: errorBlock; sym: CARDINAL;
GetVarParamTok: GetTokProcedure) ;
VAR
scope: CARDINAL ;
BEGIN
scope := GetScope (sym) ;
IF scope = NulSym
THEN
M2Error.EnterErrorScope (NIL) ;
doError (eb, GetDeclaredDef (sym))
ELSE
M2Error.EnterErrorScope (GetErrorScope (scope)) ;
IF IsVar (sym) OR IsParameter (sym)
THEN
doError (eb, GetVarParamTok (sym))
ELSIF IsProcedure (scope)
THEN
doError (eb, GetDeclaredDef (sym))
ELSIF IsModule (scope)
THEN
doError (eb, GetDeclaredMod (sym))
ELSE
Assert (IsDefImp (scope)) ;
IF GetDeclaredDefinition (sym) = UnknownTokenNo
THEN
doError (eb, GetDeclaredMod (sym))
ELSE
doError (eb, GetDeclaredDef (sym))
END
END
END ;
M2Error.LeaveErrorScope
END doErrorScopeProc ;
(*
doDeclaredVar - creates an error note where sym[bol] was declared.
*)
PROCEDURE declaredVar (VAR eb: errorBlock; sym: ARRAY OF CARDINAL; bol: CARDINAL) ;
BEGIN
IF bol <= HIGH (sym)
THEN
doErrorScopeProc (eb, sym[bol], GetVarDeclTok)
END
END declaredVar ;
(*
doDeclaredType - creates an error note where sym[bol] was declared.
*)
PROCEDURE declaredType (VAR eb: errorBlock; sym: ARRAY OF CARDINAL; bol: CARDINAL) ;
BEGIN
IF bol <= HIGH (sym)
THEN
doErrorScopeProc (eb, sym[bol], GetVarDeclTypeTok)
END
END declaredType ;
(*
doDeclaredFull - creates an error note where sym[bol] was declared.
*)
PROCEDURE declaredFull (VAR eb: errorBlock; sym: ARRAY OF CARDINAL; bol: CARDINAL) ;
BEGIN
IF bol <= HIGH (sym)
THEN
doErrorScopeProc (eb, sym[bol], GetVarDeclFullTok)
END
END declaredFull ;
(*
used - creates an error note where sym[bol] was first used.
*)
PROCEDURE used (VAR eb: errorBlock; sym: ARRAY OF CARDINAL; bol: CARDINAL) ;
BEGIN
IF bol <= HIGH (sym)
THEN
doError (eb, GetFirstUsed (sym[bol]))
END
END used ;
(*
ConCatWord - joins sentances, a, b, together.
*)
(*
PROCEDURE ConCatWord (a, b: String) : String ;
BEGIN
IF (Length (a) = 1) AND (char(a, 0) = 'a')
THEN
a := x (a, ConCatChar (a, 'n'))
ELSIF (Length (a) > 1) AND (char (a, -1) = 'a') AND IsWhite (char(a, -2))
THEN
a := x (a, ConCatChar (a, 'n'))
END ;
IF (Length (a) > 0) AND (NOT IsWhite (char (a, -1)))
THEN
a := x (a, ConCatChar (a, ' '))
END ;
RETURN x (a, ConCat(a, b))
END ConCatWord ;
*)
(*
symDesc -
*)
PROCEDURE symDesc (sym: CARDINAL) : String ;
BEGIN
IF IsConstLit (sym)
THEN
RETURN InitString ('constant literal')
ELSIF IsConstSet (sym)
THEN
RETURN InitString ('constant set')
ELSIF IsConstructor (sym)
THEN
RETURN InitString ('constructor')
ELSIF IsConst(sym)
THEN
RETURN InitString('constant')
ELSIF IsArray(sym)
THEN
RETURN InitString('array')
ELSIF IsVar(sym)
THEN
IF IsTemporary (sym)
THEN
RETURN InitString('expression')
ELSE
RETURN InitString('variable')
END
ELSIF IsEnumeration(sym)
THEN
RETURN InitString('enumeration type')
ELSIF IsFieldEnumeration(sym)
THEN
RETURN InitString('enumeration field')
ELSIF IsUnbounded(sym)
THEN
RETURN InitString('unbounded parameter')
ELSIF IsProcType(sym)
THEN
RETURN InitString('procedure type')
ELSIF IsPseudoBaseFunction (sym)
THEN
RETURN InitString('standard function procedure')
ELSIF IsPseudoBaseProcedure (sym)
THEN
RETURN InitString('standard procedure')
ELSIF IsProcedure(sym)
THEN
RETURN InitString('procedure')
ELSIF IsPointer(sym)
THEN
RETURN InitString('pointer')
ELSIF IsParameter(sym)
THEN
IF IsParameterVar(sym)
THEN
RETURN InitString('var parameter')
ELSE
RETURN InitString('parameter')
END
ELSIF IsType(sym)
THEN
IF IsHiddenType (sym)
THEN
RETURN InitString('opaque type')
ELSE
RETURN InitString('type')
END
ELSIF IsRecord(sym)
THEN
RETURN InitString('record')
ELSIF IsRecordField(sym)
THEN
RETURN InitString('record field')
ELSIF IsVarient(sym)
THEN
RETURN InitString('varient record')
ELSIF IsModule(sym)
THEN
RETURN InitString('module')
ELSIF IsDefImp(sym)
THEN
RETURN InitString('definition or implementation module')
ELSIF IsSet(sym)
THEN
RETURN InitString('set')
ELSIF IsUnknown(sym)
THEN
RETURN InitString('an unknown')
ELSIF IsSubrange(sym)
THEN
RETURN InitString('subrange')
ELSE
RETURN InitString ('')
END
END symDesc ;
(*
doDesc -
*)
PROCEDURE doDesc (VAR eb: errorBlock;
sym: ARRAY OF CARDINAL; bol: CARDINAL) ;
BEGIN
IF empty (eb)
THEN
OutGlyphS (eb, symDesc (sym[bol])) ;
IF NOT empty (eb)
THEN
eb.quotes := FALSE
END
END
END doDesc ;
(*
copySym - copies, n+1, symbols, from, ->, to.
*)
(*
PROCEDURE copySym (from: ARRAY OF CARDINAL; VAR to: ARRAY OF CARDINAL; n: CARDINAL) ;
VAR
i: CARDINAL ;
BEGIN
IF n>HIGH(to)
THEN
InternalError ('not enough room in the destination array')
ELSE
i := 0 ;
WHILE i<=n DO
to[i] := from[i] ;
INC(i)
END
END
END copySym ;
*)
(*
op := {'!'|'a'|'c'|'d'|'k'|'n'|'p'|'q'|'s'|'t'|'u'|
'A'|'B'|'C'|'D'|'E'|'F'|'G'|'H'|'K'|'M'|'N'|
'O'|'P'|'Q'|'R'|'S'|'T'|'U'|'V'|'W'|'X'|'Y'|'Z'} then =:
*)
PROCEDURE op (VAR eb: errorBlock;
sym: ARRAY OF CARDINAL; bol: CARDINAL) ;
BEGIN
WHILE (eb.ini < eb.len) AND (char (eb.in, eb.ini) # '}') DO
IF Debugging
THEN
printf0 ("while loop in op\n") ;
dump (eb)
END ;
CASE char (eb.in, eb.ini) OF
'!': eb.positive := NOT eb.positive |
'a': doName (eb, sym, bol) |
'c': eb.currentCol := readColor (eb) ;
DEC (eb.ini) |
'd': doDesc (eb, sym, bol) |
'k': unquotedKeyword (eb) ;
DEC (eb.ini) |
'n': doNumber (eb, sym, bol) |
'p': popColor (eb) |
'q': doQualified (eb, sym, bol) |
's': doSkipType (eb, sym, bol) |
't': doType (eb, sym, bol) |
'u': eb.quotes := FALSE |
'A': eb.type := aborta ;
seenAbort := TRUE |
'B': declaredType (eb, sym, bol) |
'C': eb.chain := TRUE |
'D': declaredDef (eb, sym, bol) |
'E': eb.type := error |
'F': filename (eb) ;
DEC (eb.ini) |
'G': declaredFor (eb, sym, bol) |
'H': declaredFull (eb, sym, bol) |
'K': keyword (eb) ;
DEC (eb.ini) |
'M': declaredMod (eb, sym, bol) |
'N': doCount (eb, sym, bol) |
'O': eb.type := note |
'P': pushColor (eb) |
'Q': resetDictionary |
'R': eb.root := TRUE |
'S': doGetSkipType (eb, sym, bol) |
'T': doGetType (eb, sym, bol) |
'U': used (eb, sym, bol) |
'V': declaredVar (eb, sym, bol) |
'W': eb.type := warning |
'X': pushOutput (eb) |
'Y': processDefine (eb) |
'Z': popOutput (eb) |
':': ifNonNulThen (eb, sym) ;
DEC (eb.ini) |
'1': InternalError ('incorrect format spec, expecting %1 rather than % spec 1') |
'2': InternalError ('incorrect format spec, expecting %2 rather than % spec 2') |
'3': InternalError ('incorrect format spec, expecting %3 rather than % spec 3') |
'4': InternalError ('incorrect format spec, expecting %4 rather than % spec 4')
ELSE
InternalFormat (eb, 'expecting one of [akqtdnpsuCDEFGKNOPQRSTUWXYZ:<>%]', __LINE__)
END ;
INC (eb.ini)
END ;
IF Debugging
THEN
printf0 ("finishing op\n") ;
dump (eb)
END
END op ;
(*
percenttoken := '%' (
'1' % doOperand(1) %
op
| '2' % doOperand(2) %
op
| '3' % doOperand(3) %
op
| '4' % doOperand(4) %
op
)
} =:
*)
PROCEDURE percenttoken (VAR eb: errorBlock; sym: ARRAY OF CARDINAL) ;
BEGIN
IF char (eb.in, eb.ini) = '%'
THEN
INC (eb.ini) ;
CASE char (eb.in, eb.ini) OF
'1': INC (eb.ini) ;
op (eb, sym, 0) |
'2': INC (eb.ini) ;
op (eb, sym, 1) |
'3': INC (eb.ini) ;
op (eb, sym, 2) |
'4': INC (eb.ini) ;
op (eb, sym, 3)
ELSE
op (eb, sym, 0)
END ;
IF (eb.ini < eb.len) AND (char (eb.in, eb.ini) # '}')
THEN
InternalFormat (eb, 'expecting to see }', __LINE__)
END
END
END percenttoken ;
(*
changeColor - changes to color, c.
*)
PROCEDURE changeColor (VAR eb: errorBlock; c: colorType) ;
BEGIN
eb.currentCol := c
END changeColor ;
(*
shutdownColor - shutdown existing color if it exists.
*)
PROCEDURE shutdownColor (VAR eb: errorBlock) ;
BEGIN
IF (eb.endCol # unsetColor) AND (eb.endCol # noColor)
THEN
eb.out := colorEnd (eb.out) ;
eb.endCol := noColor
END
END shutdownColor ;
(*
flushColor - flushes any outstanding color change.
*)
PROCEDURE flushColor (VAR eb: errorBlock) ;
BEGIN
IF eb.endCol # eb.currentCol
THEN
shutdownColor (eb) ;
IF eb.endCol # eb.currentCol
THEN
emitColor (eb, eb.currentCol) ;
eb.endCol := eb.currentCol
END ;
IF eb.beginCol = unsetColor
THEN
eb.beginCol := eb.currentCol
END
END
END flushColor ;
(*
emitColorGCC -
*)
PROCEDURE emitColorGCC (VAR eb: errorBlock; c: colorType) ;
BEGIN
CASE c OF
unsetColor : |
noColor : eb.out := M2ColorString.endColor (eb.out) |
quoteColor : eb.out := M2ColorString.quoteColor (eb.out) |
filenameColor: eb.out := M2ColorString.filenameColor (eb.out) |
errorColor : eb.out := M2ColorString.errorColor (eb.out) |
warningColor : eb.out := M2ColorString.warningColor (eb.out) |
noteColor : eb.out := M2ColorString.noteColor (eb.out) |
keywordColor : eb.out := M2ColorString.locusColor (eb.out) |
locusColor : eb.out := M2ColorString.locusColor (eb.out) |
insertColor : eb.out := M2ColorString.insertColor (eb.out) |
deleteColor : eb.out := M2ColorString.deleteColor (eb.out) |
typeColor : eb.out := M2ColorString.typeColor (eb.out) |
range1Color : eb.out := M2ColorString.range1Color (eb.out) |
range2Color : eb.out := M2ColorString.range2Color (eb.out)
END
END emitColorGCC ;
(*
emitColorTag -
*)
PROCEDURE emitColorTag (VAR eb: errorBlock; c: colorType) ;
VAR
s: String ;
BEGIN
CASE c OF
unsetColor : s := InitString ('<unset>') |
noColor : s := InitString ('<nocol>') ; stop |
quoteColor : s := InitString ('<quote>') |
filenameColor: s := InitString ('<filename>') |
errorColor : s := InitString ('<error>') |
warningColor : s := InitString ('<warn>') |
noteColor : s := InitString ('<note>') |
keywordColor : s := InitString ('<key>') |
locusColor : s := InitString ('<locus>') |
insertColor : s := InitString ('<insert>') |
deleteColor : s := InitString ('<delete>') |
typeColor : s := InitString ('<type>') |
range1Color : s := InitString ('<range1>') |
range2Color : s := InitString ('<range2>')
END ;
eb.out := ConCat (eb.out, Mark (s))
END emitColorTag ;
(*
emitColor - adds the appropriate color string to the output string.
*)
PROCEDURE emitColor (VAR eb: errorBlock; c: colorType) ;
BEGIN
IF ColorDebug
THEN
emitColorTag (eb, c)
ELSE
emitColorGCC (eb, c)
END
END emitColor ;
(*
openQuote -
*)
PROCEDURE openQuote (s: String) : String ;
BEGIN
IF ColorDebug
THEN
RETURN ConCat (s, Mark (InitString ('<openquote>')))
ELSE
RETURN M2ColorString.quoteOpen (s)
END
END openQuote ;
(*
closeQuote -
*)
PROCEDURE closeQuote (s: String) : String ;
BEGIN
IF ColorDebug
THEN
RETURN ConCat (s, Mark (InitString ('<closequote>')))
ELSE
RETURN M2ColorString.quoteClose (s)
END
END closeQuote ;
(*
colorEnd -
*)
PROCEDURE colorEnd (s: String) : String ;
BEGIN
stop ;
IF ColorDebug
THEN
RETURN ConCat (s, Mark (InitString ('<nocol>')))
ELSE
RETURN M2ColorString.endColor (s)
END
END colorEnd ;
(*
copyChar - copies a character from in string to out string.
*)
PROCEDURE copyChar (VAR eb: errorBlock) ;
BEGIN
IF eb.ini < eb.len
THEN
flushColor (eb) ;
checkMe ;
eb.glyph := TRUE ;
eb.out := x (eb.out, ConCatChar (eb.out, char (eb.in, eb.ini)))
END
END copyChar ;
(*
copyKeywordChar - copies a character from in string to out string
it will convert the character to lower case if the
-fm2-lower-case option was specified.
*)
PROCEDURE copyKeywordChar (VAR eb: errorBlock) ;
VAR
ch: CHAR ;
BEGIN
IF eb.ini < eb.len
THEN
flushColor (eb) ;
ch := char (eb.in, eb.ini) ;
IF LowerCaseKeywords
THEN
ch := Lower (ch)
END ;
eb.glyph := TRUE ;
eb.out := x (eb.out, ConCatChar (eb.out, ch))
END
END copyKeywordChar ;
(*
percent := '%' anych % copy anych %
=:
*)
PROCEDURE percent (VAR eb: errorBlock; sym: ARRAY OF CARDINAL) ;
BEGIN
IF char (eb.in, eb.ini)='%'
THEN
INC (eb.ini) ;
IF eb.ini < eb.len
THEN
IF char (eb.in, eb.ini) = '<'
THEN
(* %< is a quotation symbol. *)
pushColor (eb) ;
eb.currentCol := noColor ;
flushColor (eb) ;
changeColor (eb, quoteColor) ;
eb.endCol := quoteColor ; (* the openQuote will change the color. *)
(* OutGlyphS performs a flush and we are emitting the open quote glyph. *)
OutGlyphS (eb, openQuote (InitString ('')))
ELSIF char (eb.in, eb.ini) = '>'
THEN
OutGlyphS (eb, closeQuote (InitString (''))) ;
eb.endCol := noColor ; (* closeQuote also turns off color. *)
popColor (eb)
ELSE
copyChar (eb)
END
END
END
END percent ;
(*
lbra := '{' [ '!' ] percenttoken '}' =:
*)
PROCEDURE lbra (VAR eb: errorBlock; sym: ARRAY OF CARDINAL) ;
BEGIN
IF char (eb.in, eb.ini) = '{'
THEN
eb.positive := TRUE ;
INC (eb.ini) ;
IF char (eb.in, eb.ini) = '!'
THEN
eb.positive := FALSE ;
INC (eb.ini)
END ;
IF char (eb.in, eb.ini) # '%'
THEN
InternalFormat (eb, 'expecting to see %', __LINE__)
END ;
percenttoken (eb, sym) ;
IF (eb.ini < eb.len) AND (char (eb.in, eb.ini) # '}')
THEN
InternalFormat (eb, 'expecting to see }', __LINE__)
END
END
END lbra ;
PROCEDURE stop ; BEGIN END stop ;
PROCEDURE checkMe ; BEGIN END checkMe ;
(*
dumpErrorType -
*)
PROCEDURE dumpErrorType (e: errorType) ;
BEGIN
CASE e OF
none : printf0 ("none") |
error : printf0 ("error") |
warning: printf0 ("warning") |
note : printf0 ("note") |
chained: printf0 ("chained") |
aborta : printf0 ("abort")
END
END dumpErrorType ;
(*
dumpColorType -
*)
PROCEDURE dumpColorType (c: colorType) ;
BEGIN
CASE c OF
unsetColor : printf0 ("unsetColor") |
noColor : printf0 ("noColor") |
quoteColor : printf0 ("quoteColor") |
filenameColor: printf0 ("filenameColor") |
errorColor : printf0 ("errorColor") |
warningColor : printf0 ("warningColor") |
noteColor : printf0 ("noteColor") |
keywordColor : printf0 ("keywordColor") |
locusColor : printf0 ("locusColor") |
insertColor : printf0 ("insertColor") |
deleteColor : printf0 ("deleteColor") |
typeColor : printf0 ("typeColor") |
range1Color : printf0 ("range1Color") |
range2Color : printf0 ("range2Color")
END
END dumpColorType ;
(*
dump -
*)
PROCEDURE dump (eb: errorBlock) ;
VAR
ch: CHAR ;
l : CARDINAL ;
i : INTEGER ;
BEGIN
l := Length (eb.out) ;
printf0 ("\n\nerrorBlock\n") ;
printf0 ("\ntype = ") ; dumpErrorType (eb.type) ;
printf1 ("\nout = |%s|", eb.out) ;
printf1 ("\nin = |%s|", eb.in) ;
printf1 ("\nLength (out) = %d", l) ;
printf1 ("\nlen = %d", eb.len) ;
printf1 ("\nhighplus1 = %d", eb.highplus1) ;
printf1 ("\nglyph = %d", eb.glyph) ;
printf1 ("\nquotes = %d", eb.quotes) ;
printf1 ("\npositive = %d", eb.positive) ;
printf0 ("\nbeginCol = ") ; dumpColorType (eb.beginCol) ;
printf0 ("\nendCol = ") ; dumpColorType (eb.endCol) ;
printf0 ("\ncurrentCol = ") ; dumpColorType (eb.currentCol) ;
printf1 ("\nini = %d", eb.ini) ;
IF eb.ini < eb.len
THEN
ch := char (eb.in, eb.ini) ;
printf1 ("\ncurrent char = %c", ch) ;
printf1 ("\n%s\n", eb.in) ;
i := 0 ;
WHILE i<eb.ini DO
printf0 (" ") ;
INC (i)
END ;
printf0 ("^\n")
END ;
printf0 ("\n")
END dump ;
(*
ebnf := { percent
| lbra
| any % copy ch %
}
=:
*)
PROCEDURE ebnf (VAR eb: errorBlock; sym: ARRAY OF CARDINAL) ;
VAR
nb: errorBlock ;
BEGIN
IF Debugging
THEN
printf0 ("top of ebnf\n") ;
dump (eb)
END ;
WHILE eb.ini < eb.len DO
IF Debugging
THEN
printf0 ("while loop ebnf\n") ;
dump (eb)
END ;
CASE char (eb.in, eb.ini) OF
'!': eb.positive := NOT eb.positive |
'%': percent (eb, sym) |
'{': push (nb, eb) ;
lbra (nb, sym) ;
pop (eb, nb) ;
IF (eb.ini < eb.len) AND (char (eb.in, eb.ini) # '}')
THEN
InternalFormat (eb, 'expecting to see }', __LINE__)
END |
'}': RETURN
ELSE
IF ((IsWhite (char (eb.in, eb.ini)) AND (Length (eb.out) > 0) AND
(NOT IsWhite (char (eb.out, -1)))) OR
(NOT IsWhite (char (eb.in, eb.ini)))) AND (eb.highplus1 > 0)
THEN
eb.quotes := FALSE ; (* copying a normal character, don't quote the result. *)
copyChar (eb)
END
END ;
INC (eb.ini)
END ;
eb.currentCol := noColor ;
flushColor (eb) ;
IF Debugging
THEN
printf0 ("finishing ebnf\n") ;
dump (eb)
END
END ebnf ;
PROCEDURE MetaErrorStringT0 (tok: CARDINAL; m: String) ;
VAR
eb : errorBlock ;
sym: ARRAY [0..0] OF CARDINAL ;
BEGIN
sym[0] := NulSym ;
initErrorBlock (eb, m, sym) ;
ebnf (eb, sym) ;
flushColor (eb) ;
defaultError (eb, tok) ;
ErrorString (eb.e, Dup (eb.out)) ;
killErrorBlock (eb) ;
checkAbort
END MetaErrorStringT0 ;
PROCEDURE MetaErrorT0 (tok: CARDINAL; m: ARRAY OF CHAR) ;
BEGIN
MetaErrorStringT0 (tok, InitString(m))
END MetaErrorT0 ;
PROCEDURE MetaErrorStringT1 (tok: CARDINAL; m: String; s: CARDINAL) ;
VAR
eb : errorBlock ;
sym: ARRAY [0..0] OF CARDINAL ;
BEGIN
sym[0] := s ;
initErrorBlock (eb, m, sym) ;
ebnf (eb, sym) ;
flushColor (eb) ;
defaultError (eb, tok) ;
ErrorString (eb.e, Dup (eb.out)) ;
killErrorBlock (eb) ;
checkAbort
END MetaErrorStringT1 ;
PROCEDURE MetaErrorT1 (tok: CARDINAL; m: ARRAY OF CHAR; s: CARDINAL) ;
BEGIN
MetaErrorStringT1 (tok, InitString (m), s)
END MetaErrorT1 ;
PROCEDURE MetaErrorStringT2 (tok: CARDINAL; m: String; s1, s2: CARDINAL) ;
VAR
eb : errorBlock ;
sym: ARRAY [0..1] OF CARDINAL ;
BEGIN
sym[0] := s1 ;
sym[1] := s2 ;
initErrorBlock (eb, m, sym) ;
ebnf (eb, sym) ;
flushColor (eb) ;
defaultError (eb, tok) ;
ErrorString (eb.e, Dup (eb.out)) ;
killErrorBlock (eb) ;
checkAbort
END MetaErrorStringT2 ;
PROCEDURE MetaErrorT2 (tok: CARDINAL; m: ARRAY OF CHAR; s1, s2: CARDINAL) ;
BEGIN
MetaErrorStringT2 (tok, InitString (m), s1, s2)
END MetaErrorT2 ;
PROCEDURE MetaErrorStringT3 (tok: CARDINAL; m: String; s1, s2, s3: CARDINAL) ;
VAR
eb : errorBlock ;
sym: ARRAY [0..2] OF CARDINAL ;
BEGIN
sym[0] := s1 ;
sym[1] := s2 ;
sym[2] := s3 ;
initErrorBlock (eb, m, sym) ;
eb.highplus1 := HIGH (sym) + 1 ;
ebnf (eb, sym) ;
flushColor (eb) ;
defaultError (eb, tok) ;
ErrorString (eb.e, Dup (eb.out)) ;
killErrorBlock (eb) ;
checkAbort
END MetaErrorStringT3 ;
PROCEDURE MetaErrorT3 (tok: CARDINAL; m: ARRAY OF CHAR; s1, s2, s3: CARDINAL) ;
BEGIN
MetaErrorStringT3 (tok, InitString (m), s1, s2, s3) ;
END MetaErrorT3 ;
PROCEDURE MetaErrorStringT4 (tok: CARDINAL; m: String; s1, s2, s3, s4: CARDINAL) ;
VAR
eb : errorBlock ;
sym: ARRAY [0..3] OF CARDINAL ;
BEGIN
sym[0] := s1 ;
sym[1] := s2 ;
sym[2] := s3 ;
sym[3] := s4 ;
initErrorBlock (eb, m, sym) ;
ebnf (eb, sym) ;
flushColor (eb) ;
defaultError (eb, tok) ;
ErrorString (eb.e, Dup (eb.out)) ;
killErrorBlock (eb) ;
checkAbort
END MetaErrorStringT4 ;
PROCEDURE MetaErrorT4 (tok: CARDINAL; m: ARRAY OF CHAR; s1, s2, s3, s4: CARDINAL) ;
BEGIN
MetaErrorStringT4 (tok, InitString (m), s1, s2, s3, s4) ;
END MetaErrorT4 ;
PROCEDURE MetaError0 (m: ARRAY OF CHAR) ;
BEGIN
MetaErrorT0 (GetTokenNo (), m)
END MetaError0 ;
PROCEDURE MetaError1 (m: ARRAY OF CHAR; s: CARDINAL) ;
BEGIN
MetaErrorT1 (GetTokenNo (), m, s)
END MetaError1 ;
PROCEDURE MetaError2 (m: ARRAY OF CHAR; s1, s2: CARDINAL) ;
BEGIN
MetaErrorT2 (GetTokenNo (), m, s1, s2)
END MetaError2 ;
PROCEDURE MetaError3 (m: ARRAY OF CHAR; s1, s2, s3: CARDINAL) ;
BEGIN
MetaErrorT3 (GetTokenNo (), m, s1, s2, s3)
END MetaError3 ;
PROCEDURE MetaError4 (m: ARRAY OF CHAR; s1, s2, s3, s4: CARDINAL) ;
BEGIN
MetaErrorT4 (GetTokenNo (), m, s1, s2, s3, s4)
END MetaError4 ;
(*
wrapErrors -
*)
PROCEDURE wrapErrors (tok: CARDINAL;
m1, m2: ARRAY OF CHAR;
sym: ARRAY OF CARDINAL) ;
VAR
eb: errorBlock ;
BEGIN
initErrorBlock (eb, InitString (m1), sym) ;
ebnf (eb, sym) ;
flushColor (eb) ;
defaultError (eb, tok) ;
lastRoot := eb.e ;
ErrorString (eb.e, Dup (eb.out)) ;
killErrorBlock (eb) ;
initErrorBlock (eb, InitString (m2), sym) ;
eb.type := chained ;
ebnf (eb, sym) ;
flushColor (eb) ;
defaultError (eb, tok) ;
ErrorString (eb.e, Dup (eb.out)) ;
killErrorBlock (eb)
END wrapErrors ;
PROCEDURE MetaErrorsT1 (tok: CARDINAL; m1, m2: ARRAY OF CHAR; s: CARDINAL) ;
VAR
sym: ARRAY [0..0] OF CARDINAL ;
BEGIN
sym[0] := s ;
wrapErrors (tok, m1, m2, sym)
END MetaErrorsT1 ;
PROCEDURE MetaErrorsT2 (tok: CARDINAL; m1, m2: ARRAY OF CHAR; s1, s2: CARDINAL) ;
VAR
sym: ARRAY [0..1] OF CARDINAL ;
BEGIN
sym[0] := s1 ;
sym[1] := s2 ;
wrapErrors (tok, m1, m2, sym)
END MetaErrorsT2 ;
PROCEDURE MetaErrorsT3 (tok: CARDINAL; m1, m2: ARRAY OF CHAR; s1, s2, s3: CARDINAL) ;
VAR
sym : ARRAY [0..2] OF CARDINAL ;
BEGIN
sym[0] := s1 ;
sym[1] := s2 ;
sym[2] := s3 ;
wrapErrors (tok, m1, m2, sym)
END MetaErrorsT3 ;
PROCEDURE MetaErrorsT4 (tok: CARDINAL; m1, m2: ARRAY OF CHAR; s1, s2, s3, s4: CARDINAL) ;
VAR
sym : ARRAY [0..3] OF CARDINAL ;
BEGIN
sym[0] := s1 ;
sym[1] := s2 ;
sym[2] := s3 ;
sym[3] := s4 ;
wrapErrors (tok, m1, m2, sym)
END MetaErrorsT4 ;
PROCEDURE MetaErrors1 (m1, m2: ARRAY OF CHAR; s: CARDINAL) ;
BEGIN
MetaErrorsT1 (GetTokenNo (), m1, m2, s)
END MetaErrors1 ;
PROCEDURE MetaErrors2 (m1, m2: ARRAY OF CHAR; s1, s2: CARDINAL) ;
BEGIN
MetaErrorsT2 (GetTokenNo (), m1, m2, s1, s2)
END MetaErrors2 ;
PROCEDURE MetaErrors3 (m1, m2: ARRAY OF CHAR; s1, s2, s3: CARDINAL) ;
BEGIN
MetaErrorsT3 (GetTokenNo (), m1, m2, s1, s2, s3)
END MetaErrors3 ;
PROCEDURE MetaErrors4 (m1, m2: ARRAY OF CHAR; s1, s2, s3, s4: CARDINAL) ;
BEGIN
MetaErrorsT4 (GetTokenNo (), m1, m2, s1, s2, s3, s4)
END MetaErrors4 ;
PROCEDURE MetaErrorString0 (m: String) ;
BEGIN
MetaErrorStringT0 (GetTokenNo (), m)
END MetaErrorString0 ;
PROCEDURE MetaErrorString1 (m: String; s: CARDINAL) ;
BEGIN
MetaErrorStringT1 (GetTokenNo (), m, s)
END MetaErrorString1 ;
PROCEDURE MetaErrorString2 (m: String; s1, s2: CARDINAL) ;
BEGIN
MetaErrorStringT2 (GetTokenNo (), m, s1, s2)
END MetaErrorString2 ;
PROCEDURE MetaErrorString3 (m: String; s1, s2, s3: CARDINAL) ;
BEGIN
MetaErrorStringT3 (GetTokenNo (), m, s1, s2, s3)
END MetaErrorString3 ;
PROCEDURE MetaErrorString4 (m: String; s1, s2, s3, s4: CARDINAL) ;
BEGIN
MetaErrorStringT4 (GetTokenNo (), m, s1, s2, s3, s4)
END MetaErrorString4 ;
(*
checkAbort - checks to see if the boolean flag seenAbort has been set,
if so it flushes all existing errors and terminates.
*)
PROCEDURE checkAbort ;
BEGIN
IF seenAbort
THEN
FlushWarnings ;
FlushErrors
END
END checkAbort ;
(*
translate -
*)
PROCEDURE translate (m, s: String; VAR i: INTEGER; name: Name) : String ;
VAR
l : INTEGER ;
ch: CHAR ;
BEGIN
l := Length (m) ;
WHILE (i >= 0) AND (i < l) DO
ch := char (m, i) ;
IF (ch = '%') AND (i < l)
THEN
INC (i) ;
ch := char (m, i) ;
INC (i) ;
IF ch = 'a'
THEN
s := ConCat (s, Mark (InitString ('%<'))) ;
s := ConCat (s, Mark (InitStringCharStar (KeyToCharStar (name)))) ;
s := ConCat (s, Mark (InitString ('%>'))) ;
RETURN s
END ;
s := ConCatChar (s, '%')
END ;
s := ConCatChar (s, ch) ;
INC (i)
END ;
RETURN s
END translate ;
(*
MetaErrorNT0 - generate an error message at tok using format.
*)
PROCEDURE MetaErrorNT0 (tok: CARDINAL; format: ARRAY OF CHAR) ;
BEGIN
MetaErrorStringT0 (tok, InitString (format))
END MetaErrorNT0 ;
(*
MetaErrorNT1 - generate an error message at tok using format and name.
The format should contain %a for name substitution.
*)
PROCEDURE MetaErrorNT1 (tok: CARDINAL; format: ARRAY OF CHAR; name: Name) ;
VAR
i : INTEGER ;
s,
fmt: String ;
BEGIN
i := 0 ;
fmt := InitString (format) ;
s := InitString ('') ;
s := translate (fmt, s, i, name) ;
MetaErrorStringT0 (tok, s) ;
fmt := KillString (fmt) ;
END MetaErrorNT1 ;
(*
MetaErrorN1 -
*)
PROCEDURE MetaErrorN1 (m: ARRAY OF CHAR; n: Name) ;
BEGIN
MetaErrorNT1 (GetTokenNo (), m, n)
END MetaErrorN1 ;
(*
MetaErrorNT1 - generate an error message at tok using format, name1
and name2. The format should contain two occurances of %a
for name substitution.
*)
PROCEDURE MetaErrorNT2 (tok: CARDINAL; format: ARRAY OF CHAR; name1, name2: Name) ;
VAR
i : INTEGER ;
s,
fmt: String ;
BEGIN
i := 0 ;
fmt := InitString (format) ;
s := InitString ('') ;
s := translate (fmt, s, i, name1) ;
s := translate (fmt, s, i, name2) ;
MetaErrorStringT0 (tok, s) ;
fmt := KillString (fmt) ;
END MetaErrorNT2 ;
(*
MetaErrorN2 -
*)
PROCEDURE MetaErrorN2 (m: ARRAY OF CHAR; n1, n2: Name) ;
BEGIN
MetaErrorNT2 (GetTokenNo (), m, n1, n2)
END MetaErrorN2 ;
(*
wrapString - return a string which has been formatted with the specifier codes.
Color is disabled. The result string is returned.
*)
PROCEDURE wrapString (m: String;
sym: ARRAY OF CARDINAL) : String ;
VAR
eb : errorBlock ;
s : String ;
old: BOOLEAN ;
BEGIN
old := M2ColorString.SetEnableColor (FALSE) ;
initErrorBlock (eb, Dup (m), sym) ;
eb.useError := FALSE ;
ebnf (eb, sym) ;
flushColor (eb) ;
s := Dup (eb.out) ;
killErrorBlock (eb) ;
old := M2ColorString.SetEnableColor (old) ;
RETURN s
END wrapString ;
PROCEDURE MetaString0 (m: String) : String ;
VAR
sym: ARRAY [0..0] OF CARDINAL ;
BEGIN
sym[0] := NulSym ;
RETURN wrapString (m, sym)
END MetaString0 ;
PROCEDURE MetaString1 (m: String; s: CARDINAL) : String ;
VAR
sym: ARRAY [0..0] OF CARDINAL ;
BEGIN
sym[0] := s ;
RETURN wrapString (m, sym)
END MetaString1 ;
PROCEDURE MetaString2 (m: String; s1, s2: CARDINAL) : String ;
VAR
sym: ARRAY [0..1] OF CARDINAL ;
BEGIN
sym[0] := s1 ;
sym[1] := s2 ;
RETURN wrapString (m, sym)
END MetaString2 ;
PROCEDURE MetaString3 (m: String; s1, s2, s3: CARDINAL) : String ;
VAR
sym: ARRAY [0..2] OF CARDINAL ;
BEGIN
sym[0] := s1 ;
sym[1] := s2 ;
sym[2] := s3 ;
RETURN wrapString (m, sym)
END MetaString3 ;
PROCEDURE MetaString4 (m: String; s1, s2, s3, s4: CARDINAL) : String ;
VAR
sym: ARRAY [0..3] OF CARDINAL ;
BEGIN
sym[0] := s1 ;
sym[1] := s2 ;
sym[2] := s3 ;
sym[3] := s4 ;
RETURN wrapString (m, sym)
END MetaString4 ;
(*
MetaErrorDecl - if sym is a variable or parameter then generate a
declaration error or warning message. If error is
FALSE then a warning is issued.
*)
PROCEDURE MetaErrorDecl (sym: CARDINAL; error: BOOLEAN) ;
BEGIN
IF (sym # NulSym) AND IsVar (sym)
THEN
IF error
THEN
IF IsVarAParam (sym)
THEN
MetaErrorT1 (GetVarDeclFullTok (sym), 'parameter declaration for {%1ad}', sym)
ELSE
MetaErrorT1 (GetVarDeclFullTok (sym), 'variable declaration for {%1ad}', sym)
END
ELSE
IF IsVarAParam (sym)
THEN
MetaErrorT1 (GetVarDeclFullTok (sym), 'parameter declaration for {%1Wad}', sym)
ELSE
MetaErrorT1 (GetVarDeclFullTok (sym), 'variable declaration for {%1Wad}', sym)
END
END
END
END MetaErrorDecl ;
BEGIN
lastRoot := NIL ;
lastColor := noColor ;
seenAbort := FALSE ;
outputStack := InitIndex (1) ;
dictionary := InitIndex (1) ;
freeEntry := NIL
END M2MetaError.