-- CodePackProcsImpl.mesa  
--  Last edited by Lewis on  1-Mar-82 23:55:08
--  Last edited by Satterthwaite, January 12, 1983 11:46 am

DIRECTORY
  Alloc USING [AddNotify, DropNotify, Handle, Notifier, Words],
  BcdDefs USING [MTIndex, MTNull, NameRecord],
  CodePackProcs,
  Error USING [
    EmptyCodePack, Error, ErrorName, EVInDiscardCodePack, EVNotFirst,
    ModuleAlreadyPacked, NoProcFromModuleInCP, NotProcInModule,
    ProcNotPlaced, ProcPlacedTwice, TableCompModuleNotIncAsUnit],
  HashOps USING [HTIndex, SubStringForHash],
  Inline USING [BITAND, BITXOR],
  ModuleSymbols USING [InvalidSymbols, Load, Unload, outerPackArray, FindProc],
  PackagerDefs USING [
    globalData, GlobalData, packtreetype, packsttype, packmdtype],
  PackageSymbols USING [OPCatch, OPEntry, OPIndex, OPMain, OPNull],
  ProcessingOrder USING [Enumerate, IsEmpty],
  SemanticEntry USING [STIndex],
  SourceBcd USING [
    bcdBases, CTreeIndex, EnumerateModulesInConfig, IsTableCompiled,
    moduleCount, ModuleNum, ModuleNumForMti],
  String USING [SubString, SubStringDescriptor],
  Symbols USING [HTIndex, HTNull],
  SymbolOps USING [SubStringForHash],
  Table USING [Base, Index, Limit],
  Tree: FROM "PackTree" USING [Index, Link, ProcsLink, Scan, Test, nullIndex],
  TreeOps: FROM "PackTreeOps" USING [ListHead, ListLength, ScanList, SearchList];
 
CodePackProcsImpl: PROGRAM
    IMPORTS 
      Alloc, Error, HashOps, Inline, ModuleSymbols, PackagerDefs,
      ProcessingOrder, SymbolOps, SourceBcd, TreeOps
    EXPORTS CodePackProcs = 
  BEGIN  OPEN PackageSymbols, CodePackProcs;

  CPerror: PROC = {ERROR CodePackProcsError};
  CodePackProcsError: ERROR = CODE;


  gd: PackagerDefs.GlobalData ← NIL;  -- set by Determine
  table: Alloc.Handle ← NIL;

  tb, stb, mdb: Table.Base;

  UpdateBases: Alloc.Notifier = {
    tb     ← base[PackagerDefs.packtreetype];  
    stb    ← base[PackagerDefs.packsttype];
    mdb    ← base[PackagerDefs.packmdtype]};

 -- *****************      Exported Types      *****************

  ModuleIndex: TYPE = Table.Base RELATIVE POINTER [0..Table.Limit) TO ModuleRecord;
  nullModuleIndex: ModuleIndex = ModuleIndex.LAST;

  ModuleRecord: PUBLIC TYPE = MACHINE DEPENDENT RECORD [
    mti: BcdDefs.MTIndex,
    includeMAIN: BOOL,
    unused: [0..2),
    cp: Tree.Index,         -- code pack's parse tree node
    numWordPairsInProcArray: [1..4],  -- if someProcs variant
    next: ModuleIndex,     -- next module record in code pack's chain
    includeEV: BOOL,
    includeCatch: BOOL,
    link: ModuleIndex,     -- links module records with same id hash values
    unused2: [0..2),
    procDescription: SELECT kind: * FROM
      allProcs  => [],
      someProcs => [       -- up to PackageSymbols.MaxEntries procedures
        procIncluded: PACKED ARRAY [1..1) OF BOOL], 
      ENDCASE];

  MakeProcsLink: PROC [m: ModuleIndex] RETURNS [Tree.ProcsLink] = INLINE {
    RETURN [[literal[m]]]};
    
    
 -- ***************** Module Record Location and Creation *****************

  MRecHVSize: CARDINAL = 71;
  MRecHash: TYPE = [0..MRecHVSize);
  
  mRecHashVec: LONG POINTER TO MRecMap ← NIL;  -- MRecHash -> ModuleIndex
  MRecMap: TYPE = RECORD [SEQUENCE length: CARDINAL OF ModuleIndex];

  InitModuleHashVector: PROC = {
    mRecHashVec ← gd.zone.NEW[MRecMap[MRecHVSize]];
    FOR i: MRecHash IN MRecHash DO mRecHashVec[i] ← nullModuleIndex ENDLOOP};

  ReleaseModuleHashVector: PROC = {
    IF mRecHashVec # NIL THEN gd.zone.FREE[@mRecHashVec]};

  HashForModule: PROC [module: BcdDefs.MTIndex] RETURNS [MRecHash] = {
    moduleName: BcdDefs.NameRecord = SourceBcd.bcdBases.mtb[module].name;
    moduleSS: String.SubString ← @moduleSSDesc;
    moduleSSDesc: String.SubStringDescriptor ← [
      base:   @SourceBcd.bcdBases.ssb.string, 
      offset: moduleName, 
      length: SourceBcd.bcdBases.ssb.size[moduleName]];
    RETURN[HashValue[moduleSS]]};

  HashValue: PROC [ss: String.SubString] RETURNS [MRecHash] = {
    CharMask: PROC [CHARACTER, WORD] RETURNS [CARDINAL] = LOOPHOLE[Inline.BITAND];
    mask: WORD = 137B;                -- masks out ASCII case shifts
    n: CARDINAL = ss.length;
    b: LONG STRING = ss.base;
    v: WORD;
    v ← CharMask[b[ss.offset], mask]*177B + CharMask[b[ss.offset+(n-1)], mask];
    RETURN[Inline.BITXOR[v, n*17B] MOD MRecHVSize]};


 -- one element cache for <MTIndex, cpNode> -> <ModuleIndex> mapping
  lastModule: BcdDefs.MTIndex ← BcdDefs.MTNull;
  lastCpNode: Tree.Index ← Tree.nullIndex;
  lastModuleIndex: ModuleIndex ← nullModuleIndex;

  moduleRecKind: TYPE = {all, some};

  EnterModuleRec: PROC [
        module: BcdDefs.MTIndex, cpNode: Tree.Index, procs: moduleRecKind] 
      RETURNS [existingRec: ModuleIndex] = {
    existingRec ← FindModuleRec[
      module: module, cpNode: cpNode, procs: procs, create: TRUE].m};

  LocateExistingModuleRec: PROC [
        module: BcdDefs.MTIndex, cpNode: Tree.Index] 
      RETURNS [existingRec: ModuleIndex, found: BOOL] = {
    [existingRec, found] ← FindModuleRec[
      module: module, cpNode: cpNode, procs: some, create: FALSE]};

  FindModuleRec: PROC [
        module: BcdDefs.MTIndex, cpNode: Tree.Index, procs: moduleRecKind,
        create: BOOL] 
      RETURNS [m: ModuleIndex, found: BOOL] = {
    mHash: MRecHash;
    hashChainHead, moduleChainHead: ModuleIndex;
    IF cpNode = lastCpNode AND module = lastModule THEN 
      RETURN[lastModuleIndex, TRUE];            -- found in cache
    mHash ← HashForModule[module];
    hashChainHead ← mRecHashVec[mHash];
    FOR m ← hashChainHead, mdb[m].link UNTIL m = nullModuleIndex DO
      IF mdb[m].mti = module AND mdb[m].cp = cpNode THEN { 
	-- set up new cache entry
	lastModule ← module;  lastCpNode ← cpNode;  lastModuleIndex ← m;  
	RETURN[m, TRUE]}; 
      ENDLOOP;
    IF ~create THEN RETURN[nullModuleIndex, FALSE];
    m ← (IF procs = all 
      THEN NewAllProcsModuleRec[module, cpNode, hashChainHead]
      ELSE NewSomeProcsModuleRec[module, cpNode, hashChainHead]);
    mRecHashVec[mHash] ← m;             -- add to hash chain 
    -- add to code pack's module rec chain
        moduleChainHead ← NARROW[tb[cpNode].son[3], Tree.ProcsLink].index;
	IF moduleChainHead # nullModuleIndex THEN
	  mdb[m].next ← moduleChainHead;  
	tb[cpNode].son[3] ← MakeProcsLink[m];
    lastModule ← module;  lastCpNode ← cpNode;  lastModuleIndex ← m;
    RETURN[m, FALSE]};

  NewAllProcsModuleRec: PROC [
        module: BcdDefs.MTIndex, cpNode: Tree.Index, chainHead: ModuleIndex] 
      RETURNS [newRec: ModuleIndex] = {
    newRec ← table.Words[PackagerDefs.packmdtype, SIZE[allProcs ModuleRecord]];
    mdb[newRec] ← ModuleRecord[
      mti:          module, 
      includeMAIN:  FALSE, 
      unused:       0,
      cp:           cpNode, 
      numWordPairsInProcArray: 1,  -- (irrelevant for allProcs variant) 
      next:         nullModuleIndex, 
      includeEV:    FALSE, 
      includeCatch: FALSE, 
      link:         chainHead,
      unused2:      0, 
      procDescription: allProcs[]]};

  NewSomeProcsModuleRec: PROC [
        module: BcdDefs.MTIndex, cpNode: Tree.Index, chainHead: ModuleIndex] 
      RETURNS [newRec: ModuleIndex] = {
    numWordPairsInProcArray: [1..4] = SourceBcd.bcdBases.mtb[module].ngfi;
    newRec ← table.Words[
      PackagerDefs.packmdtype, 
      SIZE[someProcs ModuleRecord] + (2*numWordPairsInProcArray)];
    mdb[newRec] ← ModuleRecord[
      mti:          module, 
      includeMAIN:  FALSE, 
      unused:       0,
      cp:           cpNode, 
      numWordPairsInProcArray: numWordPairsInProcArray, 
      next:         nullModuleIndex, 
      includeEV:    FALSE, 
      includeCatch: FALSE, 
      link:         chainHead, 
      unused2:      0, 
      procDescription: someProcs[procIncluded: ]];
    WITH mdb[newRec] SELECT FROM
      someProcs =>
        FOR i: CARDINAL IN [1..(32*numWordPairsInProcArray)) DO 
          procIncluded[i] ← FALSE;  
          ENDLOOP;
      ENDCASE};


 -- ************************* Procedure Insertion *************************

  -- information about each procedure, and MAIN, ENTRY VECTOR, and CATCH CODE
  proc: LONG POINTER TO ProcMap ← NIL;
  ProcMap: TYPE = ARRAY PackageSymbols.OPIndex OF ProcData;
  ProcData: TYPE = RECORD [
    codePack: Tree.Index,  -- proc was placed if (containing) codePack # nullIndex
    mark: BOOL];        -- used during EXCEPT processing

  InitProcMap: PROC = {proc ← gd.zone.NEW[ProcMap]};

  ReleaseProcMap: PROC = {
    IF proc # NIL THEN gd.zone.FREE[@proc]};
    
  InsertProc: PROC [opi: PackageSymbols.OPIndex, m: ModuleIndex] = {
    SELECT opi FROM
      OPMain => 
        IF proc[OPMain].codePack # Tree.nullIndex --main already included-- THEN
          ReportProcIncludedTwice[
	    opi, mdb[m].mti, proc[OPMain].codePack, mdb[m].cp]
	ELSE {mdb[m].includeMAIN ← TRUE;  proc[OPMain].codePack ← mdb[m].cp};
      OPEntry => 
	IF proc[OPEntry].codePack # Tree.nullIndex THEN
	  ReportProcIncludedTwice[
	    opi, mdb[m].mti, proc[OPEntry].codePack, mdb[m].cp]
	ELSE {mdb[m].includeEV ← TRUE;  proc[OPEntry].codePack ← mdb[m].cp};
      OPCatch => 
	IF proc[OPCatch].codePack # Tree.nullIndex THEN
	  ReportProcIncludedTwice[
	    opi, mdb[m].mti, proc[OPCatch].codePack, mdb[m].cp]
	ELSE {mdb[m].includeCatch ← TRUE;  proc[OPCatch].codePack ← mdb[m].cp};
      ENDCASE => {
        IF opi = PackageSymbols.OPNull OR opi > lastOpi THEN CPerror[];
        WITH mdb[m] SELECT FROM
	  allProcs => 
	    ReportProcIncludedTwice[
	      opi, mdb[m].mti, proc[opi].codePack, mdb[m].cp];
	  someProcs => 
	    IF proc[opi].codePack # Tree.nullIndex THEN
	      ReportProcIncludedTwice[
	        opi, mdb[m].mti, proc[opi].codePack, mdb[m].cp]
	    ELSE {procIncluded[opi] ← TRUE;  proc[opi].codePack ← mdb[m].cp};
	  ENDCASE}};

  ReportProcIncludedTwice: PROC [
      opi: PackageSymbols.OPIndex, mti: BcdDefs.MTIndex,
      cpNode1, cpNode2: Tree.Index] = {
    procIdSS: String.SubString ← @procIdSSDesc;
    procIdSSDesc: String.SubStringDescriptor;
    cpId1, cpId2: HashOps.HTIndex;
    SubStringForOPIndex[procIdSS, opi];
    WITH tb[cpNode1].son[1] SELECT FROM
      hash => cpId1 ← index;
      ENDCASE => CPerror[];
    WITH tb[cpNode2].son[1] SELECT FROM
      hash => cpId2 ← index;
      ENDCASE => CPerror[];
    Error.ProcPlacedTwice[error, procIdSS, mti, cpId1, cpId2]};

  SubStringForOPIndex: PUBLIC PROC [
      ss: String.SubString, opi: PackageSymbols.OPIndex] = {
    SELECT opi FROM
      OPMain  => {ss.base ← "MAIN";  ss.offset ← 0;  ss.length ← 4};
      OPEntry => {ss.base ← "ENTRY VECTOR";  ss.offset ← 0;  ss.length ← 12};
      OPCatch => {ss.base ← "CATCH CODE";  ss.offset ← 0;  ss.length ← 10};
      ENDCASE => {
        hti: Symbols.HTIndex = ModuleSymbols.outerPackArray[opi].hti;
        IF hti = Symbols.HTNull THEN {
	  ss.base ← "(unknown)";  ss.offset ← 0;  ss.length ← 9}
        ELSE SymbolOps.SubStringForHash[ss, hti]}};


 -- ******************* Code Pack Procedure Determination ********************

  codePackProcsDetermined: BOOL ← FALSE;

  Determine: PUBLIC PROC [configTreeRoot: SourceBcd.CTreeIndex] = {
    ENABLE UNWIND => Destroy[];
    IF codePackProcsDetermined THEN CPerror[];
    gd ← PackagerDefs.globalData;
    table ← gd.ownTable;
    table.AddNotify[UpdateBases];
    InitModuleHashVector[];  InitProcMap[];
    PlaceProcedures[configTreeRoot];
    ValidatePackagingDesc[];
    codePackProcsDetermined ← TRUE};
  
  Destroy: PUBLIC PROC =  {
    ReleaseModuleHashVector[];  ReleaseProcMap[];
    IF table # NIL THEN {table.DropNotify[UpdateBases];  table ← NIL};
    gd ← NIL;
    codePackProcsDetermined ← FALSE};

  
  PlaceProcedures: PROC [configTreeRoot: SourceBcd.CTreeIndex] = INLINE { 
    SourceBcd.EnumerateModulesInConfig[
      kind: prototype,
      configTreeNode: configTreeRoot,
      userProc: PlaceOneModulesProcs]};
      
  lastOpi: PackageSymbols.OPIndex ← 0;
  hasCatchCode: BOOL ← TRUE;
  
  PlaceOneModulesProcs: PROC [module: BcdDefs.MTIndex] RETURNS [stop: BOOL] = { 

    PlaceOneCDNodesProcs: PROC [cdNode: Tree.Index] RETURNS [stop: BOOL] = {
      PlaceModulesProcsForOneCDNode[module, cdNode];  RETURN[FALSE]};
  
    IF ProcessingOrder.IsEmpty[module] THEN {
      name: BcdDefs.NameRecord = SourceBcd.bcdBases.mtb[module].name;
      Error.ErrorName[error, "was never placed in a code segment"L, name];
      RETURN[FALSE]};
    IF ~SourceBcd.IsTableCompiled[module] THEN {   -- load module's symbol table
      ModuleSymbols.Load[module
        ! ModuleSymbols.InvalidSymbols => GO TO badSymbols];
      BEGIN ENABLE UNWIND => ModuleSymbols.Unload[];
      lastOpi ← (LENGTH[ModuleSymbols.outerPackArray] - 2);
      hasCatchCode ← (ModuleSymbols.outerPackArray[lastOpi+1].length # 0);
      MarkProcsUnplaced[];
      ProcessingOrder.Enumerate[module, PlaceOneCDNodesProcs];
      VerifyProcsAllPlaced[module];
      ModuleSymbols.Unload[];
      END}
    ELSE {  -- table compiled: don't load symbols
      hasCatchCode ← FALSE;
      ProcessingOrder.Enumerate[module, PlaceOneCDNodesProcs]}; 
    RETURN[FALSE];
    EXITS
      badSymbols => {
	name: BcdDefs.NameRecord = SourceBcd.bcdBases.mtb[module].name;
	Error.ErrorName[error, "has invalid symbols"L, name];
	RETURN[FALSE]}};
  
  MarkProcsUnplaced: PROC =  {
    FOR opi: PackageSymbols.OPIndex IN PackageSymbols.OPIndex DO
      proc[opi] ← [codePack: Tree.nullIndex, mark: FALSE];
      ENDLOOP};
  
  VerifyProcsAllPlaced: PROC [module: BcdDefs.MTIndex] =  {

    NotPlaced: PROC [opi: PackageSymbols.OPIndex] = { 
      procIdSS: String.SubStringDescriptor;
      SubStringForOPIndex[@procIdSS, opi];
      Error.ProcNotPlaced[error, @procIdSS, module]};

    IF proc[OPMain].codePack = Tree.nullIndex THEN NotPlaced[OPMain];
    IF proc[OPEntry].codePack = Tree.nullIndex THEN NotPlaced[OPEntry];
    IF proc[OPCatch].codePack = Tree.nullIndex AND hasCatchCode THEN
      NotPlaced[OPCatch];
    FOR opi: PackageSymbols.OPIndex IN [1..lastOpi] DO
      IF proc[opi].codePack = Tree.nullIndex THEN NotPlaced[opi];
      ENDLOOP};
     
  PlaceModulesProcsForOneCDNode: PROC [
      module: BcdDefs.MTIndex, cdNode: Tree.Index] =  {
    saveIndex: CARDINAL = gd.textIndex;
    gd.textIndex ← tb[cdNode].info;
    SELECT tb[cdNode].name FROM
      allComp          => PlaceAllCompCDProcs[module, cdNode];
      compItems        => PlaceCompItemsCDProcs[module, cdNode];
      exceptItems      => PlaceExceptItemsCDProcs[module, cdNode];
      exceptPacks      => PlaceExceptPacksCDProcs[module, cdNode];
      itemsExceptPacks => PlaceItemsExceptPacksCDProcs[module, cdNode];
      exceptPacksItems => PlaceExceptPacksItemsCDProcs[module, cdNode];
      mainOfPL, evOfPL, catchOfPL => PlaceMiscCodeForCD[module, cdNode];
      ENDCASE => CPerror[];
    gd.textIndex ← saveIndex};


--****** Place Module's Procedures For Explicit Component Descriptions ******

  PlaceAllCompCDProcs: PROC [module: BcdDefs.MTIndex, cdNode: Tree.Index] = {
    -- ComponentDesc ::= Component 
    InsertWholeModule[module: module, cpNode: tb[cdNode].cp]};

  InsertWholeModule: PROC [module: BcdDefs.MTIndex, cpNode: Tree.Index] = {
    m: ModuleIndex;
    IF LocateExistingModuleRec[module, cpNode].found THEN {
      Error.ModuleAlreadyPacked[error, module];  
      RETURN};
    m ← EnterModuleRec[module: module, cpNode: cpNode, procs: all];
    IF ~SourceBcd.IsTableCompiled[module] THEN {
      IF ~tb[cpNode].attrs[$exceptMAIN] THEN InsertProc[OPMain, m];
      IF ~tb[cpNode].attrs[$exceptEV] THEN InsertProc[OPEntry, m];
      IF ~tb[cpNode].attrs[$exceptCatch] THEN InsertProc[OPCatch, m];
      FOR opi: PackageSymbols.OPIndex IN [1..lastOpi] DO
        proc[opi].codePack ← cpNode;
        ENDLOOP}};

  PlaceCompItemsCDProcs: PROC [module: BcdDefs.MTIndex, cdNode: Tree.Index] = {
    -- ComponentDesc ::= Component [ItemList]
    WITH tb[cdNode].son[1] SELECT FROM
      symbol => {
        componentSE: SemanticEntry.STIndex = index;
	WITH stb[componentSE] SELECT FROM
          module => { 
	    IF mti # module THEN CPerror[];
	    InsertNamedProcsFromModule[
	      module: module, cpNode: tb[cdNode].cp, 
	      itemList: tb[cdNode].son[2]]}; 
          config =>  -- ProcessingOrderImpl found module should be processed 
	    InsertWholeModule[module: module, cpNode: tb[cdNode].cp];
          ENDCASE};
      ENDCASE => CPerror[]};

  InsertNamedProcsFromModule: PROC [
      module: BcdDefs.MTIndex, cpNode: Tree.Index, itemList: Tree.Link] = {
    procSS: String.SubString ← @procSSDesc;
    procSSDesc: String.SubStringDescriptor;
    opi: PackageSymbols.OPIndex;
    m: ModuleIndex;

    InsertOneProc: Tree.Scan = {
      WITH t SELECT FROM
        hash => {
          procId: HashOps.HTIndex = index;
          HashOps.SubStringForHash[procSS, procId];
          opi ← ModuleSymbols.FindProc[procSS];
	  IF opi = PackageSymbols.OPNull THEN
	    Error.NotProcInModule[error, procId, module]
	  ELSE InsertProc[opi, m]};
        subtree => {
          itemNode: Tree.Index = index;
	  SELECT tb[itemNode].name FROM
	    main => {
	      IF tb[cpNode].attrs[$exceptMAIN] THEN {
	        Error.Error[warning, "MAIN is included in a code pack for which EXCEPT[MAIN] was specified"L];
	        RETURN};
	      InsertProc[OPMain, m]};
	    ev => {
	      IF tb[cpNode].attrs[$exceptEV] THEN {
	        Error.Error[warning, "ENTRY VECTOR is included in a code pack for which EXCEPT[ENTRY VECTOR] was specified"L];
	        RETURN};
	      InsertProc[OPEntry, m]};
	    catch => {
	      IF tb[cpNode].attrs[$exceptCatch] THEN {
	        Error.Error[warning, "CATCH CODE is included in a code pack for which EXCEPT[CATCH CODE] was specified"L];
	        RETURN};
	      InsertProc[OPCatch, m]};
	    ENDCASE => CPerror[]};
        ENDCASE => CPerror[]};
  
    m ← EnterModuleRec[module: module, cpNode: cpNode, procs: some];
    TreeOps.ScanList[itemList, InsertOneProc]};


  PlaceExceptItemsCDProcs: PROC [module: BcdDefs.MTIndex, cdNode: Tree.Index] = {
    -- ComponentDesc ::= Component EXCEPT [ItemList]
    WITH tb[cdNode].son[1] SELECT FROM
      symbol => {
        componentSE: SemanticEntry.STIndex = index;
	WITH stb[componentSE] SELECT FROM
          module => { 
	    IF mti # module THEN CPerror[];
	    ExcludeNamedProcsFromModule[
	      module: module, cpNode: tb[cdNode].cp, 
	      itemList: tb[cdNode].son[2]]}; 
          config =>  -- ProcessingOrderImpl found module should be output 
	    InsertWholeModule[module: module, cpNode: tb[cdNode].cp];
          ENDCASE};
      ENDCASE => CPerror[]};

  ExcludeNamedProcsFromModule: PROC [
      module: BcdDefs.MTIndex, cpNode: Tree.Index, itemList: Tree.Link] = {
    m: ModuleIndex = EnterModuleRec[module: module, cpNode: cpNode, procs: some];

    RemoveMarkOfNamedProc: Tree.Scan = {
      WITH t SELECT FROM
        hash => {
          procId: HashOps.HTIndex = index;
          procSS: String.SubString ← @procSSDesc;
          procSSDesc: String.SubStringDescriptor;
          opi: PackageSymbols.OPIndex;
          HashOps.SubStringForHash[procSS, procId];
          opi ← ModuleSymbols.FindProc[procSS];
	  IF opi = PackageSymbols.OPNull THEN 
	    Error.NotProcInModule[error, procId, module]
	  ELSE WITH mdb[m] SELECT FROM
            someProcs => proc[opi].mark ← FALSE;
            ENDCASE};
        subtree => {
          itemNode: Tree.Index = index;
	  SELECT tb[itemNode].name FROM
	    main  => proc[OPMain].mark ← FALSE;
	    ev    => proc[OPEntry].mark ← FALSE;
	    catch => proc[OPCatch].mark ← FALSE;
	    ENDCASE => CPerror[]};
        ENDCASE => CPerror[]};
  
    MarkAllProcs[m];  -- then remove marks for those procs to exclude 
    IF tb[cpNode].attrs[$exceptMAIN] THEN proc[OPMain].mark ← FALSE;
    IF tb[cpNode].attrs[$exceptEV] THEN proc[OPEntry].mark ← FALSE;
    IF tb[cpNode].attrs[$exceptCatch] THEN proc[OPCatch].mark ← FALSE;
    TreeOps.ScanList[itemList, RemoveMarkOfNamedProc];
    InsertRemainingMarkedProcs[m]};

  MarkAllProcs: PROC [m: ModuleIndex] = {
    WITH mdb[m] SELECT FROM
      allProcs => Error.ModuleAlreadyPacked[error, mdb[m].mti];
      someProcs => {
        proc[OPMain].mark ← TRUE;
        proc[OPEntry].mark ← TRUE;
        proc[OPCatch].mark ← TRUE;
        FOR opi: PackageSymbols.OPIndex IN [1..lastOpi] DO
	  proc[opi].mark ← TRUE;
	  ENDLOOP};
      ENDCASE};

  InsertRemainingMarkedProcs: PROC [m: ModuleIndex] = {
    IF proc[OPMain].mark THEN InsertProc[OPMain, m];  -- wasn't excluded
    IF proc[OPEntry].mark THEN InsertProc[OPEntry, m];
    IF proc[OPCatch].mark THEN InsertProc[OPCatch, m];
    FOR opi: PackageSymbols.OPIndex IN [1..lastOpi] DO
      IF proc[opi].mark THEN InsertProc[opi, m];
      ENDLOOP};


--****** Place Module's Procedures For Implicit Component Descriptions ******

  PlaceExceptPacksCDProcs: PROC [module: BcdDefs.MTIndex, cdNode: Tree.Index] = {
    -- ComponentDesc ::= Component EXCEPT PackList
    WITH tb[cdNode].son[1] SELECT FROM
      symbol => { 
        componentSE: SemanticEntry.STIndex = index;
	WITH stb[componentSE] SELECT FROM
          module => {
            IF mti # module THEN CPerror[];
	    IncludeAllProcsNotInAnyPack[
	      module: module, cpNode: tb[cdNode].cp, 
	      packList: tb[cdNode].son[2]]}; 
          config =>  -- ProcessingOrderImpl found module should be processed 
	    IncludeAllProcsNotInAnyPack[
	      module: module, cpNode: tb[cdNode].cp, 
	      packList: tb[cdNode].son[2]]; 
          ENDCASE};
      ENDCASE => CPerror[]};

  IncludeAllProcsNotInAnyPack: PROC [
      module: BcdDefs.MTIndex, cpNode: Tree.Index, packList: Tree.Link] = {
    -- include all procs not already in a code pack of packList
    m: ModuleIndex;
    found: BOOL;

    RemoveMarksOfProcsInOnePack: Tree.Scan = {
    
      RemoveMarkOfOneProc: PROC [opi: PackageSymbols.OPIndex] RETURNS [BOOL] = {
        proc[opi].mark ← FALSE;  RETURN[FALSE]};

      WITH t SELECT FROM
        symbol => {
          cpSE: SemanticEntry.STIndex = index;
	  oldMRec: ModuleIndex;
	  WITH stb[cpSE] SELECT FROM
	    codePack => {
              [oldMRec, found] ← LocateExistingModuleRec[
                module: module, cpNode: treeNode];
              IF found THEN  -- procs from module in old cp
                EnumerateProcs[oldMRec, RemoveMarkOfOneProc]};
            ENDCASE};
        ENDCASE => CPerror[]};
  
    IF SourceBcd.IsTableCompiled[module] THEN {
      [m, found] ← LocateExistingModuleRec[module: module, cpNode: cpNode];
      IF ~found THEN InsertWholeModule[module: module, cpNode: cpNode]
      ELSE Error.TableCompModuleNotIncAsUnit[error, module]}
    ELSE {
      m ← EnterModuleRec[module: module, cpNode: cpNode, procs: some];
      MarkAllProcs[m];  -- then remove marks for those procs to exclude 
      IF tb[cpNode].attrs[$exceptMAIN] THEN proc[OPMain].mark ← FALSE;
      IF tb[cpNode].attrs[$exceptEV] THEN proc[OPEntry].mark ← FALSE;
      IF tb[cpNode].attrs[$exceptCatch] THEN proc[OPCatch].mark ← FALSE;
      TreeOps.ScanList[packList, RemoveMarksOfProcsInOnePack];
      InsertRemainingMarkedProcs[m]}};


  PlaceItemsExceptPacksCDProcs: PROC [
      module: BcdDefs.MTIndex, cdNode: Tree.Index] = {
    -- ComponentDesc ::= Component [ItemList] EXCEPT PackList
    WITH tb[cdNode].son[1] SELECT FROM
      symbol => {
        componentSE: SemanticEntry.STIndex = index;
	WITH stb[componentSE] SELECT FROM  -- component must not be a module
          config =>  -- ProcessingOrderImpl found module should be processed 
	    IncludeAllProcsNotInAnyPack[
	      module: module, cpNode: tb[cdNode].cp, 
	      packList: tb[cdNode].son[3]]; 
          ENDCASE};
      ENDCASE => CPerror[]};


  PlaceExceptPacksItemsCDProcs: PROC [
      module: BcdDefs.MTIndex, cdNode: Tree.Index] = {
    -- ComponentDesc ::= Component EXCEPT PackList, [ItemList]
    WITH tb[cdNode].son[1] SELECT FROM
      symbol => {
        componentSE: SemanticEntry.STIndex = index;
	WITH stb[componentSE] SELECT FROM
          module => {
            IF mti # module THEN CPerror[];
	    IncludeProcsNotInPackNorItemLists[
	      module: module, cpNode: tb[cdNode].cp,
	      packList: tb[cdNode].son[2], itemList: tb[cdNode].son[3]]}; 
          config =>  -- ProcessingOrderImpl found module should be processed 
	    IncludeAllProcsNotInAnyPack[
	      module: module, cpNode: tb[cdNode].cp, 
	      packList: tb[cdNode].son[2]]; 
          ENDCASE};
      ENDCASE => CPerror[]};

  IncludeProcsNotInPackNorItemLists: PROC [
      module: BcdDefs.MTIndex, cpNode: Tree.Index, 
      packList: Tree.Link, itemList: Tree.Link] = {
    -- include all procs not already in a code pack or in item list
    m: ModuleIndex = EnterModuleRec[module: module, cpNode: cpNode, procs: some];
    found: BOOL;

    RemoveMarksOfProcsInOnePack: Tree.Scan = {
    
      RemoveMarkOfOneProcInPack: PROC [
	  opi: PackageSymbols.OPIndex] RETURNS [stop: BOOL] = {
        proc[opi].mark ← FALSE;  RETURN[FALSE]};

      WITH t SELECT FROM
        symbol => {
          cpSE: SemanticEntry.STIndex = index;
	  oldMRec: ModuleIndex;
	  WITH stb[cpSE] SELECT FROM
	    codePack => {
              [oldMRec, found] ← LocateExistingModuleRec[
                module: module, cpNode: treeNode];
              IF found THEN  -- procs from module in old cp
                EnumerateProcs[oldMRec, RemoveMarkOfOneProcInPack]};
            ENDCASE};
        ENDCASE => CPerror[]};

    RemoveMarkOfOneProc: Tree.Scan = {
      WITH t SELECT FROM
        hash => {
          procId: HashOps.HTIndex = index;
	  procSS: String.SubString ← @procSSDesc;
	  procSSDesc: String.SubStringDescriptor;
	  opi: PackageSymbols.OPIndex;
          HashOps.SubStringForHash[procSS, procId];
          opi ← ModuleSymbols.FindProc[procSS];
	  IF opi = PackageSymbols.OPNull THEN
	    Error.NotProcInModule[error, procId, module]
	  ELSE proc[opi].mark ← FALSE};
        subtree => {
          itemNode: Tree.Index = index;
	  SELECT tb[itemNode].name FROM
	    main  => proc[OPMain].mark ← FALSE;
	    ev    => proc[OPEntry].mark ← FALSE;
	    catch => proc[OPCatch].mark ← FALSE;
	    ENDCASE => CPerror[]};
        ENDCASE => CPerror[]};
  
    MarkAllProcs[m];  -- then remove marks for those procs to exclude 
    IF tb[cpNode].attrs[$exceptMAIN] THEN proc[OPMain].mark ← FALSE;
    IF tb[cpNode].attrs[$exceptEV] THEN proc[OPEntry].mark ← FALSE;
    IF tb[cpNode].attrs[$exceptCatch] THEN proc[OPCatch].mark ← FALSE;
    TreeOps.ScanList[packList, RemoveMarksOfProcsInOnePack];
    TreeOps.ScanList[itemList, RemoveMarkOfOneProc];
    InsertRemainingMarkedProcs[m]};


--**** Place Module's Procedures For MAIN/ENTRY VECTOR/CATCH CODE OF CDs ****

  PlaceMiscCodeForCD: PROC [module: BcdDefs.MTIndex, cdNode: Tree.Index] = {
    -- ComponentDesc ::= MAIN OF PackList
    -- ComponentDesc ::= ENTRY VECTOR OF PackList
    -- ComponentDesc ::= CATCH CODE OF PackList
    -- add module's main proc/ev/catch code if any of its other procs in packlist 
    packList: Tree.Link = tb[cdNode].son[1];
    firstInList: Tree.Link;
    miscCodeOpi: PackageSymbols.OPIndex = 
      (SELECT tb[cdNode].name FROM
         mainOfPL => OPMain,
	 evOfPL   => OPEntry,
	 ENDCASE --catchOfPL-- => OPCatch);

    EnterMiscCodeIfOtherProcsInASegmentsPack: Tree.Test = {
      WITH t SELECT FROM
        subtree => {
          segsCpNode: Tree.Index = index;
	  inserted: BOOL = 
	    InsertMiscCodeIfOtherProcsInPack[
	      module: module, cpNode: tb[cdNode].cp, miscCodeOpi: miscCodeOpi,
	      existingCpNode: segsCpNode];
	  RETURN[inserted]};  -- stop enumeration if main/ev/catch code inserted
        ENDCASE => CPerror[];
      RETURN[FALSE]};

    EnterMiscCodeIfOtherProcsInOnePack: Tree.Test = {
      WITH t SELECT FROM
        symbol => {
          cpSE: SemanticEntry.STIndex = index;
	  WITH stb[cpSE] SELECT FROM
            codePack => { 
	      inserted: BOOL =
	        InsertMiscCodeIfOtherProcsInPack[
		  module: module, cpNode: tb[cdNode].cp, miscCodeOpi: miscCodeOpi, 
		  existingCpNode: treeNode];
	      RETURN[inserted]};  -- stop if main/ev/catch code inserted
            ENDCASE};
        ENDCASE => CPerror[];
      RETURN[FALSE]};

    IF SourceBcd.IsTableCompiled[module] THEN RETURN;
    IF TreeOps.ListLength[packList] = 1 THEN {
      -- packlist might only be name of current code segment
      firstInList ← TreeOps.ListHead[packList];
      WITH firstInList SELECT FROM
        symbol => { 
          firstSE: SemanticEntry.STIndex = index;
	  WITH stb[firstSE] SELECT FROM
	    segment => {
	      segNode: Tree.Index = treeNode;  -- the current segment
	      TreeOps.SearchList[
	        tb[segNode].son[2], EnterMiscCodeIfOtherProcsInASegmentsPack];
	      RETURN};
	    ENDCASE};
        ENDCASE => CPerror[]};
    TreeOps.SearchList[packList, EnterMiscCodeIfOtherProcsInOnePack]};

  InsertMiscCodeIfOtherProcsInPack: PROC [
        module: BcdDefs.MTIndex, cpNode: Tree.Index,
	miscCodeOpi: PackageSymbols.OPIndex, existingCpNode: Tree.Index] 
      RETURNS [inserted: BOOL] = {
    -- insert module's main/ev/catch code if it has other procs in existingCpNode
    existingMRec, newMRec: ModuleIndex;
    found: BOOL;
    [existingMRec, found] ← LocateExistingModuleRec[
      module: module, cpNode: existingCpNode];
    IF found THEN {
      newMRec ← EnterModuleRec[module: module, cpNode: cpNode, procs: some]; 
      InsertProc[miscCodeOpi, newMRec];
      RETURN[TRUE]}
    ELSE RETURN[FALSE]};


 --********* Validate packaging description ********* 
 --        For each code pack, check that
 --          (1) it is nonempty, and  
 --          (2) procedures were included from each module, and  
 --        For each module, check that
 --          (1) the entry vector precedes any procedure and catch code

  evPlaced: LONG POINTER TO EVPlacedMap ← NIL;  -- SourceBcd.ModuleNum -> BOOL
  EVPlacedMap: TYPE = RECORD [SEQUENCE length: CARDINAL OF BOOL];

  ValidatePackagingDesc: PROC [] = {
    ENABLE UNWIND => ReleaseEVPlacedArray[];
    InitEVPlacedArray[];
    EnumerateSegments[CheckOneCodeSegment];
    ReleaseEVPlacedArray[]};
    
  InitEVPlacedArray: PROC = {
    evPlaced ← gd.zone.NEW[EVPlacedMap[SourceBcd.moduleCount]];
    FOR i: SourceBcd.ModuleNum IN [0..SourceBcd.moduleCount) DO
      evPlaced[i] ← FALSE;
      ENDLOOP};

  ReleaseEVPlacedArray: PROC = {
    IF evPlaced # NIL THEN gd.zone.FREE[@evPlaced]};

  CheckOneCodeSegment: PROC [segNode: Tree.Index] RETURNS [stop: BOOL] = {
    IF segNode # Tree.nullIndex THEN EnumerateCodePacks[segNode, CheckOneCodePack];
    RETURN[FALSE]};
  
  currentCPId: HashOps.HTIndex;
  cpEmpty, discardCP: BOOL;

  CheckOneCodePack: PROC [cpNode: Tree.Index] RETURNS [stop: BOOL] = {
    IF cpNode # Tree.nullIndex THEN {
      WITH tb[cpNode].son[1] SELECT FROM
	hash => currentCPId ← index;
	ENDCASE; 
      discardCP ← IsDiscardCodePack[cpNode];
      cpEmpty ← TRUE;
      EnumerateModules[cpNode, CheckOneCodePackModule];
      IF cpEmpty THEN Error.EmptyCodePack[error, currentCPId]};  
    RETURN[FALSE]}; 

  currentMti: BcdDefs.MTIndex;
  currentModuleNum: SourceBcd.ModuleNum;

  CheckOneCodePackModule: PROC [
        mti: BcdDefs.MTIndex, module: CodePackProcs.ModuleIndex] 
      RETURNS [stop: BOOL] = {
    IF SourceBcd.IsTableCompiled[mti] THEN cpEmpty ← FALSE
    ELSE
      IF AnyProcs[module] THEN {
        cpEmpty ← FALSE;
	currentMti ← mti;  currentModuleNum ← SourceBcd.ModuleNumForMti[mti];
	EnumerateProcs[module, CheckOneProc]}
      ELSE Error.NoProcFromModuleInCP[warning, mti, currentCPId];
    RETURN[FALSE]}; 

  CheckOneProc: PROC [opi: PackageSymbols.OPIndex] RETURNS [stop: BOOL] = {
    SELECT opi FROM
      OPEntry => {
        IF discardCP THEN {
	  Error.EVInDiscardCodePack[error, currentMti];
	  RETURN[TRUE]};
	evPlaced[currentModuleNum] ← TRUE};
      OPCatch =>
        IF hasCatchCode AND ~evPlaced[currentModuleNum] THEN {
	  Error.EVNotFirst[error, currentMti];
	  RETURN[TRUE]}; 
      ENDCASE =>
        IF ~evPlaced[currentModuleNum] THEN {
	  Error.EVNotFirst[error, currentMti];
	  RETURN[TRUE]}; 
    RETURN[FALSE]}; 


 --******************** Code Pack Procedure Enumeration **********************
  
  EnumerateSegments: PUBLIC PROC [
      userProc: PROC [segNode: Tree.Index] RETURNS [stop: BOOL]] = { 

    OutputOneCodeSegment: Tree.Test = {
      WITH t SELECT FROM
        subtree => {
          treeNode: Tree.Index = index;
          SELECT tb[treeNode].name FROM
	    codeSeg, merge =>
	      IF ~tb[treeNode].attrs[$superceded] THEN { 
	        IF userProc[treeNode] THEN RETURN[TRUE]}; -- stop enumeration
	    ENDCASE};
        ENDCASE => CPerror[];
      RETURN[FALSE]};
  
    TreeOps.SearchList[gd.root, OutputOneCodeSegment]};

  SubStringForSegmentNode: PUBLIC PROC [
      ss: String.SubString, segNode: Tree.Index] = {
    WITH tb[segNode].son[1] SELECT FROM
      hash => {
	segmentHti: HashOps.HTIndex = index;
	HashOps.SubStringForHash[ss, segmentHti]};
      ENDCASE => CPerror[]};


  EnumerateCodePacks: PUBLIC PROC [
      segNode: Tree.Index, 
      userProc: PROC [cpNode: Tree.Index] RETURNS [stop: BOOL]] = {
    saveIndex: CARDINAL = gd.textIndex;

    OutputOneCodePack: Tree.Test = {
      WITH t SELECT FROM
        subtree => {
          cpNode: Tree.Index = index;
          SELECT tb[cpNode].name FROM
	    codePack, unnamedCodePack, discardCodePack => {
	      IF tb[cpNode].attrs[$superceded] THEN CPerror[]; 
	      IF userProc[cpNode] THEN RETURN[TRUE]}; -- stop enumeration
	    ENDCASE => CPerror[]};
        ENDCASE => CPerror[];
      RETURN[FALSE]};
  
    gd.textIndex ← tb[segNode].info;
    IF tb[segNode].attrs[$superceded] THEN CPerror[];
    TreeOps.SearchList[tb[segNode].son[2], OutputOneCodePack];
    gd.textIndex ← saveIndex};


  SubStringForCodePackNode: PUBLIC PROC [
      ss: String.SubString, cpNode: Tree.Index] = {
    WITH tb[cpNode].son[1] SELECT FROM
      hash => {
	codePackHti: HashOps.HTIndex = index;
	HashOps.SubStringForHash[ss, codePackHti]};
      ENDCASE => CPerror[]};

  HtiForCodePackNode: PUBLIC PROC [
      cpNode: Tree.Index] RETURNS [hti: HashOps.HTIndex] = {
    WITH tb[cpNode].son[1] SELECT FROM
      hash => {hti ← index; RETURN[hti]};
      ENDCASE => CPerror[]};

  IsDiscardCodePack: PUBLIC PROC [cpNode: Tree.Index] RETURNS [yes: BOOL] = {
    IF cpNode = Tree.nullIndex THEN CPerror[];
    RETURN[ tb[cpNode].name = discardCodePack ]};


  DoneEnumeratingModules: SIGNAL = CODE;

  EnumerateModules: PUBLIC PROC [
      cpNode: Tree.Index, 
      userProc: PROC [
        mti: BcdDefs.MTIndex, module: ModuleIndex] RETURNS [stop: BOOL]] = {
    saveIndex: CARDINAL = gd.textIndex;
    gd.textIndex ← tb[cpNode].info;
    IF tb[cpNode].attrs[$superceded] THEN CPerror[]; -- code pack has been superceded
    OutputModules[
      cpNode: cpNode, userProc: userProc ! DoneEnumeratingModules => CONTINUE];
    gd.textIndex ← saveIndex};

  OutputModules: PROC [  -- called recursively when multiple layers of merging
      cpNode: Tree.Index, 
      userProc: PROC [
        mti: BcdDefs.MTIndex, module: ModuleIndex] RETURNS [stop: BOOL]] = {
    SELECT tb[cpNode].name FROM
      codePack, unnamedCodePack, discardCodePack => {
        cdList: Tree.Link = tb[cpNode].son[2];
	firstCdLink: Tree.Link = TreeOps.ListHead[cdList];
        WITH firstCdLink SELECT FROM
          symbol =>  -- cpNode is a code pack in a merged code segment
    	    OutputModulesOfMergedOldCodePacks[
	      oldCpList: cdList, userProc: userProc];
          ENDCASE =>  -- cpNode is a "normal" code pack 
	    OutputCodePackModules[
	      moduleList: tb[cpNode].son[3], userProc: userProc]};
      ENDCASE => CPerror[]};

  OutputModulesOfMergedOldCodePacks: PROC [
      oldCpList: Tree.Link, 
      userProc: PROC [
        mti: BcdDefs.MTIndex, module: ModuleIndex] RETURNS [stop: BOOL]] = {

    OutputModulesOfOneOldCodePack: Tree.Scan = {
      WITH t SELECT FROM
        symbol => {
          oldCpSE: SemanticEntry.STIndex = index;
          WITH stb[oldCpSE] SELECT FROM
	    codePack => {
	      oldCpNode: Tree.Index = treeNode;
	      OutputModules[cpNode: oldCpNode, userProc: userProc]};
	    ENDCASE};
        ENDCASE => CPerror[]};
  
    TreeOps.ScanList[oldCpList, OutputModulesOfOneOldCodePack]};

  OutputCodePackModules: PROC [
      moduleList: Tree.Link, 
      userProc: PROC [
      mti: BcdDefs.MTIndex, module: ModuleIndex] RETURNS [stop: BOOL]] = {
    moduleChainHead: ModuleIndex = NARROW[moduleList, Tree.ProcsLink].index;
    FOR m: ModuleIndex ← moduleChainHead, mdb[m].next UNTIL m = nullModuleIndex DO
      mti: BcdDefs.MTIndex = mdb[m].mti;
      stopEnumeration: BOOL;
      IF ~SourceBcd.IsTableCompiled[mti] THEN {
	ModuleSymbols.Load[mti ! ModuleSymbols.InvalidSymbols => LOOP];
	lastOpi ← (LENGTH[ModuleSymbols.outerPackArray] - 2);
	hasCatchCode ← (ModuleSymbols.outerPackArray[lastOpi+1].length # 0);
	stopEnumeration ← userProc[mti, m 
	      ! UNWIND => ModuleSymbols.Unload[]];
	ModuleSymbols.Unload[];
	IF stopEnumeration THEN SIGNAL DoneEnumeratingModules}
      ELSE {  -- table compiled: don't load symbol table 
	hasCatchCode ← FALSE;
	IF userProc[mti, m] THEN SIGNAL DoneEnumeratingModules};
      ENDLOOP};

  AnyProcs: PUBLIC PROC [module: ModuleIndex] RETURNS [reply: BOOL] = {
    -- return TRUE if any procedures are specified by a ModuleRecord
    IF SourceBcd.IsTableCompiled[mdb[module].mti] THEN RETURN[FALSE];
    IF mdb[module].includeMAIN THEN RETURN[TRUE];
    IF mdb[module].includeEV THEN RETURN[TRUE];
    IF mdb[module].includeCatch THEN RETURN[TRUE];
    WITH mdb[module] SELECT FROM
      allProcs => RETURN[TRUE];
      someProcs => 
	FOR p: PackageSymbols.OPIndex IN [1..lastOpi] DO
	  IF procIncluded[p] THEN RETURN[TRUE];
	  ENDLOOP;
      ENDCASE => CPerror[];
    RETURN[FALSE]};

  EnumerateProcs: PUBLIC PROC [
      module: ModuleIndex, 
      userProc: PROC [proc: PackageSymbols.OPIndex] RETURNS [stop: BOOL]] = {
    IF SourceBcd.IsTableCompiled[mdb[module].mti] THEN RETURN;
    IF mdb[module].includeEV THEN IF userProc[OPEntry] THEN RETURN;
    IF mdb[module].includeMAIN THEN IF userProc[OPMain] THEN RETURN;
    WITH mdb[module] SELECT FROM
      allProcs => { 
	FOR p: PackageSymbols.OPIndex IN [1..lastOpi] DO
	  IF userProc[p] THEN RETURN;
	  ENDLOOP};
      someProcs => {
        FOR p: PackageSymbols.OPIndex IN [1..lastOpi] DO
	  IF procIncluded[p] THEN IF userProc[p] THEN RETURN;
	  ENDLOOP};
      ENDCASE => CPerror[];
    IF mdb[module].includeCatch THEN [] ← userProc[OPCatch]};

  END.