DIRECTORY ConvertUnsafe USING [LS, SubString, EqualSubStrings], PrincOpsUtils USING [BITAND, BITXOR], FS, SymbolOperations, Symbols, SymbolSegment, TimeStamp USING [Stamp], Tree USING [Link, Null], VM; SymbolOperationsImpl: MONITOR IMPORTS PrincOpsUtils, ConvertUnsafe, FS, VM EXPORTS SymbolOperations = BEGIN Base: TYPE = Symbols.Base; BitAddress: TYPE = Symbols.BitAddress; BitCount: TYPE = Symbols.BitCount; BTIndex: TYPE = Symbols.BTIndex; BTNull: BTIndex = Symbols.BTNull; CSEIndex: TYPE = Symbols.CSEIndex; CSENull: CSEIndex = Symbols.CSENull; typeANY: CSEIndex = Symbols.typeANY; typeTYPE: CSEIndex = Symbols.typeTYPE; CTXIndex: TYPE = Symbols.CTXIndex; CTXNull: CTXIndex = Symbols.CTXNull; ExtIndex: TYPE = SymbolSegment.ExtIndex; ExtNull: ExtIndex = SymbolSegment.ExtNull; ExtensionType: TYPE = Symbols.ExtensionType; FGHeader: TYPE = SymbolSegment.FGHeader; FGHeaderPtr: TYPE = LONG POINTER TO FGHeader; FGTEntry: TYPE = SymbolSegment.FGTEntry; FieldBitCount: TYPE = Symbols.FieldBitCount; ISEIndex: TYPE = Symbols.ISEIndex; ISENull: ISEIndex = Symbols.ISENull; HashVector: TYPE = Symbols.HashVector; HTRecord: TYPE = Symbols.HTRecord; HVIndex: TYPE = Symbols.HVIndex; Linkage: TYPE = Symbols.Linkage; LongString: TYPE = ConvertUnsafe.LS; MDIndex: TYPE = Symbols.MDIndex; MDNull: MDIndex = Symbols.MDNull; MDRecord: TYPE = Symbols.MDRecord; Name: TYPE = Symbols.Name; nullName: Name = Symbols.nullName; PackedBitCount: TYPE = Symbols.PackedBitCount; RecordSEIndex: TYPE = Symbols.RecordSEIndex; RecordSENull: RecordSEIndex = Symbols.RecordSENull; RefClass: TYPE = Symbols.RefClass; SEIndex: TYPE = Symbols.SEIndex; SENull: SEIndex = Symbols.SENull; SERecord: TYPE = Symbols.SERecord; STHeader: TYPE = SymbolSegment.STHeader; STHeaderPtr: TYPE = LONG POINTER TO STHeader; SubString: TYPE = ConvertUnsafe.SubString; TransferMode: TYPE = Symbols.TransferMode; Type: TYPE = Symbols.Type; nullType: Type = Symbols.nullType; TypeClass: TYPE = Symbols.TypeClass; WordCount: TYPE = Symbols.WordCount; WordLength: NAT = Symbols.WordLength; wordsPerPage: NAT = VM.wordsPerPage; wordFill: CARDINAL = WordLength-1; SymbolTableBase: TYPE = SymbolOperations.SymbolTableBase; SymbolTableBaseRep: TYPE = SymbolOperations.SymbolTableBaseRep; Acquire: PUBLIC PROC [file: FS.OpenFile, startPage: CARDINAL, pages: CARDINAL] RETURNS [stb: SymbolTableBase _ NIL] = { interval: VM.Interval = VM.Allocate[count: pages]; b: LONG POINTER = VM.AddressForPageNumber[interval.page]; tB: SymbolSegment.Base = LOOPHOLE[b]; p: STHeaderPtr = b; q: FGHeaderPtr; FS.Read[file, startPage, pages, b]; -- read the contents VM.MakeReadOnly[interval]; -- don't want anyone messing with our tables stb _ NEW[SymbolTableBaseRep]; stb.file _ file; stb.interval _ interval; stb.hashVec _ b+p.hvBlock.offset; stb.ht _ DESCRIPTOR[b+p.htBlock.offset, p.htBlock.size/SIZE[Symbols.HTRecord]]; stb.ssb _ b + p.ssBlock.offset; stb.seb _ tB + p.seBlock.offset; stb.ctxb _ tB + p.ctxBlock.offset; stb.mdb _ tB + p.mdBlock.offset; stb.bb _ tB + p.bodyBlock.offset; stb.tb _ tB + p.treeBlock.offset; stb.ltb _ tB + p.litBlock.offset; stb.extb _ tB + p.extBlock.offset; stb.mdLimit _ FIRST[Symbols.MDIndex] + p.mdBlock.size; stb.extLimit _ FIRST[SymbolSegment.ExtIndex] + p.extBlock.size; stb.mainCtx _ p.outerCtx; stb.stHandle _ p; IF p.fgRelPgBase = 0 OR pages <= p.fgRelPgBase THEN { stb.sourceFile _ NIL; stb.fgTable _ NIL; } ELSE { q _ b + p.fgRelPgBase*wordsPerPage; stb.sourceFile _ LOOPHOLE[@q.sourceFile]; stb.fgTable _ DESCRIPTOR[ LOOPHOLE[stb.sourceFile, LONG POINTER TO ARRAY OF FGTEntry], q.length]; }; }; Release: PUBLIC PROC [stb: SymbolTableBase] = { interval: VM.Interval = stb.interval; IF interval # VM.nullInterval THEN { VM.Free[interval]; stb^ _ []; -- ground state }; }; FindString: PUBLIC PROC [stb: SymbolTableBase, s: SubString] RETURNS [name: Name] = { ss: SubString; name _ stb.hashVec[HashValue[stb, s]]; WHILE name # nullName DO ss _ SubStringForName[stb, name]; IF ConvertUnsafe.EqualSubStrings[s, ss] THEN EXIT; name _ stb.ht[name].link; ENDLOOP; }; HashValue: PUBLIC PROC [stb: SymbolTableBase, s: SubString] RETURNS [HVIndex] = { CharBits: PROC [CHAR, WORD] RETURNS [WORD] = LOOPHOLE[PrincOpsUtils.BITAND]; Mask: WORD = 337b; -- masks out ASCII case shifts v: WORD = CharBits[s.base[s.offset], Mask]*177b + CharBits[s.base[s.offset+(s.length-1)], Mask]; RETURN [PrincOpsUtils.BITXOR[v, s.length*17b] MOD stb.hashVec^.LENGTH]; }; SubStringForName: PUBLIC PROC [stb: SymbolTableBase, name: Name] RETURNS[s: SubString] = { s.base _ stb.ssb; IF name = nullName THEN s.offset _ s.length _ 0 ELSE s.length _ stb.ht[name].ssIndex - (s.offset _ stb.ht[name-1].ssIndex); }; CtxEntries: PUBLIC PROC [stb: SymbolTableBase, ctx: CTXIndex] RETURNS [n: CARDINAL_0] = { IF ctx = CTXNull THEN RETURN; WITH c: stb.ctxb[ctx] SELECT FROM included => IF ~c.reset THEN RETURN; ENDCASE; FOR sei: ISEIndex _ FirstCtxSe[stb, ctx], NextSe[stb, sei] UNTIL sei = ISENull DO n _ n+1; ENDLOOP; }; FirstCtxSe: PUBLIC PROC [stb: SymbolTableBase, ctx: CTXIndex] RETURNS [ISEIndex] = { RETURN [IF ctx = CTXNull THEN ISENull ELSE stb.ctxb[ctx].seList]; }; NextSe: PUBLIC PROC [stb: SymbolTableBase, sei: ISEIndex] RETURNS [ISEIndex _ ISENull] = { IF sei # ISENull THEN { WITH id: stb.seb[sei] SELECT FROM sequential => RETURN[sei + SERecord.id.sequential.SIZE]; linked => RETURN[id.link]; ENDCASE; }; }; SearchContext: PUBLIC PROC [stb: SymbolTableBase, name: Name, ctx: CTXIndex] RETURNS [ISEIndex _ ISENull] = { sei, root: ISEIndex; IF ctx # CTXNull AND name # nullName THEN { sei _ root _ stb.ctxb[ctx].seList; DO IF sei = ISENull THEN EXIT; IF stb.seb[sei].hash = name THEN RETURN [sei]; WITH id: stb.seb[sei] SELECT FROM sequential => sei _ sei + SERecord.id.sequential.SIZE; linked => IF (sei _ id.link) = root THEN EXIT; ENDCASE => EXIT; ENDLOOP; }; }; SeiForValue: PUBLIC PROC [stb: SymbolTableBase, value: CARDINAL, ctx: CTXIndex] RETURNS [ISEIndex _ ISENull] = { FOR sei: ISEIndex _ FirstCtxSe[stb, ctx], NextSe[stb, sei] UNTIL sei = ISENull DO IF stb.seb[sei].idValue = value THEN RETURN [sei] ENDLOOP; }; FindMdi: PUBLIC PROC [stb: SymbolTableBase, stamp: TimeStamp.Stamp] RETURNS [MDIndex _ MDNull] = { FOR mdi: MDIndex _ MDIndex.FIRST, mdi + MDRecord.SIZE UNTIL mdi = stb.mdLimit DO IF stb.mdb[mdi].stamp = stamp THEN RETURN [mdi] ENDLOOP; }; ArgCtx: PUBLIC PROC [stb: SymbolTableBase, type: CSEIndex] RETURNS [CTXIndex] = { sei: RecordSEIndex = ArgRecord[stb, type]; RETURN [IF sei = RecordSENull THEN CTXNull ELSE stb.seb[sei].fieldCtx]; }; ArgRecord: PUBLIC PROC [stb: SymbolTableBase, type: CSEIndex] RETURNS [RecordSEIndex _ RecordSENull] = { IF type # nullType THEN { WITH stb.seb[type] SELECT FROM record => RETURN [LOOPHOLE[type, RecordSEIndex]]; ENDCASE; }; }; ClusterSe: PUBLIC PROC [stb: SymbolTableBase, type: Type] RETURNS [Type] = { WITH t: stb.seb[type] SELECT FROM id => { next: Type = t.idInfo; IF NOT t.extended THEN WITH u: stb.seb[next] SELECT FROM id => IF t.hash = u.hash THEN RETURN[ClusterSe[stb, next]]; ENDCASE; }; ENDCASE; RETURN [type]; }; NormalType: PUBLIC PROC [stb: SymbolTableBase, type: CSEIndex] RETURNS [nType: CSEIndex] = { WITH t: stb.seb[type] SELECT FROM subrange => nType _ NormalType[stb, UnderType[stb, t.rangeType]]; long, real => nType _ NormalType[stb, UnderType[stb, t.rangeType]]; ENDCASE => nType _ type; }; RecordLink: PUBLIC PROC [stb: SymbolTableBase, type: RecordSEIndex] RETURNS [RecordSEIndex _ RecordSENull] = { WITH t: stb.seb[type] SELECT FROM linked => RETURN [LOOPHOLE[UnderType[stb, t.linkType], RecordSEIndex]]; ENDCASE; }; RecordRoot: PUBLIC PROC [stb: SymbolTableBase, type: RecordSEIndex] RETURNS [root: RecordSEIndex] = { root _ type; FOR next: RecordSEIndex _ RecordLink[stb, root], RecordLink[stb, next] WHILE next # RecordSENull DO root _ next ENDLOOP; }; ReferentType: PUBLIC PROC [stb: SymbolTableBase, type: CSEIndex] RETURNS [CSEIndex _ typeANY] = { sei: CSEIndex = NormalType[stb, type]; WITH t: stb.seb[sei] SELECT FROM ref => RETURN[UnderType[stb, t.refType]]; ENDCASE; }; TransferTypes: PUBLIC PROC [stb: SymbolTableBase, type: Type] RETURNS [typeIn, typeOut: RecordSEIndex _ RecordSENull] = { sei: CSEIndex = UnderType[stb, type]; WITH t: stb.seb[sei] SELECT FROM transfer => { typeIn _ ArgRecord[stb, t.typeIn]; typeOut _ ArgRecord[stb, t.typeOut]; }; ENDCASE; }; TypeForm: PUBLIC PROC [stb: SymbolTableBase, type: Type] RETURNS [TypeClass _ nil] = { IF type # nullType THEN RETURN[stb.seb[UnderType[stb, type]].typeTag]; }; TypeLink: PUBLIC PROC [stb: SymbolTableBase, type: Type] RETURNS [Type _ nullType] = { sei: CSEIndex = UnderType[stb, type]; WITH se: stb.seb[sei] SELECT FROM record => WITH se SELECT FROM linked => RETURN [linkType]; ENDCASE; ENDCASE; }; TypeRoot: PUBLIC PROC [stb: SymbolTableBase, type: Type] RETURNS [root: Type] = { root _ type; FOR next: Type _ TypeLink[stb, root], TypeLink[stb, next] WHILE next # nullType DO root _ next; ENDLOOP; }; UnderType: PUBLIC PROC [stb: SymbolTableBase, type: Type] RETURNS [CSEIndex] = { sei: Type _ type; WHILE sei # nullType DO WITH se: stb.seb[sei] SELECT FROM id => {IF se.idType # typeTYPE THEN ERROR; sei _ se.idInfo}; ENDCASE => EXIT; ENDLOOP; RETURN [LOOPHOLE[sei, CSEIndex]]; }; XferMode: PUBLIC PROC [stb: SymbolTableBase, type: Type] RETURNS [TransferMode _ none] = { sei: CSEIndex = UnderType[stb, type]; WITH t: stb.seb[sei] SELECT FROM transfer => RETURN [t.mode]; ENDCASE; }; Untruncate: PRIVATE PROC [n: CARDINAL] RETURNS [LONG CARDINAL] = { IF n # 0 THEN RETURN [n]; RETURN [CARDINAL.LAST.LONG+1]; }; BitsForRange: PUBLIC PROC [stb: SymbolTableBase, maxValue: CARDINAL] RETURNS [nBits: CARDINAL _ 1] = { fieldMax: CARDINAL _ 1; WHILE nBits < WordLength AND fieldMax < maxValue DO nBits _ nBits + 1; fieldMax _ 2*fieldMax + 1; ENDLOOP; }; BitsForType: PUBLIC PROC [stb: SymbolTableBase, type: Type] RETURNS [BitCount _ 0] = { sei: CSEIndex = UnderType[stb, type]; IF sei # CSENull THEN { n: BitCount _ 0; WITH t: stb.seb[sei] SELECT FROM basic => n _ t.length; enumerated => IF NOT t.empty THEN RETURN[BitsForRange[stb, Cardinality[stb, sei]-1]]; record => n _ t.length; array => { n _ BitsPerElement[stb, t.componentType, t.packed]*Cardinality[stb, t.indexType]; IF n > WordLength THEN n _ ((n + wordFill)/WordLength)*WordLength; }; opaque => n _ t.length; relative => n _ BitsForType[stb, t.offsetType]; subrange => IF NOT t.empty THEN n _ BitsForRange[stb, Cardinality[stb, sei]-1]; ENDCASE => n _ WordsForType[stb, sei]*WordLength; RETURN [n]; }; }; PackedSize: ARRAY PackedBitCount OF CARDINAL = [1, 2, 4, 4, 8, 8, 8, 8]; BitsPerElement: PUBLIC PROC [stb: SymbolTableBase, type: Type, packed: BOOL] RETURNS [BitCount] = { nBits: BitCount = BitsForType[stb, type]; RETURN [ IF packed AND (nBits#0 AND nBits<=PackedBitCount.LAST) -- IN PackedBitCount THEN PackedSize[PackedBitCount[nBits]] ELSE (nBits+wordFill)/WordLength * WordLength ]; }; Cardinality: PUBLIC PROC [stb: SymbolTableBase, type: Type] RETURNS [LONG CARDINAL _ 0] = { sei: CSEIndex = UnderType[stb, type]; WITH t: stb.seb[sei] SELECT FROM enumerated => IF NOT t.empty THEN RETURN[Untruncate[t.nValues]]; subrange => IF NOT t.empty THEN RETURN[t.range.LONG+1]; basic => IF t.code = Symbols.codeCHAR THEN RETURN[256]; relative => RETURN[Cardinality[stb, t.offsetType]]; ENDCASE; }; FindExtension: PUBLIC PROC [stb: SymbolTableBase, sei: ISEIndex] RETURNS [type: ExtensionType, tree: Tree.Link] = { OPEN SymbolSegment; FOR exti: SymbolSegment.ExtIndex _ SymbolSegment.ExtIndex.FIRST, exti + SymbolSegment.ExtRecord.SIZE UNTIL exti = stb.extLimit DO IF stb.extb[exti].sei = sei THEN RETURN [stb.extb[exti].type, stb.extb[exti].tree]; ENDLOOP; RETURN [none, Tree.Null]; }; FnField: PUBLIC PROC [stb: SymbolTableBase, field: ISEIndex] RETURNS [offset: BitAddress, size: FieldBitCount] = { word, nW: CARDINAL; word _ 0; FOR sei: ISEIndex _ FirstCtxSe[stb, stb.seb[field].idCtx], NextSe[stb, sei] DO nW _ CARDINAL[WordsForType[stb, stb.seb[sei].idType]]; IF sei = field THEN EXIT; word _ word + nW; ENDLOOP; RETURN [offset: BitAddress[wd: word, bd: 0], size: nW * WordLength]; }; NameForSe: PUBLIC PROC [stb: SymbolTableBase, sei: ISEIndex] RETURNS [Name _ nullName] = { IF sei # ISENull THEN RETURN[stb.seb[sei].hash]; }; LinkMode: PUBLIC PROC [stb: SymbolTableBase, sei: ISEIndex] RETURNS [Linkage] = { IF stb.seb[sei].idType = typeTYPE THEN IF TypeForm[stb, stb.seb[sei].idInfo] = opaque THEN RETURN[type] ELSE RETURN[manifest] ELSE SELECT XferMode[stb, stb.seb[sei].idType] FROM proc, program => IF stb.seb[sei].constant AND NOT stb.seb[sei].extended THEN RETURN[manifest] ELSE RETURN[val]; signal, error => IF stb.seb[sei].constant THEN RETURN[manifest] ELSE RETURN[val]; ENDCASE => IF stb.seb[sei].constant THEN RETURN[manifest] ELSE RETURN[ref]; }; RecField: PUBLIC PROC [stb: SymbolTableBase, field: ISEIndex] RETURNS [offset: BitAddress, size: FieldBitCount] = { RETURN [offset: stb.seb[field].idValue, size: stb.seb[field].idInfo]; }; RCType: PUBLIC PROC [stb: SymbolTableBase, type: CSEIndex] RETURNS [RefClass] = { next: Type; struc: RefClass _ simple; FOR sei: CSEIndex _ type, UnderType[stb, next] DO WITH t: stb.seb[sei] SELECT FROM record => SELECT TRUE FROM ~t.hints.refField => RETURN [none]; t.hints.unifield => next _ stb.seb[stb.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: PUBLIC PROC [stb: SymbolTableBase, type: CSEIndex] RETURNS [sei: ISEIndex _ ISENull] = { WITH t: stb.seb[type] SELECT FROM record => FOR sei _ FirstCtxSe[stb, t.fieldCtx], NextSe[stb, sei] UNTIL sei = ISENull DO SELECT TypeForm[stb, stb.seb[sei].idType] FROM sequence, union => EXIT; ENDCASE; ENDLOOP; ENDCASE; }; WordsForType: PUBLIC PROC [stb: SymbolTableBase, type: Type] RETURNS [wc: WordCount _ 0] = { sei: CSEIndex = UnderType[stb, type]; IF sei # CSENull THEN { itemsPerWord: ARRAY PackedBitCount OF [0..16] = [16, 8, 4, 4, 2, 2, 2, 2]; WITH t: stb.seb[sei] SELECT FROM mode => wc _ 1; -- fudge for compiler (Pass4.Binding) basic => wc _ (t.length + wordFill)/WordLength; enumerated => IF NOT t.empty THEN wc _ 1; record => wc _ (t.length.LONG + wordFill)/WordLength; ref => wc _ 1; array => { cc: WordCount = Cardinality[stb, t.indexType]; b: BitCount = BitsPerElement[stb, t.componentType, t.packed]; IF b # 0 AND b <= PackedBitCount.LAST THEN wc _ (cc + (itemsPerWord[b]-1))/itemsPerWord[b] ELSE wc _ cc * ((b+wordFill)/WordLength); }; arraydesc => wc _ 2; transfer => wc _ IF t.mode = port THEN 2 ELSE 1; relative => wc _ WordsForType[stb, t.offsetType]; opaque => wc _ (t.length.LONG + wordFill)/WordLength; zone => IF t.mds THEN wc _ 1 ELSE wc _ 2; subrange => IF NOT t.empty THEN wc _ 1; long => wc _ WordsForType[stb, t.rangeType] + 1; real => wc _ 2; ENDCASE; }; }; ParentBti: PUBLIC PROC [stb: SymbolTableBase, bti: BTIndex] RETURNS [BTIndex] = { UNTIL stb.bb[bti].link.which = parent DO bti _ stb.bb[bti].link.index; ENDLOOP; RETURN [stb.bb[bti].link.index]; }; SiblingBti: PUBLIC PROC [stb: SymbolTableBase, bti: BTIndex] RETURNS [BTIndex] = { RETURN [IF stb.bb[bti].link.which = sibling THEN stb.bb[bti].link.index ELSE BTNull]; }; SonBti: PUBLIC PROC [stb: SymbolTableBase, bti: BTIndex] RETURNS [BTIndex] = { RETURN [stb.bb[bti].firstSon]; }; EnumerateBodies: PUBLIC PROC [stb: SymbolTableBase, root: BTIndex, proc: PROC [BTIndex] RETURNS [stop: BOOL]] RETURNS [bti: BTIndex] = { prev: BTIndex; bti _ root; UNTIL bti = BTNull DO IF proc[bti] THEN GO TO Stopped; IF stb.bb[bti].firstSon # BTNull THEN bti _ stb.bb[bti].firstSon ELSE DO IF bti = root THEN GO TO Done; prev _ bti; bti _ stb.bb[bti].link.index; IF stb.bb[prev].link.which # parent THEN EXIT; ENDLOOP; REPEAT Stopped => NULL; Done => bti _ BTNull; ENDLOOP; }; END. DSymbolOperationsImpl.mesa Russ Atkinson, October 19, 1983 10:24 am This file has been shamelessly ripped off from SymbolPack.mesa of September 22, 1983 10:58 am. It is meant as an eventual replacement, which would permit symbol tables to exist without the use of global frames or excessive MDS (hooray!). There is NO provision for table movement! No fine-grain table There is a fine-grain table hash manipulation context management module management type manipulation information returning procedures compatibility hack b IN PackedBitCount body table management Κ¨˜šœ™J™(—J˜Jšœ™™™J˜šΟk ˜ Jšœœœ˜5Jšœœœœ˜%Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ œ ˜Jšœœ˜Jšœ˜—J˜šœ˜Jšœœ˜,Jšœ˜Jšœ˜—˜Jšœœ˜Jšœ œ˜&Jšœ œ˜"šœ œ˜ Jšœ!˜!—šœ œ˜"Jšœ$˜$Jšœ$˜$Jšœ&˜&—šœ œ˜"Jšœ$˜$—šœ œ˜(Jšœ*˜*—Jšœœ˜,Jšœ œ˜(Jš œ œœœœ ˜-Jšœ œ˜(Jšœœ˜,šœ œ˜"Jšœ$˜$—Jšœ œ˜&Jšœ œ˜"Jšœ œ˜ Jšœ œ˜ Jšœ œœ˜$šœ œ˜ Jšœ!˜!—Jšœ œ˜"šœœ˜Jšœ"˜"—Jšœœ˜.šœœ˜,Jšœ3˜3—Jšœ œ˜"šœ œ˜ Jšœ!˜!—Jšœ œ˜"Jšœ œ˜(Jš œ œœœœ ˜-Jšœ œ˜*Jšœœ˜*šœœ˜Jšœ"˜"—Jšœ œ˜$Jšœ œ˜$J˜Jšœ œ˜%Jšœœœ˜$Jšœ œ˜"J˜Jšœœ$˜9•PostFix32 sp tabStops•postfix32 sp tabStops•postFix32 sp tabStopsšœœ'˜?J–32 sp tabStops–32 sp tabStops–32 sp tabStops˜—šΟnœ ˜Jšœœœ œ˜9Jšœœ˜(Jšœ œ œ˜2Jšœœœœ%˜9Jšœœ˜%Jšœ˜Jšœ˜Jšœ#Οc˜9JšœŸ,˜HJšœœ˜Jšœ˜J˜J˜!Jšœ  œ$œ˜OJ˜J˜ J˜"J˜ J˜!J˜!J˜!J˜"Jšœœ#˜6Jšœœ+˜?J˜J˜šœœ˜.šœ˜Jšœ™Jšœœ˜Jšœœ˜Jšœ˜—šœ˜Jšœ™J˜#Jšœœ˜)šœ œ˜Jš œœœœœœ ˜˜DJšœ˜J˜—šž œ ˜Jšœ&œ˜CJšœœœ˜0Jšœ˜J˜—šžœ œ'œ˜Qšœ˜!šœœ,˜3Jšœœ˜Jšœœ ˜—š˜šœ$˜.˜šœœœ˜6Jšœœ ˜Jšœœ˜——šœ˜Jš œœœ œœ˜@—šœ˜ Jš œœœ œœ˜@————Jšœ˜J˜—šžœ ˜Jšœ'˜'Jšœ.˜5Jšœ?˜EJšœ˜J˜—šžœ œ(œ˜QJ˜ J˜šœ,˜1šœœ˜ J˜ šœœ˜Jšœœ˜#JšœG˜GJšœœ ˜—Jš œœœ œœ˜2J˜5J˜ J˜J˜Jš œ œœœ œ˜?J˜8Jš œœœ œœ˜3Jšœœ˜—Jš˜—Jšœ˜J˜—šž œ ˜Jšœ'œ˜Lšœœ˜!˜ šœ5œ˜Nšœ$˜.Jšœœ˜Jšœ˜—Jšœ˜——Jšœ˜—Jšœ˜J˜—šž œ ˜Jšœ#œ˜BJšœ%˜%šœœ˜Jšœœœ%˜Jšœœ˜ JšœŸ%˜5Jšœ/˜/Jšœœœ œ˜)Jšœœ˜5Jšœ˜˜ Jšœ.˜.Jšœ=˜=šœœ˜%Jšœ™Jšœ0˜4Jšœ%˜)—J˜—Jšœ˜Jšœœœœ˜0Jšœ1˜1Jšœœ˜5Jšœœœœ˜)Jšœ œœ œ˜'Jšœ0˜0Jšœ˜Jšœ˜—J˜—Jšœ˜——J™šœ™šž œ œ&œ˜Qšœ!˜(Jšœ˜Jšœ˜—Jšœ˜ Jšœ˜J˜—šž œ œ&œ˜RJšœœ"œœ ˜UJšœ˜J˜—šžœ œ&œ˜NJšœ˜Jšœ˜J˜—šžœ ˜Jšœ,œ œœ˜PJšœ˜J˜J˜ šœ˜Jšœ œœœ ˜ šœœ˜@Jš˜š˜Jšœ œœœ˜J˜ J˜Jšœ"œœ˜.Jšœ˜——š˜Jšœ œ˜J˜—Jšœ˜—Jšœ˜———J˜Jšœ˜J˜—…—>ώVκ