Types

Slick-CŪ types are similar to the types in C. The following types are available in Slick-C:

Strings

String variables are declared using the _str type. You can get the length of the string using the length built-in.

Slick-CŪ has additional string operators so that the compiler always knows whether to perform a string or numeric operation. The + operator always means add two numbers, and the concatenation operator :+ always means concatenate two strings.

See also Implicit Conversion to Strings.

Enumerated Types

Slick-CŪ enumerated types are very much like C enumerated types, with the exception of having relaxed type checking with respect to arithmetic and bit operations.

    enum BasicOptions {
        OPTION1=1, 
        OPTION2, 
        OPTION3,
     };

In addition, Slick-C enumerated types introduce enumerated type flags, a convenient way to create a set of bit flags.

    enum_flags OptionFlags {
        FLAG1=0x4,
        FLAG2, // 0x8
        FLAG3, // 0x10
        FLAGS_ALL=FLAG1|FLAG2|FLAG3
    };

Arrays

Array types are declared like C arrays, but cannot have a size limit. Array elements are always dynamically allocated.

Use array variables to keep a list of items. To define an array variable, use the following syntax:


    [static] TypeName variable1[][={e1 ,e2 , ...}] , variable2[][={e1 ,e2 , ...}] ...;
    

The first element of an array starts at 0. Use more than one set of brackets ([]) for multi-dimensional arrays. Do not define the maximum number of elements in the array, because array elements are allocated when you access them. The maximum number of elements that can be placed in an array is approximately 2 billion. Use the _length() method to determine the number of elements in an array. The syntax for using this method is variable._length().

To empty an array, use the following statements:

    array._makeempty();        // Empty the array.
    array=null;                // Empty the array. Same as above.

You can delete and insert items into an array using the _deleteel() and _insertel() built-in methods, respectively.

A Slick-CŪ class instance can be indexed using array syntax provided that the class implements the sc.lang.IIndexable interface. This is similar to overloading operator [] in C++.

Differences from C++

  • Space for array elements is allocated when you index into the array.

  • You cannot use pointer variables to traverse array elements.

  • You cannot limit the number of elements that the array may contain.

  • Specifying an array variable WITHOUT the [] operator does not return a pointer to the first element. Instead, it refers to the entire array. This allows you to copy one array to another, or define a function which returns a copy of an array.

  • There is no sizeof function which tells you the size of the array in bytes. There is a _length method which tells you the number of elements in the array.

  • Array initializers are not supported for local variables.

Example:

int gai[]={1, 7, 12};
int gaai[][]={{1},{1,2},{1,2,3}}; // Two dimensional array.
_str    gastring1[]={"Value1", "Value2"};
typeless   gat[]={"String", 1, 2.4};
 
defmain()
{
    t=gai;              // Copy all the array elements into a local container
                        // variable.
    t[t._length()]=45;  // Add another array element.
    for (i=0;i<t._length();++i ) {
       messageNwait("t["i"]="t[i]);
    }
}

Hash Tables

Hash tables are declared similar to array types and indexed with a string :[] operator. Use the following syntax to define a hash table variable:


    [static] TypeName variable1:[]
    [={s1=>e1, s2=>e2, ...}] , variable2:[][={s1=>e1,s2=>e2, ...}] ...;
    

A Slick-CŪ class instance can be indexed using hash table syntax provided that the class implements the sc.lang.IHashIndexable interface. This is somewhat similar to overloading operator [] in C++.

Hash tables support indexing by class objects. For example:

class FileName : sc.lang.IHashable { ... }
 
defmain() {   
   boolean ht:[];
   Filename a("C:\\temp\\test.txt");
   Filename b("C:\\Program Files\\");
   Filename c("F:\\Public\\xkcd108.jpg");
   ht:[a] = true;
   ht:[b] = false;
   ht:[c] = true;
   Filename i;
   foreach ( i => auto v in ht ) {
      _assert(i instanceof Filename);
      say("i="i._hashKey()" v="v);
   }
}

You can delete an item from a hash table using _deleteel(). Hash table initializers are not supported for local variables.

See Overloading Array Index Operators for more examples of IHashable.

Structs

Structures (structs) are typically used to logically group data. For example, a record in a database might have a name, address, and phone number. This can be logically grouped into a ContactInfo structure which is more convenient to use than accessing the fields individually. Structures can also have the added effect of reducing the number of global variables.

Slick-CŪ supports C-style structs. Slick-C structs cannot have member functions.

For consistency, we recommend that structs use initial caps (camel case) identifiers.Use the following syntax for defining a struct:


    [static] struct StructName {
    member-variable-decl1;
    member-variable-decl2;
    } ([variable1[={e1,e2, ...}] , variable2[={e1,e2, ...}], ...];)
    

The struct declaration provides the option of defining your own type called StructName and to declare one or more variables. The syntax of member-variable-decls is identical to declaring other variables, except that static structure members are not supported. Use the following syntax for accessing a member of a struct variable:


    variable.member_name
    

Example:

struct PHONERECORD {    // Define a type called PHONERECORD.
    _str Name;
    _str PhoneNumber;
} gPhoneRecord;         // Declare a variable of that type.
 
PHONERECORD gPR={       // Declare a variable of type PHONERECORD.
    "Steve","555-1346"
};
PHONERECORD gRecordArray[];    // See arrays below.
struct PHONERECORD2 {          // Define a type called PHONERECORD2.
    _str Name;
    _str PhoneNumber;
    _str FaxNumber;
};
 
defmain()
{
    messageNwait("Name="gPR.Name" PhoneNumber="gPR.PhoneNumber);
    typeless t = gPR;   // Copy phone record data into a local container variable.
                        // Container variables can access structure elements 
                        // as an array.
    messageNwait("Name="t[0]" PhoneNumber="t[1]);
}

Slick-C structs support designated initializers:

struct PhoneRecord {
   _str name;
   _str phoneNumber;
};
PhoneRecord shouldHaveKnown = { 
   .phoneNumber = "867-5309", 
   .name="Jenny" 
};

Differences from C++

  • There is no sizeof operator like in C++. Since the Slick-CŪ interpreter stores all types as container variables, the sizeof operator has no meaning.

  • Space for structure elements is allocated when you access the element.

  • Structure data is not contiguous. The Slick-C interpreter stores all types as container variables, including the members of a struct.

Unions

Slick-CŪ supports C-style unions. Unions are typically used in place of a struct in the case where you have mutually exclusive member variables. In this case, a union requires less memory than a struct. Memory is only allocated for one member variable at a time. The syntax for defining a union is shown in the following example:


    [static] union [UnionName ] {
    member-variable-decl1;
    member-variable-decl2;
    } [variable1[={e1}] , variable2[={e1}], ...];
    

The union declaration provides the option to define your own type named UnionName and to declare one or more variables. The syntax of member-variable-decls is identical to declaring other variables, except that static union members are not supported. The syntax for accessing a member union variable is variable.member_name.

Example:

union {
    int i;
    _str s;
    double d;
} gu={1};    // Type checking here is with first member variable.
 
#define KIND_INT 1
#define KIND_STRING 2
#define KIND_DOUBLE 3
defmain()
{
    struct {
       int kind;
       // Here we are nesting a union inside a struct.
       // This union only requires space for one of these members at a time.
       union {
          int i;
          _str s;
          double d;
       }u;
    } x;
 
    x.kind=KIND_INT;x.u.i=1;
...
    switch (x.kind) {
    case KIND_INT:
       messageNwait("x.u.i="x.u.i);
       break;
    case KIND_STRING:
       messageNwait("x.u.s="x.u.s);
       break;
    case KIND_DOUBLE:
       messageNwait("x.u.d="x.u.d);
       break;
    }
}

Anonymous Unions

An anonymous union is a union member variable that is not named. This saves you from having to type the union member variable name.

Example:

defmain()
{
    struct {
       int kind;
       union {
          int i;
          _str s;
          double d; 
      };// No name for this union member variable.
    } x;
    x.kind=KIND_INT; x.i=1;
}

Interfaces

Interfaces use Java-like syntax. They do not allow constructors, destructors, or member variables; only prototypes. Interfaces can inherit from other interfaces. All the prototypes in an interface are implicitly public.

Example:

    interface ICommunicationDevice {
        void talk();
        void hangup();
    };

Classes

Classes use a Java-like syntax. For example:

class Phone : ICommunicationDevice {
 
   protected typeless m_dialer = null;
   private typeless m_line = null;
   private static typeless s_operator = null;
 
   Phone(_str number="") {
   }
   ~Phone() { }
   void talk() {
   }
   void hangup() {
   }
   static void getOperator() {
   }
};

For consistency, Slick-CŪ class names should be in camel case. Member variables within classes should start with "m_". Static member variables should start with "s_". Finally, methods should be lowercase. If a method name contains multiple words, the trailing words should be camel case.

A few notes about Slick-C classes:

  • A class can extend or inherit from only one other class.

  • A class can implement multiple interfaces.

  • Use the instanceof operator to test if a class instance derives from a specific class or interface.

  • Member variables can have constant initializer expressions.

  • All member variables must be initialized, either using initializers or in the class constructor.

  • All member variables must be declared before the constructor.

  • There are no extends or implements keywords.

  • Classes are not allowed to derive from struct types.

  • The default access level is public. There is a public keyword, but it essentially does nothing.

  • Class members support protected and private.

  • There is no concept of a package scope like there is in Java.

  • Member functions are virtual by default, except for static member functions.

  • static member variables may have initializers.

  • extern member function prototypes are implemented in a DLL.

  • A class is allowed one and only one constructor.

  • If a class constructor takes arguments, they must have defaults.

  • No explicit calls to new or delete (no new or delete keywords).

  • No function overloading.

  • No operator overloading.

  • No friend relationships.

  • No templates or generics.

  • No final and no const.

  • No C#-style properties or delegates.

  • No default root "object" class.

  • No static constructors.

The life-span of a Slick-C class instance is identical to that of a similar Slick-C struct. There are no new or delete operators.

    // Construct an instance of a class, like C++.
    C1 a;
    C1 b;
 
    // Assign a class instance to another (deep copy).
    a = b;
 
    // An array of class instances. Constructor not called here.
    C1 array[];    
    // Constructor called with no args followed by deep copy.
    array[1] = a;

See the following topics in this section for more information:

Introspection

Slick-CŪ supports introspection of struct and class instances through the built-in functions shown below. In each of the functions, "index" can be either an integer index or a string containing the field or method name.

  • v._callmethod(index) - Call a class method.

  • v._construct() - Construct an instance of a class.

  • v._fieldindex(name) - Find the position of a class field.

  • v._fieldname(i) - Get the name of a class field.

  • v._findmethod(name) - Find a class method.

  • v._getfield(index) - Get a reference to a class field.

  • v._instanceof(name) - Return true if variable is instance of or derives from the given class.

  • v._length - Return the number of fields in a class.

  • v._setfield(index,value) - Modify a class field.

  • v._typename() - Return the name of variables type.

The C++ API for Slick-C includes the following functions:

  • vsHvarTypename(hvar)

  • vsHvarFieldIndex(hvar,name)

  • vsHvarFieldName(hvar,i)

  • vsHvarGetField(hvar,index)

  • vsHvarGetFieldByName(hvar,name)

  • vsHvarSetField(hvar,index,value)

  • vsHvarSetFieldByName(hvar,name,value)

  • vsHvarFindMethod(hvar,name)

  • vsHvarCallMethod(hvar,index,args)

  • vsHvarCallMethodByName(hvar,name,args)

  • vsHvarInstanceOf(hvar,name)

  • vsHvarConstruct(name,args)

Implicit Conversion to Strings

Slick-C provides the interface IToString for implicit string conversion (see Strings). If a class implements sc.lang.IToString, then an instance of that class can be implicitly converted to a string, without explicitly calling the toString() method.

Overloading Comparison and Assignment Operators

By default, Slick-CŪ class instances are compared using a deep member-wise equality test. To override the default comparison methods for a class, Slick-C provides the interfaces sc.lang.IEquals and sc.lang.IComparable. If a class implements sc.lang.IEquals, an instance of that class can be compared to another instance using operator == or operator != as defined by the equals() method. If a class implements sc.lang.IComparable, then instances of the class can be compared using the standard comparison operators, as defined by the compare() method. If a class implements IComparable, it does not have to implement IEquals to support equality and inequality tests.

Overloading Array Index Operators

Slick-CŪ supports the overloading of the [] and :[] operators. For more information, see Hash Tables.

Below is an example of IIndexable:

#import "sc/lang/IIndexable.e"
 
class PerfectSquares : sc.lang.IIndexable {
   typeless _array_el(int i) {
      return i*i;
   }
};
 
defmain()
{
   PerfectSquares ps;
   say("defmain: 3^2="ps[3]);
   say("defmain: 16^2="ps[16]);
}

Below is an example of IHashIndexable:

class PhoneBook : sc.lang.IHashIndexable {
   _str m_numbers:[];
   void loadNumbers() {
      m_numbers:["Brittany"] = "555-3825";
      m_numbers:["Vanessa"] = "555-1024";
   }
   typeless _hash_el(_str name) {
      return m_numbers:[name];
   }
};
defmain()
{
   PhoneBook pb;
   pb.loadNumbers();
   say("defmain:  Brittany's number is " pb:["Brittany"]);
   say("defmain:  Vanessa's number is " pb:["Vanessa"]);
} 

Overloading Assignment/Copy Semantics

By default, Slick-CŪ class instances are copied using a deep, member-wise copy. To override this behavior, a class can implement the sc.lang.IAssignTo interface and implement a custom copy() method.

Overloading Iteration Semantics

A Slick-CŪ class can be customized to work seamlessly in a foreach loop by implementing the sc.lang.IIterable interface. The sc.lang.Range class, which is included in the Slick-C class library, is an excellent example of how to implement and use IIterable.

SlickEditŪ Class Libraries

SlickEdit ships with a small but growing core of Slick-CŪ classes and interfaces to build upon. There are two top-level namespaces: sc (Slick-C) and se (SlickEdit). The sc namespace encompasses general purpose classes that support programming in Slick-C and are application-independent. It can be compared to java.lang and java.util in Java, the System namespace in C#, or the std namespace in C++ with respect to its purpose (not feature-by-feature). The se namespace includes the foundations and implementations of select features of the SlickEdit editor. Not all SlickEdit features use Slick-C classes.

Differences from C++ and Java

  • Slick-CŪ uses per-member access specifiers like Java rather than the grouping syntax employed by C++.

  • Slick-C supports destructors, just like C++ (Java does not have destructors).

  • Slick-C has no new or delete.

  • Slick-C does not support overloaded methods or const methods.

  • Like C++, Slick-C class instances are passed by value, unless you specifically pass them by pointer or reference.

  • Like Java, this is a reference to the current class instance, not a pointer as it is in C++.

Additionally:

  • No function overloading.

  • No operator overloading.

  • No friend relationships.

  • No templates or generics.

  • No final and no const.

  • No C#-style properties or delegates.

  • No default root "object" class.

  • No static constructors.

Pointers

Pointers to Variables

Pointer variables are declared using the following syntax:


    [static] TypeName *variable1[=&v1] , *variable2[=&v2] ...;
      

The unary & operator is used to return the address of a variable. The unary * operator is used to dereference a pointer. Use the operator -> (for example, p->m-variable) to access members of a pointer to a structure.

Caution

When a module is reloaded, static variable addresses change. Make sure you reinitialize global pointer variables which point to static (module scope) variables.

Pointers to Functions

Function pointer variables are useful for callback functions. The syntax for function pointers is:


    [static] TypeName (*variable1)([ArgDecl1, ArgDecl2,...]){=function_name};
      

Where ArgDecl has the almost the same syntax as a variable declarations, except static is not supported and the ampersand (&) operator is used to specify call by reference parameters. Call by reference array and hash table parameters require parentheses around the ampersand (&) and id.

The syntax for calling a pointer to function variable is:


    (*pfn)([e1, e2,...])
      

If accessing an invalid function pointer, the Slick-CŪ macro stops.

Caution

When a module is reloaded, static function addresses change. Make sure you reinitialize global function pointer variables which point to static (module scope) functions.

Typeless

A typeless variable can be assigned to or from any type, including structs, arrays, and hash tables.

Typeless container variables can be declared using the typeless type. A typeless container can be passed to a function using the var type. The container variable can store the contents of any typed variable. This is easy for the interpreter since all typed variables are stored as container variables. At run time, the interpreter must check the current type of the container variable (and sometimes convert it) to perform an operation.

The compiler performs (double) floating point arithmetic on container variables. Currently, there is only a very small difference in speed between arithmetic operations on integer type variables and container variables, because the Slick-CŪ language has been optimized for string and container operations.

Note that there is no sizeof operator. Since the Slick-C interpreter currently stores all types as container variables, the sizeof operator has no meaning.

Example:

    typeless t;
    t=1;   // Store an integer.
           // Convert the contents of the variable t to a 
           // floating pointer number (double type) and add 1.
           // NOTE: The interpreter is smart and will only perform
           // integer arithmetic here.
    t=t+1;
    // Since + always means addition, the compiler converts
    // string constants to the smallest possible numeric type.
    t=t+"1";
 
    // Declare string variable.
    _str s;
    s=1;   // Compiler will convert int to string.
    t=1.2;
    // Must cast string type to int or compiler will complain.
    t=(int)t+(int)s; // Result is 2, not 2.2, because of the cast of t to int.
 
    // Destroy the integer and make an array.
    // Also make the 0 element an integer.
    t[0]=1;
    t[1]=2;   // Add another element.
 
    t2=t;  // Copy the array and all its elements.
    struct {
       int x;
       int y;
    } st;
    st.x=1;st.y=2;
    t=st;
    // Print out the elements of the structure.
    for (i=0;i<t._length();++i ) {
         messageNwait("t["i"]="t[i]);
    }