Πέμπτη 14 Νοεμβρίου 2024

About the developing of Objects for M2000 Interpreter.

Back in 1999, I started to be writing an interpreter named M2000. Letter M is the first letter of Μαθητής the greek wotd for Pupil (the language target the education, from elemantatry stage) and 2000 is the start year of 21 century.

The differences between this language among others are:

  • There is a Stack of Values in every step of our program and we can push values (using Push) and read values (using Read) form the top of stack (like a LIFO) or we can use Data to add  values to the back of stack and when we read them (from top) we make a FIFO. The stack of values ins't the process stack. We can place any type of values.
  • There are a vocabulary of Greek and English words use for writing statements.
  • Modules expand the vocabulary. Passing parameters pushed to stack of values and that stack passed to module. Module's code is responsible to take the parameters and leave the size of stack the way the caller expect (so by using the stack we get values back from module). When interprer call a module actually not known about how and what parameters the module need so send everything we send. 
  • Functions are like modules, we can call then as modules (a non zero value raise error), or we can use them in an expression. Every time we call a function in an expression we pass a new stack of values with all parameters we wish to send to that function (so functions as variadic by design).
  • For modules and functions we can check the stack of values about types before we read them (and popping). Also we can shift values to top (using Shift) or back a distance from top (using ShiftBack), we can make copies using Over to copy  a value or series of values from a distance from top,

The language at those days was a better than an assembler, for writing programs. The implementation was very simple, just to prove that works. Searching of a word was just a word search in a string. The very first interpreter also has only one stack of values (there was no new stack at calling an expression in a function).

Interpreter coded using VB5, and from 2013 using VB6  From 2002 to 2013 was 12 years where I have no interest to expand the language.
At that point (2002) the language was able for 2D graphics, Bitmap manipulation, Sprites,  Video, Music, Sound (Multimedia) and databases (creating access type database, using DAO 3.5), a help system, an editor for big texts (for million of lines), but not Unicode, we have to set the charset for fonts. (Those times I have no idea how to use Unicode from VB6). 

From 2014 I found that the Windows have the VB6 dll (a library) included (and I have Widows 11 now and that dll included, so we can run any VB6 application, including the implementation of M2000 interpreter). Also I found VBForums and I start to think how to expand the interpreter. A make some own controls for unicode input and output., saving/reading files using  UTF8 and many other things. So first I made the GROUP object.

The idea to use a Group object was for passing one thing with some variables by reference.
The example below has the MyGroup with 2 members. What Interpreter did founding the definition? First make MyGroup.C then MyGroup.Y and the real object MyGroup which have a list with the members (as references) So each member is like any other variable. Each time we use Mygroup.C interpreter do a search for a name hidden_module_name.Mygroup.C (ignoring upper/lower letter case), if not found then search without the hidden_mofule_name (check if it is a Global variable).
For some other languages to find a member is a work before the execution, so the object has a "monolithic" use and replace the name with a call to a setter with a specific id, or using a query system (like in COM objects), to return the ID of the member and then using a getter or setter function using that ID.

So for M2000 we have members as defined variables at module, and the actual group has only the list of members. When we push the group to stack of values, a new object created, a floating group. This group has a list of members with values. When we read M in module OneTime the float group create the M group, and the M.C and the M.Y (or more depend of number of group items).

At this point the Group of values is an object as a set of variables.
At the ThisGroup=MyGroup we get a copy of MyGroup as a float group and that float group create the ThisGroup (because ThisGroup is a new name).
So Groups are like variables. The statement MyGroup=ThisGroup do a merging: First a float group created and the merging follow some rules: A numeric or string member take the name as MyGroup and if interpreter found it then place the new value, but if not found it then make a new one.

At checkThis we place a reference so the reference works like this, supposed we pass MyGroup:  we pass the reference (the actual name of  MyGroup) and when the Interpreter found that M is a Group, read the list (which have references and make all the references using the name M (not the MyGroup), So variables M.C is reference of MyGroupC, and M.X is reference of M.Y (a reference point to the same memory as the referenced variable point.
From the point of speed by reference passing is better than by value. Some languages like python, have by value pass only, but for objects actually they pass the pointer to object, so by using the object (and the member id), is like we have the object passing by reference. The difference is that we can't change the object with a new one (because the pointer can't changed on the caller place, and change only on the callee place).  
We can see the member list of group using group.count(), member$() and member,type$(). We can use Read From GroupName, FisrtMember, ....
Using Read From  we make local variables as references to members.  I haven't use this for last 10 years. 

So from that idea (to have something like Records, as values), I made the OOP of M2000 adding more features like members groups (groups inside groups), modules, functions, events, any type of variable including arrays, lists, stacks etc. So far we see the Named Groups (like MyGroup) and the float groups (in a(0) and a(1), and for short time as return value from expression, or from stack of values). Now there is another form, the pointer to group, which hold a group as a float group in a variable and anywhere. So before the introduce of pointer to group, all groups have one only hidden pointer, and only reference can be used (which refer to the actual value). Reference can't changed, they are hard encoded. But pointers to groups are placeholders of pointers, and as that we can change it. A named group deleted at the end of the entity which create it. A float group deleted when the array, the list or the stack deleted or the place which we place it get another group. A group which we get a pointer from that (a real one, and not a pointer as a reference inside- we can do that) deleted when the last pointer to that object deleted (and this call the Remove member of group to do something before actually deleted, using a state of "last pointer"). The Class introduced to make groups by a function, and we can make groups to return values, or to get values, and we can make operators for groups also. And many more, like private and public members, class members, superclass, properties with getters/setters, and types of groups.

So this code show the earlier use of Groups (like records without modules/functions as members)

group MyGroup {
C=10, Y=100
}
module CheckThis (&M) {
M.C++
M.Y+=100
}
ThisGroup=MyGroup ' make a new one, as a copy


CheckThis &MyGroup


Print MyGroup.C=11, MyGroup.Y=200


MyGroup=ThisGroup


Print MyGroup.C=10, MyGroup.Y=100


Group Second {
X=300
}


MyGroup=Second ' Join MyGroup with a copy of Second


Print MyGroup.C=10, MyGroup.Y=100, MyGroup.X=300


' a module with M (is a Read M statement) get a copy on M
Module OneTime (M) {
Print M.C, M.Y,
if valid(M.X) then Print M.X else Print
}


OneTime MyGroup ' has three members  print  10, 100, 300


OneTime ThisGroup ' has only 2 members  print 10, 100
Print "MyGroup"
for i=1 to group.count(MyGroup)
Print member$(MyGroup, i)+"="+eval(member$(MyGroup, i)), member.type$(MyGroup,i)
next
Print "members:"+(i-1)
Print "ThisGroup"
for i=1 to group.count(ThisGroup)
Print member$(ThisGroup, i)+"="+eval(member$(ThisGroup, i)), member.type$(ThisGroup,i)
next
Print "members:"+(i-1)


Dim a(2)
a(0)=ThisGroup
a(1)=MyGroup
a(0).Y+=1000
Print a(0).C=10, a(0).Y=1100
OneTime a(0) ' 10 1100
' passing by reference an array item was a newer work
' in 2014 we have to pass by reference the array and mark the item to change
CheckThis &a(1)
OneTime a(1) ' 11 200 300
Print a(1).C=11, a(1).Y=200, a(1).X=300


Module OldCheckThis (&k(), i) {
for k(i) {
.c++
.Y+=100
}
}
OldCheckThis &a(), 0
OneTime a(0) ' 11 1200
' those days i have an idea to link members to variables as references:
' We use the For object { } which delete all new variables
' We have to delete them because a reference can get new reference.
for this {
Read from ThisGroup, myC, myY
myC=1234
myY=2100
}
Print ThisGroup.C=1234, ThisGroup.Y=2100
' so now myC and myY not exist, we can define them again.
for this {
Read from MyGroup, myC, myY
myC=1234
myY=2100
}
Print MyGroup.C=1234, MyGroup.Y=2100


Module PassMemberByReference (&a) {
a+=50000
}
PassMemberByReference &MyGroup.C
Print MyGroup.C=51234
' we can't pass by reference something without a name like a variable
' in a(0) group is in a Float state, all members are inside and not as variables
' Using  the For object { } we open and fix the members as variables
' because we din't know the actual name we use dot or This dot
' and interpreter add the name and fix it as variable.
For a(0) {
PassMemberByReference &.C
Print .C=50011
PassMemberByReference &this.C
Print .C=100011
}





Δεν υπάρχουν σχόλια:

Δημοσίευση σχολίου

You can feel free to write any suggestion, or idea on the subject.