Operators and precedence
This page lists every operator and symbol in Cella, how tightly each one binds, which ones records and enums can declare for their own values, and where each is explained. Operators covers declaring them.
Precedence
From tightest to loosest. Operators in one item have the same precedence.
- Postfix
.,(),[]and?, left to right. - Prefix
-,+,!,~,*,@,own,mut,immandatomic, right to left. *,/,%and*%, left to right.+,-,+%and-%, left to right.<<,>>,<<<and>>>, left to right.&, left to right.^, left to right.|, left to right.<,<=,>and>=, chained.is, which doesn't chain.==and!=, chained.&&, left to right.||, left to right.=and the compound assignments, right to left.
Unlike C, the bitwise operators &, ^ and | bind tighter than comparisons, so a mask test needs no parentheses. Shifts bind looser than + and -, as in C:
main: fun{ val flags = 0b0110 val mask = 0b0100 printf("%d\n", flags & mask == mask) printf("%d\n", 1 | 2 == 3) printf("%d\n", 1 + 2 << 1)}1 1 6
flags & mask == mask is (flags & mask) == mask, and 1 | 2 == 3 is (1 | 2) == 3.
Chained comparisons
A run of <, <=, > and >= is one chained comparison: a < b <= c means a < b && b <= c, with b evaluated once. == and != chain the same way among themselves. A chain stops at the first comparison that's false:
trace: fun(value: i32) -> i32{ printf("[%d]", value) ret value} main: fun{ printf(" %d\n", 1 < trace(5) <= 10) printf(" %d\n", trace(3) < trace(2) < trace(1)) printf(" %d\n", 2 == 2 != 3)}[5] 1 [3][2] 0 1
Numbers covers comparisons and their typing rules.
Pattern tests
is sits between the comparisons and the equality operators. Its left side can be a comparison or arithmetic, and the result can be compared with ==:
Shape: enum{ circle(radius: f64) square(side: f64)} main: fun{ val a = Shape.circle(1.0) val b = Shape.square(2.0) val count = 4 printf("%d\n", a is circle(_) == b is circle(_)) printf("%d\n", count + 1 is 5)}0 1
Assignment
Assignment binds loosest and groups right to left, so a = b = 5 stores 5 in b and then in a:
main: fun{ var a = 0 var b = 0 a = b = 5 printf("%d %d\n", a, b)}5 5
Operators and line breaks
A new line ends an expression, so a line can't start with a binary operator, an assignment, is, &&, || or =>. Break the line after the operator instead:
ready: fun(a: bool, b: bool) -> bool{ val both = a && bCannot start a line with '&&' ret both}ready: fun(a: bool, b: bool) -> bool{ val both = a && b ret both}-, +, *, !, ~ and @ can start a line, but there they are prefix operators that begin a new statement:
main: fun{ val x = 1 - 2Cannot use values as statements printf("%d\n", x)}Lexical structure has the full rules for line breaks.
Arithmetic
Numbers covers these and their typing rules. Records and enums can declare all of them.
a + b: additiona - b: subtractiona * b: multiplicationa / b: divisiona % b: remaindera +% b,a -% b,a *% b: wrapping addition, subtraction and multiplication-a: negation+a: the value unchanged
Bitwise and shifts
Numbers covers these. Records and enums can declare all of them.
a & b: bitwise and; onbool, and without short-circuitinga | b: bitwise or; onbool, or without short-circuitinga ^ b: bitwise exclusive or; onbool, true when the two differ~a: bitwise nota << b: shift lefta >> b: shift right; signed types keep the sign bita <<< b: rotate lefta >>> b: rotate right
Comparison and logic
Numbers covers comparisons, Bool, char and strings the logical operators and Pattern matching is. Records and enums can declare the six comparisons, but not &&, ||, ! or is.
a == b,a != b: equal, not equala < b,a <= b,a > b,a >= b: less than, less than or equal, greater than, greater than or equala && b: logical and;bruns only whenais truea || b: logical or;bruns only whenais false!a: logical nota is p: testsaagainst the patternp
Assignment
Variables covers assignment and Numbers the compound forms. Records and enums can declare the compound assignments, but not =.
a = b: assignmenta += b,a -= b,a *= b,a /= b,a %= b: arithmetic and assigna +%= b,a -%= b,a *%= b: wrapping arithmetic and assigna &= b,a |= b,a ^= b: bitwise operation and assigna <<= b,a >>= b,a <<<= b,a >>>= b: shift or rotate and assign
Memory and access
Records and enums can declare * as *: op(self) and indexing as []: op, as Operators shows, and ? as ?: op(self). The rest can't be declared.
@a: the address ofa(Pointers)*a: the value a pointer points to (Pointers)a.b: member access, also through pointers (Records)a[i]: indexing arrays, pointers and strings (Arrays)f(x): a call (Functions)a?: calls the type's?: opmember, which gives a value or returns early from the function that usesa?(Operators)own a: movesawhere a plain use wouldn't, such as out of amutparameter or a pointer (Ownership)mut a,imm a: borrowsawith or without writing (Parameter modes)atomic ...: an atomic load, store, update or fence (Atomics)
Other symbols
| Symbol | Meaning | Chapter |
|---|---|---|
a..b | The integers from a up to but not including b, in for loops and patterns | Control flow, Pattern matching |
a..=b | The integers from a up to and including b, in for loops and patterns | Control flow, Pattern matching |
... | Extra C arguments in an ext function, a typed variadic parameter, and spreading arguments | Variadic functions |
-> | The result type of a function or function type | Functions |
=> | An expression body, a lambda body or a match arm | Functions, Pattern matching |
: | Separates a name from what it declares, and a binding from its type | Lexical structure |
, | Separates arguments, elements and members on one line | Lexical structure |
{ } | A block or body, and a hole in an interpolated string | Control flow, String templates |
[ ] | Array literals, type arguments and indexing | Arrays, Generics |
Ranges aren't values: .. and ..= appear only in for loops and patterns.
Conditions in when
The conditions of when, use when and [When."..."] project tables combine flags with their own small set of operators: !, &&, || and parentheses, with ! binding tightest and || loosest. Conditional compilation covers them.
Cella Documentation