Κυριακή 15 Νοεμβρίου 2020

A structure like record in C# 9.0 object in M2000

Reading https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-9, I found record type that can be emulated in M2000:

In the example bellow, we have a class Person, with 2 readonly properties (they have only the value part, the set part is omitted). A property is a group, and the definition is a syntactic sugar, which define a private variable, so for name$ the private is [name]$  (characters "[" and "]" can be used for names).

We make a synthesized property, the tuple. A group inside a group (a class make a group) can't read parent values, except we explicitly declare and only for parent some linked variables (we can link functions too, but no modules). Here we link the private members. Links are references. So the tuple (name$, last$) return the values of parent [name]$ and [last$].

We make an operator "==" for equality. In the expression person == brother, the calling to operator done to brother, so the Read other is the by value pass of person. We check to see if we have the type Person, so if we don't we get error. We can use push other.[name]$=.[name]$ and other.[last]$=.[last]$ which directly check private variables, because the reading group has the same type (Person) with current group, so the private members of group other exist also as public for this function (operator "=="). The push statement pus the boolean number to the current stack, and this stack reading from the expression evaluator (expect one value, otherwise we get error).

Info: Stack in M2000 is for values only, has nothing to do with return or process stack. A module pass the current stack to any calling module. A function (as those we make with Function {} statement, and the lambda functions) start with a new stack (and any parameter) when call it from expression. There are static functions (created at the end of the code (in any module or non static function) with Function / End Function) which use the current stack and called with @ before the name. Non static functions and modules can change code dynamically (for the next execution), static functions and subs can't change code. Also static functions and subs haven't a namespace, the have the same where they belong. So we have to use Local to make local variables. In modules and standard functions, and lambda functions, all variables are local by default to the name space which computed from the name and the order of execution. To finish this info, we can use GOTO and GOSUB inside module's boundaries (also standard functions). A Gosub call a code by  number or alphanumeric label, and expect to find a bare Return statement, using same namespace, same stack and without introducing local variables (it is just some line of code which we want to execute and we want to not repeat it).

To execute this example: Run m2000.exe, write Edit A, press enter, paste the code, press Esc and write A and press enter. You can save it using Save CSharpLike  so after that give a New or press Break key, and the give a Load CharpLike and with Ctrl+N you get the list of modules in ChsarpLike.gsb (the name of file, which you just load), and this list has one module the A.


Class Person {
      // read only
      property name$ {value}
      property last$ {value}
      // a synthesized property
      group tuple {
            value {
                  link parent [name]$ to name$
                  link parent [last]$ to last$
                  =(name$, last$)
            }
      }
      operator "==" {
            // check the type of other
            read other as Person
            push other.name$=.name$ and other.last$=.last$
      }
Class:
      // class: part. Here anything exist only at contruction
      // .[name]$ is the private variable under .name$ property
      module Person (.[name]$, .[last]$) {
      // no code here,
      // (.[name]$, .[last]$) is a syntactic sugar for statement Read .[name]$, .[last]$
      }
}
Function FirstName(name$) {
      group fake {
      // groups have no types
      // we can use type: Person if we wish to add one type manual
      // or we can put a list of type names: type: Person, Student
      private:
            [name]$=name$
      }
      =fake
}
Function One(what$, value$) {
      // no need to change value$ to  something like "{1}" in format$()
      // because a group definition executed in the same name space where belong,
      // for right expressions, so =value$ read the value$ of function One()
      // for left add a new name space:
      // the expanded name of this namespace with name fake
      // internal interperter compute the namespaces by the calling order, to be short for faster execution
      a$=format$({private: [{0}]$=value$ }, filter$(what$,"$"))
      // this is a way to pass a string as type,
      // not a class type, but in code type, if we use standard string,
      // but here isn't that case, we only pass a computed definition
      group fake type a$
      =fake
}
person=Person("Bill", "Vagner")
Print person.name$, person.last$
// export values as tuple
(first$, last$)=person.tuple
A=person.tuple
Print first$, last$, A#val$(0)=first$
sister = person with FirstName("Susan")
(first$, last$)=sister.tuple
// we can print tuple (not converted to string, Print statement make an iterator automatic and get each item)
Print first$, last$, sister.tuple
brother = person with one("name$","Paul")
(first$, last$)=brother.tuple
Print first$, last$
Print person == person
Print person == sister
Print person == brother



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

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

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