# VBForums CodeBank > CodeBank - Visual Basic 6 and earlier >  The VarFrame Class

## georgekar

This is a new class to hold a frame of variables. This can help on making script languages, to keep variables, which means sets of  Names and Values.
The interesting part, is how we can establish a reference system, so for each entity we can address to it by a long number, which have two parts, the target VarFrame and the actual position on that Varframe. Also the second part has two parts, the least 12 bits is the actual index number, and the last 4 bit  used to indicate the type of the variable, and the actual array (we have for each basic type, one array).


So one type for these "basic types" is the reference one. Is an array which hold references for other VarFrames. These references are not like object references (is a kind of indirection).

For use it in a Virtual Machine for a script language, we can use the low part, as the local variable (two bytes only). The reference variable can be included in the local Varframe, which means we get also two bytes and the VarFrame handle the actual reference (and get the actual type) of the variable.

So with two bytes we may have 4096 variables of same type, 15 types and 1 type for the reference system. Also the Varframe never delete an insert (supposed the varframe can be recycled after the end of use). One type is the variant type. And there is the object type so we may have arrays. through RefArray which make arrays for any type, without using an array of variants which take a lot of space. *And this is the goal for this class, to handle "local variables" for a custom virtual machine for a custom script language.*

Another use is for user Objects. I didn't show that here, but I can "draw" the idea. Some arrays in VarFrame are for Modules/Functions, which supposed to be local two. So an object is a "movable" part, a "value", and a VarFrame can be done such thing. The difference is that as a Varframe, it can't be reference through a Handler (the top part), except we can use an immediate way, when a method call another method, to add the This Varframe to a one time use of handler, and pass the compound pointer (as reference) to the external method, and then at the return of execution, and the return of the object's method, we just throw the link for the handler to This Varframe (the objec'ts VarFrame). So using Custom Objects, means an expansion for the Handlers (some Handlers may used for temporary use, means can show some other VarFrame later).

So you can now forget the mention about custom objects, and focus to the program here.

The idea for VarFrame, are to* NOT INCLUDE* names in the varframe. So we have HASH LISTS, without saving the names as "strings". We have one HashList for all names. So a name ALFA maybe is a string in VarFrame A, but a long in varFrame B. So each Identifier NAME has a unique ID. 

The Varframe has one Hash table for all types (all included arrays). Some types that not used are not exist, they have an empty "slot".

So VarFrame it's different from ordinary Hash Tables/Lists. One Hash table, addressing multiple arrays. (arrays of values for the variables, arrays per type).

For a script language, we need a way to store/retrieve values, through a "search" system (using name) and for a JIT we need a way to reference the values, without using names, just pointers (or for VarFrame, the 16bit compound indexes), and if we need references to other VarFrames, we can use handlers (16bit, as High Integer Value of a Long Value).

The HashList, and the VarFrame can used for values with same name. We can use two or more same keys, and we can "walk" easy on them.


The program in zip file has some examples:

let see the global variables:



```
Global Const VbLongLong = 20Dim poolVarFrame() As VarFrame
Dim poolMax As Integer, NextPoolIndex As Integer
Dim poolHandlers() As Integer
Dim MaxHandler As Integer, Nexthandler As Integer, TopHandler As Integer
Public Identifiers As New HashList
Public LiteralsNum As New HashList
Public LiteralsStr As New HashList
```

We use Long Long also (64bit Integers)
We make a pool of VarFrames, so each time we need we see if exist in the pool and take it from there. 
The basic idea is that we have Handlers which is the numbers which are the top part of a "Referenced" variable.


_The VarFrame is a mix of a refArray and a Hashlist. (the last two classes I was introduced them some days ago, but here we have updated classes)._

This example show how the reference work (through the WeakPtr, the compound index)



```
Sub main1()    Identifiers.Prepare 2000
    TopHandler = -1: poolMax = -1: NextPoolIndex = -1: MaxHandler = -1: Nexthandler = -1
    AddHandlers 100: ExpandPool 100:
    Dim a As Integer, b As Integer
    a = NeoVar.NewVarFrame()
    b = NeoVar.NewVarFrame()
    Dim aa As VarFrame, bb As VarFrame
    Set aa = poolVarFrame(a)
    Set bb = poolVarFrame(b)
    
    Debug.Print aa.VarCount(), bb.VarCount()
    aa.AddVar "ALFA$", "HELLO THERE"
    bb.AddVar "ALFA1$", "HELLO THERE AND THERE"
    aa.AddVar "DBLBETA", 123#
    bb.AddVar "DBLDELTA", 500#
    Dim val
    If aa.Find("ALFA$", val) Then
        Debug.Print "aa.ALFA$ = "; val
    End If
    If bb.Find("ALFA1$", val) Then
        Debug.Print "bb.ALFA1$ = "; val
    End If
    If aa.Find("DBLBETA", val) Then
        Debug.Print "aa.DBLBETA = "; val
    End If
    If bb.Find("DBLDELTA", val) Then
        Debug.Print "bb.DBLDELTA = "; val
    End If
    Dim WeakPtr, ptr1, ptr2
    If aa.Find("ALFA$", , WeakPtr) Then
        Debug.Print "aa.ALFA$ has weak ptr: ", aa.VarId(WeakPtr)
        Debug.Print "handelr: "; aa.HandlerFromWeak(WeakPtr)
        Debug.Print "Value from WeakPtr = "; aa(WeakPtr)
        WeakPtr = bb.AddRefVar("HELLO$", WeakPtr)
        Debug.Print "bb.HELLO$ is a reference to aa.ALFA$"
        Debug.Print "local ptr="; WeakPtr
        Debug.Print "Value from Local Ptr ="; bb(WeakPtr)
        bb(WeakPtr) = "New Value"
        Debug.Print "Value from Local Ptr ="; bb(WeakPtr)
    End If
    If bb.Find("HELLO$", val, WeakPtr) Then
        Debug.Print "bb.HELLO$ = "; val
        Debug.Print "bb.HELLO$ has weak ptr: ", bb.VarId(WeakPtr)
        Debug.Print "handelr: "; bb.HandlerFromWeak(WeakPtr)
        Debug.Print "Value from WeakPtr = "; bb(WeakPtr)
        
    End If
    If bb.Find("ALFA1$", , WeakPtr) Then
        Debug.Print "bb.ALFA1$ has weak ptr: ", bb.VarId(WeakPtr)
        Debug.Print "handelr: "; bb.HandlerFromWeak(WeakPtr)
        Debug.Print "Value from WeakPtr = "; bb(WeakPtr)
    End If
        If aa.Find("ALFA$", val) Then
        Debug.Print "aa.ALFA$ = "; val
    End If
    Debug.Print aa.Count(), bb.Count(), " Total fields"
    Debug.Print aa.VarCount(), bb.VarCount()
    Debug.Print "Identifier at id 0: "; Identifiers.Key(0)
    Debug.Print "Total Identifiers: "; Identifiers.Count
    Dim zero As Long
    zero = LiteralsNum.Add(0#, "0", vbVariant)
    Dim zerolong As Long
    zerolong = LiteralsNum.Add(0&, "0&", vbVariant)
    Debug.Print zero, " zero id", TypeName(LiteralsNum(zero))
    Debug.Print zerolong, " zeroLong id", TypeName(LiteralsNum(zerolong))
    Debug.Print "Total number literals: "; LiteralsNum.Count
    Dim vars As HashList
    Set vars = bb.VarNamesList()
    Dim i As Long
    For i = 0 To vars.Count - 1
        Debug.Print vars(i), Hex$(vars.KeyNum2(i))
    Next i
    Debug.Print Join$(aa.VarNamesList("AA.").Values, ", ")
    Debug.Print Join$(bb.VarNamesList("BB.").Values, ", ")
 
End Sub
```

This example show the HashList with rename function.




```
Sub main()' using HashList as Variant type
Dim hash1 As New HashList
hash1.Prepare 1000
' just mark the first item as VbVariant
hash1.Add 10, "integer1", vbVariant
hash1.Add "hello", "String1"
hash1.Add hash1.cInt64("123456789012345678"), "long long 1"
hash1.Add CDec("12345678901234567800123456"), "Decimal 1"
hash1(1) = 50000
Debug.Print hash1(0), hash1(1), hash1(2), hash1(3)
hash1(0) = hash1(0) + 100
hash1(1) = 50000  ' change type
Debug.Print hash1(0), hash1(1), hash1(2), hash1(3)
hash1(2) = "ok" 'change type
Debug.Print hash1(0), hash1(1), hash1(2), hash1(3)
Debug.Print hash1.cInt64("123456789012345678")
Dim val, there, i As Long
If hash1.Find("long long 1", , there) Then
    hash1.KeyNoReHash(there) = "New Key"
End If
If hash1.Find("New Key", val) Then
    Debug.Print "Value at New Key = ", val
End If
If hash1.Find("Decimal 1", , there) Then
    hash1.Key(there) = "New Key"
    ' so now we do rehash here
End If
If hash1.Find("New Key", val) Then
    Debug.Print "Value at New Key = ", val
End If


Debug.Print "List"
For i = 0 To hash1.Count - 1
Debug.Print hash1(i), hash1.Key(i)
Next i
End Sub
```

----------


## yokesee

Interesting, I'll read it tranquility.
Regards thank you very much

----------


## SearchingDataOnly

Hi georgekar, I tested your VarFrame carefully, but when testing main1 and main2 in *NeoVar.bas*, the system prompts "Subscript out of range" with the line of error:


```
Function AddVar(akey$, v) As Long
    
    Dim c As Long, ThisPtr As Long, pv1 As Integer, pv As Long
    Select Case VarType(v)
    Case vbString, vbDouble
    Case Else
        Err.Raise "variable type not supported"
    End Select
    c = Elements(p)
    If c = 0 Then
        Init 1
        c = Elements(p)
    ElseIf nextVal + 1 = datasize Then
        datasize = datasize * 2
        If datasize > 8192 Then Err.Raise 6
        ReHashInteger
    End If
    nextVal = nextVal + 1
    If c = 7 Then
        arrValue(nextValStore) = nextVal
    Else
        p(nextValStore) = nextVal
    End If
    ...
    ...
```

Also, in *RefArray*, the red line of code seems to be changed to the blue code:



```
Property Let Value(Optional where, Optional what, that)
    ...
    ...
    If Err Then mError = Err.Number: Err.Clear
	
    If Err Then 
        mError = Err.Number: Err.Clear   
    Else
        mError = 0
    EndIf
```


And also,
In *HashList.cls*, the constant definition of the array position looks like this:


```
Private Const keyArray = 1, DataArray = 2, HashArray = 0, FirstKey = 3, Pleft = 4, HashSizeStore = 5, nextValStore = 6
```

In *VarFrame.cls*, the constant definition of the array position is somewhat different:


```
Const keyArray = 2, DataArray = 3, HashArray = 4, FirstKey = 5, Pleft = 6, nextValStore = 7
```

I wonder if this is done intentionally? Thanks.



*Edit:*
I see a large number of variables named pv, pv1 in HashList.cls. In most cases, pv, pv1 is actually *HashCode*, right?

----------


## georgekar

Thank you for your time to see this. It was a last time change. In DefArrayAt, the Value(where)=DummyXXX have to change to arrValue(where). Because when I stop the IDE always do a ctrl+Z to go back to where I have the last edit, I think I revert the last good change (which was the renaming of Value() to arrValue() in n DefArrayAt.

So changing the names all is good. Also I do the mError = 0, for clearing the merror. So the new zip file updated.
For the names I don't have a specific pattern (and also my code indentation is terrible).

A made some hash lists before but HashList and VarFrame are something new (but at the base it is parts from code from my previous work). You can find in my git here https://github.com/M2000Interpreter/Environment these hashlists:
Hash.cls, where item is the UDT for each element which HASH table point (this is used for variables, and has function to drop the last X entries (from the reverse order from definition), Same names can exist (we get always the last enter, so this is the shadowing system, for two global which defined at some points before), also has to not deleted the variables which are references. Variables actually are in var() array, and nDx is the index to that array. So maybe there is less variables from items in hash list, because of reference system.

A hash item need at least, Key, data (which here are nDx, KeyType, originaltype, globalvar) and two more at least, the firsthash, and the pleft. The first hash keep the half work for getting the hash code, so at the Rehash stage there is no need to look Keys, just a mod operation on first key by length of hash. The pleft hold 0 or the next item which has the same hashcode (same firsthash so same mod operation, same hash code). When a new item insert to hash, and has a collision (same hashcode), get the pointer to data items (to specific item) and save it to pleft, and seat to hash table. So hash table may have less items from items in data because of collisions. So some items may exist only through pleft (speaking of, because in a serial search from start to end we can read and identify any item).

The lastpos is the extra pointer we need for the deletion of items, keeps in item where in hash table the item exist. When we delete the last N items from the Hash list we have to maintain the pleft pointers, and at ReduceHash the lastpos used for this.

VarFrame and HashList didn't have the reduceHash function. Yet. This work is in progress. I am working now for the new version of M2000 Interpreter the 12th.



```
 Private Type item
    Key As String
    nDx As Long
    KeyType As Integer  ' 0 as is , 1, reference/don't delete
    originaltype As Byte
    globalvar As Byte
    firsthash As Long
    lastpos As Long
    Pleft As Long  ' a list
 End Type
```

There are same other as LongHash, the FastCollection which is the main big hash list (has everything including sorting with various ways (can be see numbers inside string keys and automatic change the sort order (I do memory copies, so we don't have string copies)). The fastcollection can remove any item, just place to that item the last one (if itsn't the last one), adjusting the pointers (in hash list or maybe a pleft pointer. so at that point the lastpos used again, from the deleted item). There is a mk2base.cls which use the FastCollection for make databases in memory (Works but isn't complete, I have to think and write something like SQL, if you like to do something be my guest...). You can add tables, fields, records...

There is also a ThreadClass (used for keeping threads of M2000 code, which implemented using internal task switch, which means they are not separate process), which also use hash list private. In M2000 a module (something like a sub, but with own name space) may have a list of threads, and maybe call some other modules which run some threads too, and that modules can't erase the "list of threads" of modules back in calling list.

The json object also has own hash list. (JsonObj.cls),

Another one idHash used for keeping identifiers which which are known to M2000 (this used from the code colorize function which also I wrote for my UC which also I made).

The idhash has pair of keys and values (long). The ndx may return small numbers (which use in on goto, or big, as address for calling functions by pointer. Also same keys may exist (this way the M2000 mark the not to be used a reserved identifier which used by user for something else), 


```
 Private Type item
    Key As String
    nDx As Long
    firsthash As Long
    lastpos As Long
    Pleft As Long  ' a list
 End Type
```

HashList has something different from other types of hash list. It has variable length hash code. Starting from byte, then can be change to Integer, and then to Long.

VarFrame is a Hash List which also has something different. The values aren't in one array, but in multiple. 

So there is a plenty of classes, and enjoy programming (change names, if you desire something more elegant).

Happy New Year.

----------


## SearchingDataOnly

Thank you, georgekar, I'll continue to learn and test VarFrame.

Happy New Year.

----------

