Documentation
Table of Content
Basic Syntax
Statements
Statements are separated by an end of line. The end of line is ignored when something more is expected, e.g. a line terminated with:
( , : = + - * ...
Comments
Comments start with two slashes.
// This is a comment
Identifiers
An identifier is a sequence of letters, digits and underscores starting with a letter or underscore.
Identifiers are case sensitives.
The convention is to start type names with an upper case letter and other identifiers with a lower case letter.
Reserved Keywords
List of reserved keywords:
and defer if ref
attr do import return
break else loop switch
case elsif module var
class end not while
const extend or
def func pass
These identifiers are not keywords but it is recommended to not redefine them:
true self sizeof nil
false Self yield
Program Elements
At the top level, a Copper program is made of those elements:
Variable
var name = value
var name: Type
import var name: Type
Creates a global variable with or without an initial value. The initial value must be a constant.
Constant
const name = value
const name = Type
Defines an alias to a constant value or a type.
There is no expected type when evaluating the value, so it must be explicitely specified when using a literal:
const screenWidth = Int: 640
Function
// Defines a function without parameter that returns nothing
func name
...
end
// Defines a function with parameters that returns nothing
func name(arg1: Type1, arg2: Type2)
...
end
// Defines a function with parameters that returns multiple values
func name(arg1: Type1, arg2: Type2): Type1, Type2
...
end
The type of each of parameters can be replaced by a *
. In such a case the function is generic: one function will be instanciated for each type of parameters.
// Fully generic
func name(arg1: *, arg2: *)
// Mixed
func name(arg1: *, arg2: Type2)
// With a variadic parameter
func name(arg1: Type1, args: Type2...)
func name(arg1: Type1, args: *...)
Non-generic functions can be imported from other object files:
// Imports a function from another object file
import func name(Type1, Type2, ...)
// same but with return values
import func name(Type1, Type2, ...) -> (Type1, Type2, ...)
// Import a function with a name that differs from the symbol in the object file
import func "public-name" name(Type1, Type2, ...)
Class
class Name
...
end
Defines a new class named Name
. The class has no parent and represents a reference to a structure, it can have attributes.
class Name: Parent
...
end
Defines a new class named Name
that is a subclass of Parent
. The parent can be any simple type: a reference to a structure, a reference to an array or an integer type.
If the parent is a structure, it is possible to add new attributes.
A class can contain 3 specific elements:
A class can also contain any program elements except extend
and import
.
A special identifier, Self
, is accessible inside the definition of the class, it is an alias to class itself. It avoids to repeat the name of the class everywhere, it can be especially useful with generic classes:
class HashTable(Key: *, Value: *, defaultCapacity: Int)
def isEqual(other: HashTable(Key, Value, defaultCapacity)): Bool
// becomes
def isEqual(other: Self): Bool
Module
module Name
// content
end
A module can be used to define functions, variables, constants and classes into a separate namespace. A module can be seen as a class without instance, it can include all program elements but import
and extend
.
A module can be generic:
module Name(T: *, n: Int)
...
end
Import
import "module-name"
import "module-name" Name
Imports public declarations from another module inside the same namespace or inside a separate namespace Name
.
The name of the module is the path relative to the include path plus the filename without the extension (.co).
Circular imports are not permitted: modules cannot be inter-dependents.
Extension
extend Type
...
end
It adds methods to any existing type.
e.g.:
extend Int32
def high: Int32
return self >> 16
end
end
This is extensively used by the standard library to extend the built-in types, but it should not be used outside.
Directives
Directives are properties that will be assigned to a single element or all following elements in the lexical scope. Directives are defined between square brackets, separated by comma.
[private] // now all the following elements will be 'private'
var width: Int
var [public] height: Int // This variable will be public
var depth: Int // This one is still private
Some directives apply to the every elements such as the visibility (public
, private
) or the conditional compilation (when
) while other apply only to specific elements.
Visibility
public
private
At the program level, it tells which element will be visible when the file is imported by another file.
Inside a module
or a class
, it tells whether an element is accessible from another file (private elements are still accessible from the same file).
At the beginning of a file and at the beginning of a class
or module
, the visibility is always public.
Conditional compilation
when <condition>
It applies a condition to elements: if the argument evaluates to false, the elements are not compiled.
func [when debug] assert(x: Bool)
...
end
func [when not debug] assert(x: Bool)
...
end
Naming Conventions
c
native
By default, the public name of a function or variable is numbered to ensure unicity. To import or export definition with the C calling convention, use c
.
Calling Conventions
cdecl
stdcall
default
Sets the calling conventions for functions.
Prevent Ignoring a Result
mustcheck
It applies to types, it forbids a return value to be ignored. This is usually used with an error type to prevent forgetting the processing of an error.
class [mustcheck] Err: Int
end
import func fopen(filename: String): Err
fopen("dummy") // error
var res = fopen("dummy") // ok
Entry Point
entry
Marks a function as an entry point. A function that is not used is not compiled, this flag forces the compilation of a function.
Class Elements
The elements that can be found only inside a class
.
Attributes
attr name: Type
Defines an attribute in a class. Only structures can have attributes (a structure is a class without parent or a subclass of a structure).
An attribute is accessed with the same syntax as a method without parameter:
aStruct.name
The attribute can be directly a structure or an array instead of a reference:
class Line
attr color: Int[3]! // 3 integers embedded in the structure
attr from: Point! // a point embedded in the structure
attr to: Point!
end
In the above example, the attributes are not reference but the accessors of the attributes are references:
var line = ...
var c = line.color // c is a reference to the array of 3 integers (Int[3])
var p = line.from // p is a reference to the first point (Point)
Methods
def [*] name(...)
statement1
statement2
statement3
...
end
A method is just a function defined inside a class. There is a first implicit parameter, self
, that is a reference to the object, the value of the enum or the value of the extended type. Methods as well as functions can take a block parameter. The syntax is the same as function, the only difference is the use of def
in place of func
.
The optional *
means that self
is a generic parameter: The type of self is the actual type instead of the type where the method is defined. It means that a new method is instanciated for each derived type of self.
Enum Values
id
id = value
An enum value is almost equivalent to a const, but the type must be of the class that contains it and a value can be automatically assigned if the class is an integer type.
Statements
Assignment Statements
dst1, dst2, ... = src1, src2
Assigns the expressions at the right of the equal sign to the destination expressions at the left. The number of values at the left and at the right must be equal. The following is correct as long as the function f
returns two values:
v1, v2 = f(x)
The values at the left must be either a local variable, a global variable, an attribute or an array element.
All right expressions are evaluated before assignment, so a code like this to swap two variables is perfectly valid:
x, y = y, x
Assignment Operator Statements
dst += src
dst -= src
dst *= src
dst /= src
dst %= src
dst <<= src
dst >>= src
dst &= src
dst |= src
dst ^= src
dst op= src
is strictly equivalent to:
dst = dst op src
The operation can be invalid when the operator is overloaded and the return type is not compatible to the left argument.
Expression Statements
expr1, expr2, ...
Any expression can be used as a statement. This is usually used when invoking a function without value returned or then the return values are ignored.
A constant expression used as a statement is an error as it is likely a mistake in the program.
Local variables
var id: T
Defines a local variable that is uninitialized.
var id1, id2, ... = src1, src2, ...
Defines one or more variables with an initial value. The number of variables must match the number of values on the right.
If Statements
if cond1
...
elsif cond2
...
elsif ...
...
else
...
end
Where cond1
and cond2
are boolean expressions.
Switch Statements
switch e
case c1
...
case c2, c3
...
case ...
...
else
...
end
e
is an integer expression. c1
, c2
and c3
are constants. The else
block is optional.
Contrary to many languages derived from C, a case
block won't continue its execution on the next case
block, there is no need of break
statements.
While and Loop Statements
while cond
...
end
Repeat the block as long as the boolean expression cond evaluates to true.
loop
...
end
A synonym for while true
, and endless loop can that only be interrupted by a break
or a return
.
Return Statements
return
return e1
return e1, e2, ..., en
Returns zero, one or more values from a function.
Break Statements
break
break e1
break e1, e2, ..., en
The break
statement interrupts a while
or loop
operation.
The break
statement can also be used to interrupt a function when invoked from a block passed to a function.
Pass Statements
pass
pass e1
pass e1, e2, ..., en
Inside a while
or a loop
, it takes no argument and go to the next iteration.
Inside a block passed as an argument of a function, it ends the execution of the block and 'pass' the arguments back to the function.
Defer Statements
defer aStatement
defer
aStatement
aStatement2
...
end
The statement will be executed when leaving the current block, either on normal exit, on return or on break.
Expressions
Grouping Expression
(expr)
Forces the precedence of operators.
Integer Literals
123
1_000_000
0x123ABC
$A
- Decimals:
(+|-)?[0-9][_0-9]*
- Hexadecimals:
(+|-)?0x[0-9a-fA-F][_0-9a-fA-F]*
- ASCII:
$c
where c is any ASCII character from 33 to 126
String Literals
"aaaa" // 8 or 16 bit character string
A string can includes special escaped characters:
Character | Description |
---|---|
\a | Bell (7) |
\b | Backspace (8) |
\f | Form Feed (12) |
\n | Line Feed (10) |
\r | Cariage Return (13) |
\t | Tab (9) |
\v | Vertical Tab (11) |
\w | Whitespace (32) |
\' | single quote |
\" | double quotes |
Initializers
[list-of-arguments]
Creates constant structures or arrays. The initializer is always evaluated depending on the expected type. If there is no expected type, prepend the expression with a type:
Type: [list-of-arguments]
The type must be a pointer either to a structure or an array. Arguments must match the type. Each argument must be a constant.
Typed Expressions
Type: expr
It sets the expected type when evaluating expr
, this is useful when the expression is a literal or an initializer and it forces the type of the expression to the given type. The expression must be compatible with the type (an expression is compatible if its type is a sub-type or if the literal value fits with the type).
Simple Invocation
id
id(expr-list)
id(expr-list) do arg1, arg2
...
end
Without argument, id
can refer to a constant, a local variable, a global variable, a function or a type.
With arguments, id
can refer to a function or an instance of a generic type.
Contextual Invocation
expr.id
expr.id(expr-list)
expr.id(expr-list) do arg1, arg2
...
end
If the expression is an object, id
refers to an attribute or a method.
If the expression is a type, id
refers to a static definition defined inside this type (a function, a variable, a class or a module).
Contextual Invocation with Implicit Context
.id
.id(expr-list)
.id(expr-list) do arg1, arg2
...
end
If the expected type for the expression is T
, .id
is equivalent to T.id
.
Function and Attribute References
// Referencing a function accessible from the current scope
ref name
ref name(types)
// Referencing a method
ref (type) name
ref (type) name(types)
// Referencing a static function in a module or class
ref [type] methodname
ref [type] methodname(types)
name
is the name of the function followed by the types of parameters between parenthesis. Do not put parenthesis if the function has no parameter. The type of parameters are required only if the function is generic.
ref (type) attrname
The offset of the attribute attrname
of the class type
. The expression has a Size
type.
ref (expression) attrname
The address of an attribute of a given object.
Prefix Operators
- expr
Negates the value of a number.
not expr
On a boolean it inverts the value, on an integer it inverts all bits.
Infix Operators
Arithmetic Operators:
Operator | Description |
---|---|
+ | Add |
- | Subtract |
~ | Distance, this is used for the difference of two pointers |
* | Multiply |
/ | Divide |
% | Modulo |
Comparisons:
Operator | Description |
---|---|
== | Equal |
<> | Different |
< | Less than |
> | Greater than |
<= | Less or equal |
>= | Greater or equal |
Bitwise Logical Operators:
Operator | Description |
---|---|
& | and |
| | or |
^ | exclusive or |
<< | left shift |
>> | right shift |
and | and, eval 2nd expr if and only if 1st is true |
or | or, eval 2nd expr if and only if 1st is false |
Indexed Access
expr[]
expr[index]
Access the element of an array. If the elements of the array are not references, the value is a reference to the element.
If the index is omitted, it is equivalent to a 0
index.
The index can be any kind of integer.
This operator can be overloaded and can use any number or parameters (0, 1 or more).
Operator Precedence
Operator priority from lowest to highest:
and or |
== <> < > <= >= |
+ - ~ & | ^ |
* / % << >> |
: |
- not |
. [] |
Operator Overloading
Operators are just regular methods.
a + b
is strictly equivalent to:
a._add(b)
To overload an operator, you just have to know its secret name. Here is the list:
_add +
_sub -
_mul *
_div /
_mod %
_shl <<
_shr >>
_eq ==
_ne <>
_le <=
_lt <
_ge >=
_gt >
_and &
_xor ^
_or |
_not not
_at []
Built-in Definitions
Globals
Boolean constants:
true
false
The nil constant
nil
It represents the null pointer and can be used as a reference to any structure or array.
sizeof(Type)
Gives the size of a given type as a Size
type.
To get the size of an array or a structure, don't forget to append a !
to the type or you'll get the size of the pointer.
class Color
attr r: Uint8
attr g: Uint8
attr b: Uint8
end
sizeof(Color) // 8 == the size of a pointer on a 64 bit platform
sizeof(Color!) // 3 == the size of the structure
Yield
yield
yield(e1)
yield(e1, e2, ..., en)
This expression evaluates the block passed to the function. The arguments of the yield are passed to the block as parameters.
func rept(n: Int) do (Int)
var i = Int: 1
while i <= n
yield(i)
i += 1
end
end
func main
rept(10) do x
println("\a", j)
end
end
The yield expression can return one or more values if the block returns one or more values using pass
.
Type Methods
Type.Super
Returns the parent type. If the type has no parent, it will generate an error.
Type.Cell
Returns the cell type of an array type.
Type1.vtable(Type2)
Returns an initializer for the given structure Type1
that is automatically initialized using the methods and constants from Type2
. For each attribute of the structure, the initializer will search for a definition with the same name in the given type: it must be either a constant or a method. If it is a function, the attribute will be initialized with a reference to this function. This automatic initializer is useful to generate virtual tables.
Conversion Methods
expr.super
Returns the same expression but with its parent type.
expr.as(type)
Returns the same expression but with the given type. The new type must be a subtype of the expression.
expr.cast(type)
Converts an expression to a new type. The following conversion are allowed:
- Integer to integer, the value will be truncated or extended.
- Pointer to pointer.
- Pointer to Size.
- Size to pointer.
Function Pointer Calls
e.call
e.call(e1, e2, ...)
e
is a pointer to a function and e1, e2, ...
are the arguments to pass to the function.
Variadic Methods
The variadic parameter of a function supports 3 methods:
- each
- size
- values
each
args.each do arg
// Do something with arg
end
Enumerates all additional arguments of a variadic function. e.g.:
func f(a: *, b: *, c: *...)
c.each do arg
print(arg)
end
end
An expression such as:
f(Int32:1, Int32:2, Int16:3, String:"hello", true)
will be generate an instance of f
like this:
func f(a: Int32, b: Int32, arg1: Int16, arg2: String, arg3: Bool)
print(arg1)
print(arg2)
print(arg3)
end
size
The number of arguments.
values
args.values
is expanded to the variadic arguments. It can be used to forward the arguments to another function.
func new(T: *, args: *): T
var t = T.alloc
t.init(args.values)
return t
end
Types
Built-in Types
All types known by the compiler:
Name | Description |
---|---|
Uint8 | Unsigned 8 bit integer |
Uint16 | Unsigned 16 bit integer |
Uint32 | Unsigned 32 bit integer |
Uint64 | Unsigned 64 bit integer |
Int8 | Signed 8 bit integer |
Int16 | Signed 16 bit integer |
Int32 | Signed 32 bit integer |
Int64 | Signed 64 bit integer |
Size | Unsigned integer with the size of a pointer |
Bool | Boolean type |
Anything | Special type matching all types |
Pointer | A pointer to Anything. This is the parent class to all pointers. |
Nil | The type of 'nil' |
Arrays
Type[n]
A pointer to an array of n
Type
s. n
is a positive constant integer.
Type[]
Is an equivalent to Type[0]
.
Function Types
& (Type1, Type2, ...)
& (Type1, Type2, ...) -> (ReturnType1, ReturnType2, ...)
& [stdcall] (Type1, Type2, ...) -> (ReturnType1, ReturnType2, ...)
Defines a function type: the type of parameters, the type of returns and optionally a calling-convention.
Local Types
Type!
Structures, arrays and function types are implicitely pointer types. To use the type directly, e.g. to allocate on stack, embed in another structure or another array, the type must be followed by an exclamation mark.