Cerberus X language reference
This manual describes the syntax and general concepts behind the core Cerberus X language.
Contents
Programs
Declarations
Strict mode
Comments
Identifiers
Language keywords
Reserved identifiers
Cerberus X naming conventions
Literals
Types
The Bool type
Boolean literals
The Int type
Integer literals
Hexadecimal literals
The Float type
Floating point literals
The String type
String indexing and slicing
String methods
String literals
String escape sequences
Character literals
The Array type
Array indexing and slicing
Array methods
Array literals
The Object type
Object literals
Variables
Local variables
Global variables
Field variables
Constants
Enumerations
Expressions
Operators
Order of operations (Precedence)
Balancing argument types
Conditional operators
Bitwise operators
Implicit conversions
Explicit conversions
Boxing and unboxing conversions
Statements
The If statement
The Select statement
The While loop
The Repeat loop
The numeric For loop
The For EachIn loop
Exit and Continue
Assignment statements
Update assignment statements
Expression statements
Functions
Function overloading
Methods
Property Methods
Classes
Constructors
Generics
Interfaces
Exceptions
Modules
Alias
Public and private
External declarations
Memory management
Preprocessor
Custom preprocessor directives
Optimisation
Asset import
I've Seen...
Glossary
About this reference
A monospaced font is used for program code examples, for example:
Function
Main
()
Print
"Hello World!"
End
Language syntax explanations are generally formatted as follows:
- Anything that appears in monospaced font indicates syntax and pseudo-code. For example:
Print StringLiteral
means the keyword 'Print' followed by a string literal. - Anything that appears in bold indicates literal text. For example: Function means the actual text 'Function'.
- Anything that appears in italics indicates other syntax. For example: Identifier means any text that is a valid identifier.
- Anything that appears in square brackets [ ] is optional. For example: [ Step ] means the literal text Step is optional.
- The pipe character | is use for alternatives. For example: To | Until means you can use To or Until - but not both.
- A three dots ellipsis is used to indicate repetition.
These rules are not strictly followed. Where it makes sense to do so, syntax may be shown in a simplified form with explanatory notes.
Programs
Every Cerberus X program has a 'main module' that must contain a public function called Main
that takes no parameters and returns an integer. For example:
Function
Main
:
Int
()
' take nothing
Print
"That's all folks!"
' do something
Return
0
' return 0 to indicate everything went fine
End
This is the entry point of the program and is where program execution begins.
If you are using the mojo framework of modules, you must create a new class (which extends the base mojo.app class) and create a new instance of it in the Main
function. See the mojo.app Module Reference for more information. An example of this is:
Import
mojo
.
app
Import
mojo
.
graphics
Class
MyApp
Extends
App
Method
OnRender
()
DrawText
"Hello World!"
,
0
,
0
End
End
Function
Main
()
New
MyApp
End
Declarations
A Cerberus X program consists of one or more modules, each of which is a separate file, consisting of a series of declarations.
A declaration associates a 'meaning' with an identifier. For example, this declaration...
Global
x
:
Int
...indicates that the identifier x
is a global variable of type 'Int' (an integer).
Cerberus X supports the following kinds of declarations:
A module is itself a kind of declaration and is represented by a single source file. The name of the module is taken from the name of the source file. For example, if your file is named "particles.cxs" then the module will be called "particles".
Modules may 'import' other modules, which may in turn import other modules and so on. For more details about that, refer to the Modules section.
Strict mode
By default, Cerberus X allows you to take certain shortcuts when programming.
However, Cerberus X also offers a strict mode for programmers who prefer a stricter language definition.
The differences between strict and non-strict mode are:
- In non-strict mode, variable type, function return type and function parameter types can be optionally omitted. In this case they will default to Int. In strict mode, you must always specify the type of all variables, functions return values and function parameters.
- In non-strict mode, Return statements can be omitted at the end of a function. In this case, a Return statement will be automatically generated that will return an appropriate default value for the function type. That is, False for bool, 0 for int and float, "" for string, [] for arrays and Null for objects. In strict mode, a function that does not return Void must end with a Return statement.
- In non-strict mode, the brackets around function call parameters are optional if the function is being used as a statement, or if the function can be called with no parameters. In strict mode, functions returning a value must have their call parameters enclosed in brackets.
To use strict mode, the Strict
directive must be placed at the very top of your module. For example:
Strict
Import
mojo
' in Strict mode, type definition for functions is compulsory:
Function
Main
:
Int
()
' in Strict mode, functions returning a value must be invoked with brackets:
Print
"the year is "
+
GetDate
()[
0
]
' in Strict mode, functions returning a value must use the Return keyword:
Return
0
End
Most of the examples in this document will be presented in non-strict form.
Comments
You can add line comments to your programs using the '
(apostrophe) character. Everything following the '
character until the end of the line will be ignored.
You can add block comments to your programs using the #Rem
and #End
preprocessor directives. These must appear at the start of a new line, although they may optionally have whitespace characters in front. Everything between #Rem
and #End
will be ignored. Block comments can also be nested.
Here is an example of using comments:
Print
"Hello World"
'This is a line comment!
#Rem 'start of a block comment
Print "The sound of silence!" 'inside a block comment
#End
Identifiers
Identifiers must start with an alphabetic character, or a single underscore followed by an alphabetic character. The rest of the identifier may contain any combination of alphanumeric characters and/or underscores.
Identifiers are case sensitive (except for language keywords - see below). For example, player
, Player
, PLAYER
and PLayER
are all different identifiers. This allows you to reuse the same name for different purposes. For example, Actor
may refer to a class while actor
refers to an object of that class.
Here are some examples of valid Cerberus X identifiers:
score
player1
player_up
_internal
helloworld
HelloWorld
Language keywords and reserved identifiers
The following identifiers are Language keywords and are reserved for use by the Cerberus X language:
Void Strict Public Private Property Bool Int Float String Array
Object Mod Continue Exit Import Extern New Self Super Try Catch
Eachin True False Not Extends Abstract Final Select Case Default
Const Enumerate Local Global Field Method Function Class And Or Shl Shr
End If Then Else ElseIf EndIf While Wend Repeat Until Forever
For To Step Next Return Module Interface Implements Inline Throw
Language keywords are case insensitive - for example, you may use the keyword function
, Function
or indeed even fUNCTION
(not recommended) to declare a function.
The keywords Module
, Inline
and Array
are not currently used by the Cerberus X language but are reserved for future use.
Cerberus X naming conventions
The standard Cerberus X modules use a simple naming convention:
- All-caps case (eg: '
ALLCAPS
' ): Constants. - Pascal case (eg: '
PascalCase
' ): Globals, functions, classes, methods, properties. - Camel case (eg: '
camelCase
' ): Fields, locals and function parameters.
You are of course free to use your own convention, but for the sake of consistency it is recommended that this convention be used for the public interface of any modules you create intended for use by the Cerberus X community.
Literals
Literals are values within the code used in assignments or comparisons.
True
' Bool literal
1234
' Integer literal
$CAFEBABE
' Hexadecimal literal
9.81
' Floating point literal
"Hello World"
' String literal
`A`
' Character literal
[
2
,
4
,
6
]
' Array literal
See also: Boolean literals, Integer literals, Hexadecimal literals, Floating point literals, String literals, Character literals, Array literals, Object literals
Types
Cerberus X is a statically typed language, which means that all variables, function parameters, function return values and expressions have an associated type that is known at compile time.
The following types are supported:
Type | Long notation | Short notation |
---|---|---|
Boolean | :Bool | ? |
Integer | :Int | % |
Floating Point | :Float | # |
String | :String | $ |
Array | :ElementType[] | |
Object | :ClassIdentifier |
The Bool type
Values of type 'Bool' are boolean values used to express the result of conditional expressions, such as the comparison operators, and to represent a true/false 'state' in general. A boolean value can only be either true or false.
The syntax used for declaring values and variables of boolean type is :Bool
. For example:
Local
gamePaused
:
Bool
=
False
Boolean values are usually generated by the use of the comparison operators, for example:
If
livesLeft
<>
0
doSomething
()
End
However, in some circumstances Cerberus X will automatically convert a non-bool value to bool. This will occur when evaluating an expression for use with the If
or While
statements; the Until
part of a Repeat
loop; and when evaluating the arguments for the Not
, Or
and And
operators. For example:
If
livesLeft
doSomething
()
End
See the conversions section in the expressions chapter for more information.
Also, notice that boolean identifiers can also be declared by using the ?
character. This two lines of source code are both valid and syntactically correct:
Local
myVariable
:
Bool
=
True
Local
myVariable
? =
True
Boolean literals
The keywords True
and False
can be used as 'pseudo' boolean literals:
True
False
The Int type
Values of type 'Int' are signed integer values - that is, values with no fractional part. The range of integer values supported is target dependent, but is at least 32 bits. A 32 bit integer can represent a range of values from: -2,147,483,648 to 2,147,483,647
The syntax used for declaring values and variables of integer type is :Int
. For example:
Local
x
:
Int
=
5
Also, notice that integer identifiers can also be declared by using the %
character. This two lines of source code are both valid and syntactically correct:
Local
myVariable
:
Int
=
1024
Local
myVariable
% =
1024
Integer literals
Integer literals are sequences of digits without a fractional part:
0
1234
Hexadecimal literals
Hexadecimal literals are also supported with the $
prefix. For example, the following are also valid integer literals:
$3D0DEAD
$CAFEBABE
The Float type
Values of type 'Float' are signed numeric values with both an integer and fractional part. The range of floating point values support is target dependent, but is at least 32 bits.
The syntax used for declaring values and variables of floating point type is :Float
. For example:
Local
gravity
:
Float
=
9.81
Also, notice that floating point identifiers can also be declared by using the #
character. This two lines of source code are both valid and syntactically correct:
Local
myVariable
:
Float
=
3.141516
Local
myVariable
# =
3.141516
Floating point literals
Floating point literals are sequences of digits that include a fractional part, for example:
.0
0.0
.5
0.5
1.0
1.5
1.00001
3.14159265
The String type
Values of type 'String' are used to represent sequences of characters, such as text. The size of each character in a string value is target dependent, but is at least 8 bits.
The syntax used for declaring values and variables of string type is :String
. For example:
Local
name
:
String
=
"John Smith"
Also, notice that string identifiers can also be declared by using the $
character. This two lines of source code are both valid and syntactically correct:
Local
message
:
String
=
"Hello world"
Local
message
$ =
"Hello world"
Strings are immutable meaning that once they are created they cannot be modified. Operations that 'modify' a string will always return a new string.
String indexing and slicing
Strings can also be indexed and sliced.
The syntax for indexing a string is: StringExpression [ IndexExpression ]
Indexing a string returns the character code of the character at IndexExpression. Index 0 is the first character in the string.
IndexExpression must be greater than or equal to 0 and less than the length of StringExpression otherwise an error occurs.
Here are some examples of indexing a string:
Print
"ABC"
[
0
]
' prints 65 - the character code of 'A'
Print
"ABC"
[
1
]
' prints 66 - the character code of 'B'
Print
"Hi~n"
[
2
]
' prints 10 - the character code of '~n'
The syntax for slicing a string is: StringExpression [ StartExpression .. EndExpression ]
Slicing a string returns a new string consisting of the characters within StringExpression from index StartExpression (inclusively) to index EndExpression (exclusively).
Both StartExpression and EndExpression are optional. If StartExpression is omitted, it defaults to 0. If EndExpression is omitted, it defaults to the length of the string.
StartExpression and EndExpression can also be negative, in which case they refer to offsets from the end of the string.
Here are some examples of slicing a string:
Print
"Hello World"
[
4
..
7
]
' prints "o W'
Print
"Hello World"
[..
5
]
' prints "Hello"
Print
"Hello World"
[
6
..]
' prints "World"
Print
"Hello World"
[..]
' prints "Hello World"
Print
"Hello World"
[-
4
..-
2
]
' prints "or"
String Methods
Strings also support a number of 'pseudo' methods and functions:
Method / Function | Description |
---|---|
Method Length() Property | Returns the number of characters in the string. |
Method Compare( str:String ) | Returns a value < 0 if the current string is less than str, a value > 0 if the current string is greater than str or 0 if this string is equal to str. |
Method Find( subString:String ) | Returns the index of the first occurrence of subString within the current string. |
Method Find( subString:String, startIndex ) | Returns the index of the first occurrence of subString within the current string, starting at index startIndex. |
Method FindLast( subString:String ) | Returns the index of the last occurrence of subString within the current string. |
Method FindLast( subString:String, startIndex ) | Returns the index of the last occurrence of subString within the current string, starting at index startIndex. |
Method Contains( subString:String ) | Returns true if the current string contains subString. |
Method StartsWith( subString:String ) | Returns true if the current string starts with subString. |
Method EndsWith( subString:String ) | Returns true if the current string ends with subString. |
Method ToLower:String() | Returns the current string converted to lower-case. |
Method ToUpper:String() | Returns the current string converted to upper-case. |
Method Trim:String() | Returns the current string with all leading and trailing whitespace characters removed. |
Method Split:String[]( separator:String ) | Returns an array of strings that contains the substrings in the current string deliminated by separator. |
Method ToChars:Int[]() | Converts string to an array of character codes. |
Method Join:String( pieces:String[] ) | Returns the elements of pieces concatenated together with the current string inserted between each substring. |
Function FromChar:String( char ) | Returns a string of length 1 consisting of a single character code. |
Function FromChars:String( chars:Int[] ) | Creates a string from an array of character codes. |
StringExpression + StringExpression | Concatenates two strings |
For example:
Print
" Hello World ~n"
.
Trim
()
'prints "Hello World"
Print
"Hello World"
.
ToUpper
()
'prints "HELLO WORLD"
String literals
String literals are sequences of characters enclosed in ""
(quotation marks):
"Hello world"
"1234"
String escape sequences
String literals may also include escape sequences - sequences of characters used to represent unprintable characters.
You can use the following escape sequences in string literals:
Escape sequence | Character code |
---|---|
~q | 34 (quotation mark ") |
~g | 96 (back-tick `) |
~n | 10 (newline) |
~r | 13 (return) |
~t | 9 (tabulator) |
~z | 0 (null) |
~u006F | unicode char 0x006F |
~~ | 126 (tilde ~) |
Here are some examples of string literals using escape sequences:
"~qHello World~q"
"~tIndented~n"
Character literals
Character literals are single printable characters enclosed in ``
(grave accents or back-ticks). They allow you to put character codes directly into the code:
`A`
Character literals support the same escape sequences as strings do.
They're typically used for string analysis:
Local
txt
$ =
"ABC"
If
txt
[
0
] =
`A`
Then
Print
"Yes, that string starts with 'A'"
The Array type
An array is a linear sequence of values that can be addressed using an integer index. Arrays in Cerberus X cannot be multi-dimensional.
Each array has an associated element type - that is, the type of the elements actually contained in the array. Due to the nature of Cerberus X, an array's element type is a purely static property. It is only known at compile time so arrays cannot be downcast at runtime.
The syntax used for declaring values and variables of array type is: : ElementType []
For example:
Local
box
:
Int
[]
' an array of ints
Local
ratio
:
Float
[]
' an array of floats
Local
thing
:
Int
[][]
' an array of arrays of ints
Array indexing and slicing
The syntax for indexing an array is: ArrayExpression [ IndexExpression ]
. For example:
Local
score
:
Int
[]=[
10
,
20
,
30
]
'a comma separated sequence
Print score
[
1
]
'prints "20"
Indexing an array yields a 'pseudo variable' of the array's element type that can be both read from and written to.
IndexExpression must be an integer expression greater than or equal to 0 and less than the length of the array otherwise an error occurs.
Like strings, arrays can also be sliced. The syntax for slicing an array is: ArrayExpression [ StartExpression .. EndExpression ]
.
Slicing an array returns 'sub array' of ArrayExpression from index StartExpression (inclusively) to index EndExpression (exclusively).
Both StartExpression and EndExpression are optional. If StartExpression is omitted, it defaults to 0. If EndExpression is omitted, it defaults to the length of the array.
StartExpression and EndExpression can also be negative, in which case they refer to offsets from the end of the array.
Here is an example of slicing an array:
Local
text
:
String
[]=[
"Cruel"
,
"Hello"
,
"World"
,
"There"
]
'a comma separated sequence
Local
helloWorld
:=
text
[
1
..
3
]
'contains ["Hello","World"]
Array methods
Arrays also support a number of 'pseudo' methods:
Method | Description |
---|---|
Method Length() Property | The number of elements in the array. |
Method Resize:Array( newLength ) | Copies the first newLength elements of the current array into a new array of length newLength, and returns the new array. |
For example:
Local
text
:
String
[]=[
"Hello"
,
"There"
,
"World"
]
' a comma separated sequence
Print text
.
Length
' prints '3'
text
=
text
.
Resize
(
2
)
Print text
.
Length
' prints '2'
Array literals
An array literal is a (possibly empty) comma separated sequence of expressions enclosed with [
and ]
. The expressions used in an array literal must be of the same type. For example:
Local
box
:
Int
[]=[]
' an empty array literal
Local
scores
:
Int
[]=[
10
,
20
,
30
]
' a comma separated sequence
Local
text
:
String
[]=[
"Hello"
,
"There"
,
"World"
]
' a comma separated sequence
The Object type
An object is an instance of a class, and contains a set of constants, variables, methods and functions.
The syntax used for declaring values and variables of object type is: : ClassIdentifier
For example:
Local
mine
:
MyClass
=
New
MyClass
Please see the classes section for more information on declaring classes and creating objects.
Object literals
The keyword Null
can be used as 'pseudo' object literal, representing the null object, which can be used for comparisons or to 'reset' a reference:
Null
Variables
A variable is a storage location used to hold values that change while your program runs.
All variables have an identifier, a type, and an optional initialiser - an expression used to set the variable to an initial value.
The type of a variable can either be declared literally, or can be deduced from the variable's initialiser.
Local variables
Local variables are temporary variables that disappear when the local scope they are declared in is destroyed.
Local variables may be declared within any local scope.
Each of the following creates a local scope:
- The statements inside a function or method.
- The statements inside an If, Else If or If block.
- The statements inside a Case or Default block.
- The statements inside a While, Repeat or For loop.
The syntax for declaring a local variable is:
Local Identifier : Type [ = Expression ]
Or...
Local Identifier := Expression
For example:
Local
age
:
Int
=
10
Local
age
:=
10
Global variables
Global variables are variables that persist during the execution of your program.
Global variables may be declared at module scope, or within a class declaration.
The syntax for declaring a global variable is:
Global Identifier : Type [ = Expression ]
Or...
Global Identifier := Expression
For example:
Global
isPlayerAlive
:
Bool
=
True
Field variables
Field variables are variables that persist as long as the object they belong to.
Field variables can only be declared within a class declaration.
The syntax for declaring a field variable is:
Field Identifier : Type [ = Expression ]
Or...
Field Identifier := Expression
Constants
A constant is a value that is evaluated at compile time, and that does not change throughout the execution of a program.
Constants may be declared at module scope, within class scope or within any local scope.
The syntax for declaring a constant is:
Const Identifier : Type = Expression
Or...
Const Identifier := Expression
Integer enumerations
An enumeration is a sequence of constant integer values that are evaluated at compile time, and that do not change throughout the execution of a program.
Enumerations may be declared at module scope, within class scope or within any local scope.
Their corresponding values are assigned automatically and unless you state a specific value, start with 0.
The syntax for declaring an enumeration is:
Enumerate Identifier , Identifier , Identifier [ , ...]
Or...
Enumerate Identifier = Startvalue , Identifier , Identifier = Startvalue [ , ...]
Expressions
Expressions are the parts of a program that perform calculations, make logical comparisons and return values from methods or functions. Expressions always evaluate to - or are used to evaluate to - a value of certain type.
Operators and Order of Operations (Precedence)
The following table shows the operators available in Cerberus X, grouped by priority:
Operator syntax | Description | Update assignment equivalent |
---|---|---|
New ClassType | Create a new object | |
Null | The null object | |
True | Boolean true | |
False | Boolean false | |
Self | Self object reference | |
Super | Super object reference | |
Literal | Literal | |
Identifier | Identifier | |
. Identifier | Scope member access | |
( ExpressionSeq ) | Invoke | |
[ IndexExpression ] | Index | |
+ | Unary plus | |
- | Unary minus | |
~ | Bitwise complement | |
Not | Boolean inverse | |
* | Multiplication | *= |
/ | Division | /= |
Mod | Modulus | Mod= |
Shl | Bitwise shift left | Shl= |
Shr | Bitwise shift right (signed) | Shr= |
+ | Addition | += |
- | Subtraction | -= |
& | Bitwise 'and' | &= |
~ | Bitwise 'xor' | ~= |
| | Bitwise 'or' | |= |
= | Equals | |
< | Less than | |
> | Greater than | |
<= | Less than or equals | |
>= | Greater than or equals | |
<> | Not equals | |
And | Conditional 'and' | |
Or | Conditional 'or' |
Note that in update assignments the right-hand-side is completely evaluated first before the update takes place.
Balancing argument types
When performing binary arithmetic (*
, /
, Mod
, +
, -
) or comparison operations (=
, <
, >
, <=
, >=
, <>
), operator arguments are 'balanced' according to the following rules:
- If either operand is not string, float or int, then error,
- else if either operand is a string, then the balanced type is string,
- else if either operand is a float, then the balanced type is float,
- else the balanced type is int.
In the case of arithmetic operations, arguments are first implicitly converted to the balanced type if necessary, and the result is also of the balanced type.
In the case of comparison operations, arguments are first implicitly converted to the balanced type if necessary, and the result is boolean.
The only valid arithmetic operation that can be performed on strings is addition, which performs string concatenation on the arguments.
Conditional operators
The arguments of conditional operations (And
, Or
) are first converted to boolean if necessary and the result is boolean.
In the case of Or
, if the left-hand-side expression evaluates to true, the right-hand-side expression is not evaluated.
For example:
If
car
<>
Null Or
GetSpeed
()>
10
Then
...
In the above example, if car is not null, then the right-hand-side of the Or
is not evaluated - ie: the GetSpeed
function is never called.
In the case of And
, if the left-hand-side expression evaluates to false, the right-hand-side expression is not evaluated.
For example:
If
enemies
.
Count
>
0
And
HasEnemyInSight
()
Then
...
In the above example, if enemies.Count
is <= 0, then the right-hand-side of the And is not evaluated - ie: the HasEnemyInSight
function is never called.
Bitwise operators
When performing bitwise operations (Shl
, Shr
, &
, |
, ~
), arguments are first implicitly converted to integers if necessary before the operation is performed. The result is also an integer.
Implicit conversions
Implicit conversions are automatic conversions performed when assigning a value to a variable, passing parameters to a function, returning a value from a function or when balancing operator operands.
Cerberus X supports the following implicit conversions:
Source type | Target type | Notes |
---|---|---|
Boolean | Integer | Result is 1 if source is true, 0 if false. |
Integer | Floating point | |
Integer | String | |
Floating point | Integer | Value is converted by discarding fractional part. |
Floating point | String | Conversion is target dependent. |
Derived class object | Base class object | Upcast operation. |
Explicit conversions
Explicit conversions are conversions from one type to another type which can be performed manually by the programmer .
The syntax for performing an explicit conversion is: TargetType ( Expression )
For example:
Local
energyFloat
:
Float
=
120.1000002001
Local
energyInt
:
Int
=
Int
(
energyFloat
)
'Is 120 now
You can perform the following explicit conversions in Cerberus X:
Source type | Target type | Notes |
---|---|---|
Integer | Boolean | Result is true if source <> 0, else false. |
Floating point | Boolean | Result is true if source <> 0.0, else false. |
Array | Boolean | Result is true if source.Length <> 0, else false. |
Object | Boolean | Result is true if source <> null, else false. |
String | Boolean | Result is true if source <> "", else false. |
String | Integer | Conversion is target dependent. |
String | Floating point | Conversion is target dependent. |
Base class object | Derived class object | Result is null if source is not a superclass of derived class. |
In some circumstances, Cerberus X will automatically perform an explicit conversion of a non-bool value to bool for you. This will occur when evaluating an expression for use with the If
and While
statements; the Until
part of a Repeat
loop; and when evaluating the arguments for the Not
, Or
and And
operators. This allows you to use 'shortcut' code such as: If x Then y
without the need to compare x
with 0
, ""
, []
or Null
.
Boxing and unboxing conversions
A 'box' object is an object designed to contain a single primitive int, float or string value. The process of placing a value into a box object is known as 'boxing', while extracting a value from an object is known as 'unboxing'. To help with writing box objects, Cerberus X provides some simple features for boxing and unboxing values:
- An int, float or string value will be automatically converted to a new box object if that object provides a suitable
New( Int )
,New( Float )
orNew( String )
constructor. - An object will be automatically converted to an int, float or string value if that object provides a suitable
ToInt:Int()
,ToFloat:Float()
orToString:String()
method.
For example, here is a simple box class designed to hold an integer value:
Class
IntBox
Field
value
:
Int
Method New
(
value
:
Int
)
Self
.
value
=
value
End
Method
ToInt
:
Int
()
Return
value
End
End
Function
Main
()
Local
box
:
IntBox
box
=
10
Local
t
:
Int
=
box
Print t
End
Statements
Program statements may only appear within method or function declarations.
A ;
character may optionally appear after any statement, and multiple statements may be placed on the same source code line if separated by the ;
character.
The If statement
The If
statement allows you to conditionally execute a block of statements depending on the result of a series of boolean expressions.
The first boolean expression that evaluates to true will cause the associated block of statements to be executed. No further boolean expressions will be evaluated.
If no boolean expression evaluates to true, then the final Else
block will be executed if present.
The syntax for the If statement is:
If Expression [ Then ]
Statements...
ElseIf Expression [ Then ]
Statements...
Else
Statements...
EndIf
There may be any number of ElseIf
blocks, or none. The final Else
block is optional.
End
or End If
may be used instead of EndIf
, and Else If
may be used instead of ElseIf
.
In addtion, a simple one line version of If is also supported:
If Expression [ Then ] Statement [ Else Statement ]
The Select statement
The Select
statement allows you to execute a block of statements depending on a series of comparisons.
The first comparison to produce a match will cause the associated block of statements to be executed.
If no comparisons produce a match, then the final Default
block will be executed if present.
The syntax for the Select statement is:
Select Expression
Case Expression [ , Expression... ]
Statements...
Default
Statements...
End [ Select ]
There may be any number of Case
blocks, or none. The final Default
block is optional. If the Default
block is present, it must appear after all Case
blocks.
The While loop
The While
loop allows you to execute a block of statements repeatedly while a boolean expression evaluates to true.
Note that a While
loop may never actually execute any of it's statements if the expression evaluates to false when the loop is entered.
The syntax for the While
loop is:
While Expression
Statements...
Wend
End
or End While
may be used instead of Wend
.
Exit and Continue may be used within a While
loop to abruptly terminate or continue loop execution.
The Repeat loop
Like the While
loop, the Repeat
loop also allows you to execute a block of statement repeatedly while a boolean expression evaluates to true.
However, unlike a While
loop, a Repeat
loop is guaranteed to execute at least once, as the boolean expression is not evaluated until the end of the loop.
The syntax for Repeat
/Until
loops is:
Repeat
Statements...
Until Expression
Or...
Repeat
Statements...
Forever
Exit and Continue may be used within a Repeat
loop to abruptly terminate or continue loop execution.
The numeric For loop
A numeric For
loop will continue executing until the value of a numeric index variable reaches an exit value.
The index variable is automatically updated every loop iteration by adding a constant step value.
The syntax for a numeric For
loop is:
For [ Local ] IndexVariable = FirstValue To | Until LastValue [ Step StepValue ]
Statements...
Next
End
or End For
may be used instead of Next
.
If present, Local
will create a new local index variable that only exists for the duration of the loop. In addition, IndexVariable must include the variable type, or :=
must be used instead of =
to implicitly set the variable's type.
If Local
is not present, IndexVariable must be a valid, existing variable.
The use of To
or Until
determines whether LastValue should be inclusive or exclusive.
If To
is used, the loop will exit once the index variable is greater than LastValue (or less than if StepValue is negative).
If Until
is used, the loop will exit once the index variable is greater than or equal to LastValue (or less than or equal to if StepValue is negative).
If omitted, StepValue defaults to 1.
Exit and Continue may be used within a numeric For
loop to abruptly terminate or continue loop execution.
The For EachIn loop
A For
EachIn
loop allows you to iterate through the elements of a collection.
A collection is either an array, a string, or a specially designed object.
The syntax for a For EachIn loop is:
For [ Local ] IndexVariable = EachIn Collection
Statements...
Next
End
or End For
may be used instead of Next
.
If present, Local
will create a new local index variable that only exists for the duration of the loop. In addition, IndexVariable must include the variable type, or :=
must be used instead of =
to implicitly set the variable's type.
If Local
is not present, IndexVariable must be a valid, existing variable.
If Collection is an array, the loop will iterate through each element of the array, and the type of the index variable must match the element type of the array.
If Collection is a string, the loop will iterate through each each character code of the string, and the type of the index variable must be numeric.
If Collection is an object, it must provide the following method:
Method
ObjectEnumerator
:
Object
()
The object returned by this method must itself provide the following methods:
Method
HasNext
:
Bool
()
Method
NextObject
:
Object
()
This allows you to build 'collection' style objects, such as the List and Map classes included in the standard Cerberus X modules that can be iterated through using For EachIn loops.
Exit and Continue
Exit
can be used within While
, Repeat
and For
loops to abruptly exit the loop before the loop termination condition has been met.
Continue
can be used within While
, Repeat
and For
loops to force the loop to abruptly skip to the next loop iteration, skipping over any statements that may be remaining in the current loop iteration.
Assignment and Update Assignment statements
An assignment statement modifies a variable's value, and has the syntax:
VarExpression Operator Expression
Where VarExpression is an expression that evaluates to a variable, and Operator is one of the following:
Operator | Description |
---|---|
= | Assignment |
*= | Multiplication update assignment |
/= | Division update assignment |
Mod= | Modulus update assignment |
Shl= | Bitwise shift left update assignment |
Shr= | Bitwise shift right (signed) update assignment |
+= | Addition update assignment |
-= | Subtraction update assignment |
&= | Bitwise 'and' update assignment |
~= | Bitwise 'xor' update assignment |
|= | Bitwise 'or' update assignment |
The =
operator is used for plain assignment, while the remaining operators are used for update assignments. An update assignment is a short notation equivalent to:
VarExpression = VarExpression Operator ( Expression )
Expression statements
You may also use certain expressions as program statements. These are:
- Function or method call expressions.
New
expression.
Functions
A function is a self contained block of statements that can be called (or invoked) repeatedly from elsewhere in the program. Functions can also be passed parameters and return a result.
The syntax for declaring a function is:
Function Identifier : ReturnType ( Parameters )
Statements...
End [ Function ]
Functions that don't return a value use :Void
as ReturnType.
For example:
Function
Eat
:
Void
(
amount
:
Int
)
...
End
Parameters is a comma separated sequence of function parameters:
Identifier : Type [ = InitExpression ]
Or...
Identifier := InitExpression
If you provide an InitExpression when declaring a function parameter, this means that the parameter has a default value and that the parameter can be optionally omitted when the function is called.
Once you have declared a function, it can be called with the syntax:
FunctionIdentifier ( Arguments )
where Arguments is a comma separated sequence of expressions.
For example:
Function
Sum
:
Int
(
x
:
Int
,
y
:
Int
)
Return
x
+
y
End
Function
Main
()
Print Sum
(
10
,
20
)
End
Here is an example of using default parameters:
Function
Sum
(
x
:
Int
=
0
,
y
:
Int
=
0
,
z
:
Int
=
0
)
Print x
+
y
+
z
End
Function
Main
()
Print Sum
()
'same as calling Sum( 0,0,0 )
Print Sum
(
10
,
20
)
'same as calling Sum( 10,20,0 )
Print Sum
(
10
,,
30
)
'same as calling Sum( 10,0,30 )
End
Function overloading
Functions can also be overloaded. This means that multiple declarations can share the same name, as long as they each have different parameters. Methods can be overloaded in exactly the same way as functions.
When a function is called, Cerberus X looks at the number and type of the parameters used in the call and looks for a matching overloaded version to use. For example:
Function
Add
(
value
:
Int
)
End
Function
Add
(
value
:
Float
)
End
Function
Add
(
value
:
String
)
End
Function
Main
()
Add
(
10
)
'calls first version as 10 is of type Int
Add
(
10.0
)
'calls second version as 10.0 is of type Float
Add
(
"10"
)
'calls third version as "10" is of type String
End
The number of parameters can also be used to differentiate between overloaded functions. For example:
Function
Set
(
x
)
End
Function
Set
(
x
,
y
)
End
Function
Main
()
Set
(
10
)
'calls first version
Set
(
10
,
20
)
'calls second version as it has two parameters
End
When determining which overloaded version to actually use, Cerberus X uses the following logic:
- If an overloaded version is found that is an exact match for the number and type of function call parameters, that version is used. Note that if the function call involves the use of any default parameters, it is not considered an exact match.
- Otherwise, if there is exactly one overloaded version that can be called by implicitly converting the function call parameters, that version is used.
- Otherwise, an error is generated. In this case, you will need to manually cast some or all of the function call parameters to create an exact match.
For example:
Function
Add
(
value
:
Int
)
End
Function
Add
(
value
:
String
)
End
Function
Main
()
Add
(
10
)
'OK, calls first version
Add
(
"10"
)
'OK, calls second version
Add
(
10.0
)
'error - unable to determine which version to call
End
The error is caused by the fact that the function call parameter - '10.0' - is a floating point value and can therefore be implicitly converted to either an integer or a string, so Cerberus X cannot decide which overload to use.
To solve this problem, you would need to explicitly cast the parameter to either an integer or a string to give Cerberus X a 'hint' about which version you want used. For example:
Add
(
Int
(
10.0
) )
'Casts the float to an integer, calls first version
Methods
A Method is a function that is bound to a class. A method has implicit access to all members of its class, such as fields, globals and other methods and functions. Methods can be overloaded in exactly the same way as functions.
The syntax for declaring a method is similar to that for declaring a function:
Method Identifier : ReturnType ( Parameters ) [ Property ]
Statements...
End [ Method ]
Within a method you can also use the Self
and Super
keywords:
Self
may be used within a method to access the object the method is associated with.Super
may be used within a method to call 'super class' methods.
Property methods
The optional Property
keyword can be used to declare a 'property method'.
A property method with 0 parameters can be invoked without any brackets. A property method with 1 parameter can be invoked be using it as the left-hand-side of an assignment statement, in which case the right-hand-side expression of the assignment is passed to the property method.
You can therefore create methods that behave like fields, but actually execute code when they are read or written. You can use method overloading to provide both read and write property methods, or provide just a read method, or even just a write method. For example:
Class
MyClass
Field
_value
:
Int
Method
Value
(
val
:
Int
)
Property
_value
=
val
Print
"Set _value to "
+
_value
End
End
Function
Main
()
Local
myObject
:=
New
MyClass
myObject
.
Value
=
100
' sets _value and outputs "Set _value to 100"
End
It is illegal to declare a property method with 2 or more parameters.
Classes
A class is a kind of 'blueprint' for creating objects at runtime.
The syntax for declaring a class is:
Class Identifier [ < Parameters > ] [ Extends BaseClass ] [ Implements Interfaces ]
Declarations...
End [ Class ]
Classes can contain field, method, constant, global and function declarations.
If no base class is specified using Extends
, the class defaults to extending the built in Object class.
The Implements
keyword is used to implement interfaces, and must be followed by a comma separated list of interface names. Please refer to the Interfaces section for more on interfaces.
Fields and methods are class members only accessible via a valid object reference of that class. But constants, globals and functions can be accessed via the class identifier itself. As a class is also a valid scope, any non-private constants, globals and functions declared within a class can be accessed outside of the class using the scope member access operator '.
'. For example:
Class
C
Global
T
End
Function
Main
()
C
.
T
=
10
End
Once you have declared a class, you can create objects of that class at runtime using the New
operator. For example:
Class
MyClass
Field
x
,
y
,
z
End
Function
Main
()
Local
myObject
:=
New
MyClass
myObject
.
x
=
10
myObject
.
y
=
20
myObject
.
z
=
30
End
Constructors
Constructors are methods that are called each time an object is created with New
.
To declare a constructor, you simply declare a class method and name it New
. Constructors may not have a type declaration nor a return statement.
Constructors can take parameters and can be overloaded.
To invoke a super class constructor within a constructor, use the Super
keyword.
Class
MyClass
Field
_value
:
Int
Method New
(
val
:
Int
)
' constructor or MyClass
_value
=
val
End
End
Function
Main
()
Local
myObject
:=
New
MyClass
(
100
)
' calls the constructor of MyClass
Print myObject
.
_value
' prints "100"
End
Generic classes
Generic classes allow you to write code that is not specific to a single data type.
A generic class is declared in a similar way to a normal class, only with an additional set of 'type parameters' enclosed within <
and >
.
For example:
Class
Pointer
<
T
>
Method
Set
(
data
:
T
)
_data
=
data
End
Method
Get
:
T
()
Return
_data
End
Private
Field
_data
:
T
End
Type parameters can be any valid identifier. Here, T
is such a type parameter.
Within the declaration of a generic class, type parameters may be used anywhere a type is normally expected, for example, when declaring variables and function return types, and when creating new objects or arrays.
When it comes to actually using a generic class, you must provide actual types to be used in place of type parameters. Types parameters can be of any valid type, including int, float, string and array.
For example:
Class
Actor
End
Function
Main
()
Local
pointer
:
Pointer
<
Actor
>
pointer
=
New
Pointer
<
Actor
>
pointer
.
Set
New
Actor
Local
actor
:=
pointer
.
Get
()
End
The syntax Pointer<Actor>
indicates an 'instantiation' of the generic class Pointer<T>
.
This is in itself a unique class, so each time you use the Pointer<T>
class with a different type for T
, you are actually creating a whole new class.
Generic classes are commonly used for writing container classes such as lists, stacks and so on, and the standard Cerberus X modules provide a set of simple generic container classes.
Interfaces
An interface is similar to a class, except that it can only contain constants and abstract methods.
Classes can implement an interface using the Implements
keyword in the class declaration.
Classes that implement an interface must declare each method declared in the interface.
An interface can be used where ever a class is expected, for example when declaring the types of variables, or function return types. An interface cannot however be used with New
.
An Interface can also optionally extend existing interfaces, in which cases all methods in all extended interfaces must be declared by any implementing classes.
Interfaces can not be generic.
The syntax for declaring an interface is:
Interface Identifier [ Extends Interfaces ]
Declarations...
End [ Interface ]
All methods declared inside an interface are automatically treated as abstract, and can therefore have no 'body'.
Here is an example of using interfaces:
Interface
Moveable
Method
Move
()
End
Interface
Drawable
Method
Draw
()
End
Class
Actor
Implements
Moveable
,
Drawable
Method
Move
()
Print
"Actor.Move()"
End
Method
Draw
()
Print
"Actor.Draw()"
End
End
Function
Move
(
moveable
:
Moveable
)
' moveable can be any class, as long as it implements 'Moveable'
moveable
.
Move
End
Function
Draw
(
drawable
:
Drawable
)
' drawable can be any class, as long as it implements 'Drawable'
drawable
.
Draw
End
Function
Main
()
Local
actor
:=
New
Actor
Move actor
Draw actor
End
Exceptions
Exceptions are objects that can be 'thrown' to inform your program of unusual or abnormal behaviour.
By placing code inside a Try
block, you can catch thrown exceptions with a corresponding Catch
block. For example...
Function
Main
()
Try
Print
"Hello World!"
Throw New Throwable
Print
"Where am I?"
Catch
ex
:
Throwable
Print
"Caught a Throwable!"
End
End
Try this with and without the throw statement.
Throw
statements may appear anywhere in your application, not just inside a Try
block. When an object is thrown, it is thrown to the most recently executed Try
block capable of catching the object.
The class of objects used with throw and catch must extend the built-in class Throwable
. The Throwable
class itself simply extends Object and provides no new methods or fields, so you may wish to extend Throwable
to create your own exception classes with more functionality.
You can also catch multiple classes of exception object per Try
block, for example:
Class
Ex1
Extends Throwable
End
Class
Ex2
Extends Throwable
End
Function
Main
()
For Local
i
:=
1
To
10
Try
If
(
i
&
1
)
Throw New
Ex1
Else Throw New
Ex2
Catch
ex
:
Ex1
Print
"Caught an ex1!"
Catch
ex
:
Ex2
Print
"Caught an ex2!"
End
Next
End
When a Try
block has multiple Catch
blocks and an exception is thrown, the first Catch
block capable of handling the exception is executed. If no suitable Catch
block can be found, the exception is passed to the next most recently executed Try
block, and so on.
If no Catch
block can be found to catch an exception, a runtime error occurs and the application is terminated.
Modules
A Cerberus X module corresponds to a single Cerberus X source file, and provides a named scope for the constants, globals, functions and classes declared in that file. Every Cerberus X source file declares a module, and every module has an associated source file.
The name of the module scope is the same as the name of the file (minus the directory path and file extension), so the names of Cerberus X source files must also be valid Cerberus X identifiers.
It is also strongly recommend that file/module names be completely lower-case - both to prevent any issues with cased/uncased file systems and to provide consistency with the standard module set.
One module may import another using the import statement. All import statements must appear at the top of a module before any declarations. The syntax for an import statement is:
Import ModulePath
Where ModulePath describes the file location of the Cerberus X module to import. This must be a sequence of 'dot' separated identifiers, and is treated as a relative file system path with dots representing directory separators. The last component in the path represents either an actual .cxs source file, or a directory containing a .cxs source file of the same name.
Given a module's relative path, Cerberus X will look for an actual module to import in the following locations (and in this order):
- The current directory - ie: the directory the importing file is in.
- The project directory - ie: the directory the main source file is in. This is the source file that was passed to the 'trans' compiler, and that contains the declaration for Main().
- The modules directory - ie: the directory named 'modules' in the Cerberus X distribution.
For example, given the following import directive:
Import
myutil
.
mycolor
Cerberus X will first look for the files myutil/mycolor.cxs and myutil/mycolor/mycolor.cxs in the current directory.
(Note: the reason modules are allowed to be represented as either a single .cxs file, or as a .cxs file within a directory of the same name is for pure convenience. Sometimes it's more convenient for a module to consist of a single file, while sometimes it's more convenient for a module to have its own directory.)
If either is found, it is imported into the current module and the search ends.
If both are found, an error is generated.
If neither is found the search continues with the project directory and, failing that, the modules directory.
If the module is not found anywhere, an error is generated.
Once successfully imported, the importing module can access the declarations made in the imported module, by using the imported module's name as a scope.
Here is a simple import example:
'----- file1.cxs -----
Import
file2
'after this, file2 can be used as a scope
Function
Main
()
Print file2
.
X
'access global X in file2 module
file2
.
Test
'access function Test in file2 module
End
'---- file2.cxs ----
Global
X
:=
1
Function
Test
()
Print
"file2.Test"
End
When accessing identifiers in imported modules, Cerberus X allows you to omit the module scope as long as there are no 'clashes' between identifiers in multiple modules. For example:
'----- file1.cxs ----
Import
file2
Import
file3
Function
Main
()
Print X
'OK, accesses file2.X
Print Y
'OK, accesses file3.Y
Test
'ERROR! Which Test? file2.Test or file3.Test?
file2
.
Test
'OK, I now know which module to get Test from
End
'----- file2.cxs -----
Global
X
:=
1
Function
Test
()
Print
"file2.Test"
End
'----- file3.cxs -----
Global
Y
:=
2
Function
Test
()
Print
"file3.Test"
End
By default, any imports made by a module are automatically made available to importers of that module. That is, if module X imports module Y, and module Y imports module Z, the result is that module X effectively imports module Z.
However, if an import is declared to be private, that import is NOT made available. For example:
'----- file1.cxs -----
Import
file2
Function
Main
()
Print X
'OK, accesses file2.X
Print Y
'OK, accesses file3.Y
Print Z
'ERROR! can't see file.Z
End
'----- file2.cxs -----
Import
file3
'Public import: When you import file2, you also import file3
Private
Import
file4
'Private - ie: internal use only. Only file2 can access file4. file1 cannot access file4.
Public
Global
X
:=
1
'----- file3.cxs -----
Global
Y
:=
2
'----- file4.cxs -----
Global
Z
:=
3
Modules can be stored in a directory hierarchy and imported using a 'dotted' module path, for example:
'----- file1.cxs -----
Import
file2
Import
util
.
file3
Function
Main
()
Print file2
.
X
Print file3
.
Y
End
'----- util/file2.cxs ----
Global
X
:=
1
'----- util/file3.cxs -----
Global
Y
:=
2
Note that the directory path (in this case, 'util') is not used in any way except to locate the module. The module name is still just 'file3' - not 'util.file3'.
The Alias directive
The Alias
directive allows you to assign a local name to a constant, global, function or class declared in another module. This can be used to create 'shortcuts' for clashing identifiers.
The syntax for Alias
is:
Alias Identifier = ModulePath . Identifier
Alias
directives must appear in the 'import' section of a module, before any code.
For example:
'----- file1.cxs -----
Import
file2
Import
file3
Alias
T
=
file2
.
T
'which 'T' to use
Function
Main
()
Print T
'Prints '1'
End
'----- file2.cxs -----
Global
T
:=
1
'----- file3.cxs -----
Global
T
:=
2
Public and Private
The Public
and Private
directives are used to control the visibility of subsequent declarations in a module or class.
If the Public
directive is used in the main body of a module, all subsequent declarations will be public, and will be visible outside of the current module.
If the Private
directive is used in the main body of a module, all subsequent declarations will be private and will not be visible outside of the current module.
For example:
Private
Global
x
,
y
,
z
'These are private to the current module
Public
Global
P
,
Q
,
R
'These can be used by any module
When used inside a class declaration, Public
and Private
work in a similar way to control the visibility of subsequent member declarations. For example:
Class
MyClass
Private
Field
x
,
y
,
z
'these are NOT visible outside of this module.
Public
Field
P
,
Q
,
R
'these ARE visible outside of this module.
End
Note that private class members are not private to the class, but to the entire module. This means that code outside of the class but within the same module can still access class private members.
External declarations
The Extern
directive is used to connect Cerberus X code to non-Cerberus X code. It lets you mix Cerberus X code (to be translated into the target platform language) with native target platform code.
When the Extern
directive is used in the main body of a module, all subsequent global, function and class declarations will be treated as external declarations.
External declarations are assumed to be implemented elsewhere in native code, and as such may not contain a 'body'.
In the case of external global variables, this means the global may not be initialized - it is assumed to be initialized by native code.
In the case of external functions, this means the function may not contain any code, and must not be terminated with an End directive.
In the case of external classes, this means any globals or methods declared in the class may not contain a 'body' either.
External declarations may however be assigned a 'symbol' in the form of a string literal. This is the native symbol to be used by the Cerberus X translator when the declaration is referenced by Cerberus X code.
By default, external declarations are public. You can use Extern Private
to prevent external declarations from being visible outside the current module.
Here are some examples of using extern:
Extern
Global
ActiveDriver
:
Driver
=
"xyzActiveDriver"
'Native name of this global is xyzActiveDriver
Class
Driver
=
"xyzDriver"
'The native name of this class is 'xyzDriver'.
Method
Method1
()
'By default, native name is same as declaration name - Method1 here.
Method
Method2
()
'native name is Method2
End
Public
'return to public declarations
Memory Management
Cerberus X is a garbage collected language, and depends on the underlying target language to provide memory management.
Finalizers are not supported. If you need an object to be 'destroyable', you will need to add a 'Destroy' type method.
The garbage collector is capable of collecting cyclic data structures such as linked lists automatically.
The current C++ garbage collector will only collect garbage when control is returned to the OS. In the case of C++ Mojo targets such as IOS and GLFW, this occurs after any of the 'On' methods such as OnCreate
, OnUpdate
etc return.
In general, the best way to use the garbage collector is to ignore it! Although such practices as 'nulling out' object references are common, they are seldom required.
But it's a good idea to monitor your apps memory requirements as you develop anyway. This will allow you to catch any memory issues, GC related or otherwise, early on.
The preprocessor
Cerberus X contains a very simple built-in preprocessor based on the syntax of the Cerberus X If statement that allows you to enable or disable blocks of code from being generated or translated, based on certain conditions.
The following preprocessor directives are supported:
Directive | Description |
---|---|
#If | Start of conditional directive block |
#ElseIf | Alternative conditional directive block |
#Else | Alternative conditional directive block |
#End | End of directive block |
#Rem | Start of block comment |
#Print | Writes to the 'build log' |
#Error | Writes to the 'build log' and aborts the building process |
Preprocessor directives must appear at the start of a line, and may be preceded by optional whitespace.
The #If
and #ElseIf
directives must be followed by a constant Cerberus X expression. If this expression evaluates to true, then code generation is enabled, otherwise it is disabled.
The following built in constants may be used in preprocessor expressions:
Constant | Description |
---|---|
HOST | Host operating system, one of: winnt , macos , linux |
LANG | Target language, one of: js , as , cs , cpp , java |
TARGET | Target system, one of: ios , android , winrt , glfw , html5 , flash |
CONFIG | Target build config, one of: debug , release |
CD | Directory of source file being built |
MODPATH | Module being built |
The #Rem
directive is exactly the same as #If False
- it unconditionally disables code generation. Note that this allows 'block rems' to be 'nested'.
Custom preprocessor directives
It is possible to add your own preprocessor directives in your code to make it customisable:
#BLOCK_TO_RUN =
1
#If
BLOCK_TO_RUN
=
0
Print
"Do this."
#Else
Print
"Do that."
#End
Optimisation
Cerberus X optimises your code during translation for size. This means, unused functions and class members are not copied to the transpiled result. It doesn’t matter whether the code in question is in an imported module or in the main file.
The following example code demonstrates this behaviour:
Strict
Function
Main
:
Int
()
Print
"Starting test..."
' creating instances of the erroneous classes alone does not provoke
' any errors, since the erroneous methods, functions and fields will
' be optimized away
Local
t
:
Test
=
New
Test
()
Local
ext
:
ExtTest
=
New
ExtTest
()
Local
err
:
ErrClass
=
New
ErrClass
()
' EXAMPLE 1
' Unused functions are optimized away.
' As long as ErryFunc() ain't called, it won't cause troubles:
'ErryFunc() ' (un-comment for proof)
' EXAMPLE 2
' Unused methods are optimized away.
' As long as t.ErryMeth() ain't called, it won't cause troubles:
't.ErryMeth() ' (un-comment for proof)
' EXAMPLE 3
' Although ExtClass does a few things wrong, like overwriting an erroneous
' method and overwriting unmatching methods, it won't cause troubles as
' long as the extending methods aren't called:
'ext.ErryMeth() ' (un-comment for proof)
'ext.ErryMeth2() ' (un-comment for proof)
ext
.
OkMeth
()
' EXAMPLE 4
' Although ErrClass has an illegal initial value for a field, it won't cause
' troubles as long as the field ain't accessed:
'err.all.Sort() ' (un-comment for proof)
Print
"Done."
Return
0
End
' erroneous function
Function
ErryFunc
:
Void
()
' Error : Void functions may not return a value.
Return False
End
' class with erroneous method
Class
Test
' erroneous method
Method
ErryMeth
:
Void
()
' Error : Void functions may not return a value.
Return False
End
' correct method (just named like that for compatibility with ErryClass)
Method
ErryMeth2
:
Int
()
Return
1
End
End
' erroneous extending class
Class
ExtTest
Extends
Test
' would be ok, but the extended method is erroneous
Method
ErryMeth
:
Void
()
Print
"ExtTest.ErryMeth() invoked"
End
' Error : Overriding method does not match any overridden method.
Method
ErryMeth2
:
Int
(
arg
:
Int
)
Return
1
End
' totally ok
Method
OkMeth
:
Void
()
Print
"ExtTest.OkMeth() invoked"
End
End
' erroneous class
Class
ErrClass
' Error : Cannot convert from Bool to List<ErrClass>.
Field
all
:
List
<
ErrClass
> =
True
End
Asset import
The Import
keyword can be used to provide assets to the current project which are not stored in the project data directory. See the resource paths documentation for the definition of the project data directory and how to load resources.
However, if you need access to a file that is not in the data directory, you can tell Cerberus X to include that file in your build and make it visible and accessible for data loading by 'importing' it with it's relative file path. For example:
Import
"assets/myfile.png"
' makes assets/myfile.png accessible
Image
.
Load
(
"myfile.png"
)
' loads the file
I've seen...
Have you seen a strange notation in a Cerberus X source file and don’t know what it means? This condensed overview will help you:
Notation | Description |
---|---|
Identifier ? | Short notation for the Bool type |
Identifier % | Short notation for the Int type |
Identifier # | Short notation for the Float type |
Identifier $ | Short notation for the String type |
:= | Implicit typing when declaring variables |
$ Literal | Hexadecimal literal |
` Character ` | Character literal |
[ .. ] | Array slicing used to copy an array |
[ Index0 .. Index1 ] | String slicing or array slicing |
< Identifier > | Generic class |
Extern Identifier = "AltIdentifier" | Native name of object in external code |
*= | Multiplication update assignment |
/= | Division update assignment |
Mod= | Modulus update assignment |
Shl= | Bitwise shift left update assignment |
Shr= | Bitwise shift right (signed) update assignment |
+= | Addition update assignment |
-= | Subtraction update assignment |
&= | Bitwise 'and' update assignment |
~= | Bitwise 'xor' update assignment |
|= | Bitwise 'or' update assignment |
Statement ; Statement | The ; character is used to put multiple statements on one line |
#If | Preprocessor directive |
Glossary
If you didn't read the language documentation attentive enough to find what you were looking for, you can try to find a cue in this alphabetically index.
Arithmetic operations
Array
Assets
Assignments
Balancing
Bitwise operators
Bool
Boxing
Casting
Catch
Char literals
Classes
Collections
Comments
Comparison operators
Conditional operators
Constants
Constructors
Continue
Conversions
Datatypes
Declarations
EachIn
Enumerations
Escape sequences
Exceptions
Exit
Extends
Extern
External declaration
Field
Float
For
Garbage collector
Generics
Global
HasNext
Hexadecimal literals
Identifiers
If
Import assets
Import modules
Inheritance
Int
Interfaces
Literals
Local
Logical operators
Main
Media
Memory management
Module Paths
Modules
Naming Conventions
NextObject
Object
ObjectEnumerator
Operators
Optimisation
Optional parameters
Order of operations
Overloading
Precedence
Preprocessor
Private
Properties
Public
Repeat
Resources
Select
Self
Short notation for assignments
Short notation for types
Slicing (Arrays)
Slicing (Strings)
Strict
String
Super
Throw
Try
Types
Update assignments
Visibility
Void functions
While