First we have to distinguish between Dynamic vs Static typing.
Types in M2000 are most static typing. We say most static because we can alter some types to specific types.
Look this example. Start M2000.exe, write Edit A, then copy this code from browser and press Esc, then write A and look the results.
Module DynamicArguments1 {
Print Envelope$( ) \\ print type of arguments as letters
If Match("L") Then Long X
Read X
Print Type$(x)
Long a=x*2
\\ check without Long
Print a, Type$(a)
}
a=10.4
Link a To b \\ b is reference to a \\ b can't change reference
Print Type$(a) \\ double
Print a
DynamicArguments a
DynamicArguments 2.5
\\ now we can make it something else
\\ b is a reference to a so any change of a can change b
Long a
Print Type$(a), Type$(b)
Print a
\\ all arguments passes as double, but using ! we can pass long
DynamicArguments ! a
DynamicArguments ! 2.5
\\ we get same results
DynamicArguments1 ! a
DynamicArguments1 ! 2.5
In this example, we have X in DynamicArguments module, as a weak typed argument. We can't pass a string though. Strings in M2000 are like strings in basic, need a $ at the end of variable name.
What we see here is the Long a which make a 32bit sign integer a or if a exist then convert it to this type of integer.
In module DynamicArguments1 we test the type of argument and we can prepare X to be the same type. So we have a way to test type before assign it to a value. (we can use number from stack of values, using function StackItem() with no parameter - by default is 1 here).
For modules, the module code is responsible to drop values from stack (functions called in expressions have new stack, so if we leave something, then this lost at return). Stack for values used for returning values (any number) from modules, with any way we can think for safe passing (maybe a starting value as a count number, for values after that)
About conversion to Integer. There are two ways, but one way used for stored a number to a variable, if variable is a Long (32 bit) or an integer (by using % in the name, which is a double inside). Round function with 0 decimals works like this: 5.5 became 6, 5.1 became 5 and -5.5 became -6, -5.1 became -5 (less than -5.5). Int function change 5.5 to 5 same as 5.1 and -5.5 same as -5.1 to 6.
Print Int(2.5*3) \\ 7
\\ round performed for Long too
Long A
A=2.5*3
Print A \\ 8
Automatic conversion exist from string to number, only when we assign value using simple = for double type variables (by default any name without % and $ is a double variable).
As we see in this example we can check the input value and do conversion by code.
(x) in Module definition is a macro for interpreter, and using it place a Read X as first line in the module.
Module testme (x){
Print x
}
\\ error no automatic convertion to number
\\ testme "12345"
testme 12345
\\ we can change definition even when we run this module
Module testme {
If Match("S") then { read N$: X=N$ } Else read X
Print X
}
\\ now work witn internal automatic convertion;
testme "12345"
testme 12345
From M2000 console execute Help Match( to find what other letters exist in M2000 for types.
So until now we see that M2000 is mostly weak type, but we can check in execution,in code, in any pass to module or function the type of variable.
Automatic Conversions exist for specific type of variables (integer and long, using round) and for double (string to number). Passing long can be done using ! before number expression.
All numeric expressions evaluated using double type as accumulator. Operators such as +=, -=, *=, /=, -!, ~, ++, -- work in same type (long or double - integer is double inside, but the result rounded to zero decimals)
A variable A maybe is a double, or a pointer to Array or Stack or Inventory, or a Group, or a COM object, or a Gui Object (are COM objects too, but internal to M2000). We can check the name of it using Type$().
So we see that M2000 passing with a dynamic way values to modules (and functions and subs), when types can be checked or not.
There is a special object named Group that may have a value, a number or a string. If we wish a string value then we have to define group with a name with a $ as last character. For those groups there is a second name, the same name without $ at the end. And this needed for access numeric values in group:
Special function Value make Alfa to return a value. If we want to take the object we have to use Group$(), which extract a copy of group. So Alfa is not just a pointer to object, is the only pointer to object, so is "the" object. Group objects may have references to them, but not pointers to them. We can use This as pointer to group inside a group, or we can make a reference to Group or a copy to Group by using This and call some external module/function (a global one perhaps).
Print Alfa$, Alfa.anynumber
\\ Groups are objects prototypes
Beta$=Group$(Alfa$)
\\ so now we have a second object
Print Beta$, Beta.anynumber
Module TakeMe (x){
Print x$
}
TakeMe Beta
\\ works using x$ too, because interpreter read stack and then decide what to do
Module TakeMe (x$){
Print x$
}
TakeMe Beta
Scope for variables in M2000 are local, except we explicit declare as global. A local/global variable in a module shadow a global one. Global variables can alter value from code by using <= and not =. If there is a Global Const we have to explicit declare a variable as const inside a module. If a module exist in a module then it can't see parent's variables.
There is a way for preparing a variable for a module before a module exist:
Alfa.Zeta
Now change module Zeta with this (using This is the same as without it):
module Zeta {
Print This.Beta.m \\100
Print This.m \\ 500
This.Beta.Something \\ 100
}
We see the same result. From Beta (which is part of Alfa) Alfa.m isn't visible. This is normal, so we have groups without dependencies in parent groups. This in not hold with groups that are Properties. There we can Link parent variables to local variables, and the linking performed by Link command.
Print Beta.Prop \\10
Beta++
Print Beta.Prop \\11
The above construction can be written like this:
Print Beta.Prop \\10
Beta++
Print Beta.Prop \\11
We can't change Prop, if only Value exist, but we can change from a module inside Beta the .[Prop] variable, because is private, but a variable. If we wish to change Prop using Beta.Prop=100 then we have to declare it in Property:
Print Beta.Prop \\10
Beta++
Print Beta.Prop \\11
Beta.Prop=400
Beta++
Print Beta.Prop \\ 401
A set clause in Property make property writable. Prop is a group so we can define some other properties, methods or and functions:
Group Beta {
Operator "++" {
.[Prop]++
}
Property Prop { Value, Set } =10
Group Prop {
Module Half {
Link parent [Prop] to prop
prop/=2
}
}
}
Beta.Prop=400
Beta.Prop.Half
Beta++
Print Beta.Prop \\ 201
Groups can be written to Arrays (and other containers, Stacks and Inventories). Beta group give a copy to A(1). If A(1) hold a Group then the next time we give a new group, replace the old one (and because there is no second pointer to Group object then the old one destroyed)
Dim A(10)
A(1)=Beta
Beta.Prop=2
Print Beta.Prop \\ 2
Print A(1).Prop \\ 201
A(1)++
A(1).Prop.Half
Print A(1).Prop \\ 101
So we see that we make groups with "types" as we wish. Groups are "Typeless" objects. But anything inside group maybe is number, or string or a container for that.
We can use weak references for using something which can't make a reference as an array item (references are for names only, one name for an array):
K$=Weak$(A(1))
\\ K$. is a weak reference
K$.++
K$.Prop.Half
\\ we can't use K$. Prop in right expression out of Eval()
Print Eval(K$.Prop)
In A(1) we have a closed group (all members are in group closed). In Beta all members are members of module too. So if we make a X as variable in Beta, and Beta exist in module A then A.Beta.X is a variable of module A too, and we can get a reference only for this.
As we see K$. is a weak pointer. (We can see what is in K$ by using Print K$). We can pass as weak reference Beta (an open Group)
Types in M2000 are most static typing. We say most static because we can alter some types to specific types.
Look this example. Start M2000.exe, write Edit A, then copy this code from browser and press Esc, then write A and look the results.
Module
DynamicArguments (x
){
Long a=x*2
\\ check without Long
Print a, Type$(a)
}
Long a=x*2
\\ check without Long
Print a, Type$(a)
}
Module DynamicArguments1 {
Print Envelope$( ) \\ print type of arguments as letters
If Match("L") Then Long X
Read X
Print Type$(x)
Long a=x*2
\\ check without Long
Print a, Type$(a)
}
a=10.4
Link a To b \\ b is reference to a \\ b can't change reference
Print Type$(a) \\ double
Print a
DynamicArguments a
DynamicArguments 2.5
\\ now we can make it something else
\\ b is a reference to a so any change of a can change b
Long a
Print Type$(a), Type$(b)
Print a
\\ all arguments passes as double, but using ! we can pass long
DynamicArguments ! a
DynamicArguments ! 2.5
\\ we get same results
DynamicArguments1 ! a
DynamicArguments1 ! 2.5
In this example, we have X in DynamicArguments module, as a weak typed argument. We can't pass a string though. Strings in M2000 are like strings in basic, need a $ at the end of variable name.
What we see here is the Long a which make a 32bit sign integer a or if a exist then convert it to this type of integer.
In module DynamicArguments1 we test the type of argument and we can prepare X to be the same type. So we have a way to test type before assign it to a value. (we can use number from stack of values, using function StackItem() with no parameter - by default is 1 here).
For modules, the module code is responsible to drop values from stack (functions called in expressions have new stack, so if we leave something, then this lost at return). Stack for values used for returning values (any number) from modules, with any way we can think for safe passing (maybe a starting value as a count number, for values after that)
About conversion to Integer. There are two ways, but one way used for stored a number to a variable, if variable is a Long (32 bit) or an integer (by using % in the name, which is a double inside). Round function with 0 decimals works like this: 5.5 became 6, 5.1 became 5 and -5.5 became -6, -5.1 became -5 (less than -5.5). Int function change 5.5 to 5 same as 5.1 and -5.5 same as -5.1 to 6.
B%=2.5*3
Print B% \\8
Print B%*2 \\ 16
Print (2.5*3)*2 \\ 15
Print Int(2.5*3)*2 \\ 14
Print Round(2.5*3,
0) \\
8Print B% \\8
Print B%*2 \\ 16
Print (2.5*3)*2 \\ 15
Print Int(2.5*3)*2 \\ 14
Print Int(2.5*3) \\ 7
\\ round performed for Long too
Long A
A=2.5*3
Print A \\ 8
Automatic conversion exist from string to number, only when we assign value using simple = for double type variables (by default any name without % and $ is a double variable).
As we see in this example we can check the input value and do conversion by code.
(x) in Module definition is a macro for interpreter, and using it place a Read X as first line in the module.
\\ error - no
automatic convertion to number
\\ long a="1212"
\\ not error - convertion to number
a="12345"
Print a
\\ long a="1212"
\\ not error - convertion to number
a="12345"
Print a
Module testme (x){
Print x
}
\\ error no automatic convertion to number
\\ testme "12345"
testme 12345
\\ we can change definition even when we run this module
Module testme {
If Match("S") then { read N$: X=N$ } Else read X
Print X
}
\\ now work witn internal automatic convertion;
testme "12345"
testme 12345
From M2000 console execute Help Match( to find what other letters exist in M2000 for types.
So until now we see that M2000 is mostly weak type, but we can check in execution,in code, in any pass to module or function the type of variable.
Automatic Conversions exist for specific type of variables (integer and long, using round) and for double (string to number). Passing long can be done using ! before number expression.
All numeric expressions evaluated using double type as accumulator. Operators such as +=, -=, *=, /=, -!, ~, ++, -- work in same type (long or double - integer is double inside, but the result rounded to zero decimals)
A variable A maybe is a double, or a pointer to Array or Stack or Inventory, or a Group, or a COM object, or a Gui Object (are COM objects too, but internal to M2000). We can check the name of it using Type$().
So we see that M2000 passing with a dynamic way values to modules (and functions and subs), when types can be checked or not.
There is a special object named Group that may have a value, a number or a string. If we wish a string value then we have to define group with a name with a $ as last character. For those groups there is a second name, the same name without $ at the end. And this needed for access numeric values in group:
Special function Value make Alfa to return a value. If we want to take the object we have to use Group$(), which extract a copy of group. So Alfa is not just a pointer to object, is the only pointer to object, so is "the" object. Group objects may have references to them, but not pointers to them. We can use This as pointer to group inside a group, or we can make a reference to Group or a copy to Group by using This and call some external module/function (a global one perhaps).
Group
Alfa$ {
anynumber=100
Value {
="George"
}
}
anynumber=100
Value {
="George"
}
}
Print Alfa$, Alfa.anynumber
\\ Groups are objects prototypes
Beta$=Group$(Alfa$)
\\ so now we have a second object
Print Beta$, Beta.anynumber
Module TakeMe (x){
Print x$
}
TakeMe Beta
\\ works using x$ too, because interpreter read stack and then decide what to do
Module TakeMe (x$){
Print x$
}
TakeMe Beta
Scope for variables in M2000 are local, except we explicit declare as global. A local/global variable in a module shadow a global one. Global variables can alter value from code by using <= and not =. If there is a Global Const we have to explicit declare a variable as const inside a module. If a module exist in a module then it can't see parent's variables.
There is a way for preparing a variable for a module before a module exist:
m@c=10
Module m {
Print c
}
m
For groups a group variable is "global" to group, but not in groups inside group. Before dot we can use This. There are no class variables, all are group variables.Module m {
Print c
}
m
Group
Alfa {
m=500
Group Beta {
m=100
module something {
print .m
}
}
module Zeta {
Print .Beta.m \\100
Print .m \\ 500
.Beta.Something \\ 100
}
}
m=500
Group Beta {
m=100
module something {
print .m
}
}
module Zeta {
Print .Beta.m \\100
Print .m \\ 500
.Beta.Something \\ 100
}
}
Alfa.Zeta
Now change module Zeta with this (using This is the same as without it):
module Zeta {
Print This.Beta.m \\100
Print This.m \\ 500
This.Beta.Something \\ 100
}
We see the same result. From Beta (which is part of Alfa) Alfa.m isn't visible. This is normal, so we have groups without dependencies in parent groups. This in not hold with groups that are Properties. There we can Link parent variables to local variables, and the linking performed by Link command.
Group
Beta
{
Private:
m=10
Public:
Operator "++" {
.m++
}
Group Prop {
value {
link parent m to m
=m
}
}
}
Private:
m=10
Public:
Operator "++" {
.m++
}
Group Prop {
value {
link parent m to m
=m
}
}
}
Print Beta.Prop \\10
Beta++
Print Beta.Prop \\11
The above construction can be written like this:
Group
Beta {
Operator "++" {
.[Prop]++
}
Property Prop { Value } =10
}
Operator "++" {
.[Prop]++
}
Property Prop { Value } =10
}
Print Beta.Prop \\10
Beta++
Print Beta.Prop \\11
We can't change Prop, if only Value exist, but we can change from a module inside Beta the .[Prop] variable, because is private, but a variable. If we wish to change Prop using Beta.Prop=100 then we have to declare it in Property:
Group
Beta {
Operator "++" {
.[Prop]++
}
Property Prop { Value, Set } =10
}
Operator "++" {
.[Prop]++
}
Property Prop { Value, Set } =10
}
Print Beta.Prop \\10
Beta++
Print Beta.Prop \\11
Beta.Prop=400
Beta++
Print Beta.Prop \\ 401
A set clause in Property make property writable. Prop is a group so we can define some other properties, methods or and functions:
Group Beta {
Operator "++" {
.[Prop]++
}
Property Prop { Value, Set } =10
Group Prop {
Module Half {
Link parent [Prop] to prop
prop/=2
}
}
}
Beta.Prop=400
Beta.Prop.Half
Beta++
Print Beta.Prop \\ 201
Groups can be written to Arrays (and other containers, Stacks and Inventories). Beta group give a copy to A(1). If A(1) hold a Group then the next time we give a new group, replace the old one (and because there is no second pointer to Group object then the old one destroyed)
Dim A(10)
A(1)=Beta
Beta.Prop=2
Print Beta.Prop \\ 2
Print A(1).Prop \\ 201
A(1)++
A(1).Prop.Half
Print A(1).Prop \\ 101
So we see that we make groups with "types" as we wish. Groups are "Typeless" objects. But anything inside group maybe is number, or string or a container for that.
We can use weak references for using something which can't make a reference as an array item (references are for names only, one name for an array):
K$=Weak$(A(1))
\\ K$. is a weak reference
K$.++
K$.Prop.Half
\\ we can't use K$. Prop in right expression out of Eval()
Print Eval(K$.Prop)
In A(1) we have a closed group (all members are in group closed). In Beta all members are members of module too. So if we make a X as variable in Beta, and Beta exist in module A then A.Beta.X is a variable of module A too, and we can get a reference only for this.
As we see K$. is a weak pointer. (We can see what is in K$ by using Print K$). We can pass as weak reference Beta (an open Group)
K$=Weak$(Beta)
\\ K$. is a weak reference
K$.++
K$.Prop.Half
\\ we can't use K$. Prop in right expression out of Eval()
Print Eval(K$.Prop)
\\ K$. is a weak reference
K$.++
K$.Prop.Half
\\ we can't use K$. Prop in right expression out of Eval()
Print Eval(K$.Prop)
Δεν υπάρχουν σχόλια:
Δημοσίευση σχολίου
You can feel free to write any suggestion, or idea on the subject.