-- SourceBcdImpl.mesa
--    Last edited by Lewis on 18-May-81 19:01:52
--    Last edited by Sweet on July 17, 1980  11:18 AM
--    Last edited by Levin on July 6, 1982 4:45 pm

DIRECTORY
  Alloc USING [AddNotify, DropNotify, Handle, Notifier, Words],
  BcdDefs USING [
    CTIndex, CTNull, CTRecord, EXPIndex, FTIndex, FTNull, IMPIndex,
    MTIndex, MTRecord, Namee, NameRecord, NTIndex, NTRecord, SGIndex, SPIndex,
    VersionID],
  Error USING [Error, AmbiguousComponent, UnknownComponent],
  Inline USING [BITAND, BITXOR],
  PackagerDefs USING [packctreetype, globalData],
  PackEnviron USING [BcdHandle, SourceBcdSegmentBase],
  PackHeap USING [FreeSpace, GetSpace],
  Segments USING [
    BaseFromSegment, DeleteSegment, FHandle, HardDown, MoveSegment, NewSegment, Read,
    SegmentAddress, SHandle, SwapIn, Unlock],
  Strings USING [EqualSubStrings, String, SubString, SubStringDescriptor],
  SymTabOps USING [SubStringForHash],
  SymTabDefs USING [HTIndex, HTNull],
  SourceBcd,
  Table USING [Base];

SourceBcdImpl: PROGRAM
    IMPORTS Alloc, Error, Inline, PackagerDefs, PackHeap, Segments, Strings,
      SymTabOps
    EXPORTS SourceBcd =
  BEGIN OPEN SourceBcd, BcdDefs;

 -- Source Bcd is obsolete, already repackaged, or was compiled for Alto
  BadSourceBcd: PUBLIC ERROR = CODE;

  ConfigTreeBuildingError: ERROR = CODE;
  CTreeBuildError: PROC = {ERROR ConfigTreeBuildingError};

  SubStringDescriptor: TYPE = Strings.SubStringDescriptor;
  SubString: TYPE = Strings.SubString;


  table: Alloc.Handle ← NIL;
  
 -- ****************** Source BCD Loading and Unloading ****************** 

  bcdHeader: PUBLIC PackEnviron.BcdHandle ← NIL;

  bcdBases:  PUBLIC LONG POINTER TO BcdBaseRec ← NIL;
  bcdLimits: PUBLIC LONG POINTER TO BcdLimitRec ← NIL;

  moduleCount: PUBLIC CARDINAL ← 0;


  bcdSegment: Segments.SHandle ← NIL;

  Load: PUBLIC PROC =
    BEGIN
    pages: CARDINAL;
    table ← PackagerDefs.globalData.ownTable;
    bcdSegment ← Segments.NewSegment[
      file: PackagerDefs.globalData.sourceBcdFile,  
      base: 1, pages: 10, access: Segments.Read];
    Segments.SwapIn[
      seg: bcdSegment, base: PackEnviron.SourceBcdSegmentBase, 
      info: Segments.HardDown];  
    bcdHeader ← Segments.SegmentAddress[bcdSegment];
    IF bcdHeader.versionIdent # BcdDefs.VersionID OR bcdHeader.definitions THEN
      BEGIN
      Error.Error[error, "Invalid input BCD file: obsolete version or definitions BCD"L];
      GO TO bogus
      END;
    IF bcdHeader.repackaged THEN
      BEGIN
      Error.Error[error, "Already packaged BCDs cannot be repackaged"L];
      GO TO bogus
      END;
    IF (pages ← bcdHeader.nPages) > 10 THEN  -- load entire bcd
      BEGIN
      Segments.Unlock[bcdSegment];
      Segments.MoveSegment[
	seg: bcdSegment, base: Segments.BaseFromSegment[bcdSegment], pages: pages];
      Segments.SwapIn[
        seg: bcdSegment, base: PackEnviron.SourceBcdSegmentBase, 
	info: Segments.HardDown];  
      bcdHeader ← Segments.SegmentAddress[bcdSegment];
      END;
    PackagerDefs.globalData.sourceBcdVersion ← bcdHeader.version;
    bcdBases ← PackHeap.GetSpace[SIZE[BcdBaseRec]];
    bcdBases↑ ← [
      ctb: LOOPHOLE[bcdHeader + bcdHeader.ctOffset],
      mtb: LOOPHOLE[bcdHeader + bcdHeader.mtOffset],
      lfb: LOOPHOLE[bcdHeader + bcdHeader.lfOffset],
      rfb: LOOPHOLE[bcdHeader + bcdHeader.rfOffset],
      tfb: LOOPHOLE[bcdHeader + bcdHeader.tfOffset],
      etb: LOOPHOLE[bcdHeader + bcdHeader.expOffset],
      itb: LOOPHOLE[bcdHeader + bcdHeader.impOffset],
      sgb: LOOPHOLE[bcdHeader + bcdHeader.sgOffset],
      ftb: LOOPHOLE[bcdHeader + bcdHeader.ftOffset],
      ssb: LOOPHOLE[bcdHeader + bcdHeader.ssOffset],
      evb: LOOPHOLE[bcdHeader + bcdHeader.evOffset],
      spb: LOOPHOLE[bcdHeader + bcdHeader.spOffset],
      ntb: LOOPHOLE[bcdHeader + bcdHeader.ntOffset],
      tyb: LOOPHOLE[bcdHeader + bcdHeader.typOffset],
      tmb: LOOPHOLE[bcdHeader + bcdHeader.tmOffset],
      fpb: LOOPHOLE[bcdHeader + bcdHeader.fpOffset]];
    IF bcdBases.mtb[FIRST[MTIndex]].altoCode THEN
      BEGIN
      PackHeap.FreeSpace[bcdBases];  bcdBases ← NIL;
      Error.Error[error, "Packaging is not supported for Alto programs"L];
      GO TO bogus
      END;
    bcdLimits ← PackHeap.GetSpace[SIZE[BcdLimitRec]];
    bcdLimits↑ ← [
      ct: bcdHeader.ctLimit,
      sg: bcdHeader.sgLimit,
      ft: bcdHeader.ftLimit,
      mt: bcdHeader.mtLimit,
      et: bcdHeader.expLimit,
      it: bcdHeader.impLimit,
      nt: bcdHeader.ntLimit,
      sp: bcdHeader.spLimit,
      tm: bcdHeader.tmLimit,
      fp: bcdHeader.fpLimit];
    CountModules[];
    InitializeMtiArray[];
    EXITS
      bogus =>
        BEGIN
        Segments.Unlock[bcdSegment];  Segments.DeleteSegment[bcdSegment];
        bcdSegment ← NIL; bcdHeader ← NIL;
        ERROR BadSourceBcd
	END;
    END;

  Unload: PUBLIC PROC =
    BEGIN
    IF bcdSegment = NIL THEN RETURN;
    Segments.Unlock[bcdSegment];  Segments.DeleteSegment[bcdSegment];
    bcdSegment ← NIL;
    PackHeap.FreeSpace[bcdBases];
    PackHeap.FreeSpace[bcdLimits];
    ReleaseMtiArray[];
    moduleCount ← 0;
    table ← NIL;
    END;

  EnumerateConfigs: PUBLIC PROC [
      userProc: PROC [CTIndex] RETURNS [stop: BOOLEAN]] =
    BEGIN
    cti: CTIndex ← FIRST[CTIndex];
    UNTIL cti = bcdLimits.ct DO  
      IF userProc[cti] THEN RETURN;
      cti ← cti + SIZE[CTRecord] + bcdBases.ctb[cti].nControls;
      ENDLOOP;
    END;

  EnumerateModules: PUBLIC PROC [
      userProc: PROC [MTIndex] RETURNS [stop: BOOLEAN]] =
    BEGIN
    mti: MTIndex ← FIRST[MTIndex];
    UNTIL mti = bcdLimits.mt DO
      mtRecSize: CARDINAL;
      IF userProc[mti] THEN RETURN;
      WITH mth: bcdBases.mtb[mti] SELECT FROM
        direct => mtRecSize ← SIZE[direct MTRecord] + mth.length;
	indirect => mtRecSize ← SIZE[indirect MTRecord];
	multiple => mtRecSize ← SIZE[multiple MTRecord];
	ENDCASE;
      mti ← mti + mtRecSize;
      ENDLOOP;
    END;

  IsTableCompiled: PUBLIC PROC [
      mti: BcdDefs.MTIndex] RETURNS [reply: BOOLEAN] =
    BEGIN
    RETURN[ bcdBases.mtb[mti].tableCompiled ];
    END;

  SubStringForName: PUBLIC PROC [ss: Strings.SubString, name: NameRecord] =
    BEGIN
    ss.base ← @bcdBases.ssb.string;
    ss.offset ← name;  ss.length ← bcdBases.ssb.size[name];
    END;

  EqualIdAndName: PUBLIC PROC [
      id: SymTabDefs.HTIndex, name: NameRecord] RETURNS [yes: BOOLEAN] =
    BEGIN
    idSS: SubString ← @idSSDesc;
      idSSDesc: SubStringDescriptor;
    nameSS: SubString ← @nameSSDesc;
      nameSSDesc: SubStringDescriptor;
    SymTabOps.SubStringForHash[idSS, id];
    SubStringForName[nameSS, name];
    RETURN[Strings.EqualSubStrings[idSS, nameSS]];
    END;


  CountModules: PROC =
    BEGIN

    CountOneModule: PROC [mti: BcdDefs.MTIndex] RETURNS [stop: BOOLEAN] =
      {moduleCount ← moduleCount+1;  RETURN[FALSE]};
  
    moduleCount ← 0;
    EnumerateModules[CountOneModule];
    END;


 -- BcdDefs.MTIndex -> ModuleNum mapping related declarations

  mtiArray: PUBLIC LONG DESCRIPTOR FOR ARRAY ModuleNum OF BcdMTIndex;

  InitializeMtiArray: PROC =
    BEGIN
    i: ModuleNum;

    EnterOneModule: PROC [mti: BcdDefs.MTIndex] RETURNS [stop: BOOLEAN] =
      BEGIN
      mtiArray[i] ← LOOPHOLE[mti, BcdMTIndex];  i ← i+1;  
      RETURN[FALSE];
      END;
  
    mtiArray ← (IF moduleCount # 0 
      THEN DESCRIPTOR[PackHeap.GetSpace[moduleCount*SIZE[BcdMTIndex]], moduleCount]
      ELSE DESCRIPTOR[NIL, 0]);
    i ← 0;
    EnumerateModules[EnterOneModule];
    END;

  ReleaseMtiArray: PROC = {PackHeap.FreeSpace[BASE[mtiArray]]};

  ModuleNumForMti: PUBLIC PROC [
      mti: BcdDefs.MTIndex] RETURNS [mNum: ModuleNum] =
    BEGIN  -- map i-th module index to i
    orderedMti: BcdMTIndex ← LOOPHOLE[mti];
    l, m, u: ModuleNum;
    l ← 0;  u ← moduleCount;
    UNTIL l > u DO
      m ← (l+u)/2;
      SELECT mtiArray[m] FROM
        < orderedMti => l ← m+1;
        > orderedMti => u ← m-1;
        ENDCASE => RETURN[m];  -- mti found at mtiArray[m]
      ENDLOOP;
    RETURN[NullModuleNum];     -- could not find mti 
    END;


 -- ******************** Configuration tree creation ******************** 

  configTreeRoot: PUBLIC CTreeIndex ← NullCTreeIndex;
  rootPointsToModule: BOOLEAN ← FALSE;

  BuildConfigTree: PUBLIC PROC =
    BEGIN
    table.AddNotify[UpdateConfigTreeBase];
    InitializeHashVectors[];
    configTreeRoot ← NullCTreeIndex;  rootPointsToModule ← FALSE;
    EnumerateModules[EnterOneModule];
    EnumerateConfigs[EnterOneConfig];
    END;
    
  DestroyConfigTree: PUBLIC PROC =
    BEGIN
    IF table ~= NIL THEN table.DropNotify[UpdateConfigTreeBase];
    configTreeRoot ← NullCTreeIndex;
    END;


  ctreeb: Table.Base;

  UpdateConfigTreeBase: Alloc.Notifier =
    BEGIN
    ctreeb ← base[PackagerDefs.packctreetype];
    END;


  CTreeHVSize: CARDINAL = 71;
  CTreeHash: TYPE = [0..CTreeHVSize);
  instHashVec, protoHashVec: ARRAY CTreeHash OF CTreeIndex;

  InitializeHashVectors: PROC =
    BEGIN
    i: CTreeHash;
    FOR i IN CTreeHash DO 
      instHashVec[i] ← protoHashVec[i] ← NullCTreeIndex 
      ENDLOOP;
    END;

  HashForName: PROC [name: NameRecord] RETURNS [CTreeHash] =
    BEGIN
    desc: SubStringDescriptor ← [base: @bcdBases.ssb.string, 
      offset: name, length: bcdBases.ssb.size[name]];
    ss: SubString = @desc;
    RETURN[HashValue[ss]];
    END;

  HashValue: PROC [s: SubString] RETURNS [CTreeHash] =
    BEGIN  -- computes the hash index for substring s
    CharMask: PROC [CHARACTER, WORD] RETURNS [CARDINAL] =
      LOOPHOLE[Inline.BITAND];
    mask: WORD = 137B;                -- masks out ASCII case shifts
    n: CARDINAL = s.length;
    b: Strings.String = s.base;
    v: WORD;
    v ← CharMask[b[s.offset], mask]*177B + CharMask[b[s.offset+(n-1)], mask];
    RETURN[Inline.BITXOR[v, n*17B] MOD CTreeHVSize]
    END;


  EnterOneModule: PROC [module: MTIndex] RETURNS [stop: BOOLEAN] =
    BEGIN  OPEN mRec: bcdBases.mtb[module];
    m, c: CTreeIndex;
    config: CTIndex = mRec.config;  -- config containing module
    m ← InsertModuleNode[module];
    IF config = CTNull THEN 
      BEGIN  -- might be processing compiler-generated Bcd: record tree root
      IF configTreeRoot = NullCTreeIndex THEN 
	{configTreeRoot ← m;  rootPointsToModule ← TRUE};
      RETURN[FALSE];
      END;    
    c ← InsertConfigNode[config];
    ctreeb[m].father ← c;
    ctreeb[m].brother ← ctreeb[c].firstSon;  ctreeb[c].firstSon ← m;
    RETURN[FALSE];
    END;

  EnterOneConfig: PROC [config: CTIndex] RETURNS [stop: BOOLEAN] =
    BEGIN  OPEN cRec: bcdBases.ctb[config];
    c, e: CTreeIndex;
    encloser: CTIndex = cRec.config;  -- configuration enclosing config
    c ← InsertConfigNode[config];
    IF encloser = CTNull THEN  -- we have found the config tree's root
      IF configTreeRoot # NullCTreeIndex AND ~rootPointsToModule THEN
        CTreeBuildError[]
      ELSE {configTreeRoot ← c;  rootPointsToModule ← FALSE;  RETURN[FALSE]};
    e ← InsertConfigNode[encloser];
    ctreeb[c].father ← e;
    ctreeb[c].brother ← ctreeb[e].firstSon;  ctreeb[e].firstSon ← c;
    RETURN[FALSE];
    END;


  InsertModuleNode: PROC [module: MTIndex] RETURNS [newNode: CTreeIndex] =
    BEGIN
    mProtoName, mInstName: NameRecord;
    mProtoHash, mInstHash: CTreeHash;
    protoPrev, instPrev: CTreeIndex;
    mProtoName ← bcdBases.mtb[module].name;
    mProtoHash ← HashForName[mProtoName];
    IF bcdBases.mtb[module].namedInstance THEN
      BEGIN 
      mInstName ← NameFromNameTable[Namee[module[module]]];
      mInstHash ← HashForName[mInstName];
      newNode ← NewInstanceNode[
        instanceName: mInstName, prototypeName: mProtoName,
        index: BcdTableLoc[module[module]]];
      END
    ELSE
      BEGIN 
      mInstName ← mProtoName;  mInstHash ← mProtoHash;
      newNode ← NewPrototypeNode[
        prototypeName: mProtoName, index: BcdTableLoc[module[module]]];
      END;
    -- set newNode's prototypeLink (hash chain), prototypePrev (same id) links
    protoPrev ← PrevNodeSameProtoName[mProtoName, protoHashVec[mProtoHash]];
    IF protoPrev = NullCTreeIndex THEN  -- mProtoName has not been seen before
      BEGIN  -- add node to (prototype) hash chain for mProtoName
      ctreeb[newNode].prototypeLink ← protoHashVec[mProtoHash];
      protoHashVec[mProtoHash] ← newNode;
      ctreeb[newNode].prototypePrev ← NullCTreeIndex;
      END
    ELSE    -- mProtoName has been seen before; don't put in hash chain,
      BEGIN -- just add to "nodes with same prototype id" chain off protoPrev
      ctreeb[newNode].anotherNodeWSameProtoName ← TRUE;
      ctreeb[protoPrev].anotherNodeWSameProtoName ← TRUE;
      ctreeb[newNode].prototypePrev ← ctreeb[protoPrev].prototypePrev;
      ctreeb[protoPrev].prototypePrev ← newNode;
      ctreeb[newNode].prototypeLink ← NullCTreeIndex;
      END;
    -- set newNode's instanceLink and instancePrev links
    instPrev ← PrevNodeSameInstName[mInstName, instHashVec[mInstHash]];
    IF instPrev = NullCTreeIndex THEN  -- mInstName has not been seen before
      BEGIN  -- add node to (instance) hash chain for mInstName
      ctreeb[newNode].instanceLink ← instHashVec[mInstHash];
      instHashVec[mInstHash] ← newNode;
      ctreeb[newNode].instancePrev ← NullCTreeIndex;
      END
    ELSE    -- mInstName has been seen before; don't put in hash chain,
      BEGIN -- just add to "nodes with same instance id" chain off instPrev
      ctreeb[newNode].instancePrev ← ctreeb[instPrev].instancePrev;
      ctreeb[instPrev].instancePrev ← newNode;
      ctreeb[newNode].instanceLink ← NullCTreeIndex;
      END;
    RETURN[newNode];
    END;


  InsertConfigNode: PROC [config: CTIndex] RETURNS [newNode: CTreeIndex] =
    BEGIN
    kind: ComponentKind;
    cProtoName, cInstName: NameRecord;
    cProtoHash, cInstHash: CTreeHash;
    protoPrev, instPrev: CTreeIndex;
    c: CTreeIndex;
    cProtoName ← bcdBases.ctb[config].name;
    cProtoHash ← HashForName[cProtoName];
    IF bcdBases.ctb[config].namedInstance THEN
      BEGIN 
      kind ← instance; 
      cInstName ← NameFromNameTable[Namee[config[config]]];
      cInstHash ← HashForName[cInstName];
      END
    ELSE {kind ← prototype;  cInstName ← cProtoName;  cInstHash ← cProtoHash};
    c ← protoHashVec[cInstHash];  -- see if node for config already exists 
    WHILE c # NullCTreeIndex DO
      IF ctreeb[c].prototypeName = cProtoName THEN
        WITH ctreeb[c] SELECT FROM
	  instance => 
	    IF kind = instance AND instanceName = cInstName THEN RETURN[c];
	  prototype =>
	    IF kind = prototype THEN RETURN[c];
	  ENDCASE;
      c ← ctreeb[c].prototypeLink;
      ENDLOOP;
    newNode ← (IF kind = instance 
      THEN NewInstanceNode[
        instanceName: cInstName, prototypeName: cProtoName,
        index: BcdTableLoc[config[config]]]
      ELSE NewPrototypeNode[
        prototypeName: cProtoName, index: BcdTableLoc[config[config]]]);
    -- set newNode's prototypeLink (hash chain), prototypePrev (same id) links
    protoPrev ← PrevNodeSameProtoName[cProtoName, protoHashVec[cProtoHash]];
    IF protoPrev = NullCTreeIndex THEN  -- cProtoName has not been seen before
      BEGIN  -- add node to (prototype) hash chain for cProtoName
      ctreeb[newNode].prototypeLink ← protoHashVec[cProtoHash];
      protoHashVec[cProtoHash] ← newNode;
      ctreeb[newNode].prototypePrev ← NullCTreeIndex;
      END
    ELSE    -- cProtoName has been seen before; don't put in hash chain,
      BEGIN -- just add to "nodes with same prototype id" chain off protoPrev
      ctreeb[newNode].anotherNodeWSameProtoName ← TRUE;
      ctreeb[protoPrev].anotherNodeWSameProtoName ← TRUE;
      ctreeb[newNode].prototypePrev ← ctreeb[protoPrev].prototypePrev;
      ctreeb[protoPrev].prototypePrev ← newNode;
      ctreeb[newNode].prototypeLink ← NullCTreeIndex;
      END;
    -- set newNode's instanceLink and instancePrev links
    instPrev ← PrevNodeSameInstName[cInstName, instHashVec[cInstHash]];
    IF instPrev = NullCTreeIndex THEN  -- cInstName has not been seen before
      BEGIN  -- add node to (instance) hash chain for cInstName
      ctreeb[newNode].instanceLink ← instHashVec[cInstHash];
      instHashVec[cInstHash] ← newNode;
      ctreeb[newNode].instancePrev ← NullCTreeIndex;
      END
    ELSE    -- cInstName has been seen before; don't put in hash chain,
      BEGIN -- just add to "nodes with same instance id" chain off instPrev
      ctreeb[newNode].instancePrev ← ctreeb[instPrev].instancePrev;
      ctreeb[instPrev].instancePrev ← newNode;
      ctreeb[newNode].instanceLink ← NullCTreeIndex;
      END;
    RETURN[newNode];
    END;

  NewPrototypeNode: PROC [
      prototypeName: NameRecord, index: BcdTableLoc]
      RETURNS [newNode: CTreeIndex] =
    BEGIN
    newNode ← table.Words[
      PackagerDefs.packctreetype, SIZE[prototype ConfigTreeNode]];
    ctreeb[newNode] ← ConfigTreeNode[
      father:        NullCTreeIndex,
      brother:       NullCTreeIndex,
      firstSon:      NullCTreeIndex,
      prototypeName: prototypeName,
      anotherNodeWSameProtoName: FALSE,
      instanceLink:  NullCTreeIndex,
      prototypeLink: NullCTreeIndex,
      instancePrev:  NullCTreeIndex,
      prototypePrev: NullCTreeIndex,
      index:         index,
      body:          prototype[] ];
    END;  

  NewInstanceNode: PROC [
        instanceName, prototypeName: NameRecord, 
        index: BcdTableLoc]
      RETURNS [newNode: CTreeIndex] =
    BEGIN
    newNode ← table.Words[
      PackagerDefs.packctreetype, SIZE[instance ConfigTreeNode]];
    ctreeb[newNode] ← ConfigTreeNode[
      father:        NullCTreeIndex,
      brother:       NullCTreeIndex,
      firstSon:      NullCTreeIndex,
      prototypeName: prototypeName,
      anotherNodeWSameProtoName: FALSE,
      instanceLink:  NullCTreeIndex,
      prototypeLink: NullCTreeIndex,
      instancePrev:  NullCTreeIndex,
      prototypePrev: NullCTreeIndex,
      index:         index,
      body:          instance[instanceName: instanceName] ];
    END;  

  NameFromNameTable: PROC [namee: BcdDefs.Namee] RETURNS [name: NameRecord] =
    BEGIN
    nti: NTIndex;
    FOR nti ← FIRST[NTIndex], nti + SIZE[NTRecord] UNTIL nti = bcdLimits.nt DO
      IF bcdBases.ntb[nti].item = namee THEN RETURN[bcdBases.ntb[nti].name];
      ENDLOOP;
    CTreeBuildError[];
    END;

  PrevNodeSameInstName: PROC [
      name: NameRecord, chainHead: CTreeIndex] RETURNS [CTreeIndex] =
    BEGIN  -- locate in hash chain a previous node with given instance name
    p: CTreeIndex;
    p ← chainHead;
    WHILE p # NullCTreeIndex DO
      WITH ctreeb[p] SELECT FROM
	instance => 
	  IF instanceName = name THEN RETURN[p];
	prototype =>  -- for a prototype, instance name = prototype name  
	  IF prototypeName = name THEN RETURN[p];
	ENDCASE;
      p ← ctreeb[p].instanceLink;
      ENDLOOP;
    RETURN[NullCTreeIndex];
    END;

  PrevNodeSameProtoName: PROC [
      name: NameRecord, chainHead: CTreeIndex] RETURNS [CTreeIndex] =
    BEGIN  -- locate in hash chain a previous node with given prototype name
    p: CTreeIndex;
    p ← chainHead;
    WHILE p # NullCTreeIndex DO
      IF ctreeb[p].prototypeName = name THEN RETURN[p];
      p ← ctreeb[p].prototypeLink;
      ENDLOOP;
    RETURN[NullCTreeIndex];
    END;


 -- ******* Enumerate module (instances/prototypes) in a configuration *******

  DoneEnumerating: SIGNAL = CODE;

  EnumerateModulesInConfig: PUBLIC PROC [
      kind: ComponentKind, 
      configTreeNode: CTreeIndex, 
      userProc: PROC [mti: BcdDefs.MTIndex] RETURNS [stop: BOOLEAN]] =
    BEGIN
    IF kind = instance THEN 
      EnumerateModuleInstancesInConfig[configTreeNode, userProc]
    ELSE 
      EnumerateModulePrototypesInConfig[configTreeNode, userProc];
    END;

  EnumerateModuleInstancesInConfig: PROC [
      configTreeNode: CTreeIndex, 
      userProc: PROC [mti: BcdDefs.MTIndex] RETURNS [stop: BOOLEAN]] =
    BEGIN

    OutputConfigSubTree: PROC [cTreeNode: CTreeIndex] =
      BEGIN  OPEN node: ctreeb[cTreeNode];
      son: CTreeIndex;
      WITH node.index SELECT FROM
        module => IF userProc[mti] THEN SIGNAL DoneEnumerating;
        ENDCASE;
      IF node.firstSon # NullCTreeIndex THEN
        FOR son ← node.firstSon, ctreeb[son].brother UNTIL son = NullCTreeIndex DO
	  OutputConfigSubTree[son];
	  ENDLOOP;
      END;
    
    IF configTreeNode # NullCTreeIndex THEN
      OutputConfigSubTree[configTreeNode ! DoneEnumerating => CONTINUE];
    END;

  EnumerateModulePrototypesInConfig: PROC [
      configTreeNode: CTreeIndex, 
      userProc: PROC [mti: MTIndex] RETURNS [stop: BOOLEAN]] =
    BEGIN  -- no duplications must appear in the output

    OutputConfigSubTree: PROC [cTreeNode: CTreeIndex] =
      BEGIN  OPEN node: ctreeb[cTreeNode];
      son: CTreeIndex;
      WITH node.index SELECT FROM
        module => 
	  BEGIN -- use a representative one
	  firstProto: CTreeIndex = FirstModulePrototype[cTreeNode];
	  WITH fp: ctreeb[firstProto].index SELECT FROM
	    module => ConditionallyOutputModulePrototype[fp.mti, userProc];
	    ENDCASE;
	  END;
        ENDCASE;
      IF node.firstSon # NullCTreeIndex THEN
        FOR son ← node.firstSon, ctreeb[son].brother UNTIL son = NullCTreeIndex DO
	  OutputConfigSubTree[son];
	  ENDLOOP;
      END;
    
    IF configTreeNode # NullCTreeIndex THEN
      BEGIN
      InitModuleHashVector[];
      OutputConfigSubTree[configTreeNode ! DoneEnumerating => CONTINUE];
      FreeModuleHashVector[];
      END;
    END;

  FirstModulePrototype: PROC [this: CTreeIndex] RETURNS [first: CTreeIndex] =
    BEGIN
    first ← this;
    DO 
      this ← ctreeb[first].prototypePrev;
      IF this = NullCTreeIndex THEN EXIT;
      IF ~AmbiguousPrototypeReference[first, this] THEN first ← this;
      ENDLOOP;
    END;

  ModuleHVSize: CARDINAL = 71;
  ModuleHash: TYPE = [0..ModuleHVSize);
  
  -- reduces time needed to discover whether a prototype was previously output
  ModuleHashVec: ARRAY ModuleHash OF OutputModuleRec; 

  OutputModuleRec: TYPE = RECORD [  -- describes modules already output
    file: FTIndex,
    link: ModulePtr];
  ModulePtr: TYPE = LONG POINTER TO OutputModuleRec;

  NewOutputModuleRec: PROC [
      file: FTIndex, link: ModulePtr] RETURNS [new: ModulePtr]  =
    BEGIN
    new ← PackHeap.GetSpace[SIZE[OutputModuleRec]];
    new↑ ← OutputModuleRec[file: file, link: link];
    END;

  InitModuleHashVector: PROC =
    BEGIN
    FOR i: ModuleHash IN ModuleHash DO 
      ModuleHashVec[i] ← OutputModuleRec[file: FTNull, link: NIL]; 
      ENDLOOP;
    END;

  FreeModuleHashVector: PROC =
    BEGIN
    i: ModuleHash;
    p, first, next: ModulePtr;
    FOR i IN ModuleHash DO 
      first ← ModuleHashVec[i].link;
      FOR p ← first, next UNTIL p = NIL DO
        next ← p.link;  PackHeap.FreeSpace[p];
	ENDLOOP; 
      ModuleHashVec[i] ← OutputModuleRec[file: FTNull, link: NIL]; 
      ENDLOOP;
    END;

  ConditionallyOutputModulePrototype: PROC [
      mti: MTIndex, 
      userProc: PROC [mti: MTIndex] RETURNS [stop: BOOLEAN]] =
    BEGIN  -- output only if no prior module with same FTIndex was output
    moduleName: NameRecord ← bcdBases.mtb[mti].name;
    moduleHash: ModuleHash ← HashForName[moduleName];
    moduleFile: FTIndex;
    p: ModulePtr;
    IF ModuleHashVec[moduleHash].file = FTNull THEN
      ModuleHashVec[moduleHash].file ← bcdBases.mtb[mti].file
    ELSE  -- look for a previously output module with same FTIndex
      BEGIN
      moduleFile ← bcdBases.mtb[mti].file;
      IF ModuleHashVec[moduleHash].file = moduleFile THEN RETURN;
      FOR p ← ModuleHashVec[moduleHash].link, p.link UNTIL p = NIL DO
	IF p.file = moduleFile THEN RETURN;
	ENDLOOP;
      ModuleHashVec[moduleHash].link ← NewOutputModuleRec[
	moduleFile, ModuleHashVec[moduleHash].link];
      END;
    IF userProc[mti] THEN SIGNAL DoneEnumerating;
    END;


 -- ********** Locate a module or configuration instance/prototype **********

  FindModuleOrConfig: PUBLIC PROC [
        kind: ComponentKind, 
        ResetIdStream: PROC,
        FirstQualId, NextQualId: PROC RETURNS [id: SymTabDefs.HTIndex]] 
      RETURNS [component: CTreeIndex] = {
    component ← (IF kind = instance 
      THEN FindInstance[ResetIdStream, FirstQualId, NextQualId]
      ELSE FindPrototype[ResetIdStream, FirstQualId, NextQualId]);
    RETURN[component]};
    
  FindInstance: PROC [
        ResetIdStream: PROC,
        FirstQualId, NextQualId: PROC RETURNS [id: SymTabDefs.HTIndex]] 
      RETURNS [component: CTreeIndex] = {
    start, t: CTreeIndex;
    mainPartOfId, nextId: SymTabDefs.HTIndex;
    componentFullyQual, fullyQual, immediateMatch: BOOLEAN;
    component ← NullCTreeIndex;  componentFullyQual ← FALSE;
    ResetIdStream[];
    mainPartOfId ← FirstQualId[];
    start ← LookupId[mainPartOfId, instance];
    WHILE start # NullCTreeIndex DO -- attempt to match qualified id stream beginning at start
      BEGIN
      fullyQual ← TRUE;             -- assume id stream is fully qualified initially 
      t ← start;                    -- t runs from start up father links in the config tree
      nextId ← NextQualId[];
      WHILE nextId # SymTabDefs.HTNull DO -- attempt to match nextId among t's ancestor nodes
        immediateMatch ← FALSE; 
        IF (t ← ctreeb[t].father) # NullCTreeIndex THEN
	  WITH ctreeb[t] SELECT FROM  -- try to match instance name
	    instance => IF EqualIdAndName[nextId, instanceName] THEN immediateMatch ← TRUE;
	    prototype => IF EqualIdAndName[nextId, prototypeName] THEN immediateMatch ← TRUE;
	    ENDCASE;
	IF ~immediateMatch THEN {
	  fullyQual ← FALSE; 
          UNTIL t = NullCTreeIndex DO
	    WITH ctreeb[t] SELECT FROM
	      instance => IF EqualIdAndName[nextId, instanceName] THEN EXIT;
	      prototype => IF EqualIdAndName[nextId, prototypeName] THEN EXIT;
	      ENDCASE;
	    t ← ctreeb[t].father;
            REPEAT
  	      FINISHED => GOTO NoMatchFromStart;
            ENDLOOP};
        nextId ← NextQualId[];
        ENDLOOP;
      -- a match has been found beginning at start
      IF component = NullCTreeIndex THEN {component ← start;  componentFullyQual ← fullyQual}
      ELSE {  -- another match was found; keep the best one 
        IF fullyQual THEN {  
	  IF componentFullyQual THEN { 
            Error.AmbiguousComponent[error, instance, component, start];
            RETURN[NullCTreeIndex]};
	  component ← start;  componentFullyQual ← TRUE}
	ELSE  -- if old match was fully qaulified continue to use it, otherwise...
	  IF ~componentFullyQual THEN {
            Error.AmbiguousComponent[error, instance, component, start];
            RETURN[NullCTreeIndex]}}; 
      EXITS
        NoMatchFromStart => NULL;
      END;
      start ← ctreeb[start].instancePrev;  -- try an alternative starting node
      ResetIdStream[];
      ENDLOOP;
    IF component = NullCTreeIndex THEN Error.UnknownComponent[error, instance, mainPartOfId];    
    RETURN[component]};
    
  FindPrototype: PROC [
        ResetIdStream: PROC,
        FirstQualId, NextQualId: PROC RETURNS [id: SymTabDefs.HTIndex]] 
      RETURNS [component: CTreeIndex] = {
    start, t: CTreeIndex;
    mainPartOfId, nextId: SymTabDefs.HTIndex;
    componentFullyQual, fullyQual, immediateMatch: BOOLEAN;
    component ← NullCTreeIndex;  componentFullyQual ← FALSE;
    ResetIdStream[];
    mainPartOfId ← FirstQualId[];
    start ← LookupId[mainPartOfId, prototype];
    WHILE start # NullCTreeIndex DO -- attempt to match qualified id stream beginning at start
      BEGIN
      fullyQual ← TRUE;             -- assume id stream is fully qualified initially 
      t ← start;                    -- t runs from start up father links in the config tree
      nextId ← NextQualId[];
      WHILE nextId # SymTabDefs.HTNull DO -- attempt to match nextId among t's ancestor nodes
        immediateMatch ← FALSE; 
        IF (t ← ctreeb[t].father) # NullCTreeIndex THEN
	  IF EqualIdAndName[nextId, ctreeb[t].prototypeName] THEN immediateMatch ← TRUE;
        IF ~immediateMatch THEN {
	  fullyQual ← FALSE;
          UNTIL t = NullCTreeIndex DO
	    IF EqualIdAndName[nextId, ctreeb[t].prototypeName] THEN EXIT;
	    t ← ctreeb[t].father;
            REPEAT
  	      FINISHED => GOTO NoMatchFromStart;
            ENDLOOP};
        nextId ← NextQualId[];
        ENDLOOP;
      -- a match has been found beginning at start
      IF component = NullCTreeIndex THEN {component ← start;  componentFullyQual ← fullyQual}
      ELSE {  -- another match was found; keep the best one
        IF fullyQual THEN {  
	  IF componentFullyQual AND AmbiguousPrototypeReference[component, start] THEN { 
            Error.AmbiguousComponent[error, prototype, component, start];
            RETURN[NullCTreeIndex]};
	  component ← start;  componentFullyQual ← TRUE}
	ELSE  -- if old match was fully qaulified continue to use it, otherwise...
	  IF ~componentFullyQual THEN {
	    IF AmbiguousPrototypeReference[component, start] THEN { 
              Error.AmbiguousComponent[error, prototype, component, start];
              RETURN[NullCTreeIndex]};
	    component ← start;  componentFullyQual ← FALSE}}; 
      EXITS
        NoMatchFromStart => NULL;
      END;
      start ← ctreeb[start].prototypePrev;  -- try an alternative starting node
      ResetIdStream[];
      ENDLOOP;
    IF component = NullCTreeIndex THEN Error.UnknownComponent[error, prototype, mainPartOfId];    
    RETURN[component]};

  AmbiguousPrototypeReference: PROC [
      comp1, comp2: CTreeIndex] RETURNS [isAmbiguous: BOOLEAN] =
    BEGIN
    bcdLoc1: SourceBcd.BcdTableLoc = ctreeb[comp1].index;
    bcdLoc2: SourceBcd.BcdTableLoc = ctreeb[comp2].index;
    file1, file2: BcdDefs.FTIndex;
    WITH bcdLoc1 SELECT FROM  -- ambiguous if not same FTIndex (name-stamp pair)
      config =>
	BEGIN
	file1 ← bcdBases.ctb[cti].file;
	WITH bcdLoc2 SELECT FROM
          config => file2 ← bcdBases.ctb[cti].file;
          module => RETURN[TRUE];  -- one is module and the other a config
          ENDCASE;
	IF file1 # file2 THEN RETURN[TRUE];
	END;
      module =>
	BEGIN
	file1 ← bcdBases.mtb[mti].file;
	WITH bcdLoc2 SELECT FROM
          config => RETURN[TRUE];  -- one is module and the other a config
          module => file2 ← bcdBases.mtb[mti].file;
          ENDCASE;
	IF file1 # file2 THEN RETURN[TRUE];
	END;
      ENDCASE;
    RETURN[FALSE];
    END;


 -- ******** Determine if module prototype/instance is in a config ********

  IsModuleInConfig: PUBLIC PROC [
        kind: ComponentKind, 
        mti: BcdDefs.MTIndex,
        configTreeNode: CTreeIndex] 
      RETURNS [reply: BOOLEAN] =
    BEGIN
    moduleName: NameRecord ← bcdBases.mtb[mti].name;
    start, t: CTreeIndex;
    start ← LookupName[moduleName, kind];
    WHILE start # NullCTreeIndex DO
      IF ctreeb[start].index.kind = module THEN
        FOR t ← ctreeb[start].father, ctreeb[t].father 
         UNTIL t = NullCTreeIndex DO
	  IF t = configTreeNode THEN RETURN[TRUE];
          ENDLOOP;
      -- any alternative starting nodes?
      IF kind = instance THEN start ← ctreeb[start].instancePrev
      ELSE start ← ctreeb[start].prototypePrev; 
      ENDLOOP;
    RETURN[FALSE];
    END;


 -- ******** Find first node with given instance or prototype id ********

  LookupId: PUBLIC PROC [
        id: SymTabDefs.HTIndex, kind: ComponentKind]
      RETURNS [firstTreeLoc: CTreeIndex] =
    BEGIN  -- find first node with given instance or prototype id
    idSS: SubString ← @idSSDesc;
      idSSDesc: SubStringDescriptor;
    SymTabOps.SubStringForHash[idSS, id];
    RETURN[LookupSS[idSS, kind]];
    END;

  LookupName: PUBLIC PROC [
        name: NameRecord, kind: ComponentKind]
      RETURNS [firstTreeLoc: CTreeIndex] =
    BEGIN  -- find first node with instance/prototype name (NameRecord)
    nameSS: SubString ← @nameSSDesc;
      nameSSDesc: SubStringDescriptor;
    SubStringForName[nameSS, name];
    RETURN[LookupSS[nameSS, kind]];
    END;

  LookupSS: PUBLIC PROC [
        idSS: SubString, kind: ComponentKind]
      RETURNS [firstTreeLoc: CTreeIndex] =
    BEGIN  -- find first node with given instance or prototype id substring
    idHash: CTreeHash;
    treeSS: SubString ← @treeSSDesc;
      treeSSDesc: SubStringDescriptor;
    p: CTreeIndex;
    idHash ← HashValue[idSS];
    IF kind = instance THEN
      BEGIN  -- find first node with given instance id
      p ← instHashVec[idHash];
      WHILE p # NullCTreeIndex DO
        WITH ctreeb[p] SELECT FROM
	  instance => SubStringForName[treeSS, instanceName];
	  prototype => SubStringForName[treeSS, prototypeName];
	  ENDCASE;
        IF Strings.EqualSubStrings[idSS, treeSS] THEN RETURN[p];
	p ← ctreeb[p].instanceLink;
        ENDLOOP
      END
    ELSE
      BEGIN  -- find first node with given prototype id
      p ← protoHashVec[idHash];
      WHILE p # NullCTreeIndex DO
        SubStringForName[treeSS, ctreeb[p].prototypeName];
        IF Strings.EqualSubStrings[idSS, treeSS] THEN RETURN[p];
        p ← ctreeb[p].prototypeLink;
        ENDLOOP;
      END;
    RETURN[NullCTreeIndex];
    END;

  END.