Παρασκευή, 24 Νοεμβρίου 2017

Dynamic & Lexical Scope in M2000, and lifetime of entities

Dynamic Scope
This example run in a module
(print 3 and 1)

\\ x isn't global
\\ but x is visible from subs in this module
x=1
f()
Print x
sub g()
      Print x
      x=2
end sub
sub f()
      local x=3
      g()
end sub


Lexical Scope
This example run in a module and return 1 and 2. We need to make Global x and Global g(), and also to change a global value we need to use <= and not a  simple =
(print 1 and 2)

Global x=1
Function Global g {
     Print x
     \\ for globals we have to use <=
     x<=2
}
Function f {
      \\ x=3 make a local x, or if x exist as local then change value to 3
      \\ but local x make always a new variable
      \\ so Local x,x,x make three x and last one is visible.
      Local x=3
      Call g()
}
Call f()
Print x


Lexical Scope using only one global x
(print 1 and 2)

Global x=1
Function f {
      Function g {
           Print x
           \\ for globals we have to use <=
           x<=2
      }
      x=3
      Call g()
}
Call f()
Print x

Dynamic Scope using functions
(print 3 and 1)
We use Call Local which call functions with parent name. So each time scope is the same. Here x=3 in Function F can't make a local x, we have to use Local x=3.
x=1
Function g {
     Print x
     x=2
}
Function f {
      Local x=3
      Call local g()
}
Call Local f()
Print x

Dynamic Scope using Global to shadow global


\\ Dynamic scope
Global b=5
Function Global foo {
      a=b+5
      =a
}
Function bar {
      Global b=2
      =foo()
}

Print foo()
Print bar()


Print 10 and 7

Lexical (Static) Scope  (just not using global b in bar)


\\ Static Scope
Global b=5
Function Global foo {
      a=b+5
      =a
}
Function bar {
      b=2
      =foo()
}

Print foo()
Print bar()

print 10 and 10




Lifetime of an entity

Entities are named values/containers (we say variables, but include constants) and named code
A name scope begins where first time interpret assign value, or perform a declaration which automatic assign zero/empty_string/empty_array values. This is not true for subs, because subs names searched once from the bottom, when we call them, and in an another call interpreter use a hash table to get entry point for them. The same happen for labels, numeric or named, for Goto and Gosub (simple routines, exactly like Basic's), interpreter use a hash table to find them. These hash tables cleared after the use of an Inline statement. Lables/Subs can called from any direction. Block {} of code are exit using goto.


Function something {
      \\ test simple goto (else 200 is same like else goto 200)
      \\ and simple routines using gosub
      x=1
      {
            gosub alfa
            {
                  gosub alfa
                  {
                        gosub alfa
                       if x>1 else 200
                  }
                  Print "ok2"
            }
      200 Print "ok3"
      210 x++
      220 if x<5 then loop
      }
      Print "ok4"
      exit
      alfa:
      Print "ok"
      Return
}
Call Something()






A clear command inside a module/function clear variables from that module/function and newer. A clear command in manual mode clear every variable, global and static, and perform garbage collection. Using command Flush Garbage we can perform garbage collection without clearing variables.
Named entities have lifetime of parent (entity which defines entities). Global entities have same lifetime as normal.

Threads (have no name but a handler) normally have also lifetime same as parent module/function, except for those threads that starts in a thread, so they have no parent.

Com external objects, or internal Com like GUI elements (Forms, Controls), and properties for them (early bindings to objects), have also life time  same of parent. Pointers for them are "smart" which means that one pointer only exist for one object, and every time we need a pointer as a copy of pointer, we get just  a reference to first name which hold the actual pointer
Groups (standard objects of M2000), are value types and can be named, so have lifetime of parent, or can be copied in containers, and then can be leave until a new group replace them or when containers get out of scope.
Arrays, Inventories (inventory, an association list with hash table), Stacks are three type of containers, and handled by pointers. Arrays are two kinds, those arrays that can be copied, and those that can be passed by pointer. In each case, using stack to pass an array always passed a pointer to array, end when we pop the array we get a pointer to a copy of array or a copy of pointer to array depends of where we read, if we read pointer to a name with parenthesis "()" then we get a copy (shallow copies, for containers in containers, but copies for groups in containers), else if we read a simple name (without ( or  % or $ as last symbol) we get pointer to array. There is no null pointer for containers. There are empty containers, or a starting value of a double, and then this can be change to a pointer to a container, and can get any type of container, but no a double value (is a Missing Object error)

Event objects are value types, so we get always a copy, and these leave like any named entity. Events hold code for functions, and sometimes we want some functions to run in a group context because are functions belonging to groups, or in module context, changing function to act as module, replacing the name appropriate and automatic (Using a Lazy$() function, see example below), we say that these functions utilize another context (parent group or parent module). If we get a copy of event and later call event (means raise event), calling all functions inside (multicast), and if some of them utilize another context, maybe we get an error if any of these functions is out of context.

Lambda functions may have closures as local for them, and any call of them (recursive call using Lambda() or lambda$() as name of function). Lambda functions are value types, we can copy them. Containers as closures copied by pointer (so they passed by reference). A lambda function has own context as a black box. Each lambda function has a name without parenthesis to use it as an entity and a name with parenthesis to call the included function. If we pass by reference a lamda function, as function reference then we miss the object, and all variables inside lambda object. If we pass by reference the lambda object then we get all, closures and the included function. If we pass by value we get a copy of all (except those closures that are pointers, so we get a copy of that pointers)

Lifetime for a Bag for static variables. Each call from a parent to a child may hold a bag of static variables. These variables can be cleared if parent bag cleared. Functions that are called by Events objects that are used by forms, feed global bag,so we have to clear global bag manual from M2000 terminal.
Static variables created per calling object (first object is the terminal's execution object, and all other are children, or children of children). So static variables can be erased if a clear command executed at parent, except for terminal, which has no parent, and clear then clear all static variables.

Static modules/functions are those in level 0, and are visible in terminal mode. They can erased using New, erase modules/functions list, or Start (warm reset for M2000 runtime environment), or using remove for removing the newest of them. They can change definition using Set Load (like load from terminal) or using load from terminal. Also we can change definition by editing it as it run, but edited definition can be used next time, or when we call a sub or using goto to a label (which first time we use in the first code, after edit command)

A Sub is named code in module or function, and uses parent context, all parent context is visible in sub, but also can make temporary variables and modules/functions. Subs have no static variables, they use parent static variables.
A For object {} block define a block for temporary definitions. A for This {} block can be used any where for temporary definitions.


Scope
Static A after local A is an error, we can't mix static and local with same name. Local A after static A has same effect but no error happen. If we perform A++ then local A change, not static A, and always we read static A first.


A Local A shadow Global A, for lifetime of local one. A second Local A shadow old Local A, for life time of second one.

Any local variable is visible in module/Function/Sub/For object block, where defined
Any Global variable is visible from where we define it., but we can shadow it with a local with same name.

If we use global const x=123456 then we have to use inside a child module "const x" without value to make a reference as local x to prevent a new local by using X=10

Check Visibility by code

We can check if a variable is visible using Valid(). We can check if a module/function exist using module$(). We can't examine if a sub exist.
local modules/functions are not visible by child modules and parent modules. This hold for variables.
We can decorate a module by replacing a inner module definition at calling, and for that call only, using a parent module. Module Decoration is a way to handle code inside module at calling. functions and subs can't decorated.
We can load libraries, as local/global modules/functions. Loading as global may we change existing globals, if we use Set Load nameoledib. To get global without altering global with same names, we have to explicity define modules as globals in library. So we load library as local and get modules as globals, shadowing any global with same name.

A group in a container has all data encapsulated (closed for altering). When we use it, interpreter open it using a hidden name, and at the end all data closed again. We can define private and public members for groups. Also we can define class members (members that is no actual members of group, but act as members). A local member (variable) in a closed group always intialised at the opening, and never save value, but can alter value in lifetime of opening. A local member in a named group (not in container) never be referenced, among other members when we pass group by reference (and this is a way to check if a group is a reference or not). A reference of group is a new group which have all members passed by reference, on another group. (it is not a pointer to group). In a reference of group we can add members, but this addition wasn't performed in first group)/

A SuperClass is a group with an internal pointer to a closed group, with no members. When we asign a superclass or a group with superclass in a name then performed a copy of all members of superclass (except those members that we defined as unique to super class) plus a setting to group internal superclass pointer to that superclass. A group may have many sub groups and each sub group may have own super class. Superclasses can't hold pointer to any superclass. When we use For SuperClass {} to access superclass, interpreter open superclass, but always allow one time opening,and if second time happen before first closed then an error occur. Superclass used for holding common members. If no pointer to superclass exist then superclass removed.

A Class definition is a function which return a group. A class is always global, except those class definitions inside classes or groups which are member to group.

Group A {
      X=10
}
Module alfa (&B) {
      Group B {
            Y=30
      }
      Module delta (&C) {
            Group C {
                  Module XY {
                        Print .X*.Y
                  }
            }
            C.X/=2
            Push C
      }
      delta &B
}
alfa &A
A.X+=100
Print A.X
Read AA
AA.XY
Rem : list  \\ unhide List to see list of variables)



Example for changing code using editor as code runs.
edit a
(open editor for module A)
inline "edit "+module$
goto 100

(press esc to exit)
When run this, using A as command in M2000 terminal, you get editor with context above. So delete all and leave first line empty then write from second line 100 Print "Ok"
\\ one line left
100 Print "Ok"

(Press Exit) and now you get Ok because interpreter search from second line for label 100, and found it. If no label 100 exist then goto used as exit. A goto to a not exist label is an exit (same for a Gosub to a non exist label, but Gosub to a non exist sub is an error).



\\ Example of using user event in a Form
\\ use clear before enter this module to clear all conainers for static variables
clear
Declare alfa form event aa
X=1
Function that {
      Stack
      Static Y=100
      ' show all messages which they don't use arguments by reference
      ' using Lazy$() we push this function at the same lexical scope as
      ' parent, which is the same for X, so X is visible
      X++
      Y++
      Print X, Y

}
Function that1 {
      Static X=10000
      Stack
      X++
      Print X
      if X>10020 then {
            Drop : Read &Form1
            Print Type$(Form1)
            Method Form1, "CloseNow"
      }
      ' get same message
      ' but X isn't visible
      refresh
}
Event aa New Lazy$(&that()), &that1()
method alfa,"show", 1
Declare alfa nothing