-- RTList.mesa  
-- last edited by Satterthwaite on August 1, 1983 1:51 pm

DIRECTORY
  BcdDefs: TYPE USING [Base, FTIndex, FTRecord, VersionStamp, FTNull],
  BcdOps: TYPE USING [BcdBase],
  CharIO: TYPE USING [PutChar, PutDecimal, PutString],
  Environment: TYPE USING [bitsPerWord, wordsPerPage],
  Heap: TYPE USING [systemZone],
  Inline: TYPE USING [LongMult, LowHalf],
  ListerUtil: TYPE USING [PutVersionId],
  RCMap: TYPE USING [Base, FieldDescriptor, Index, Object, RCField],
  RTBcd: TYPE USING [
    RefLitList, RTBase, StampIndex, StampList, RefLitItem, TypeItem, TypeList,
    UTInfo, VersionID, AnyStamp],
  Stream: TYPE USING [Handle],
  Strings: TYPE USING [String],
  TypeStrings: TYPE USING [Code, TypeString];

RTList: PROGRAM
    IMPORTS CharIO, Heap, Inline, ListerUtil
    EXPORTS ListerUtil = {
  
  out: Stream.Handle ← NIL;
  
  PutChar: PROC [c: CHAR] ~ INLINE {CharIO.PutChar[out, c]};
  PutDecimal: PROC [i: INTEGER] ~ INLINE {CharIO.PutDecimal[out, i]};
  PutString: PROC [s: Strings.String] ~ INLINE {CharIO.PutString[out, s]};
  
  PrintRTBcd: PUBLIC PROC [
      dest: Stream.Handle, bcd: BcdOps.BcdBase, sorted: BOOL] ~ {
    rtHeader: RTBcd.RTBase ~ IF bcd.rtPages.pages # 0
      THEN LOOPHOLE[bcd + bcd.rtPages.relPageBase*Environment.wordsPerPage]
      ELSE NIL;
    out ← dest;
    IF rtHeader = NIL THEN PutString["No RT pages\n"L]
    ELSE IF rtHeader.versionIdent # RTBcd.VersionID THEN
      PutString["Invalid RT version stamp\n"L]
    ELSE {
      PrintHeader[rtHeader];
      PrintTypes[rtHeader, bcd, sorted];
      PrintStamps[rtHeader];
      PrintRCMap[rtHeader];
      PrintRefLits[rtHeader, sorted]};
    out ← NIL};
    
  PrintHeader: PROC [rtHeader: RTBcd.RTBase] ~ {
    PutString["Types: "L]; PutDecimal[rtHeader[rtHeader.typeTable].length];
    PutString[", Ref Literals: "L]; PutDecimal[rtHeader[rtHeader.refLitTable].length];
    PutString[", "L];
    PutDecimal[rtHeader.rcMapLength]; PutString[" Words of RC Map"L];
    PutString[", "L];
    PutDecimal[rtHeader.litLength]; PutString[" Words of Literals\n\n"L]};
    
  PrintTypes: PROC [rtHeader: RTBcd.RTBase, bcd: BcdOps.BcdBase, sorted: BOOL] ~ {
    typeList: LONG POINTER TO RTBcd.TypeList ~ @rtHeader[rtHeader.typeTable];
    stampList: LONG POINTER TO RTBcd.StampList ~ @rtHeader[rtHeader.stampTable];
    textBase: LONG POINTER ~ @rtHeader[rtHeader.litBase];

    ftb: BcdDefs.Base ~ LOOPHOLE[bcd, BcdDefs.Base] + bcd.ftOffset;
    ftLimit: BcdDefs.FTIndex ~ bcd.ftLimit;
    
    VersionToFile: PROC [i: RTBcd.StampIndex] RETURNS [fti: BcdDefs.FTIndex] ~ {
      FOR fti ← BcdDefs.FTIndex.FIRST, fti + BcdDefs.FTRecord.SIZE UNTIL fti = ftLimit DO
        IF stampList[i] = ftb[fti].version THEN RETURN;
	ENDLOOP;
      RETURN [BcdDefs.FTNull]};
      
    PrintTypeString: PROC [offset: CARDINAL] RETURNS [valid: BOOL] ~ INLINE {
      text: TypeStrings.TypeString ~ textBase + offset;
      valid ← offset <= rtHeader.litLength
        AND offset+StringBody[text.length].SIZE <= rtHeader.litLength;
      PutChar[' ];
      IF ~valid THEN PrintGarbage[]
      ELSE
	IF PutType[text, 0] # text.length THEN PutString[" ???"L]};
      
    PrintType: PROC [i: NAT] RETURNS [success: BOOL] ~ {
      Tab[2];
      PrintIndex[i];
      PutString[" sei: "L];  PutIndex[typeList[i].sei];
      PutString[", segment: "L];  PrintIndex[typeList[i].table];
      PutString[", rcMap: "L];  PrintIndex[typeList[i].rcMap];
      PutString[", UTF: [stamp: "L];
	IF typeList[i].ut.version = RTBcd.AnyStamp THEN PutString["(any)"L]
	ELSE PrintIndex[typeList[i].ut.version];
	PutString[", sei: "L];
	PutIndex[typeList[i].ut.sei];
	PutChar[']];
      IF typeList[i].canonical THEN PutString[", canonical"L]
      ELSE IF typeList[i].ut.version # RTBcd.AnyStamp THEN {
	fti: BcdDefs.FTIndex ~ VersionToFile[typeList[i].ut.version];
	IF fti # BcdDefs.FTNull THEN {
	  PutString[" (file: "L]; PrintIndex[fti]; PutChar[')]}};
      Tab[4];
      PrintIndex[typeList[i].ct.index];
      RETURN [PrintTypeString[typeList[i].ct.index]]};

    PutString["Types"L];  PrintIndex[rtHeader.typeTable];
    IF sorted THEN PutString[" (ordered)"L];
    PutString[":\n"L];
    IF sorted THEN {
      typeTree: LONG POINTER TO Nodes ← (Heap.systemZone).NEW[Nodes[typeList.length]];
      
      EnterType: PROC [n: NAT] ~ {
	i: Branch ← 0;
	typeTree[n] ← [l~nullBranch, r~nullBranch];
	DO
	  SELECT CompareTypes[typeList[n], typeList[i]] FROM
	    $ls => {
	      IF typeTree[i].l = nullBranch THEN typeTree[i].l ← n;
	      i ← typeTree[i].l};
	    $gr => {
	      IF typeTree[i].r = nullBranch THEN typeTree[i].r ← n;
	      i ← typeTree[i].r};
	    ENDCASE => EXIT
	  ENDLOOP};
     
      PrintBranch: PROC [i: Branch] RETURNS [success: BOOL ← TRUE] ~ {
        UNTIL i = nullBranch OR ~success DO
	  success ← PrintBranch[typeTree[i].l] AND PrintType[i];
	  i ← typeTree[i].r;
	  ENDLOOP;
	RETURN}; 

      FOR n: NAT IN [0 .. typeList.length) DO EnterType[n] ENDLOOP;
      [] ← PrintBranch[IF typeList.length = 0 THEN nullBranch ELSE 0];
      (Heap.systemZone).FREE[@typeTree]}
    ELSE
      FOR i: NAT IN [0 .. typeList.length) DO
        IF ~PrintType[i] THEN EXIT;
        ENDLOOP;
    PutString["\n\n"L]};
    
    
  PrintStamps: PROC [rtHeader: RTBcd.RTBase] ~ {
    stampList: LONG POINTER TO RTBcd.StampList ~ @rtHeader[rtHeader.stampTable];
    PutString["Version Stamps"L];
    PrintIndex[rtHeader.stampTable];
    PutString[":\n"L];
    FOR i: NAT IN [1 .. stampList.limit) DO
      Tab[2];
      PrintIndex[i];
      PutChar[' ];
      ListerUtil.PutVersionId[out, stampList[i]];
      ENDLOOP;
    PutString["\n\n"L]};


  PrintRCMap: PROC [rtHeader: RTBcd.RTBase] ~ {
    rcmb: RCMap.Base ~ LOOPHOLE[@rtHeader[rtHeader.rcMapBase]];
    next: RCMap.Index;
    PutString["RC Maps"L];
    PrintIndex[CARDINAL[LOOPHOLE[rtHeader.rcMapBase, LONG CARDINAL]]];
    PutString[":\n"L];
    FOR rcmi: RCMap.Index ← RCMap.Index.FIRST, next
     WHILE LOOPHOLE[rcmi, CARDINAL] < rtHeader.rcMapLength DO
      Tab[2];
      PrintIndex[rcmi];
      PutChar[' ];
      WITH m~~rcmb[rcmi] SELECT FROM
        null => {
	  PutString["null"L];
	  next ← rcmi + RCMap.Object.null.SIZE};
        ref => {
	  PutString["ref"L];
	  next ← rcmi + RCMap.Object.ref.SIZE};
        controlLink => {
	  PutString["controlLink"L];
	  next ← rcmi + RCMap.Object.controlLink.SIZE};
        oneRef => {
	  PutString["oneRef[offset: "L];  PutDecimal[m.offset];  PutChar[']];
	  next ← rcmi + RCMap.Object.oneRef.SIZE};
        simple => {
	  PutString["simple[length: "L];  PutDecimal[m.length];
	  PutString[", offsets: ["L];
	  FOR i: NAT IN [0 .. m.length) DO
	    IF m.refs[i] THEN {
	      PutDecimal[i];
	      IF i + 1 # m.length THEN PutString[", "L]};
	    ENDLOOP;
	  PutString["]]"L];
	  next ← rcmi + RCMap.Object.simple.SIZE};
        nonVariant => {
	  PutString["nonVariant[nComponents: "L];  PutDecimal[m.nComponents];
	  PutString[", components: ["L];
	  FOR i: NAT IN [0..m.nComponents) DO
	    IF i MOD 3 = 0 THEN Tab[8];
	    PrintField[m.components[i]];
	    IF i+1 # m.nComponents THEN PutString[", "L];
	    ENDLOOP;
	  PutString["]]"L];
	  next ← rcmi + (RCMap.Object.nonVariant.SIZE + m.nComponents*RCMap.RCField.SIZE)};
        variant => {
	  PutString["variant[fdTag: "L];  PrintFD[m.fdTag];
	  PutString[", nVariants: "L];  PutDecimal[m.nVariants];
	  PutString[", variants: ["L];
	  FOR i: NAT IN [0..m.nVariants) DO
	    PrintIndex[m.variants[i]];
	    IF i+1 # m.nVariants THEN PutString[", "L];
	    ENDLOOP;
	  PutString["]]"L];
	  next ← rcmi + (RCMap.Object.variant.SIZE + m.nVariants*RCMap.Index.SIZE)};
        array => {
	  PutString["array[wordsPerElement: "L];  PutDecimal[m.wordsPerElement];
	  PutString[", nElements: "L];  PutDecimal[m.nElements];
	  PutString[", rcmi: "L];  PrintIndex[m.rcmi];  PutChar[']];
	  next ← rcmi + RCMap.Object.array.SIZE};
        sequence => {
	  PutString["sequence[wordsPerElement: "L];  PutDecimal[m.wordsPerElement];
	  PutString[", fdLength: "L];  PrintFD[m.fdLength];
	  PutString[", dataOffset: "L];  PutDecimal[m.dataOffset];
	  PutString[", rcmi: "L];  PrintIndex[m.rcmi];  PutChar[']];
	  next ← rcmi + RCMap.Object.sequence.SIZE};
	ENDCASE => {PrintGarbage[]; EXIT};
      ENDLOOP;
    PutString["\n\n"L]};
    
  PrintField: PROC [f: RCMap.RCField] ~ {
    PutString["[offset: "L];  PutDecimal[f.wordOffset];
    PutString[", rcmi: "L];  PrintIndex[f.rcmi];  PutChar[']]};
    
  PrintFD: PROC [fd: RCMap.FieldDescriptor] ~ {
    PutChar['(];  PutDecimal[fd.wordOffset];
    IF fd.bitFirst # 0 OR fd.bitCount # Environment.bitsPerWord THEN {
      PutChar[':];  PutDecimal[fd.bitFirst];
      PutString[".."L];  PutDecimal[fd.bitFirst + fd.bitCount - 1]};
    PutChar[')]};
    
        
  PrintRefLits: PROC [rtHeader: RTBcd.RTBase, sorted: BOOL] ~ {
    litList: LONG POINTER TO RTBcd.RefLitList ~ @rtHeader[rtHeader.refLitTable];
    textBase: LONG POINTER ~ @rtHeader[rtHeader.litBase];

    PutLitString: PROC [offset, length: CARDINAL] RETURNS [valid: BOOL] ~ INLINE {
      text: LONG POINTER TO TEXT ~ textBase + offset;
      valid ← offset + length <= rtHeader.litLength AND length = TEXT[text.length].SIZE;
      IF valid THEN PrintText[text] ELSE PrintGarbage[]};
      
    PrintRefLit: PROC [i: NAT] RETURNS [valid: BOOL] ~ {
      Tab[2];
      PrintIndex[i];
      PutString[" type: "L];
      PrintIndex[litList[i].referentType];
      PutString[",  "L];
      RETURN [PutLitString[litList[i].offset, litList[i].length]]};
    
    PutString["Atoms and REF Literals"L];  PrintIndex[rtHeader.refLitTable];
    IF sorted THEN PutString[" (ordered)"L];
    PutString[":\n"L];
    IF sorted THEN {
      litTree: LONG POINTER TO Nodes ← (Heap.systemZone).NEW[Nodes[litList.length]];
      
      EnterLit: PROC [n: NAT] ~ {
	i: Branch ← 0;
	litTree[n] ← [l~nullBranch, r~nullBranch];
	DO
	  SELECT CompareLits[litList[n], litList[i]] FROM
	    $ls => {
	      IF litTree[i].l = nullBranch THEN litTree[i].l ← n;
	      i ← litTree[i].l};
	    $gr => {
	      IF litTree[i].r = nullBranch THEN litTree[i].r ← n;
	      i ← litTree[i].r};
	    ENDCASE => EXIT
	  ENDLOOP};
     
      PrintBranch: PROC [i: Branch] RETURNS [success: BOOL ← TRUE] ~ {
        UNTIL i = nullBranch OR ~success DO
	  success ← PrintBranch[litTree[i].l] AND PrintRefLit[i];
	  i ← litTree[i].r;
	  ENDLOOP;
	RETURN}; 

      FOR n: NAT IN [0 .. litList.length) DO EnterLit[n] ENDLOOP;
      [] ← PrintBranch[IF litList.length = 0 THEN nullBranch ELSE 0];
      (Heap.systemZone).FREE[@litTree]}
    ELSE
      FOR i: NAT IN [0 .. litList.length) DO
        IF ~PrintRefLit[i] THEN EXIT;
        ENDLOOP;
    PutString["\n\n"L]};
    
    
  PutIndex: PROC [index: UNSPECIFIED] ~ INLINE {PutDecimal[LOOPHOLE[index]]};
    
  PutType: PROC [s: TypeStrings.TypeString, i: CARDINAL] RETURNS [CARDINAL] ~ {
    
    PutCode: PROC [c: TypeStrings.Code] ~ {
      SELECT c FROM
	$leftParen => PutChar['[];
	$definition => PutChar['&];
	$name => PutChar['.];
	$ref => PutChar['@];
	$list => PutChar['*];
	ENDCASE => {
	  repr: NAT = c.ORD;
	  offset: NAT = repr - 200b;
	  PutChar[VAL[IF offset < 'Z.ORD-'A.ORD+1
	    THEN 'A.ORD + offset
	    ELSE 'a.ORD + (offset - ('Z.ORD-'A.ORD+1))]]}};
      
    Skip: PROC [nBytes: CARDINAL] ~ {THROUGH [1..nBytes] DO i ← i+1 ENDLOOP};
      
    PutNum: PROC [nBytes: [1..2]] ~ {
      v: CARDINAL ← 0;
      THROUGH [1..nBytes] DO
        v ← 256*v + (s[i]-0c); i ← i+1;
	ENDLOOP;
      PutDecimal[v]};
      
    PutId: PROC ~ {
      n: NAT ~ s[i].ORD;
      PutChar[''];  i ← i + 1;
      THROUGH [1..n] DO
        PutChar[s[i]];  i ← i+1
	ENDLOOP;
      PutChar['']};
      
    PutPaint: PROC ~ {
      hex: STRING ~ "0123456789abcdef"L;
      PutChar['{];
      THROUGH [1..6] DO
        v: NAT ~ s[i] - 0c;
	PutChar[hex[v/16]]; PutChar[hex[v MOD 16]];
	i ← i + 1;
	ENDLOOP;
      PutNum[2];
      PutChar['}]};
      
    PutSubType: PROC ~ {
      c: TypeStrings.Code ~ LOOPHOLE[s[i]];
      PutCode[c];
      i ← i + 1;
      SELECT c FROM
        $definition => {PutNum[1]; PutSubType[]};
        $name => {PutNum[1]};
        $leftParen => {
	  WHILE LOOPHOLE[s[i], TypeStrings.Code] # $rightParen DO
	    PutId[]; PutSubType[];
	    ENDLOOP;
	  PutChar[']]; i ← i+1};

        $paint, $opaque, $union => {PutPaint[]};

        $subrange => {
	  PutSubType[];
	  Skip[2]; PutNum[2];
	  PutChar[',];
	  Skip[2]; PutNum[2]};

        $sequence => {PutId[]; PutSubType[]; PutSubType[]};

        $array, $relativeRef,
        $port, $program, $procedure, $safeProc, $signal => {
	  PutSubType[]; PutSubType[]};	-- binary

        $list, $ref, $var, $pointer, $longPointer, $descriptor, $longDescriptor,
	$process, $error,
	$readOnly, $packed, $ordered =>
	  PutSubType[];	-- unary

        ENDCASE => NULL};	-- nullary
      
    PutChar['(]; PutDecimal[s.length]; PutChar[')]; PutChar[' ];
    PutSubType[];
    RETURN [i]};
  

  PrintIndex: PROC [index: UNSPECIFIED] ~ {
    PutString["["L];  PutDecimal[index];  PutChar[']]};
    
  PrintText: PROC [t: LONG POINTER TO TEXT] ~ {
    IF t = NIL THEN PutString["(nil)"L]
    ELSE
      FOR i: NAT IN [0 .. t.length) DO PutChar[t[i]] ENDLOOP};
    
  PrintGarbage: PROC ~ INLINE {
    PutString["? Looks like garbage ...\n"L]};
    
  Tab: PROC [n: CARDINAL] ~ {
    PutChar['\n];
    THROUGH [1..n/8] DO PutChar['\t] ENDLOOP;
    THROUGH [1..n MOD 8] DO PutChar[' ] ENDLOOP};


 -- auxiliary types and predicates for sorting (from BcdLiteralsImpl)
 
  Relation: TYPE ~ {ls, gr, eq};
  Branch: TYPE ~ CARDINAL --[0..NAT.LAST+1]--;
  nullBranch: Branch ~ NAT.LAST+1;
  Nodes: TYPE ~ RECORD [SEQUENCE length: NAT OF RECORD [l, r: Branch]];
  
  Scramble: PROC [n: CARDINAL] RETURNS [WORD] ~ INLINE {  -- see Knuth, v 3, p. 509-511
    RETURN [Inline.LowHalf[Inline.LongMult[n, 44451]]]};
    
  CompareTypes: PROC [l, r: RTBcd.TypeItem] RETURNS [Relation] ~ {
    sl: WORD ~ Scramble[l.ct];
    sr: WORD ~ Scramble[r.ct];
    RETURN [
      SELECT sl FROM
	< sr => $ls,  > sr => $gr,
	ENDCASE => 
	  SELECT TRUE FROM
	    l.canonical AND ~r.canonical => $ls,
	    ~l.canonical AND r.canonical => $gr,
	    ENDCASE =>	-- l.canonical = r.canonical
	      IF l.canonical THEN $eq ELSE CompareUTFs[l.ut, r.ut]]};

  CompareUTFs: PROC [l, r: RTBcd.UTInfo] RETURNS [Relation] ~ {
    UTWords: TYPE ~ ARRAY [0 .. RTBcd.UTInfo.SIZE) OF WORD;
    FOR i: NAT IN [0 .. RTBcd.UTInfo.SIZE) DO
      SELECT LOOPHOLE[l, UTWords][i] FROM
        < LOOPHOLE[r, UTWords][i] => RETURN [$ls];
	> LOOPHOLE[r, UTWords][i] => RETURN [$gr];
	ENDCASE;
      ENDLOOP;
    RETURN [$eq]};

  CompareLits: PROC [l, r: RTBcd.RefLitItem] RETURNS [Relation] ~ {
    sl: WORD ~ Scramble[l.offset];
    sr: WORD ~ Scramble[r.offset];
    RETURN [SELECT sl FROM
      < sr => $ls,
      > sr => $gr,
      ENDCASE => SELECT l.length FROM
	= r.length =>
	  SELECT l.referentType - r.referentType FROM
	    = 0 => $eq, > 0 => $gr, ENDCASE => $ls,
	< r.length => $ls,
	ENDCASE => $gr]};

  }.