DIRECTORY JaMBasic USING [Object, Tuple], JaMDict USING [DD, freeHash, Hash, Loc, locNIL, Slot, slotNIL], JaMInternal USING [Cache, CacheRecord, Frame, HashSequence, TupleSequence], JaMOps USING [AGet, APut, Assert, Bug, Compare, Empty, Equal, Error, ForEachFrame, ForEachFrameExcept, Full, Install, InstallReason, KeyName, limitchk, MakeName, MarkLoop, nullOb, Pop, PopCardinal, PopDict, Push, PushBoolean, PushCardinal, PushInteger, RegisterExplicit, RegisterInternal, StackForAll, TopDict, typechk, UnmarkLoop], JaMStorage USING [Zone], JaMVM USING [AllocArray, AllocDict, GetDict, GetTuple, PutDict, PutTuple]; JaMDictImpl: PROGRAM IMPORTS JaMDict, JaMOps, JaMStorage, JaMVM EXPORTS JaMDict, JaMOps = { OPEN VM:JaMVM, JaMOps, JaMDict, JaMInternal, JaMBasic; nullTuple: Tuple = [nullOb,nullOb]; nullDict: dict Object = [L,dict[NIL]]; maxlenLimit: CARDINAL = 20000; -- maximum maxlen for a dictionary maxattLimit: CARDINAL = 1000; -- maximum number of attached dictionaries initCacheSize: CARDINAL = 67; zone: UNCOUNTED ZONE = JaMStorage.Zone[]; undefkey,dictfull,attachmentcycle,notattached,dictundflw,dictovrflw: name Object; dfacmd,afacmd: command Object; AGetDict: PROC[array: array Object, i: CARDINAL] RETURNS[dict Object] = { ob: Object _ AGet[array,i]; WITH ob:ob SELECT FROM dict => RETURN[ob]; ENDCASE => ERROR Bug; }; LookUpDict: PUBLIC PROC[dd: POINTER TO READONLY DD, hash: CARDINAL, key: Object] RETURNS[BOOLEAN,Slot] = { slot: Slot _ dd.beg + LONG[hash MOD dd.size]*SIZE[Tuple]; IF key.type = null THEN ERROR Error[typechk]; -- better not allow this for a key THROUGH[0..dd.size) DO tuple: Tuple _ VM.GetTuple[slot]; IF Equal[tuple.key,key] THEN RETURN[TRUE,slot]; -- found a match IF tuple.key.type = null THEN -- found an empty slot RETURN[FALSE,IF dd.curlen ERROR Bug; -- no empty slots ENDLOOP; dd.curlen _ dd.curlen - 1; VM.PutDict[dict,dd]; RETURN[TRUE]; }; ClearDict: PROC[dict: dict Object] = { dd: DD _ VM.GetDict[dict]; slot: Slot _ dd.beg; THROUGH[0..dd.size) DO VM.PutTuple[slot,nullTuple]; slot _ slot + SIZE[Tuple]; ENDLOOP; Assert[slot=dd.end]; dd.curlen _ 0; VM.PutDict[dict,dd]; }; LookUpCache: PROC[cache: Cache, hash: CARDINAL, key: Object] RETURNS[BOOLEAN,Loc] = { loc: Loc _ hash MOD cache.size; cache.probes _ cache.probes + 1; THROUGH[0..cache.size) DO h: CARDINAL _ cache.hash[loc]; IF h=hash THEN { k: Object _ cache.table[loc].key; -- compare keys IF Equal[k,key] THEN { cache.hits _ cache.hits + 1; RETURN[TRUE,loc] }; } ELSE IF h=freeHash THEN RETURN[FALSE,IF cache.curlen [known, slot, dict] _ FindInDict[ob, hash, name]; ENDCASE => ERROR Error[typechk]; RETURN[known] }; IF StackForAll[frame.dictstk, Find] THEN { EnterInCache[frame.cache,hash,VM.GetTuple[slot]]; -- put in cache, anticipating a Get RETURN[TRUE,dict] } ELSE RETURN[FALSE,nullDict]; }; Get: PUBLIC PROC[dict: dict Object, key: Object] RETURNS[Object] = { known: BOOLEAN; value: Object; [known,value] _ TryToGet[dict,key]; IF known THEN RETURN[value] ELSE ERROR Error[undefkey]; }; TryToGet: PUBLIC PROC[dict: dict Object, key: Object] RETURNS[BOOLEAN,Object] = { name: Object _ KeyName[key]; hash: CARDINAL _ Hash[name]; dd: DD _ VM.GetDict[dict]; known: BOOLEAN; slot: Slot; [known,slot] _ LookUpDict[@dd,hash,name]; IF known THEN RETURN[TRUE,VM.GetTuple[slot].value] ELSE RETURN[FALSE,nullOb]; }; Put: PUBLIC PROC[dict: dict Object, key, value: Object] = { tuple: Tuple _ [KeyName[key],value]; hash: CARDINAL _ Hash[tuple.key]; Delete: PROC[f: Frame] = { DeleteInCache[f.cache,hash,tuple.key] }; EnterInDict[dict,hash,tuple]; ForEachFrame[Delete]; -- in case dict is on stack }; Def: PUBLIC PROC[frame: Frame, key, value: Object] = { dict: dict Object = TopDict[frame.dictstk]; tuple: Tuple _ [KeyName[key],value]; hash: CARDINAL _ Hash[tuple.key]; Delete: PROC[f: Frame] = { DeleteInCache[f.cache,hash,tuple.key] }; EnterInDict[dict,hash,tuple]; EnterInCache[frame.cache,hash,tuple]; -- insert in this frame's cache ForEachFrameExcept[frame,Delete]; -- in case dict is on stack }; Load: PUBLIC PROC[frame: Frame, key: Object] RETURNS[Object] = { known: BOOLEAN; value: Object; [known,value] _ TryToLoad[frame,key]; IF known THEN RETURN[value] ELSE ERROR Error[undefkey]; }; TryToLoad: PUBLIC PROC[frame: Frame, key: Object] RETURNS[BOOLEAN,Object] = { cache: Cache _ frame.cache; name: Object _ KeyName[key]; hash: CARDINAL _ Hash[name]; known: BOOLEAN; loc: Loc; dict: dict Object; slot: Slot; Find: PROC[ob: Object] RETURNS[BOOLEAN] = { WITH ob:ob SELECT FROM dict => [known,slot,dict] _ FindInDict[ob,hash,name]; ENDCASE => ERROR Error[typechk]; RETURN[known] }; [known,loc] _ LookUpCache[cache,hash,name]; -- try the cache first IF known THEN RETURN[TRUE,cache.table[loc].value]; -- found it! (this should be common) known _ StackForAll[frame.dictstk, Find]; IF known THEN { tuple: Tuple _ VM.GetTuple[slot]; InsertInCache[cache,hash,tuple,loc]; RETURN[TRUE,tuple.value] } ELSE RETURN[FALSE,nullOb]; }; Store: PUBLIC PROC[frame: Frame, key, value: Object] = { tuple: Tuple _ [KeyName[key],value]; hash: CARDINAL _ Hash[tuple.key]; known: BOOLEAN; dict: dict Object; slot: Slot; Find: PROC[ob: Object] RETURNS[BOOLEAN] = { WITH ob:ob SELECT FROM dict => [known,slot,dict] _ FindInDict[ob,hash,tuple.key]; ENDCASE => ERROR Error[typechk]; RETURN[known] }; IF StackForAll[frame.dictstk, Find] THEN VM.PutTuple[slot,tuple] ELSE EnterInDict[TopDict[frame.dictstk],hash,tuple]; EnterInCache[frame.cache,hash,tuple]; -- update cache }; Del: PUBLIC PROC[dict: dict Object, key: Object] = { name: Object _ KeyName[key]; hash: CARDINAL _ Hash[name]; Delete: PROC[f: Frame] = { DeleteInCache[f.cache,hash,name] }; IF NOT DeleteInDict[dict,hash,name] THEN ERROR Error[undefkey]; ForEachFrame[Delete]; -- in case dict is on stack }; ClrDict: PUBLIC PROC[dict: dict Object] = { ClearDict[dict]; ClearAllCaches[]; -- in case dict is on stack }; ClearAllCaches: PROC = { Clear: PROC[f: Frame] = { ClearCache[f.cache] }; ForEachFrame[Clear]; -- in case dict is on stack }; AttachDict: PUBLIC PROC[dict, adict: dict Object] = { IF AttachmentPath[adict,dict] THEN ERROR Error[attachmentcycle] ELSE { oldatt,newatt: array Object; dd: DD _ VM.GetDict[dict]; oldatt _ dd.attach; FOR i: CARDINAL IN[0..dd.curatt) DO IF AGetDict[oldatt,i] = adict THEN RETURN; -- already attached ENDLOOP; IF dd.curatt { undefkey _ MakeName[".undefkey"L]; dictfull _ MakeName[".dictfull"L]; attachmentcycle _ MakeName[".attachmentcycle"L]; notattached _ MakeName[".notattached"L]; dictundflw _ MakeName[".dictundflw"L]; dictovrflw _ MakeName[".dictovrflw"L]; dfacmd _ RegisterInternal["@dictforall"L, DFAProc]; afacmd _ RegisterInternal["@attachedforall"L, AFAProc]; RegisterExplicit[frame, ".dict"L, JDict]; RegisterExplicit[frame, ".maxlength"L, JMaxLength]; RegisterExplicit[frame, ".known"L, JKnown]; RegisterExplicit[frame, ".where"L, JWhere]; RegisterExplicit[frame, ".get"L, JGet]; RegisterExplicit[frame, ".put"L, JPut]; RegisterExplicit[frame, ".def"L, JDef]; RegisterExplicit[frame, ".load"L, JLoad]; RegisterExplicit[frame, ".store"L, JStore]; RegisterExplicit[frame, ".del"L, JDel]; RegisterExplicit[frame, ".clrdict"L, JClrDict]; RegisterExplicit[frame, ".begin"L, JBegin]; RegisterExplicit[frame, ".end"L, JEnd]; RegisterExplicit[frame, ".dictforall"L, JDictForall]; RegisterExplicit[frame, ".curdict"L, JCurDict]; RegisterExplicit[frame, ".attachdict"L, JAttachDict]; RegisterExplicit[frame, ".detachdict"L, JDetachDict]; RegisterExplicit[frame, ".attachedforall"L, JAttachedForall]; RegisterExplicit[frame, ".detachall"L, JDetachAll]; RegisterExplicit[frame, ".clrdictcache"L, JClrDictCache]; RegisterExplicit[frame, ".dictcachestats"L, JDictCacheStats]; RegisterExplicit[frame, ".resetdictcachestats"L, JResetDictCacheStats]; }; ENDCASE; }; Install[InstallDict]; }. Paxton December 12, 1980 5:25 PM added attached dictionaries Paxton December 14, 1980 10:31 AM added CacheHash array and automatic growth for full dictionaries Paxton January 8, 1981 9:34 AM add MakeName and StringForName; change Hash and Match. Wyatt 16-Apr-81 0:16:54 Pilot conversion use String instead of StringDefs Wyatt 7-May-81 11:34:07 use hash field for NameStrings Wyatt 29-Aug-81 22:32:37 rewrite ÈJaMDictImpl.mesa Original version by Martin Newell, January 1979 Bill Paxton, 29-Jan-82 15:44:13 Doug Wyatt, 21-Oct-81 17:21:22 Russ Atkinson, July 22, 1983 6:10 pm Administers dictionaries in VM. A dictionary is a type of Object, however additional information is held in a DictDesc, which also lives in VM. A Dictionary is a set of tuples, in which both key and value are Objects (key is not necessarily a string). Present implementation: Dictionary is allocated as a contiguous vector of tuples Hash coding is used to access tuples Dictionary stack is cached with single level cache. Types and constants GLOBALS Low level dictionary operations Looks in dictionary for object with key, hashed to hash. If found then returns [TRUE, where it is] else returns [FALSE, where it would go or slotNIL if no room] Looks in dictionary for object with key, hashed to hash. Will check in attached dictionaries also. If found then returns [TRUE, where it is, which dict] else returns [FALSE, where it would go or locNIL if no room, dict] Rehash entries beyond this one since they may be result of collisions Cache operations Looks in dictionary cache for object with key, hashed to hash. If found then returns [TRUE, where it is] else returns [FALSE, where it would go or locNIL if no room] CREATION Return a new dictionary for up to maxlen tuples ATTRIBUTES Return current number of entries in dictionary dict Return maximum allowable length of dictionary dict ACCESS Generates undefkey: Generates dictfull: Enters into dictionary on top of dictstk, Generates dictfull: Return value looked up in highest dictionary having key in dictstk, searching attached dictionaries if necessary Not in cache, must search dictionary stack Enters into highest dictionary having key in dictstk, or into dict on top of dictstk if key not found. Generates dictfull: Deletes object key from dictionary Generates undefkey if object not found: Deletes all objects from dictionary dict attach adict to dict Generates attachmentcycle returns TRUE iff there is an attachment path from dict1 to dict2 detach adict from dict Generates notattached Detaches all attached dictionaries *** JaM INTRINSICS *** Expects opstk: (maxLength) Returns opstk: (new dictionary to hold up to maxLength objects) Expects opstk: (dictionary) Returns opstk: (maximum number of objects in dictionary) Expects opstk: (dictionary, key) Returns opstk: (boolean) Expects opstk: (key), dictstk: a dictionary stack Returns opstk: (dictionary(iff key known), boolean("known")) dictstk: unchanged Expects opstk: (dictionary, key) Returns opstk: (value looked up in dictionary) Expects opstk: (dictionary, key, value) Enters into dictionary Returns opstk: () No indication of whether an object with that key already existed. Expects opstk: (key, value), dictstk: (dictionary) Enters into dictionary Returns opstk: (), dictstk: (dictionary) No indication of whether an object with that key already existed. Expects opstk: (key), dictstk: a dictionary stack Returns opstk: (value looked up in highest dictionary having key in dictstk), dictstk: unchanged Expects opstk: (key, value), dictstk: a dictionary stack Enters into highest dictionary having key in dictstk, or into dict on top of dictstk Returns opstk: (), dictstk: unchanged Expects opstk: (dictionary, key) Deletes object key from dictionary Returns opstk: () Expects opstk: (dictionary) Deletes all objects from dictionary Returns opstk: () Expects opstk: (dictionary), dictstk: () Returns opstk: (), dictstk: (dictionary) Expects dictstk: (dictionary) Returns dictstk: () Increments old to location of next non-null tuple and returns new location together with the key and object of the tuple Initial call should have old=locNIL Returns new=locNIL if no more tuples Normal enumeration is not used because of way JaM control works Expects opstk: (dictionary)(object) For each tuple in dictionary put (key)(value) onto opstk and execute object Returns opstk: () save them on exec stack prime state start it get tuple onto opstk set up opstk set up execstk and let it happen Expects opstk: (), dictstk: (dictionary) Returns opstk: (dictionary), dictstk: (dictionary) Expects opstk: (dictionary1) (dictionary2) Returns opstk: () Generates attachmentcycle Expects opstk: (dictionary1) (dictionary2) detach dict2 from dict1 Returns opstk: () Generates notattached Expects opstk: (dictionary1) (object) For each dictionary2 attached to dictionary1 put dictionary2 onto opstk and execute object Returns opstk: () save them on exec stack prime state start it set up opstk set up execstk and let it happen Expects opstk: (dictionary) Detaches all attached dictionaries Returns opstk: () initialization Dictionary commands Êg˜šœ™Jšœ/™/Jšœ™Jšœ™J™$—J˜Jšœ‰™‰šœ™JšœH™HJšœ$™$Jšœ3™3—J˜šÏk ˜ Jšœ œ˜Jšœœœ.˜?Jšœ œ:˜Kšœ˜ Jšœ¿˜¿—Jšœ œ˜Jšœœ?˜JJ˜—šœ ˜Jšœ#˜*Jšœ˜Jšœœ/˜6—J˜Jšœ™˜J˜#Jšœœœ˜&J˜Jšœ œ Ïc"˜AJšœ œ ž*˜HJ˜Jšœœ˜J˜—šœ™J˜Jšœ œœ˜)J˜J˜QJ˜J˜—Jšœ™J˜šÏnœœœœ˜IJ˜šœœœ œ˜*Jšœœ˜—J˜J˜—šŸ œœœœœœœ˜3Jšœœœœ ˜6Jšœ8™8Jšœ)™)Jšœ=™=Jšœœœ œ˜9Jšœœœž"˜Pšœ ˜Jšœœ˜!Jš œœœœž˜@šœœž˜4Jš œœœœœ ˜<—Jšœœ œ œž˜LJšœ˜—Jšœž˜J˜J˜—š Ÿ œœœœœœ˜,Jšœœ˜-šœœ œ˜&Jšœž#˜1Jšœ.ž˜EJšœœ ž˜(Jšœœœž˜—Jšœœ ˜Jšœ˜—Jšœœ˜5J˜9J˜—J˜J˜—šŸ œœœ˜@Jšœœ%˜;Jšœ8™8Jšœ)™)Jšœ5™5JšœB™BJšœœœ˜J˜(Jš œœœœ ž˜8šœž˜š œœœœž#˜GJ˜8J˜3Jšœœœœ˜'Jšœ˜—J˜—Jšœœ ˜J˜J˜—šŸ œœœ˜FJšœœœ˜Jšœœ ˜J˜.Jšœœœ˜%Jšœ&œ˜@J˜J˜—š Ÿ œœœœœ˜WJšœœœ˜Jšœœ ˜J˜(Jš œœœœœœ˜=JšœE™Ešœ˜J˜Jšœœ œ œž˜LJšœœ˜Jšœœœž ˜/J˜™>Jšœ)™)Jšœ<™Jšœœœœ˜?Jšœž˜1J˜J˜—šŸœœœ˜+Jšœ(™(J˜Jšœž˜-J˜J˜—šŸœœ˜JšŸœœ%˜0Jšœž˜0J˜J˜—šŸ œœœ˜5Jšœ™Jšœ™Jšœœœ˜?šœ˜J˜Jšœœœ˜J˜šœœœ˜#Jšœœœž˜>Jšœ˜—Jšœœ˜/šœœ˜%Jš œœœœž˜JJšœ œœ˜.Jšœœ˜*—š œœ œœ˜.Jšœ%ž˜9Jšœ˜—Jšœž˜,Jšœœž˜DJ˜—Jšœž˜-J˜J˜—šŸœœœœ˜DJšœ@™@Jšœœœœ˜"šœœœ˜"šœœœ˜#Jšœ-œœœ˜AJšœ˜ ——Jšœœ˜J˜J˜—šŸ œœœ˜5Jšœ™Jšœ™Jšœœœ˜šœœœ˜#šœœ˜%J˜šœœœ˜#Jšœ+ž˜>Jšœ˜—Jšœž˜)Jšœž˜-Jšœ˜ —Jšœ˜—Jšœž˜+J˜J˜—šŸ œœœ˜-Jšœ"™"Jšœœœœ˜>Jšœž˜-J˜J˜—šŸœœœ%˜7Jšœœœ˜4J˜2J˜J˜—šŸœœœ˜"J˜6šœœž˜;Jšœœ˜2—J˜J˜J˜—Jšœ™J˜šŸœœœ˜$Jšœ™Jšœ?™?Jšœœ˜,J˜!J˜J˜J˜—šŸ œœœ˜)Jšœ™Jšœ8™8J˜)Jšœœ˜$J˜!J˜J˜—šŸœœœ˜%Jšœ ™ Jšœ™J˜J˜)J˜)J˜J˜—šŸœœœ˜%Jšœ™Jšœ™Jšœ<™šœ ˜Jšœœ˜!Jšœœœ˜@Jšœœ˜)Jšœ˜—Jšœœ˜2J˜J˜—šŸ œœœ˜*Jšœ#™#JšœK™KJšœ™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šœ2™2J˜+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˜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˜J˜—šŸœœœ˜3J˜J˜-J˜J˜—Jšœ™J˜šŸ œœ&˜7šœ˜˜ Jšœ™J˜"J˜"J˜0J˜(J˜&J˜&J˜3J˜7J˜)J˜3J˜+J˜+J˜'J˜'J˜'J˜)J˜+J˜'J˜/J˜+J˜'J˜5J˜/J˜5J˜5J˜=J˜3J˜9J˜=J˜GJ˜—Jšœ˜—J˜J˜—J˜J˜J˜J˜šœ ˜"J˜J˜—šœ!˜#J˜@J˜—šœ˜ J˜6J˜—˜J˜J˜ J˜—˜J˜J˜—˜J˜J˜J˜——…—O{5