Operators & precedence
Falx's parser builds expressions in a fixed precedence chain, from loosest-binding to tightest. This table lists it in that order - earlier rows bind more loosely.
| Level | Operators | Associativity |
|---|---|---|
| 1. Assignment | = += -= *= /= %= ++ -- | Right |
| 2. Logical OR | || | Left |
| 3. Logical AND | && | Left |
| 4. Equality | == != | Left |
| 5. Comparison | < <= > >= | Left |
| 6. Addition | + - | Left |
| 7. Multiplication | * / % | Left |
| 8. Unary | ! - (negation) | Right |
| 9. Call / member / index | () . [] | Left |
output(2 + 3 * 4); // 14 - multiplication binds tighter
output((2 + 3) * 4); // 20 - parens override precedence
**.
The token exists internally, but the parser never wires it into an expression - writing 2 ** 3 is a syntax error today. For now, use repeated multiplication.
Arithmetic (+ - * / %)
Numbers behave as you'd expect, and an integer stays an integer unless a float is involved:
output(7 / 2); // 3 - integer Ă· integer truncates towards zero
output(7.0 / 2); // 3.5 - a float on either side promotes the result
output(10 % 3); // 1
+ is overloaded for strings and mixed types too - this is how string building works throughout the examples in this guide:
output("count: " + 5); // "count: 5" - number is stringified
output(3 * "ab"); // "ababab" - number * string repeats it
Comparison (< <= > >=)
FalxNumber implements a comparison; comparing two strings with < or > raises a runtime error rather than doing lexicographic comparison. If you need to order strings, convert or compare manually (e.g. via .length, or a custom function).
output(3 > 2); // true
output("a" < "b"); // runtime error: Cannot compare 'string' with 'b'
Equality (== !=)
Unlike ordering, equality is defined for every type and never errors - it just compares by value, and different types are simply unequal instead of raising:
output(1 == "1"); // false - different types, not an error
output(1 == 1.0); // true - numeric value, not representation, is compared
output([1,2] == [1,2]); // true - arrays/maps compare by contents
Logical (&& ||) and !
Both && and || short-circuit (the right side is only evaluated if needed), and both call the value's own boolean conversion directly rather than going through if's stricter rule - see Truthiness for exactly which types that works on before you reach for these on a non-boolean.
Assignment & compound assignment
= works on a plain variable, a struct field (obj.field = ...), an index (arr[0] = ...), or a tuple pattern (see Tuples & destructuring). The compound forms desugar to the equivalent binary operation:
let x = 10;
x += 5; // same as: x = x + 5;
x -= 3; x *= 2; x /= 4; x %= 4;
output(x); // 2
++ and -- are postfix only - x++ works, ++x is a syntax error. Both desugar the same way as the compound operators (x++ is x = x + 1), so there's no distinction between "pre" and "post" increment the way there is in C - the expression itself doesn't yield the old value, it's purely a statement-level shorthand.