User Defined Types¶
As explained in Type System, Lumi allows variety of typing styles for creating user defined types.
In TL5 only basic structures and classes are supported.
User defined types behave like built-in complex types with references, static allocation and dynamic allocation.
Static Structures¶
Syntax for the static structure typing style.
In TL5, structures may not contain string or array variables, only their respected references. It will be supported in the final Lumi syntax.
Structures are declared using the struct
keyword:
struct ExampleStruct
var Uint32 integer-variable
user String string-reference
Members of struct can be accessed using .
operator:
var ExampleStruct struct-variable
struct-variable.integer-variable := 3
struct-variable.string-reference := "some string"
Structures are implemented in C as simple C structures. Structure references are implemented as pointers to the C structure.
Global Members¶
This is not supported yet in TL5.
Global members are declared under the type scope:
struct ExampleStruct
const Uint32 GLOBAL-CONSTANT(12)
global var Uint32 global-variable
Outside the type definitions they can only be accessed with the type name as prefix:
some-integer := ExampleStruct.GLOBAL-CONSTANT
ExampleStruct.global-variable := 5
Methods¶
Methods are declared as normal functions, except they are
declared inside the type scope, and the self
parameter should not be
declared, instead, its access is declared before the function name.
Inside the
method implementation self
keyword can be used to access the implicit self
parameter. Constants and global variables of the type can be accessed using
global
keyword.
struct ExampleStruct
var Uint32 integer-variable
const Uint32 GLOBAL-CONSTANT(12)
global var Uint32 global-variable
~~~ "self" access is "user" ~~~
func user method(copy Uint32 num)
self.integer-variable := num + global.GLOBAL-CONSTANT
global.global-variable := num
It possible to split the function deceleration from its implementation. In this
case the function deceleration should be followed by _
.
struct ExampleStruct
func user method(copy Uint32 num) _
func user ExampleStruct.method(copy Uint32 num)
; implementation...
There are two ways to call a method:
instance.method(copy 4) ; OOP style
ExampleStruct.method(var instance, copy 4) ; functional style
Constructor Method¶
If possible, structure members are automatically initialized to their default
value on construction. This can be extended by defining a “constructor” method
for the structure. This method will be called on every instance construction
after the default initialization. A constructor is declared with a dedicated
name new
.
struct ExampleStruct
new() _
func ExampleStruct.new()
; custom initialization
A constructor cannot have outputs, and if it has parameters - they must be given on every object creation:
struct ExampleStruct
var Uint32 integer-variable
owner String string-reference
new(copy Uint32 x, owner String s)
self.integer-variable := x
self.string-reference := s
func usage()
var ExampleStruct variable(copy 4, owner String{12}(user "some string"))
owner ExampleStruct reference := ExampleStruct(copy 4,
owner String{12}(user "some string"))
Structures that have members without a defined default value must implement a constructor. The constructor must also directly initialize these fields. Members without a defined default value are:
non-conditional references
integers that
0
is not a legal value of their rangevariables of types with a constructor
Destructor Method¶
A “destructor” method can also be defined for a structure. This method will be
called just before any object destruction. A destructor is declared as a normal
method with a dedicated name cleanup
.
struct ExampleStruct
cleanup() _
func ExampleStruct.cleanup()
; destruction code
A destructor cannot have any kind of arguments.
In TL5 destructors cannot raise errors - but it may be supported in the future.
Note
Lumi Automatically deletes any memory allocated in the structure and calls the cleanup function of all members and base classes - there is no need to do it manually
Extending Structures¶
In TL5 a structure may only extend one other structure.
struct ExtendingStruct(BaseStruct, OtherBaseStruct)
var Uint32 additional-field
The extending structure may be used in any place one of its base structures is expected:
owner BaseStruct base-struct := ExtendingStruct()
The extending structure may overwrite a base method, the overwriting method arguments access and type must be identical to the base overridden method.
struct BaseStruct
func method(copy Uint32 num)
; implementation...
struct ExtendingStruct(BaseStruct)
func method(copy Uint32 num)
; other implementation...
An overwriting function can call the overwritten function using base
keyword. Other overwritten methods can be called using base.other-method
.
struct ExtendingStruct(BaseStruct)
func method(copy Uint32 num)
base(copy num)
base.other-method()
Example for the static dispatch of structures:
var ExtendingStruct extending-struct
user BaseStruct base-struct(user extending-struct)
extending-struct.method(copy 4) ; will call ExtendingStruct.method
base-struct.method(copy 4) ; will call BaseStruct.method
BaseStruct.method(var extending-struct, copy 4) ; will call BaseStruct.method
Dynamic Interfaces¶
Syntax for the dynamic interface typing style.
Dynamic interfaces (or “dynamics” in short) are declared using the dynamic
keyword:
dynamic ExampleDynamic
func dynamic-method(copy Uint32 num)
func another-method()->(var Uint32 result)
var Uint32 dynamic-variable
Dynamic variables are not supported in TL5.
Dynamics are always used as references and cannot be allocated because they have no structure:
func use-dynamic(user ExampleDynamic example)
example.dynamic-method(copy 3)
Now use-dynamic
function can be called with any item that implements
ExampleDynamic
.
Dynamics are implemented in C as a C structure containing all the dynamic members, where dynamic methods are implemented as pointer to functions. Each implementation of the dynamic is a global instance of this structure. Dynamic references are implemented as 2 references: one reference to the dynamic structure and another reference to the implementing type instance.
Non-Dynamic Members¶
This is not supported in TL5.
Constants and global variables are declared and used exactly as global members in static structures.
Static methods must be declared using static
prefix:
dynamic ExampleDynamic
func dynamic-method(copy Uint32 num)
static func static-method(copy Uint32 num)
; implementation
Extending Dynamics¶
This is not supported in TL5.
Same syntax as structures:
dynamic ExtendingDynamic(BaseDynamic, OtherBaseDynamic)
func additional-method(copy Uint32 num)
Support Dynamics in Structures¶
A structure can support a dynamic by implementing all
its dynamic members and explicitly declare it using the support
keyword.
some implemented members may be added under the support
line:
struct ExampleStructure
func method(copy Uint32 num)
; implementation...
support ExampleDynamic in ExampleStructure
func another-method()->(var Uint32 result)
; another implementation...
When a structure supports a dynamic, every structure
that extends it also supports the dynamic using the base structure
implementation. The extending structure may override some members of the
dynamic, but to use these overrides as the implementation of the dynamic
another support
statement for the extended structure must be added:
struct BaseStruct
func method(copy Uint32 num)
; base implementation...
support ExampleDynamic in BaseStruct
struct ExtendingStruct(BaseStruct)
func method(copy Uint32 num)
; overriding implementation...
support ExampleDynamic in ExtendingStruct
Example for the dynamic dispatch of dynamics:
var ExampleStructure example-struct
var ExtendingStruct extending-struct
user BaseStruct base-struct(user extending-struct)
user ExampleDynamic example-dynamic
example-dynamic := example-struct
example-dynamic.method(copy 4) ; will call ExampleStructure.method
example-dynamic := extending-struct
example-dynamic.method(copy 4) ; will call ExtendingStruct.method
example-dynamic := base-struct
example-dynamic.method(copy 4) ; will call BaseStruct.method
; will not call ExtendingStruct.method becasue structure dispach is static
Default Dynamic Member Implementation¶
This is not supported in TL5.
A dynamic may give a default implementation to some or all of its members and
its base dynamics members. Method implementations can use self
and
global
keywords to access its own members.
dynamic ExampleDynamic
func implemented-method(copy Uint32 num) _
func unimplemented-method()->(var Uint32 result)
var Uint32 implemented-variable(copy 5)
var Uint32 unimplemented-variable
func ExampleDynamic.implemented-method(copy Uint32 num) _
; implementation...
Classes and Binds¶
Syntax for the class typing style.
In TL5 this only partially implemented:
Only
class
type definition is supported,Bind
is notAll restrictions on structures also apply to classes
Only methods can be dynamic
Variables don’t need to start with
static
keyword - as they cannot be dynamic or global
A straightforward way to use classes is using the built-in Bind
typed
references. References of this type only accept types that extend all bound
structures and implement all bound dynamics.
user Bind{ExampleStruct:ExampleDynamic} class-reference
Another way to use classes is to declare a type as a class in its definition
using the class
keyword. Each non-global member of the class must come
after a static
or a dynamic
keyword to declare witch implicit type this
member belongs to: the structure or the dynamic. Global members are only
defined under the name-space of the class.
class ExampleClass
static var Uint32 static-field ; part of the implicit structure
dynamic func dynamic-method(copy Uint32 num) ; part of the implicit dynamic
global var Uint32 global-variable ; defined under the class name-space
Classes can implement dynamics using the same syntax as structures.
Class references are implemented using two C pointers: one for the structure, and one for the dynamic.
Extending Classes¶
As all types:
class ExtendingClass(BaseStruct, BaseDynamic, BaseClass)
static var Uint32 addition-static-field
dynamic func addition-dynamic-method(copy Uint32 num)
In TL5 a class may only extend one other class or structure.
Example for the dynamic dispatch of classes:
var ExtendingClass extending-class
user BaseClass base-class(user extending-class)
user ExampleDynamic example-dynamic
extending-class.method() ; will call ExtendingClass.method
base-class.method() ; will call ExtendingClass.method
example-dynamic := extending-struct
example-dynamic.method() ; will call ExtendingStruct.method
example-dynamic := base-struct
example-dynamic.method() ; will call ExtendingStruct.method
Using the Implicit Structure or Dynamic of a Class¶
This is not supported in TL5.
The implicit structure of a class can be used using the built-in Struct
type, and the implicit dynamic can be used using the built-in Dynamic
type. This is not supported in TL5.
var Struct{ExampleClass} static-structure-only
user Dynamic{ExampleClass} dynamic-interface-only
Parameterized Types¶
Syntax for the parameterized type typing style.
Each type parameter must have a type and a name. For static type names Type
should be used as the parameter type, and for dynamic parameters Generic
should be used as the parameter type. The parameter name must conform the
naming standard of types if one of these is used, else it must conform naming
standard of constants.
struct ParametrizedType{Uint32 CONSTANT-PARAMETER:Type TypeParameter:Generic GenericParameter}
var String{CONSTANT-PARAMETER} parametrized-sized-string
var TypeParameter static-parametrized-typed-variable
user GenericParameter dynamic-parametrized-typed-reference
Whenever a parameterized type is used it must be set with appropriate values for each parameter
var ParametrizedType{8:Uint32:File} specific-variable
This is partially supported in TL5:
only dynamic parameters are supported (
Generic
type)no need to add
Generic
- only the parameter name is neededArrays are not supported as parameter values
Embedded Dynamic Reference¶
Syntax for the embedded dynamic reference typing style.
This is not supported in TL5.
Embedded classes can be declared using the built-in Embed
type:
; "ExampleStruct" structure with "ExampleDynamic" reference embedded
; inside it
var Embed{ExampleStruct:ExampleDynamic} explicit-embedded-variable
; "ExampleClass" static structure with a reference to its dynamic structure
; embedded inside it
var Embed{ExampleClass} implicit-embedded-variable
The syntax may change as this typing style is still under planning.