Skip to content

Language

John Wilkes edited this page Mar 13, 2022 · 55 revisions

Comments

Line comments start with # and continue to the end of the line.

# This is a comment
x = a + b; # This is another comment

Block comments start with #! and end with !#. Block comments can be nested.

#!
    Multi-line comment
    #!
        Nested comment
    !#
    Back in the outer comment
!#

Integer Literals

Integer literals can be specified in the following bases: binary, octal, decimal, and hexadecimal. Binary, octal, and hexadecimal integers have the respective prefixes 0b, 0o, and 0x.

Base Prefix Valid Characters
2 0b 0 and 1
8 0o 0-7
10 none 0-9
16 0x 0-9, a-f, A-F

Also, an underscore (_) may be used as a separator.

Examples:

0b101
0b_1101_0101
0o026
1783
12_345
0x1234_abcd

Floating-point Literals

Floating-point literals can be specified by using a decimal point (.), an exponent (after e), or both. An underscore (_) may be used as a separator.

Examples:

1.23
2e-8
1_234.567_890
1e1_000

Variables

Variables are declared using the var keyword followed by the variable name. They must be defined when they are declared. The variable's type may optionally follow its name. Otherwise, the type will be inferred from the expression being assigned. Note that the type must be specified when assigning an integer literal.

var <name> [type] = <expression>;

Below are several examples:

var x i32 = 10;
var y = x * 2;
var isEqual bool = (x == y);

Constants

Constants are declared using the const keyword followed by the constant name. They must be defined when they are declared. The constant's type may optionally follow its name. Otherwise, the type will be inferred from the expression being assigned. Note that the type must be specified when assigning an integer literal.

const <name> [type] = <expression>;

The assigned expression must be a constant expression. Constant expressions are one of the following:

  • A boolean literal (i.e. true or false)
  • An integer literal (e.g. 123, 0xA5)
  • A string literal (e.g. "abc")
  • Another constant
  • A struct initialization where all members are initialized to constant expressions
  • The following unary operators with a constant operand: -, !
  • The following binary operators with constant operands: ==, !=, <, <=, >, >=, +, -, *, /, %, <<, >>, >>>, &, ^, |, .., ..<, []
  • A cast with a constant argument
  • A member expression where the struct expression is constant
  • An array expression where all array element expressions are constant

Below are several examples:

const n1 i32 = 10;
const n2 = n1 * 2;
const b bool = true;
const s = "abc";
const a1 []i32 = [n1, n2, 30, 40, 50];
const a2 = a1[1..3];

Strings

String literals are surrounded in double quotes (") and may contain any valid UTF-8 code point. A backslash (\) is used to start an escape sequences. Escape sequences are listed below:

Escape Sequence Description
\' single quote
\" double quote
\\ backslash
\n new line
\r carriage return
\t horizontal tab
\u{} unicode
\x byte

Examples:

"abc"
"$1.00 ÷ 2 = 50¢"
"line 1\nline 2\nline 3\n"
"\x41\x42\x43"
"\u{03c0} = 3.14159"

Note that, unlike C/C++, strings are not null terminated.

Individual bytes in a string can be accessed with the subscript ([]) operator.

var s = "Hello!";
var b u8 = s[4]; # get byte "o" at index 4

Functions

Functions are defined with the fun keyword followed by the function name, an optional list of parameters in parenthesis, and the return type. Each parameter must have its type specified after the name. Function bodies are surrounded by curly braces ({ and }). A function's return value is the last expression in the function body and does not have a trailing semicolon. Alternatively, the return keyword can be used to return a value from a function.

fun add(num1 i32, num2 i32) i32
{
    var sum = num1 + num2;
    sum
}

fun sub(num1 i32, num2 i32) i32
{
    var diff = num1 - num2;
    return diff;
}

Note that functions need not be defined before they are called within a file. Thus, the following is valid:

fun calculate(num i32) i32
{
    # function is called here...
    add(num, 2)
}

# ...but not defined until here
fun add(num1 i32, num2 i32) i32
{
    num1 + num2
}

Functions in C libraries may also be called by providing an extern function declaration:

extern fun myCFunction(x i32) i32;

Function types start with the fun keyword followed by an optional list of parameters in parenthesis and the return type. For example, a function pointer variable can be defined like so:

var funPtr fun(a i32, b i32) i32 = add;

Branches

Branches are created using the if and else keywords. Branches are expressions that evaluate to the expression of the branch taken.

fun myFunction(x i32, y i32, z i32) i32
{
    if x == 0
    {
        y + z
    }
    else
    {
        y - z
    }
}

Multiple branches may be added by using elif to specify more conditions.

fun myFunction(x i32, y i32, z i32) i32
{
    if x == 0
    {
        y + z
    }
    elif x == 1
    {
        y - z
    }
    else
    {
        y * z
    }
}

Notes:

  • Each branch's body must be surrounded in curly braces ({ and })
  • Each if and elif condition must evaluate to a boolean (bool) value

Since branches are expressions, they can be nested in other expressions:

var y i64 = 10 *
    if x < 0 { -1 }
    elif x == 0 { 0 }
    else { 1 };

Loops

While loops are created using the while keyword:

var num i32 = 1;
var i i32 = 0;
while i < 10
{
    num *= 2;
    i += 1;
}

Notes:

  • Each loop's body must be surrounded in curly braces ({ and })
  • Each while condition must evaluate to a boolean (bool) value

For loops are created with the for and in keywords. The iterator type can be explicitly specified. Otherwise, it will be inferred from the iterable expression. Note that the type must be specified when assigning a range or array of integer literals.

for loop (closed interval range):

for i i32 in 0..3
{
    # i is 0, 1, 2, and 3
}

for loop (half-open interval range):

for i i32 in 0..<3
{
    # i is 0, 1, and 2
}

for loops can also be used to iterate over arrays:

var array []i32 = [10, 20, 30];
for x in array
{
    # ...
}

An index variable can also be added to a for loop:

var array []i32 = [10, 20, 30];
for x, i in array
{
    # x will be 10, 20, 30
    # i will be 0, 1, 2
}

Notes:

  • Each loop's body must be surrounded in curly braces ({ and })
  • For loops can iterate over ranges or arrays

The break and continue keywords can be used in both while and for loops. break terminates the loop. continue jumps back to the start of the loop.

Pointers

A pointer type is created by prepending & to the type it points to. The & operator is used to get the pointer to a value.

var x i32 = 123;
var ptr &i32 = &x; # create a pointer to x

A pointer is dereferenced with the * operator.

*ptr = 10;

Structs

Structs are defined with the struct keyword followed by the struct name. The struct's members are defined in curly braces ({ and }).

struct MyStruct
{
    name str,
    x i32,
    y i32,
}

Structs are initialized as follows:

var t MyStruct =
    MyStruct
    {
        name: "Al",
        x: 42,
        y: -17
    };

All members must be initialized when initializing a struct.

Note that structs need not be defined before they are referenced within a file. Thus, the following is valid:

struct Line
{
    # Point struct is used here...
    p1 Point,
    p2 Point,
}

# ...but not defined until here
struct Point
{
    x i32,
    y i32,
}

Struct members are accessed with the . operator.

var point = Point { x: 1, y: 2 };
point.x += 3;

Note that . also works with struct pointers.

var ptr = &point;
ptr.x += 3;

Arrays

An array type is created by prepending the array element type with square brackets ([]). For example, []i32 is an array of i32 integers.

Arrays are initialized in one of two ways: a multi-value expression or a size-value expression.

var a1 []i32 = [10, 20, 30]; # 3-element multi-value expression with initial values 10, 20, and 30
var a2 []i32 = [5; 0]; # 5-element size-value expression with initial values of 0

The number of elements in an array can be accessed with the Size property which has the type usize.

var a1 []i32 = [100; 0];
var len usize = a1.Length; # len will be 100

Arrays have 0-based indexing. Examples of getting/setting array elements:

x = a1[2]; # get element at index 2
a1[4] = 123; # set element at index 4 to 123

Arrays may be sliced with the subscript operator ([]) and a range. A slice is, itself, an array.

var a1 []i32 = [0, 1, 2, 3, 4];
var a2 = a1[1..<3]; # a2 = [1, 2]
var a3 = a1[1..3];  # a3 = [1, 2, 3]

Casting

Use cast(<type>, <expression>) to cast expressions.

Example:

var x i32 = 123;
var y i8 = cast(i8, x);

Casting rules:

  • Boolean to an integer results in 0 if the boolean is false, otherwise 1
  • Boolean to a float results in 0.0 if the boolean is false, otherwise 1.0
  • Integer to a boolean results in false if the integer is 0, otherwise true
  • Integer to a larger integer:
    • If expression is signed: sign extension
    • If expression is unsigned: zero extension
  • Integer to a smaller integer: truncate
  • Integer to another integer of the same size: no change
  • Integer to float: truncate if necessary
  • Float to a boolean results in false if the float is 0.0 or -0.0, otherwise true
  • Float to integer: truncate
  • Float to larger float: resize
  • Float to smaller float: truncate

Keywords

  • bool - Boolean type
  • break - Break out of a loop
  • cast - Cast an expression
  • const - Declare a constant
  • continue - Jump to the start of a loop
  • elif - Start of else-if block
  • else - Start of else block
  • extern - External function declaration
  • f32 - 32-bit floating-point type
  • f64 - 64-bit floating-point type
  • false - Boolean false literal
  • for - Start of for loop
  • fun - Function definition
  • i8 - Signed 8-bit integer type
  • i16 - Signed 16-bit integer type
  • i32 - Signed 32-bit integer type
  • i64 - Signed 64-bit integer type
  • if - Start of if block
  • in - Used in for loops between the iterator variable and expression
  • isize - Signed pointer-size integer type
  • return - Return from a function
  • str - String literal type
  • struct - Struct definition
  • true - Boolean true literal
  • type - Type type
  • u8 - Unsigned 8-bit integer type
  • u16 - Unsigned 16-bit integer type
  • u32 - Unsigned 32-bit integer type
  • u64 - Unsigned 64-bit integer type
  • usize - Unsigned pointer-size integer type
  • var - Declare a variable
  • while - Start of while loop

Operators

Operator Description Example
- negation (2's complement) -a
! 1's complement !a
& address of &a
* dereference *a
= assignment a = b
== equal a == b
!= not equal a != b
< less than a < b
<= less than or equal a <= b
> greater than a > b
>= greater than or equal a >= b
+ addition a + b
- subtraction a - b
* multiplication a * b
/ division a / b
% remainder a % b
<< shift left a << b
>> logical shift right a >> b
>>> arithmetic shift right a >>> b
& bitwise AND a & b
^ bitwise XOR a ^ b
| bitwise OR a | b
&& logical AND a && b
|| logical OR a || b
+= add and assign a += b
-= subtract and assign a -= b
*= multiply and assign a *= b
/= divide and assign a /= b
%= remainder and assign a %= b
<<= shift left and assign a <<= b
>>= logical shift right and assign a >>= b
>>>= arithmetic shift right and assign a >>>= b
&= bitwise AND and assign a &= b
^= bitwise XOR and assign a ^= b
|= bitwise OR and assign a |= b
.. closed interval range a..b
..< half-open interval range a..<b
. member access a.b
[] subscript a[b]
Clone this wiki locally