-- file LogPack.Mesa
-- last modified by Satterthwaite, December 12, 1979  8:35 AM

DIRECTORY
  CharIO: FROM "chario"
    USING [ControlZ, CR, PutChar, PutDecimal, PutNumber, PutString],
  ComData: FROM "comdata"
    USING [
      bodyIndex, errorStream, nErrors, nWarnings, sourceStream,
      switches, textIndex],
  CompilerUtil: FROM "compilerutil" USING [error, TableSegment],
  ErrorTable: FROM "errortable" USING [CSRptr],
  LiteralOps: FROM "literalops" USING [Value, StringValue],
  Log: FROM "log" USING [ErrorCode],
  SegmentDefs: FROM "segmentdefs"
    USING [FileSegmentHandle, SegmentAddress, SwapIn, Unlock],
  StreamDefs: FROM "streamdefs"
    USING [
      StreamIndex,
      CloseDiskStream, ModifyIndex, NormalizeIndex, OpenDiskStream, SetIndex],
  StringDefs: FROM "stringdefs" USING [SubString, SubStringDescriptor],
  Symbols: FROM "symbols"
    USING [seType, bodyType, HTIndex, ISEIndex, HTNull, SENull, BTNull],
  SymbolOps: FROM "symbolops" USING [SubStringForHash],
  Table: FROM "table" USING [Base, Bounds],
  Tree: FROM "tree" USING [Index, Link, NodeName, Scan, Null, treeType],
  TreeOps: FROM "treeops" USING [ScanList];

LogPack: PROGRAM
    IMPORTS
      CharIO, CompilerUtil, LiteralOps, SegmentDefs, StreamDefs,
      SymbolOps, Table, TreeOps,
      dataPtr: ComData
    EXPORTS Log =
  BEGIN
  OPEN Symbols;

  ErrorCode: TYPE = Log.ErrorCode;
  SubString: TYPE = StringDefs.SubString;

 -- public interface

  Error: PUBLIC PROCEDURE [code: ErrorCode] =
    BEGIN LockStringTable[]; ErrorLog[code, TRUE]; UnlockStringTable[] END;

  ErrorHti: PUBLIC PROCEDURE [code: ErrorCode, hti: HTIndex] =
    BEGIN ErrorTree[code, Tree.Link[hash[hti]]] END;

  ErrorN: PUBLIC PROCEDURE [code: ErrorCode, n: INTEGER] =
    BEGIN  OPEN CharIO;
    LockStringTable[];
    PutDecimal[dataPtr.errorStream, n];  PutChar[dataPtr.errorStream, ' ];
    ErrorLog[code, TRUE];
    UnlockStringTable[];
    END;

  ErrorNode: PUBLIC PROCEDURE [code: ErrorCode, node: Tree.Index] =
    BEGIN ErrorTree[code, Tree.Link[subtree[node]]] END;

  ErrorSei: PUBLIC PROCEDURE [code: ErrorCode, sei: ISEIndex] =
    BEGIN ErrorTree[code, Tree.Link[symbol[sei]]] END;

  ErrorString: PUBLIC PROCEDURE [code: ErrorCode, s: STRING] =
    BEGIN  OPEN CharIO;
    LockStringTable[];
    PutString[dataPtr.errorStream, s];  PutChar[dataPtr.errorStream, ' ];
    ErrorLog[code, TRUE];
    UnlockStringTable[];
    END;

  ErrorTree: PUBLIC PROCEDURE [code: ErrorCode, t: Tree.Link] =
    BEGIN  OPEN CharIO;
    LockStringTable[];
    PrintOperand[t, 0, 0];  PutString[dataPtr.errorStream, "  "L];
    ErrorLog[code, TRUE];
    UnlockStringTable[];
    END;

  Warning: PUBLIC PROCEDURE [code: ErrorCode] =
    BEGIN
    IF dataPtr.switches['w] THEN
      BEGIN
      LockStringTable[];
      CharIO.PutString[dataPtr.errorStream, "warning: "L];
      ErrorLog[code, FALSE];
      UnlockStringTable[];
      END;
    END;

  WarningNode: PUBLIC PROCEDURE [code: ErrorCode, node: Tree.Index] =
    BEGIN WarningTree[code, Tree.Link[subtree[node]]] END;

  WarningSei: PUBLIC PROCEDURE [code: ErrorCode, sei: ISEIndex] =
    BEGIN WarningTree[code, Tree.Link[symbol[sei]]] END;

  WarningTree: PUBLIC PROCEDURE [code: ErrorCode, t: Tree.Link] =
    BEGIN
    IF dataPtr.switches['w] THEN
      BEGIN  OPEN CharIO;
      LockStringTable[];
      PutString[dataPtr.errorStream, "warning:  "L];
      PrintOperand[t, 0, 0];  PutString[dataPtr.errorStream, "  "L];
      ErrorLog[code, FALSE];
      UnlockStringTable[];
      END;
    END;

 -- source printing

  PrintTextLine: PROCEDURE [i: CARDINAL] =
    BEGIN  OPEN StreamDefs, CharIO;
    start, lineIndex: StreamIndex;
    char: CHARACTER;
    n: [1..100];
    OpenDiskStream[dataPtr.sourceStream];
    start ← lineIndex ← NormalizeIndex[[page:0, byte:i]];
    FOR n IN [1..100] UNTIL lineIndex = [0, 0]
      DO
      lineIndex ← ModifyIndex[lineIndex, -1];
      SetIndex[dataPtr.sourceStream, lineIndex];
      IF dataPtr.sourceStream.get[dataPtr.sourceStream] = CR THEN EXIT;
      start ← lineIndex;
      ENDLOOP;
    SetIndex[dataPtr.sourceStream, start];
    FOR n IN [1..100] WHILE ~dataPtr.sourceStream.endof[dataPtr.sourceStream]
      DO
      SELECT (char ← dataPtr.sourceStream.get[dataPtr.sourceStream]) FROM
	CR, ControlZ => EXIT;
	ENDCASE => PutChar[dataPtr.errorStream, char];
      ENDLOOP;
    NewLine[];  
    CloseDiskStream[dataPtr.sourceStream];
    END;


  -- CSRp and desc.base are set by LockStringTable

  errorSeg: SegmentDefs.FileSegmentHandle =
    CompilerUtil.TableSegment[CompilerUtil.error];

  CSRp: ErrorTable.CSRptr;
  desc: StringDefs.SubStringDescriptor;
  ss: SubString = @desc;

  LockStringTable: PROCEDURE =
    BEGIN
    SegmentDefs.SwapIn[errorSeg];
    CSRp ← LOOPHOLE[SegmentDefs.SegmentAddress[errorSeg]];
    ss.base ← @CSRp[CSRp.stringOffset];
    END;

  UnlockStringTable: PROCEDURE = BEGIN SegmentDefs.Unlock[errorSeg] END;


  WriteSubString: PROCEDURE [ss: SubString] =
    BEGIN
    i: CARDINAL;
    FOR i IN [ss.offset..ss.offset + ss.length)
      DO  CharIO.PutChar[dataPtr.errorStream, ss.base[i]]  ENDLOOP;
    END;

  WriteErrorString: PROCEDURE [n: ErrorCode] =
    BEGIN
    ss.offset ← CSRp.ErrorMessages[n].offset;
    ss.length ← CSRp.ErrorMessages[n].length;
    WriteSubString[ss];
    END;

  WriteHti: PROCEDURE [hti: HTIndex] =
    BEGIN  OPEN CharIO;
    desc: StringDefs.SubStringDescriptor;
    s: SubString = @desc;
    IF hti = HTNull
      THEN  PutString[dataPtr.errorStream, "(anonymous)"L]
      ELSE  BEGIN SymbolOps.SubStringForHash[s, hti]; WriteSubString[s] END;
    END;

  WriteSei: PROCEDURE [sei: ISEIndex] =
    BEGIN
    WriteHti[IF sei=SENull
	THEN HTNull
	ELSE (Table.Bounds[seType].base)[sei].hash];
    END;


  WriteLti: PROCEDURE [t: literal Tree.Link] =
    BEGIN  OPEN CharIO;
    WITH t.info SELECT FROM
      word =>  PutDecimal[dataPtr.errorStream, LiteralOps.Value[index]];
      string =>
	BEGIN
	PutChar[dataPtr.errorStream, '"];
	PutString[dataPtr.errorStream, LiteralOps.StringValue[index]];
	PutChar[dataPtr.errorStream, '"];
	END;
      ENDCASE;
    END;


  -- tables used for printing trees


--   OpName: ARRAY Tree.NodeName[assignx..uparrow] OF STRING ←
--     ["←",
--      " OR ", " AND ", "=", "#", "<", ">=", ">", "<=", " IN ", " ~IN ",
--      "+", "-", "*", "/", " MOD ",
--      ".", ".", ".",
--      " NEW ", "~", "-", "@", "↑"];

  WriteOpName: PROCEDURE[n: Tree.NodeName[assignx..uparrow]] =
    BEGIN
    ss.offset ← CSRp.OpName[n].offset;
    ss.length ← CSRp.OpName[n].length;
    WriteSubString[ss];
    END;

    OpPrec: ARRAY Tree.NodeName[assignx..uparrow] OF CARDINAL =
     [1, 
      2, 3, 5, 5, 5, 5, 5, 5, 5, 5,
      6, 6, 7, 7, 7,
      10, 10, 10,
      1, 4, 8, 9, 10];


--   FnName: ARRAY Tree.NodeName[min..loophole] OF STRING ←
--     ["MIN", "MAX", "LONG", "ABS", "ALL", "SIZE", "FIRST", "LAST",
--      "DESCRIPTOR", "LENGTH", "BASE", "LOOPHOLE"];

  WriteFnName: PROCEDURE[n: Tree.NodeName[min..loophole]] =
    BEGIN
    ss.offset ← CSRp.FnName[n].offset;  ss.length ← CSRp.FnName[n].length;
    WriteSubString[ss];
    END;

  Cutoff: CARDINAL = 3;

  PrintOperand: PROCEDURE [t: Tree.Link, tPrec, depth: CARDINAL] =
    BEGIN
    node: Tree.Index;
    prec: CARDINAL;
    op: Tree.NodeName;
    args: Tree.Link;
    tb: Table.Base;
    IF t = Tree.Null THEN RETURN;
    WITH e: t SELECT FROM
      hash =>  WriteHti[e.index];
      symbol =>  WriteSei[e.index];
      literal =>  WriteLti[e];
      subtree =>
	BEGIN  OPEN CharIO;
	tb ← Table.Bounds[Tree.treeType].base;
	node ← e.index;  op ← tb[node].name;
	IF depth > Cutoff THEN
	  BEGIN PutString[dataPtr.errorStream, "..."L]; RETURN END;
	SELECT op FROM
	  syserror, syserrorx =>
	    PutString[dataPtr.errorStream, "ERROR"L];
	  IN [call .. rowcons], stringinit, IN [min .. loophole] =>
	    BEGIN  OPEN tb[node];
	    SELECT op FROM
	      IN [call .. rowcons], stringinit =>
	        BEGIN
	        IF son[1] # Tree.Null THEN PrintOperand[son[1], 0, depth];
	        args ← son[2];
	        END;
	      IN [min .. loophole] =>
		BEGIN  WriteFnName[op];  args ← son[1]  END;
	      ENDCASE;
	    PutChar[dataPtr.errorStream, '[];
	    IF depth = Cutoff AND args.tag = subtree
	      THEN PutString[dataPtr.errorStream, "..."L]
	      ELSE PrintOperandList[args, depth+1];
	    IF op IN [call .. joinx] AND nSons > 2
	      THEN PutString[dataPtr.errorStream, " !..."L];
	    PutChar[dataPtr.errorStream, ']];
	    END;
	  IN [assignx .. uparrow] =>
	    BEGIN  OPEN tb[node];
	    prec ← OpPrec[op];
	    IF prec < tPrec THEN PutChar[dataPtr.errorStream, '(];
	    SELECT op FROM
	      IN [new .. addr] =>
		BEGIN WriteOpName[op]; PrintOperand[son[1], prec, depth] END;
	      IN [assignx .. dollar] =>
		BEGIN
		PrintOperand[son[1], prec, depth+1];
		WriteOpName[op];
		PrintOperand[son[2], prec+1, depth+1];
		END;
	      uparrow =>
		BEGIN
		PrintOperand[son[1], prec, depth];
		PutChar[dataPtr.errorStream, '↑];
		END;
	      ENDCASE =>  PutChar[dataPtr.errorStream, '?];
	    IF prec < tPrec THEN PutChar[dataPtr.errorStream, ')];
	    END;
	  IN [intOO .. intCC] =>
	    BEGIN  OPEN tb[node];
	    PutChar[dataPtr.errorStream,
		IF op = intOO OR op = intOC THEN '( ELSE '[];
	    PrintOperand[son[1], 0, depth];
	    PutString[dataPtr.errorStream, ".."L];
	    PrintOperand[son[2], 0, depth];
	    PutChar[dataPtr.errorStream,
		IF op = intOO OR op = intCO THEN ') ELSE ']];
	    END;
	  clit =>
	    BEGIN  PutChar[dataPtr.errorStream, ''];
	    WITH e1: tb[node].son[1] SELECT FROM
	      literal =>
		WITH e1.info SELECT FROM
		  word =>
		    PutChar[dataPtr.errorStream, LiteralOps.Value[index]+0C];
		  ENDCASE;
	      ENDCASE;
	    END;
	  llit, IN [cast .. openx], thread =>
	    PrintOperand[tb[node].son[1], tPrec, depth];
	  item =>
	    PrintOperand[tb[node].son[2], tPrec, depth];
	  ENDCASE =>  PutString[dataPtr.errorStream, "..."L];
	END;
      ENDCASE;
    END;

  PrintOperandList: PROCEDURE [t: Tree.Link, depth: CARDINAL] =
    BEGIN
    firstSon: BOOLEAN ← TRUE;

    PrintItem: Tree.Scan =
      BEGIN  OPEN CharIO;
      IF ~firstSon THEN PutString[dataPtr.errorStream, ", "L];
      firstSon ← FALSE;
      IF t # Tree.Null THEN PrintOperand[t, 0, depth];
      END;

    TreeOps.ScanList[t, PrintItem];
    END;


 -- error-handling routines

  NewLine: PROCEDURE =
    BEGIN OPEN CharIO;  PutChar[dataPtr.errorStream, CR]  END;

  ErrorLog: PROCEDURE [code: ErrorCode, error: BOOLEAN] =
    BEGIN  OPEN CharIO;
    bodyId: ISEIndex;
    index: CARDINAL = dataPtr.textIndex;
    WriteErrorString[code];
    IF error
      THEN  dataPtr.nErrors ← dataPtr.nErrors + 1
      ELSE  dataPtr.nWarnings ← dataPtr.nWarnings + 1;
    PutString[dataPtr.errorStream, ", at "L];
    IF dataPtr.bodyIndex # BTNull
      THEN
	BEGIN
	bodyId ← Table.Bounds[bodyType].base[dataPtr.bodyIndex].id;
	IF bodyId # SENull THEN WriteSei[bodyId];
	END;
    IF index # LAST[CARDINAL] THEN
      BEGIN
      PutChar[dataPtr.errorStream, '[];
      PutNumber[dataPtr.errorStream, index,
	[base:10, zerofill:FALSE, unsigned:TRUE, columns:0]];
      PutChar[dataPtr.errorStream, ']];
      END;
    PutChar[dataPtr.errorStream, ':];  NewLine[];
    IF index # LAST[CARDINAL]
      THEN  PrintTextLine[index]
      ELSE
	BEGIN
	PutString[dataPtr.errorStream, "(source from inline)"L]; NewLine[];
	END;
    NewLine[];
    END;

  END.