Basic Types
The system has a limited number of basic types. These types can be used to build up more complex compound types.
Number
A Number holds numeric data. The value can be either an integer ( a whole number with no fractional parts ) or it can be a floating point number. An integer can be in the range of about -1,000,000,000 to +1,000,000,000 ( that is a 30 bit absolute value ). The absolute value of a floating point number is in the range up to 1038 with about 7 significant digits. This is high enough for general data plotting but not for advanced numerical analysis.
If a Number datum has not had a value assigned to it then it will contain the type specific Null value - this is not the same as zero.
Logical
A Logical datum can contain either the value True or the value False. If a Logical datum has not had a value assigned to it then it will contain the type specific Null value - this is not the same as False. There are a number of operators and statements that work explicitly with Logical datums.
Text
A Text datum can contain a sequence of any character for the Unicode set. If a Text datum has not had a value assigned to it then it will contain the type specific Null value - this is not the same as the empty string "".
Time
The Time type is a system basic type that can hold a full time and date value for any date from 1st January 1AD to 31st January 9999AD. The dates assume the Gregorian calender ( even though it has not been used over the whole period ). The time has a resolution of 1 millisecond.
The value cannot be used directly but the are a number of functions for manipulating it.
Compound Types
The user can define compound types built up from the basic types. There are also a number of compound types defined with the system module that the user can use.
Type definitions should only occur in the global section of a source file - i.e. not within a function definition - there are no local types within a function.
Structure Types
A structure type ( sometimes call a record type ) is a type consisting of a number of fields of different types. As structure datum can either be manipulated as a single entity or have individual fields accessed. The basic format of a structure type definition is :-
typedefstruct ::= [ scope ] Type typename = { fieldlist };
The scope will be one of Public, Private or Export - if it is omitted then the scope for types will default to Private. The idea of scope is covered elsewhere.
The typename is the name by which the new type will be known.
The fieldlist is a comma separated list of field definitions :-
fieldlist ::= field [[ , field ]]
Each field definition has the following form :-
field ::= [ access ] type fieldname [ = expression ]
The access is optional keyword Const. If this is specified then that field cannot be changed within a datum.
The type will be the name of any type already defined ( basic type and other user or system defined compound types ).
The fieldname will be the name that will be used to access that specific field.
The = expression part is optional. If it exist then it will be used as the default value for the field. If it is omitted then the standard type-specific Null will be used as the default.
Example :-
Public Type StaffRecord = { Const Text name, Number age = 21 };
This declares a public type called 'StaffRecord' that is a structure type.
Array Types
An array type defines a multidimensional list of elements of the same type. An array can be manipulated as a single object or individual elements can be accessed. The basic format of an array definition is :-
typedefarray ::= scope Type typename = type [ indexlist ];
The scope and typename have the same meaning as for structure types.
The type value is the name of an existing type, either basic or compound, that all the elements in the array will have.
The indexlist is a comma separated list of the indexes :-
indexlist ::= index [[ , index ]]
Each index in the list will have the form :-
index ::= lowexpr To highexpr | enumtype | blank
The lowexpr and highexpr are expressions that define the valid range of indexes. The range specifier may also be an existing enumerated type name. If a range is omitted ( blank ) then the array index will be 'open' and that range will be set later - either when an array datum is initialised or with the Array function at runtime.
Example :-
Public Type StaffArray = StaffRecord[1 To 3, 1 To 10];
This will define a two dimension array of staff records - for example if you have three teams of ten.
It is possible for the expressions to be enumerated types, but the lower must be specified using a constant of the correct type - it is also good practice to use a constant for the upper limit a well. See the following example :-
Type Months = ( Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec ); Const Months SumStart = May, SumEnd = Aug; Type SummerList = Number[SumStart To SumEnd];
Enumerated Types
Enumerated types are a way of defining a series of constants that are closely related. If the values are used just within the program then the actual numeric values do not need to be explicitly set. The definition of an enumerated type has the form :-
tyepdefenum ::= scope Type typename = ( enumlist );
The scope and typename have the same meaning as for structure types.
The enumlist is a comma separate list of enumeration values :-
enumlist ::= enumdef [[ , enumdef ]]
enumdef ::= enumname [ = numexpr] [ = textexpr ]
The enumname is the only obligatory part - this specifies the name the value will have within the program.
The '= numexpr' is optional, and if defined should be a numeric expression which will be used when reading in data files with numeric values for the enumerated types.
The '= textexpr' is optional, and if defined should be a text expression which will be used when reading in data files with text values for the enumerated types.
Examples :-
Public Type CardSuits = ( Hearts, Diamonds, Spades, Clubs ); Public Type ChessPiece = ( Pawn = "P", Rook = "R", Knight = "N", Bishop = "B", King = "K", Queen = "Q" );
Routine Types
Routine types are a way of saving the identity of a function or shape to be called later. The routine can then be called via the datum rather that with the original identifier. A routine type can hold both system routines or user defined routines and they can be defined for both Functions and Shapes. Any routine stored in a routine datum must have a signature the matches the routine datum type. The definition of a routine type has the form :-
scope Type typename = rettype ( [ parmlist ] );
The scope, Type and typename are the same as for the kinds of type.
The rettype is the return type of the functions that can be store by this type. If you are defining a shape type then this is replaced with the Shape keyword.
The parmlist is the same as the parmlist part of a normal routine definition and together with the return type defines the type of routine that can be stored in a datum of this type.
Examples :-
Type TrigFunct = Number(Number angle); . . . TrigFunct myfn = Sin; . . . y := myfn(30);
In this example you are defining a type that can hold a function that takes a single Number and returns a Number. A datum, 'myfn' is created with this type and initialised with the built-in Sin function. So 'y' will be assigned the value of Sin(30);
The types in the signatures need to match exactly. The matching of the attributes of the parameters is more relaxed. The only restrictions are: the Ref attributes must match and if the parameter is a reference then a non-Const must not be assigned to a Const target.
If a routine is called via a routine datum the default value is lost. But the type can define its own default expression that will be used. The parameter name of the type will always be used rather than the parameter names in the original routine.
The routine datum is quite a complex concept and you should look at various example to get a clear idea of their use. They can be incredibly useful in certain circumstances.
Forward Declaration
There are a few cases where two type definitions may be mutually dependent - such as a structure type that contains a function field that return the other structure. In this cases one of the type needs to be defined as existing but without the details being filled in. In this case we use the syntax :-
scope Type typename = Forward;
Forward declared structures can be used as the base type of open arrays but not fixed size arrays.
Here is an example :-
Type Rec1 = Forward; Type Fn1 = Rec1(); Type Rec2 = { Fn1 funct }; Type Fn2 = Rec2(); Type Rec1 = { Fn2 funct };
OK, this will probably never be used. The feature was originally implemented to allow mutually referential pointers - but pointers were dropped from the language.