# Flux Language Syntax Reference ## Lexical Tokens All tokens listed here are produced by the lexer (lexical analysis phase) and appear as UPPERCASE terminals in `GRAMMAR.ebnf`. ### Literals | Token | Description | Examples | | ------------ | ------------------------------------------------------------------- | ------------------------------ | | `INT_LIT` | Integer literal (decimal, hex `0x`, octal `0o`, binary `0b`) | `42`, `0xFF`, `0o77`, `0b1010` | | `FLOAT_LIT` | Floating-point literal | `3.14`, `1.0e-9`, `0.5` | | `STRING_LIT` | Double-quoted UTF-8 string, supports `\n \t \\ \"` escape sequences | `"hello\nworld"` | | `CHAR_LIT` | Single-quoted Unicode scalar value | `'a'`, `'\n'`, `'\u{1F600}'` | | `TRUE` | Boolean true literal | `true` | | `FALSE` | Boolean false literal | `false` | ### Identifier | Token | Description | | ------- | ------------------------------------------------------------------------------------------------------------ | | `IDENT` | Identifier: starts with a letter or `_`, followed by letters, digits, or `_`. Unicode letters are permitted. | ### Operator Tokens | Token | Lexeme | Description | | ------------ | ------ | -------------------------------------- | | `PLUS` | `+` | Addition / unary plus (not in grammar) | | `MINUS` | `-` | Subtraction / unary negation | | `STAR` | `*` | Multiplication / pointer dereference | | `SLASH` | `/` | Division | | `PERCENT` | `%` | Modulo (remainder) | | `AMP` | `&` | Bitwise AND / address-of | | `PIPE` | `\|` | Bitwise OR | | `CARET` | `^` | Bitwise XOR | | `BANG` | `!` | Logical NOT | | `TILDE` | `~` | Bitwise NOT | | `DOT` | `.` | Member access | | `SHL` | `<<` | Left shift | | `SHR` | `>>` | Right shift | | `EQ` | `=` | Assignment | | `PLUS_EQ` | `+=` | Add-assign | | `MINUS_EQ` | `-=` | Subtract-assign | | `STAR_EQ` | `*=` | Multiply-assign | | `SLASH_EQ` | `/=` | Divide-assign | | `PERCENT_EQ` | `%=` | Modulo-assign | | `AMP_EQ` | `&=` | Bitwise-AND-assign | | `PIPE_EQ` | `\|=` | Bitwise-OR-assign | | `CARET_EQ` | `^=` | Bitwise-XOR-assign | | `SHL_EQ` | `<<=` | Left-shift-assign | | `SHR_EQ` | `>>=` | Right-shift-assign | ### Keyword Tokens #### Operator Keywords | Lexeme | Description | | ------ | ----------- | | `and` | Logical AND | | `or` | Logical OR | #### Boolean Literals | Lexeme | Description | | ------- | ------------------- | | `true` | Boolean true value | | `false` | Boolean false value | #### Primitive Type Keywords | Lexeme | Description | | ------ | ------------------------------ | | `u8` | Unsigned 8-bit integer | | `u16` | Unsigned 16-bit integer | | `u32` | Unsigned 32-bit integer | | `u64` | Unsigned 64-bit integer | | `i8` | Signed 8-bit integer | | `i16` | Signed 16-bit integer | | `i32` | Signed 32-bit integer | | `i64` | Signed 64-bit integer | | `f32` | 32-bit IEEE 754 floating-point | | `f64` | 64-bit IEEE 754 floating-point | | `bool` | Boolean (`true` or `false`) | | `char` | Unicode scalar value (32-bit) | #### Pointer Keyword | Lexeme | Description | | -------- | ------------------------------------------------------- | | `opaque` | Used in `*opaque` to denote a pointer with no type info | #### Statement Keywords | Lexeme | Description | | ---------- | ------------------------------------- | | `let` | Introduces a variable binding | | `mut` | Marks a binding or pointer as mutable | | `return` | Exits the enclosing function | | `if` | Conditional statement | | `else` | Alternative branch of an `if` | | `while` | Condition-controlled loop | | `loop` | Infinite loop | | `break` | Exit the immediately enclosing loop | | `continue` | Skip to the next iteration of a loop | #### Definition Keywords | Lexeme | Description | | -------- | -------------------------------- | | `fn` | Introduces a function definition | | `struct` | Introduces a struct definition | > **Lexer note:** All keywords above are reserved and must be recognised before > the general `IDENT` rule. An identifier may not shadow any keyword. ### Delimiter / Punctuation Tokens | Token | Lexeme | Description | | ----------- | ------ | ------------------------------------------------------ | | `LPAREN` | `(` | Left parenthesis | | `RPAREN` | `)` | Right parenthesis | | `LBRACKET` | `[` | Left square bracket | | `RBRACKET` | `]` | Right square bracket | | `COMMA` | `,` | Argument / element separator | | `SEMICOLON` | `;` | Statement terminator / array size separator (`[T; N]`) | | `LCURLY` | `{` | Block / compound expression open | | `RCURLY` | `}` | Block / compound expression close | | `ARROW` | `->` | Function return type separator | | `COLON` | `:` | Type annotation separator | --- ## Expressions Expressions produce a value. The grammar defines them through a hierarchy of precedence levels — lower in the list means lower precedence (binds less tightly). ### Operator Precedence Table | Level | Operators | Associativity | Description | | ----- | -------------------------------------------------------- | -------------- | -------------------------------- | | 1 | `=` `+=` `-=` `*=` `/=` `%=` `&=` `\|=` `^=` `<<=` `>>=` | right | Assignment (lowest) | | 2 | `or` | left | Logical OR | | 3 | `and` | left | Logical AND | | 4 | `\|` | left | Bitwise OR | | 5 | `^` | left | Bitwise XOR | | 6 | `&` | left | Bitwise AND | | 7 | `+` `-` | left | Addition, subtraction | | 8 | `<<` `>>` | left | Bit shift | | 9 | `*` `/` `%` | left | Multiplication, division, modulo | | 10 | `!` `~` `-` `*` `&` | right (unary) | Prefix unary operators | | 11 | `.` `[…]` `(…)` | left (postfix) | Member access, index, call | | 12 | literals, identifiers, `()` | — | Primary expressions (highest) | ### Operator Descriptions #### Binary Operators | Operator | Name | Example | Notes | | -------- | -------------- | --------- | ---------------------------------------------- | | `=` | Assignment | `a = b` | Right-associative; `a = b = c` → `a = (b = c)` | | `+=` | Add-assign | `a += b` | Expands to `a = a + b` | | `-=` | Sub-assign | `a -= b` | Expands to `a = a - b` | | `*=` | Mul-assign | `a *= b` | Expands to `a = a * b` | | `/=` | Div-assign | `a /= b` | Expands to `a = a / b` | | `%=` | Rem-assign | `a %= b` | Expands to `a = a % b` | | `&=` | BitAnd-assign | `a &= b` | Expands to `a = a & b` | | `\|=` | BitOr-assign | `a \|= b` | Expands to `a = a \| b` | | `^=` | BitXor-assign | `a ^= b` | Expands to `a = a ^ b` | | `<<` | Left shift | `a << b` | Shift `a` left by `b` bits; integer types | | `>>` | Right shift | `a >> b` | Shift `a` right by `b` bits; integer types | | `<<=` | Shl-assign | `a <<= b` | Expands to `a = a << b` | | `>>=` | Shr-assign | `a >>= b` | Expands to `a = a >> b` | | `or` | Logical OR | `a or b` | Short-circuits; both operands must be `bool` | | `and` | Logical AND | `a and b` | Short-circuits; both operands must be `bool` | | `\|` | Bitwise OR | `a \| b` | Integer types | | `^` | Bitwise XOR | `a ^ b` | Integer types | | `&` | Bitwise AND | `a & b` | Integer types (binary context) | | `+` | Addition | `a + b` | | | `-` | Subtraction | `a - b` | | | `*` | Multiplication | `a * b` | Binary context (both operands are values) | | `/` | Division | `a / b` | Integer division truncates toward zero | | `%` | Modulo | `a % b` | Sign follows the dividend | #### Unary Prefix Operators | Operator | Name | Example | Notes | | -------- | ----------- | ------- | ------------------------------------------------ | | `!` | Logical NOT | `!cond` | Operand must be `bool` | | `~` | Bitwise NOT | `~mask` | Bitwise complement; integer types | | `-` | Negation | `-x` | Arithmetic negation | | `*` | Dereference | `*ptr` | Unary context; operand must be a pointer type | | `&` | Address-of | `&x` | Unary context; produces a pointer to the operand | #### Postfix Operators | Operator | Name | Example | Notes | | -------- | ------------- | ----------- | ------------------------------------------------- | | `.` | Member access | `obj.field` | Accesses a named field or method of a struct/type | | `[…]` | Subscript | `arr[i]` | Indexes into an array, slice, or map | | `(…)` | Call | `f(a, b)` | Invokes a function or closure | > **Disambiguation:** `*` and `&` are context-sensitive. > When appearing as the first token of a `unary_expr` they are **unary** > (dereference / address-of). When appearing between two `unary_expr` > sub-trees inside `multiplicative_expr` or `bitand_expr` they are **binary** > (multiplication / bitwise AND). The parser resolves this purely from > grammatical position — no look-ahead beyond 1 token is required. ### Parenthesised Expressions Any expression may be wrapped in parentheses to override default precedence: ``` (a + b) * c ``` ### Function Call Argument List Arguments are comma-separated expressions. A trailing comma is **not** permitted at this grammar level. ``` f() f(x) f(x, y, z) ``` ### Examples ```flux // Arithmetic a + b * c - d % 2 // Bitwise flags & MASK | extra ^ toggle // Logical ready and not_done or fallback // Mixed unary / postfix *ptr.field &arr[i] !cond // Chained postfix obj.method(arg1, arg2)[0].name // Explicit precedence override (a or b) and c ``` --- ## Types Types describe the shape and interpretation of values. All type positions in the grammar reference the `type` non-terminal. ### Primitive Types Primitive types are single-keyword types built into the language. | Type | Kind | Width | Range / Notes | | ------ | ---------------- | ------ | ------------------------------------------ | | `u8` | Unsigned integer | 8-bit | 0 … 255 | | `u16` | Unsigned integer | 16-bit | 0 … 65 535 | | `u32` | Unsigned integer | 32-bit | 0 … 4 294 967 295 | | `u64` | Unsigned integer | 64-bit | 0 … 2⁶⁴ − 1 | | `i8` | Signed integer | 8-bit | −128 … 127 | | `i16` | Signed integer | 16-bit | −32 768 … 32 767 | | `i32` | Signed integer | 32-bit | −2 147 483 648 … 2 147 483 647 | | `i64` | Signed integer | 64-bit | −2⁶³ … 2⁶³ − 1 | | `f32` | Floating-point | 32-bit | IEEE 754 single precision | | `f64` | Floating-point | 64-bit | IEEE 754 double precision | | `bool` | Boolean | 1 byte | `true` or `false` | | `char` | Unicode scalar | 32-bit | Any Unicode scalar value (not a surrogate) | ### Named Types A named type is any user-defined type referenced by its identifier — typically a struct name. Because all primitive-type keywords (`u8`, `bool`, etc.) are reserved, an `IDENT` in type position is always a named type, never a primitive. ```flux Point // struct Point { x: f32, y: f32 } Node // struct Node { value: i64, next: *Node } *Point // pointer to a named type [Node; 8] // array of a named type ``` ### Pointer Types A pointer type is written with a leading `*`. The optional `mut` keyword following `*` marks the pointer as **mutable** — the pointee can be written through. Without `mut` the pointer is **immutable** — the pointee can only be read. | Syntax | C equivalent | Description | | ------------- | ----------------- | -------------------------------------------------- | | `*T` | `const T *` | Immutable typed pointer — read-only through ptr | | `*mut T` | `T *` | Mutable typed pointer — read-write through ptr | | `*opaque` | `const void *` | Immutable untyped pointer — no pointee type info | | `*mut opaque` | `void *` | Mutable untyped pointer — no pointee type info | Pointer mutability (`*mut`) and binding mutability (`let mut`) are **independent**: | Declaration | Can reassign ptr? | Can write through ptr? | | ------------------------- | ----------------- | ---------------------- | | `let p: *T` | no | no | | `let mut p: *T` | yes | no | | `let p: *mut T` | no | yes | | `let mut p: *mut T` | yes | yes | Pointer types may be nested. The `mut` qualifier applies only to the outermost indirection; each `*` in the chain can independently carry `mut`. ```flux *u8 // immutable pointer to u8 (cannot write *p) *mut u8 // mutable pointer to u8 (can write *p) **u8 // immutable pointer to immutable pointer to u8 *mut *mut u8 // mutable pointer to mutable pointer to u8 *opaque // immutable untyped pointer *mut opaque // mutable untyped pointer ``` ### Array Types Arrays have a fixed size known at compile time. ``` [ ; ] ``` `` must be a non-negative integer literal (`INT_LIT`). The element type may itself be any `type`, including pointers or nested arrays. ```flux [u8; 256] // array of 256 u8 values [*u8; 4] // array of 4 pointers to u8 [[f32; 3]; 3] // 3×3 matrix of f32 (array of arrays) [*opaque; 8] // array of 8 opaque pointers ``` ### Type Grammar Summary ```ebnf type = primitive_type | named_type | pointer_type | array_type ; primitive_type = "u8" | "u16" | "u32" | "u64" | "i8" | "i16" | "i32" | "i64" | "f32" | "f64" | "bool" | "char" ; named_type = IDENT ; pointer_type = "*" , [ "mut" ] , ( "opaque" | type ) ; array_type = "[" , type , ";" , INT_LIT , "]" ; ``` --- ## Struct Literals A struct literal constructs a value of a named struct type by providing values for each field. ``` { : , ... } ``` Fields may appear in any order and need not match the declaration order. No trailing comma is permitted. ### Examples ```flux let p = Point { x: 1.0, y: 2.0 }; let n = Node { value: 42, next: get_next() }; // Nested struct literal let outer = Rect { origin: Point { x: 0.0, y: 0.0 }, size: Point { x: 10.0, y: 5.0 } }; // Empty struct let u = Unit {}; ``` ### Struct Literals in Conditions Struct literals are **not permitted** as the outermost expression in `if` and `while` conditions. This restriction exists because `{` after the condition is ambiguous — it could start a struct literal body or the statement block. ```flux // ERROR — ambiguous: is `{` a struct body or the if block? if Flags { verbose: true } { ... } // OK — parentheses resolve the ambiguity if (Flags { verbose: true }).verbose { ... } ``` The grammar enforces this through the `expr_ns` (no-struct) hierarchy used in condition positions. Struct literals remain valid everywhere else: `let`, `return`, function arguments, field values, etc. ### Struct Literal Grammar Summary ```ebnf primary_expr = IDENT , [ struct_lit_body ] | INT_LIT | FLOAT_LIT | STRING_LIT | CHAR_LIT | "true" | "false" | "(" , expr , ")" ; struct_lit_body = "{" , struct_field_list , "}" ; struct_field_list = [ struct_field , { "," , struct_field } ] ; struct_field = IDENT , ":" , expr ; ``` ### No-Struct Expression (`expr_ns`) `expr_ns` is a parallel expression hierarchy identical to `expr` except its primary level (`primary_expr_ns`) does not allow the `struct_lit_body` suffix after an `IDENT`. Struct literals are still permitted when enclosed in parentheses (`"(" , expr , ")"`), because the `(` unambiguously marks the start of a grouped expression. `if_stmt` and `while_stmt` use `expr_ns` for their condition; all other expression positions use the full `expr`. --- ## Statements Statements perform an action and do not produce a value. Each statement is terminated by a semicolon `;`. ### Let Statement Introduces a new named binding in the current scope. ``` let [mut] [: ] [= ] ; ``` | Part | Required | Description | | ---------- | -------- | --------------------------------------------- | | `mut` | no | Makes the binding mutable; omit for immutable | | `` | yes | The identifier being bound | | `: ` | no | Explicit type annotation | | `= ` | no | Initialiser expression | | `;` | yes | Statement terminator | Bindings are **immutable by default**. Attempting to assign to a binding declared without `mut` is a compile-time error. At least one of the type annotation or the initialiser must be present so the compiler can determine the binding's type. This is a semantic constraint, not a syntactic one — the grammar permits bare `let x;` and the type checker rejects it if no type can be inferred from context. #### Examples ```flux // Immutable, type inferred from initialiser let x = 42; // Immutable, explicit type let y: f64 = 3.14; // Mutable, type inferred let mut count = 0; // Mutable, explicit type, no initialiser (must be assigned before use) let mut buf: [u8; 128]; // Mutable pointer to u32 let mut ptr: *u32 = &value; // Shadowing a previous binding is allowed let x = "hello"; // x is now a string, previous x is gone ``` ### Return Statement Exits the enclosing function immediately, optionally producing a return value. ``` return [] ; ``` `return;` (no expression) is used when the function's return type is the unit type `()`. `return ;` returns the value of the expression. Explicit `return` is only needed for early exits. The idiomatic way to return a value from a function is the implicit return of its body block. ```flux return; // unit return return 42; // return an integer return x * 2 + 1; // return an expression ``` ### Expression Statement Evaluates an expression for its side effects; the resulting value is discarded. A semicolon is required. ``` ; ``` ```flux do_something(x); // call for side effects count + 1; // legal but silly — value discarded ``` ### Statement Grammar Summary ```ebnf stmt = let_stmt | return_stmt | if_stmt | while_stmt | loop_stmt | break_stmt | continue_stmt | block_stmt | expr_stmt ; let_stmt = "let" , [ "mut" ] , IDENT , [ ":" , type ] , [ "=" , expr ] , ";" ; return_stmt = "return" , [ expr ] , ";" ; if_stmt = "if" , expr_ns , block_stmt , [ "else" , else_branch ] ; else_branch = if_stmt | block_stmt ; while_stmt = "while" , expr_ns , block_stmt ; loop_stmt = "loop" , block_stmt ; break_stmt = "break" , ";" ; continue_stmt = "continue" , ";" ; block_stmt = "{" , { stmt } , "}" ; expr_stmt = expr , ";" ; ``` --- ## If Statement Conditionally executes a block based on a boolean expression. ``` if [else ] ``` The condition `` must be an expression of type `bool`. The body is always a `block_stmt` — braces are mandatory. ### Else Branch The optional `else` branch is either a plain block or another `if` statement, enabling `else if` chains of arbitrary length. ```flux if x > 0 { pos(); } if x > 0 { pos(); } else { non_pos(); } if x > 0 { pos(); } else if x < 0 { neg(); } else { zero(); } ``` ### If Statement Grammar Summary ```ebnf if_stmt = "if" , expr_ns , block_stmt , [ "else" , else_branch ] ; else_branch = if_stmt | block_stmt ; ``` --- ## While Loop Repeatedly executes a block as long as a boolean condition holds. The condition is tested before each iteration; if it is false on entry, the body never runs. ``` while ``` ```flux let mut i = 0; while i < 10 { process(i); i = i + 1; } ``` ### While Loop Grammar Summary ```ebnf while_stmt = "while" , expr_ns , block_stmt ; ``` --- ## Loop Executes a block unconditionally and indefinitely. The loop runs until a `break` or `return` inside the body transfers control out. ``` loop ``` ```flux loop { let msg = recv(); if msg.is_quit() { break; } handle(msg); } ``` ### Loop Grammar Summary ```ebnf loop_stmt = "loop" , block_stmt ; ``` --- ## Break and Continue `break` and `continue` are only valid inside the body of a `while` or `loop`. The compiler enforces this as a semantic rule. | Statement | Effect | | ------------ | -------------------------------------------------------------- | | `break ;` | Exits the immediately enclosing loop immediately | | `continue ;` | Skips the rest of the current iteration; jumps to the next one | For `while`, `continue` jumps back to the condition check. For `loop`, `continue` jumps back to the top of the body. ```flux let mut i = 0; while i < 20 { i = i + 1; if i % 2 == 0 { continue; // skip even numbers } if i > 15 { break; // stop after 15 } process(i); } ``` ### Break / Continue Grammar Summary ```ebnf break_stmt = "break" , ";" ; continue_stmt = "continue" , ";" ; ``` --- ## Block Statement A block groups zero or more statements into a single statement and introduces a new lexical scope. Blocks do not produce a value. ``` { * } ``` ### Scoping Bindings declared inside a block are not visible outside it. A binding in an inner scope may shadow a name from an outer scope without affecting it. ```flux let x = 1; { let x = 2; // shadows outer x inside this block only f(x); // uses 2 } // x is still 1 here ``` ### Nesting Blocks may be nested freely to any depth. ```flux { let a = compute_a(); { let b = compute_b(); use(a, b); } // b is no longer in scope here } ``` ### Block Grammar Summary ```ebnf block = "{" , { stmt } , "}" ; ``` --- ## Top-Level Definitions A Flux source file is a sequence of top-level definitions. ```ebnf program = { top_level_def } ; top_level_def = func_def | struct_def ; ``` The leading token unambiguously selects the definition kind: `fn` → function, `struct` → struct. --- ## Function Definition Defines a named, callable function. ``` fn ( [] ) [-> ] ``` | Part | Required | Description | | ------------------ | -------- | -------------------------------------------------------- | | `` | yes | The function's identifier | | `( [] )` | yes | Comma-separated parameter list, may be empty | | `-> ` | no | Return type; omitting it means the function returns `()` | | `` | yes | Function body — a `block_stmt` | ### Parameters Each parameter is a name with a mandatory type annotation. Parameters are immutable by default; `mut` makes the local binding mutable within the body. ``` [mut] : ``` ```flux fn add(a: i32, b: i32) -> i32 { return a + b; } fn greet(name: *u8) { print(name); } fn increment(mut x: i32) -> i32 { x = x + 1; return x; } fn apply(f: *opaque, mut buf: [u8; 64]) -> bool { return call(f, &buf); } ``` ### Return Type If `->` is omitted the return type is implicitly `()` (the unit type). An explicit `-> ()` is also permitted but redundant. ```flux fn do_work() { // returns () side_effect(); } fn get_value() -> i64 { // returns i64 return 42; } ``` ### Function Definition Grammar Summary ```ebnf func_def = "fn" , IDENT , "(" , param_list , ")" , [ "->" , type ] , block_stmt ; param_list = [ param , { "," , param } ] ; param = [ "mut" ] , IDENT , ":" , type ; ``` --- ## Struct Definition Defines a named product type with zero or more typed fields. ``` struct { : , ... } ``` Fields are separated by commas. No trailing comma is permitted. An empty struct (zero fields) is valid. ### Fields Each field is a name and a type. Fields may be of any type including pointers, arrays, and other structs. Field names must be unique within the struct. ```flux struct Point { x: f32, y: f32 } struct Node { value: i64, next: *Node } struct Buffer { data: *u8, len: u64, cap: u64 } struct Unit {} ``` ### Member Access Fields of a struct value are accessed with the `.` operator (defined in the expression grammar). If the value is behind a pointer, dereference it first with `*`. ```flux let p: Point = make_point(); let x = p.x; let ptr: *Point = get_point_ptr(); let y = (*ptr).y; ``` ### Struct Definition Grammar Summary ```ebnf struct_def = "struct" , IDENT , "{" , field_list , "}" ; field_list = [ field , { "," , field } ] ; field = IDENT , ":" , type ; ```