Παρασκευή 23 Νοεμβρίου 2018

Revision 12, Version 9.5

Improvements for types in read statement
From revision 11 we have List and Queue the two types of Inventories, to give anonymous inventories . The List type (the default) has unique keys, the Queue may have same keys. For the default type (List) we can delete any key, in O(1), we can search a key in O(1) and we append key in O(1). We can sort (no stable sort, a quicksort used), but if we delete a key we miss the sort order. For queue type the search of key when two keys are the same, we get the last append. We can delete from position (start from 0) or a number of items from the last appended items. We can use stable sort, so same keys preserve the order of appending. Normally we use Inventory name1 and Inventory Queue name2 to make the two types. But from revision 11 we can use (List:=1,2,3,4), we can use pairs like this: (list:=1:="a",2:="b", 30:="c"). Parenthesis needed if isn't the last parameter in the line, or after a case with one statement, because this definition use a colon character (and colon characters also used for splitting statements in a line).


First example to pass optional a list (default inventory) in a module where we have a static variable of the same type. In module Checkit we have module Checkit.  A module can't call itself without Call statement. Also modules have scope only in module level (can't see anything outside, except global identifiers, and can't see anything in inner modules too). A module called always with same stack of values of the parent. So we can return values to stack, any number and type but the caller has to get them using a Read statement. Generally speaking, the caller to module has to know if module return values to stack. Any variable has to get a value, otherwise we get error (not initialized). A read statement using ? can skip reading for a variable if that variable has a value and there is nothing in stack or is the optional item ? (we can pass optional item like this CheckIt ?).  If we want the first parameter not optional and the second as optional we can use two Read statements, the second with ? character (this say the following is optional), but we have to provide before the read a value, else may we get an error.

So for revision 12 we can check the type of optional value (and for non optional value) for static variables too.

Module Checkit {
      \\ just empty the stack
      flush
      Rem : Clear ' to clear all statics for this module
      \\ we make a module with a static variable as an Inventory (type List)
      module Checkit {
            \\ only the
            static k=(List:=1,2)
          
            Read ? k as list
            Print k
      }
      \\ call module with no parameter
      Checkit
      \\ passing a list
      Checkit (list:=1,2,3,11,22,33)
      Try {
            Checkit (Queue:=1,2,3,1,2,3)
      }
      Print Error$ ' we get wrong object
      \\ try to pass an array
      Try {
            Checkit (1,2,3,1,2,3)
      }
      Print Error$ ' we get wrong object
      \\ try to pass a lambda function
      Try {
            Checkit lambda->0
      }
      Print Error$ ' we get wrong object
      \\ try to pass a group
      Group Pair {x, y}
      Try {
            Checkit Pair
      }
      Print Error$ ' we get wrong object
      Inventory z=1,2,3
      Append z,4,5,6,7
      Delete z, 4 ' break order
      Checkit
      Checkit z
      Sort z
      Checkit z
      Checkit
}
Checkit


These are some test programs too:
a=10
Module Checkit {
      if random(10)=1 then Clear ' erase all statics and variables from this level
      Module Checkit {
            flush
            module Checkit {
                  static k=(Queue:=1,2)
                  Try {
                        Read ? k as queue
                  }
                  Print type$(k)
                  Print k
            }
            group alfa {
                  x=10
            }
            z->(alfa)
            Checkit
            Checkit z
            Checkit (Queue:=1,2,3,1,2,3)
            Checkit (List:=1,2,3)
      }
      Checkit
}
Checkit
Print a ' a exist always



This example show how we can use pointers to groups and pass to a subroutine optional. Optional means that we have provide another pointer before we read it as argument (or because it is optional we get the one we have provide). Subs have same scope as the module from where we call them. We have to use local to make local identifiers. But we can't shadow a group if we want to have it as optional (not the pointers, and that show this example). When we make a group using Group, say Group b2 {x=20} we get two identifiers, the b2 as group and the b2.x as the member. When we use b2.x the interpreter not know that this is a member of this group. When we use b2 which have an internal list of members so then the x has meaning as a members. For modules and functions inside groups also exist as separate identifiers but they have an internal field which tell to interpreter that THIS is the name of the group which they belong. Private members of a group can called only from these modules and functions because they are hidden from module scope where the group exist, and exist in the group namespace, which THIS provide. To simplify this here is a small part of a program:

Group alfa {
      private:
            x=10
      public:
      \\ we can use Print as module name
      Module Print {
            Print .x
      }
      Module IncX {
            .x++
      }
}
alfa.print    ' print 10
Print valid(alfa.x)=false
alfa.incX
alfa.print   ' print 11

The interpreter make alfa (if not exist, else add/replace these members, if not final clause are before module statement), make hidden alfa.x,  and two modules, alfa.print and alfa.incX
From these modules .x is the same as This.x and isn't hidden. If we want to assign a value we have to use <=, so .x<=100 change private alfa.x to 100, but .x=100 make a local to module, and shadow the member until module's execution finish. If we do this: beta=alfa as last statement we get a copy of alfa in beta. If we do this delta->alfa we get a pointer to alfa, and if we do this epsilon->(alfa) we get a pointer to a copy of alfa. This last copy is a float group, a unnamed group. We can move this group as return value or anything else. So we have two types of pointers to groups, one for named groups (only a weak reference, same as THIS, curried from the pointer) and one for float groups (unnamed groups) which are true pointers to objects (and all members of object are in internal space). 

So this is the example, which we use a Class which return always a float group, so b and z are pointers to float groups (true pointers). We can pass a pointer by reference, but here we use optional so we pass by value. If we change the pointer inside then we have a true pass by value, but if not we have a reference pass, because the new local variable has a pointer to same object, before the call.

Also note that beta() sub called inside alfa() and outside alfa(). The z inside beta() must exist before the call, but it isn't the same, and this depends form the dynamic scope, which introduce the local statement. Anything new identifier in a sub, and this happen for modules and functions, erased after the end of the call. For modules and functions we can use static variables, but not for groups, except true pointers for groups.

module checkit {
      def long counter=0
      class b1 {
            x=10
      }
      \\ b is a pointer to a new group from b1()
      b->b1()
      Try {
            beta()
      }
      Print Error$ ' z not found in scope, we can't use it as pointer
      \\ z is a pointer to a new group from b1()
      z->b1()
      \\ now z exist
      alfa() ' 11 - use of internal pointer to group
      beta() ' 11
      Print b=>x=10 ' true
      alfa(z) ' 12
      alfa(z) '13
      beta() ' 14
      alfa() ' 11  - use of internal pointer to group
      Print b=>x=10 ' true
      alfa(b) '11
      alfa(b) '12
      Print b=>x=12 ' true
      Print z=>x=14
      beta() ' 15
      Print z=>x=15
      Print counter

      Sub alfa()
            \\ we make a local variable b
            \\ we assign a fresh group
            \\ then we read optional a pointer
            local b : b->b1() : Read ? b as pointer
            \\ now we make a second local variable
            \\ we assign a pointer
            local z : z->b
            beta()
      End Sub
      Sub beta()
            \\ increment member x
            z=>x++
            Print z=>x
            \\ subs have same scope as the module from where called
            counter++
      End Sub
}
Checkit


In the following example we see how we can use a pointer to call three subs. One sub make a group from the pointer (a copy) and use it (we have to use a name not in scope in module). Another sub get the same input as a pointer (which is a pointer), and finally we pass the pointer by reference.


Module Checkit {
      group alfa {
      x=10
      }
      b->alfa
      checkme(b)
      Print b=>x=10
      Print alfa.x=10
      checkme2(b)
      Print b=>x=11
      Print alfa.x=11
      checkme3(&b)
      Print b=>x=12
      Print alfa.x=12
    
      \\ sub is like End if execution find it
      sub checkme(a as group)
            print type$(a)
            a.x++
            Print a.x
      End sub
      sub checkme2(a as pointer)
            print type$(a)
            a=>x++
            Print a=>x
      End sub
      sub checkme3(&a as pointer)
            print type$(a)
            a=>x++
            Print a=>x
      End sub
}
Checkit


The same program can be written like this using modules. We can place modules at last lines, but we have to send the flow to them to register, so this happen using a Gosub to a label and we have a simple Return (like in Basic).

gosub modules
group alfa {
x=10
}
b->alfa
checkme b
Print b=>x=10
Print alfa.x=10
checkme2 b
Print b=>x=11
Print alfa.x=11
checkme3 &b
Print b=>x=12
Print alfa.x=12
End
modules:
module checkme (a as group) {
            print type$(a)
            a.x++
            Print a.x
}
module checkme2 (a as pointer) {
            print type$(a)
            a=>x++
            Print a=>x
}
Module  checkme3 (&a as pointer) {
            print type$(a)
            a=>x++
            Print a=>x
}
Return


The same program can be written as this (we place the statement Read inside the code of each module). The previous program use a list of arguments in module name, and this converted to a Read statement inside code (so this is the real form)

gosub modules
group alfa {
x=10
}
b->alfa
checkme b
Print b=>x=10
Print alfa.x=10
checkme2 b
Print b=>x=11
Print alfa.x=11
checkme3 &b
Print b=>x=12
Print alfa.x=12
End
modules:
module checkme {
            Read a as group
            print type$(a)
            a.x++
            Print a.x
}
module checkme2 {
            Read a as pointer
            print type$(a)
            a=>x++
            Print a=>x
}
Module  checkme3 {
            Read &a as pointer
            print type$(a)
            a=>x++
            Print a=>x
}
Return

So now we can alter this to include optional Read. Because a module has own namespace, we don't have to think about names and shadowing from the caller scope. So now we can make a group a and read it if exist (we get a merge of the existing a to the input a which we read it, see checkme). Optional we don't have in checkme3 because is a by reference call. A by reference call put a string in stack, which is the weak reference, and the Read statement resolve it. Here the refence is only a pointer to group. If we change the pointer this change to the caller's variable too.  If we pass by reference a group (not a pointer to group) then also a string placed in stack and from that in Read statement resolved to a group with same name, and same modules/functions (here we don't have any of them), but local to the module we read it,  and make all members as new identifiers (also local) by reference (use the same data). The new identifier of group is not by reference (all items are, except for modules/functions which are copies), so in a created by reference group we can add members, alter modules/functions without altering the reference group. So if we want to attach a module to group temporary we can send this group by reference to a module, then we can use Group name1 {module NewNameHere { ...code...}} and we can use it, to alter some variables, and the private also, and then at the exit the real group was as is before the call but with new values. So by reference for groups (named groups) means by reference for the members with values. Because we can use lambda functions as members of groups we can alter functions if we alter the value of a lambda function, which is a function.


gosub modules
group alfa {
x=10
}
b->alfa
checkme
checkme b
Print b=>x=10
Print alfa.x=10
checkme2
checkme2 b
Print b=>x=11
Print alfa.x=11
checkme3 &b
Print b=>x=12
Print alfa.x=12
End
modules:
module checkme {
            group a {
                  x=100
            }
            Read ? a as group
            print type$(a)
            a.x++
            Print a.x
}
module checkme2 {
            group a1 {
                  x=100
            }
            a->a1
            Read ? a as pointer
            print type$(a)
            a=>x++
            Print a=>x
}
Module  checkme3 {
            Read &a as pointer
            print type$(a)
            a=>x++
            Print a=>x
}
Return









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

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

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