-- SourceBcdImpl.mesa -- Last edited by Lewis on 4-Jan-82 14:16:52 -- Last edited by Satterthwaite, December 30, 1982 10:38 am 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], BcdOps USING [BcdBase], BcdUtilDefs USING [BcdBasePtr, BcdBases, BcdLimitPtr, BcdLimits], CIFS: TYPE USING [GetFC], Error USING [Error, AmbiguousComponent, UnknownComponent], HashOps USING [HTIndex, htNull, SubStringForHash], Inline USING [BITAND, BITXOR], PackagerDefs USING [globalData, GlobalData, packctreetype], Space: TYPE USING [ Handle, nullHandle, virtualMemory, Create, Delete, LongPointer, Map], String USING [EqualSubStrings, SubString, SubStringDescriptor], SourceBcd, Table: TYPE USING [Base, Limit]; SourceBcdImpl: PROGRAM IMPORTS Alloc, CIFS, Error, Inline, HashOps, PackagerDefs, Space, String 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 = String.SubStringDescriptor; SubString: TYPE = String.SubString; gd: PackagerDefs.GlobalData ← NIL; -- initialized by Load table: Alloc.Handle ← NIL; -- ****************** Source BCD Loading and Unloading ****************** bcdHeader: PUBLIC BcdOps.BcdBase ← NIL; bcdBases: PUBLIC BcdUtilDefs.BcdBasePtr ← NIL; bcdLimits: PUBLIC BcdUtilDefs.BcdLimitPtr ← NIL; moduleCount: PUBLIC CARDINAL ← 0; bcdSegment: Space.Handle ← Space.nullHandle; Load: PUBLIC PROC = { ENABLE UNWIND => Unload[]; pages: CARDINAL; gd ← PackagerDefs.globalData; table ← gd.ownTable; bcdSegment ← Space.nullHandle; bcdHeader ← NIL; bcdBases ← NIL; bcdLimits ← NIL; bcdSegment ← Space.Create[size: 10, parent: Space.virtualMemory]; bcdSegment.Map[window: [file: gd.sourceBcdFile.GetFC, base: 1]]; bcdHeader ← bcdSegment.LongPointer; IF bcdHeader.versionIdent # BcdDefs.VersionID OR bcdHeader.definitions THEN { Unload[]; Error.Error[ error, "Invalid input BCD file: obsolete version or definitions BCD"L]; ERROR BadSourceBcd}; IF bcdHeader.repackaged THEN { Unload[]; Error.Error[error, "Already packaged BCDs cannot be repackaged"L]; ERROR BadSourceBcd}; IF (pages ← bcdHeader.nPages) > 10 THEN { -- load entire bcd Space.Delete[bcdSegment]; bcdSegment ← Space.Create[size: pages, parent: Space.virtualMemory]; bcdSegment.Map[window: [file: gd.sourceBcdFile.GetFC, base: 1]]; bcdHeader ← bcdSegment.LongPointer}; gd.sourceBcdVersion ← bcdHeader.version; bcdBases ← gd.zone.NEW[BcdUtilDefs.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] ]]; bcdLimits ← gd.zone.NEW[BcdUtilDefs.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[]}; Unload: PUBLIC PROC = { IF bcdSegment # Space.nullHandle THEN { Space.Delete[bcdSegment]; bcdSegment ← Space.nullHandle; bcdHeader ← NIL}; IF bcdBases # NIL THEN gd.zone.FREE[@bcdBases]; IF bcdLimits # NIL THEN gd.zone.FREE[@bcdLimits]; ReleaseMtiArray[]; moduleCount ← 0; table ← NIL; gd ← NIL}; EnumerateConfigs: PUBLIC PROC [ userProc: PROC [CTIndex] RETURNS [stop: BOOL]] = BEGIN cti: CTIndex ← CTIndex.FIRST; UNTIL cti = bcdLimits.ct DO IF userProc[cti] THEN RETURN; cti ← cti + CTRecord.SIZE + bcdBases.ctb[cti].nControls; ENDLOOP; END; EnumerateModules: PUBLIC PROC [ userProc: PROC [MTIndex] RETURNS [stop: BOOL]] = { FOR mti: MTIndex ← MTIndex.FIRST, mti + MTRecord.SIZE UNTIL mti = bcdLimits.mt DO IF userProc[mti] THEN RETURN; ENDLOOP}; IsTableCompiled: PUBLIC PROC [mti: BcdDefs.MTIndex] RETURNS [BOOL] = { RETURN[bcdBases.mtb[mti].tableCompiled]}; SubStringForName: PUBLIC PROC [ss: String.SubString, name: NameRecord] = { ss.base ← @bcdBases.ssb.string; ss.offset ← name; ss.length ← bcdBases.ssb.size[name]}; EqualIdAndName: PUBLIC PROC [ id: HashOps.HTIndex, name: NameRecord] RETURNS [yes: BOOL] = { idSS: SubString ← @idSSDesc; idSSDesc: SubStringDescriptor; nameSS: SubString ← @nameSSDesc; nameSSDesc: SubStringDescriptor; HashOps.SubStringForHash[idSS, id]; SubStringForName[nameSS, name]; RETURN[String.EqualSubStrings[idSS, nameSS]]}; CountModules: PROC = { CountOneModule: PROC [mti: BcdDefs.MTIndex] RETURNS [stop: BOOL←FALSE] = { moduleCount ← moduleCount+1}; moduleCount ← 0; EnumerateModules[CountOneModule]}; -- BcdDefs.MTIndex -> ModuleNum mapping related declarations mtiArray: LONG POINTER TO ModuleMap; -- ModuleNum -> MTIndex ModuleMap: TYPE = RECORD [SEQUENCE length: CARDINAL OF MTIndex]; InitializeMtiArray: PROC = { i: ModuleNum ← 0; EnterOneModule: PROC [mti: BcdDefs.MTIndex] RETURNS [stop: BOOL←FALSE] = { mtiArray[i] ← mti; i ← i+1}; mtiArray ← gd.zone.NEW[ModuleMap[moduleCount]]; EnumerateModules[EnterOneModule]}; ReleaseMtiArray: PROC = { IF mtiArray # NIL THEN gd.zone.FREE[@mtiArray]}; ModuleNumForMti: PUBLIC PROC [mti: BcdDefs.MTIndex] RETURNS [ModuleNum] = BEGIN -- map i-th module index to i Ord: PROC [mti: BcdDefs.MTIndex] RETURNS [CARDINAL] = INLINE { RETURN [mti-BcdDefs.MTIndex.FIRST]}; orderedMti: CARDINAL = Ord[mti]; l, m, u: ModuleNum; l ← 0; u ← moduleCount; UNTIL l > u DO m ← (l+u)/2; SELECT Ord[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 node attributes ***************** ConfigTreeNode: PUBLIC TYPE = RECORD [ father: CTreeIndex, -- containing config brother: CTreeIndex, -- next config/module in containing config firstSon: CTreeIndex, -- first contained config/module prototypeName: BcdDefs.NameRecord, anotherNodeWSameProtoName: BOOL, instanceLink, prototypeLink: CTreeIndex, -- links nodes w = hash values instancePrev, prototypePrev: CTreeIndex, -- links nodes w = ids index: BcdTableLoc, -- module or config table index body: SELECT kind: ComponentKind FROM instance => [instanceName: BcdDefs.NameRecord], prototype => [] -- for prototypes, instanceName = prototypeName ENDCASE]; CTreeIndex: TYPE = Table.Base RELATIVE POINTER[0..Table.Limit) TO ConfigTreeNode; -- Conceptually, all components (modules and configurations) stored in the -- Configuration Tree have names of the form [instanceName prototypeName]. -- If the component is a prototype, however, only the prototype name is -- actually stored; its instance name is the same as the prototype name. ctreeb: Table.Base; UpdateBases: Alloc.Notifier = {ctreeb ← base[PackagerDefs.packctreetype]}; Father: PUBLIC PROC [self: CTreeIndex] RETURNS [CTreeIndex] = { RETURN [ctreeb[self].father]}; EnumerateSons: PUBLIC PROC [ self: CTreeIndex, userProc: PROC [CTreeIndex] RETURNS [stop: BOOL]] = { IF self # nullCTreeIndex THEN FOR cti: CTreeIndex ← ctreeb[self].firstSon, ctreeb[cti].brother UNTIL cti=nullCTreeIndex DO IF userProc[cti] THEN EXIT; ENDLOOP; RETURN}; Kind: PUBLIC PROC [self: CTreeIndex] RETURNS [ComponentKind] = { RETURN [ctreeb[self].kind]}; Name: PUBLIC PROC [ self: CTreeIndex, kind: ComponentKind] RETURNS [BcdDefs.NameRecord] = { RETURN [SELECT kind FROM $prototype => ctreeb[self].prototypeName, $instance => WITH s~~ctreeb[self] SELECT FROM instance => s.instanceName, ENDCASE => s.prototypeName, ENDCASE => ERROR]}; SharedProtoName: PUBLIC PROC [self: CTreeIndex] RETURNS [BOOL] = { RETURN [ctreeb[self].anotherNodeWSameProtoName]}; Link: PUBLIC PROC [self: CTreeIndex, kind: ComponentKind] RETURNS [CTreeIndex] = { RETURN [SELECT kind FROM $prototype => ctreeb[self].prototypeLink, $instance => ctreeb[self].instanceLink, ENDCASE => ERROR]}; Prev: PUBLIC PROC [self: CTreeIndex, kind: ComponentKind] RETURNS [CTreeIndex] = { RETURN [SELECT kind FROM $prototype => ctreeb[self].prototypePrev, $instance => ctreeb[self].instancePrev, ENDCASE => ERROR]}; Index: PUBLIC PROC [self: CTreeIndex] RETURNS [BcdTableLoc] = { RETURN [ctreeb[self].index]}; -- ******************** Configuration tree creation ******************** BuildConfigTree: PUBLIC PROC RETURNS [root: CTreeIndex ← nullCTreeIndex] = BEGIN rootPointsToModule: BOOL ← FALSE; EnterOneModule: PROC [module: MTIndex] RETURNS [stop: BOOL] = 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 root = nullCTreeIndex THEN {root ← 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: BOOL] = 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 root # nullCTreeIndex AND ~rootPointsToModule THEN CTreeBuildError[] ELSE {root ← 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; table.AddNotify[UpdateBases]; InitializeHashVectors[]; InitializeModuleVector[]; BEGIN ENABLE UNWIND => DestroyConfigTree[root]; EnumerateModules[EnterOneModule]; EnumerateConfigs[EnterOneConfig]; END; RETURN END; DestroyConfigTree: PUBLIC PROC [root: CTreeIndex] = BEGIN ReleaseHashVectors[]; ReleaseModuleVector[]; IF table # NIL THEN table.DropNotify[UpdateBases]; END; CTreeHVSize: CARDINAL = 71; CTreeHash: TYPE = [0..CTreeHVSize); instHashVec: LONG POINTER TO CTreeMap ← NIL; -- CTreeHash -> CTreeIndex protoHashVec: LONG POINTER TO CTreeMap ← NIL; CTreeMap: TYPE = ARRAY CTreeHash OF CTreeIndex; InitializeHashVectors: PROC = { instHashVec ← gd.zone.NEW[CTreeMap ← ALL[nullCTreeIndex]]; protoHashVec ← gd.zone.NEW[CTreeMap ← ALL[nullCTreeIndex]]}; ReleaseHashVectors: PROC = { IF instHashVec # NIL THEN gd.zone.FREE[@instHashVec]; IF protoHashVec # NIL THEN gd.zone.FREE[@protoHashVec]}; 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: LONG 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; 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, ConfigTreeNode.prototype.SIZE]; 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, ConfigTreeNode.instance.SIZE]; 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 ← NTIndex.FIRST, nti + NTRecord.SIZE 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 [ configTreeNode: CTreeIndex, kind: ComponentKind, userProc: PROC [mti: BcdDefs.MTIndex] RETURNS [stop: BOOL]] = BEGIN IF kind = instance THEN EnumerateModuleInstancesInConfig[configTreeNode, userProc] ELSE EnumerateModulePrototypesInConfig[configTreeNode, userProc]; END; EnumerateModuleInstancesInConfig: PROC [ configTreeNode: CTreeIndex, userProc: PROC [mti: BcdDefs.MTIndex] RETURNS [stop: BOOL]] = BEGIN OutputConfigSubTree: PROC [cTreeNode: CTreeIndex] = BEGIN OPEN node: ctreeb[cTreeNode]; WITH node.index SELECT FROM m: BcdTableLoc.module => IF userProc[m.mti] THEN SIGNAL DoneEnumerating; ENDCASE; FOR son: CTreeIndex ← 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: BOOL]] = BEGIN -- no duplications must appear in the output OutputConfigSubTree: PROC [cTreeNode: CTreeIndex] = BEGIN OPEN node: ctreeb[cTreeNode]; IF node.index.kind = module THEN BEGIN -- use a representative one firstProto: CTreeIndex = FirstModulePrototype[cTreeNode]; WITH ctreeb[firstProto].index SELECT FROM fp: BcdTableLoc.module => ConditionallyOutputModulePrototype[fp.mti, userProc]; ENDCASE; END; FOR son: CTreeIndex ← node.firstSon, ctreeb[son].brother UNTIL son = nullCTreeIndex DO OutputConfigSubTree[son]; ENDLOOP; END; IF configTreeNode # nullCTreeIndex THEN { OutputConfigSubTree[configTreeNode ! DoneEnumerating => CONTINUE]; ResetModuleVector[]}; 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; -- reduces time needed to discover whether a prototype was previously output ModuleHVSize: CARDINAL = 71; ModuleHash: TYPE = [0..ModuleHVSize); moduleHashVec: LONG POINTER TO OutModuleMap ← NIL; OutModuleMap: TYPE = ARRAY ModuleHash OF OutputModuleRec; OutputModuleRec: TYPE = RECORD [ -- describes modules already output file: FTIndex, link: ModulePtr]; ModulePtr: TYPE = LONG POINTER TO OutputModuleRec; InitializeModuleVector: PROC = { moduleHashVec ← gd.zone.NEW[OutModuleMap ← ALL[[file: FTNull, link: NIL]]]}; ResetModuleVector: PROC = { p, first, next: ModulePtr; FOR i: ModuleHash IN ModuleHash DO first ← moduleHashVec[i].link; FOR p ← first, next UNTIL p = NIL DO next ← p.link; gd.zone.FREE[@p]; ENDLOOP; moduleHashVec[i] ← OutputModuleRec[file: FTNull, link: NIL]; ENDLOOP}; ReleaseModuleVector: PROC = { IF moduleHashVec # NIL THEN gd.zone.FREE[@moduleHashVec]}; NewOutputModuleRec: PROC [ file: FTIndex, link: ModulePtr] RETURNS [new: ModulePtr] = { new ← gd.zone.NEW[OutputModuleRec ← [file: file, link: link]]}; ConditionallyOutputModulePrototype: PROC [ mti: MTIndex, userProc: PROC [mti: MTIndex] RETURNS [stop: BOOL]] = { -- 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 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]}; IF userProc[mti] THEN SIGNAL DoneEnumerating}; -- ********** Locate a module or configuration instance/prototype ********** FindModuleOrConfig: PUBLIC PROC [ kind: ComponentKind, ResetIdStream: PROC, FirstQualId, NextQualId: PROC RETURNS [id: HashOps.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: HashOps.HTIndex]] RETURNS [component: CTreeIndex] = { start, t: CTreeIndex; mainPartOfId, nextId: HashOps.HTIndex; componentFullyQual, fullyQual, immediateMatch: BOOL; 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 # HashOps.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: HashOps.HTIndex]] RETURNS [component: CTreeIndex] = { start, t: CTreeIndex; mainPartOfId, nextId: HashOps.HTIndex; componentFullyQual, fullyQual, immediateMatch: BOOL; 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 # HashOps.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 [BOOL] = BEGIN -- ambiguous if not same FTIndex (name-stamp pair) RETURN [WITH ctreeb[comp1].index SELECT FROM c1: BcdTableLoc.config => WITH ctreeb[comp2].index SELECT FROM c2: BcdTableLoc.config => bcdBases.ctb[c1.cti].file # bcdBases.ctb[c2.cti].file, ENDCASE => TRUE, -- one is module, the other a config m1: BcdTableLoc.module => WITH ctreeb[comp2].index SELECT FROM m2: BcdTableLoc.module => bcdBases.mtb[m1.mti].file # bcdBases.mtb[m2.mti].file, ENDCASE => TRUE, -- one is module and the other a config ENDCASE => TRUE] END; -- ******** Determine if module prototype/instance is in a config ******** IsModuleInConfig: PUBLIC PROC [ kind: ComponentKind, mti: BcdDefs.MTIndex, configTreeNode: CTreeIndex] RETURNS [BOOL] = 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: HashOps.HTIndex, kind: ComponentKind] RETURNS [firstTreeLoc: CTreeIndex] = BEGIN -- find first node with given instance or prototype id idSS: SubString ← @idSSDesc; idSSDesc: SubStringDescriptor; HashOps.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 String.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 String.EqualSubStrings[idSS, treeSS] THEN RETURN[p]; p ← ctreeb[p].prototypeLink; ENDLOOP; END; RETURN[nullCTreeIndex]; END; END.