-- file Debug.Mesa
-- last modified by Bruce, September 5, 1980  12:34 AM

DIRECTORY
  Ascii USING [TAB],
  BcdDefs USING [Link],
  ComData USING [definitionsOnly],
  CompilerUtil USING [debug, LockTableSegment, UnlockTableSegment],
  DebugFormat USING [LongSubString, LongSubStringDescriptor],
  DebugTable USING [CSRptr],
  DOutput USING [Char, Decimal, EOL, Octal, LongSubString, SubString, Text],
  Literals USING [LitDescriptor, ltType],
  LiteralOps USING [DescriptorValue, MasterString, StringValue],
  P1 USING [],
  Strings USING [SubString, SubStringDescriptor],
  Symbols USING [
    BitAddress, CTXRecord, TransferMode, TypeClass,
    HTIndex, SEIndex, ISEIndex, RecordSEIndex, CTXIndex, BTIndex,
    HTNull, SENull, lG, lZ, RootBti, typeTYPE,
    seType, ctxType, mdType, bodyType],
  SymbolOps USING [
    EnumerateBodies, FindExtension, HashForSe, NextSe, SubStringForHash,
    TypeLink, XferMode],
  Table USING [Base, Notifier, AddNotify, DropNotify, Bounds],
  Tree USING [Index, Link, Map, NodeName, NullIndex, treeType],
  TreeOps USING [GetNode, UpdateTree];

Debug: PROGRAM
    IMPORTS
      CompilerUtil, DOutput, LiteralOps, SymbolOps, Table, TreeOps,
      dataPtr: ComData
    EXPORTS CompilerUtil, P1 =
  BEGIN
  OPEN Symbols;

  tb: Table.Base;
  seb: Table.Base;
  ctxb: Table.Base;
  mdb: Table.Base;
  bb: Table.Base;
  ltb: Table.Base;

  DebugNotify: Table.Notifier = {
    tb ← base[Tree.treeType];
    seb ← base[seType];  ctxb ← base[ctxType];  mdb ← base[mdType];
    bb ← base[bodyType];
    ltb ← base[Literals.ltType]};

 -- basic io

  WriteChar: PROC [c: CHARACTER] = {DOutput.Char[c]};

  WriteString: PROC [s: STRING] = {DOutput.Text[s]};

  WriteDecimal: PROC [n: INTEGER] = {DOutput.Decimal[n]};

  WriteSubString: PROC [ss: DebugFormat.LongSubString] =  {
    DOutput.LongSubString[LOOPHOLE[ss]]};

  NewLine: PROC = {DOutput.EOL[]};

  Indent: PROC [n: CARDINAL] = {
    NewLine[];
    THROUGH [1..n/8] DO DOutput.Char[Ascii.TAB] ENDLOOP;
    THROUGH [1..n MOD 8] DO DOutput.Char[' ] ENDLOOP};


  -- csrP and desc.base are set by LockStringTable

  csrP: DebugTable.CSRptr;
  desc: DebugFormat.LongSubStringDescriptor;
  ss: DebugFormat.LongSubString = @desc;

  LockStringTable: PROC = {
    csrP ← CompilerUtil.LockTableSegment[CompilerUtil.debug];
    ss.base ← @csrP[csrP.stringOffset]};

  UnlockStringTable: PROC = {CompilerUtil.UnlockTableSegment[CompilerUtil.debug]};


  -- tree printing

  PrintLiteral: PROC[t: literal Tree.Link] = {
    desc: Literals.LitDescriptor;
    v: WORD;
    WITH t.info SELECT FROM
      string => {
	WriteChar['"];
	DOutput.Text[LiteralOps.StringValue[index]]; WriteChar['"];
	IF index # LiteralOps.MasterString[index] THEN WriteChar['L]};
      word => {
	desc ← LiteralOps.DescriptorValue[index];
	IF desc.length # 1 THEN WriteChar['[];
	FOR i: CARDINAL IN [0 .. desc.length)
	  DO
	  IF (v ← ltb[desc.offset][i]) < 1000
	    THEN WriteDecimal[v]
	    ELSE DOutput.Octal[v];
	  IF i+1 # desc.length THEN WriteChar[',];
	  ENDLOOP;
	IF desc.length # 1 THEN WriteChar[']]};
      ENDCASE};

  PrintNodeName: PUBLIC PROC [n: Tree.NodeName] = {
   LockStringTable[]; WriteNodeName[n]; UnlockStringTable[]};

  WriteNodeName: PROC [n: Tree.NodeName] = {
    ss.offset ← csrP.NodePrintName[n].offset; ss.length ← csrP.NodePrintName[n].length;
    WriteSubString[ss]};

  PrintSubTree: PROC [t: Tree.Link, nBlanks: CARDINAL] = {
    OPEN Tree;

    Printer: Tree.Map = {
      node: Tree.Index;
      Indent[nBlanks];
      WITH s: t SELECT FROM
	hash => PrintHti[s.index];
	symbol => {PrintSei[s.index]; WriteChar['[]; PrintIndex[s.index]; WriteChar[']]};
	literal => PrintLiteral[s];
	subtree => {
	  node ← s.index;
	  IF node = Tree.NullIndex
	    THEN WriteString["<empty>"L]
	    ELSE {
	      OPEN tb[node];
	      WriteNodeName[name];
	      WriteChar['[];  PrintIndex[node];  WriteString["] "L];
	      IF info # 0 THEN {WriteString["  info="L]; PrintIndex[info]};
	      IF attr1 OR attr2 OR attr3
		THEN {
		  IF info = 0 THEN WriteChar[' ];
		  WriteChar['(];
		  IF attr1 THEN WriteChar['1];
		  IF attr2 THEN WriteChar['2];
		  IF attr3 THEN WriteChar['3];
		  WriteChar[')]};
	      nBlanks ← nBlanks + 2;
	      IF name # thread
		THEN [] ← TreeOps.UpdateTree[s, Printer]
		ELSE {
		  WriteString[" link="L]; PrintIndex[TreeOps.GetNode[son[2]]];
		  [] ← Printer[son[1]]};
	      nBlanks ← nBlanks - 2}};
	ENDCASE;
      RETURN [t]};

    [] ← Printer[t]};


  PrintTree: PUBLIC PROC [t: Tree.Link] = {
    Table.AddNotify[DebugNotify];  LockStringTable[];
    PrintSubTree[t, 0];  NewLine[];  NewLine[];
    UnlockStringTable[];  Table.DropNotify[DebugNotify]};

  PrintBodies: PUBLIC PROC = {
    Table.AddNotify[DebugNotify];  LockStringTable[];
    [] ← SymbolOps.EnumerateBodies[RootBti, PrintBody];  NewLine[];
    UnlockStringTable[];  Table.DropNotify[DebugNotify]};

  PrintBody: PROC [bti: BTIndex] RETURNS [BOOLEAN] = {
    OPEN body: bb[bti];
    WriteString["Body: "L];
    WITH b: body SELECT FROM
      Callable => {
	PrintSei[b.id];
	IF b.inline
	  THEN WriteString[" [inline]"]
	  ELSE {
	    WriteString[", ep: "L];  WriteDecimal[b.entryIndex];
	    WITH b SELECT FROM
	      Inner => {WriteString[", frame addr: "L]; WriteDecimal[frameOffset]};
	      ENDCASE}};
      ENDCASE => WriteString["(anon)"L];
    Indent[2];
    WriteString["context: "L];  PrintIndex[body.localCtx];
    WriteString[", level: "L];  WriteDecimal[body.level];
    WITH body.info SELECT FROM
      Internal => {
	WriteString[", frame size: "L];  WriteDecimal[frameSize];
	IF body.kind = Callable
	  THEN PrintSubTree[[subtree[index: bodyTree]], 0]
	  ELSE {WriteString[", tree root: "L]; PrintIndex[bodyTree]}};
      ENDCASE;
    NewLine[];  NewLine[];  RETURN [FALSE]};


  PrintSymbols: PUBLIC PROC = {
    ctx: CTXIndex;
    limit: CTXIndex = LOOPHOLE[Table.Bounds[Symbols.ctxType].size];
    ctx ← FIRST[CTXIndex] + SIZE [nil CTXRecord];
    UNTIL ctx = limit
      DO
      PrintContext[ctx];  NewLine[];  NewLine[];
      ctx ← ctx + (WITH ctxb[ctx] SELECT FROM
	included => SIZE [included CTXRecord],
	imported => SIZE [imported CTXRecord],
	ENDCASE => SIZE [simple CTXRecord]);
      ENDLOOP;
    NewLine[]};

  PrintContext: PROC [ctx: CTXIndex] = {
    sei, root: ISEIndex;
    Table.AddNotify[DebugNotify];  LockStringTable[];
    WriteString["Context: "L];  PrintIndex[ctx];
    IF ctxb[ctx].level # lZ THEN {WriteString[", level: "L]; WriteDecimal[ctxb[ctx].level]};
    WITH ctxb[ctx] SELECT FROM
      included => {
	WriteString[", copied from: "L]; PrintHti[mdb[module].moduleId];
	WriteString[" [file: "L]; PrintHti[mdb[module].fileId];
	WriteString["], context: "L];  PrintIndex[map]};
      imported => {
	WriteString[", imported from: "L]; PrintHti[mdb[ctxb[includeLink].module].moduleId]};
      ENDCASE;
    root ← sei ← ctxb[ctx].seList;
      DO
      IF sei = SENull THEN EXIT;
      PrintSE[sei, 2];
      IF (sei ← SymbolOps.NextSe[sei]) = root THEN EXIT;
      ENDLOOP;
    UnlockStringTable[];  Table.DropNotify[DebugNotify]};


  PrintSE: PROC [sei: ISEIndex, nBlanks: CARDINAL] = {
    OPEN seb[sei];
    typeSei: SEIndex;
    Indent[nBlanks];
    PrintSei[sei];
    WriteString[" ["L];  PrintIndex[sei];  WriteChar[']];
    IF public THEN WriteString[" [public]"L];
    IF mark3
      THEN {
	WriteString[", type = "L];
	IF idType = typeTYPE
	  THEN {
	    typeSei ← idInfo;
	    WriteString["TYPE, equated to: "L];
	    PrintType[typeSei];
	    IF ctxb[idCtx].level = lZ AND SymbolOps.TypeLink[sei] # SENull
	      THEN {WriteString[", tag code: "L]; WriteDecimal[idValue]}}
	  ELSE {
	    typeSei ← idType;  PrintType[typeSei];
	    SELECT TRUE FROM
	      constant => WriteString[" [const]"L];
	      immutable => WriteString[" [init only]"L];
	      ENDCASE;
	    IF ~mark4
	      THEN {WriteString[", # refs: "L]; WriteDecimal[idInfo]}
	      ELSE
		SELECT TRUE FROM
		  constant =>
		    IF ~ extended
		      THEN {
			WriteString[", value: "L];
			SELECT SymbolOps.XferMode[typeSei] FROM
			  proc, program, signal, error =>  PrintLink[idValue];
			  ENDCASE =>
			    IF LOOPHOLE[idValue, CARDINAL] < 1000
			      THEN WriteDecimal[idValue]
			      ELSE DOutput.Octal[idValue]};
		  (dataPtr.definitionsOnly AND ctxb[idCtx].level = lG) => {
		    WriteString[", index: "L]; WriteDecimal[idValue]};
		  ENDCASE => {
		    addr: BitAddress = idValue;
		    WriteString[", address: "L];
		    WriteDecimal[addr.wd];  WriteChar[' ];
		    WriteChar['[];  WriteDecimal[addr.bd];
		    WriteChar[':];  WriteDecimal[idInfo];  WriteChar[']];
		    IF linkSpace THEN WriteChar['*]}};
	PrintTypeInfo[typeSei, nBlanks+2];
	IF extended THEN PrintSubTree[SymbolOps.FindExtension[sei].tree, nBlanks+4]}};


  PrintHti: PROC [hti: HTIndex] = {
    desc: Strings.SubStringDescriptor;
    s: Strings.SubString = @desc;
    IF hti = HTNull
      THEN WriteString["(anon)"L]
      ELSE {SymbolOps.SubStringForHash[s, hti]; DOutput.SubString[s]}};

  PrintSei: PROC [sei: ISEIndex] = {PrintHti[SymbolOps.HashForSe[sei]]};


  WriteTypeName: PROC [n: TypeClass] = {
    ss.offset ← csrP.TypePrintName[n].offset;  ss.length ← csrP.TypePrintName[n].length;
    WriteSubString[ss]};

  WriteModeName: PROC [n: TransferMode] = {
    ss.offset ← csrP.ModePrintName[n].offset;  ss.length ← csrP.ModePrintName[n].length;
    WriteSubString[ss]};

  PrintType: PROC [sei: SEIndex] = {
    tSei: SEIndex;
    IF sei = SENull
      THEN WriteChar['?]
      ELSE
	WITH t: seb[sei] SELECT FROM
	  cons =>
	    WITH t SELECT FROM
	      transfer => WriteModeName[mode];
	      ENDCASE =>  WriteTypeName[t.typeTag];
	  id =>
	    FOR tSei ← sei, SymbolOps.TypeLink[tSei] UNTIL tSei = SENull
	      DO
	      WITH seb[tSei] SELECT FROM
		id => {
		  IF sei # tSei THEN WriteChar[' ];
		  PrintSei[LOOPHOLE[tSei, ISEIndex]];
		  IF ~mark3 OR ctxb[idCtx].level # lZ THEN EXIT};
		ENDCASE;
	      ENDLOOP;
	  ENDCASE;
    WriteString[" ["L];  PrintIndex[sei];  WriteChar[']]};

  PrintTypeInfo: PROC [sei: SEIndex, nBlanks: CARDINAL] = {
    IF sei # SENull
      THEN
	WITH s: seb[sei] SELECT FROM
	  cons => {
	    Indent[nBlanks];
	    WriteChar['[];  PrintIndex[sei];  WriteString["] "L];
	    WITH s SELECT FROM
	      transfer => WriteModeName[mode];
	      ENDCASE => WriteTypeName[s.typeTag];
	    WITH t: s SELECT FROM
	      basic => NULL;
	      enumerated => {
		IF t.machineDep THEN WriteString[" (md)"L];
		WriteString[", value ctx: "L];  PrintIndex[t.valueCtx]};
	      record => {
		IF t.machineDep THEN WriteString[" (md)"L];
		IF t.monitored THEN WriteString[" (monitored)"L];
		IF t.hints.variant THEN WriteString[" (variant)"L];
		OutRecordCtx[", field ctx: "L, LOOPHOLE[sei, RecordSEIndex]];
		WITH ctxb[t.fieldCtx] SELECT FROM
		  included => IF ~complete THEN WriteString[" [partial]"L];
		  imported => WriteString[" [partial]"L];
		  ENDCASE;
		WITH t SELECT FROM
		  linked => {WriteString[", link: "L]; PrintType[linkType]};
		  ENDCASE};
	      ref => {
		IF t.counted THEN WriteString[" (counted)"L];
		IF t.ordered THEN WriteString[" (ordered)"L];
		IF t.basing THEN WriteString[" (base)"L];
		WriteString[", to: "L]; PrintType[t.refType];
		IF t.readOnly THEN WriteString[" (readonly)"L];
		PrintTypeInfo[t.refType, nBlanks+2]};
	      array => {
		IF t.packed THEN WriteString[" (packed)"L];
		WriteString[", index type: "L]; PrintType[t.indexType];
		WriteString[", component type: "L]; PrintType[t.componentType];
		PrintTypeInfo[t.indexType, nBlanks+2];
		PrintTypeInfo[t.componentType, nBlanks+2]};
	      arraydesc => {
		WriteString[", described type: "L]; PrintType[t.describedType];
		IF t.readOnly THEN WriteString[" (readonly)"L];
		PrintTypeInfo[t.describedType, nBlanks+2]};
	      transfer => {
		OutRecordCtx[", input ctx: "L, t.inRecord];
		OutRecordCtx[", output ctx: "L, t.outRecord]};
	      definition => {
		WriteString[", ctx: "L];  PrintIndex[t.defCtx];
		WriteString[", ngfi: "L];  WriteDecimal[t.nGfi]};
	      union => {
		IF t.overlaid THEN WriteString[" (overlaid)"L];
		IF t.controlled THEN {WriteString[", tag: "L]; PrintSei[t.tagSei]};
		WriteString[", tag type: "L];
		PrintType[seb[t.tagSei].idType];
		WriteString[", case ctx: "L];  PrintIndex[t.caseCtx];
		IF t.controlled THEN PrintSE[t.tagSei, nBlanks+2]};
	      relative => {
		WriteString[", base type: "L]; PrintType[t.baseType];
		WriteString[", offset type: "L]; PrintType[t.offsetType];
		PrintTypeInfo[t.baseType, nBlanks+2];
		PrintTypeInfo[t.offsetType, nBlanks+2]};
	      opaque => {
		WriteString[", id: "L]; PrintSei[t.id];
		IF t.lengthKnown THEN  {WriteString[", size: "L]; WriteDecimal[t.length]}};
	      zone => {
		IF t.counted THEN WriteString[" (counted)"L];
		IF t.mds THEN WriteString[" (mds)"L]};
	      subrange => {
		WriteString[" of: "L];  PrintType[t.rangeType];
		IF t.filled
		  THEN {
		    WriteString[" origin: "L];  WriteDecimal[t.origin];
		    WriteString[", range: "L]};
		PrintTypeInfo[t.rangeType, nBlanks+2]};
	      long, real => {
		WriteString[" of: "L];  PrintType[t.rangeType];
		PrintTypeInfo[t.rangeType, nBlanks+2]};
	      ENDCASE};
	  ENDCASE};

  OutRecordCtx: PROC [message: STRING, sei: RecordSEIndex] = {
    WriteString[message];
    IF sei = SENull
      THEN WriteString["NIL"L]
      ELSE PrintIndex[seb[sei].fieldCtx]};

  PrintIndex: PROC [v: UNSPECIFIED] = LOOPHOLE[WriteDecimal];

  PrintLink: PROC [link: BcdDefs.Link] = {
    SELECT TRUE FROM
      link.proc => {
	WriteString["proc["L]; WriteDecimal[link.gfi]; WriteChar[',]; WriteDecimal[link.ep]};
      link.type => {WriteString["type["L]; PrintIndex[link.typeID]};
      ENDCASE => {
	WriteString["var["L]; WriteDecimal[link.gfi]; WriteChar[',]; WriteDecimal[link.var]};
    WriteChar[']]};

  END.