Copper

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

String Literals

"aaaa" // 8 or 16 bit character string 

A string can includes special escaped characters:

CharacterDescription
\aBell (7)
\bBackspace (8)
\fForm Feed (12)
\nLine Feed (10)
\rCariage Return (13)
\tTab (9)
\vVertical Tab (11)
\wWhitespace (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:

OperatorDescription
+Add
-Subtract
~Distance, this is used for the difference of two pointers
*Multiply
/Divide
%Modulo

Comparisons:

OperatorDescription
==Equal
<>Different
<Less than
>Greater than
<=Less or equal
>=Greater or equal

Bitwise Logical Operators:

OperatorDescription
&and
|or
^exclusive or
<<left shift
>>right shift
andand, eval 2nd expr if and only if 1st is true
oror, 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:

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

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:

NameDescription
Uint8Unsigned 8 bit integer
Uint16Unsigned 16 bit integer
Uint32Unsigned 32 bit integer
Uint64Unsigned 64 bit integer
Int8Signed 8 bit integer
Int16Signed 16 bit integer
Int32Signed 32 bit integer
Int64Signed 64 bit integer
SizeUnsigned integer with the size of a pointer
BoolBoolean type
AnythingSpecial type matching all types
PointerA pointer to Anything. This is the parent class to all pointers.
NilThe type of 'nil'

Arrays

Type[n]

A pointer to an array of n Types. 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.