Flux Operator Reference

This document covers every operator available in Flux, organized by category. For keyword operators (and, or, not, is, xor), see also the Keyword Reference.


Arithmetic

+ - Addition

flux
int z = x + y;

- - Subtraction

flux
int z = x - y;

* - Multiplication

flux
int z = x * y;

/ - Division

flux
int z = x / y;

% - Modulo

flux
int z = x % y;

^ - Exponentiation Note: ^ is not XOR. Use ^^ or the xor keyword for XOR.

flux
float z = 2.0 ^ 10.0;   // 1024.0

++ - Increment (prefix and postfix)

flux
x++;    // postfix: use then increment
++x;    // prefix: increment then use

-- - Decrement (prefix and postfix)

flux
x--;    // postfix: use then decrement
--x;    // prefix: decrement then use

Assignment

= - Assign

flux
int x = 10;

+= - Add and assign

flux
x += 5;   // x = x + 5

-= - Subtract and assign

flux
x -= 3;

*= - Multiply and assign

flux
x *= 2;

/= - Divide and assign

flux
x /= 4;

%= - Modulo and assign

flux
x %= 7;

^= - Exponentiate and assign

flux
x ^= 2;   // x = x ^ 2

?= - Conditional assign (assign only if target is zero/null) Assigns the right-hand value to the left-hand target only if the target is currently zero or null. The target is left unchanged if it already holds a non-zero value.

flux
int x = 10,
    y;

x ?= 50;   // x stays 10 - already non-zero
y ?= x;    // y becomes 10 - was zero

Comparison

== - Equal

flux
if (x == y) { ... };

!= - Not equal

flux
if (x != y) { ... };

< - Less than

flux
if (x < y) { ... };

<= - Less than or equal

flux
if (x <= y) { ... };

> - Greater than

flux
if (x > y) { ... };

>= - Greater than or equal

flux
if (x >= y) { ... };

is - Equality (keyword alias for ==) Preferred for enum, state, and type comparisons where it reads naturally.

flux
if (x is 5) { ... };
if (tok.kind is kinds.TOK_END) { ... };

Logical

Logical operators work on boolean truth values. Short-circuit evaluation applies.

& - Logical AND

flux
if (x > 0 & y > 0) { ... };

| - Logical OR

flux
if (x == 0 | y == 0) { ... };

! - Logical NOT

flux
if (!flag) { ... };

!& - Logical NAND

flux
if (x !& y) { ... };   // true unless both are true

!| - Logical NOR

flux
if (x !| y) { ... };   // true only if both are false

^^ - Logical XOR

flux
if (x ^^ y) { ... };   // true if exactly one is true

and - Logical AND (keyword alias for &)

flux
if (x > 0 and y > 0) { ... };

or - Logical OR (keyword alias for |)

flux
if (x == 0 or y == 0) { ... };

not - Logical NOT (keyword alias for !)

flux
if (not flag) { ... };

xor - Bitwise XOR (keyword form) Note: xor operates on integer values as bitwise XOR. ^^ is its symbolic equivalent in logical context.

flux
int result = a xor b;
byte k = ipad_key[i] xor byte(0x36);

Logical Assignment

&= - AND and assign

flux
x &= mask;

|= - OR and assign

flux
x |= flag;

^^= - XOR and assign

flux
x ^^= y;

Bitwise

Bitwise operators are prefixed with a backtick (` ``) to distinguish them from logical operators.

` ! `` - Bitwise NOT

flux
int inv = `!x;

` & `` - Bitwise AND

flux
int masked = a `& 0xFF;

` | `` - Bitwise OR

flux
int flags = a `| b;

` !& `` - Bitwise NAND

flux
int result = a `!& b;

` !| `` - Bitwise NOR

flux
int result = a `!| b;

` ^^ `` - Bitwise XOR

flux
int result = a `^^ b;

` ^^! `` - Bitwise XNOT (XOR then NOT)

flux
int result = a `^^! b;

` ^^!& `` - Bitwise XNAND

flux
int result = a `^^!& b;

` ^^!| `` - Bitwise XNOR

flux
int result = a `^^!| b;

Bitwise Assignment

` &= `` - Bitwise AND and assign

flux
x `&= 0x0F;

` |= `` - Bitwise OR and assign

flux
x `|= 0x80;

` !&= `` - Bitwise NAND and assign

flux
x `!&= y;

` !|= `` - Bitwise NOR and assign

flux
x `!|= y;

` ^^= `` - Bitwise XOR and assign

flux
x `^^= y;

` ^^!= `` - Bitwise XNOT and assign

flux
x `^^!= y;

` ^^!&= `` - Bitwise XNAND and assign

flux
x `^^!&= y;

` ^^!|= `` - Bitwise XNOR and assign

flux
x `^^!|= y;

Shift

<< - Bit-shift left When used as a postfix with no operand, shifts left by one.

flux
int shifted = x << 3;
byte b;
b<<;          // shift left by 1 (postfix form)
byte c = b << 2;

>> - Bit-shift right When used as a postfix with no operand, shifts right by one.

flux
int shifted = x >> 2;
b>>;          // shift right by 1 (postfix form)

<<= - Shift left and assign

flux
x <<= 4;

>>= - Shift right and assign

flux
x >>= 1;

Pointer and Address

@ - Address-of Returns a pointer to the operand. Used without spaces between the operator and its operand.

flux
int x = 10;
int* p = @x;

sha256_update(@inner_ctx, @ipad_key[0], 64);
int* p = @42;   // Address of a literal

* - Dereference When applied as a prefix to a pointer, yields the value at that address.

flux
int val = *p;
*p = 20;

(@) - Address-cast (integer to pointer) Reinterprets an integer value as a pointer address. The resulting pointer has the width of the configured default pointer width.

flux
uint* px = @x;
u64 kx = px;        // Store address as integer
uint* py = (@)kx;   // Reinterpret integer as address

long val = 0x4700FF33324EBA60;
byte* some_byte = (@)val;

@= - Address-assign Assign address of RHS to LHS

flux
int x = 10;

int* px @= x;

Ternary and Coalescing

? : - Ternary conditional Evaluates the condition; yields the first expression if true, the second if false.

flux
int z = x < y ? y : 0;
int y = x if (x > 5) else noinit;   // if-expression form
int y = x ? (x > 5) : noinit;       // ternary form

?? - Null coalesce Returns the left operand if it is non-zero, otherwise returns the right operand.

flux
int z = y ?? 0;   // z = y if y != 0, else 0

Function and Call

-> - Return arrow Separates a function's parameter list from its return type in a signature.

flux
def add(int x, int y) -> int { return x + y; };

<- - Chain arrow Pipes the return value of the right-hand function as the argument to the left-hand function. Equivalent to wrapping a call.

flux
int z = foo() <- bar();   // equivalent to: int z = foo(bar());

<~ - Recurse arrow Used in place of -> to declare a strictly-recursive (tail-call) function. Every return inside such a function re-enters itself. Stack frame never grows. Use escape to exit.

flux
def factorial <~ int (int n)
{
    if (n <= 1) { escape some_other_function(); };
    return factorial(n - 1, acc * n);
};

{}<em> - Function pointer type marker Used as part of def{}</em> to declare a function pointer variable. The calling convention keyword precedes it for non-default conventions.

flux
def{}* pfoo(int) -> int = @foo;
vectorcall{}* some_simd_fn() -> u64*;

... - Variadic parameter / ellipsis Declares that a function accepts a variable number of arguments. Arguments are accessed by indexing ....

flux
def variadic(...) -> void
{
    print(...[0]); // 1
    print(...[1]); // 2
};

variadic(1, 2, 3, 4);

Scope and Member Access

:: - Scope resolution Accesses a member of a namespace, object, or type.

flux
standard::io::console;

. - Member access Accesses a member of a struct, object, enum instance, or union.

flux
newStruct.x;
myObj.method();
tok.kind;
err._;   // Tagged union discriminant

._ - Tagged union discriminant access Accesses or sets the active tag of a tagged union.

flux
err._ = ErrorUnionEnum.BOOL_ACTIVE;
switch (e._) { ... };

String and Interpolation

$ - Stringify Converts an identifier name to its string representation at compile time.

flux
int Hello = 5;
print($Hello);   // prints the string \"Hello\"

f\"...\" - f-string (format string) Interpolates variable values inline using {variable} syntax. Must be null-terminated with \\0.

flux
string y = f\"{a} {b}\\0\";
print(f\"Value: {x}\\n\\0\");

i\"...\":{...}; - i-string (indexed interpolation string) Interpolates the results of expressions in order. Brackets in the string correspond to statements in the block.

flux
print(i\"Hello {} {}\" : { bar() + \"!\\0\"; \"test\\0\"; });
string x = i\"Bar {}\":{bar();};

Codification

Inverse of stringification, turns a string into source code in-place.

flux
byte* str = "int x;";

~$str;

x = 25;

This code becomes:

flux
byte* str = "int x;";

int x;

x = 25;

Combining stringification and codification at comptime with emitflux creates a powerful combination:

flux
comptime
{
    compiler.io.console.println("Stage 1: deciding types");

    byte*[] types = ["int", "float"],
            tags  = ["INT", "FLOAT"];
    int     count = 2;
    byte*   T, TAG;

    for (int idx = 0; idx < count; idx++)
    {
        T   = types[idx];
        TAG = tags[idx];

        emitflux
        {
            comptime
            {
                compiler.io.console.println(f"Stage 2: generating for type {$~$T}, tag {$~$TAG}");
                emitflux
                {
                    def ~$f"clamp_{T}"(~$T val, ~$T lo, ~$T hi) -> ~$T
                    {
                        if (val < lo) { return lo; };
                        if (val > hi) { return hi; };
                        return val;
                    };

                    def ~$f"is_{T}"(~$T x) -> bool
                    {
                        return true;
                    };
                };
            };
        };
    };
};

This generates 4 functions at the top level:

flux
def clamp_int(int val, int lo, int hi) -> int
{
    if (val < lo) { return lo; };
    if (val > hi) { return hi; };
    return val;
};

def is_int(int x) -> bool
{
    return true;
};

def clamp_float(float val, float lo, float hi) -> int
{
    if (val < lo) { return lo; };
    if (val > hi) { return hi; };
    return val;
};

def is_float(float x) -> bool
{
    return true;
};

The compile-time execution looks like:

flux
[INFO] [codegen] ► LLVM IR code generation
[AST] Begining codegen for Flux program ...
[AST] Total statements in AST: 81
[AST] Namespace definitions: 11
[AST] Pass 1: Processing using statements...
[AST] Pass 2: Processing not using statements...
[AST] Pre-pass: Registering all object types and extern blocks...
[AST] Pass 3: Processing all other statements...
Stage 1: deciding types
Stage 2: generating for type int, tag INT
Stage 2: generating for type float, tag FLOAT
[AST] Pass 4: Re-emitting pending bodies...
[INFO] [compiler] ► Compiling to object file (<your OS>)

Range

.. - Range Defines an inclusive integer range. Used in for-in loops and array comprehensions.

flux
for (int x in 1..10) { ... };
int[10] squares = [x ^ 2 for (int x in 1..10)];

Type System

(type) - Cast Explicitly reinterprets a value as another type. There is no implicit narrowing conversion.

flux
i32 y = (i32)x;
byte* p = (byte*)patch_page;
ulong addr = (ulong)@my_function;

(void) - Free / deallocate cast Frees the memory occupied by the operand immediately. Works on both stack and heap allocations. After a (void) cast, any access to the freed variable is a use-after-free.

flux
heap int x = 5;
(void)x;   // x is now freed

~ - Tie Binds a type alias to another alias, chaining type definitions.

flux
unsigned data{16} as dbyte;
dbyte as xbyte;
xbyte as ybyte;
ybyte as zbyte = 0xFF;

as - Type alias declaration (keyword) Gives a data type declaration a named alias.

flux
unsigned data{32} as u32;
signed data{64} as i64;

You can also alias tied (~) types, like so: ~int as tint;

!! - No-mangle Prevents the compiler from mangling the name of a function declared in an extern block. Cascades across comma-separated prototype lists.

flux
extern def !!foo() -> void;

extern
{
    def !!
        malloc(size_t) -> void*,
        free(void*)   -> void;
};

Built-in Built-ins

sizeof(x) - Size in bits Returns the size of a type or value in bits, not bytes. Divide by 8 or sizeof(byte) to get bytes.

flux
sizeof(int)    // 32
sizeof(byte)   // 8 (default; configurable via __BYTE_WIDTH__)

size_t n = (size_t)(SIM_W * SIM_H * sizeof(double) / 8);

alignof(x) - Alignment in bits Returns the alignment requirement of a type in bits.

flux
alignof(int)      // 32
alignof(string)   // 8

typeof(x) - Type kind constant Resolves a type name to its kind constant at compile time. Useful for type comparisons.

flux
if (typeof(x) == typeof(int)) { ... };

endianof(x) - Endianness Returns the endianness of a type. 1 is big-endian (default for data types), 0 is little-endian.

flux
endianof(string)   // 1

Operator Overloading and Custom Operators

Flux allows defining new infix operators and overloading existing ones.

Custom symbol operator:

flux
operator (int L, int R) [+++] -> int
{
    return ++L + ++R;
};

a +++ b;   // usage

Identifier-based operator:

flux
operator (int L, int R) [NOPOR] -> bool
{
    return !L | !R;
};

a NOPOR b;   // usage

Overloading a built-in operator: At least one parameter must be a non-built-in type. Precedence and associativity cannot be changed.

flux
operator (int L, BigInt R) [+] -> BigInt
{
    // addition between int and BigInt
};

Operator Precedence

Higher rows bind more tightly. Within a row, associativity is left-to-right unless noted.

PrecedenceOperatorsNotes
Highest() [] . ._ ::Grouping, indexing, member access
@ * (unary) ! ` ! ` ++ -- $ (type)`Unary / prefix
^Exponentiation (right-associative)
* / %Multiplicative
+ -Additive
<< >>Shift
` & ` ` ^^ ` !& ` !` ^^! ` ^^!& ` ^^!``Bitwise
< <= > >=Relational
== != isEquality
& !&Logical AND / NAND
^^Logical XOR
` !`Logical OR / NOR
??Null coalesce
? :Ternary conditional
<-Chain arrow
Lowest= += -= *= /= %= ^= ?= &= `= ^^= &= ` = ` ^^= ` !&= ` != ` ^^!= ` ^^!&= ` ^^!= ` <<= >>=`Assignment (right-associative)

Quick Reference Table

OperatorCategoryDescription
+ - * / %ArithmeticBasic math
^ArithmeticExponentiation
++ --ArithmeticIncrement / decrement
=AssignmentAssign
+= -= *= /= %= ^=AssignmentCompound arithmetic assignment
?=AssignmentAssign if zero/null
== != < <= > >=ComparisonRelational
isComparisonEquality (keyword alias for ==)
& `\\ ! !& !\\ ^^`LogicalBoolean logic
and or notLogicalKeyword aliases
xorLogical/BitwiseXOR (keyword form)
&= `\\= ^^=`Logical assignmentCompound logical assignment
` ! ` & ` \\` !& ` !\\` ^^ ` ^^! ` ^^!& ` ^^!\\``BitwiseBit-level logic
` &= ` \\= ` ^^= ` !&= ` !\\= ` ^^!= ` ^^!&= ` ^^!\\= ``Bitwise assignmentCompound bitwise assignment
<< >> <<= >>=ShiftBit shift
@PointerAddress-of
* (unary)PointerDereference
(@)PointerInteger-to-pointer cast
? :ConditionalTernary
??ConditionalNull coalesce
->FunctionReturn type arrow
<-FunctionChain call arrow
<~FunctionStrict-recursion arrow
{}*FunctionFunction pointer marker
...FunctionVariadic parameter/index
::ScopeNamespace/scope resolution
.MemberMember access
._MemberTagged union discriminant
$StringifyStringification
~$CodifyCodification
..RangeInclusive range
(type)TypeCast
(void)TypeFree / deallocate
~TypeTie (alias chain)
!!FFINo-mangle
sizeofBuilt-inSize in bits
alignofBuilt-inAlignment in bits
typeofBuilt-inType kind constant
endianofBuilt-inEndianness of type