File IDMAP.PS

Directory of image this file is from
This file as a plain text file

{$X+}
Program IdMap(Source	: 'DSK:.ps',
	      Listing	: 'LP:listing.ls',
	      Output	: 'TT:',
	      UsageTable: 'temp.tm');  {$T-,P-,N- }

  { Pascal Source Cross Referencer, 1980-03-18.

    Copyright (c) 1980, by

      James F. Miner
      Social Science Research Facilities Center
      University of Minnesota
      Minneapolis, MN  55455
      U.S.A.

    All rights reserved.  }{$L-}

  { Modified on 1980-08-06 to use indexed file for usage table. }
  { Modified on 1981-08-27 to fix bug in indexed file use. }

  const
    AlphaLength =     10;	{ number of chars per Alpha, must be }
				{ > length of every reserved word. }
    HashSize =	      1499;	{ number of entries in hash table, }
				{ must be odd, preferably prime. }
    HashFactor =      3;	{ used in hash function, must be > 0. }
    MaxSourceWidth =  150;	{ significant characters per source line. }
    LinesOnPage =     60;	{ non-blank lines printed per page. }
    TopMargin =       3;	{ blank lines at top of page, >= 0. }
    NumsOnLine =      13;	{ line number slots per line in map; }
				{ map width = 1 + NumsOnLine*NumWidth. }
    NumWidth =	      6;	{ width of line number slots in map. }
    NameUndent =      2;	{ line number slots used for short names. }
    ShiftBias =       -32;	{ = ord (upper case) - ord(lower case), }
				{ = -32 (ASCII), 64 (EBCDIC), 0 (single case)}
    MaxSimilar =      8;	{ significant characters in similarity test. }
    MaxBlock =	      4095;	{ maximum index for usage file. }
    UsagesPerBlock =  64;	{ in UsageTable. }

  type
    Natural = 0..MaxInt;
    HashIndex = 0..HashSize;
    AlphaRange = 1..AlphaLength;
    Alpha = packed array [AlphaRange] of Char;
    CaseSet = packed set of AlphaRange;

    SegmentPointer = ^ SegmentRecord;
    SegmentRecord =		{ piece of an identifier. }
      packed record
	Image: Alpha;
	CaseShift: CaseSet;
	NextSegment: SegmentPointer
      end;

    IdentRecord =		{ representing an identifier. }
      packed record
	Length: 0..MaxSourceWidth;   { chars in identifier. }
	FirstSegment: SegmentRecord  { first segment in list. }
      end;

    UsagePointer =
      packed record
	BlockNum: 0..MaxBlock;
	BlockInx: 1..UsagesPerBlock
      end;
    UsageRecord =		{ record of a use of an identifier. }
      packed record
	LineNum: Natural;
      case { pass 1 : } Boolean of
	True:	(IdIndex: HashIndex);
	False:	(NextUsage: UsagePointer)
      end;

    EntryPointer = ^ HashEntry;
    HashEntry = 		{ each entry in the hash table. }
      record
	Ident: IdentRecord;
	ConsistentCase: Boolean;  { all uses are consistently capitalized. }
      case Reserved: Boolean of
	False: (UsageList: UsagePointer); { first component of a linear list. }
	True:  ()
      end;

  var
    Listing, Source: Text;
    ListingInfo:
      record
	LinesRemainingOnPage: 0..LinesOnPage;
	PageCount: Natural
      end;
    IdentTable:
      record
	Entries:		{ the hash table. }
	  array [HashIndex] of EntryPointer;
	ReserveCount,		{ the number of reserved entries. }
	EntryCount: HashIndex;	{ the number of non-empty entries. }
	MaxHashSum: Natural	{ to avoid overflow in the hash function. }
      end;
    UsageTable:
      file [1..MaxBlock] of
	array [1..UsagesPerBlock] of
	  UsageRecord;
    HighUsage, NilUsage: UsagePointer;
    CurrentBlock: 0..MaxBlock;
    UpdateBlock: Boolean;
    UsageCount: Natural;


  procedure StartLineOfListing;
    var
      Count:  0..TopMargin;
  begin { StartLineOfListing }
    with ListingInfo do
      if LinesRemainingOnPage = 0 then
	begin
	  PageCount := PageCount + 1;
	  Page(Listing);
	  Count := TopMargin;
	  while Count > 0 do begin Count := Count - 1; WriteLn(Listing) end;
	  LinesRemainingOnPage := LinesOnPage - 1
	end
      else
	LinesRemainingOnPage := LinesRemainingOnPage - 1;
    Write(Listing, ' ');
  end { StartLineOfListing } ;

  function NonNil(UsePtr: UsagePointer): Boolean;
  begin
    NonNil := UsePtr.BlockNum <> 0
  end { NonNil } ;

  procedure NewUsage(var UsePtr: UsagePointer);
  begin
    with HighUsage do
      if BlockInx = UsagesPerBlock then
	begin BlockInx := 1;  BlockNum := BlockNum + 1 end
      else BlockInx := BlockInx + 1;
    UsePtr := HighUsage
  end { NewUsage } ;

  procedure GetUsage(UsePtr: UsagePointer;  var Use: UsageRecord);
  begin
    with UsePtr do
      begin
	if BlockNum <> CurrentBlock then
	  begin
	    if UpdateBlock then Put(UsageTable, CurrentBlock);
	    Get(UsageTable, BlockNum);
	    CurrentBlock := BlockNum;
	    UpdateBlock := False
	  end;
	Use := UsageTable^[BlockInx]
      end
  end { GetUsage } ;

  procedure PutUsage(UsePtr: UsagePointer;  Use: UsageRecord);
  begin
    with UsePtr do
      begin
	if BlockNum <> CurrentBlock then
	  begin
	    if UpdateBlock then Put(UsageTable, CurrentBlock);
	    if BlockNum <= Length(UsageTable) then
	      Get(UsageTable, BlockNum);
	    CurrentBlock := BlockNum
	  end;
	UsageTable^[BlockInx] := Use;
	UpdateBlock := True
      end
  end { PutUsage } ;

  procedure ScanSource;

    var
      SourceInfo:
	record
	  CharCount:  Natural;	       { chars seen on current line. }
	  LineCount:  Natural;	       { current line number. }
	  InComment:  Boolean;	       { scanning a comment. }
	end;
      SpareSegments:		       { used by DoIdentifier. }
	record
	  First, Last: SegmentPointer  { never nil. }
	end;
      NullImage: Alpha; 	       { Structured "constants"... }
      Digits,
      DigitsE,
      UpperCaseLetters,
      LettersAndDigits,
      IDContinuators,
      ImportantChars:  set of Char;

    procedure StartSourceLine;
    begin { StartSourceLine }
      StartLineOfListing;
      with SourceInfo do
	begin
	  LineCount := LineCount + 1;
	  Write(Listing, LineCount:5);
	  if InComment then Write(Listing, '.') else Write(Listing, ' ');
	  CharCount := 1;  Write(Listing, Source^)
	end
    end { StartSourceLine } ;

    procedure NextChar;
    begin { NextChar }
      { not Eof(Source) }
      with SourceInfo do
	if Eoln(Source) then
	  begin  WriteLn(Listing);  ReadLn(Source);
	    if not Eof(Source) then StartSourceLine
	  end
	else
	  if CharCount >= MaxSourceWidth then
	    repeat Get(Source); Write(Listing, Source^) until Eoln(Source)
	  else
	    begin Get(Source); Write(Listing, Source^);
	      CharCount := CharCount + 1
	    end
    end { NextChar } ;

    procedure DoIdentifier;

      var
	SourceId:
	  record
	    Id: IdentRecord;
	    LastSeg: SegmentPointer;
	    HashCode:  HashIndex;
	  end;

      procedure GetSourceId;

	var
	  Len:	Natural;
	  Seg: SegmentPointer;
	  SegImage: Alpha;
	  SegShift: CaseSet;
	  HashSum: Natural;

	procedure NewSegment;
	begin { NewSegment }
	  with SpareSegments do
	    if First <> Last then
	      begin Seg := First;  First := Seg^.NextSegment end
	    else New(Seg)
	end { NewSegment } ;

	procedure GatherSegment;
	  var
	    SegIndex:  0..AlphaLength;
	    LowerCh: Char;
	begin { GatherSegment }
	  SegIndex := 0;  SegImage := NullImage;  SegShift := [];
	  repeat { Source^ in IDContinuators }
	    SegIndex := SegIndex + 1;
	    if Source^ in UpperCaseLetters then
	      begin  SegShift := SegShift + [SegIndex];
		LowerCh := Chr( Ord(Source^) - ShiftBias )
	      end
	    else LowerCh := Source^;
	    SegImage[SegIndex] := LowerCh;
	    if HashSum > IdentTable.MaxHashSum then
	      HashSum := HashSum mod HashSize;
	    HashSum := HashSum * HashFactor + Ord(LowerCh);
	    NextChar
	  until not (Source^ in IDContinuators) or (SegIndex = AlphaLength);
	  Len := Len + SegIndex
	end { GatherSegment } ;

      begin { GetSourceId }
	SourceId.LastSeg := nil;  HashSum := 0;  Len := 0;
	GatherSegment;
	with SourceId.Id.FirstSegment do
	  begin
	    Image := SegImage; CaseShift := SegShift; NextSegment := nil
	  end;
	while Source^ in LettersAndDigits do
	  begin  GatherSegment;  NewSegment;
	    with Seg^ do
	      begin Image := SegImage;	CaseShift := SegShift;
		NextSegment := nil
	      end;
	    if SourceId.LastSeg = nil then
	      SourceId.Id.FirstSegment.NextSegment := Seg
	    else SourceId.LastSeg^.NextSegment := Seg;
	    SourceId.LastSeg := Seg
	  end;
	SourceId.Id.Length := Len;
	if MaxInt-Len < HashSum then
	  HashSum := HashSum mod HashSize;
	SourceId.HashCode := (HashSum + Len) mod HashSize
      end { GetSourceId } ;

      procedure RecordUseOfSourceId;
	var
	  Entered: Boolean;
	  Skip: HashIndex;
	  Index: Natural;
	  Use: UsageRecord;  UsePtr: UsagePointer;
	  SameSpelling, SameCase: Boolean; { set by CompareIdents. }

	procedure CompareIdents(var A, B: IdentRecord);
	  { Sets SameSpelling = ("A and B have same spelling"). }
	  { If SameSpelling, then SameCase indicates A and B have }
	  { identical capitalization. }
	  var
	    SegA, SegB:  SegmentPointer;
	    StillChecking:  Boolean;
	begin { CompareIdents }
	  if (A.Length = B.Length) and
	     (A.FirstSegment.Image = B.FirstSegment.Image)  then
	    begin
	      SegA := A.FirstSegment.NextSegment;
	      SegB := B.FirstSegment.NextSegment;
	      SameSpelling := True;  StillChecking := SegB <> nil;
	      SameCase := A.FirstSegment.CaseShift = B.FirstSegment.CaseShift;
	      while StillChecking do
		if SegA^.Image = SegB^.Image then
		  begin
		    if SegA^.CaseShift <> SegB^.CaseShift then
		      SameCase := False;
		    SegA := SegA^.NextSegment;
		    SegB := SegB^.NextSegment;
		    StillChecking := SegB <> nil
		  end
		else
		  begin  StillChecking := False;  SameSpelling := False end
	    end
	  else SameSpelling := False
	end { CompareIdents } ;

	procedure ReclaimSegments;
	begin { ReclaimSegments }
	  with SourceId do
	    if LastSeg <> nil then
	      begin
		SpareSegments.Last^.NextSegment :=
		  Id.FirstSegment.NextSegment;
		SpareSegments.Last := LastSeg
	      end
	end { ReclaimSegments } ;

      begin { RecordUseOfSourceId }
	with IdentTable do
	  begin
	    Entered := False;  Skip := 1;  Index := SourceId.HashCode;
	    repeat
	      if Entries[Index] = nil then { Empty Entry }
		begin
		  New(Entries[Index]);
		  with Entries[Index]^ do
		    begin Ident := SourceId.Id;  Reserved := False;
		      ConsistentCase := True;
		      NewUsage(UsePtr);  UsageCount := UsageCount + 1;
		      Use.LineNum := SourceInfo.LineCount;
		      Use.IdIndex := Index;  UsageList := NilUsage;
		      PutUsage(UsePtr, Use)
		    end;
		  EntryCount := EntryCount + 1;  Entered := True
		end
	      else
		with Entries[Index]^ do
		  begin   CompareIdents(Ident,SourceId.Id);
		    if SameSpelling then
		      begin  Entered := True;  { Entry already exists }
			if not Reserved then { not reserved }
			  begin
			    NewUsage(UsePtr);  UsageCount := UsageCount + 1;
			    Use.LineNum := SourceInfo.LineCount;
			    Use.IdIndex := Index;
			    PutUsage(UsePtr, Use);
			    if not SameCase then ConsistentCase := False
			  end;
			ReclaimSegments
		      end
		    else
		      begin { collision }
			Index := Index + Skip;	Skip := Skip + 2;
			if Index >= HashSize then Index := Index - HashSize;
			if Skip = HashSize then
			  begin WriteLn(Output, ' Name table full!'); Halt end
		      end
		  end
	    until Entered
	  end
      end { RecordUseOfSourceId } ;

    begin { DoIdentifier }
      GetSourceId;
      RecordUseOfSourceId
    end { DoIdentifier } ;

    procedure SkipString;
      var  Quote: Char;
    begin { SkipString }
      Quote := Source^;
      repeat NextChar until Eoln(Source) or (Source^ = Quote);
      if Source^ <> Quote then
	WriteLn(Output, ' Unclosed string at line number ',
			SourceInfo.LineCount : 1);
      NextChar
    end { SkipString } ;

    procedure SkipComment;
    begin { SkipComment }
      NextChar;
      with SourceInfo do
	begin  InComment := True;
	  repeat { InComment and not Eof(Source) }
	    if Source^ = '}' then InComment := False
	    else
	      if Source^ = '*' then
		begin  NextChar;
		  if Source^ = ')' then InComment := False
		end
	      else NextChar
	  until Eof(Source) or not InComment;
	  if not InComment then NextChar
	end
    end { SkipComment } ;

    procedure InitScan;
      var
	Seg: SegmentPointer;
	SegIndex: AlphaRange;
    begin { InitScan }
      with SourceInfo do
	begin
	  LineCount := 0;  CharCount := 0;  InComment := False
	end;
      StartSourceLine;
      New(Seg);
      with SpareSegments do
	begin  First := Seg;  Last := Seg;  end;
      for SegIndex := 1 to AlphaLength do NullImage[SegIndex] := ' ';
      Digits := ['0','1','2','3','4','5','6','7','8','9'];
      DigitsE := Digits + ['E','e'];
      UpperCaseLetters :=
		['A','B','C','D','E','F','G','H','I','J','K','L','M',
		 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z'];
      LettersAndDigits := Digits + UpperCaseLetters +
		['a','b','c','d','e','f','g','h','i','j','k','l','m',
		 'n','o','p','q','r','s','t','u','v','w','x','y','z'];
      IDContinuators := LettersAndDigits + ['_'];
      ImportantChars := LettersAndDigits + ['''','{','('];
    end { InitScan } ;

  begin { ScanSource }
    InitScan;
    while not Eof(Source) do
      begin
	if Source^ in ImportantChars then
	  case Source^ of
	    'A','B','C','D','E','F','G','H','I','J','K','L','M',
	    'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
	    'a','b','c','d','e','f','g','h','i','j','k','l','m',
	    'n','o','p','q','r','s','t','u','v','w','x','y','z':
	      DoIdentifier;
	    '0','1','2','3','4','5','6','7','8','9':
	      repeat NextChar until not (Source^ in DigitsE);
	    '''': SkipString;
	    '{':  SkipComment;
	    '(':  begin  NextChar;
		    if Source^ = '*' then SkipComment
		  end
	  end { case }
	else NextChar
      end { while not Eof(Source) } ;
    StartLineOfListing;  WriteLn(Listing);
    StartLineOfListing;
    WriteLn(Listing, (IdentTable.EntryCount - IdentTable.ReserveCount):1,
		     ' names, ',  UsageCount:1, ' occurrences.');
    if SourceInfo.InComment then
      WriteLn(Output, ' Unclosed comment at end of source!')
  end { ScanSource } ;

  procedure PrintMap;

    procedure LinkUsages;
      var
	Use: UsageRecord;
	UsePtr: UsagePointer;
    begin
      UsePtr := HighUsage;
      while NonNil(UsePtr) do
	begin  GetUsage(UsePtr, Use);
	  with Use, IdentTable.Entries[IdIndex]^ do
	    begin  NextUsage := UsageList;  UsageList := UsePtr end;
	  PutUsage(UsePtr, Use);
	  { move toward beginning of table }
	  with UsePtr do
	    if BlockInx = 1 then
	      begin  BlockInx := UsagesPerBlock;  BlockNum := BlockNum - 1 end
	    else BlockInx := BlockInx - 1
	end
    end { LinkUsages } ;

    procedure CompressEntries;
      { In IdentTable, entries which are nil are considered empty.
	CompressEntries moves non-empty entries with indices
	EntryCount .. HashSize-1 down into the empty entries
	with indices 0 .. EntryCount-1.
	Assumes EntryCount > 0. }

      var
	Low, High: HashIndex;

    begin { CompressEntries }
      with IdentTable do
	begin
	  Low := 0;  High := HashSize-1;
	  while Entries[High] = nil do High := High - 1;
	  while High >= EntryCount do
	    begin
	      while Entries[Low] <> nil do Low := Low + 1;
	      Entries[Low] := Entries[High];  Low := Low + 1;
	      repeat High := High - 1
	      until Entries[High] <> nil
	    end
	end
    end { CompressEntries } ;

    procedure SortEntries(Min, Max: HashIndex);
      { QuickSort with bounded recursion of elements Min..Max of
	IdentTable.Entries.  Assumes Min <= Max. }
      var
	Low, High: -1..HashSize;
	MidKey: IdentRecord;
	TempEntry: EntryPointer;

      function Ascending(var A, B: IdentRecord) : Boolean;
	{ Check string A < string B }
	var
	  ASeg, BSeg:  SegmentPointer;
	  Done: Boolean;
      begin { Ascending }
	if A.FirstSegment.Image < B.FirstSegment.Image then
	  Ascending := True
	else
	  if A.FirstSegment.Image > B.FirstSegment.Image then
	    Ascending := False
	  else
	    begin  { check subsequent segments }
	      ASeg := A.FirstSegment.NextSegment;
	      BSeg := B.FirstSegment.NextSegment;
	      repeat
		if BSeg = nil then
		  begin Ascending := False;  Done := True end
		else
		  if ASeg = nil then
		    begin Ascending := True;  Done := True end
		  else
		    begin
		      Done := ASeg^.Image <> BSeg^.Image;
		      if Done then
			Ascending := ASeg^.Image < BSeg^.Image
		      else
			begin
			  ASeg := ASeg^.NextSegment;
			  BSeg := BSeg^.NextSegment
			end
		    end
	      until Done
	    end
      end { Ascending } ;

    begin { SortEntries }
      repeat { pick split point }
	with IdentTable do
	  begin
	    MidKey := Entries[(Min + Max) div 2]^.Ident;
	    Low := Min;  High := Max;
	    repeat { partition }
	      while Ascending(Entries[Low]^.Ident,MidKey) do Low := Low + 1;
	      while Ascending(MidKey,Entries[High]^.Ident) do High := High - 1;
	      if Low <= High then
		begin
		  TempEntry := Entries[Low];
		  Entries[Low] := Entries[High];
		  Entries[High] := TempEntry;
		  Low := Low + 1;
		  High := High - 1
		end;
	    until Low > High
	  end;

	{ recursively sort shorter sub-sequence }
	if High - Min < Max - Low then
	  begin
	    if Min < High then SortEntries(Min,High);
	    Min := Low
	  end
	else
	  begin
	    if Low < Max then SortEntries(Low,Max);
	    Max := High
	  end
      until Max <= Min
    end { SortEntries };

    procedure PrintEntries;
      var
	Index, PrevIndex:  HashIndex;
	Use: UsageRecord;  UsePtr: UsagePointer;
	NumberCount:  Natural;
	ThisSegment: SegmentRecord;
	Len:  Natural;
	FoundInconsistency, FoundSimilarity, Similar: Boolean;

      procedure CheckSimilarity(var A, B: IdentRecord);
	{ Test A and B for similarity over their first MaxSimilar chars. }
	{ Similar gets the results of the test. }
	var
	  SegmentA, SegmentB: SegmentRecord;
	  Len: 0..MaxSimilar;
	  SegIndex: 0..AlphaLength;
      begin { CheckSimilarity }
	if A.Length > B.Length then CheckSimilarity(B, A)
	else { A.Length <= B.Length }
	  if A.Length >= MaxSimilar then
	    begin
	      Len := MaxSimilar;  Similar := True;  SegIndex := 0;
	      SegmentA := A.FirstSegment;  SegmentB := B.FirstSegment;
	      while (Len > 0) and Similar do
		begin  Len := Len - 1;
		  if SegIndex = AlphaLength then
		    begin  SegIndex := 0;
		      SegmentA := SegmentA.NextSegment^;
		      SegmentB := SegmentB.NextSegment^
		    end;
		  SegIndex := SegIndex + 1;
		  if SegmentA.Image[SegIndex] <> SegmentB.Image[SegIndex] then
		    Similar := False
		end
	    end
	  else Similar := False
      end { CheckSimilarity } ;

      procedure WriteSegment(Length: AlphaRange);
	var
	  SegIndex: AlphaRange;
      begin { WriteSegment }
	with ThisSegment do
	  for SegIndex := 1 to Length do
	    if SegIndex in CaseShift then
	      Write(Listing, Chr( Ord(Image[SegIndex]) + ShiftBias ))
	    else Write(Listing, Image[SegIndex])
      end { WriteSegment } ;

    begin { PrintEntries }
      FoundInconsistency := False;  FoundSimilarity := False;
      PrevIndex := HashSize;
      with IdentTable do
	for Index := 0 to EntryCount-1 do
	  with Entries[Index]^ do
	    if not Reserved then
	      begin  StartLineOfListing;
		if PrevIndex < HashSize then
		  begin  CheckSimilarity(Entries[PrevIndex]^.Ident,Ident);
		    if Similar then
		      begin Write(Listing, '?'); FoundSimilarity := True end
		  end
		else Similar := False;
		if not ConsistentCase then
		  begin Write(Listing, '*'); FoundInconsistency := True end;
		if Similar or not ConsistentCase then Write(Listing, ' ');
		with Ident do
		  begin  Len := Length;  ThisSegment := FirstSegment;
		    while Len > AlphaLength do
		      begin  Len := Len - AlphaLength;
			WriteSegment(AlphaLength);
			ThisSegment := ThisSegment.NextSegment^
		      end;
		    WriteSegment(Len);
		    Len := Length
		  end;
		if Similar or not ConsistentCase then
		  Len := Len + Ord(Similar) + Ord(not ConsistentCase) + 1;
		PrevIndex := Index;
		NumberCount := (Len+NumWidth-1) div NumWidth;
		if (Len mod NumWidth <> 0) and (NumberCount < NumsOnLine) then
		  Write(Listing, ' ' : NumWidth - Len mod NumWidth);
		if NumberCount < NameUndent then
		  begin
		    Write(Listing, ' ' : NumWidth * (NameUndent - NumberCount));
		    NumberCount := NameUndent
		  end;
		UsePtr := UsageList;
		while NonNil(UsePtr) do
		  begin
		    if NumberCount >= NumsOnLine then
		      begin WriteLn(Listing); StartLineOfListing;
			Write(Listing, ' ': NameUndent * NumWidth);
			NumberCount := NameUndent
		      end;
		    NumberCount := NumberCount + 1;
		    GetUsage(UsePtr, Use);
		    Write(Listing, Use.LineNum : NumWidth);
		    UsePtr := Use.NextUsage
		  end;
		WriteLn(Listing)
	      end;
      if FoundInconsistency or FoundSimilarity then
	begin StartLineOfListing;  WriteLn(Listing);
	  if FoundSimilarity then
	    begin  StartLineOfListing;
	      WriteLn(Listing, 'Identifiers marked with a "?" are similar ',
			       'to the previous identifiers');
	      StartLineOfListing;
	      WriteLn(Listing, ' in their first ',  MaxSimilar:1,
			       ' characters.');
	    end;
	  if FoundInconsistency then
	    begin  StartLineOfListing;
	      WriteLn(Listing, 'Identifiers marked with a "*" are not ',
			       'consistently capitalized.')
	    end
	end
    end { PrintEntries } ;

  begin { PrintMap }
    if IdentTable.EntryCount > 0 then
      begin
	LinkUsages;
	CompressEntries;
	SortEntries(0,IdentTable.EntryCount-1);
	ListingInfo.LinesRemainingOnPage := 0;	{ Force a page eject }
	PrintEntries
      end
  end { PrintMap } ;

  procedure Initialize;
    var
      Index: HashIndex;
      HighChar: Char;

    procedure Reserve(SegImage: Alpha);
      { Assume all letters in SegImage are lower case. }
      var
	SegIndex:  AlphaRange;
	Sum: Natural;
    begin { Reserve }
      Sum := 0;  SegIndex := 1;
      repeat
	if Sum > IdentTable.MaxHashSum then
	  Sum := Sum mod HashSize;
	Sum := Sum * HashFactor + Ord(SegImage[SegIndex]);
	SegIndex := SegIndex + 1
      until SegImage[SegIndex] = ' ';
      SegIndex := SegIndex - 1;
      if MaxInt-SegIndex < Sum then Sum := Sum mod HashSize;
      Sum := (Sum+SegIndex) mod HashSize;
      with IdentTable do
	if Entries[Sum] <> nil then
	  begin WriteLn(Output, ' Reserved word collision!'); Halt end
	else
	  begin
	    New(Entries[Sum]);
	    with Entries[Sum]^, Ident, FirstSegment do
	      begin
		EntryCount := EntryCount + 1;  Length := SegIndex;
		Image := SegImage;  NextSegment := nil;
		CaseShift := [];  ConsistentCase := True;
		Reserved := True;  ReserveCount := ReserveCount + 1
	      end
	  end
    end { Reserve } ;

  begin { Initialize }
    Reset(Source);
    if Eof(Source) then WriteLn(Output, ' Empty source file!')
    else
      begin
	Rewrite(Listing);
	with ListingInfo do
	  begin LinesRemainingOnPage := 0;  PageCount := 0 end;
	with IdentTable do
	  begin  EntryCount := 0;  ReserveCount := 0;
	    for Index := 0 to HashSize-1 do Entries[Index] := nil;
	    HighChar := '9';
	    if HighChar < 'Z' then HighChar := 'Z';
	    if HighChar < 'z' then HighChar := 'z';
	    MaxHashSum := (MaxInt - Ord(HighChar)) div HashFactor;
	    if (HashSize-1 > MaxHashSum) or
	       (HashSize-1 > MaxInt-MaxSourceWidth) then
	      begin WriteLn(Output, ' Hash function overflow!'); Halt end
	  end;
	{ Reserve all word-symbols except "label" and "goto". }
	Reserve('and	   ');	  Reserve('array     ');
	Reserve('begin	   ');	  Reserve('case      ');
	Reserve('const	   ');	  Reserve('div	     ');
	Reserve('do	   ');	  Reserve('downto    ');
	Reserve('else	   ');	  Reserve('end	     ');
	Reserve('file	   ');	  Reserve('for	     ');
	Reserve('function  ');	  Reserve('if	     ');
	Reserve('in	   ');	  Reserve('mod	     ');
	Reserve('nil	   ');	  Reserve('not	     ');
	Reserve('of	   ');	  Reserve('or	     ');
	Reserve('packed    ');	  Reserve('procedure ');
	Reserve('program   ');	  Reserve('record    ');
	Reserve('repeat    ');	  Reserve('set	     ');
	Reserve('then	   ');	  Reserve('to	     ');
	Reserve('type	   ');	  Reserve('until     ');
	Reserve('var	   ');	  Reserve('while     ');
	Reserve('with	   ');
	{ Usage Table and related variables: }
	Rewrite(UsageTable);
	NilUsage.BlockNum := 0;  NilUsage.BlockInx := UsagesPerBlock;
	HighUsage := NilUsage;
	CurrentBlock := 0;  UpdateBlock := False;
	UsageCount := 0
      end
  end { Initialize } ;

begin { IdMap }
  Initialize;
  if not Eof(Source) then
    begin ScanSource;  PrintMap;  Rewrite(UsageTable)  end
end { IdMap } .



Feel free to contact me, David Gesswein djg@pdp8online.com with any questions, comments on the web site, or if you have related equipment, documentation, software etc. you are willing to part with.  I am interested in anything PDP-8 related, computers, peripherals used with them, DEC or third party, or documentation. 

PDP-8 Home Page   PDP-8 Site Map   PDP-8 Site Search