Παρασκευή, 12 Ιανουαρίου 2018

Range Object (M2000 code) rev 2


This is an implementation of a range object, a group which used to check if it has a value in a specific range, and can be use for iterations.
What benefits can we get from this object?
First range object works for numbers and for strings. We can define the nest function as a lambda function. Also we check if something is in range, using only the limits, or using next function also.

Intresting Points
Using the function we have to scan values until we found it or until end the range scan. Check private function InspectNum(), to see how scaning can be done inside object. We make as copy of This, then we invoke a reset and start a While loop scanning all items. Object This always return object type, not value. Check public function Skip, where we make a link from This to a new name. The new name has a reference to This, so now this name, if used as variable, return value, so change the state of This.
We have two properties, Val and Value$ (this name is blue, becaue is known identifier for interpreter, but we don't have a problem here, we can use it).  We can make a Value property, because each property like Value$ has also name Value for it.  Properties are Groups in Groups (objects in objects) and name Value can be used to access some other properties, or variables, or functions, or modules inside it. Although here we use them only as read only properties, we have a block {value} only. Intepreter take the definition and replace it with a Group definition with a Value block. All properties change to public and leave public flag to true for the remain of members inside group definition. Automatic interpreter make a [val] and a [value]$ as private variables. So we can handle properties as variables, inside group.

Also see that range object produced by a Class function. A class function has a group definition and return that group. We have three parts, using labels, the private, the public and the class. The class label tell to interpreter that all members after aren't copied to final group. So in that part we have the constructor, a module with same name as the class name. When we call class function, a new stack for values, with any argument passed to module. So there we check with Match() function what we have, and we want this only for two first items. In M2000 we make the concept of overloading in the same module/function, inspecting types of arguments. Also note that a Read ? is a way to read optional something. This means that if stack is empty, that means optional. We can pass ? as value in a call and that means "no value", but we have to read it in a Read ? statement.

Range group has a Value {} part. Any group with a Value {} means is a property. So we can make properties out of Groups too. Because we have no Set {} definition, property has no assign function. This is true for inner properties Value$ and Val. If we try to assign something we get error. But we can use Let statement. Let statement is two statements, a Push and a Read, a Push to stack of values and a Read from stack of values. When a Group read from stack expect group and copy all items of that group to it. Groups in M2000 are values (not reference types), and assign to a named group by reading or using = if no value block defined, means we merge a copy inside group.

After we make a range group we can use it like a variable, We can push it to stack, to return it or as an argument, using Group() function (return always group from a named group, without looking Value part), and that new group is a nameless group, a float group, and we can put that kind of groups in containers, and we can make them with a name if we wish too in a later point, as a copy always. We can pass by reference too using & before name. We can't push back reference, because references in M2000 are not pointers to objects, but a weak reference to be resolved from a Read statement (we use & and a name to make that name as a reference). So pushing a reference back (at exit of a module or function), actual group destroyed, so there is no way for Read statement to resolve the weak reference. We can also put a group as a closure in a lambda function, to be used from this function only. This closure deleted when lambda function deleted. A lambda function is also a value type. So when we assign a lambda function to a new name, then a copy of closures happen, so if a closure is value type, then we get a copy, here a copy of a group, else if we have a reference type, like a container (stack, array with no brackects in name, and inventories) then we get a second pointer only to same object. To pass a group to multiple lambda functions, we have to put it in a container. This is the way to use a pointer to Group, through a container. 

Group using with Events/Threads (Skip this paragraph, is for curius only)
When we use a group from a container, then interpreter make a named group, as a copy of "float" group, and at the end a float group produced and stored back. If a thread or an event from gui, or a COM object, use the same container, and same group before the first close, then take the last "Ready" group, save them, and at the end of event, we still work without problem, because we have the group "outside" of container. If Group has members which are reference type then maybe "Ready" group has changes. A group in container has all members in a private list. A named group has all members as individuals, in global variable list, and global module/function list. These global lists, have keys, to prevent access, but they have one way to expand, only by append, and one way to reduce, only by droping last append items. When an event produced use these lists, adding anything, as code need to do, and at the exit drop them. Because M2000 is written in VB6 there is no way to run code in parallel. Always a new event wait to execute, and is the way M2000 is written, to insert events and threads in specific points. To allow an event to run inside an opening of a group in an container, we have to use Wait (a kind of DoEvents). A thread work using a private Task Manager inside M2000, and because they don't reduce lists (the global for variables, and modules/functions), we don't make inside them declarations (we can but is unsafe), so we use only static variables, and thread's stack of values, and these variables are saved to thread for the next run. Aa thread is code which run in intervals, we can produce as many as we wish, and have two plans, the concurrent, where simple statements executed and task manager skip execution (swiching) to another thread, or sequential where all statements executed and then task manager allow other thread to run. In concurrent plan, blocks of code in { } run always without switching thread.

New version for work nice for strings, as for numbers. Also work nice the Has() function too.

\\ Range object ver 3
\\ Constructor: Range( value_From, value_to [, lambda function])
\\      value types numbers or strings, lambda function must be the same type also
\\      lambda function produce the next item
\\  Properties
\\      Value$  : used if we have a range of strings
\\      Val : used if we have a range of numbers
\\ Function
\\      Has(string or number [, True]) : return True if range has value,
\\      if optional second argument is True then scan all values using lambda function, before compare
\\ Method
\\      Reset
\\            reset val or value$ to match the first item of range
\\      Skip number
\\            skip a number of items, and maybe change to new item, or set state to false
\\            skip can used before using Value, see below, or in a while loop using Value.
\\ Value
\\      We use value of Range, value of object, as a state flag, and this call lambda for next item
\\            state False means object go out of range. Use Reset to begin again. Use Skip to skip some items.
\\            We use While RangeObj { } to walk through all items


Class Range {
Private:
      from$, to$, begin=True
      fromnum, tonum
      option=1
      Function Inspect (k$) {
             copy1=This
             copy1.reset
             While copy1 {
                  if k$=copy1.value$ then =True : exit
            }
      }
      Function InspectNum (k) {
             copy1=This
             copy1.reset
             While copy1 {
                  if k=copy1.val then =True : exit
            }
      }
      NextStep$=Lambda$ ->{Error 500}
      NextStepNum=Lambda ->{Error 500}
Public:
      Function Has {
                        if .option = 1 Then {
                        Read what$
                        Read ? UseFunc ' optional
                        If UseFunc then =.Inspect(what$) : exit
                        If what$<>"" Then {
                           if .from$<=.to$ Then {
                                     = what$>=.from$ and what$<=.to$
                              } else {
                                    = what$>=.to$ and what$<=.from$
                              }
                        }
                  } Else {
                        Read what
                        Read ? UseFunc ' optional
                        If UseFunc then =.InspectNum(what) : exit
                        If .fromnum<=.tonum then {
                              = what>=.fromnum and what<=.tonum
                        } else {
                               = what>=.tonum and what<=.fromnum
                        }
                       
                  }
      }
      Property Value$ { Value }
      Property Val { Value }
      Module Skip (k){
            k=abs(k)
            if k Else Exit
            Link This to M1
            While k {
                  k--
                  If M1 Else Exit
            }
      }
      Module Reset {
            .begin<=True
            .[Value]$<=.from$
            .[Val]<=.fromnum
      }
      Value {
            If .option=1 Then {
                       Try {
                              m$=.NextStep$(.[Value]$)
                              if Error Then Exit
                              if .begin Then .begin~ : =True : Exit
                              If abs(compare(m$, .to$)+compare(.from$, .to$))=0 Then : Exit
                              .[Value]$<=m$
                              =True
                        }
            } Else {
                       Try {
                              m=.NextStepNum(.[Val])
                              if Error Then Exit
                              if .begin Then .begin~ : =True : Exit
                              If abs(compare(m, .tonum)+compare(.fromnum, .tonum))=0 Then Exit
                              .[Val]<=m
                              =True
                        }                  
            }
      }
      class:
      Module Range {
             If match("SS") Then {
                  .option<=1
                  Read .from$, .to$
                  Read ? .NextStep$
                  .[Value]$<=.from$
            } Else.if match("NN") Then {
                 Read .fromnum, .tonum
                 .option<=2
                 If empty Then {
                       .NextStepNum<=if(.fromnum<=.tonum -> (lambda (x) ->x+1), (lambda (x) ->x-1))
                  } Else {
                              Read .NextStepNum
                  }
                  .[Val]<=.fromnum
           } Else Error "not supported stack types:"+Envelope$()
      }
}
NextChar$=lambda$ (x$) -> Chrcode$(Chrcode(x$)+1)
PrevChar$=lambda$ (x$) -> Chrcode$(Chrcode(x$)-1)


M=Range("Z", "A", PrevChar$)
Print M.Value$
Print M.Has("K"), M.Has("a")
While M {
      Print M.Value$;
}
Print  "",,       \\ break ; set next line using columns
M1=Range(1, 20)
Print M1.Has(500), M1.Has(15)
While M1 {
      Print M1.val,
}
Print
\\ using Let because M1 is a property now, and only LET can change it.
\\ Let a=b is same as Push b : Read a
let M1=Range(30, 20)
While M1 {
      Print M1.val,
}
Print
let M1=Range(30, 20, lambda (a)->a-2)
While M1 {
      Print M1.val,
      M1.Skip 3
}
Print
\\ M1 is false now
While M1 {
      Print M1.val,
}
For M1 {
      .Reset
      .Skip 2 ' start from 3rd     
}
While M1 {
      Print M1.val,
}
Print
Inventory K=10,40,50,70,1000
Set1=Range(0, Len(K)-1)
Set2=Range(1, Len(K)-1)
\\ we get 10 50 1000
While Set1 {
      Print K(Set1.val!),   \\ using ! we pass index - from 0- not key
      Set1.Skip 1
  
}
\\ we get 40, 70
While Set2 {
      Print K(Set2.val!),
      Set2.Skip 1 ' skip
}
Print
Counter=Range("100", "110", lambda$ (a$)->Str$(val(a$)+2,"000"))
\\ using optional second argument as True (default false) we walk through all range using lambda function
Print Counter.Has("108"), Counter.Has("109", True), Counter.Has("104", True) \\ True false True
While Counter {
      Print Counter.Value$
}