-- file LogPack.Mesa
-- last modified by Satterthwaite, September 9, 1982 1:00 pm

DIRECTORY
  Alloc: TYPE USING [Bounds],
  CharIO: TYPE USING [
    ControlZ, CR, SP,
    GetChar, PutChar, PutDecimal, PutNumber, PutString, PutSubString],
  ComData: TYPE USING [
    bodyIndex, nErrors, nWarnings, switches, table, textIndex],
  CompilerUtil: TYPE USING [
    AcquireStream, AcquireTable, ReleaseStream, ReleaseTable],
  ErrorTable: TYPE USING [CSRptr],
  FileStream: TYPE USING [FileByteIndex, EndOf, SetIndex],
  LiteralOps: TYPE USING [Value, StringValue],
  Log: TYPE USING [ErrorCode],
  Stream: TYPE USING [Handle],
  Strings: TYPE USING [String, SubString, SubStringDescriptor],
  Symbols: TYPE USING [seType, bodyType, HTIndex, ISEIndex, HTNull, SENull, BTNull],
  SymbolOps: TYPE USING [SubStringForHash],
  Tree: TYPE USING [Base, Index, Link, NodeName, Scan, Null, treeType],
  TreeOps: TYPE USING [ScanList];

LogPack: PROGRAM
    IMPORTS
      Alloc, CharIO, CompilerUtil, FileStream, LiteralOps, SymbolOps, TreeOps,
      dataPtr: ComData
    EXPORTS Log = {
  OPEN Symbols;

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

 -- public interface

  Error: PUBLIC PROC [code: ErrorCode] = {
    Enter[]; ErrorLog[code, TRUE]; Exit[]};

  ErrorHti: PUBLIC PROC [code: ErrorCode, hti: HTIndex] = {
    ErrorTree[code, [hash[hti]]]};

  ErrorN: PUBLIC PROC [code: ErrorCode, n: INTEGER] = {
    Enter[];
    CharIO.PutDecimal[errorStream, n];  CharIO.PutChar[errorStream, ' ];
    ErrorLog[code, TRUE];
    Exit[]};

  ErrorNode: PUBLIC PROC [code: ErrorCode, node: Tree.Index] = {
    ErrorTree[code, [subtree[node]]]};

  ErrorSei: PUBLIC PROC [code: ErrorCode, sei: ISEIndex] = {
    ErrorTree[code, [symbol[sei]]]};

  ErrorTree: PUBLIC PROC [code: ErrorCode, t: Tree.Link] = {
    Enter[];
    PrintOperand[t, 0, 0];  CharIO.PutString[errorStream, "  "L];
    ErrorLog[code, TRUE];
    Exit[]};

  Warning: PUBLIC PROC [code: ErrorCode] = {
    IF dataPtr.switches['w] THEN {
      Enter[];
      CharIO.PutString[errorStream, "warning: "L];
      ErrorLog[code, FALSE];
      Exit[]}};

  WarningNode: PUBLIC PROC [code: ErrorCode, node: Tree.Index] = {
    WarningTree[code, [subtree[node]]]};

  WarningSei: PUBLIC PROC [code: ErrorCode, sei: ISEIndex] = {
    WarningTree[code, [symbol[sei]]]};

  WarningSubString: PUBLIC PROC [code: ErrorCode, s: Strings.SubString] = {
    IF dataPtr.switches['w] THEN {
      Enter[];
      CharIO.PutSubString[errorStream, s]; CharIO.PutChar[errorStream, ' ];
      ErrorLog[code, FALSE];
      Exit[]}};

  WarningTree: PUBLIC PROC [code: ErrorCode, t: Tree.Link] = {
    IF dataPtr.switches['w] THEN {
      Enter[];
      CharIO.PutString[errorStream, "warning:  "L];
      PrintOperand[t, 0, 0];  CharIO.PutString[errorStream, "  "L];
      ErrorLog[code, FALSE];
      Exit[]}};

 -- source printing

  PrintTextLine: PROC [i: CARDINAL] = {
    OPEN CharIO;
    start, lineIndex: FileStream.FileByteIndex;
    char: CHARACTER;
    n: [1..100];
    sourceStream: Stream.Handle ← CompilerUtil.AcquireStream[source];
    start ← lineIndex ← i;
    FOR n IN [1..100] UNTIL lineIndex = 0 DO
      lineIndex ← lineIndex - 1;
      FileStream.SetIndex[sourceStream, lineIndex];
      IF CharIO.GetChar[sourceStream] = CR THEN EXIT;
      start ← lineIndex;
      ENDLOOP;
    FileStream.SetIndex[sourceStream, start];
    FOR n IN [1..100] WHILE ~FileStream.EndOf[sourceStream] DO
      SELECT (char ← CharIO.GetChar[sourceStream]) FROM
	CR, ControlZ => EXIT;
	ENDCASE => PutChar[errorStream, char];
      ENDLOOP;
    NewLine[];  
    CompilerUtil.ReleaseStream[source]};


  -- errorStream, CSRp and desc.base are set by Enter

  errorStream: Stream.Handle ← NIL;

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

  Enter: PROC = {
    errorStream ← CompilerUtil.AcquireStream[log];
    CSRp ← CompilerUtil.AcquireTable[error];
    ss.base ← @CSRp[CSRp.stringOffset]};

  Exit: PROC = {
    CompilerUtil.ReleaseTable[error];
    CompilerUtil.ReleaseStream[log];  errorStream ← NIL};


  WriteErrorString: PROC [n: ErrorCode] = {
    ss.offset ← CSRp.ErrorMessages[n].offset;
    ss.length ← CSRp.ErrorMessages[n].length;
    CharIO.PutSubString[errorStream, ss]};

  WriteHti: PROC [hti: HTIndex] = {
    OPEN CharIO;
    desc: Strings.SubStringDescriptor;
    s: SubString = @desc;
    IF hti = HTNull THEN PutString[errorStream, "(anonymous)"L]
    ELSE {SymbolOps.SubStringForHash[s, hti]; PutSubString[errorStream, s]}};

  WriteSei: PROC [sei: ISEIndex] = {
    WriteHti[IF sei=SENull
	THEN HTNull
	ELSE ((dataPtr.table).Bounds[seType].base)[sei].hash]};


  WriteLti: PROC [t: literal Tree.Link] = {
    WITH t.info SELECT FROM
      word => CharIO.PutDecimal[errorStream, LiteralOps.Value[index]];
      string => {
	s: Strings.String = LiteralOps.StringValue[index];
	CharIO.PutChar[errorStream, '"];
	FOR i: CARDINAL IN [0..s.length) DO
	  CharIO.PutChar[errorStream, s[i]] ENDLOOP;
	CharIO.PutChar[errorStream, '"]};
      ENDCASE};


  -- tables used for printing trees


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

  WriteOpName: PROC[n: Tree.NodeName[assignx..uparrow]] = {
    ss.offset ← CSRp.OpName[n].offset;
    ss.length ← CSRp.OpName[n].length;
    CharIO.PutSubString[errorStream, ss]};

    OpPrec: ARRAY Tree.NodeName[assignx..uparrow] OF CARDINAL = [
      1, 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", "NIL"];

  WriteFnName: PROC[n: Tree.NodeName[min..nil]] = {
    ss.offset ← CSRp.FnName[n].offset;  ss.length ← CSRp.FnName[n].length;
    CharIO.PutSubString[errorStream, ss]};

  Cutoff: CARDINAL = 3;

  PrintOperand: PROC [t: Tree.Link, tPrec, depth: CARDINAL] = {
    IF t = Tree.Null THEN RETURN;
    WITH e: t SELECT FROM
      hash => WriteHti[e.index];
      symbol => WriteSei[e.index];
      literal => WriteLti[e];
      subtree => {
	OPEN CharIO;
	node: Tree.Index = e.index;
	tb: Tree.Base ← (dataPtr.table).Bounds[Tree.treeType].base;
	op: Tree.NodeName = tb[node].name;
	IF depth > Cutoff THEN {PutString[errorStream, "..."L]; RETURN};
	SELECT op FROM
	  syserror, syserrorx => PutString[errorStream, "ERROR"L];
	  lengthen, --abs,-- IN [first..succ], IN [length..base], nil => {
	    PrintOperand[tb[node].son[1], OpPrec[dot], depth+1];
	    PutChar[errorStream, '.];
	    WriteFnName[op]};
	  IN [call .. rowcons], stringinit, IN [min .. nil] => {
	    OPEN tb[node];
	    args: Tree.Link;
	    SELECT op FROM
	      IN [call .. rowcons], stringinit => {
	        IF son[1] # Tree.Null THEN PrintOperand[son[1], 0, depth];
	        args ← son[2]};
	      IN [min .. nil] => {WriteFnName[op];  args ← son[1]};
	      ENDCASE;
	    IF args # Tree.Null OR op # nil THEN {
	      PutChar[errorStream, '[];
	      IF depth = Cutoff AND args.tag = subtree THEN
	        PutString[errorStream, "..."L]
	      ELSE PrintOperandList[args, depth+1];
	      IF op IN [call .. joinx] AND nSons > 2 THEN
	        PutString[errorStream, " !..."L];
	      PutChar[errorStream, ']]}};
	  IN [assignx .. uparrow] => {
	    OPEN tb[node];
	    prec: CARDINAL = OpPrec[op];
	    IF prec < tPrec THEN PutChar[errorStream, '(];
	    SELECT op FROM
	      IN [create .. addr] => {WriteOpName[op]; PrintOperand[son[1], prec, depth]};
	      IN [assignx .. dollar] => {
		PrintOperand[son[1], prec, depth+1];
		WriteOpName[op];
		PrintOperand[son[2], prec+1, depth+1]};
	      uparrow => {
		PrintOperand[son[1], prec, depth];
		PutChar[errorStream, '↑]};
	      ENDCASE => PutChar[errorStream, '?];
	    IF prec < tPrec THEN PutChar[errorStream, ')]};
	  IN [intOO .. intCC] => {
	    OPEN tb[node];
	    PutChar[errorStream, IF op = intOO OR op = intOC THEN '( ELSE '[];
	    PrintOperand[son[1], 0, depth];
	    PutString[errorStream, ".."L];
	    PrintOperand[son[2], 0, depth];
	    PutChar[errorStream, IF op = intOO OR op = intCO THEN ') ELSE ']]};
	  clit => {
	    c: CHARACTER = WITH e1: tb[node].son[1] SELECT FROM
	      literal =>
		WITH e1.info SELECT FROM
		  word => LiteralOps.Value[index]+0C,
		  ENDCASE => ERROR,
	      ENDCASE => ERROR;
	    IF c >= SP THEN {PutChar[errorStream, '']; PutChar[errorStream, c]}
	    ELSE {
	      PutNumber[errorStream, c-0C,
		[base:8, zerofill:FALSE, unsigned:TRUE, columns:0]];
	      PutChar[errorStream, 'c]}};
	  new, cons, listcons => {
	    IF tb[node].son[1] # Tree.Null THEN {
	      PrintOperand[tb[node].son[1], OpPrec[dot], depth+1];
	      PutChar[errorStream, '.]};
	    PutString[errorStream,
	      SELECT op FROM new => "NEW"L, cons => "CONS"L, ENDCASE => "LIST"L];
	    PutChar[errorStream, '[];
	    IF depth = Cutoff THEN PutString[errorStream, "..."L]
	    ELSE PrintOperandList[tb[node].son[2], depth+1];
	    PutChar[errorStream, ']]};
	  atom => {
	    PutChar[errorStream, '$];
	    WITH e1: tb[node].son[1] SELECT FROM hash => WriteHti[e1.index]; ENDCASE};
	  llit, IN [cast .. openx], thread => PrintOperand[tb[node].son[1], tPrec, depth];
	  item => PrintOperand[tb[node].son[2], tPrec, depth];
	  ENDCASE => PutString[errorStream, "..."L]};
      ENDCASE};

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

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

    TreeOps.ScanList[t, PrintItem]};


 -- error-handling routines

  NewLine: PROC = {CharIO.PutChar[errorStream, CharIO.CR]};

  ErrorLog: PROC [code: ErrorCode, error: BOOLEAN] = {
    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[errorStream, ", at "L];
    IF dataPtr.bodyIndex # BTNull THEN {
      bodyId ← ((dataPtr.table).Bounds[Symbols.bodyType]).base[dataPtr.bodyIndex].id;
      IF bodyId # SENull THEN WriteSei[bodyId]};
    IF index # LAST[CARDINAL] THEN {
      PutChar[errorStream, '[];
      PutNumber[errorStream, index,
	[base:10, zerofill:FALSE, unsigned:TRUE, columns:0]];
      PutChar[errorStream, ']]};
    PutChar[errorStream, ':];  NewLine[];
    IF index # LAST[CARDINAL] THEN PrintTextLine[index]
    ELSE {PutString[errorStream, "(source from inline)"L]; NewLine[]};
    NewLine[]};

  }.