-- file SymbolPack.Mesa -- last modified by Satterthwaite, 9-Feb-82 9:56:48 DIRECTORY Inline: TYPE USING [BITAND, BITXOR], Literals: TYPE USING [Base], Strings: TYPE USING [String, SubString, SubStringDescriptor, EqualSubStrings], Symbols: TYPE, SymbolOps: TYPE USING [PackedSize], SymbolSegment: TYPE USING [Base, ExtIndex, ExtRecord, FGTEntry, STHeader], TimeStamp: TYPE USING [Stamp], Tree: TYPE USING [Base, Link, Null]; SymbolPack: PROGRAM IMPORTS Inline, Strings EXPORTS SymbolOps = PUBLIC { OPEN Symbols; SymbolTableBase: TYPE = POINTER TO FRAME[SymbolPack]; link: SymbolTableBase; cacheInfo: LONG POINTER; -- tables defining the current symbol table hashVec: LONG POINTER TO HashVector; -- hash index ht: LONG DESCRIPTOR FOR ARRAY HTIndex OF HTRecord; -- hash table ssb: Strings.String; -- id string seb: Symbols.Base; -- se table ctxb: Symbols.Base; -- context table mdb: Symbols.Base; -- module directory base bb: Symbols.Base; -- body table tb: Tree.Base; -- tree area ltb: Literals.Base; -- literal area extb: SymbolSegment.Base; -- extension map mdLimit: MDIndex; -- module directory size extLimit: SymbolSegment.ExtIndex; -- extension size mainCtx: CTXIndex; stHandle: LONG POINTER TO SymbolSegment.STHeader; -- info defining the source file links sourceFile: Strings.String; fgTable: LONG DESCRIPTOR FOR ARRAY OF SymbolSegment.FGTEntry; -- the following procedure is called if the base values change notifier: PROC [SymbolTableBase]; NullNotifier: PROC [SymbolTableBase] = { }; -- hash manipulation SubString: TYPE = Strings.SubString; FindString: PROC [s: SubString] RETURNS [hti: HTIndex] = { desc: Strings.SubStringDescriptor; ss: SubString = @desc; hti ← hashVec[HashValue[s]]; WHILE hti # HTNull DO SubStringForHash[ss, hti]; IF Strings.EqualSubStrings[s,ss] THEN EXIT; hti ← ht[hti].link; ENDLOOP; RETURN}; HashValue: PROC [s: SubString] RETURNS [HVIndex] = { CharBits: PROC [CHARACTER, WORD] RETURNS [WORD] = LOOPHOLE[Inline.BITAND]; Mask: WORD = 337b; -- masks out ASCII case shifts n: CARDINAL = s.length; b: Strings.String = s.base; v: WORD; v ← CharBits[b[s.offset], Mask]*177b + CharBits[b[s.offset+(n-1)], Mask]; RETURN [Inline.BITXOR[v, n*17b] MOD LENGTH[hashVec↑]]}; SubStringForHash: PROC [s: SubString, hti: HTIndex] = { s.base ← ssb; IF hti = HTNull THEN s.offset ← s.length ← 0 ELSE s.length ← ht[hti].ssIndex - (s.offset ← ht[hti-1].ssIndex)}; -- context management CtxEntries: PROC [ctx: CTXIndex] RETURNS [n: CARDINAL] = { n ← 0; IF ctx = CTXNull THEN RETURN; WITH c: ctxb[ctx] SELECT FROM included => IF ~c.reset THEN RETURN; ENDCASE; FOR sei: ISEIndex ← FirstCtxSe[ctx], NextSe[sei] UNTIL sei = SENull DO n ← n+1 ENDLOOP; RETURN}; FirstCtxSe: PROC [ctx: CTXIndex] RETURNS [ISEIndex] = { RETURN [IF ctx = CTXNull THEN ISENull ELSE ctxb[ctx].seList]}; NextSe: PROC [sei: ISEIndex] RETURNS [ISEIndex] = { RETURN [ IF sei = SENull THEN ISENull ELSE WITH id: seb[sei] SELECT FROM terminal => ISENull, sequential => sei + SIZE[sequential id SERecord], linked => id.link, ENDCASE => ISENull]}; SearchContext: PROC [hti: HTIndex, ctx: CTXIndex] RETURNS [ISEIndex] = { sei, root: ISEIndex; IF ctx # CTXNull AND hti # HTNull THEN { sei ← root ← ctxb[ctx].seList; DO IF sei = SENull THEN EXIT; IF seb[sei].hash = hti THEN RETURN [sei]; WITH id: seb[sei] SELECT FROM sequential => sei ← sei + SIZE[sequential id SERecord]; linked => IF (sei ← id.link) = root THEN EXIT; ENDCASE => EXIT; ENDLOOP}; RETURN [ISENull]}; SeiForValue: PROC [value: CARDINAL, ctx: CTXIndex] RETURNS [ISEIndex] = { FOR sei: ISEIndex ← FirstCtxSe[ctx], NextSe[sei] UNTIL sei = ISENull DO IF seb[sei].idValue = value THEN RETURN [sei] ENDLOOP; RETURN [ISENull]}; -- module management FindMdi: PROC [stamp: TimeStamp.Stamp] RETURNS [MDIndex] = { FOR mdi: MDIndex ← FIRST[MDIndex], mdi + SIZE[MDRecord] UNTIL mdi = mdLimit DO IF mdb[mdi].stamp = stamp THEN RETURN [mdi] ENDLOOP; RETURN [MDNull]}; -- type manipulation ArgCtx: PROC [type: CSEIndex] RETURNS [CTXIndex] = { sei: RecordSEIndex = ArgRecord[type]; RETURN [IF sei = RecordSENull THEN CTXNull ELSE seb[sei].fieldCtx]}; ArgRecord: PROC [type: CSEIndex] RETURNS [RecordSEIndex] = { RETURN [IF type = SENull THEN RecordSENull ELSE WITH seb[type] SELECT FROM record => LOOPHOLE[type, RecordSEIndex], ENDCASE => RecordSENull]}; ClusterSe: PROC [type: SEIndex] RETURNS [SEIndex] = { WITH t: seb[type] SELECT FROM id => { next: SEIndex = t.idInfo; RETURN [IF t.extended THEN type ELSE WITH u: seb[next] SELECT FROM id => IF t.hash = u.hash THEN ClusterSe[next] ELSE type, ENDCASE => type]}; ENDCASE => RETURN [type]}; NormalType: PROC [type: CSEIndex] RETURNS [nType: CSEIndex] = { RETURN [WITH t: seb[type] SELECT FROM subrange => NormalType[UnderType[t.rangeType]], long, real => NormalType[UnderType[t.rangeType]], ENDCASE => type]}; RecordLink: PROC [type: RecordSEIndex] RETURNS [RecordSEIndex] = { RETURN [WITH t: seb[type] SELECT FROM linked => LOOPHOLE[UnderType[t.linkType], RecordSEIndex], ENDCASE => RecordSENull]}; RecordRoot: PROC [type: RecordSEIndex] RETURNS [root: RecordSEIndex] = { next: RecordSEIndex; root ← type; WHILE (next ← RecordLink[root]) # SENull DO root ← next ENDLOOP; RETURN}; ReferentType: PROC [type: CSEIndex] RETURNS [CSEIndex] = { sei: CSEIndex = NormalType[type]; RETURN [WITH t: seb[sei] SELECT FROM ref => UnderType[t.refType], ENDCASE => typeANY]}; TransferTypes: PROC [type: SEIndex] RETURNS [typeIn, typeOut: RecordSEIndex] = { sei: CSEIndex = UnderType[type]; WITH t: seb[sei] SELECT FROM transfer => RETURN [typeIn: ArgRecord[t.typeIn], typeOut: ArgRecord[t.typeOut]]; ENDCASE; RETURN [RecordSENull, RecordSENull]}; TypeForm: PROC [type: SEIndex] RETURNS [TypeClass] = { RETURN [IF type = SENull THEN nil ELSE seb[UnderType[type]].typeTag]}; TypeLink: PROC [type: SEIndex] RETURNS [SEIndex] = { sei: CSEIndex = UnderType[type]; RETURN [WITH se: seb[sei] SELECT FROM record => WITH se SELECT FROM linked => linkType, ENDCASE => SENull, ENDCASE => SENull]}; TypeRoot: PROC [type: SEIndex] RETURNS [root: SEIndex] = { next: SEIndex; root ← type; WHILE (next ← TypeLink[root]) # SENull DO root ← next ENDLOOP; RETURN}; UnderType: PROC [type: SEIndex] RETURNS [CSEIndex] = { sei: SEIndex ← type; WHILE sei # SENull DO WITH se: seb[sei] SELECT FROM id => {IF se.idType # typeTYPE THEN ERROR; sei ← se.idInfo}; ENDCASE => EXIT; ENDLOOP; RETURN [LOOPHOLE[sei, CSEIndex]]}; XferMode: PROC [type: SEIndex] RETURNS [TransferMode] = { sei: CSEIndex = UnderType[type]; RETURN [WITH t: seb[sei] SELECT FROM transfer => t.mode, ENDCASE => none]}; -- information returning procedures WordFill: CARDINAL = WordLength-1; BytesPerWord: CARDINAL = WordLength/ByteLength; BitsForType: PROC [type: SEIndex] RETURNS [CARDINAL] = { n: CARDINAL; sei: CSEIndex = UnderType[type]; RETURN [IF sei = SENull THEN 0 ELSE WITH t: seb[sei] SELECT FROM basic => t.length, enumerated => BitsForRange[Cardinality[sei]-1], record => t.length, array => IF (n←BitsPerElement[t.componentType, t.packed]*Cardinality[t.indexType]) > WordLength THEN ((n + (WordLength-1))/WordLength)*WordLength ELSE n, opaque => t.length, subrange => IF t.empty THEN 0 ELSE BitsForRange[Cardinality[sei]-1], ENDCASE => WordsForType[sei]*WordLength]}; BitsForRange: PROC [maxValue: CARDINAL] RETURNS [nBits: CARDINAL] = { fieldMax: CARDINAL; nBits ← 1; fieldMax ← 1; WHILE nBits < WordLength AND fieldMax < maxValue DO nBits ← nBits + 1; fieldMax ← 2*fieldMax + 1 ENDLOOP; RETURN}; BitsPerElement: PROC [type: SEIndex, packed: BOOLEAN] RETURNS [CARDINAL] = { nBits: CARDINAL = BitsForType[type]; RETURN [IF packed AND nBits <= ByteLength THEN SymbolOps.PackedSize[nBits] ELSE (nBits+WordFill)/WordLength * WordLength]}; Cardinality: PROC [type: SEIndex] RETURNS [CARDINAL] = { sei: CSEIndex = UnderType[type]; RETURN [WITH t: seb[sei] SELECT FROM enumerated => t.nValues, subrange => IF t.empty THEN 0 ELSE t.range+1, basic => IF t.code = codeCHAR THEN 256 ELSE 0, relative => Cardinality[t.offsetType], ENDCASE => 0]}; FindExtension: PROC [sei: ISEIndex] RETURNS [type: ExtensionType, tree: Tree.Link] = { OPEN SymbolSegment; FOR exti: ExtIndex ← FIRST[ExtIndex], exti + SIZE[ExtRecord] UNTIL exti = extLimit DO IF extb[exti].sei = sei THEN RETURN [extb[exti].type, extb[exti].tree]; ENDLOOP; RETURN [none, Tree.Null]}; FnField: PROC [field: ISEIndex] RETURNS [offset: BitAddress, size: CARDINAL] = { word, nW: CARDINAL; word ← 0; FOR sei: ISEIndex ← FirstCtxSe[seb[field].idCtx], NextSe[sei] DO nW ← WordsForType[seb[sei].idType]; IF sei = field THEN EXIT; word ← word + nW; ENDLOOP; RETURN [offset: BitAddress[wd:word, bd:0], size: nW * WordLength]}; HashForSe: PROC [sei: ISEIndex] RETURNS [HTIndex] = { RETURN [IF sei = ISENull THEN HTNull ELSE seb[sei].hash]}; LinkMode: PROC [sei: ISEIndex] RETURNS [Linkage] = { RETURN [IF seb[sei].idType = typeTYPE THEN (IF TypeForm[seb[sei].idInfo] = opaque THEN type ELSE manifest) ELSE SELECT XferMode[seb[sei].idType] FROM proc, program => IF seb[sei].constant THEN (IF seb[sei].extended THEN val ELSE manifest) ELSE val, signal, error => IF seb[sei].constant THEN manifest ELSE val, ENDCASE => IF seb[sei].constant THEN manifest ELSE ref]}; RCType: PROC [type: CSEIndex] RETURNS [RefClass] = { next: SEIndex; struc: RefClass ← simple; FOR sei: CSEIndex ← type, UnderType[next] DO WITH t: seb[sei] SELECT FROM record => SELECT TRUE FROM ~t.hints.refField => RETURN [none]; t.hints.unifield => next ← seb[ctxb[t.fieldCtx].seList].idType; ENDCASE => RETURN [composite]; ref => RETURN [IF t.counted THEN struc ELSE none]; array => {struc ← composite; next ← t.componentType}; relative => next ← t.offsetType; subrange => next ← t.rangeType; long => next ← t.rangeType; union => RETURN [IF t.hints.refField THEN composite ELSE none]; sequence => {struc ← composite; next ← t.componentType}; zone => RETURN [IF t.counted THEN struc ELSE none]; ENDCASE => RETURN [none]; ENDLOOP}; VariantField: PROC [type: CSEIndex] RETURNS [sei: ISEIndex] = { WITH t: seb[type] SELECT FROM record => FOR sei ← FirstCtxSe[t.fieldCtx], NextSe[sei] UNTIL sei = ISENull DO SELECT TypeForm[seb[sei].idType] FROM sequence, union => EXIT; ENDCASE; ENDLOOP; ENDCASE => sei ← ISENull; RETURN}; WordsForType: PROC [type: SEIndex] RETURNS [CARDINAL] = { sei: CSEIndex = UnderType[type]; b: CARDINAL; RETURN [IF sei = SENull THEN 0 ELSE WITH t: seb[sei] SELECT FROM mode => 1, -- fudge for compiler (Pass4:Binding) basic => (t.length + WordFill)/WordLength, enumerated => 1, record => (t.length + WordFill)/WordLength, ref => 1, array => IF (b←BitsPerElement[t.componentType, t.packed]) < WordLength THEN (Cardinality[t.indexType]+(WordLength/b-1))/(WordLength/b) ELSE Cardinality[t.indexType] * ((b+WordFill)/WordLength), arraydesc => 2, transfer => IF t.mode = port THEN 2 ELSE 1, relative => WordsForType[t.offsetType], opaque => (t.length + WordFill)/WordLength, zone => (IF t.mds THEN 1 ELSE 2), subrange => IF t.empty THEN 0 ELSE 1, long => WordsForType[t.rangeType] + 1, real => 2, ENDCASE => 0]}; -- body table management ParentBti: PROC [bti: BTIndex] RETURNS [BTIndex] = { UNTIL bb[bti].link.which = parent DO bti ← bb[bti].link.index ENDLOOP; RETURN [bb[bti].link.index]}; SiblingBti: PROC [bti: BTIndex] RETURNS [BTIndex] = { RETURN [IF bb[bti].link.which = sibling THEN bb[bti].link.index ELSE BTNull]}; SonBti: PROC [bti: BTIndex] RETURNS [BTIndex] = {RETURN [bb[bti].firstSon]}; EnumerateBodies: PROC [root: BTIndex, proc: PROC [BTIndex] RETURNS [stop: BOOLEAN]] RETURNS [bti: BTIndex] = { prev: BTIndex; bti ← root; UNTIL bti = BTNull DO IF proc[bti] THEN GO TO Stopped; IF bb[bti].firstSon # BTNull THEN bti ← bb[bti].firstSon ELSE DO IF bti = root THEN GO TO Done; prev ← bti; bti ← bb[bti].link.index; IF bb[prev].link.which # parent THEN EXIT; ENDLOOP; REPEAT Stopped => NULL; Done => bti ← BTNull; ENDLOOP; RETURN}; }.