CLIP home page

#### Extensions of language features

new statement "switch" - select 1 from M node

     	switch (expr)
            	case const1 [,const2 [,const3] ... ]
                    	// statemetns
                    case const11 [, .... ]
                    	// statements
                    otherwise
            end

create new command "do switch","endswich" via "do case","endcase"

create new operation ":=@", reference assign:

     a:="a"
     b:=@a
     ? a,b  // "a","a"
     b:="b"
     ? a,b  // "b","b"
     b:=@NIL // remove reference

For an easy work with MAP and objects, the following cycle has been made: (a detailed description of MAP and objects is given below)

     FOR value IN map_var
            ? value
     NEXT

will look through all elements in map_var and put them into value; or

     FOR key_value IN map_var KEYS
            ? key, map_var[key]
     NEXT

will look through all elements in map_var, and the key value will indicate the key of access to current elements of MAP or object.

Macro may consist of more complex expressions than in CA-CLIPPER, including recursive calls of macros. Practically all lexemes, exept commands and operators like for, while, etc., are processed more accurately.

Rational numbers with unlimited accuracy!
Currently, it is able to do only simple mathematical operations -+/*^% on large numbers.
To realize these possibilities, it's necessary to write somewhere in program

      set(_SET_RATIONAL,.t.)

or via the command described in clip.ch

    set rational on|off

The compiler does not support large numbers as constants, that is, when using expressions like

    x:=123456789012345678901234567890,

the compiler will make an attempt to transform this into double, and only coprocessor knows what the result will be :)
To actually use large constants, it's necessary to use VAL() function

    x:=val("123456789012345678901234567890")

It's possible as follows:

    x:=val("1234.567890")
      x:=val("1/3")

STR(val,len,dec) function now supports len, dec parameters of any value. For example:

    ? str(val("7/3"),1000,990)

Also, RSTR(val) function is added, and this gives a string in the form of "numerator/denominator", i.e. this string can be put into val(), and at the same time there will be no accuracy loss during transforming!

    ? rstr(val("7/3")+val("5/6")) // ---->>> "19/6"

Hash code is the number more than 1 000 000 000, as a result of hashing a string to a number by using bit operations. For strings of less than 20 characters, the probability of coincidence of hash codes is approximately 1/1000000. For longer strings, the probability is reduced.

The compiler accepts hexadecimal constants in the form of 0x20, 0x30.

Association arrays (AA) - to understand these, read about OO model. For instance, an ordinary array may be written as follows

      declare m[5]
      m[1]=11; m[2]=12; ....

To get access to elements, a consecutive numbers set is used, and it is limited with the array size. Inserting a new element becomes a problem, and it results in time losses. Opposite to ordinary arrays, elements of association arrays are identified by strings, for example:

      m=map()
      m:asdf=11; m:qwer=12
      ? valtype(m) // 'O'  //object
      ? len(m)     //  2
      ? m:qwer     // 12

Actually, elements of association array are identified by a hash code of string calculated during compile-time. The compiler calculates hash codes of strings enclosed in "`" characters. For example:

    ? m:qwer==m[`qwer`]  // .t.

During run-time, hash code can be calculated ashashstr("QWER")

    ? m:qwer==m[hashstr("QWER")] // .t.

Pay attention to QWER which is written in capital letters, because the compiler does not distinguishes letter cases.
It's possible to get a list of indexes in the associate array via mapkeys(m) which returns the array with hash codes being in m array

    mm=mapkeys(m)

(instead of mapkeys() function, a special cycle can be used - its description is given above)

      ? len(mm) // 2
      for i to len(mm)
        ? mm[i]   //will return something like 1233345677, 124321423
      next

Also, the associate array is characterized by a very fast access to elements, as a necessary element is actually searched in a hash table. And instead of ascan(mm,`QWER`), it's better to use `QWER` $ m // -> .t.

OO model.

Firstly, some words about CA-Clipper built-in OO model. Its OO is based on usual array, and any call like

    obj:attribute or obj:method()

results in the situation when in obj array there is searching of an element whose first element coincides with attribute or method, name, and such search is executed lineary and is practically analogues to

    ascan(obj,{|x|x[1]=="attribut"})

function, which greatly reduces the efficiency of OO model of "pure" CA-Clipper. This, of course, is rather a simplified explanation, but the sense is still that as described.

We believe, now it's clear for what purpose association arrays were made. OO model based on association arrays is faster by an order!

At the same time, there's no need in expressions like

    obj:=tclass(class_name):new()

and in tclass class itself, which raises OO model efficiency by several times.

How could you make your own class? It's very simple:

      function  MyClassNew()
         obj:=map()                // empty object
         clone(MyClass2New(),obj)  // adoption of MyClass2 structure
         clone(MyClass3New(),obj)  // adoption of MyClass3 structure
                                   // if there are coinciding attributes or
                                   // methods, elements of the last class
                                   // become main.
         obj:attribute1:=0
         obj:attribute2:=date()
         obj:method1:=@func1()   // method1 becomes a function reference
         obj:method2:=@func2()   // these functions must be defined in
                                 // prg file
                                 // if methods have been addopted from other classes,
                                 // they will be reassigned to indicated classes
      return obj                 // returning a ready object

      static function func1
          ::attribute1++
      retun NIL

      static function func2(self)
          self:attribute1--
      retun self

Also, we'd like to add two simple rules:

- 1. The attribute is created when something, including NIL, is assigned to it.
- 2. At any moment, in run-time,the method can assign or reassign any function announced in this mudule as static function, or can adopt this function from another object, as a usual assignment of values.

      myObj1:method1 := myObj2:methodX

In what way can objects be used? As in CA-Clipper, or even more simply.

      obj:=MyClassNew()
      obj:metod1()
      ? obj:attribute1

In an object, destroy() method can be announced, but it isn't quite destructor, which is usual in languages of the third generation. There is

    local myObj:=myclassNew() variable,

and this is just an object. Upon leaving the function body, this variable, along with all its data, is destroyed. For example, this myObj has attribute

      myObj:hFile:=fopen(trali_vali).

When destroying myObj, it's necessary to close hfile, but the compiler doesn't know this; the compiler (rather a virtual machine) only knows that in hFile there's a number and will destroy the number only, but the file will remain open! Just for this purpose destroy is created, and it'll called (if it exists ) before destroying myObj variable.

      static function my_destroy()
            fclose(::hFile)
      return

Control of change attributes.

If it's necessary to control changes of object attributes, make modify method and set mapmodify(map_obj, .t. ), modify method will be called, and only then the attribute value will be changed. New value of attribute changet to return value of method modify()

Recovering/reviving objects.

CLIP is able to store the data of any type to MEMO fields, including objects. But there is no way to store object methods (methods can be changed).

Recovering is made with steps below: data are decoded; if data are of 'object' type and object have CLASSNAME attribute then called function _recover_&(var:CLASSNAME)(var). This function must assign this object methods.

This feature can be used to send object as string via e-mail or TCP :)

Here is an example of using this feature:

    x:=asdfNew()  /* Constructor */
    ? "x:m1",x:m1() /* See wheter it works */
    ? "x:m2",x:m2()
    y:=var2str(x) /* object -> string */
                  /* or field->memo_field:=x */
    ? "y=",y
    z:=str2var(y) /* string -> object, _recover_asdf() is called automatically */
                  /* or z:=field->memo_field */
    ? "z=",z
    ? "z:m1",z:m1() /* see wheter it works now */
    ? "z:m2",z:m2()
    ?
    return

    function asdfNew()
    	local o:=map()
    	o:classname := "ASDF"
    	o:a1 := "asdf"
            o:a2 := "qwer"
    	_recover_asdf(o)
    return o

    function _recover_asdf(o)
    	o:m1 :=@ asdf_1()
            o:m2 :=@ asdf_2()
            ? "recovering"
    return o

    static function asdf_1
        ? "asdf_1",::a1
    return ::a1
    static function asdf_2
        ? "asdf_2",::a2
    return ::a1

Due to such OO model and compiling fast code via the translation into C-program, there appears a possibility to write TBrowse and Get standard classes in Clipper itself!
At the same time, the efficiency of these classes is not worse than those written in pure C in CA-Clipper.

CLIP home page

© Uri Khnykin, uri@itk.ru, 2001