Skip to content

Expressions

Operator Precedence (highest to lowest)

  1. Postfix: . (member access), as (cast), function call ()
  2. Unary: - (negation), not (logical/bitwise not)
  3. Multiplicative: * / mod
  4. Additive: + -
  5. Shift: shl shr
  6. Comparison: == != < > <= >= is is not
  7. AND: and
  8. XOR: xor
  9. OR: or
  10. Conditional: <true_value> if <condition> else <false_value> (lowest precedence)

Arithmetic Operators

OperatorDescriptionTypesExample
+Additionint, floata + b
-Subtractionint, floata - b
*Multiplicationint, floata * b
/Divisionint, floata / b
modModuloint onlya mod b

Notes:

  • Mixed int/float operations promote int to float

Comparison Operators

OperatorDescriptionResult Type
==Equal tobool
!=Not equal tobool
<Less thanbool
>Greater thanbool
<=Less than or equalbool
>=Greater than or equalbool

Using == or != on struct types requires the type to implement the Equatable interface (error E3069 if it does not). Primitives, String, and Array support == and != without restriction. For reference identity comparison (same heap object), use is and is not instead.

Reference Identity Operators

OperatorDescriptionResult Type
isSame reference (same heap object)bool
is notDifferent referencesbool

is and is not compare whether two struct-typed variables refer to the same heap object. They cannot be used on primitive types (int, float, bool, byte) — using them produces error E3068.

function areSame(a Point, b Point) returns bool
return a is b
end 'areSame'

Logical / Bitwise Operators

The keyword operators and, or, xor, and not are context-dependent: they perform logical operations on bool operands and bitwise operations on int operands.

OperatorOn boolOn intExample
andLogical ANDBitwise ANDa > 0 and b < 10 / flags and 0xff
orLogical ORBitwise ORx == 1 or x == 2 / flags or 0x01
xorLogical XORBitwise XORa xor b / value xor mask
notLogical NOT (unary)Bitwise NOT (unary)not done / not mask

and and or short-circuit when both operands are bool: the right-hand side is evaluated only if the left-hand side does not already determine the result (false and _ skips the right; true or _ skips the right). This lets a left-hand guard make the right-hand side safe to evaluate, e.g. i < arr.count() and (try arr.get(i) otherwise default) > 0. Integer and/or remain bitwise and always evaluate both sides.

Shift Operators

Shift operators work on integers only.

OperatorDescriptionExample
shlShift left1 shl 4 (result: 16)
shrShift right256 shr 4 (result: 16)

Unary Operators

OperatorDescriptionExample
-Negation-x
notLogical NOT / Bitwise NOTnot condition / not mask

The - operator cannot be chained directly. Use parentheses for nested negation:

var y = -(-x) // OK: parenthesized
var z = -(x + 1) // OK: subexpression
// var w = --x // Error: consecutive negation operators

The not operator can be applied repeatedly:

var a = not not x // OK: double bitwise NOT (identity for integers)

Parentheses

Override precedence:

(2 + 3) * 5 // 25, not 17

Conditional (Ternary) Expression

The conditional expression evaluates one of two values based on a boolean condition:

<true_value> if <condition> else <false_value>

The condition must be bool. Both arms must produce the same type. The conditional expression binds looser than all binary operators, so operands are evaluated naturally without extra parentheses:

let x = a + b if flag else c * d // (a + b) if flag else (c * d)
let abs = x if x > 0 else -x
let label = "yes" if enabled else "no"

Conditional expressions can be chained. They associate to the right:

let tier = "gold" if score > 90 else "silver" if score > 70 else "bronze"
// equivalent to: "gold" if score > 90 else ("silver" if score > 70 else "bronze")

Conditional expressions work inside string interpolation, including with nested string literals:

print("Status: {"on" if flag else "off"}")

Array Access

Array elements are accessed using the .get() method, which throws ArrayError.indexOutOfBounds if the index is out of range, or ArrayError.emptySlot if the slot at that index is empty (null pointer, e.g. after resize() without filling every slot):

var arr = [1, 2, 3, 4, 5]
var first = try arr.get(0) otherwise 0
var last = try arr.get(arr.count() - 1) otherwise 0

Array elements are modified using the .set() method:

var arr = [1, 2, 3]
arr.set(0, value: 100) // First positional, second named

Creating Empty Arrays

Create an empty typed array using a type alias:

typealias Integer = int(i64.min to i64.max)
typealias IntArray = Array with Integer
var numbers = IntArray.create() // Empty array
numbers.push(42) // Add elements with push

To preallocate with a specific length (elements zero-initialized):

typealias Integer = int(i64.min to i64.max)
typealias IntArray = Array with Integer
var buffer = IntArray.create()
buffer.resize(100) // Length is now 100
buffer.set(0, value: 42) // Can set any index 0-99

To preallocate capacity without changing length (for performance):

typealias Integer = int(i64.min to i64.max)
typealias IntArray = Array with Integer
var buffer = IntArray.create()
buffer.reserve(100) // Capacity is 100, length is 0
buffer.push(42) // Now length is 1