If I had the chance of designing a low-level programming language, similar to C, how would I go about it? Unlike C, though, I wanted to make this a strongly typed language and meant to be portable across a wide variety of devices without worrying too much about target differences.
Monotype text in bold is used for reserved words, and should not be used for new identifiers. Monotype text in italic is meant to be replaced by actual identifiers or other code.
The source code is divided between files of two types: ch and cm, just like C. ch contains the declarations and public bits of the program, while cm holds the implementations and all the private things.
There are two kinds of comments: single-line and multi-line. The first runs from a semicolon until the end of the line:
; single line comment
The second kind is text delimited by braces:
{ multiple line comment
{ nested comment }
the rest of the first comment }
An identifier is a sequence of characters that begins with either an underscore or a letter (uppercase or lowercase) from a to z and is followed by more underscores or letters, or digits. Identifiers follow camel case notation. Variables and functions begin with a lowercase letter. Constants begin with the character 'k'. Types begin with uppercase letters.
In a way similar to Java, ch files begin with
package developer-name:module-name
Here, developer-name and module-name are identifiers, and are used together as a sort of namespace. Then follow the imports:
import file-name, ...
import developer-name:module-name, ...
import developer-name:module-name:file-name, ...
...
These are useful not only for not specifying during use the full name of the entities declared in ch files, but also for telling the compiler about such entities and their features in order to aid in error prevention. file-name does not have an extension - it's assumed to always be ch.
The most basic type is the familiar type
void
used to tell the compiler that a function returns no value, or that it receives no values, or that a pointer variable is able to point to anything in memory. Then comes the boolean type
bool
There are only two literals of this type, namely
false
true
We also have the integral signed types
byte
short
int
long
and the unsigned versions
ubyte
ushort
uint
ulong
which are, each respectively, 8, 16, 32, and 64 bits wide. Literals of these types can be
0b01, -0o07, 0h0F
0d32, 32
-5, +5
0d is optional, and is used to write a decimal number. 0b just before a number tells the compiler that that number is in binary, 0o is used for octal numbers, and 0h for hexadecimal ones. When writing literals, there is no need to specify their sizes - the compiler adjusts them accordingly. The floating point types
float
double
are 32 or 64 bits wide, respectively. Some examples of literals of these types are
0.0, 1.0, -3.1415
2.0e-5, -4.75E14
It is mandatory to have digits before and after the decimal period. There are three more numeric types, namely
word
uword
fword
Their sizes depend on the architecture where they are used. In 8-bit CPUs they are 8 bits wide, in 16-bit CPUs they are 16 bits wide, and so forth. Then there are the (Unicode) characters, declared to be of type
char
A literal of this type is any character enclosed in single quotes, as in
'a', '\n', '\u0011EEFF'
The last examples show that escape sequences can also be used to define characters. A
string
is a series of (Unicode) characters, one after the other. It is written as those characters enclosed in double quotes, as in
"This is a string!"
A string is a bit safer than C's strings, for it is not null-terminated. Instead, its size is recorded along with its contents. If we wish, we could write
"This is a string!\0"
Finally, the last basic type is the
range
A literal of this type is written
0..10
This is useful in loops, as we'll see below. The extremes (0 and 10 in the example) are part of the range.
A variable called name of a certain type is declared using the keyword data, like this:
type data name
However, data is optional, so we can write more briefly
type name
We can declare more than one variable of the same type at the same time using commas, and we can also assign values to them at the time of declaration, using the assignment operator ':=', like this:
int data i1, i2, i3 := 1
Here i3 is initialized with the value one. If no assignment takes place, the default value of zero is used, that is, upon program entry variables are always initialized to their type's default value. Pointers are declared in the usual way of C, so:
int data *p1, *p2 := &i1
Notice that p2 was initialized with the address of i1 using the symbol '&'. Also, it is sometimes useful to indicate that a pointer points to nothing with the keyword
nil
An array is also declared in the usual way:
byte data a1[5], a2[6][7][8], a3[] := [1, 2, 3, 4]
Variables a1, a2, and a3 are not pointers, as in C, because this language is strongly typed, but the address of a2 (say) can be obtained by saying
byte data *pa2 := &a2[0][0][0]
a3 above was initialized with byte values separated by commas and enclosed in braces. Its size is 4. This is also valid:
char data sa[3][2] := [['a', 'b'], ['y', 'z'], ['0', '1']]
We can say
sa.count
to obtain the size of the array (3, in this case). If we say sa[1].count, we obtain the value 2. All arrays are unidimensional. Multiple sequences of square brackets are just syntatic sugar. Instances can be inline, static, and extern. A constant named name of type type with value value is declared by
inline type data name := value
It's OK to omit data. Unlike variables, constants can be initialized only once, but, like variables, we can declare more than one constant in a single inline data clause using commas, and we can also initialize them in the same way as we initialized variables. However, unlike C, if we want a constant pointer to a constant int, for instance, we must first declare the int, and only then the pointer, as in:
inline int ic := 1, *cpic := &ic
In this way, nor ic nor cpic can be changed in future parts of the code.
The usual operators are included, plus a few new ones. They are:
() [] . -> :
! ~ + - ++ -- * &
* / \
+ -
& | ^ << >> <<< >>>
..
== != < <= > >=
&& ||
?:
:= *= /= \= += -= &= |= ^= <<= >>= <<<= >>>=
: is the scope resolution operator (same as :: in C++). Then come the usual unary operators. \ is the remainder of the integer division of two numbers. <<< and >>> are the cyclic rotation operators. They work in unsigned numbers and rotate their contents, with one bit coming out of one end and entering the other end. .. is the range operator. With these operators in place, we can write expressions like:
a /= (x + 3.5) * (2 <<< 5)
k := b != 1 && c < 5 ? t & 0b01001011 : ~0b11001001
A function is defined using the syntax
return-type func name(args-list)
statement-1
statement-2
statement-3
...
If we're just declaring it (instead of defining it), the statements are simply absent. Notice that there are no braces enclosing the statements - they're indented instead. Nor are there semicolons at the end of a statement, as the end-of-line character also works as an end-of-statement character, if appropriate. Here's an example of a function that swaps two integers:
void func swap(int &a, int &b)
int data tmp := a
a := b
b := tmp
We can assign predetermined values to the last arguments of the function, like this:
int f(int a1, int a2 := 0, int a3 := 1)
This also shows that the keyword func can be omitted. Also, some of the arguments can be constant, thus telling the compiler that they cannot be modified. Here's an example:
float f(double a1, inline double a2, int data a3 := 1)
a2 cannot be modified by the function. Constants can also be given predetermined values, like this:
char f(string data a1, inline char data a2 := 'a')
This makes the character 'a' the value of a2, if the function is called f("A string!"), or, if the function is called f("A string!", 'c'), the value of a2 is now 'c' instead. Also, functions can be overloaded, so that the above three functions called f can co-exist in the same program at ease. All that is required is that the types of the arguments that have no predetermined values be different among the various versions of the function.
If a function returns no values, or has no arguments, the keyword void is used to explain it:
char g(void)
void func h(int data a)
Functions that receive a variable number of parameters are declared using the ellipsis, like this:
void k(byte b, ...)
Then, inside its body, its variadic arguments can be accessed with the special array args, as in args[0], args[1], args[2], etc. Also, we can write args.count to determine how many variadic parameters there are.
To call a function, simply type its name, followed by the comma-separated arguments enclosed in parentheses. For example,
example(100, 40, 1)
As in C, a function that has no arguments still needs to be called using parentheses:
char data c := g()
Functions can be inline, static, and extern, as in C++.
All entries in this section define new types, like enumerations, structures, classes, and so forth. If a type has name type-name, we can declare a variable of that type (and named name) in the usual way, like this:
type-name data name
The same applies to constants.
This language supports enums, in a way similar to C. To define them, we write:
super-enums-list enum name
enum-entry-1
enum-entry-2
enum-entry-3a, enum-entry-3b
...
Here we can see that individual entries appear by themselves in a single line, but we can create synonyms by separating them with commas. Here is a simple example of an enumeration:
enum CardinalPoints
East
North
West
South
And then, we can add to this definition the following:
CardinalPoints enum MoreCardinalPoints
NorthEast
NorthWest
SouthWest
SouthEast
CardinalPoints is the supertype of MoreCardinalPoints, and so we can say
MoreCardinalPoints data compass1 := South, compass2 := NorthEast
Enums can have more than one supertype (as long as there are no repeated entries), in which case the new enum will consist of all the superenums plus its new definitions. For example:
enum Enum0
A
B
C
D
enum Enum1
X
Y, Z
Enum0, Enum1 enum Enum2
Zero
One
Two
In Enum1 (and Enum2), Y and Z are the same and can be used interchangeably. Enums and the integral types are different: assigning an integer to a variable of an enum type is not valid, and vice-versa.
Errors are special kinds of enums, and are defined in a similar way:
super-errors-list errors name
error-entry-1
error-entry-2
error-entry-3a, error-entry-3b
...
Everything that was said about enums also applies to errors. They differ in that only errors can be used with the statements error() and iferror(). See more about these statements below, in their own section. Here is an example of a list of errors:
errors FileError
ReadAfterEOF
DiskFull
BadHandle
FileAlreadyOpen
Bitfields are a way of separating a sequence of bits into logically meaningful sections. They are declared like this:
super-bitfields-list bitfield name
bitfield-entry-1
bitfield-entry-2
bitfield-entry-3
...
Bitfield entries can be any of the following:
bool bool-name
bit bit-name
ubits(total-bits) ubits-name
ubits-enum-1 := value-1
ubits-enum-2 := value-2
ubits-enum-3 := value-3
...
sbits(total-bits) sbits-name
sbits-enum-1 := value-1
sbits-enum-2 := value-2
sbits-enum-3 := value-3
...
A bool is simply a bit that can be false or true. A bit can be 0 or 1. ubits are unsigned bits and sbits are signed bits, in two's complement. There's one special name called
reserved
that can be used as many times as needed instead of a usual field name. Also, ubits and sbits can have constants assigned to individual values. These constants are optional and don't have to be exhaustive. For example:
bitfield ExampleBits
bool data isAccessed, isDirty
bit reserved
bit data reserved
ubits(4) pageType
kPageTypeSys := 1
kPageTypeDrv := 2
kPageTypeLib := 4
kPageTypeApp := 8
ubits(8) reserved
sbits(16) data offset
ExampleBits data myBits := [false, false, , , kPageTypeSys, , -14]
A union is declared like this
super-unions-list union name
union-entry-1
union-entry-2
union-entry-3
...
For example:
union Union0
int i
double d
union Union1
byte data b
As shown in Union1, unions can have just one field. Unions can have more than one super union, as long as fields are not repeated in the hierarchy:
Union0, Union1 union Union2
long l
double f
If we declare u2 to be a union of type Union2, so:
Union2 data u2
we can access its members with the member access operator:
u2.i := 15
u2.b := 1
u2.f := 0.5
In case pu2 is a pointer to a union of type Union2, as in
Union2 data *pu2 := alloc(Union2)
its members are accessed thus:
pu2->b := 0
pu2->f := 0.5
We can also say:
Union2 data myUnion := [1, 2.0, 255, 17269182756915, 1.25e-300]
A union can be anonymous, in which case its entries have a scope identical to the one where the union was declared. For that, the entries must not have been declared elsewhere in that scope, that is, no repeated names must occur. For example, we can declare
union
byte b
char c
if b and c have not been declared anywhere else in the module. In this way, no confusion with repeated identifiers can happen. To access these union entries, we simply type, for example,
b := 5
f(c)
for some function f.
Structures are similar to the ones in C, operate in a way similar to unions as described above, and are declared by:
super-structs-list struct name
struct-entry-1
struct-entry-2
struct-entry-3
...
Everything that was said about unions also applies to structures. In addition, we can nest types as we wish, like this:
enum StructType
ItsAnAge
ItsAName
struct PersonalData
StructType type
union
int age
string name
PersonalData data me
me.type := ItsAnAge
me.age := 42
inline PersonalData data someone := [ItsAName, "Name"]
Notice the use of an anonymous union. In this case, the identifiers age and name have struct PersonalData scope, so, for this to be valid, no other identifiers in the structure must match them.
A protocol is simply a list of function declarations:
super-protocols-list protocol name
function-declaration-1
function-declaration-2
function-declaration-3
...
It can inherit from other protocols, so that a class implementing it must implement all the functions in the super protocols along with the newly declared functions.
A class is a structure containing members which can be instances, constants, or functions.
super-class, protocols-list class name
member-declaration-1
member-declaration-2
member-declaration-3
...
It may not have a super class, in which case it inherits from no class. Also, its protocols list may be empty. For example:
Object class Rectangle
float data x, y
float w, h
Rectangle* func init(float x, float y, float w, float h)
float area(void)
This class has four instances (all floats) and two methods, one to initialize its contents and another to return an area. Also, its superclass is the class Object. The class declaration for Rectangle above goes in a ch header file, if we wish to make it public. Then, in a cm file comes the implementation of the declared function members, in a way similar to C++, so:
Rectangle* func Rectangle:init(float x, float y, float w, float h)
self->x := x
self->y := y
self->w := w
self->h := h
return(self)
float Rectangle:area(void)
return(w * h)
Now, when we wish to use the class Rectangle, we simply declare it and initialize it, like this:
Rectangle data *myRect := Rectangle->alloc()->init(0.0, 0.0, 1.0, 0.5)
log("The area is \(myRect->area()).")
This creates an instance of class Rectangle by allocating space in the heap first, and then initializing the x, y, w, and h members with some appropriate values.
Members can be static, like the alloc() function above, and also they can be overloaded. Also, members can be inline (and so they are either constant fields, and thus cannot be setters, or inlined functions). Polymorphism works as expected, and because of it, only pointers to classes are acceptable. That is, an instance of a class cannot be declared so:
Rectangle data thisIsNotAValidRectDeclaration
It must be declared as a pointer, like this:
Rectangle data *thisIsAValidRectDeclaration
Generally, we declare a class instance as
class-name data *instance-name
The keyword data is optional. A constant is declared similarly:
inline class-name data *constant-name := value
If we wish to use any class that implements a certain protocol, we can declare it so:
protocol-name data *instance-name
inline protocol-name data *constant-name := value
There is one primitive type that may sometimes be useful: obj. It stands for any class and we can use it thus:
obj data *instance-name
inline obj data *constant-name := value
From then on, instance-name (or constant-name) may refer to any class we desire. If the class has at least one member function that is not implemented anywhere, it is said to be abstract, and cannot be instantiated. We'll have to create a subclass that implements that function and then use that subclass. The class Rectangle above is a subclass of the class Object. Here is an example of a subclass of Rectangle called Lozenge:
Rectangle class Lozenge
float data side
override Lozenge* func init(float x, float y, float w, float h)
float side(void)
It has a new member instance, called side, it redefines the init() member function (note the mandatory use of override used when redefining a function with equal name and arguments as the ones in the super class), and it adds a new member function called side() to obtain the length of its side (however, see getter below). The implementations could be like this:
Lozenge* func Lozenge:init(float x, float y, float w, float h)
super->init(x, y, w, h)
self->side := sqrt(((w * w) + (h * h)) / 2.0)
return(self)
float Lozenge:side(void)
return(side)
Additionally, this illustrates the use of
super
and also of
self
Extensions work like categories in Objective-C. They allow the addition of new member functions to existing classes without having to subclass those classes. Extensions cannot contain new member instances, only functions, like this:
class name "ExtensionName"
function-declaration-1
function-declaration-2
function-declaration-3
...
Here is an example:
class Rectangle "Perimeter"
float func perimeter(void)
This code is inserted into a file called RectanglePerimeter.ch if we wish to make it public. Then in RectanglePerimeter.cm we add:
float Rectangle:perimeter(void)
return(2 * (w + h))
Member instances, like x, y, w, and h of the class Rectangle above, are always private, while member functions declared in a ch file are always public. If we wish to access the instances outside of the class, we must either add member functions to do so, or we must use the getter and setter modifiers. An instance member can be a getter and a setter at the same time. Here's an example:
class Circle
getter float x, y
setter float radius
Circle* func init(float x, float y, float radius)
float area(void)
float perimeter(void)
Now, x and y are getters, so they can appear on the right-hand side of an assignment. Similarly, radius is a setter, and that means it can appear on the left-hand side of an assignment. Like so:
Circle data *c := Circle->alloc()->init(0.0, 1.0, 2.0)
float data myX := c->x, myDoubleY := 2.0 * c->y
c->radius := 3.0
If we wish to declare member functions as private we simply do it in a cm file. There are no private and public keywords.
We can create aliases of type names, to call them by another name, like so:
type typedef new-name
For example,
uword typedef Handle
After this, uword and Handle can be used interchangeably. We can also create aliases of functions in a similar way:
return-type(arg-types-list) typedef new-name
For example, if we have a function declared as:
int func f(int i, float data f[], inline char *c)
we can say:
int(int, float[], char*) typedef MyFuncType
With this new definition, instead of typing:
void g(char data c, int(int, float[], char*) data myFunc)
we can type:
void g(char data c, MyFuncType data myFunc)
and then we can call g like this:
g('a', f)
Inside g we use f, or a similar function passed as the myFunc argument, so:
int i := myFunc(1, [1.0, 2.0, 3.0], &c)
Generics are accomplished through the introduction of a new primitive type:
type
which stands for any type in the language, and through the ability to give parameters to almost any type. The exceptions are the functions, which accept type parameters using angle brackets:
<name>
The reason for these two different mechanisms is that function types are built up of instance types rather than type names, like the other types.
The simplest case concerns functions. As we've seen, they already accept parameters, so we can say things like:
void func f(int i, <T> t)
To call such a function we say, for example:
f(-1, "A string!")
and T becomes type string during this call to f. Functions can also return values of generic types, like this:
<T> func f(<T> t[])
So we can write:
char data c := f(['a', 'b', 'c'])
Using this function f as an example, we can also say
char(char[]) typedef MyFuncType
MyFuncType data myFunc := f
char data c := myFunc(['a', 'b', 'c'])
to achieve the same result as the call to f just above. Also, note that more than one parameter can be of a generic type. Here's another example to further illustrate the use of typedefs with generics in functions. Suppose we have the following function defined somewhere:
<Value> map(<Key> k, bool erase)
Its type is <Value>(<Key>, bool). Elsewhere we can say something like:
int(string, bool) typedef MyMapFuncType
MyMapFuncType data f1 := map
int value1 := f1("key", true)
This is the same as saying
int value1 := map("key", true)
with the generic type <Value> becoming int and <Key> becoming string during this call to map. Here's another example using map and this time typedef with parameters:
int(key, false) typedef AnotherMapFuncType(<Key> key)
AnotherMapFuncType(<Key> key) data f2 := map
int value2 := f2('M')
int value3 := f2(3.14159)
Note the default value of false for the bool parameter. This is the same as saying:
int value2 := map('M', false)
int value3 := map(3.14159, false)
And now, a typical example. Let us build a generic swap function:
void func swap(<T> &a, <T> &b)
T tmp := a
a := b
b := tmp
We can now write:
int myInt1 := 2, myInt2 := 4
swap(myInt1, myInt2)
char myChar1 := 'A', myChar2 := 'B'
swap(myChar1, myChar2)
Unions and structs can also receive parameters, of any type:
super-unions-list union name(args-list)
union-entry-1
union-entry-2
union-entry-3
...
super-structs-list struct name(args-list)
struct-entry-1
struct-entry-2
struct-entry-3
...
There follows an example of a struct with two parameters. Note the use of type instead of the angle brackets seen previously in the case of functions.
struct STList(type S, type T)
S s
T t
STList(S, T) data *next
STList(int, float) data myIntFloatList
myIntFloatList.s := 10
myIntFloatList.t := 2.5
myIntFloatList.next := nil
Here's an example of another struct, that also illustrates the use of typedef with parameters:
struct Year(type data Day, ushort data totalDays := 365)
int year
Day days[totalDays]
Year(D, 366) typedef LeapYear(type D)
LeapYear(float) data myLeapYear
myLeapYear.year := 2016
for(ushort i := 0, i < 366, i++)
myLeapYear.days[i] := f(i)
In this last line, assume f is declared somewhere as
float f(ushort day)
Protocols and classes can also accept parameters. The general syntax is as follows and these work in a way similar to structs and unions.
super-protocols-list protocol name(args-list)
function-declaration-1
function-declaration-2
function-declaration-3
...
super-class, protocols-list class name(args-list)
member-declaration-1
member-declaration-2
member-declaration-3
...
Here is a familiar example:
Dictionary(string, int) data *myDictionary := Dictionary(string, int)->alloc()->init()
myDictionary->add("key1", 1024)
myDictionary->add("key2", 2048)
The simplest statement is
nop
which does nothing! Expressions and declarations are statements also:
expression
data-declaration
Then, there are the usual if statements:
if(boolean-expression)
true1-statement-1
true1-statement-2
true1-statement-3
...
else if(boolean-expression)
true2-statement-1
true2-statement-2
true2-statement-3
...
...
else
false-statement-1
false-statement-2
false-statement-3
...
And the usual switch statement:
switch(expression)
case(constant-1a, constant-1b, ...)
case1-statement-1
case1-statement-2
case1-statement-3
...
case(constant-2a, ...)
case2-statement-1
case2-statement-2
case2-statement-3
...
...
else
false-statement-1
false-statement-2
false-statement-3
...
There is no fallthrough to the next case. Instead, we have to use the statement
next
as the last statement in the case to achieve that result. There can be more than one constant in each case. Notice the lack of braces and semicolons. Instead, we have indentation - one tab per level, here shown as two spaces, but in the source code they should be tabs. Next we have loops. The simplest one is
loop
statement-1
statement-2
statement-3
...
It runs forever. We can add a stopping condition at the end with:
loop
statement-1
statement-2
statement-3
...
until(boolean-expression)
which is the familiar do-while loop of other languages. We can also add a stopping condition at the beginning, like this:
loop(boolean-expression)
statement-1
statement-2
statement-3
...
or like this:
loop(boolean-expression)
statement-1
statement-2
statement-3
...
until(boolean-expression)
In this last loop, both expressions are evaluated, one at the beginning of each iteration, the other at the end. The loop stops when the expression at the beginning becomes false or when the expression at the end becomes true. Next we have what other languages call the for-each loop:
loop(instance-declaration, iterable-object)
statement-1
statement-2
statement-3
...
or this one with a condition check at the end of each iteration:
loop(instance-declaration, iterable-object)
statement-1
statement-2
statement-3
...
until(boolean-expression)
See above for examples of instance declarations. iterable-object can be a string, an array, a range, or any instance of a class that implements the Iterable protocol. Here's an example (assume stringAppend() and charUpper() are functions defined somewhere in the program):
string data s1 := "Example", s2
loop(char data c, s1)
stringAppend(s2, charUpper(c))
After running this program, s2 contains the string "EXAMPLE". The final loop is the usual for loop of other languages:
loop(instance-declaration, boolean-expression, iteration-expression)
statement-1
statement-2
statement-3
...
This loop also exists in a variant with a condition check at the end of each iteration:
loop(instance-declaration, boolean-expression, iteration-expression)
statement-1
statement-2
statement-3
...
until(boolean-expression)
To exit a loop or a switch use the statement
break
and to perform the next loop iteration use
continue
The statement
return
exits from a function. If it is the last statement in the function, it can be omitted. Its variant
return(expression)
is used when the function wants to return a value. Next, comes the inline assembler statement. It goes like this:
asm(cpu-name-1)
asm-op-1
label-example:asm-op-2
asm-op-3
...
asm(cpu-name-2)
asm-op-1
asm-op-2
asm-op-3
...
...
Only the currently targeted CPU is compiled; the others are ignored. Finally, we have the errors statements. To throw an error, we simply say:
error(type.name)
To catch errors say
iferror(type.name, ...)
statement-1
statement-2
statement-3
...
iferror(type, ...)
statement-1
statement-2
statement-3
...
...
purge
statement-1
statement-2
statement-3
...
These iferror() statements usually come at the end of a function. Their arguments can be type.name to catch a single error, or simply type to catch a whole family of errors. These names are defined by the errors enumeration, as described in its own section above. Also, more than one error (or family) can be caught in each iferror() statement. Finally, the purge statements are executed after the code to handle the error is executed.
Finally, we have the #if directives. The general structure is like this:
#if name-1
code-1
...
#if name-2
code-2
...
...
#else
code-false
...
#end
name-1 and the others may have been defined in the call to the compiler, through the use of an option, say
cubo -dname-1 file-name
If name-1 has been defined in this way, then code-1 is compiled, otherwise it is ignored, and the next #if is checked. This process repeats itself until #end is reached. The code of multiple #if's may be compiled, if more than one is satisfied before reaching the #end. #else may or may not be present. Its code is compiled if no previous #if was satisfied. Following each #if may be more complicated boolean expressions, involving names defined (or not) in the call to the compiler, in a way similar to this:
(name-1 && name-2) || !name-3
Copyright © 2016 Rui Cuco. All rights reserved.
All trademarks mentioned in these pages belong to their respective owners.