# Flaris Language Reference

Version: 1.0.0.5
Spec Revision: 2026-05-11

This document is the authoritative technical reference for the Flaris language and runtime.
For a narrative introduction and guided tour, see the **Guide**.

---

## Table of Contents

- [R1 - CLI Reference](#r1--cli-reference)
- [R2 - Type System](#r2--type-system)
- [R3 - Built-in Functions](#r3--built-in-functions)
- [R4 - The Exception class](#r4--the-exception-class)
- [R5 - Operator Precedence](#r5--operator-precedence)
- [R6 - VM Limits](#r6--vm-limits)
- [R7 - Performance Model](#r7--performance-model)
- [R8 - Standard Library](#r8--standard-library)
- [R9 - Debug Reference](#r9--debug-reference)
- [R10 - Writing Fast Flaris Code](#r10---writing-fast-flaris-code)
- [R11 - JIT Compilation](#r11---jit-compilation)

---

## R1 - CLI Reference

The Flaris VM binary is `flarisvm`.

### Invocation Modes

| Command | Description |
| --------- | ------------- |
| `flarisvm <file.fls> [options]` | Compile and run source file |
| `flarisvm - [options]` | Compile and run source from **stdin** |
| `flarisvm -c <input.fls> <output.flx> [options]` | Compile source to bytecode |
| `flarisvm -c - <output.flx> [options]` | Compile source from **stdin** to bytecode |
| `flarisvm -e <file.flx> [options]` | Execute compiled bytecode |
| `flarisvm -e - [options]` | Execute bytecode from **stdin** |
| `flarisvm -d <file.flx>` | Disassemble bytecode (debug view) |
| `flarisvm -r '<code>' [options]` | Run code directly from string |
| `flarisvm -f <file.flx>` | Print the whole-file SHA-256 (identical to `sha256sum`) - the value to pin via `library(file, version, hash)` |
| `flarisvm --sig-info <file.flx>` | Print the embedded signature status, machine-readable: `unsigned`, or `signed\|<trusted\|valid\|invalid>\|<pubkey-hex>\|<signer>` (see *Package signing*) |
| `flarisvm --import-key <name> <pubkey>` | Add a 64-hex Ed25519 public key (with a label) to `~/.flaris/trusted_keys` |
| `flarisvm --check <file.fls>` | Syntax check only (no execution) |
| `flarisvm --version` | Print version string |
| `flarisvm -c <input.fls> <output.flx> --bundle='<file1.flx>;...' [options]` | Compile and bundle named modules into a self-contained `.flx` |

### Flags - Disablers (default ON)

| Flag | Effect                                           |
|------|--------------------------------------------------|
| `-O` | Disable compiler optimizations                   |
| `-D` | Disable debug symbols (smaller, faster bytecode) |
| `-S` | Disable FFI library (Ffi.Load) SHA-256 checks    |

### Flags - Enablers (default OFF)

| Flag | Effect |
|------|--------|
| `-u` | Enable unsafe code and FFI |
| `-m` | Memory report at shutdown: peak RSS, slab footprint, peak/total object counts, heap bytes, block regions, and leak count. Exits non-zero if leaks are detected. |
| `-s` | Print VM statistics after execution |
| `-t` | Print timing report |
| `-v` | Verbose output |
| `-vv` | Very verbose / print disassembled bytecode before execution |
| `-j` | Enable JIT: embeds JIT IR at compile time; activates native compilation at run time (ARM65 and x86-64) |

### VM Tuning Flags

These override the compiled-in defaults. Values must be within min/max ranges listed in [R6](#r6--vm-limits).

| Flag | Effect |
| ------ | -------- |
| `--stack=<int>` | Override evaluation stack size |
| `--fifo=<int>` | Override object cache (FIFO) size |
| `--slabs=<int>` | Override slab allocator count |
| `--fibers=<int>` | Override maximum fiber count |
| `--frames=<int>` | Override maximum call-frames |

### Imports

| Flag | Effect |
| ------ | -------- |
| `--version=<M.m.p.b>` | Set library version in compiled `.flx` output. Compile mode only; silently ignored at runtime. Examples: `--version=1.0`, `--version=2.1.3`. Default: `1.0.0.0` |
| `--libs=<path>` | Set default library loading-path (otherwise `.`) for VM to load libraries for later resolving imports |
| `--sign=<hex\|keyfile>` | Compile mode only. Embed an Ed25519 signature in the `.flx`. Accepts the 128-hex-char secret key inline or a path to a file containing it. See *Package signing*. |
| `--require-signed` | Runtime. Refuse to load any `.flx` that is unsigned, has an invalid signature, or is signed by a key not in `~/.flaris/trusted_keys`. |

### Module name resolution (case-sensitive)

`library(name, …)` (and `VM.Import`) resolve `name` to a file by **exact,
case-sensitive match**: `library("Signals")` → `Signals.flx` (on the `--libs`
path), `library("./x/Y")` → `./x/Y.flx`, a URL → that exact path. The name is
never lowercased, so `library("signals")` finds `Signals.flx` only on
case-insensitive filesystems (macOS/Windows) and **fails on Linux**. Keep the
import string, the on-disk filename, and any pinned name byte-identical
(convention: capitalized first letter). Imported symbol names must also match the
library's `export` exactly - `{ Jwt }` ≠ `{ JWT }`.

Search order for a non-path name: (1) the name as a path relative to the working
directory, (2) each `;`-separated entry of `--libs`, (3) the per-user install
dir - `~/.flaris/libs` (POSIX) / `%USERPROFILE%\.flaris\libs` (Windows), or
`$FLARIS_LIBS` if set. The first version-satisfying candidate wins (`--libs`
beats the global dir). Step 3 lets a package manager install once into the home
directory and have `flarisvm script.fls` resolve imports with no `--libs` flag.

### Library integrity (pinning)

`library(name, version, hash)` (and `VM.Import(name, version, hash)`) verify the
loaded module against `hash` - the **whole-file SHA-256** of the `.flx`, identical
to `sha256sum file.flx` and to `flarisvm -f file.flx`. The hash is recomputed over
the actual bytes loaded, so it detects any tampering of a downloaded library.
Supply it from a trusted source (a registry, a README, or `flaris.json`).

- An optional `sha256:` prefix is accepted, so a value recorded by flarispm in
  `flaris.json` (`"sha256:<hex>"`) can be pasted verbatim.
- A mismatch halts the VM (fail-closed) and is **not** affected by `-S`
  (`-S` only disables the FFI shared-library check in `Ffi.Load`).
- Omit the hash to load without pinning.
- No hash is embedded in the `.flx` itself: a self-recomputed embedded hash would
  be self-certifying and offer no protection against tampering. The trust anchor
  is the hash you pass in, delivered out-of-band.

### Package signing (Ed25519)

Where the pinned hash above answers *"are these the same bytes I vetted?"* (trust
on first use), an **embedded Ed25519 signature** answers *"who produced this?"* -
authenticity that survives an untrusted transport, registry, or mirror.

A signature is carried inside the `.flx` header (192-byte header; algorithm +
32-byte public key + 64-byte signature). It covers the SHA-256 of the **entire
file with the signature field masked to zero**, so it authenticates the header
metadata (name, version, entry), the constants, and the code together.

**Producing a signed `.flx`:**

```sh
# One-time: generate a key pair (keep the secret key offline).
flarisvm -r 'let kp = Crypto.Ed25519KeyPair();
             File.WriteText("publisher.sk", kp.SecretKey);   // 128 hex chars
             File.WriteText("publisher.pk", kp.PublicKey)'    // 64 hex chars

# Compile and sign (signing is deterministic - the .flx stays reproducible).
flarisvm -c mylib.fls mylib.flx --sign=publisher.sk
```

The public key is printed at compile time and embedded in the file; verify it
later with `Crypto.Ed25519Verify`.

**Verifying at load:**

| Situation | Default (soft) | With `--require-signed` |
| --------- | -------------- | ----------------------- |
| Unsigned `.flx` | runs (no warning) | **rejected** |
| Valid signature, signer **in** `trusted_keys` | runs | runs |
| Valid signature, signer **not** in `trusted_keys` | runs | **rejected** (untrusted signer) |
| Invalid / tampered signature | **rejected (fail-closed)** | **rejected** |

- A **tampered** signed file always fails to load, in both modes - the signature
  no longer matches the recomputed digest.
- **The official Flaris signing key is compiled into the runtime** as a built-in
  trust anchor, so the bundled standard library verifies under `--require-signed`
  with no file to download or configure. The published key is also on the website as a cross-check.
- Additional publishers are trusted via `~/.flaris/trusted_keys` (or
  `$FLARIS_TRUSTED_KEYS`): one 64-hex public key per line, optionally followed by
  whitespace and a human-readable label, e.g. `b6e2…79be  acme corp`. Blank lines
  and `#` comments are ignored. This file is **additive** on top of the built-in
  anchor - add a third-party publisher's key here to trust their packages too. Use
  `flarisvm --import-key <name> <pubkey>` to append one safely (it dedupes). The
  label (and the built-in `flaris-lang.org` name) is shown as the *signer* by
  `flarisvm -d` and `flarisvm --sig-info`.
- Signature verification is independent of `-S` and of the `library(..., hash)`
  pin; use signing for *authenticity* and the hash pin for *exact-artifact* checks.

### Security model (trust boundary)

A `.flx` is **executable code, not a sandboxed format**. Running one is equivalent
to running a native program: it can read and write files, spawn processes
(`Os.Run`), open sockets, and - with `-u` - call arbitrary C via FFI and access raw
memory. On load the VM validates bytecode structure (validate chunks, JIT-IR
validation, size/count caps) to contain *malformed* input safely, but it is **not**
a security sandbox for *malicious* input.

- **Only run `.flx` files you trust.** For third-party libraries, pin the
  whole-file hash via `library(name, version, hash)` (above) so you execute exactly
  the artifact you vetted, and/or require a trusted Ed25519 signature
  (`--require-signed`, see *Package signing*) to authenticate the publisher. These
  are the primary defenses.
- **`-j` (JIT) widens the trust surface.** JIT-compiled functions omit the runtime
  type checks the interpreter performs, so a *tampered* `.flx` run with `-j` can
  crash or corrupt memory where the interpreter would raise a clean error. Do not
  enable `-j` on untrusted bytecode.
- `-u` grants FFI and raw-memory access; never combine it with untrusted code.

### Bundle files

A bundle is a self-contained `.flx` file produced with `--bundle=<file.flx>;...`. It embeds
named dependency chunks in a single file with a TOC at the start. At runtime the
VM pre-loads all bundled deps before execution begins - no filesystem access
occurs for those modules.

**Limitations:**

- Only named modules will be loaded, and they have to be readable from direct path or via `--libs` folder

**Inspecting a bundle:**

```sh
flarisvm -d my_bundle.flx
```

The disassembler shows the bundle TOC, each dep chunk, and the main chunk.

### Common Recipes

```sh
# Development - run with timing and verbose
flarisvm app.fls -v -t

# Production - execute pre-compiled bytecode
flarisvm -e app.flx

# Compile for production (no debug symbols, optimized)
flarisvm -c app.fls app.flx -D

# Enable FFI, skip the FFI shared-library (.so) SHA-256 check
flarisvm app.fls -u -S

# Quick syntax check
flarisvm --check app.fls

# Compile library with explicit version
flarisvm -c mylib.fls mylib.flx --version=1.2.0

# Print the whole-file SHA-256 (== sha256sum) to pin as the third arg to library()
flarisvm -f mylib.flx

# Compile and embed an Ed25519 signature, then run requiring a trusted signer
flarisvm -c mylib.fls mylib.flx --sign=publisher.sk
flarisvm -e mylib.flx --require-signed

# Inspect a package's signature (machine-readable) and trust a publisher
flarisvm --sig-info mylib.flx
flarisvm --import-key "acme corp" <ed25519-pubkey-hex>

# Compile with all imports bundled into a single .flx
flarisvm -c app.fls app.flx --bundle='./data.flx'

# Bundle with explicit version
flarisvm -c app.fls app.flx --bundle='./data.flx' --version=2.0.0

# Inspect bundle contents
flarisvm -d app.flx

# Compile with JIT IR embedded (for hot+eligible functions)
flarisvm -c -j app.fls app.flx

# Run with JIT active (compiles embedded JIT IR to native on function load)
flarisvm -j -e app.flx

# See which functions are JIT-eligible during compilation
flarisvm -v app.fls

# Read source from stdin, compile and run
echo 'fn Main() { Console.WriteLine("hello"); }' | flarisvm -

# Compile from stdin to a .flx file
cat app.fls | flarisvm -c - app.flx

# Execute pre-compiled bytecode piped via stdin
cat app.flx | flarisvm -e -

# End-to-end pipe: compile and execute without touching the filesystem
cat app.fls | flarisvm -c - /tmp/app.flx && flarisvm -e - < /tmp/app.flx
```

---

## R2 - Type System

### Runtime Types

These are the actual types values carry at runtime.

#### Primitive Types

| Type | Description |
|------|-------------|
| `nil` | Absence of value. Only one instance exists. |
| `bool` | Boolean `true` or `false`. |
| `char` | Single Unicode codepoint (unsigned 32-bit). |
| `int` | Signed 64-bit integer. Values within ±2⁶⁰ are tagged immediates (no allocation); larger values are heap-allocated. Full int64 range supported. |
| `float` | 64-bit IEEE-754 double-precision. |
| `string` | Immutable UTF-8 byte sequence. |

#### Composite Types

| Type | Description |
|------|-------------|
| `array` | Dynamic, zero-indexed array. Max 10,000,000 elements. |
| `object` | Hash-based key/value dictionary. Max 10,000,000 properties. |
| `exception` | Exception with code and message. |

#### Structured Types

| Type | Description |
|------|-------------|
| `class` | Class definition object. |
| `instance` | Instance of a class. |

#### Execution Types

| Type | Description |
|------|-------------|
| `fiber` | Lightweight cooperative coroutine. |
| `function` | Callable function value. May be a named function, an anonymous function, or a closure (anonymous function with captured outer variables). |
| `module` | Built-in module namespace. |

#### Binary Types

| Type | Description |
|------|-------------|
| `block` | Raw memory buffer. Element count × element size bytes. |
| `pointer` | Runtime allocated pointer (FFI use). |
| `stream` | File/IO stream handle. |

---

### Type Limits

#### Integer

Flaris integers are full signed 64-bit. Values within ±2⁶⁰ are stored as **tagged pointer immediates** (zero heap allocation, fast path). Values outside that range fall back to heap-allocated objects - same type, same semantics, just slower to create.

| Property | Value |
|----------|-------|
| **Representation** | tagged immediate for \|x\| < 2⁶⁰, heap-allocated otherwise |
| **Minimum** | −9,223,372,036,854,775,808 (−2⁶³) |
| **Maximum** | 9,223,372,036,854,775,807 (2⁶³−1) |
| **Fast (no-alloc) range** | −1,152,921,504,606,846,976 to 1,152,921,504,606,846,975 (~±2⁶⁰) |
| **Overflow behavior** | Wraps (no trap) |
| **Division** | `/` always returns `float` |

Integer literals:

```js
42          // decimal
-10         // negative
0x1234      // hexadecimal
0b1001001   // binary
0o755       // octal
1_000_000   // underscore separator
```

#### Float

| Property | Value |
|----------|-------|
| **Standard** | IEEE-754 double precision (64-bit) |
| **Precision** | ~15–17 significant decimal digits |
| **Minimum positive** | 2.2250738585072014e−308 |
| **Maximum** | 1.7976931348623157e+308 |
| **Special values** | `Infinity`, `-Infinity`, `NaN` |

Float literals:

```js
3.14
-0.5
1.0e6     // scientific notation
.5        // leading zero optional
```

Mixed int + float operations always yield `float`.

#### String

| Property | Value |
|----------|-------|
| **Encoding** | UTF-8 |
| **Immutable** | Yes - mutation creates a new string |
| **Indexing** | Byte-level (returns 1-byte string) |
| **Max size** | 500 MB |

**UTF-8 Indexing Model**

Strings store raw UTF-8 bytes. Characters may occupy 1–4 bytes. The language distinguishes:

- **Byte index** - position in the raw byte buffer. Used by `foreach`, `String.Utf8CpAt`, `String.Utf8CpNext`. Range `0..string.byteLength`.
- **Code-point index** - the Nth Unicode scalar value. Used by `String.Utf8GetCharAt`, `String.Utf8Substr`, `String.Utf8ByteIndexOf`. Range `0..String.Utf8Length(s)`.

Use `String.*` UTF-8 helpers for correct Unicode handling. Standard string indexing (`s[i]`) returns bytes, not characters.

#### Char

`char` is an unsigned 32-bit Unicode codepoint value. The `char()` built-in creates one from an integer codepoint.

#### Bool

Only `nil` and `false` are falsy. Everything else - including `0`, `""`, `[]` - is truthy.

---

### Type Annotations

Type annotations are optional. They produce **warnings**, not errors. All existing unannotated code continues to work.

#### Annotation Syntax

```js
// Parameter types only
fn greet(name: string) { ... }

// Return type only
fn add(a, b): int { ... }

// Full signature
fn calculate(x: int, y: int): int { return x * y; }

// Mixed - some typed, some not
fn format(template: string, value) { return template + str(value); }
```

#### Type Keywords

**Primitive:**

| Keyword | Runtime Type | Notes |
|---------|-------------|-------|
| `int` | Integer | Full int64; tagged immediate for \|x\| < 2⁶⁰ |
| `float` | Float | 64-bit IEEE-754 |
| `number` | int or float | Shorthand for `int\|float` |
| `string` | String | UTF-8 text |
| `bool` | Boolean | true / false |
| `char` | Char | 32-bit codepoint |
| `nil` | Nil | Null / absent |

**Collection:**

| Keyword | Runtime Type |
|---------|-------------|
| `array` | Dynamic array |
| `object` | Key-value hashmap |

**Execution:**

| Keyword | Runtime Type |
|---------|-------------|
| `function` | Callable function |
| `fiber` | Cooperative coroutine |
| `class` | Class definition |
| `instance` | Class instance |

**Special:**

| Keyword | Meaning |
|---------|---------|
| `any` | Accepts any type - disables checking for that parameter |
| `stream` | File / stream handle |
| `block` | Binary data buffer |
| `pointer` | Runtime pointer (FFI) |

#### Union Types

Multiple acceptable types separated by `|`:

```js
fn findUser(id: int): object|nil { ... }
fn abs(n: int\|float): int\|float { ... }

// 'number' is shorthand for int\|float
fn abs(n: number): number { ... }
```

The checker validates that provided values match **at least one** type in the union.

#### Array Type Annotations

Wrap an element type in `[...]` to annotate an array variable or parameter:

```js
let scores:[int] = [10, 20, 30];
let names:[string] = ["alice", "bob"];

fn sum(values:[int]): int { ... }
fn process(rows:[[object]]): nil { ... }  // nested arrays
```

The element type is checked by the analyzer but is not tracked at runtime - the value's runtime type remains `array`. The `[T]` form can appear anywhere a plain type name is valid, including unions:

```js
let data:[int]|nil = nil;
```

#### Optional Parameters

Append `?` directly after the parameter name to mark it as optional. The caller may omit trailing optional arguments; the VM automatically passes `nil` for each one not provided.

```js
fn greet(name?: string)            { ... }  // name is nil if omitted
fn log(msg: string, level?: int)   { ... }  // level is nil if omitted
fn tag(a: string, b?, c?: string)  { ... }  // b is any?, c is string?
```

Rules:

- `?` goes on the **name**, before the `:` - `x?`, `x?: int`, both valid. `x: int?` is not valid.
- Optional parameters must trail all required ones.
- Omitted arguments always arrive as `nil` - use `??` to substitute a default inside the function body.
- Type checking still applies for arguments that *are* provided.

```js
fn greet(name?: string) {
    Console.WriteLine("Hello " + (name ?? "stranger"));
}

greet("World");  // Hello World
greet();         // Hello stranger
```

#### Variable Type Annotations

| Form | Scope | Notes |
| ------ | ------- | ------- |
| `let name = expr` | Local (block / function) | Cannot be used at module top level |
| `var name = expr` | Local (block / function) | Identical to `let` |
| `const name = expr` | Local or global | Immutable binding |
| `global name = expr` | Module (global) | Warns if name already defined; accessible from all scopes in the file |
| `global name:type = expr` | Module (global) | Type-annotated global |

---

Variables can be optionally annotated with a type at declaration time using `let name:type = value`. Once annotated, only values of that type may be assigned.

```js
// Inside a function - use let/var:
let y:int = 2;
y = "test";         // Error - y declared as int

let i:instance = nil;
i = new MyClass();  // OK - instance, string, object, array may be nil

let scores:[int] = [1, 2, 3];  // array of int

// At module level - use global:
global counter:int = 0;
global config:object = { debug: false };
```

---

### Type Cast Expression

A **type cast** is a compile-time and (for scalar types) runtime type coercion.
Syntax: `(type)expr`

```js
let arr = (array)Object.Clone(original);
let n   = (int)3.9;       // - 3 (truncates)
let s   = (string)42;     // - "42"
let b   = (bool)0;        // - false
```

#### Supported Cast Types

| Type | Kind | Runtime effect |
| ------ | ------ | --------------- |
| `int` | Value | `OP_TO_INT` - truncates float, converts string/bool/char |
| `float` | Value | `OP_TO_FLOAT` - widens int to double |
| `string` | Value | `OP_TO_STRING` - any value - string representation |
| `bool` | Value | Double logical-NOT (`!!x`) - truthy - `true`, falsy - `false` |
| `array` | Hint | No bytecode emitted - analyzer type update only |
| `object` | Hint | No bytecode emitted - analyzer type update only |
| `class` | Hint | No bytecode emitted - analyzer type update only |
| `instance` | Hint | No bytecode emitted - analyzer type update only |
| `block` | Hint | No bytecode emitted - analyzer type update only |
| `stream` | Hint | No bytecode emitted - analyzer type update only |

#### Analyzer Behaviour

- The inferred type of `(T)expr` is always `T`, regardless of what `expr`
  would otherwise infer to.
- If the cast is the RHS of a `let`/`var` declaration or assignment, the
  variable's tracked type is updated to `T` immediately. All subsequent
  uses of that variable are checked against the new type.
- No warning is emitted for type changes caused by a cast - the programmer's
  intent is explicit.

#### Compiler Behaviour

- Value casts (`int`, `float`, `char`, `string`, `bool`) compile the inner expression
  then emit one conversion opcode.
- Hint casts compile only the inner expression - zero extra bytecode.
- Cast nodes are fully transparent to the optimizer and inliner.

#### Disambiguation

The parser uses two-token lookahead to distinguish a cast from a grouped
expression:

```js
(int)x        // cast - type keyword followed immediately by ')'
(int + 1)     // grouped expression - not a cast
(myVar)expr   // grouped expression - myVar is not a cast-type keyword
```

Only the ten listed type names are recognised as cast targets. All other
identifiers inside `(...)` are treated as grouped expressions.

---

### Closures

A **closure** is an anonymous function that captures variables from its enclosing scope.
Captured variables are snapshotted **by value** at the moment the inner function is
created (i.e., when the enclosing function executes the expression `fn(...) { ... }`).

#### What can be captured

Both parameters and body-local variables of the immediately enclosing function are
eligible for capture. Module-level globals are always accessible directly and are
not captured - they are read live from the global environment on each access.

#### Capture semantics

- **Scalars** (`int`, `float`, `bool`, `string`, `char`, `nil`): snapshot copy. Later changes to the outer variable do not affect the captured value.
- **References** (`array`, `object`, `instance`): reference copy. The closure and the outer scope share the same heap object; mutations are visible on both sides.

#### Example

```js
fn makeAdder(x) {
    return fn(n) { return x + n; };
}

let add5 = makeAdder(5);
Console.WriteLine(add5(3));   // 8
```

```js
fn makeCounter() {
    let state = [0];
    return fn() {
        state[0] = state[0] + 1;
        return state[0];
    };
}

let c = makeCounter();
Console.WriteLine(c());  // 1
Console.WriteLine(c());  // 2
```

#### Loop capture

Each loop iteration creates a new closure with its own snapshot of the loop variable:

```js
fn makeFns() {
    let funcs = [];
    iter(i from 0 to 3) {
        Array.Append(funcs, fn() { return i; });
    }
    return funcs;
}

let fs = makeFns();
Console.WriteLine(fs[0]());  // 0
Console.WriteLine(fs[2]());  // 2
```

#### Runtime representation

A closure is a cloned function object with a private environment table that holds the
captured names and values. At call time, the VM sets this table as the active
environment, so captured names resolve before the module globals. The `function` type
applies to both plain functions and closures; `VM.GetFunctionInfo` returns the same
fields for both.

---

## R3 - Built-in Functions

These functions are VM instructions - available everywhere, no namespace required.

### Type Information

| Function | Description |
| ---------- |------------- |
| `type(x)` | Returns the runtime type as an `int` bitmask. Compare with `Type` module constants: `type(x) == Type.Int`. |
| `is_nil(x)` | Returns `true` if `x` is `nil`. |
| `is_array(x)` | Returns `true` if `x` is an array. |
| `is_object(x)` | Returns `true` if `x` is an object. |

### Type Conversion

| Function | Converts to | Notes |
|----------|-------------|-------|
| `int(x)` | Integer | Truncates floats toward zero |
| `float(x)` | Float | Widens int to double |
| `str(x)` | String | Converts any value to string |
| `char(x)` | Char | 32-bit Unicode codepoint |

### Bitcast / Narrowing

| Function | Result type | Wraps on overflow |
|----------|-------------|-------------------|
| `u8(x)` | unsigned 8-bit (0–255) | Yes |
| `i8(x)` | signed 8-bit (−128–127) | Yes |
| `u16(x)` | unsigned 16-bit | Yes |
| `i16(x)` | signed 16-bit | Yes |
| `u32(x)` | unsigned 32-bit | Yes |
| `i32(x)` | signed 32-bit | Yes |

```js
let x = u8(300);   // - 44 (wraps)
let y = i8(-200);  // - 56 (wraps)
```

### Length

```js
len([1, 2, 3]);        // 3 (array element count)
len("hello");          // 5 (byte count, not char count)
len({ a: 1, b: 2 });   // 2 (property count)
```

### Guard

```js
guard(expr)   // Raises Exception.GuardCheck if expr evaluates to nil
```

---

## R4 - The Exception class

`Exception` is a built-in base **class**. Every thrown value - whether from a
user `throw` or an internal VM error (index-out-of-bounds, divide-by-zero, …) -
is an `Exception` (or subclass) instance.

**Construct / throw:**

```js
throw new Exception(code, msg);   // explicit
throw(code, msg);                 // shorthand - desugars to new Exception(code, msg)
class NetworkError : Exception {}  // subclass; new NetworkError(503, "down") inherits the constructor
```

**Constructor:** `Exception(code:int, msg:string)` - sets `Code`, `Error`, and
captures `StackTrace`.

**Instance fields:**

| Field | Type | Description |
|-------|------|-------------|
| `Code` | int | Numeric error code (see the constants below) |
| `Error` | string | Human-readable message |
| `StackTrace` | array | Call frames at throw time, as strings |

**Instance methods:**

| Method | Signature | Description |
|--------|-----------|-------------|
| `ToString` | `ToString() - string` | `"<TypeName> (code N): message"` (uses the subclass name) |
| `Name` | `Name() - string` | The exception's type name (its class name) |
| `StackTraceString` | `StackTraceString() - string` | `StackTrace` joined into one block of text |

Generic class introspection works too (`Class.InstanceOf(e, Exception)`,
`Class.Name(e)`, …). For type dispatch use `e is SomeError` or
`switch (e) { case SomeError: … }`. See the language guide's *Exceptions*
section for full semantics.

### Error-code constants

Exposed as **static members of the `Exception` class** (`Exception.RuntimeError`,
…). Each has a numeric code (stable ABI - must not be renumbered) used as the
`Code` of the raised exception.

| Constant | Code | Description |
|----------|------|-------------|
| `Exception.NullPtr` | `0` | Null-pointer access |
| `Exception.DivByZero` | `1` | Division by zero |
| `Exception.ModByZero` | `2` | Modulo by zero |
| `Exception.InvalidArguments` | `3` | Invalid function arguments |
| `Exception.OutOfBounds` | `4` | Index out of bounds |
| `Exception.IOError` | `5` | I/O error (file, stream, socket) |
| `Exception.RuntimeError` | `6` | General VM runtime error |
| `Exception.InvalidState` | `7` | VM or object in invalid state |
| `Exception.OutOfMemory` | `8` | Memory allocation failed |
| `Exception.InvalidMemoryAccess` | `9` | Invalid memory access |
| `Exception.SizeLimit` | `10` | Container or allocation size limit exceeded |
| `Exception.GuardCheck` | `11` | `guard()` failed - value was nil |
| `Exception.StackError` | `12` | Stack overflow / underflow / corruption |
| *(reserved)* | `13` | Intentionally unused |
| `Exception.UnsafeOperation` | `14` | Unsafe operation blocked (requires `-u`) |
| `Exception.NestingError` | `15` | Excessive recursion or nesting depth |
| `Exception.IllegalInstruction` | `16` | Invalid bytecode instruction |
| `Exception.ExecOutOfMemory` | `17` | Out of memory during execution |
| `Exception.OutOfFibers` | `18` | Fiber limit reached |
| `Exception.ConstAssign` | `19` | Attempt to assign to a constant |
| `Exception.ChecksumError` | `20` | FFI shared-library SHA-256 verification failed (`Ffi.Load`) |
| `Exception.ClassNonStaticCall` | `21` | Non-static method called without instance |

**Design notes:**

- Exceptions 16, 20 indicate corrupted or hostile bytecode - treated as hard failures.
- `OutOfMemory` (8) and `ExecOutOfMemory` (17) are separate to enable more precise diagnostics.
- When an exception fires, the current fiber aborts and unwinds to the nearest error boundary. Other fibers continue. The VM itself stays alive.

---

## R5 - Operator Precedence

Operators listed from **highest** (evaluated first) to **lowest** (evaluated last).

| Level | Category | Operators | Associativity |
|-------|----------|-----------|---------------|
| 1 | Primary | literals, identifiers, `(...)` | N/A |
| 2 | Postfix | `obj.prop` `arr[i]` `fn(...)` `x++` `x--` | Left |
| 3 | Prefix Unary | `+x` `-x` `!x` `~x` `await` `yield` `new` `guard()` | Right |
| 4 | Multiplicative & Bitwise | `*` `/` `%` `^^` `<<` `>>` `&` `\|` `^` | Left |
| 5 | Additive | `+` `-` | Left |
| 6 | Comparison | `<` `<=` `>` `>=` | Left |
| 7 | Equality | `==` `!=` `≈` `in` `is` | Left |
| 8 | Logical AND | `&&` `and` | Left |
| 9 | Logical OR | `\|\|` `or` | Left |
| 10 | Null Coalescing | `??` | Left |
| 11 | Conditional | `? :` | Right |
| 12 | Assignment | `=` `+=` `-=` `*=` `/=` `%=` `&=` `\|=` `^=` `<<=` `>>=` `??=` `^^=` | Right |

**Key rules:**

- `&&` binds tighter than `||` - `a || b && c` means `a || (b && c)`. `and`/`or` are tokenizer aliases with identical precedence.
- `??` (null coalescing) is lower than all arithmetic - `x + y ?? z` means `(x + y) ?? z`.
- Division `/` always produces `float` even with integer operands.
- `^^` is exponentiation (`x ^^ y` = x raised to y).
- `≈` (approximate equality) uses `APPROX_REL_EPS = 1e-6` and `APPROX_ABS_EPS = 1e-3`.

---

## R6 - VM Limits and portability

### Fibers and Scheduling

| Limit | Default | Maximum | Description |
| ------- | --------- | --------- | ------------- |
| Fibers | 256 | 1024 | Concurrent fibers tracked by the scheduler |
| Events | 64 | 64 | Event slots (I/O, async, etc.) |
| Timers | 64 | 64 | Active timers |
| Per fiber instruction buffer | 100,000 | 1,000,000 | Instructions executed per fiber before auto-yield |

### Call Stack and Execution

| Limit | Default | Maximum | Description |
| ------- | ------- | ------- | ------------- |
| Call frames | 64 | 1,024 | Call stack depth; override with `--frames=` |
| Nested try/catch handlers | 24 | 24 | Maximum nested try/catch handlers |
| Evaluation stack | 4,096 | 65,535 | VM evaluation stack size; override with `--stack=` |

### Locals and Arguments

| Limit | Value | Description |
| ------- | ------- | ------------- |
| Local variables per function | 128 | Maximum local variables per function |
| Function call arguments | 16 | Maximum arguments in user function calls |

### Compiler and Parser

| Limit | Value | Description |
|-------|-------|-------------|
| AST nodes per compilation unit | 100,000 | |
| Parser nesting depth | 256 | |
| Object/array walk depth | 64 | Max nesting when walking objects/arrays (stringify, clone, etc.) |
| `VM.Eval()` / `VM.Compile()` source | 10 KB | Max source length |

### Bytecode and Chunks

| Limit | Value | Description |
|-------|-------|-------------|
| Source file size | 10 MB | Maximum source file size |
| Bytecode per function | 10 MB | Maximum compiled bytecode per function |
| Constants per function | 16,385 | Maximum constant pool entries per function |

### Strings and Text

| Limit | Value | Description |
|-------|-------|-------------|
| String size | 100 MB | Maximum size of a single string |
| String split parts | 100,000 | Maximum parts returned by string split |
| OS command output | 16 MB | Maximum captured stdout/stderr from OS commands |

### Memory Blocks and Buffers

| Limit | Value | Description |
|-------|-------|-------------|
| Single block allocation | 2 GiB | Maximum size of a single block allocation |
| Total block allocations | 64 GiB | Total cap across all active block allocations |
| `Memory.Process()` input | 16 MB | Max bytes per call |

### Collections

| Limit | Value | Description |
| --- | --- | --- |
| Array elements / object properties | 10,000,000 | Max elements in an array or properties in an object |
| Hash function input | 1 GiB | Max input to hashing functions |

### Classes

| Limit | Value | Description |
| --- | --- | --- |
| Inheritance chain depth | 8 | Maximum class inheritance depth |
| CLI arguments | 128 | Maximum CLI arguments passed to the VM |

#### Special Methods

| Method | Called when | Notes |
| --- | --- | --- |
| `Constructor(...)` | `new ClassName(...)` | Optional. Return value is ignored; `new` always returns the instance. |
| `Destructor()` | Last reference to the instance is released | Optional. `this` is the dying instance. Exceptions are swallowed. Not inherited. |

### Float Approximation Thresholds

| Threshold | Value | Description |
| --- | --- | --- |
| Relative epsilon (`≈`) | `1e-6` | Relative tolerance for the `≈` operator |
| Absolute epsilon (`≈`) | `1e-3` | Absolute tolerance for the `≈` operator |

### Platform Portability (32-bit vs 64-bit)

#### Compiled Bytecode (.flx)

`.flx` files are fully portable between 32-bit and 64-bit systems running the
same VM version. The bytecode format uses only fixed-width integer types; no
pointer-sized fields are stored on disk.

#### Integer Range

On 64-bit systems, small integers are stored as tagged pointers (range ±2^62).
On 32-bit systems the tagged range is narrower (±2^29 ≈ ±536 million). Integers
outside this range are heap-allocated transparently - behavior is identical,
only allocation strategy differs. Scripts do not need to account for this.

#### Pointer-width APIs

Functions that expose raw memory addresses (`Buffer.GetAddress`,
`Memory.Alloc`) return integers sized to the host platform: 32-bit values on
32-bit systems, 64-bit on 64-bit systems. Addresses stored in `.flx` constants
or serialized to disk are not portable between platforms.

#### Detection

Use `Os.Bits()` and `Os.Arch()` to branch on platform at runtime:

```js
if (Os.Bits() == 32) {
    // 32-bit specific path
}
```

---

## R7 - Performance Model

Understanding how Flaris executes helps write fast, predictable programs.

### Execution Model

Flaris compiles source to bytecode, then runs it on a stack-based VM. On ARM64 (`aarch64`) and x86-64, eligible functions are additionally compiled to native machine code at first call - see [R11](#r11---jit-compilation) for details. On other platforms the bytecode interpreter is the only execution tier.

### Dynamic Typing and Runtime Checks

Types are checked at runtime. Invalid operations fail fast with no undefined behavior. In hot loops, avoid changing the type of a variable frequently - type stability helps the VM dispatch efficiently.

```js
// Good - type-stable loop
let sum = 0;
for (let i = 0; i < n; i += 1) {
    sum += arr[i];
}
```

### Memory Management

Flaris uses **deterministic reference counting**, not a garbage collector.

- Objects are freed immediately when the last reference drops.
- No stop-the-world pauses.
- Avoid creating short-lived objects inside tight loops.
- Avoid reference cycles (they are not collected).
- Reuse arrays and objects where possible.

### Locals vs Property Access

Local variables are the fastest storage. Property lookups require a hashmap traversal.

```js
// Cache property before a hot loop
let v = obj.value;
for (...) {
    sum += v;   // fast - local
}
```

### Arrays vs Objects

| Structure | Access | Memory | Best for |
|-----------|--------|--------|----------|
| `array` | O(1) index | Compact | Numeric loops, dense data |
| `object` | O(1) hash | Flexible | Structured data, named fields |

Prefer arrays for anything numeric or iteration-heavy.

### Built-ins vs Script Loops

Built-in functions execute closer to the VM and reduce dispatch overhead. Prefer them over equivalent hand-written loops.

```js
// Better - built-in
let squares = Array.Select(xs, fn(x){ return x * x; });

// Slower - script loop
let r = [];
for (...) { Array.Append(r, x * x); }
```

### Fibers and Scheduling

Fibers are cooperatively scheduled - they never run unless resumed or awaited. No data races by default.

- Batch work inside a fiber before yielding.
- Avoid spawning fibers for tiny tasks.
- Avoid `yield` inside tight numeric loops.
- Default scheduling quantum per fiber: 100,000 checkpoints (tunable via `Fiber.SetQuantum()`). A checkpoint occurs at each loop back-edge and at call/return boundaries - not at every instruction.

### Strings

Strings are immutable. Each concatenation allocates.

- Avoid repeated concatenation in loops.
- Accumulate data first, convert to string once.
- For large text building, collect into an array and use `String.Join()`.

### Binary and Numeric Heavy Work

For heavy data processing, use `block` values and `Memory.*` functions. This minimizes object allocation and works similarly to `Span<byte>` in systems languages.

```js
let buf = Buffer.Create(1024, 1);
Buffer.Fill(buf, 0);
Memory.Process(Buffer.GetAddress(buf), 1024, 1, fn(x){ return x + 1; });
```

### Measure, Don't Guess

Most common bottlenecks are:

- Excessive allocations in hot paths
- Too many small fibers
- Repeated property lookups in loops
- Overuse of dynamic object structures where arrays suffice

Use `-t` (timing) and `-s` (statistics) flags. Profile real workloads before optimizing.

### Mental Model Summary

| What | Cost |
|------|------|
| Local variable read/write | Very cheap |
| Array index access | Cheap |
| Property lookup | Moderate (hashmap) |
| Built-in function call | Cheap |
| Script function call | Moderate |
| Object/array allocation | Moderate |
| String concatenation | Creates new string |
| Fiber creation | Cheap |
| Fiber context switch | Cheap |

---

## R8 - Standard Library

Flaris ships 33 built-in modules covering everything from math and string processing to networking, concurrency, graphics, and low-level memory access.

### Usage

All standard library functions are accessed through their namespace:

```fls
let sorted = Array.Sort(myArr);
let digest = Hash.Sha256("hello");
let body = Http.Get("https://example.com");
```

No import statement is needed - all modules are always available. Two modules (`FFI`, `Memory`) additionally require the `-u` flag at runtime.

### Module Index

| Category | Module | Description |
| ---------- | -------- | ------------- |
| **Data** | `Array` | Map, filter, sort, and aggregate arrays |
| | `Buffer` | Low-level fixed-size binary buffers |
| | `Collections` | Stack, Queue, HashMap, PriorityQueue, MaxPriorityQueue, and LinkedList factory functions |
| | `Object` | Introspect and manipulate objects and instances |
| | `Class` | Type-level reflection and inheritance inspection |
| | `Type` | Runtime type constants for use with `type()` |
| **Strings & Text** | `String` | UTF-8 string operations |
| | `Regex` | POSIX extended regular expression matching |
| | `Json` | JSON parse, serialize, and path query |
| | `Convert` | Type conversion across all integer widths |
| **Math & Encoding** | `Math` | Full mathematical library |
| | `Hash` | Non-cryptographic and cryptographic hashes |
| | `Crypto` | XChaCha20-Poly1305 encryption and CSPRNG |
| | `Compress` | zlib/gzip-compatible compression |
| | `Util` | Encoding helpers and value comparison |
| **I/O & Files** | `File` | File read, write, and metadata |
| | `Directory` | Filesystem directory operations |
| | `Path` | Cross-platform path string manipulation |
| | `Stream` | Unified I/O for files, sockets, and serial ports |
| | `Console` | Terminal I/O, color, and cursor control |
| | `FileWatch` | Watch file-states on files and callbacks |
| **Networking** | `Http` | HTTP client (sync/async) and minimal server |
| | `Tls` | Built-in TLS byte transport (OS-native: Secure Transport / OpenSSL / SChannel) |
| | `Net` | DNS, IP conversion, and interface inspection |
| **Concurrency** | `Fiber` | Green-thread creation, messaging, and lifecycle |
| | `Event` | One-shot fiber synchronization (max 64 events) |
| | `Scheduler` | Low-level fiber scheduling primitives |
| | `Timers` | Fiber-based timer scheduling (max 256 timers) |
| **Graphics** | `Gfx` | Software rasterizer, up to 32768×32768 canvas |
| **System** | `OS` | Process, environment, and system interface |
| | `VM` | VM introspection and control |
| | `FFI` | Dynamic native library loading ⚠️ |
| | `Memory` | Unsafe raw memory access ⚠️ |
| **Time** | `Time` | Unix timestamp manipulation |
| **Debug** | `Debug` | Assertions, introspection, and runtime checks |

⚠️ = requires `-u` flag (`flarisvm -u <file>`)

### Type Annotation Shorthands

| Shorthand | Meaning |
| ----------- | --------- |
| `any` | every type available |
| `number` | `int` or `float` |
| `numeric` | `int`, `float`, `bool`, or `char` |
| `ptr` | `int` (raw address), `string`, `block`, or `pointer` - anything pointer-like |
| `string\|block` | string or binary block |
| `object\|instance\|class` | any object-like value |

Return type is shown after `-`. Optional arguments are shown as `arg?`.

---

### Array

Namespace: **`Array`**

Higher-level operations on arrays: mapping, filtering, sorting, aggregation, and capacity management. Most functions create new arrays; some mutate in-place (noted in Description).

| Function | Signature | Description |
| ---------- | ----------- | ------------- |
| **All** | `All(arr:array, func:function ) - bool` | `true` if all elements satisfy `fn(x)`. Stops early on first falsy. |
| **Any** | `Any(arr:array, func:function ) - bool` | `true` if any element satisfies `fn(x)`. Stops early. |
| **Append** | `Append(arr:array, value:any) - arr` | Appends `value` to end of `arr`. Mutates. |
| **Average** | `Average(arr:array) - float` | Returns the arithmetic mean of all elements as `float`. Returns `nil` if empty. |
| **Capacity** | `Capacity(arr:array) - int` | Returns current internal capacity (may exceed length). |
| **Clear** | `Clear(arr:array) - bool` | Removes all elements; capacity kept. Mutates. |
| **Concat** | `Concat(a:array, b:array) - array` | Returns new array: all of `a` followed by all of `b`. |
| **Contains** | `Contains(arr:array, value:any) - bool` | `true` if any element equals `value` (deep equality). |
| **Create** | `Create(size:int, type?:int) - array` | Creates new array with `size` pre-allocated slots. Optional `type` (a `Type.*` constant) pre-fills all slots with the zero value for that type - `Type.Float` gives a `[float]` array of 0.0 values; `Type.Int` gives a `[int]` array of 0 values; `Type.String` gives a `[string]` array of "" valyes. Without a type argument all slots are `nil`. |
| **Distinct** | `Distinct(arr:array) - array` | Returns new array with only first occurrence of each value. |
| **Fill** | `Fill(arr:array, value:any) - array` | Sets all existing elements to `value` in-place. Returns same array. Mutates. |
| **ForEach** | `ForEach(arr:array, func:function) - array` | Calls `fn(x)` for every element; callback return value is discarded. Returns the original array. |
| **First** | `First(arr:array) - any` | Returns first element or `nil` if empty. |
| **Flatten** | `Flatten(arr:array) - array` | Returns new array with one level of nested arrays collapsed. Non-array elements are kept as-is. |
| **GroupBy** | `GroupBy(arr:array, func:function) - object` | Groups elements by key. Calls `fn(x)` for each element; the return value becomes the group key (coerced to string). Returns an object where each key maps to an array of the elements that produced it. Empty array returns `{}`. |
| **Chunk** | `Chunk(arr:array, size:int) - array` | Splits `arr` into sub-arrays of at most `size` elements. The last chunk may be smaller. Returns a new array of arrays. |
| **CountBy** | `CountBy(arr:array, func:function) - object` | Like `GroupBy` but returns `{key: count}` instead of `{key: [elements]}`. Calls `fn(x)` for each element; result is coerced to string as the key. |
| **FlatMap** | `FlatMap(arr:array, func:function) - array` | Calls `fn(x)` for each element; if the result is an array its elements are spread into the output, otherwise the result itself is appended. One level of flattening. |
| **IndexOf** | `IndexOf(arr:array, value:any) - int` | Returns first index of `value` or `-1` if not found. |
| **InsertAt** | `InsertAt(arr:array, index:int, value:any) - bool` | Inserts `value` at `index`, shifts remaining right. Mutates. |
| **Last** | `Last(arr:array) - any` | Returns last element or `nil` if empty. |
| **Process** | `Process(arr:array, func:function) - array` | Applies `fn(value:any)` to every element in-place, replacing each element with the return value. Returns same array. Mutates. |
| **ProcessIdx** | `ProcessIdx(arr:array, func:function) - array` | Applies `fn(value:any, index:int)` to every element in-place, replacing each element with the return value. Returns same array. Mutates. |
| **Reduce** | `Reduce(arr:array, func:function , initial:int) - int` | Aggregates: `acc = fn(acc, x)` starting from `initial`. Integer only. |
| **RemoveAt** | `RemoveAt(arr:array, index:int) - bool` | Removes element at `index`, shifts remaining left. Mutates. |
| **Reserve** | `Reserve(arr:array, capacity:int) - bool` | Ensures capacity ≥ `capacity`. Does not affect length. Mutates capacity. |
| **Reverse** | `Reverse(arr:array) - array` | Reverses elements in-place. Returns same array. Mutates. |
| **Select** | `Select(arr:array, func:function ) - array` | Returns new array by applying `fn(x)` to every element (map). |
| **Shuffle** | `Shuffle(arr:array) - array` | Randomly shuffles elements in-place (Fisher-Yates, CSPRNG). Returns same array. Mutates. |
| **ShrinkToFit** | `ShrinkToFit(arr:array) - bool` | Reduces capacity to match current length. Mutates capacity. |
| **Skip** | `Skip(arr:array, count:int) - array` | Returns new array with first `count` elements removed. |
| **MaxBy** | `MaxBy(arr:array, func:function) - any` | Returns the element for which `fn(x)` produces the largest key. Returns `nil` if `arr` is empty. Builtin functions (e.g. `String.Length`) use the fast path. |
| **MinBy** | `MinBy(arr:array, func:function) - any` | Returns the element for which `fn(x)` produces the smallest key. Returns `nil` if `arr` is empty. Builtin functions use the fast path. |
| **Partition** | `Partition(arr:array, func:function) - array` | Returns a two-element array `[matches, rest]` where `matches` contains elements for which `fn(x)` is truthy and `rest` contains the remainder. |
| **Sort** | `Sort(arr:array, fn?:function) - array` | Sorts in-place. Optional comparator `fn(a,b)-bool`. Mutates. |
| **SortBy** | `SortBy(arr:array, func:function) - array` | Sorts in-place by extracted key: calls `fn(x)` once per element and compares the results. Builtin functions (e.g. `String.Length`, `Type.Of`) use the fast path. Mutates. |
| **Tally** | `Tally(arr:array) - object` | Counts occurrences of each element (converted to string). Returns `{value: count}`. |
| **UniqueBy** | `UniqueBy(arr:array, func:function) - array` | Returns a new array keeping only the first element for each distinct value of `fn(x)`. Preserves order. |
| **Subset** | `Subset(arr:array, from:int, count:int) - array` | Returns new array of `count` elements starting at index `from`. |
| **Swap** | `Swap(arr:array, i:int, j:int) - bool` | Swaps elements at indices `i` and `j`. Mutates. |
| **Take** | `Take(arr:array, count:int) - array` | Returns new array of first `count` elements. |
| **Where** | `Where(arr:array, func:function ) - array` | Returns new array of elements for which `fn(x)` is truthy (filter). |
| **Zip** | `Zip(a:array, b:array) - array` | Returns array of `[a[i], b[i]]` pairs. Length is `min(len(a), len(b))`. |

#### Array instance method syntax

All `Array` functions where the array is the first argument can be called directly on an array value:

```flaris
var a: array = [3, 1, 4, 1, 5];
a = a.Append(9)              // same as Array.Append(a, 9)
a = a.Sort()                 // same as Array.Sort(a)
a.First()                    // 3
a.Last()                     // 9
a.Contains(4)                // true
a.Sum()                      // 23.0
a.Select(fn(x) { return x * 2; })
a.Where(fn(x) { return x > 3; })
a.Reverse()
```

Functions that do not take an array as their first argument (`Create`, and similar factories) are not available as instance methods. Both forms compile to identical bytecode when the variable is typed (`: array` or inferred). Untyped variables fall back to a runtime dispatch.

---

### Buffer

Namespace: **`Buffer`**

Low-level binary memory operations. A buffer is `count` elements × `size` bytes per element.

| Function | Signature | Description |
| -------- | --------- | ----------- |
| **ChangeToString** | `ChangeToString(buf:block) - bool` | Reinterprets buffer in-place as `string`. Destructive - original block handle is invalid after call. |
| **Copy** | `Copy(src:block, srcOff:int, dst:block, dstOff:int, len:int) - bool` | Copies `len` bytes from `src` to `dst`. Offsets in bytes. Bounds-checked. |
| **CopyBytesToArray** | `CopyBytesToArray(buf:block, arr:array, offset:int) - bool` | Writes `len(buf)` raw bytes from `buf` into `arr` starting at `offset`. Existing elements are updated in-place; elements past the current array length are appended. Optional `offset` defaults to 0. |
| **CopyStringAt** | `CopyStringAt(buf:block, offset:int, s:string, n:int) - bool` | Copies `n` bytes of `s` into `buf` at byte `offset`. `n` defaults to `len(s)`. Bounds-checked; returns `false` if `offset + n > len(buf)`. |
| **Create** | `Create(count:int, size:int) - block` | Allocates new buffer: `count` elements, `size` bytes each. |
| **Fill** | `Fill(buf:block, value:int) - bool` | Fills entire buffer with byte `value` (0–255). Mutates. |
| **FromArray** | `FromArray(buf:block, arr:array) - bool` | Writes integer array into buffer elements. Element size must be 1, 2, 4, or 8. Mutates. |
| **FromString** | `FromString(s:string) - block` | Copies the raw bytes of `s` into a new block (`count = len(s)`, `size = 1`). Non-destructive - both string and block remain valid. |
| **GetAddress** | `GetAddress(buf:block) - int` | Returns raw memory address as integer (for FFI/Memory API use). |
| **ReadU8At** | `ReadU8At(buf:block, offset:int) - int` | Reads one byte at `offset`. Returns 0 if out of bounds. |
| **ReadU16At** | `ReadU16At(buf:block, offset:int, bigEndian:bool) - int` | Reads 2-byte unsigned integer at `offset`. Optional `bigEndian` defaults to `false` (little-endian). Returns 0 if out of bounds. |
| **ReadU32At** | `ReadU32At(buf:block, offset:int, bigEndian:bool) - int` | Reads 4-byte unsigned integer at `offset`. Optional `bigEndian` defaults to `false`. Returns 0 if out of bounds. |
| **ReadU64At** | `ReadU64At(buf:block, offset:int, bigEndian:bool) - int` | Reads 8-byte integer at `offset`. Optional `bigEndian` defaults to `false`. Returns 0 if out of bounds. |
| **Reserve** | `Reserve(buf:block, capacity:int) - bool` | Ensures `buf` has at least `capacity` bytes allocated. Grows the buffer in-place via `realloc` if needed; the block object is mutated and `len(buf)` reflects the new capacity. No-op if already large enough. |
| **Slice** | `Slice(buf:block, offset:int, count:int) - block` | Returns new buffer: `count` elements starting at element index `offset`. |
| **StringToArray** | `StringToArray(s:string, arr:array, offset:int) - int` | Writes the raw UTF-8 bytes of `s` directly into `arr` at `offset` without allocating an intermediate block. Returns the number of bytes written (`len(s)`). Optional `offset` defaults to 0. |
| **ToArray** | `ToArray(buf:block, offset:int, count:int) - object` | Returns object with array of integers from buffer elements. Element size must be 1, 2, 4, or 8. |
| **ToString** | `ToString(buf:block) - string` | Copies the raw bytes of `buf` into a new string. Non-destructive - block remains valid. Total bytes read = `count × size`. |
| **WriteU8At** | `WriteU8At(buf:block, offset:int, v:int) - bool` | Writes `v & 0xFF` at byte `offset`. Returns `false` if out of bounds. |
| **WriteU16At** | `WriteU16At(buf:block, offset:int, v:int, bigEndian:bool) - bool` | Writes 2-byte integer at `offset`. Optional `bigEndian` defaults to `false`. Returns `false` if out of bounds. |
| **WriteU32At** | `WriteU32At(buf:block, offset:int, v:int, bigEndian:bool) - bool` | Writes 4-byte integer at `offset`. Optional `bigEndian` defaults to `false`. Returns `false` if out of bounds. |
| **WriteU64At** | `WriteU64At(buf:block, offset:int, v:int, bigEndian:bool) - bool` | Writes 8-byte integer at `offset`. Optional `bigEndian` defaults to `false`. Returns `false` if out of bounds. |

- **FromString / ToString**
  - Both are non-destructive copies - unlike `ChangeToString`, neither modifies the source object.
  - `ToString` reads `count × size` bytes, so a buffer created with `Buffer.Create(n, 4)` will produce a string of `n × 4` bytes. For raw byte buffers intended for string conversion, use `size = 1`.
  - Primary use case is passing string data to FFI functions and reading string results back:
  
```js
    let buf    = Buffer.FromString(http_response.body);
    let result = my_ffi_fn(buf);
    Console.WriteLine(Buffer.ToString(result));
```

- **CopyBytesToArray / StringToArray**
  - These avoid the round-trip overhead of `ToArray` + a Flaris loop when writing into an existing byte array.
  - `StringToArray` reads directly from the string's internal bytes - no block is allocated.
  - Both are zero-copy for the destination: existing array slots (0–255 byte values) are updated in-place without heap allocation.
  
```js
    // Write UTF-8 bytes of a string into a pre-allocated byte array at offset 10
    let n:int = Buffer.StringToArray(s, my_arr, 10);

    // Copy raw bytes from a block into an array
    let blk:block = Buffer.FromString(s);
    Buffer.CopyBytesToArray(blk, my_arr, 0);
```

---

### Class

Namespace: **`Class`**

Type-level introspection, inheritance inspection, and dynamic reflection on user-defined classes.

| Function | Signature | Description |
| -------- | --------- | ----------- |
| **GetBase** | `GetBase(obj:instance\|class) - class` | Returns immediate base class of an instance or class. |
| **Instantiate** | `Instantiate(name:string) - instance` | Returns new instance of class named `name`. |
| **InstanceMethods** | `InstanceMethods(cls:class) - array` | Returns array of instance method names (not inherited). |
| **InstanceOf** | `InstanceOf(inst:instance, cls:class) - bool` | `true` if instance was created from `class` or any base class. |
| **Invoke** | `Invoke(target:instance\|class, name:string, ...args) - any` | Dynamically calls method `name` on instance or class. |
| **IsClass** | `IsClass(value:any) - bool` | `true` if value is a class object. |
| **IsInstance** | `IsInstance(value:any) - bool` | `true` if value is a class instance. |
| **Name** | `Name(cls:class) - string` | Returns class name as string. Zero-allocation (interned). |
| **Fields** | `Fields(inst:instance) - array` | Returns array of field names on an instance. |
| **StaticMethods** | `StaticMethods(cls:class) - array` | Returns array of static method names (not inherited). |
| **SubclassOf** | `SubclassOf(A:class, B:class) - bool` | `true` if A equals B or A inherits from B. |

---

### Collections

Namespace: **`Collections`**

Factory functions for Stack, Queue, HashMap, PriorityQueue, MaxPriorityQueue, and LinkedList. All factories return `object`.

#### Stack - `Collections.Stack() - object`

| Method | Description |
| -------- | ------------- |
| `s.Push(v)` | Push value to top. |
| `s.Pop()` | Remove and return top, or `nil` if empty. |
| `s.Peek()` | Return top without removing, or `nil` if empty. |
| `s.IsEmpty()` | `true` if stack has no elements. |
| `s.Size()` | Number of elements. |
| `s.Clear()` | Remove all elements. |
| `s.ToArray()` | Return current elements as array (bottom-to-top). |

#### Queue - `Collections.Queue() - object`

| Method | Description |
| -------- | ------------- |
| `q.Enqueue(v)` | Add value to back. |
| `q.Dequeue()` | Remove and return front, or `nil` if empty. |
| `q.Peek()` | Return front without removing, or `nil` if empty. |
| `q.IsEmpty()` | `true` if queue has no elements. |
| `q.Size()` | Number of elements. |
| `q.Clear()` | Remove all elements. |
| `q.ToArray()` | Return current elements as array (front-to-back). |

#### HashMap - `Collections.HashMap() - object`

| Method | Description |
| -------- | ------------- |
| `m.Set(key, value)` | Set key to value. |
| `m.Get(key)` | Return value for key, or `nil`. |
| `m.Has(key)` | `true` if key exists. |
| `m.Remove(key)` | Remove key; return `true` if existed. |
| `m.Clear()` | Remove all entries. |
| `m.Size()` | Number of entries. |
| `m.Keys()` | Return array of all keys. |
| `m.Values()` | Return array of all values. |

#### PriorityQueue - `Collections.PriorityQueue() - object`

Min-heap: `Dequeue()` always returns the smallest value.

| Method | Description |
| -------- | ------------- |
| `q.Enqueue(v, priority)` | Insert value with numeric priority. |
| `q.Dequeue()` | Remove and return the value with the lowest priority, or `nil`. |
| `q.Peek()` | Return the lowest-priority value without removing, or `nil`. |
| `q.IsEmpty()` | `true` if queue has no elements. |
| `q.Size()` | Number of elements. |
| `q.Clear()` | Remove all elements. |
| `q.ToArray()` | Return values as array ordered lowest-to-highest priority. |

#### MaxPriorityQueue - `Collections.MaxPriorityQueue() - object`

Max-heap: `Dequeue()` always returns the largest value. Same API as `PriorityQueue`.

#### LinkedList - `Collections.LinkedList() - object`

Singly-linked list with a cached tail pointer. `PushFront`, `PushBack`, `PopFront`, `PeekFront`, and `PeekBack` are O(1). `PopBack` and `Clear` are O(n).

| Method | Description |
| -------- | ------------- |
| `l.PushFront(v)` | Insert at head. Returns `self` for chaining. |
| `l.PushBack(v)` | Insert at tail. Returns `self` for chaining. |
| `l.PopFront()` | Remove and return head value, or `nil` if empty. |
| `l.PopBack()` | Remove and return tail value, or `nil` if empty. O(n). |
| `l.PeekFront()` | Return head value without removing, or `nil`. |
| `l.PeekBack()` | Return tail value without removing, or `nil`. |
| `l.IsEmpty()` | `true` if list has no elements. |
| `l.Size()` | Number of elements. |
| `l.Clear()` | Remove all elements. O(n). |
| `l.ToArray()` | Return elements as array (front-to-back). O(n). |

---

### Compress

Namespace: **`Compress`**

Raw zlib compression. Max input 4 GB.

| Function | Signature | Description |
| -------- | --------- | ----------- |
| **Zip** | `Zip(data:string\|block, level?:int) - block` | Compress data (raw zlib). Optional level 1–9 (default 6). Returns compressed block. |
| **Unzip** | `Unzip(data:block, maxSize?:int) - block` | Decompress data (raw zlib/gzip). Optional max output size hint. Returns decompressed block. |

---

### Archive

Namespace: **`Archive`**

ZIP archive creation and extraction. Uses a factory pattern: `Open`/`OpenFile` return reader objects; `Create` returns a writer object.

**Factory functions:**

| Function | Signature | Description |
| -------- | --------- | ----------- |
| **Open** | `Open(data:block) - reader` | Open a ZIP archive from in-memory bytes. Returns a reader object. |
| **OpenFile** | `OpenFile(path:string) - reader` | Open a ZIP archive from disk. Returns a reader object. **Requires unsafe mode.** |
| **Create** | `Create() - writer` | Create a new empty ZIP archive writer. |

**Reader methods** (returned by `Open` / `OpenFile`):

| Method | Signature | Description |
|--------|-----------|-------------|
| **List** | `r.List() - array` | List all entries. Each element is `{Name:string, Size:int, CompressedSize:int, IsDir:bool}`. |
| **Read** | `r.Read(name:string) - block\|nil` | Extract one file by name. Returns raw bytes block, or `nil` if not found. |
| **Extract** | `r.Extract(name:string, dest:string) - bool` | Extract one file to a path on disk. Creates intermediate directories. Returns `true` on success. **Requires unsafe mode.** |
| **ExtractAll** | `r.ExtractAll(dir:string) - int` | Extract all files into `dir`. Creates intermediate directories. Returns number of files extracted. **Requires unsafe mode.** |

**Writer methods** (returned by `Create`):

| Method | Signature | Description |
|--------|-----------|-------------|
| **Add** | `w.Add(name:string, data:string\|block) - nil` | Add a file entry from a string or block. |
| **AddFile** | `w.AddFile(name:string, path:string) - nil` | Add a file entry by reading from disk. **Requires unsafe mode.** |
| **Build** | `w.Build() - block` | Finalise the archive and return ZIP bytes as a block. |
| **Save** | `w.Save(path:string) - bool` | Finalise the archive and write it to disk. Returns `true` on success. **Requires unsafe mode.** |

**Example:**

```js
// Create
let w = Archive.Create();
w.Add("readme.txt", "Hello!");
w.Add("data/nums.txt", "1\n2\n3");
let bytes = w.Build();        // returns block
w.Save("/tmp/out.zip");       // or save directly to disk

// Read
let r = Archive.Open(bytes);
let entries = r.List();       // [{Name:"readme.txt", Size:6, ...}, ...]
let raw = r.Read("readme.txt"); // block -> str(raw) == "Hello!"
r.ExtractAll("/tmp/extracted");
```

---

### Console

Namespace: **`Console`**

Terminal I/O, color, and cursor control.

| Function | Signature | Description |
| -------- | --------- | ----------- |
| **Clear** | `Clear() - nil` | Clear the entire terminal screen and move cursor to home (1,1). |
| **ClearLine** | `ClearLine() - nil` | Erase the current line and move cursor to column 1. |
| **Error** | `Error(...args:any) - nil` | Print to stderr in red. |
| **GetCursor** | `GetCursor() - object` | Returns `{x, y}` cursor position object. |
| **IsTTY** | `IsTTY() - bool` | `true` if stdout is a real terminal (not piped). |
| **KeyPressed** | `KeyPressed() - bool` | `true` if a key is waiting in the input buffer (non-blocking). |
| **Ok** | `Ok(...args:any) - nil` | Print to stdout in green. |
| **ReadChar** | `ReadChar() - char` | Read a single character from stdin (blocking). |
| **ReadFloat** | `ReadFloat() - float` | Read a float from stdin. |
| **ReadInt** | `ReadInt() - int` | Read an integer from stdin. |
| **ReadKey** | `ReadKey() - object` | Blocking read of one keypress. Returns `{Key:string, Char:char, IsSpecial:bool}`. `Key` is `"Up"`, `"Down"`, `"Left"`, `"Right"`, `"Home"`, `"End"`, `"PageUp"`, `"PageDown"`, `"Insert"`, `"Delete"`, `"Enter"`, `"Escape"`, `"Backspace"`, `"Tab"`, `"F1"`–`"F12"`, or a single printable character. `IsSpecial` is `true` for arrows, function keys, and the navigation cluster. |
| **ReadLine** | `ReadLine() - string` | Read a line from stdin (blocking, strips newline). |
| **ReadPassword** | `ReadPassword() - string` | Read without echo. |
| **RestoreCursor** | `RestoreCursor() - nil` | Restore previously saved cursor position. |
| **SaveCursor** | `SaveCursor() - nil` | Save current cursor position. |
| **SetBackground** | `SetBackground(color:int) - nil` | Set background color. Use `ConsoleColor.*` constants. |
| **SetColor** | `SetColor(color:int) - nil` | Set foreground color. Use `ConsoleColor.*` constants. |
| **SetCursor** | `SetCursor(x:int, y:int) - nil` | Move cursor to column `x`, row `y`. |
| **SetRaw** | `SetRaw(enable:bool) - nil` | Enable or disable terminal raw mode. When `true`: disables line buffering and echo so individual keypresses are available immediately without pressing Enter. Restore with `SetRaw(false)` before exit. Has no effect if not running on a TTY. |
| **Size** | `Size() - object` | Returns `{width, height}` of terminal. |
| **TryReadChar** | `TryReadChar() - char` | Non-blocking: returns `char` if key pressed, else `\0`. |
| **Warn** | `Warn(...args:any) - nil` | Print to stderr in yellow. |
| **Write** | `Write(...args:any) - nil` | Print to stdout without trailing newline. |
| **WriteLines** | `WriteLines(...args:any) - nil` | Print multiple values each on its own line. |
| **WriteLine** | `WriteLine(...args:any) - nil` | Print to stdout with trailing newline. |

**ConsoleColor constants:** `Default`, `Red`, `Green`, `Blue`, `Yellow`, `Cyan`

---

### Convert

Namespace: **`Convert`**

Type conversion and parsing with full range of integer widths.

| Function | Signature | Description |
| -------- | --------- | ----------- |
| **ToBool** | `ToBool(v:int\|float\|bool\|char) - bool` | Convert numeric to bool. |
| **ToChar** | `ToChar(v:int\|float\|bool\|char) - char` | Convert numeric to char (Unicode codepoint). |
| **ToFloat** | `ToFloat(v:int\|float\|bool\|char) - float` | Convert numeric to float. |
| **ToInt** | `ToInt(v:int\|float\|bool\|char) - int` | Convert numeric to int. |
| **ToInt8** | `ToInt8(v:int\|float\|bool\|char) - int` | Truncate to signed 8-bit, stored as int. |
| **ToInt16** | `ToInt16(v:int\|float\|bool\|char) - int` | Truncate to signed 16-bit, stored as int. |
| **ToInt32** | `ToInt32(v:int\|float\|bool\|char) - int` | Truncate to signed 32-bit, stored as int. |
| **ToInt64** | `ToInt64(v:int\|float\|bool\|char) - int` | Truncate to signed 64-bit, stored as int. |
| **ToUInt8** | `ToUInt8(v:int\|float\|bool\|char) - int` | Truncate to unsigned 8-bit, stored as int. |
| **ToUInt16** | `ToUInt16(v:int\|float\|bool\|char) - int` | Truncate to unsigned 16-bit, stored as int. |
| **ToUInt32** | `ToUInt32(v:int\|float\|bool\|char) - int` | Truncate to unsigned 32-bit, stored as int. |
| **ToUInt64** | `ToUInt64(v:int\|float\|bool\|char) - int` | Truncate to unsigned 64-bit, stored as int. |
| **ToString** | `ToString(v:int\|float\|bool\|char\|string) - string` | Convert numeric to string representation. |
| **ParseBool** | `ParseBool(s:string) - bool` | Parse "true"/"false". Raises on invalid input. |
| **ParseChar** | `ParseChar(s:string) - char` | Parse single-character string to char. |
| **ParseFloat** | `ParseFloat(s:string) - float` | Parse decimal string to float. Raises on failure. |
| **ParseInt** | `ParseInt(s:string) - int` | Parse integer string (supports `0x`, `0b`, `0o`). Raises on failure. |
| **ParseUInt** | `ParseUInt(s:string) - int` | Parse unsigned integer string. Raises on failure. |
| **TryParseBool** | `TryParseBool(s:string) - bool` | Parse bool; returns `false` on failure instead of raising. |
| **TryParseChar** | `TryParseChar(s:string) - char` | Parse char; returns `\0` on failure. |
| **TryParseFloat** | `TryParseFloat(s:string) - float` | Parse float; returns `0.0` on failure. |
| **TryParseInt** | `TryParseInt(s:string) - int` | Parse int; returns `0` on failure. |
| **TryParseUInt** | `TryParseUInt(s:string) - int` | Parse unsigned int; returns `0` on failure. |

**Range constants:** `Int8Min/Max`, `Int16Min/Max`, `Int32Min/Max`, `Int64Min/Max`, `UInt8Max`, `UInt16Max`, `UInt32Max`, `UInt64Max`, `Float32Max`,`Float32Min`, `Float64Max`, `Float64Min`, `Float64Epsilon`, `Float32Epsilon`, `FloatEpsilon`

---

### Char Module

Namespace: **`Char`**

Character classification and conversion. All functions operate on the full 32-bit Unicode codepoint stored inside a `char` value. Classification functions are Unicode-aware - they recognise letters, digits, and whitespace across all major scripts, not just ASCII (see per-function notes below). 18 of the 20 functions are emitted as direct `JIT_CALL_BUILTIN_I` fast-calls inside `hot` functions.

#### Classification - `char - bool`

| Function | Signature | Unicode-aware | Description |
| ---------- | ----------- | :-----------: | ------------- |
| **IsAlpha** | `IsAlpha(c:char) - bool` | ✓ | Letter in any major script (Latin, Greek, Cyrillic, Arabic, CJK, Hangul, …). |
| **IsDigit** | `IsDigit(c:char) - bool` | ✓ | Decimal digit (Unicode Nd category): ASCII 0–9 and script-native digits (Arabic-Indic, Devanagari, Thai, …). |
| **IsAlNum** | `IsAlNum(c:char) - bool` | ✓ | Letter or decimal digit (Unicode-aware for both). |
| **IsUpper** | `IsUpper(c:char) - bool` | ✓ | Uppercase letter (Latin, Greek, Cyrillic, Armenian, Georgian, Fullwidth, …). |
| **IsLower** | `IsLower(c:char) - bool` | ✓ | Lowercase letter (same script coverage as `IsUpper`). |
| **IsCased** | `IsCased(c:char) - bool` | ✓ | Has an upper/lower case distinction - equivalent to `IsUpper(c) \|\| IsLower(c)`. |
| **IsSpace** | `IsSpace(c:char) - bool` | ✓ | Whitespace: ASCII space/tab/newlines plus Unicode spaces (En Space, Em Space, No-Break Space, …). |
| **IsNewline** | `IsNewline(c:char) - bool` | ✓ | Line-ending character: LF `\n`, CR `\r`, VT, FF, NEL U+0085, Line Separator U+2028, Paragraph Separator U+2029. |
| **IsAscii** | `IsAscii(c:char) - bool` | - | Codepoint is in the ASCII range (U+0000–U+007F). |
| **IsPunct** | `IsPunct(c:char) - bool` | - | ASCII punctuation only (`. , ! ? " ' ; : - ( ) [ ] { } …`). |
| **IsXDigit** | `IsXDigit(c:char) - bool` | - | Hexadecimal digit: 0–9, A–F, a–f (ASCII only - hex is an ASCII concept). |
| **IsPrint** | `IsPrint(c:char) - bool` | ~ | Printable: U+0020–U+007E plus codepoints above U+009F. |
| **IsControl** | `IsControl(c:char) - bool` | ~ | C0 and C1 control characters: codepoint < U+0020 or U+007F–U+009F. |

#### Conversion - `char - char`

| Function | Signature | Unicode-aware | Description |
| ---------- | ----------- | :-----------: | ------------- |
| **ToUpper** | `ToUpper(c:char) - char` | ✓ | Uppercase mapping; non-letters returned unchanged. |
| **ToLower** | `ToLower(c:char) - char` | ✓ | Lowercase mapping; non-letters returned unchanged. |
| **SwapCase** | `SwapCase(c:char) - char` | ✓ | Upper-lower, lower-upper, all other characters unchanged. |

#### Numeric and UTF-8 helpers - `char - int`

| Function | Signature | Description |
| ---------- | ----------- | ------------- |
| **DigitValue** | `DigitValue(c:char) - int` | Decimal value of a Unicode digit (0–9), or **-1** if the character is not a digit. Works for any Nd script block (Arabic-Indic `'٣'` - 3, Devanagari `'९'` - 9, …). |
| **Utf8Len** | `Utf8Len(c:char) - int` | Number of UTF-8 bytes needed to encode the codepoint: 1 (ASCII), 2 (U+0080–U+07FF), 3 (U+0800–U+FFFF), 4 (U+10000+). |

#### Codepoint accessors

| Function | Signature | Description |
| ---------- | ----------- | ------------- |
| **Code** | `Code(c:char) - int` | Raw Unicode codepoint as an integer (`'A'` - 65, `'中'` - 0x4E2D). |
| **FromInt** | `FromInt(n:int) - char` | Wrap an integer codepoint as a `char`. No range check performed. |

```flaris
// Unicode-aware classification
Char.IsAlpha('ä')   // true  (Latin Extended)
Char.IsAlpha('中')  // true  (CJK)
Char.IsDigit('٣')  // true  (Arabic-Indic digit 3)
Char.IsUpper('Α')  // true  (Greek capital Alpha)

// Case conversion
Char.ToUpper('ä')   // 'Ä'
Char.ToLower('Α')   // 'α'
Char.SwapCase('A')  // 'a'

// Numeric helpers
Char.DigitValue('٣')           // 3
Char.DigitValue('a')           // -1
Char.Utf8Len('a')              // 1
Char.Utf8Len('ä')              // 2
Char.Utf8Len('中')             // 3
Char.Utf8Len(Char.FromInt(0x1F600))  // 4

// JIT fast-calls in hot functions
fn hot count_letters(s: string, n: int): int {
    let count: int = 0;
    for (let i: int = 0; i < n; i++) {
        if (Char.IsAlpha(s[i])) { count++; }
    }
    return count;
}
```

#### Char instance method syntax

All `Char` functions can also be called directly on a `char` value:

```flaris
let c = String.CharAt("hello", 0);   // 'h'
c.IsAlpha()    // true   - same as Char.IsAlpha(c)
c.Code()       // 104    - same as Char.Code(c)
c.ToUpper()    // 'H'    - same as Char.ToUpper(c)
```

Both forms compile to identical bytecode when the variable is typed (`: char` or inferred from a builtin return). Untyped variables fall back to a runtime dispatch.

---

### Crypto

Namespace: **`Crypto`**

Authenticated encryption (XChaCha20-Poly1305), Ed25519 signatures, X25519 key exchange, Argon2id password hashing, HMAC, and a CSPRNG. Public keys and signatures are hex strings.

| Function | Signature | Description |
| -------- | --------- | ----------- |
| **Argon2** | `Argon2(password:string\|block, salt:string\|block, iterations?:int, memKiB?:int) - string` | Argon2id password hash (64-hex). `salt` >= 8 bytes (16 recommended); `iterations` default 3; `memKiB` memory cost in KiB, default 65536 (64 MiB), capped at 1 GiB. |
| **Decrypt** | `Decrypt(data:string\|block, keyMode:int, keyOrNonce:string\|block, ciphertext:string\|block, tag:string) - object` | Decrypt ciphertext. Returns `{ok:bool, data:block}`. |
| **Ed25519KeyPair** | `Ed25519KeyPair() - object` | Generate an Ed25519 signing key pair. Returns `{PublicKey:string(64 hex), SecretKey:string(128 hex)}`. |
| **Ed25519Sign** | `Ed25519Sign(secretKey:string, message:string\|block) - string` | Sign `message` with a hex secret key; returns a 128-hex signature. |
| **Ed25519Verify** | `Ed25519Verify(signature:string, publicKey:string, message:string\|block) - bool` | Verify a hex signature against a hex public key and message. |
| **Encrypt** | `Encrypt(data:string\|block, keyMode:int, key:string\|block) - object` | Encrypt data. Returns `{ok:bool, nonce:block, ciphertext:block, tag:string}`. 32-byte key, 24-byte nonce. Max 256 MB. |
| **HmacSha1** | `HmacSha1(key:string\|block, data:string\|block) - string` | Compute HMAC-SHA1 (40-hex). Legacy/interop: TOTP/HOTP 2FA, OAuth1, AWS SigV2. |
| **HmacSha256** | `HmacSha256(key:string\|block, data:string\|block) - string` | Compute HMAC-SHA256 (64-hex). |
| **HmacSha512** | `HmacSha512(key:string\|block, data:string\|block) - string` | Compute HMAC-SHA512 (128-hex). E.g. JWT HS512. |
| **RandomBytes** | `RandomBytes(count:int) - block` | Generate `count` cryptographically random bytes (CSPRNG). |
| **X25519KeyPair** | `X25519KeyPair() - object` | Generate an X25519 key-exchange key pair. Returns `{PublicKey:string(64 hex), SecretKey:string(64 hex)}`. |
| **X25519Shared** | `X25519Shared(secretKey:string, peerPublicKey:string) - string` | X25519 ECDH; returns the 64-hex raw shared secret. Hash it (e.g. `Hash.Blake2b`) before using it as a key. |

---

### Debug

Namespace: **`Debug`**

Runtime introspection and assertion tools. Available in all builds; overhead is negligible for non-tracing calls.

| Function | Signature | Description |
| ---------- | ----------- | ------------- |
| **Assert** | `Assert(left:any, right:any, ?message:any)` | Raises `RuntimeError` if `left` != `right`. Optionally prints `message` |
| **AssertTrue** | `AssertTrye(condition:any,?message:any)` | Raises `RuntimeError` if `condition` is falsy. Optionally prints `message` |
| **GuardAddress** | `GuardAddress(addr:int)` | Installs memory watchpoint; raises `InvalidMemoryAccess` on access to `addr`. |
| **Here** | `Here()` | Prints current file, line, and function name to stdout. |
| **Pool** | `Pool()` | Prints current memory pool statistics. |
| **Refs** | `Refs(value:any)` | Prints current reference count for a value. |
| **Stack** | `Stack()` | Prints the current call stack. |
| **StackPtr** | `StackPtr()` | Prints internal VM stack pointer value. |
| **Value** | `Value(v:any)` | Prints detailed internal representation of a value. |

---

### Directory

Namespace: **`Directory`**

Filesystem directory operations.

| Function | Signature | Description |
| -------- | --------- | ----------- |
| **Create** | `Create(path:string) - bool` | Create directory at `path`. Returns `false` if already exists. |
| **Delete** | `Delete(path:string) - bool` | Delete directory (must be empty). |
| **Ensure** | `Ensure(path:string) - bool` | Create directory (and parents) if not exists; no-op if exists. |
| **Exists** | `Exists(path:string) - bool` | `true` if path exists and is a directory. |
| **GetModifiedTime** | `GetModifiedTime(path:string) - int` | Returns Unix timestamp of last modification. |
| **List** | `List(path:string) - array` | Returns array of all entry names (files + dirs) in `path`. |
| **ListDirs** | `ListDirs(path:string) - array` | Returns array of subdirectory names only. |
| **ListFiles** | `ListFiles(path:string) - array` | Returns array of file names only. |

---

### Event

Namespace: **`Event`**

One-shot fiber synchronization primitives. Max 64 events.

| Function | Signature | Description |
| -------- | --------- | ----------- |
| **Create** | `Create() - int` | Allocates a new event handle (integer ID). |
| **Set** | `Set(id:int, value?:any)` | Signal the event; optionally attach a value. Wakes waiting fiber. |
| **Wait** | `Wait(id:int) - any` | Suspends current fiber until `Set` is called; returns the set value. |

---

### FFI

Namespace: **`Ffi`**

Dynamic loading of native shared libraries (`.so`, `.dylib`). Requires `-u` flag. FFI calls cross a clean isolation boundary - arguments and return values are marshalled through a structured type system; no VM internals are exposed to native code.

| Function | Signature | Description |
| ---------- | ----------- | ------------- |
| **Load** | `Load(path:string, sha256:string\|nil) - pointer` | Load shared library at `path`. When `sha256` is a 64-character hex string, the SHA-256 fingerprint of the library is verified before loading - pass `nil` to skip the check. SHA verification is optional security hardening; libraries distributed without a known hash should pass `nil`. Returns a handle. Same library is never loaded twice - returns cached handle on repeated calls. |
| **Unload** | `Unload(handle:pointer) - bool` | Unload a previously loaded library and release all associated callbacks. |
| **GetFunction** | `GetFunction(handle:pointer, name:string, sig?:string) - ffi_function` | Retrieve a named symbol from a loaded library as a callable value. When `sig` is provided, argument types and return type are validated on every call. Omitting `sig` disables validation (all types accepted). |
| **HasFunction** | `HasFunction(handle:pointer, name:string) - bool` | Returns `true` if the named symbol exists in the library. Does not allocate. Use to guard optional symbols. |
| **SetCallback** | `SetCallback(handle:pointer, name:string, fn:function) - bool` | Register a Flaris function as a named callback. The library must export `flaris_set_callback`. Callbacks are global - use a lib-specific prefix to avoid name collisions across plugins (e.g. `"mylib_ondata"`). |
| **RemoveCallback** | `RemoveCallback(handle:pointer, name:string) - bool` | Unregister a previously registered callback by name without unloading the library. Useful for one-shot callbacks. |
| **GetVersion** | `GetVersion(handle:pointer) - int` | Returns the plugin's version as a `uint32` in `0xMMmmppbb` format. Plugin must export `uint32_t flaris_version(void)`. Returns `0` if symbol not found. |
| **Call** | `Call(handle:pointer, name:string, ...args) - any` | Resolve and call a named symbol in one step without allocating an `ffi_function` object. Accepts up to 4 extra arguments. Calls `dlsym` on every invocation - use `GetFunction` for hot paths. |

**`Ffi.Load` path resolution.** The name resolves in order: (1) the path as
given if it exists; (2) the per-user FFI dir - `~/.flaris/ffi` (POSIX) /
`%USERPROFILE%\.flaris\ffi` (Windows), or `$FLARIS_FFI` - where a **bare name**
gets the platform extension appended (`Ffi.Load("myplugin")` →
`~/.flaris/ffi/myplugin.{dylib,so,dll}`); (3) `<name><ext>` in the working
directory; (4) the system loader (so `"libsqlite3.so"` still works). Prefer a
bare name and install the library to `~/.flaris/ffi` for portable, path-free
loading. See [ffi.md](ffi.md).

**FFI Type Mapping**

| Flaris type | C type | Notes |
| --- | --- | --- |
| `int` | `int64_t` | |
| `float` | `double` | |
| `bool` | `int` (0/1) | |
| `string` | `char*` | Copied across boundary in both directions. VM frees strings returned from C. |
| `block` | `void*` | Zero-copy pointer into VM memory. C must never free or reallocate. |
| `pointer` | `void*` | |
| `array` | `FfiObject**` | Deep-copied. Avoid in hot paths. |
| `object` | `FfiKV*` | Deep-copied. Keys are duplicated. Avoid in hot paths. |

Prefer `int` and `float` for high-frequency callback arguments. Use `block` for bulk data. `string` is acceptable for low-frequency events (errors, lifecycle). `array` and `object` should be avoided in callbacks called at high rate.

**Function Signature Format**

Signatures passed to `GetFunction` use the syntax:

```js
fn(arg_type, arg_type, ...):return_type
```

Types may be unioned with `|`. Zero-argument functions use `fn():return_type`.

| Type name | Matches |
| --- | --- |
| `any` | Any value |
| `nil` | Nil |
| `bool` | Boolean |
| `int` | Integer (`u8`, `u16`, `u32`, `u64`, `i8`, `i16`, `i32`, `i64` are aliases) |
| `float` | Float |
| `char` | Character |
| `string` | String |
| `array` | Array |
| `object` | Object |
| `block` | Block (raw memory buffer) |
| `pointer` / `ptr` | Pointer |
| `function` / `fn` | Flaris function |
| `ffi` | FFI function |
| `number` / `numeric` | `int\|float` |
| `callable` | `fn\|builtin\|ffi` |

Examples:

```js
let fn = Ffi.GetFunction(lib, "add", "fn(int, int):int");
let fn = Ffi.GetFunction(lib, "process", "fn(string, pointer):bool");
let fn = Ffi.GetFunction(lib, "compute", "fn(number):float");
let fn = Ffi.GetFunction(lib, "reset", "fn():nil");
let fn = Ffi.GetFunction(lib, "anything", "fn(any):any");
```

For a complete plugin development guide including helpers, examples, and ownership rules, see `ffi.md`.

---

### Fiber

Namespace: **`Fiber`**

Cooperative green-thread creation, communication, and lifecycle management.

| Function | Signature | Description |
| -------- | --------- | ----------- |
| **Cancel** | `Cancel(f:fiber) - bool` | Request cancellation of a running fiber. |
| **FromId** | `FromId(id:int) - fiber` | Returns fiber handle from its integer ID. |
| **GetMessage** | `GetMessage(f:fiber) - any` | Retrieve the most recent message sent to `f`. |
| **HasMessage** | `HasMessage(f:fiber) - bool` | `true` if fiber has an unread message. |
| **Id** | `Id() - int` | Returns current fiber's integer ID. |
| **IsDone** | `IsDone(f:fiber) - bool` | `true` if fiber has completed or been cancelled. |
| **New** | `New(func:function ) - fiber` | Create a new fiber (not yet started). |
| **Resume** | `Resume(f:fiber\|nil, value?:any) - any` | Resume a suspended fiber; optionally send a value in. Returns yielded/returned value. |
| **Run** | `Run(func:function ) - int` | Create and immediately start a fiber. Returns its integer ID. |
| **SendMessage** | `SendMessage(f:fiber, msg:any) - nil` | Send message to fiber (non-blocking). |
| **SetQuantum** | `SetQuantum(f:fiber, quantum:int) - bool` | Override the scheduling quantum for `f` - the number of checkpoints (loop back-edges and call/return boundaries) before yielding to the scheduler. |
| **Sleep** | `Sleep(ms:int) - nil` | Suspend current fiber for at least `ms` milliseconds. |
| **Status** | `Status(f:fiber) - int` | Returns status code. Constants: `Fiber.Status.New=0`, `Running=1`, `Suspended=2`, `Finished=3`, `WaitIO=4`. |

#### Fiber instance method syntax

All `Fiber` functions where the fiber is the first argument can be called directly on a `fiber` value:

```flaris
let f = Fiber.New(fn() { /* ... */ });
f.Resume()            // same as Fiber.Resume(f)
f.IsDone()            // same as Fiber.IsDone(f)
f.Status()            // same as Fiber.Status(f)
f.Cancel()            // same as Fiber.Cancel(f)
f.HasMessage()        // same as Fiber.HasMessage(f)
f.GetMessage()        // same as Fiber.GetMessage(f)
f.SendMessage("hi")   // same as Fiber.SendMessage(f, "hi")
```

Factory functions (`New`, `Run`, `Id`, `Sleep`) are not available as instance methods. Both forms compile to identical bytecode when the variable is typed (`: fiber` or inferred from a builtin return). Untyped variables fall back to a runtime dispatch.

---

### File

Namespace: **`File`**

File read, write, and metadata operations.

| Function | Signature | Description |
| -------- | --------- | ----------- |
| **AppendLines** | `AppendLines(path:string, lines:array) - bool` | Append array of strings as lines to file. |
| **AppendText** | `AppendText(path:string, text:string) - bool` | Append text string to file. |
| **Copy** | `Copy(src:string, dst:string) - bool` | Copy file from `src` to `dst`. |
| **Delete** | `Delete(path:string) - bool` | Delete file at `path`. |
| **Exists** | `Exists(path:string) - bool` | `true` if path exists and is a file. |
| **GetModifiedTime** | `GetModifiedTime(path:string) - int` | Returns Unix timestamp of last modification. |
| **Move** | `Move(src:string, dst:string) - bool` | Move/rename file. |
| **ReadAllBytes** | `ReadAllBytes(path:string) - block` | Read entire file as binary block. |
| **ReadAllBytesAsync** | `ReadAllBytesAsync(path:string) - fiber` | Non-blocking `ReadAllBytes`. Use with `await`. Other fibers run while the file is read. |
| **ReadLines** | `ReadLines(path:string) - array` | Read file into array of strings (one per line). |
| **ReadText** | `ReadText(path:string) - string` | Read entire file as UTF-8 string. |
| **ReadTextAsync** | `ReadTextAsync(path:string) - fiber` | Non-blocking `ReadText`. Use with `await`. Other fibers run while the file is read. |
| **Sha1** | `Sha1(data:string\|block) - string` | SHA-1 hash (40-char lowercase hex string). |
| **Sha256** | `Sha256(path:string, raw?:bool) - string` | Compute SHA-256 of file contents. Optional `raw` for binary output. |
| **Size** | `Size(path:string) - int` | File size in bytes. |
| **Touch** | `Touch(path:string) - bool` | Create file if not exists; update modification time if it does. |
| **WriteAllBytes** | `WriteAllBytes(path:string, data:string\|block) - bool` | Write binary data to file (overwrite). |
| **WriteLines** | `WriteLines(path:string, lines:array) - bool` | Write array of strings as lines (overwrite). |
| **WriteText** | `WriteText(path:string, text:string) - bool` | Write text string to file (overwrite). |
| **WriteTextAsync** | `WriteTextAsync(path:string, text:string) - fiber` | Non-blocking `WriteText`. Use with `await`. Other fibers run while the file is written. |

---

### FileWatch

Namespace: **`FileWatch`**

Single-file filesystem event monitoring. Up to 64 concurrent watchers. Uses `inotify` on Linux and `kqueue` on macOS/BSD. The callback fires on every scheduler tick where an event is pending - no fiber is blocked.

| Function | Signature | Description |
| -------- | --------- | ----------- |
| **Open** | `Open(path:string, callback:function) - int` | Watch `path` for changes using the default mode (`"write"`). Returns a slot id (≥ 0) or `-1` on failure. |
| **Open** | `Open(path:string, mode:string, callback:function) - int` | Watch `path` with explicit mode. Returns `-1` if the mode is unsupported on the current platform. |
| **Close** | `Close(id:int) - bool` | Stop watching and release the slot. |

**Mode tokens** - comma-separated, any combination:

| Token | Description | Linux | macOS |
|-------|-------------|-------|-------|
| `"write"` | File content modified or saved | ✓ | ✓ |
| `"delete"` | File deleted or renamed away | ✓ | ✓ |
| `"attrib"` | Metadata changed (chmod, chown, timestamps) | ✓ | ✓ |
| `"open"` | File opened for reading or writing | ✓ | - |
| `"all"` | All of the above | ✓ | ✓ (except open) |

Default mode (no mode argument) is `"write"`.

**Callback signature:** `fn(event:object, id:int)`

| Field | Type | Description |
|-------|------|-------------|
| `event.event` | string | `"modify"`, `"delete"`, `"attrib"`, `"open"` |
| `event.name` | string | The watched path as passed to `Open` |

**Notes:**

- The file must exist when `Open` is called. Watching a non-existent path returns `-1`.
- On macOS, editors like `nano` and `vim` save via rename - the file's inode is replaced. After receiving a `"delete"` event, close and re-open the watch to re-attach to the new inode.
- `"open"` mode is Linux-only. Passing it on macOS returns `-1`.

**Example:**

```js
var id = FileWatch.Open("./config.json", fn(ev, id) {
    Console.WriteLine(ev.event + " " + ev.name)
})

Fiber.Sleep(30_000)
FileWatch.Close(id)
```

---

### Gfx

Namespace: **`Gfx`**

Software rasterizer. Colors are `0xAARRGGBB` integers. Max canvas 32768×32768.

| Function | Signature | Description |
| -------- | --------- | ----------- |
| **Create** | `Create(width:int, height:int) - instance` | Create blank canvas. |
| **CreateFromFile** | `CreateFromFile(path:string) - instance` | Load PNG from disk into canvas. |

**Static color helpers:**

| Function | Signature | Description |
| -------- | --------- | ----------- |
| **RGB** | `RGB(r:int, g:int, b:int) - int` | Pack RGB channels into a fully-opaque `0xFFRRGGBB` color. |
| **RGBA** | `RGBA(r:int, g:int, b:int, a:int) - int` | Pack RGBA channels into `0xAARRGGBB`. |
| **HSL** | `HSL(h:float, s:float, l:float) - int` | Convert HSL to `0xFFRRGGBB`. `h` is 0–360, `s` and `l` are 0.0–1.0. |

**`IGfx` instance fields:** `Width:int`, `Height:int`, `Pixels:array`

**`IGfx` instance methods:**

| Method | Signature | Description |
| -------- | ----------- | ------------- |
| Clear | `Clear(color:int)` | Fill entire canvas with `color`. |
| SetPixel | `SetPixel(x, y, color)` | Set single pixel. |
| GetPixel | `GetPixel(x, y) - int` | Return pixel color. |
| DrawLine | `DrawLine(x1, y1, x2, y2, color)` | Bresenham line. |
| DrawHLine | `DrawHLine(x, y, len, color)` | Horizontal line. |
| DrawVLine | `DrawVLine(x, y, len, color)` | Vertical line. |
| DrawRect | `DrawRect(x, y, w, h, color)` | Rectangle outline. |
| FillRect | `FillRect(x, y, w, h, color)` | Filled rectangle. |
| DrawCircle | `DrawCircle(cx, cy, r, color)` | Circle outline (Midpoint algorithm). |
| FillPolygon | `FillPolygon(points, color)` | Fill polygon from array of `{x,y}` objects. |
| StrokePath | `StrokePath(points, color)` | Stroke open polyline. |
| DrawChar | `DrawChar(x, y, ch, color, scale?)` | Render a single character. |
| DrawText | `DrawText(x, y, text, color, scale?)` | Render a string. |
| Blit | `Blit(x, y, w, h, pixels)` | Copy a flat ARGB array (`QRCode.ToPixels`, custom bitmaps) onto the canvas at `(x, y)`. `pixels` must have at least `w × h` elements. |
| DrawImage | `DrawImage(x, y, src)` | Blit another `IGfx` canvas onto this one. |
| SaveToFile | `SaveToFile(path) - bool` | Save canvas as PNG. |
| SaveToBlock | `SaveToBlock() - block` | Return canvas as PNG bytes. |
| Draw | `Draw(fn)` | Batch-draw callback: `fn(canvas)`. |
| CopyRect | `CopyRect(x, y, w, h) - instance` | Copy sub-rectangle into new canvas. |
| Resize | `Resize(w, h) - instance` | Resize (nearest-neighbor) into new canvas. |
| Rescale | `Rescale(factor) - instance` | Scale by factor into new canvas. |
| Filter | `Filter(fn, x, y, w, h)` | Apply per-pixel callback `fn(pixel:int) - int` over region. Return `nil` to leave pixel unchanged. Mutates in place. |
| Grayscale  | `Grayscale(x, y, w, h)`                          | Convert region to grayscale using BT.601 luminance weights. Alpha channel preserved. Mutates in place. |
| Brightness | `Brightness(x, y, w, h, factor:float)`            | Multiply RGB channels by `factor` (1.0 = no change, 0.5 = half, 2.0 = double). Values clamped to 0–255. Alpha preserved. Mutates in place. |
| BlendRect  | `BlendRect(src:instance, dx:int, dy:int, alpha?:int)` | Composite `src` canvas onto this canvas at `(dx, dy)` using source-over alpha blending. Optional `alpha` (0–255) scales the overall opacity of `src`; defaults to 255. Mutates in place. |

#### PNG Compatibility

**Reading** (`CreateFromFile`, `DrawImage`) - decoded to RGBA8:

- Color types: grayscale, RGB, palette (indexed), grayscale+alpha, and RGBA
- Bit depths: 1/2/4/8/16 as the color type allows (16-bit is reduced to 8-bit; sub-byte depths are unpacked)
- Palette `tRNS` (per-index transparency) is applied; palette images require a `PLTE` chunk
- All five filter types supported (None, Sub, Up, Average, Paeth)
- Not supported: interlaced (Adam7) files, and color-key `tRNS` for grayscale/RGB. These throw `EXCEPTION_IO_ERROR`

**Writing** (`SaveToFile`, `SaveToBlock`):

- Always writes RGBA 32-bit, 8-bit per channel
- Filter: None (fast, slightly larger files than adaptive filtering)
- Compression: zlib level 6

---

### Hash

Namespace: **`Hash`**

Non-cryptographic and cryptographic hashes. Input accepts `string` or `block`. Max input 1 GB.

| Function | Signature | Description |
| ---------- | ----------- |------------- |
| **Adler32** | `Adler32(data:string\|block) - int` | Adler-32 checksum. |
| **Blake2b** | `Blake2b(data:string\|block, outLen?:int) - string` | BLAKE2b hash (hex string). `outLen` 1-64 bytes, default 32. |
| **Blake2s** | `Blake2s(data:string\|block) - string` | BLAKE2s hash (hex string). |
| **Crc32** | `Crc32(data:string\|block) - int` | CRC-32 checksum. |
| **Crc32c** | `Crc32c(data:string\|block) - int` | CRC-32C (Castagnoli) checksum. |
| **Md5** | `Md5(data:string\|block) - string` | MD5 (hex string). Legacy/interop checksums only (Content-MD5, ETags); **not** for security. |
| **Sha1** | `Sha1(data:string\|block) - string` | SHA-1 hash (hex string). Legacy; prefer SHA-256/BLAKE2 for security. |
| **Sha256** | `Sha256(data:string\|block) - string` | SHA-256 hash (hex string). |
| **Sha512** | `Sha512(data:string\|block) - string` | SHA-512 hash (hex string). |
| **SipHash24** | `SipHash24(data:string\|block, key:string) - int` | SipHash-2-4 with 16-byte key. |
| **Xxhash32** | `Xxhash32(data:string\|block, seed?:int) - int` | xxHash-32. Optional seed. |
| **Xxhash64** | `Xxhash64(data:string\|block, seed?:int) - int` | xxHash-64. Optional seed. |

---

### HttpUtil

Namespace: **`HttpUtil`**

Small HTTP-utility functions, plus minimal HTTP server. For HTTP-functionality (GET, PUT etc) use the Http-library from Http.fls (shipped as standard-library with source)

```js
import { Http, Status, ContentType } from library("Http", "1.0");
```

| Function | Signature | Description |
| ---------- | ----------- | ------------- |
| **EncodeQuery** | `EncodeQuery(params:object) - string` | Encode object as URL query string. |
| **UrlEncode** | `UrlEncode(s:string) - string` | Percent-encode a string. |
| **UrlDecode** | `UrlDecode(s:string) - string` | Decode percent-encoded string. |
| **ParseQuery** | `ParseQuery(s:string) - object` | Parse query string into object. |

**HTTP Server (requires `-u`):**

| Function | Signature | Description |
| ---------- | ----------- | ------------- |
| **StartServer** | `StartServer(port:int, handler:function) - pointer` | Start server; `handler(request)-response`. Returns server handle. |
| **HandleRequests** | `HandleRequests(handle:pointer) - bool` | Process one pending request. Call in a loop. |
| **WaitReadable** | `WaitReadable(handle:pointer, timeoutMs?:int) - bool` | Park the calling fiber until the listen socket has a pending connection. Resumes with `true` on readiness, `nil` on timeout. `timeoutMs <= 0` (default `-1`) waits without a deadline. Callable from a plain function — no `await` needed. |
| **StopServer** | `StopServer(handle:pointer) - bool` | Shutdown server. |
| **RouteMatch** | `RouteMatch(req:object, method:string, pattern:string) - bool` | Match `req` (using its `method` and `path`) against the given method + URL pattern. On match, captured path parameters are written into `req.params`. |

**Response object** (returned by the handler): `{ status:int, body, headers:object }`.

- All entries in `headers` are emitted (e.g. `Access-Control-Allow-Origin`, `Location`, `Set-Cookie`, `Cache-Control`). Header names and values are validated to block CRLF injection; `Content-Type`, `Content-Length` and `Connection` are managed by the server and skipped if present.
- `body` may be a **string** or a **block** (bytes). When a block is used, `Content-Length` is the exact byte count, so binary payloads with embedded NUL bytes (e.g. `File.ReadAllBytes` for static assets) are sent intact.

**Route patterns:** `:name` captures a single segment into `params.name`. A trailing `*` is a catch-all matching the remaining path; the matched tail is captured into `params["*"]` (used by `Router.Static`).

### Json

Namespace: **`Json`**

JSON parsing, serialization, path-based query and mutation, and NDJSON.

| Function | Signature | Description |
| ---------- | ----------- |------------- |
| **Exists** | `Exists(path:string, value:any) - bool` | `true` if path resolves to a node within `value` (a parsed object/array). |
| **FastSelect** | `FastSelect(path:string, json:string) - any` | Fast path extraction from raw JSON text (no full parse). Dot/bracket notation; `nil` if absent. |
| **Find** | `Find(path:string, value:any) - any` | Navigate a parsed value by path. Supports wildcards (`*`, `[*]`). Returns the matched node or `nil`. |
| **IsValid** | `IsValid(json:string) - bool` | `true` if the string is exactly one well-formed JSON document. Disambiguates the case `Parse` cannot (it returns `nil` for both invalid input and a literal `null`). |
| **Parse** | `Parse(json:string) - any` | Parse JSON string into a Flaris value (object/array/string/int/float/bool/nil). `nil` on malformed input. Max depth 256. |
| **ParseLines** | `ParseLines(text:string) - array` | Parse NDJSON / JSON Lines: one JSON value per line. Blank lines skipped; `nil` if any non-empty line is invalid. |
| **Remove** | `Remove(root:object\|array, path:string) - bool` | Remove the value at `path` (object key or array index, in place). `false` on a missing node or type mismatch. |
| **Set** | `Set(root:object\|array, path:string, value:any) - bool` | Set `value` at `path` in place, creating missing intermediate objects/arrays. `false` on a wrong-type intermediate (never clobbered) or an out-of-range array index (only append-at-length allowed). |
| **Stringify** | `Stringify(value:any, pretty?:int) - string` | Serialize to JSON. Truthy `pretty` enables 2-space indenting. Non-finite floats (NaN/Inf) serialize as `null`. |
| **StringifyLines** | `StringifyLines(array:array) - string` | Serialize each element as a compact JSON value on its own line (NDJSON); inverse of `ParseLines`. |

---

### Math

Namespace: **`Math`**

Full mathematical library. `number` accepts `int` or `float`.

| Function | Signature | Description |
| ---------- | ----------- | ------------- |
| **Abs** | `Abs(x:int\|float) - int` | Absolute value as integer. |
| **AbsF** | `AbsF(x:int\|float) - float` | Absolute value as float. |
| **Acos** | `Acos(x:int\|float) - float` | Arc cosine (radians). |
| **Acosh** | `Acosh(x:int\|float) - float` | Inverse hyperbolic cosine. |
| **Acot** | `Acot(x:int\|float) - float` | Arc cotangent. |
| **Acoth** | `Acoth(x:int\|float) - float` | Inverse hyperbolic cotangent. |
| **Asin** | `Asin(x:int\|float) - float` | Arc sine (radians). |
| **Asinh** | `Asinh(x:int\|float) - float` | Inverse hyperbolic sine. |
| **Atan** | `Atan(x:int\|float) - float` | Arc tangent (radians). |
| **Atan2** | `Atan2(y:int\|float, x:int\|float) - float` | Two-argument arc tangent. |
| **Atanh** | `Atanh(x:int\|float) - float` | Inverse hyperbolic tangent. |
| **Cbrt** | `Cbrt(x:int\|float) - float` | Cube root. |
| **Ceil** | `Ceil(x:int\|float) - int` | Ceiling (round up). |
| **Clamp** | `Clamp(x:int\|float, min:int\|float, max:int\|float) - float` | Clamp x to [min, max]. |
| **CopySign** | `CopySign(x:int\|float, y:int\|float) - float` | Returns magnitude of x with sign of y. |
| **Cos** | `Cos(x:int\|float) - float` | Cosine. |
| **Cosh** | `Cosh(x:int\|float) - float` | Hyperbolic cosine. |
| **Cot** | `Cot(x:int\|float) - float` | Cotangent. |
| **Csc** | `Csc(x:int\|float) - float` | Cosecant. |
| **Csch** | `Csch(x:int\|float) - float` | Hyperbolic cosecant. |
| **Deg** | `Deg(x:int\|float) - float` | Convert radians to degrees. |
| **Exp** | `Exp(x:int\|float) - float` | e^x. |
| **Floor** | `Floor(x:int\|float) - int` | Floor (round down). |
| **Gcd** | `Gcd(a:int\|float, b:int\|float) - int` | Greatest common divisor (>= 0; signs ignored). Gcd(0,0)=0. |
| **Hypot** | `Hypot(x:int\|float, y:int\|float) - float` | Euclidean distance sqrt(x²+y²). |
| **IsEven** | `IsEven(x:int\|float) - bool` | `true` if x is even. |
| **IsFinite** | `IsFinite(x:int\|float) - bool` | `true` if x is finite (not NaN or Inf). Note: return type is float in registry; treat as bool. |
| **IsInteger** | `IsInteger(x:int\|float) - bool` | `true` if x has no fractional part. Note: return type is float in registry; treat as bool. |
| **IsNaN** | `IsNaN(x:int\|float) - bool` | `true` if x is NaN. |
| **IsNegative** | `IsNegative(x:int\|float) - bool` | `true` if x < 0. |
| **IsOdd** | `IsOdd(x:int\|float) - bool` | `true` if x is odd. |
| **IsPositive** | `IsPositive(x:int\|float) - bool` | `true` if x > 0. |
| **Lcm** | `Lcm(a:int\|float, b:int\|float) - int` | Least common multiple (>= 0). Lcm(x,0)=0. May overflow int64 for large inputs. |
| **Log** | `Log(x:int\|float) - float` | Natural logarithm. |
| **Log10** | `Log10(x:int\|float) - float` | Base-10 logarithm. |
| **LogBase** | `LogBase(x:int\|float, base:int\|float) - float` | Logarithm with arbitrary base. |
| **Max** | `Max(a:int\|float, b:int\|float) - int` | Larger of a, b as integer. |
| **MaxF** | `MaxF(a:int\|float, b:int\|float) - float` | Larger of a, b as float. |
| **MaxOf** | `MaxOf(arr:array) - int\|float` | Largest element of a numeric array (type preserved). `nil` if empty or non-numeric. |
| **Mean** | `Mean(arr:array) - float` | Arithmetic mean of array elements. |
| **Median** | `Median(arr:array) - float` | Median of array elements. |
| **Min** | `Min(a:int\|float, b:int\|float) - int` | Smaller of a, b as integer. |
| **MinF** | `MinF(a:int\|float, b:int\|float) - float` | Smaller of a, b as float. |
| **MinOf** | `MinOf(arr:array) - int\|float` | Smallest element of a numeric array (type preserved). `nil` if empty or non-numeric. |
| **Mod** | `Mod(a:int\|float, b:int\|float) - int\|float` | Euclidean/floored modulo: result takes sign of b, so Mod(-1,5)=4. `nil` if b=0. |
| **Mode** | `Mode(arr:array) - float` | Mode of array elements. |
| **Pow** | `Pow(base:int\|float, exp:int\|float) - float` | base^exp. |
| **Product** | `Product(arr:array) - int\|float` | Product of array elements (int if all-int, else float). `1` if empty; `nil` if non-numeric. |
| **Rad** | `Rad(x:int\|float) - float` | Convert degrees to radians. |
| **RandBool** | `RandBool() - bool` | Random boolean. |
| **RandChoice** | `RandChoice(arr:array) - any` | Random element from array. |
| **RandFloat** | `RandFloat(min:int\|float, max:int\|float) - float` | Random float in [min, max]. |
| **Random** | `Random() - float` | Random float in [0.0, 1.0). |
| **RandomInt** | `RandomInt(min:int\|float, max:int\|float) - int` | Random integer in [min, max]. |
| **RandomNormal** | `RandomNormal(mean:int\|float, stddev:int\|float) - float` | Normal (Gaussian) distribution sample. |
| **Round** | `Round(x:int\|float) - int` | Round to nearest integer (half-up). |
| **RoundTo** | `RoundTo(x:int\|float, decimals:int) - float` | Round to `decimals` fractional digits (clamped 0..15). |
| **Sec** | `Sec(x:int\|float) - float` | Secant. |
| **Sech** | `Sech(x:int\|float) - float` | Hyperbolic secant. |
| **Sign** | `Sign(x:int\|float) - int` | Returns -1, 0, or 1. |
| **Sin** | `Sin(x:int\|float) - float` | Sine. |
| **Sinh** | `Sinh(x:int\|float) - float` | Hyperbolic sine. |
| **Sqrt** | `Sqrt(x:int\|float) - float` | Square root. |
| **Stddev** | `Stddev(arr:array) - float` | Standard deviation of array elements. |
| **Sum** | `Sum(arr:array) - int\|float` | Sum of array elements (int if all-int, else float). `0` if empty; `nil` if non-numeric. |
| **Tan** | `Tan(x:int\|float) - float` | Tangent. |
| **Tanh** | `Tanh(x:int\|float) - float` | Hyperbolic tangent. |
| **Trunc** | `Trunc(x:int\|float) - float` | Truncate toward zero. |
| **Variance** | `Variance(arr:array) - float` | Variance of array elements. |
| **Wrap** | `Wrap(x:int\|float, min:int\|float, max:int\|float) - float` | Wrap x into [min, max] range. |

**Additional scalar functions** (less common):

| Function | Signature | Description |
| ---------- | ----------- | ------------- |
| **Clamp01** | `Clamp01(x:int\|float) - float` | Clamp to [0.0, 1.0]. |
| **Combination** | `Combination(n:int\|float, r:int\|float) - int` | Binomial coefficient C(n,r). |
| **Distance** | `Distance(x1:int\|float, y1:int\|float, x2:int\|float, y2:int\|float) - float` | 2D Euclidean distance. |
| **Expm1** | `Expm1(x:int\|float) - float` | e^x − 1 (accurate near zero). |
| **Factorial** | `Factorial(n:int\|float) - int` | n! |
| **Fract** | `Fract(x:int\|float) - float` | Fractional part of x. |
| **IeeeRemainder** | `IeeeRemainder(x:int\|float, y:int\|float) - float` | IEEE 754 remainder. |
| **IsClose** | `IsClose(a:int\|float, b:int\|float) - bool` | Approximate equality check. |
| **Lerp** | `Lerp(a:int\|float, b:int\|float, t:int\|float) - float` | Linear interpolation. |
| **Log1p** | `Log1p(x:int\|float) - float` | ln(1 + x) (accurate near zero). |
| **Log2** | `Log2(x:int\|float) - float` | Base-2 logarithm. |
| **Permutation** | `Permutation(n:int\|float, r:int\|float) - int` | P(n,r) ordered permutations. |

**Bit intrinsics** (integer operands; all four emit as `JIT_CALL_BUILTIN_I` inside `hot` functions):

| Function | Signature | Description |
| ---------- | ----------- | ------------- |
| **BitCount** | `BitCount(n:int) - int` | Number of set bits in the 64-bit two's-complement representation of n. |
| **BitLength** | `BitLength(n:int) - int` | Minimum bits to represent \|n\| (0 for n=0). Equivalent to Python `int.bit_length()`. |
| **LeadingZeros** | `LeadingZeros(n:int) - int` | Number of leading zero bits in the 64-bit representation (64 when n=0). |
| **TrailingZeros** | `TrailingZeros(n:int) - int` | Number of trailing zero bits in the 64-bit representation (64 when n=0). |

**Matrix operations** (operate on 2D arrays of numbers):

| Function | Signature | Description |
| ---------- | ----------- | ------------- |
| **MatAdd** | `MatAdd(a:array, b:array) - array` | Element-wise matrix addition. |
| **MatApply** | `MatApply(mat:array, vec:array) - array` | Multiply matrix by vector. |
| **MatDet** | `MatDet(mat:array) - float` | Matrix determinant. |
| **MatIdentity** | `MatIdentity(n:int\|float) - array` | Create n×n identity matrix. |
| **MatInverse** | `MatInverse(mat:array) - array` | Matrix inverse. |
| **MatMul** | `MatMul(a:array, b:array) - array` | Matrix multiplication. |
| **MatRotate2D** | `MatRotate2D(angle:int\|float) - array` | 2D rotation matrix for `angle` radians. |
| **MatScale2D** | `MatScale2D(sx:int\|float, sy:int\|float) - array` | 2D scale matrix. |
| **MatSub** | `MatSub(a:array, b:array) - array` | Element-wise matrix subtraction. |
| **MatTranslate2D** | `MatTranslate2D(tx:int\|float, ty:int\|float) - array` | 2D translation matrix. |
| **MatTranspose** | `MatTranspose(mat:array) - array` | Transpose matrix. |

**Vector operations** (operate on flat arrays of numbers):

| Function | Signature | Description |
| ---------- | ----------- | ------------- |
| **VecAdd** | `VecAdd(a:array, b:array) - array` | Element-wise addition. |
| **VecDistance** | `VecDistance(a:array, b:array) - float` | Euclidean distance between two vectors. |
| **VecDot** | `VecDot(a:array, b:array) - float` | Dot product. |
| **VecLength** | `VecLength(v:array) - float` | Vector magnitude. |
| **VecNormalize** | `VecNormalize(v:array) - array` | Unit vector. |
| **VecScale** | `VecScale(v:array, s:int\|float) - array` | Scale vector by scalar. |
| **VecSub** | `VecSub(a:array, b:array) - array` | Element-wise subtraction. |

**Constants:**

| Constant | Value |
| ---------- | ------- |
| `Math.PI` | `3.14159265358979323846` |
| `Math.TAU` | `6.28318530717958647692` |
| `Math.E` | `2.71828182845904523536` |
| `Math.Infinity` | `+Inf` |
| `Math.NaN` | `NaN` |

---

### Memory

Namespace: **`Memory`**

Unsafe raw memory access. Requires `-u` flag. `ptr` = `int\|string\|block\|pointer` (any pointer-like value - see legend).

| Function | Signature | Description |
| ---------- | ----------- | ------------- |
| **Alloc** | `Alloc(size:int) - int` | Allocate `size` bytes; returns raw address. Caller must free. |
| **BitClear** | `BitClear(ptr:int\|string\|block\|pointer, bit:int) - nil` | Clear bit `bit` at address. |
| **BitSet** | `BitSet(ptr:int\|string\|block\|pointer, bit:int) - nil` | Set bit `bit` at address. |
| **BitTest** | `BitTest(ptr:int\|string\|block\|pointer, bit:int) - int` | Return 0 or 1 for bit at address. |
| **BitToggle** | `BitToggle(ptr:int\|string\|block\|pointer, bit:int) - nil` | Toggle bit at address. |
| **Compare** | `Compare(a:int\|string\|block\|pointer, b:int\|string\|block\|pointer, len:int) - int` | memcmp of `len` bytes. Returns <0, 0, >0. |
| **Copy** | `Copy(dst:int\|string\|block\|pointer, src:int\|string\|block\|pointer, len:int) - nil` | memmove `len` bytes. |
| **Free** | `Free(addr:int) - nil` | Free previously allocated memory. |
| **IsLittleEndian** | `IsLittleEndian() - bool` | `true` if host is little-endian. |
| **Pin** | `Pin(addr:int, size:int) - nil` | Mark memory region as pinned (prevent GC movement). |
| **Process** | `Process(addr:int, size:int, chunkSize:int, func:function ) - bool` | Iterate memory in chunks calling `fn(ptr, len)`. |
| **Read8** | `Read8(ptr:int\|string\|block\|pointer, offset?:int) - int` | Read 1 byte at ptr+offset. |
| **Read16** | `Read16(ptr:int\|string\|block\|pointer, offset?:int) - int` | Read 2 bytes (little-endian). |
| **Read32** | `Read32(ptr:int\|string\|block\|pointer, offset?:int) - int` | Read 4 bytes (little-endian). |
| **Read64** | `Read64(ptr:int\|string\|block\|pointer, offset?:int) - int` | Read 8 bytes (little-endian). |
| **ReadBytes** | `ReadBytes(dst:int\|string\|block\|pointer, src:int\|string\|block\|pointer, len?:int) - block` | Read bytes into block. |
| **ReadFloat32** | `ReadFloat32(ptr:int\|string\|block\|pointer, offset?:int) - float` | Read IEEE-754 single. |
| **ReadFloat64** | `ReadFloat64(ptr:int\|string\|block\|pointer, offset?:int) - float` | Read IEEE-754 double. |
| **ReadString** | `ReadString(ptr:int\|string\|block\|pointer, maxLen?:int) - string` | Read null-terminated string from address. |
| **Swap16** | `Swap16(v:int) - int` | Byte-swap 16-bit value. |
| **Swap32** | `Swap32(v:int) - int` | Byte-swap 32-bit value. |
| **Swap64** | `Swap64(v:int) - int` | Byte-swap 64-bit value. |
| **Unpin** | `Unpin(addr:int) - nil` | Unpin previously pinned region. |
| **Write8** | `Write8(ptr:int\|string\|block\|pointer, value:int, offset?:int) - nil` | Write 1 byte. |
| **Write16** | `Write16(ptr:int\|string\|block\|pointer, value:int, offset?:int) - nil` | Write 2 bytes (little-endian). |
| **Write32** | `Write32(ptr:int\|string\|block\|pointer, value:int, offset?:int) - nil` | Write 4 bytes (little-endian). |
| **Write64** | `Write64(ptr:int\|string\|block\|pointer, value:int, offset?:int) - nil` | Write 8 bytes (little-endian). |
| **WriteBytes** | `WriteBytes(dst:int\|string\|block\|pointer, src:int\|string\|block\|pointer, data:string, len?:int) - nil` | Write bytes to address. |
| **WriteFloat32** | `WriteFloat32(ptr:int\|string\|block\|pointer, value:float, offset?:int) - nil` | Write IEEE-754 single. |
| **WriteFloat64** | `WriteFloat64(ptr:int\|string\|block\|pointer, value:float, offset?:int) - nil` | Write IEEE-754 double. |
| **WriteString** | `WriteString(ptr:int\|string\|block\|pointer, s:string, maxLen?:int) - nil` | Write string bytes to address (null-terminated). |

---

### Net

Namespace: **`Net`**

Network utility functions: DNS resolution, IP conversion, and connectivity checks. Available on all platforms.

| Function | Signature | Description |
| ---------- | ----------- |------------- |
| **BytesToIP** | `BytesToIP(bytes:array) - string` | Convert byte array to IP string (IPv4 or IPv6). |
| **GetHostname** | `GetHostname() - string` | Returns machine hostname. |
| **GetLocalIP** | `GetLocalIP() - string` | Returns primary local IP address (IPv4 preferred; falls back to IPv6). |
| **IpToBytes** | `IpToBytes(ip:string) - array` | Convert IP string to byte array. |
| **IsReachable** | `IsReachable(host:string, port:int, timeoutMs?:int) - bool` | TCP reachability check. `timeoutMs` defaults to 1000. |
| **IsValidIP** | `IsValidIP(s:string) - bool` | `true` if string is valid IPv4 or IPv6. |
| **ResolveDNS** | `ResolveDNS(host:string) - array` | Resolve hostname to array of IP strings (IPv4 and IPv6). Returns empty array on failure. |
| **ResolveDNSAsync** | `ResolveDNSAsync(host:string) - fiber` | Non-blocking `ResolveDNS`. Use with `await`. Other fibers run during the DNS lookup. |
| **ResolveIPv4** | `ResolveIPv4(host:string) - array` | Resolve to array of IPv4 address strings. |
| **ResolveIPv6** | `ResolveIPv6(host:string) - array` | Resolve to array of IPv6 address strings. |
| **ReverseDNS** | `ReverseDNS(ip:string) - string` | Reverse lookup: IP - hostname. |

---

### Object

Namespace: **`Object`**

Introspection and manipulation of `object`, `instance`, or `class` values.

| Function | Signature | Description |
| ---------- | ----------- | ------------- |
| **Clear** | `Clear(obj:object\|instance\|class) - bool` | Remove all properties from `obj`. Mutates. |
| **Clone** | `Clone(obj:object) - object` | Deep clone of object. Primitives copied; functions shared. |
| **Count** | `Count(obj:object\|instance\|class) - int` | Number of properties/fields. |
| **Delete** | `Delete(obj:object, key:string) - bool` | Delete property `key`. Returns `true` if existed. |
| **Entries** | `Entries(obj:object\|instance\|class) - array` | Array of `[key, value]` pairs. |
| **Freeze** | `Freeze(obj:object) - bool` | Makes `obj` read-only. Any subsequent write throws. Shallow only. |
| **FromKeys** | `FromKeys(arr:array, value:any) - object` | Creates new object from string key array, all set to `value`. |
| **All** | `All(obj:object, func:function) - bool` | Returns `true` if `fn(key, value)` is truthy for every entry. Returns `true` for an empty object. |
| **Any** | `Any(obj:object, func:function) - bool` | Returns `true` if `fn(key, value)` is truthy for at least one entry. Returns `false` for an empty object. |
| **HasKey** | `HasKey(obj:object, key:string) - bool` | `true` if property `key` exists. |
| **Invert** | `Invert(obj:object) - object` | Returns a new object with keys and values swapped. Values are coerced to string to become keys. |
| **IsNil** | `IsNil(val:any) - bool` | `true` if `val` is `nil`. Single-arg; usable as a fast-path callback to `Array.Where`, `Array.Any`, `Array.GroupBy`, etc. |
| **Keys** | `Keys(obj:object\|instance\|class) - array` | Array of property names. |
| **MapKeys** | `MapKeys(obj:object, func:function) - object` | Returns a new object with keys transformed by `fn(key)`. Values are kept; new keys are coerced to string. Builtin functions (e.g. `String.ToUpper`) use the fast path. |
| **Merge** | `Merge(dst:object, src:object) - bool` | Copy all `src` properties into `dst`. Mutates `dst`. Shallow. |
| **NotNil** | `NotNil(val:any) - bool` | `true` if `val` is not `nil`. Single-arg; usable as a fast-path callback to `Array.Where`, `Array.Any`, `Array.GroupBy`, etc. |
| **Pick** | `Pick(obj:object, keys:array) - object` | Returns new object with only the listed keys. |
| **Reduce** | `Reduce(obj:object, func:function, initial:any) - any` | Folds over entries: `acc = fn(acc, key, value)` starting from `initial`. |
| **Select** | `Select(obj:object, func:function ) - object` | Returns new object with values transformed by `fn(key, value)`. |
| **TryGet** | `TryGet(obj:object, key:string, default:any) - any` | Returns `obj[key]` if present, otherwise `default`. |
| **Values** | `Values(obj:object\|instance\|class) - array` | Array of property values. |
| **Where** | `Where(obj:object, func:function ) - object` | Returns new object containing only keys where `fn(key, value)` is truthy. |

#### Object instance method syntax

All `Object` functions where the object is the first argument can be called directly on an `object` value:

```flaris
var o = { name: "Alice", age: 30 };
o.Keys()                          // ["name", "age"]  - same as Object.Keys(o)
o.Values()                        // ["Alice", 30]
o.HasKey("age")                   // true
o.Count()                         // 2
o.Delete("age")                   // same as Object.Delete(o, "age")
o.Merge({ city: "Stockholm" })    // same as Object.Merge(o, ...)
o.Where(fn(k, v) { return v != nil; })
o.Select(fn(k, v) { return String.ToUpper(v); })
```

Functions that do not take an object as first argument (`FromKeys`) are not available as instance methods. Both forms compile to identical bytecode when the variable is typed (`: object` or inferred). Untyped variables fall back to a runtime dispatch.

---

---

### Type

Namespace: **`Type`**

Integer constants representing every runtime type. Used with the `type()` built-in, which returns the type of a value as an `int` bitmask.

```js
type(42)      == Type.Int;       // true
type("hello") == Type.String;    // true
(type(x) & Type.Callable ) != 0; // true for functions, builtins, bound methods, FFI
(type(x) & Type.Number) != 0;    // true for int or float

Type.Is(x, Type.Callable);               // true for any function type
Type.Is(x, Type.Int | Type.Float);       // ad-hoc union without a named composite

Type.Name(42);                           // "int"
Type.Name([]);                           // "array"

Type.Assert(x, Type.Int);               // throws if x is not int
Type.Assert(x, Type.Number, "expected a number");
```

#### Functions

| Function | Signature | Description |
| ---------- | ----------- | ------------- |
| **Assert** | `Assert(x:any, mask:int, msg?:string) - bool` | Throws `InvalidArgs` if `type(x)` does not match `mask`. Optional custom message. Returns `true` on success. |
| **Is** | `Is(x:any, mask:int) - bool` | Returns `true` if `type(x)` matches any bit in `mask`. Use with composite constants. |
| **IsArray** | `IsArray(x:any) - bool` | `true` if `x` is an array. Single-arg; usable as a fast-path callback to `Array.Where`, `Array.Select`, `Array.GroupBy`, etc. |
| **IsBool** | `IsBool(x:any) - bool` | `true` if `x` is a bool. |
| **IsFloat** | `IsFloat(x:any) - bool` | `true` if `x` is a float. |
| **IsFunction** | `IsFunction(x:any) - bool` | `true` if `x` is any callable (function, builtin, or FFI). |
| **IsInt** | `IsInt(x:any) - bool` | `true` if `x` is an integer. |
| **IsNumber** | `IsNumber(x:any) - bool` | `true` if `x` is an integer or float. |
| **IsObject** | `IsObject(x:any) - bool` | `true` if `x` is a plain object. |
| **IsString** | `IsString(x:any) - bool` | `true` if `x` is a string. |
| **Name** | `Name(x:any) - string` | Returns the type as a human-readable string: `"int"`, `"array"`, `"fiber"`, etc. Single-arg; usable as a callback. |
| **Of** | `Of(x:any) - int` | Returns the type bitmask of `x` - the referenceable equivalent of the `type()` built-in. Single-arg; usable as a callback (e.g. `Array.GroupBy(items, Type.Of)`). |


#### Primitive Types

| Constant | Value | Matches |
| ---------- | ------- | --------- |
| `Type.Nil` | `0x000001` | `nil` |
| `Type.Bool` | `0x000080` | `true`, `false` |
| `Type.Char` | `0x000002` | character literals |
| `Type.Int` | `0x000008` | integers |
| `Type.Float` | `0x000004` | floating-point numbers |
| `Type.String` | `0x000010` | strings |

#### Collection Types

| Constant | Value | Matches |
| ---------- | ------- | --------- |
| `Type.Array` | `0x000040` | arrays |
| `Type.Object` | `0x000020` | plain objects |

#### Callable Types

| Constant | Value | Matches |
| ---------- | ------- | --------- |
| `Type.Function` | `0x000100` | user-defined functions |
| `Type.BuiltinFunction` | `0x000200` | built-in functions |
| `Type.FFIFunction` | `0x010000` | FFI-loaded native functions |
| `Type.BoundMethod` | `0x002000` | bound instance methods |

#### Object-Oriented Types

| Constant | Value | Matches |
| ---------- | ------- | --------- |
| `Type.Class` | `0x020000` | class definitions |
| `Type.Instance` | `0x040000` | class instances |
| `Type.Module` | `0x000400` | built-in modules |

#### Runtime Types

| Constant | Value | Matches |
| ---------- | ------- | --------- |
| `Type.Fiber` | `0x008000` | fibers |
| `Type.Exception` | `0x000800` | exception objects |
| `Type.Stream` | `0x080000` | file/network streams |
| `Type.Block` | `0x001000` | memory blocks |
| `Type.Pointer` | `0x004000` | raw pointers (internal) |

#### Composite Constants

These are bitmask unions - use `&` not `==`.

| Constant | Matches |
| ---------- | --------- |
| `Type.Number` | `int` or `float` |
| `Type.Numeric` | `int`, `float`, `bool`, or `char` |
| `Type.Callable` | any callable: function, builtin, bound method, FFI |
| `Type.ObjectLike` | `object`, `instance`, `class`, or `module` |

### OS

Namespace: **`OS`**

Process, environment, and system interface. Functions marked **unsafe** require the `-u` flag.

| Function | Signature | Description |
| ---------- | ----------- | ------------- |
| **Arch** | `Arch() - string` | Returns the CPU architecture: `"x86_64"`, `"x86"`, `"mips"`, `"arm64"`, `"arm"`, or `"unknown"`. |
| **Args** | `Args() - array` | Returns CLI argument strings as passed to the script. |
| **Bits** | `Bits() - int` | Returns the pointer width of the platform: `32` or `64`. |
| **Chdir** | `Chdir(path:string) - bool` | Change working directory. |
| **Exec** | `Exec(path:string, args?:array) - nil` | Replace the current process image with `path` via `execvp`. Only returns on error. Does not require `-u`. |
| **Execute** | `Execute(cmd:string) - string` | Run a shell command via `popen`; returns stdout+stderr merged. Returns `nil` on failure. Requires `-u`. **The command string is passed directly to the shell with no escaping or sanitization - never pass untrusted or externally-supplied input.** If you need to run a program with separate arguments and do not need to capture output, use `OS.Exec` instead (which bypasses the shell entirely via `execvp`). Does not need `-u` |
| **ExecuteAsync** | `ExecuteAsync(cmd:string) - fiber` | Non-blocking `Execute`. Use with `await`. Other fibers run while the command runs. Same security warning as `Execute` applies. |
| **Getenv** | `Getenv(name:string) - string` | Read environment variable. Returns `nil` if not set. |
| **GetArgValue** | `GetArgValue(name:string, default?:string) - string` | Extract value from CLI arg matching `name=value`. Returns `default` (or `nil`) if not found. |
| **Gid** | `Gid() - int` | Returns the real group ID of the process. |
| **IsRoot** | `IsRoot() - bool` | `true` if the process is running as root (UID 0). |
| **Kill** | `Kill(pid:int, signal:int) - bool` | Send a signal to a process. Use standard signal numbers (e.g. `15` for SIGTERM, `9` for SIGKILL). Requires `-u`. |
| **Name** | `Name() - string` | Returns OS name: `"Windows"`, `"Linux"` or `"macOS"`. |
| **Pid** | `Pid() - int` | Returns the current process ID. |
| **Ppid** | `Ppid() - int` | Returns the parent process ID. |
| **Setenv** | `Setenv(name:string, value:string) - bool` | Set environment variable. |
| **Sleep** | `Sleep(ms:int) - bool` | Sleep current OS thread for `ms` milliseconds. Blocks all fibers - prefer `Fiber.Sleep`. |
| **TempDir** | `TempDir() - string` | Returns path to system temp directory. |
| **Uid** | `Uid() - int` | Returns the real user ID of the process. |
| **Wait** | `Wait(pid:int) - int` | Wait for child process to exit. Returns exit code, or negative signal number if killed by signal. |
| **Spawn** | `Spawn(cmd:string, args?:array) - object` | Fork and exec `cmd` with separate stdin/stdout/stderr pipes. Returns `{Pid:int, Stdin:int, Stdout:int, Stderr:int}` where the int values are raw file descriptors for use with `ReadPipe`/`WritePipe`/`ClosePipe`. Returns `nil` on error. Stdout and stderr fds are non-blocking. |
| **ReadPipe** | `ReadPipe(fd:int) - string` | Non-blocking read from a pipe fd returned by `Spawn`. Returns `nil` when no data is available or the pipe is closed. |
| **WritePipe** | `WritePipe(fd:int, data:string) - int` | Write `data` to a pipe fd (stdin of a spawned process). Returns bytes written, or `-1` on error. |
| **ClosePipe** | `ClosePipe(fd:int) - bool` | Close a pipe fd. Call on stdin to signal EOF to the child process. |
| **IsAlive** | `IsAlive(pid:int) - bool` | Returns `true` if the process with `pid` is still running. Non-blocking. Does not reap the child - safe to call before `TryWait`/`Wait`. |
| **TryWait** | `TryWait(pid:int) - object` | Non-blocking wait. Returns `{Alive:bool, Exit:int}`. When `Alive` is `false` the child has been reaped and `Exit` holds the exit code. Prefer over polling `IsAlive`+`Wait` to avoid double-reap. |
| **KillChild** | `KillChild(pid:int, signal:int) - bool` | Send `signal` to a process previously spawned via `Os.Spawn`. Does **not** require `-u`. Returns `false` if `pid` was not spawned by this VM instance. For sending signals to arbitrary PIDs use `Os.Kill` (requires `-u`). |
| **RunEx** | `RunEx(cmd:string, args?:array) - object` | Run `cmd` and wait for it to finish. Returns `{Exit:int, Stdout:string, Stderr:string}` with stdout and stderr captured separately. Returns `nil` on fork/spawn failure. |
| **GetEnvAll** | `GetEnvAll() - object` | Returns all environment variables as an object `{NAME: "value", ...}`. |

---

### Path

Namespace: **`Path`**

Cross-platform path string manipulation.

| Function | Signature | Description |
| -------- | --------- | ----------- |
| **ChangeExtension** | `ChangeExtension(path:string, ext:string) - string` | Replace file extension. |
| **Combine** | `Combine(part1:string, part2:string, ...more) - string` | Join path segments with separator. Up to 15 arguments. |
| **GetCurrentDir** | `GetCurrentDir() - string` | Returns current working directory. |
| **GetDirectoryName** | `GetDirectoryName(path:string) - string` | Returns directory portion of path. |
| **GetExtension** | `GetExtension(path:string) - string` | Returns extension including dot (e.g. `".txt"`). |
| **GetFileName** | `GetFileName(path:string) - string` | Returns filename with extension. |
| **GetFileNameWithoutExtension** | `GetFileNameWithoutExtension(path:string) - string` | Returns filename without extension. |
| **GetFullPath** | `GetFullPath(path:string) - string` | Resolve to absolute path. |
| **GetRoot** | `GetRoot(path:string) - string` | Returns root component (`"/"` or `"C:\"`) |
| **HasExtension** | `HasExtension(path:string) - bool` | `true` if path has an extension. |
| **TempFile** | `TempFile() - string` | Returns path to a new unique temporary file. |

---

### Regex

Namespace: **`Regex`**

Regular expression matching, search, replace, and split. The engine is a lightweight custom implementation supporting the syntax documented below. Patterns are matched against UTF-8 strings, but character-level operations are byte-based.

#### Functions

| Function | Signature | Description |
| -------- | --------- | ----------- |
| **IsMatch** | `IsMatch(input:string, pattern:string) - bool` | `true` if pattern matches anywhere in input. |
| **Match** | `Match(input:string, pattern:string) - string` | Returns first match substring, or `nil`. |
| **Matches** | `Matches(input:string, pattern:string) - array` | Returns all non-overlapping match substrings. |
| **Capture** | `Capture(input:string, pattern:string) - array` | Returns `[fullMatch, group1, group2, ...]` for the first match, or `nil` if no match. Element 0 is the whole match; subsequent elements are the capture groups in order of their opening `(`. A group that did not participate in the match (e.g. an unmatched optional group) is `nil`. |
| **Replace** | `Replace(input:string, pattern:string, replacement:string) - string` | Replace all matches with replacement string. |
| **Split** | `Split(input:string, pattern:string) - array` | Split input at pattern boundaries. Always includes remainder as last element. |

#### Supported syntax

**Anchors**

| Syntax | Description |
| -------- | ------------- |
| `^` | Match start of string |
| `$` | Match end of string |

**Wildcards and quantifiers**

| Syntax | Description |
| -------- | ------------- |
| `.` | Any character except `\n` and `\r` |
| `*` | Zero or more (greedy) |
| `+` | One or more (greedy) |
| `?` | Zero or one |
| `{n}` | Exactly n repetitions |
| `{n,}` | At least n repetitions |
| `{n,m}` | Between n and m repetitions (inclusive) |

**Character classes**

| Syntax | Description |
| -------- | ------------- |
| `[abc]` | Match any of `a`, `b`, `c` |
| `[^abc]` | Match anything not in set |
| `[a-z]` | Character range |
| `[a-zA-Z0-9]` | Multiple ranges |
| `[[:space:]]` | POSIX named class (see below) |

**POSIX named classes** (for use inside `[...]`)

| Class | Equivalent | Description |
| ------- | ------------ | ------------- |
| `[:alpha:]` | `[a-zA-Z]` | Letters |
| `[:digit:]` | `[0-9]` | Decimal digits |
| `[:alnum:]` | `[a-zA-Z0-9_]` | Letters, digits, underscore |
| `[:space:]` | `[ \t\n\r\f\v]` | Whitespace |
| `[:blank:]` | `[ \t]` | Space and tab only |
| `[:upper:]` | `[A-Z]` | Uppercase letters |
| `[:lower:]` | `[a-z]` | Lowercase letters |
| `[:punct:]` | | Punctuation characters |
| `[:print:]` | | Printable characters (including space) |
| `[:graph:]` | | Printable characters (excluding space) |
| `[:cntrl:]` | | Control characters |
| `[:xdigit:]` | `[0-9a-fA-F]` | Hexadecimal digits |

**Escape sequences**

| Syntax | Description |
| -------- | ------------- |
| `\d` | Digit `[0-9]` |
| `\D` | Non-digit |
| `\w` | Word character `[a-zA-Z0-9_]` |
| `\W` | Non-word character |
| `\s` | Whitespace |
| `\S` | Non-whitespace |
| `\t` | Tab character |
| `\n` | Newline character |
| `\r` | Carriage return |
| `\b` | Word boundary (zero-width) |
| `\B` | Non-word boundary (zero-width) |
| `\.` `\*` etc. | Escaped literal character |

**Alternation**

| Syntax | Description |
| -------- |------------- |
| `a\|b` | Match `a` or `b`. Left branch is tried first. Multiple alternatives supported: `a\|b\|c`. |

**Grouping and capture**

| Syntax | Description |
| -------- |------------- |
| `(...)` | Capturing group. Scopes alternation/quantifiers (e.g. `(ab)+`, `(cat\|dog)s`) and records the matched substring for `Regex.Capture`. Groups are numbered 1, 2, … by the position of their opening `(`. |
| `(?:...)` | Non-capturing group. Groups for alternation/quantifiers without producing a capture. |

Groups may be nested and quantified. With `Regex.Capture`, a quantified group reports the substring of its **last** iteration (e.g. `(ab)+` over `"abab"` captures `"ab"`).

**Flags**

| Syntax | Description |
| -------- |------------- |
| `(?i)` | Case-insensitive matching. Must appear at the start of the pattern. |

#### Examples

```js
// Quantifiers
Regex.IsMatch("year 2024",          "[0-9]{4}")          // true
Regex.IsMatch("123",                "^[0-9]{2,4}$")      // true

// Alternation
Regex.IsMatch("my dog",             "cat|dog")           // true
Regex.Match("I have a fish",        "cat|dog|fish")      // "fish"

// Capture groups
Regex.Capture("2024-12-31", "(\\d+)-(\\d+)-(\\d+)")     // ["2024-12-31", "2024", "12", "31"]
Regex.Capture("name: Stefan", "(\\w+): (\\w+)")          // ["name: Stefan", "name", "Stefan"]
Regex.Capture("foo", "(a)?(foo)")                        // ["foo", nil, "foo"]
Regex.Capture("nope", "(\\d+)")                          // nil

// POSIX named classes
Regex.Split("a  b   c",             "[[:space:]]+")      // ["a", "b", "c"]
Regex.Replace("hello!",             "[[:punct:]]+", "")  // "hello"

// Escape sequences
Regex.IsMatch("a\tb",              "a\\tb")           // true
Regex.Split("line1\nline2\nline3","\\n")             // ["line1", "line2", "line3"]

// Word boundaries
Regex.IsMatch("concatenate",        "\\bcat\\b")     // false
Regex.IsMatch("my cat",             "\\bcat\\b")     // true
Regex.Matches("x1 22 333",          "\\b\\d+\\b")  // ["1", "22", "333"]

// Case-insensitive
Regex.IsMatch("HELLO WORLD",        "(?i)hello")         // true
Regex.Match("HELLO",                "(?i)[a-z]+")        // "HELLO"
Regex.Replace("FOO bar foo",        "(?i)foo", "baz")    // "baz bar baz"
```

#### Limitations

- No backreferences (`\1`, `\2`, etc.)
- No lookahead or lookbehind
- No non-greedy quantifiers (`*?`, `+?`) - all repetitions are greedy
- No Unicode-aware matching - character classes and ranges operate on bytes
- `(?i)` must be at the start of the pattern; inline flags like `foo(?i)bar` are not supported
- Up to 31 capture groups per pattern; pattern length is limited to 511 bytes

#### Matching semantics

The engine finds the **leftmost** match. Among matches starting at the same
position, alternation is leftmost-first (`a|b` prefers `a`) and quantifiers are
greedy. For multi-alternative patterns, the match starting at the earliest
position in the input wins regardless of branch order - e.g.
`Regex.Match("dog cat", "cat|dog")` returns `"dog"`. Matching is linear in the
length of the input (no catastrophic backtracking).

---

### Scheduler

Namespace: **`Scheduler`**

Low-level fiber scheduling primitives.

| Function | Signature | Description |
| -------- | --------- | ----------- |
| **HasReadyFibers** | `HasReadyFibers() - bool` | `true` if any fiber is ready to run. |
| **Schedule** | `Schedule(f:fiber) - nil` | Enqueue fiber `f` for next scheduler tick. |

---

### Stream

Namespace: **`Stream`**

Unified I/O for files, network sockets, pipes, and serial ports. All functions operate on a `stream` value backed by a file descriptor.

| Function | Signature | Description |
| -------- | --------- | ----------- |
| **Accept** | `Accept(s:stream) - stream` | Accept an incoming connection on a listening socket. Returns `nil` if no connection is pending. |
| **Close** | `Close(s:stream) - bool` | Close the stream. Flushes first. |
| **Connect** | `Connect(proto:string, host:string, port:int) - stream` | Open socket. `proto`: `"tcp"` or `"udp"`. `host`: hostname or IP (v4/v6). Returns `nil` on failure. |
| **Copy** | `Copy(src:stream, dst:stream, limit?:int) - bool` | Copy data from `src` to `dst`. Optional byte limit. |
| **Flush** | `Flush(s:stream) - bool` | Flush write buffer (`fsync` for files, `tcdrain` for serial, no-op for sockets). |
| **IsOpen** | `IsOpen(s:stream) - bool` | `true` if stream is still open. |
| **Listen** | `Listen(proto:string, port:int, backlog?:int) - stream` | Create a listening socket. `proto`: `"tcp"` or `"udp"`. Tries dual-stack IPv6 first, falls back to IPv4. Sets `SO_REUSEADDR` and `SO_REUSEPORT`. `backlog` defaults to `128`. |
| **Open** | `Open(path:string, mode:string) - stream` | Open file stream. Mode: `"r"` (read-only), `"w"` (write/create/truncate), or `"rw"` (read-write/create). Returns `nil` on failure. |
| **OpenSerial** | `OpenSerial(port:string, baud:int) - stream` | Open serial port in raw mode. Valid baud rates: `9600`, `19200`, `38400`, `57600`, `115200`. |
| **Pipe** | `Pipe() - array` | Create a pipe. Returns `[readStream, writeStream]`. |
| **ReadAll** | `ReadAll(s:stream, limit?:int, timeout?:int) - block` | Read until EOF or limit. `timeout` in seconds (default 3). Returns block. |
| **ReadAsync** | `ReadAsync(s:stream, count?:int, timeout?:int, cb?:function) - fiber` | Async read; callback `cb(block)` on completion. |
| **ReadByte** | `ReadByte(s:stream) - int` | Read one byte as integer. |
| **ReadBytes** | `ReadBytes(s:stream, count:int) - block` | Read exactly `count` bytes into block. |
| **ReadLine** | `ReadLine(s:stream) - string` | Read until newline (`\n` or `\r\n`). |
| **ReadString** | `ReadString(s:stream, count:int) - string` | Read `count` bytes as string. |
| **Seek** | `Seek(s:stream, pos:int) - bool` | Seek to byte offset (file streams only). |
| **SendFile** | `SendFile(s:stream, path:string, offset?:int, count?:int) - int` | Zero-copy file-to-socket transfer. Uses `sendfile` on Linux and macOS/BSD; falls back to read/write loop on other targets (e.g. OpenWrt). Returns bytes sent. |
| **SetTimeout** | `SetTimeout(s:stream, readMs:int, writeMs?:int) - bool` | Set read and write timeouts in milliseconds on a socket stream. If `writeMs` is omitted, both directions use `readMs`. |
| **Tell** | `Tell(s:stream) - int` | Return current byte offset. |
| **WaitReadable** | `WaitReadable(s:stream, timeoutMs?:int) - bool` | Park the calling fiber until `s` (typically a listen socket from `Listen`) becomes readable. Resumes with `true` on readiness, `nil` on timeout. `timeoutMs <= 0` (default `-1`) waits without a deadline. Suspends without `await` — callable from a plain function, so a server loop is `while (Stream.WaitReadable(srv, -1)) { let c = Stream.Accept(srv); ... }`. |
| **WriteAll** | `WriteAll(s:stream, data:string\|block) - bool` | Write all bytes, retrying on partial writes. |
| **WriteAsync** | `WriteAsync(s:stream, data:string\|block, count?:int) - fiber` | Async write. |
| **WriteByte** | `WriteByte(s:stream, value:int) - bool` | Write single byte. |
| **WriteBytes** | `WriteBytes(s:stream, buf:block, count:int) - int` | Write `count` bytes from block; returns bytes written. |
| **WriteString** | `WriteString(s:stream, s:string) - bool` | Write string bytes. |
| **ReadU8** | `ReadU8(s:stream) - int` | Read one unsigned byte (0–255). Returns `nil` on EOF. |
| **ReadU16** | `ReadU16(s:stream, bigEndian?:bool) - int` | Read 2 bytes as unsigned 16-bit integer. Default little-endian. |
| **ReadU32** | `ReadU32(s:stream, bigEndian?:bool) - int` | Read 4 bytes as unsigned 32-bit integer. Default little-endian. |
| **ReadU64** | `ReadU64(s:stream, bigEndian?:bool) - int` | Read 8 bytes as 64-bit integer. Default little-endian. |
| **WriteU8** | `WriteU8(s:stream, value:int) - bool` | Write one byte. Returns `true` on success. |
| **WriteU16** | `WriteU16(s:stream, value:int, bigEndian?:bool) - bool` | Write 2 bytes. Default little-endian. Returns `true` on success. |
| **WriteU32** | `WriteU32(s:stream, value:int, bigEndian?:bool) - bool` | Write 4 bytes. Default little-endian. Returns `true` on success. |
| **WriteU64** | `WriteU64(s:stream, value:int, bigEndian?:bool) - bool` | Write 8 bytes. Default little-endian. Returns `true` on success. |

#### Stream instance method syntax

All `Stream` functions where the stream is the first argument can be called directly on a `stream` value:

```flaris
let s = Stream.Open("data.bin", "r");
s.ReadLine()          // same as Stream.ReadLine(s)
s.ReadByte()          // same as Stream.ReadByte(s)
s.WriteByte(0x0A)     // same as Stream.WriteByte(s, 0x0A)
s.Seek(0)             // same as Stream.Seek(s, 0)
s.Tell()              // same as Stream.Tell(s)
s.IsOpen()            // same as Stream.IsOpen(s)
s.Flush()             // same as Stream.Flush(s)
s.Close()             // same as Stream.Close(s)
```

Factory functions (`Open`, `Connect`, `Listen`, `Pipe`, `OpenSerial`) are not available as instance methods. Both forms compile to identical bytecode when the variable is typed (`: stream` or inferred from a builtin return). Untyped variables fall back to a runtime dispatch.

---

### String

Namespace: **`String`**

UTF-8 string operations. `Length` returns **byte count**; use `Utf8Length` for codepoint count.

All `String` functions can also be called directly on a string value:

```flaris
let s = "Hello, World!";
s.Length()             // 13    - same as String.Length(s)
s.ToLower()            // "hello, world!"
s.Contains("World")    // true
s.Substr(7, 5)         // "World"
s.Replace("o", "0")    // "Hell0, W0rld!"
s.Split(",")           // ["Hello", " World!"]
"  hi  ".Trim()        // "hi"
```

Both forms compile to identical bytecode when the variable is typed (`: string` or inferred from a builtin return). Untyped variables fall back to a runtime dispatch.

| Function | Signature | Description |
| ---------- | ----------- | ------------- |
| **CharAt** | `CharAt(s:string, pos:int) - char` | Character at byte position `pos` as a `char` immediate. Returns `'\0'` if `pos` is out of range. Zero allocation - prefer over `Substr(s, pos, 1)` in tight loops. |
| **Contains** | `Contains(s:string, sub:string) - bool` | `true` if `sub` appears anywhere in `s`. |
| **Count** | `Count(s:string, sub:string) - int` | Returns the number of non-overlapping occurrences of `sub` in `s`. Raises an error if `sub` is empty. |
| **Create** | `Create(n:int, fill?:int\|char) - string` | Create a mutable string of length `n` filled with `fill` (default `0`). Returns `""` for `n ≤ 0`, `nil` for `n` exceeding the string size limit. Intended for use with JIT functions that write characters in-place via `s[i] = ch`. The string's hash is computed at creation time and becomes stale if you modify the string - do not use as a map key after in-place modification. |
| **Empty** | `Empty(s:string) - bool` | `true` if string is empty. |
| **EndsWith** | `EndsWith(s:string, suffix:string) - bool` | `true` if `s` ends with `suffix`. |
| **EqualsIgnoreCase** | `EqualsIgnoreCase(a:string, b:string) - bool` | Case-insensitive equality. |
| **Format** | `Format(fmt:string, ...args) - string` | Printf-style formatting. At least 1 arg required. |
| **IndexOf** | `IndexOf(s:string, sub:string) - int` | Byte index of first occurrence of `sub`, or `-1`. |
| **IndexOfAnyFrom** | `IndexOfAnyFrom(s:string, charset:string, start:int) - int` | Byte index of the first character in `s` (starting from `start`) that appears anywhere in `charset`, or `-1`. Uses a single `strcspn` scan - efficient for finding the first of several possible delimiter characters. |
| **IndexOfFrom** | `IndexOfFrom(s:string, sub:string, start:int) - int` | Byte index of first occurrence of `sub` at or after byte offset `start`, or `-1`. Avoids allocating a substring; use instead of `IndexOf(Substr(s, start))` in parsing loops. |
| **Insert** | `Insert(s:string, pos:int, sub:string) - string` | Insert `sub` at byte position `pos`. |
| **IsAlnum** | `IsAlnum(s:string) - bool` | `true` if all characters are alphanumeric. |
| **IsAlpha** | `IsAlpha(s:string) - bool` | `true` if all characters are alphabetic. |
| **IsUpper** | `IsUpper(s:string) - bool` | `true` if all alphabetic characters are uppercase and at least one alphabetic character is present. |
| **IsLower** | `IsLower(s:string) - bool` | `true` if all alphabetic characters are lowercase and at least one alphabetic character is present. |
| **IsNumeric** | `IsNumeric(s:string) - bool` | `true` if all characters are numeric digits. |
| **IsWhitespace** | `IsWhitespace(s:string) - bool` | `true` if all characters are whitespace. |
| **Join** | `Join(arr:array, sep:str\|char) - string` | Concatenate array elements with separator. |
| **JsonPretty** | `JsonPretty(json:string) - string` | Pretty-print a JSON string. |
| **Left** | `Left(s:string, n:int) - string` | Return first `n` bytes. |
| **Length** | `Length(s:string) - int` | Byte length of string. |
| **PadLeft** | `PadLeft(s:string, width:int, fill?:str\|char) - string` | Left-pad to `width` with `fill` (default space). |
| **PadRight** | `PadRight(s:string, width:int, fill?:str\|char) - string` | Right-pad to `width` with `fill` (default space). |
| **Repeat** | `Repeat(s:string, n:int) - string` | Concatenate `s` with itself `n` times. |
| **Replace** | `Replace(s:string, old:string, new:string) - string` | Replace all occurrences of `old` with `new`. |
| **Reverse** | `Reverse(s:string) - string` | Reverse bytes of string. |
| **Right** | `Right(s:string, n:int) - string` | Return last `n` bytes. |
| **Sanitize** | `Sanitize(s:string) - string` | Remove non-printable and control characters. |
| **Split** | `Split(s:string, sep:str\|char) - array` | Split by separator; returns array of strings. |
| **SplitLines** | `SplitLines(s:string) - array` | Split by newlines; returns array of strings. |
| **StartsWith** | `StartsWith(s:string, prefix:string) - bool` | `true` if `s` starts with `prefix`. |
| **Substr** | `Substr(s:string, start:int, len?:int) - string` | Byte substring starting at `start`, optional `len`. |
| **ToAscii** | `ToAscii(s:string, replacement?:str\|char\|int) - string` | Strip/replace non-ASCII characters. |
| **ToLower** | `ToLower(s:string) - string` | Lowercase ASCII characters. |
| **ToUpper** | `ToUpper(s:string) - string` | Uppercase ASCII characters. |
| **Trim** | `Trim(s:string) - string` | Remove leading and trailing whitespace. |
| **TrimLeft** | `TrimLeft(s:string) - string` | Remove leading whitespace. |
| **TrimRight** | `TrimRight(s:string) - string` | Remove trailing whitespace. |
| **Truncate** | `Truncate(s:string, maxLen:int) - string` | Truncate to `maxLen` bytes. |

**UTF-8 helpers:**

| Function | Signature | Description |
| -------- | --------- | ----------- |
| **Utf8ByteIndexOf** | `Utf8ByteIndexOf(s:string, cpIndex:int) - int` | Byte offset of codepoint at index `cpIndex`. |
| **Utf8Concat** | `Utf8Concat(...parts:str\|char\|int) - string` | Concatenate strings, chars, or codepoints. 1–5 args. |
| **Utf8CpAt** | `Utf8CpAt(s:string, bytePos:int) - char` | Codepoint at byte position `bytePos`. |
| **Utf8CpNext** | `Utf8CpNext(s:string, bytePos:int) - int` | Byte offset of next codepoint after `bytePos`. |
| **Utf8GetCharAt** | `Utf8GetCharAt(s:string, cpIndex:int) - char` | Character at codepoint index `cpIndex`. |
| **Utf8Length** | `Utf8Length(s:string) - int` | Number of Unicode codepoints. |
| **Utf8Substr** | `Utf8Substr(s:string, cpStart:int, cpLen:int) - string` | Substring by codepoint indices. |

---

### Tls

Namespace: **`Tls`**

Built-in TLS byte transport - the encrypted counterpart to the TCP side of `Stream`, and the transport beneath the `Https` library (most code should use `Https`). TLS comes from the OS stack (macOS Secure Transport, Linux OpenSSL via `dlopen`, Windows SChannel) and the system trust store. Certificates and hostnames are verified by default (TLS >= 1.2); connections are opaque `int` handles from a generation-tagged table, so a stale or forged handle is rejected rather than dereferenced.

| Function | Signature | Description |
| -------- | --------- | ----------- |
| **Available** | `Available() - bool` | `true` if a TLS backend is compiled in and usable (on Linux, also that `libssl` loaded at runtime). |
| **Connect** | `Connect(host:string, port:int, insecure?:bool, timeoutMs?:int) - int` | Open a TLS connection; returns a handle, or `0` on failure. `insecure=true` skips certificate/hostname verification (trusted hosts only). Default timeout 30000 ms. |
| **Write** | `Write(h:int, data:string) - int` | Encrypt and send all bytes; returns bytes written, or `-1` on error. |
| **ReadLine** | `ReadLine(h:int) - string\|nil` | Read one CRLF/LF-terminated line, terminator stripped; `nil` at EOF. |
| **ReadN** | `ReadN(h:int, n:int) - string` | Read exactly `n` bytes (fewer only at EOF). Binary-safe. |
| **ReadAll** | `ReadAll(h:int) - string` | Read until the peer closes the connection. |
| **Close** | `Close(h:int) - bool` | Close the connection and free the handle. |
| **LastError** | `LastError(h:int) - string\|nil` | Last error message for the handle, or `nil`. |

---

### Time

Namespace: **`Time`**

Unix timestamp manipulation (seconds since epoch). `timestamp` is always `int`.

| Function | Signature | Description |
| -------- | --------- | ----------- |
| **AddDays** | `AddDays(t:int, n:number) - int` | Add `n` days to timestamp. Fractional values allowed. |
| **AddHours** | `AddHours(t:int, n:number) - int` | Add `n` hours to timestamp. Fractional values allowed. |
| **AddMinutes** | `AddMinutes(t:int, n:number) - int` | Add `n` minutes to timestamp. Fractional values allowed. |
| **AddSeconds** | `AddSeconds(t:int, n:number) - int` | Add `n` seconds to timestamp. Fractional values allowed. |
| **AddWeeks** | `AddWeeks(t:int, n:number) - int` | Add `n` weeks to timestamp. Fractional values allowed. |
| **Day** | `Day(t:int) - int` | Day of month (1–31). |
| **DiffDays** | `DiffDays(a:int, b:int) - int` | Integer difference in full days between two timestamps (`a - b`). |
| **Format** | `Format(t:int, fmt?:string) - string` | Format timestamp as string. Named aliases: `"short"` (`YYYY-MM-DD`), `"long"` / `"log"` (`YYYY-MM-DD HH:MM:SS`), `"time"` (`HH:MM:SS`), `"iso"` (`YYYY-MM-DDTHH:MM:SSZ`). Default: `"long"`. Custom strftime patterns also accepted. |
| **FromMillis** | `FromMillis(ms:int) - int` | Convert milliseconds to seconds (truncate). |
| **Hour** | `Hour(t:int) - int` | Hour (0–23). |
| **Millis** | `Millis() - int` | Current time as milliseconds since epoch. |
| **Minute** | `Minute(t:int) - int` | Minute (0–59). |
| **Month** | `Month(t:int) - int` | Month (1–12). |
| **Now** | `Now() - int` | Current Unix timestamp in seconds. |
| **Parse** | `Parse(s:string, fmt?:string) - int` | Parse timestamp string. Named aliases: `"ISO"` (default, `%Y-%m-%dT%H:%M:%S`), `"RFC"` (`%a, %d %b %Y %H:%M:%S`), `"DATE_ONLY"` (`%Y-%m-%d`). Custom strptime patterns also accepted. Raises error on failure. |
| **Second** | `Second(t:int) - int` | Second (0–59). |
| **TicksToMs** | `TicksToMs(ticks:int) - float` | Convert a nanosecond tick delta to milliseconds. Example: `Time.TicksToMs(Time.Ticks() - t0)` - `1.234` |
| **TicksToUs** | `TicksToUs(ticks:int) - float` | Convert a nanosecond tick delta to microseconds. Example: `Time.TicksToUs(Time.Ticks() - t0)` - `1234.5` |
| **TimeZoneOffsetMins** | `TimeZoneOffsetMins() - int` | Local timezone offset in minutes from UTC. |
| **Weekday** | `Weekday(t:int) - int` | Day of week: 0=Sunday … 6=Saturday. |
| **Year** | `Year(t:int) - int` | Four-digit year. |

---

### Timers

Namespace: **`Timers`**

Fiber-based timer scheduling.
Max 64 simultaneous timers.
Timer resolution is approx 1-3ms.

| Function | Signature | Description |
| ---------- | ----------- | ------------- |
| **Cancel** | `Cancel(id:int\|float) - bool` | Cancel a timer by its handle. |
| **SetAtTime** | `SetAtTime(timestamp:int\|float, func:function ) - int` | Fire `fn` at Unix timestamp. Returns handle. |
| **SetInterval** | `SetInterval(ms:int\|float, func:function ) - int` | Fire `fn` every `ms` milliseconds. Returns handle. |
| **SetTimeout** | `SetTimeout(ms:int\|float, func:function ) - int` | Fire `fn` once after `ms` milliseconds. Returns handle. |

---

### Util

Namespace: **`Util`**

Encoding, hashing utilities, and value comparison.

| Function | Signature | Description |
| ---------- | ----------- | ------------- |
| **AscToHex** | `AscToHex(s:string, upper?:bool) - string` | Convert ASCII string to hex representation. |
| **Base64Encode** | `Base64Encode(data:string\|block) - string` | Encode string or block as standard Base64 (padded, `+/` alphabet). |
| **Base64UrlEncode** | `Base64UrlEncode(data:string\|block) - string` | Encode string or block as Base64url (no padding, `-_` alphabet). Suitable for JWT. |
| **Base64DecodeString** | `Base64DecodeString(s:string) - string` | Decode Base64 or Base64url string to UTF-8 string. Accepts unpadded input and `-_` alphabet. Throws on invalid data. |
| **Base64DecodeBlock** | `Base64DecodeBlock(s:string) - block` | Decode Base64 or Base64url string to binary block. Accepts unpadded input and `-_` alphabet. Throws on invalid data. |
| **BinToHex** | `BinToHex(buf:block) - string` | Convert binary block to hex string. |
| **Coalesce** | `Coalesce(args...:any) - any` | Return first non-nil argument. Also accepts a single array and scans it. |
| **CoalesceEmpty** | `CoalesceEmpty(args...:any) - any` | Return first non-nil and non-empty argument. Empty means `nil`, `""`, `0`, `{}` or `[]`. Also accepts a single array. |
| **Compare** | `Compare(a:any, b:any) - int` | Deep comparison: returns <0, 0, or >0. |
| **HexToAsc** | `HexToAsc(s:string) - string` | Convert hex string back to ASCII. |
| **HexToBin** | `HexToBin(s:string) - block` | Convert hex string to binary block. |
| **Nanoid** | `Nanoid(size?:int) - string` | Generate a URL-safe random ID. Optional size. |
| **Uuid** | `Uuid() - string` | Generate a UUID v4 string. |

---

### VM

Namespace: **`VM`**

Virtual machine introspection and control.

| Function | Signature | Description |
| -------- | --------- | ----------- |
| **Compile** | `Compile(src:string, ...args) - function` | Compile source string to function. Max 10 KB. |
| **CompactMemory** | `CompactMemory()` | Run memory compaction pass. |
| **CurrentAllocations** | `CurrentAllocations() - int` | Current live allocation count. |
| **Eval** | `Eval(src:string, ...args) - any` | Compile and execute source string. Max 10 KB. |
| **Exit** | `Exit(code:int) - nil` | Terminate VM with exit code. |
| **GetFunctionInfo** | `GetFunctionInfo(fn:callable) - object` | Return an object describing a function. Accepts `function`, `builtin`, `ffi`, and bound methods. See field table below. |
| **GetModuleInfo** | `GetModuleInfo(name:string) - object\|nil` | Return an object describing a built-in module. `Members` array contains `{Name, Signature}` per function; `Constants` array contains `{Name, Type, Value}` per constant. Returns `nil` if the module name is not found. |
| **GetStartTimeMs** | `GetStartTimeMs() - int` | VM start time in milliseconds since epoch. |
| **Import** | `VM.Import(name:string, version:string, fingerprint?:string)` | Load or return cached module by name and version requirement. `version` uses the same syntax as `library()`: `"1.0"`, `">=1.2 <2.0"`, etc. Optional `fingerprint` is the whole-file SHA-256 (== `sha256sum`; a `sha256:` prefix is accepted) - if provided and mismatched, the VM halts regardless of `-S`. `let m = VM.Import("jwt", "1.0");`. See version syntax table in Guide §6. |
| **OnSignal** | `OnSignal(signum:int, handler:function) - bool` | Register signal handler. Supported: SIGHUP(1), SIGINT(2), SIGUSR1(10), SIGUSR2(12), SIGTERM(15). |
| **PatchFunction** | `PatchFunction(original:function, replacement:function) - bool` | Replace function at runtime. Requires running unsafe-mode `-u`. The replacement may be a Flaris-function, a builtin-function or a FFI-function |
| **PeakAllocations** | `PeakAllocations() - int` | Peak allocation count since VM start. |

#### `GetFunctionInfo` - returned object fields

| Field | Type | Description |
| ------- | ------ | ------------- |
| `Kind` | string | `"function"`, `"builtin"`, `"ffi"`, or `"unknown"` |
| `Name` | string\|nil | Function name. For builtins resolved as `"Module.Name"`. Nil if anonymous. |
| `Arity` | int | Number of required parameters. |
| `Optional` | int | Number of optional parameters (builtins only; 0 for scripted and FFI). |
| `Returns` | string | Return type as a human-readable string (e.g. `"string"`, `"int\|nil"`). |
| `ReturnType` | int | Return type as a raw type-mask integer. |
| `ArgTypes` | array\<int\> | Per-argument type masks in declaration order. |
| `ArgTypeNames` | array\<string\> | Per-argument type names in declaration order. |
| `IsStatic` | bool | `true` if the function is a static method. |
| `IsAsync` | bool | `true` if declared `async`. |
| `IsMethod` | bool | `true` if the function is an instance method (thiscall). |
| `Owner` | string\|nil | Class name for methods; nil for free functions. |

Bound methods are automatically unwrapped - passing a bound method returns info about the underlying function.

## R9 - Debug Reference

### Debug Opcodes

The compiler emits these opcodes when debug symbols are enabled (default; disabled by `-D` flag):

| Opcode | Arguments | Purpose | When emitted |
| ------ | --------- | ------- | ------------ |
| `OP_DBG_LINE` | `u16 line` | Mark source line number | Before each statement |
| `OP_DBG_FUNC_NAME` | `u16 const_idx` | Mark function name | At function entry |
| `OP_DBG_FILE_NAME` | `u16 const_idx` | Mark source file | Once per compilation unit |
| `OP_DBG_BREAK` | `u16 code` | Breakpoint | At `breakpoint` statement |

**Bytecode size overhead with debug symbols:** ~15%.
**Execution overhead:** ~2–3%.

#### Bytecode Example

```js
fn compute(x) { return x * 2; }
```

With debug symbols:

```bash
[0000] OP_DBG_FILE_NAME    "main.fls"
[0003] OP_DBG_FUNC_NAME    "compute"
[0006] OP_DBG_LINE         line=2
[0009] OP_GET_LOCAL        x
[0011] OP_IMM8             2
[0013] OP_MULTIPLY
[0014] OP_RETURN
```

With `-D` (stripped):

```bash
[0000] OP_GET_LOCAL
[0002] OP_IMM8             2
[0004] OP_MULTIPLY
[0005] OP_RETURN
```

---

### Quick Reference

#### CLI Flags for Debugging

| Flag | Effect | When to use |
| ------ | -------- | ------------- |
| *(default)* | Debug symbols ON | Development, testing |
| `-D` | Debug symbols OFF | Production, embedded |
| `-v` | Verbose output | High-level debugging |
| `-vv` | Very verbose | Opcode-level tracing |
| `-t` | Timing report | Performance analysis |
| `-s` | Statistics | Memory debugging |

#### Debug Module Quick Summary

| Function | Purpose |
| ---------- | --------- |
| `Debug.Stack()` | Dump VM stack to stdout |
| `Debug.Pool()` | Memory slab statistics |
| `Debug.Value(v)` | Inspect value (type, ref-count, address) |
| `Debug.Assert(a, b)` | Runtime equality check |
| `Debug.GuardAddress(p)` | Watch memory address - triggers on any R/W |
| `Debug.StackPtr()` | Current stack depth as integer |
| `Debug.Refs(o)` | Reference count of object |
| `Debug.Halt()` | Stop VM immediately |
| `Debug.Here(msg?)` | Print location marker (file/line/function) |

#### Breakpoint Statement

```js
breakpoint 100;              // Unconditional - always triggers
if (error) breakpoint 200;   // Conditional
```

Codes are user-defined integers. Use a namespace convention (e.g. 1000 = startup, 2000 = after validation) to make them meaningful.

---

### Real-World Debugging Examples

#### Breakpoint with Bytecode Context

When a breakpoint fires with `-vv`, you see the instruction stream around the current position:

```bash
[Breakpoint:100] in function main, line 1649
[4928] MUL
[4929] OP_LESS_EQUAL
[4930] OP_CALL1_BUILTIN     mod-id:12 func-id:1 -> Console.WriteLine
[4933] POP
>> [4957] OP_BREAKPOINT     code=100
[4960] OP_LINE              line=1651
Press Enter to continue...
```

#### Memory Guard Trigger

```js
Debug.GuardAddress(critical_data);
run_entire_program(critical_data);
// Any code that touches this object will stop here:
// [Mem-guard] R/W-operation on memory address 0xC98CDC1D0 in function main, line 1491
```

#### Tracking Stack Leaks

```js
let before = Debug.StackPtr();
risky_operation(data);
let after = Debug.StackPtr();
if (after != before) {
    Console.WriteLine("Stack leak: delta =", after - before);
    Debug.Stack();
}
```

#### Full Debug Session Pattern

```js
fn investigate(data) {
    Debug.Here("start");
    let alloc_before = VM.CurrentAllocations();
    let stack_before = Debug.StackPtr();
    Debug.Pool();

    Debug.GuardAddress(data);
    breakpoint 100;

    let result = risky_operation(data);

    breakpoint 200;

    if (Debug.StackPtr() != stack_before) Console.WriteLine("STACK LEAK!");
    Debug.Value(result);
    Debug.Pool();
    Console.WriteLine("Alloc delta:", VM.CurrentAllocations() - alloc_before);
    Debug.Here("end");
}
```

---

*For FFI plugin development, see `ffi.md`.*

---

## R10 - Writing Fast Flaris Code

This chapter translates the execution model from R7 into concrete coding patterns. Each recommendation is grounded in how many VM dispatch cycles a pattern consumes per iteration.

Dispatch count is the most direct proxy for loop speed.

---

### Counting loops: use `iter` for known integer ranges

`iter` is the fastest loop construct in Flaris. A single fused instruction both increments the counter and checks the bound - one dispatch for what `while` needs three to do.

```js
// Best - 2 dispatches/iteration overhead
iter (i from 0 to n) {
    sum += arr[i];
}
```

Equivalent `while` costs at least 3 dispatches overhead (compare-jump + increment + loop-back). Use `iter` whenever the range is a fixed integer expression known at the loop head.

`iter` allocates no iterator object. `i` is a local slot that starts at `from` and stops just before `to`.

---

### Comparing locals to constants: `while` fuses to a single instruction

When the loop condition is `local < constant` (or `<=`, `>`, `>=`), the compiler fuses the compare and branch into a single instruction instead of the four-instruction sequence a generic condition would require. A literal bound up to ±127 encodes in 1 byte; larger bounds encode in 4 bytes - both still one dispatch.

```js
// Fused - 1 dispatch for the entire condition
while (i < 1_000_000) { ... }   // large literal bound (4-byte encoding)
while (i < 100)       { ... }   // small literal bound (1-byte encoding)

// Also fused - local vs local
while (i < len) { ... }
```

All comparison operators (`<`, `<=`, `>`, `>=`, `==`, `!=`) are eligible. The bound must be a literal integer or a local variable resolved at compile time. If it is an expression (e.g. `arr.count + 1`), cache it into a local before the loop:

```js
let limit = arr.count + 1;      // evaluated once
while (i < limit) { ... }       // fused, 1 dispatch
```

---

### Incrementing counters: prefer `++` over `+= 1` over `= x + 1`

Three ways to increment a counter produce meaningfully different instruction sequences:

| Form | Encoding | Dispatches |
| --- | --- | --- |
| `i++` | 2 bytes | 1 |
| `i += 1` | 4 bytes | 1 |
| `i = i + 1` | 6 bytes (padded) | 3 |

`i = i + 1` is peephole-optimized in-place, but the padding bytes that fill the freed space still execute as individual dispatches.

`i++` is the most compact form - a 2-byte instruction with no arithmetic subcode. It is both the smallest encoding and the clearest signal to the compiler.

```js
// Best - 2 bytes, 1 dispatch
while (i < n) {
    process(arr[i]);
    i++;
}

// Good - 4 bytes, 1 dispatch
while (i < n) {
    process(arr[i]);
    i += 1;
}

// Avoid - 6 bytes padded, 3 dispatches
while (i < n) {
    process(arr[i]);
    i = i + 1;
}
```

The same rule applies to `--` and any compound-assign: `n += x` is always cheaper than `n = n + x`.

---

### Compound assignment beats explicit assignment for all arithmetic

`n += x`, `n -= x`, `n *= x` etc. compile to a compact fused instruction (zero stack, 1 dispatch). Writing `n = n + x` instead forces the optimizer to patch the result in-place, leaving padding bytes that still dispatch individually.

```js
// Good - zero-stack, 1 dispatch
sum += arr[i];
n   *= factor;

// Slower - fused + 1–3 padding dispatches depending on operand sizes
sum = sum + arr[i];
n   = n * factor;
```

This matters most inside loops. The operand encoding determines how much padding remains after optimization.

---

### Cache repeated property and array accesses into locals

Every `obj.prop` or `arr[expr]` inside a loop re-executes a hashmap lookup or bounds-checked index. Pull stable reads out of the loop body.

```js
// Without caching - hashmap + bounds check every iteration
while (i < data.count) {
    process(data.items[i]);
    i++;
}

// With caching - local reads only inside the loop
let items = data.items;
let count = data.count;
while (i < count) {
    process(items[i]);
    i++;
}
```

This combines with the fused-compare rule: `while (i < count)` fuses to a single instruction because `count` is now a local.

For array element reads inside loops, the compiler uses a zero-stack fast path when both the array and the index are locals, avoiding the generic dispatch path entirely.

The same principle applies to method calls. Storing a method reference in a local before the loop avoids repeating the hash lookup on every iteration:

```js
// Without caching - hash lookup on every call
while (i < count) {
    obj.process(data[i]);
    i++;
}

// With caching - direct call, no lookup
let fn = obj.process;
while (i < count) {
    fn(data[i]);
    i++;
}
```

---

### Keep variables type-stable inside loops

The VM fast-paths for arithmetic and comparison check the type tag on every operation. A variable that starts as an integer and becomes a float mid-loop forces the slow path on every subsequent instruction.

```js
// Good - tag-int fast path throughout
let sum: int = 0;
iter (i from 0 to n) {
    sum += values[i];   // stays int the whole way
}

// Risky - if any values[i] is a float, sum flips type mid-loop
let sum = 0;
iter (i from 0 to n) {
    sum += values[i];
}
```

Type annotations on locals (`let sum: int = 0`) are enforced by the analyzer and allow the compiler to take the fastest arithmetic path.

---

### Type your function parameters for faster calls

Flaris supports two styles: quick untyped scripts and fully typed code. The style you choose affects call performance.

When a function is called, the VM validates each argument's type against the declared parameter type. For untyped parameters the check still runs - it just accepts anything. For performance-critical functions that are called many times, this overhead adds up.

```js
// Untyped - runtime validates each argument on every call
fn fib(n) {
    if (n < 2) return n;
    return fib(n - 1) + fib(n - 2);
}

// Typed - compiler verifies types statically; runtime skips the check entirely
fn fib(n: int): int {
    if (n < 2) return n;
    return fib(n - 1) + fib(n - 2);
}
```

When the compiler can prove at the call site that every argument's type matches the declared parameter type, it emits `CALL_TYPED` instead of `CALL`. The VM fast path then skips argument validation completely - saving roughly 10 cycles per call.

| Style | Validation | Bytecode emitted |
| --- | --- | --- |
| Untyped params | At runtime, every call | `CALL` |
| Typed params + typed arguments | Skipped - done at compile time | `CALL_TYPED` |

The gain is proportional to call frequency. A function called 100 million times saves ~300 ms on M-series hardware when typed. A function called once saves nothing measurable.

**Guideline:** write untyped for glue code, one-off scripts, and prototypes. Add type annotations to any function that sits on a hot call path.

---

### Strip debug symbols for production

By default, the compiler injects debug tracking instructions for line numbers and symbol names. These execute as individual dispatches inside every function, including loop bodies. With `-D` the compiler omits them.

| Mode | Extra dispatches per loop iteration |
| --- | --- |
| Default (debug on) | 1–3 per statement |
| `-D` (debug off) | 0 |

For a 10 million-iteration loop with two body statements, debug symbols add roughly 20–30 ms on M-series hardware. Use `-D` for any compiled production binary.

```sh
# Development - full debug symbols, optimizations on
flarisvm myapp.fls

# Production - strip debug symbols (optimizations are on by default)
flarisvm -D myapp.fls

# Compile for distribution
flarisvm -c myapp.fls myapp.flx -D
```

---

### Dispatch budget summary for common loop patterns

All figures measured with `-D` on M-series hardware. "Overhead" is the number of VM dispatches per iteration that are not the loop body.

| Pattern | Overhead dispatches | Notes |
| --- | --- | --- |
| `iter (i from 0 to N) { ... }` | 2 | fused increment+check + loop-back |
| `while (i < N) { i++; }` | 3 | fused compare + compact increment + loop-back |
| `while (i < N) { i += 1; }` | 3 | fused compare + compound assign + loop-back |
| `while (i < N) { i = i + 1; }` | 5 | same + 2 padding dispatches |
| `while (i < N) { ... }` without `-D` | +1–3 | per body statement, debug tracking |
| `obj.method()` per iteration | - | hash lookup each call |
| `let fn = obj.method; fn()` per iteration | - | direct call; saves the lookup |

The gap between the best and worst row above is 2.5×. On a 10 million-iteration loop that is the difference between 70 ms and 175 ms for a single counter pattern alone.

---

### Worked example: arithmetic benchmark

```js
// Baseline - explicit assignments, no fusion (slowest)
fn slow_sum(n) {
    let sum = 0;
    let i = 0;
    while (i < n) {
        sum = sum + i;   // padded, 3 dispatches
        i = i + 1;       // padded, 3 dispatches
    }
    return sum;
}

// Optimized - compound assign + iter (fastest)
fn fast_sum(n) {
    let sum = 0;
    iter (i from 0 to n) {
        sum += i;        // fused, 1 dispatch
    }
    return sum;
}
```

`fast_sum` inner loop (with `-D`): 3 dispatches per iteration for 2 meaningful operations - fused increment+check, compound assign, loop-back.

---

## R11 - JIT Compilation

On ARM64 and x86-64, Flaris includes a second execution tier: a function-level JIT compiler that translates eligible functions to native machine code. JIT-compiled functions run much faster than the bytecode interpreter for numeric workloads.

JIT is controlled by a single flag, `-j`, which gates the pipeline at both ends:

| Step | Command | Effect |
| --- | --- | --- |
| Compile | `flarisvm -c -j app.fls app.flx` | Generates JIT IR and embeds it in the `.flx` alongside standard bytecode |
| Run | `flarisvm -j -e app.flx` | Compiles the embedded JIT IR to native code on first function load |

Without `-j` at compile time, no JIT IR is written - the `.flx` is standard bytecode only. Without `-j` at run time, any JIT IR in the file is loaded but not compiled; functions run on the bytecode interpreter.

The JIT backend is automatically selected at run time based on the CPU: ARM64 on Apple Silicon / Raspberry Pi / etc., and x86-64 on Linux/macOS/Windows PCs. On other platforms the bytecode interpreter is the only execution tier.

### The JIT boundary - why it exists

The JIT compiler works entirely on **unboxed, statically-typed values**. Inside a `fn hot` function, integers are bare 64-bit integers, floats are bare 64-bit doubles, and typed-array parameters are raw pointers into pre-allocated memory. No garbage-collected objects are created, and no dynamic dispatch occurs.

This means anything that requires the GC heap **cannot** run inside a JIT function:

| Not allowed in `fn hot` | Why |
| --- | --- |
| Array literal `[1, 2, 3]` | Allocates a new heap object |
| String concatenation `s + t` | Allocates a new string object |
| `new Foo(...)` | Calls constructor, allocates object |
| `obj.field` member access | Requires dynamic property lookup |
| Calls to ordinary script functions | May trigger GC, boxing, etc. |
| `try`/`catch`, `yield`, `await` | Require interpreter machinery |

The practical rule: **allocate and assemble data structures in normal interpreter code; pass typed arrays and scalars into `fn hot` functions that do the heavy computation**.

### Structuring code for JIT

The recommended split is:

```flaris
// Interpreter layer: set up data, call hot functions, use results
fn process_signal(raw: array) : array {
    let n: int = len(raw);
    let buf: [float] = Array.Create(n, Type.Float);
    // copy raw values into typed float array
    for (let i: int = 0; i < n; i++) { buf[i] = float(raw[i]); }

    // hand off to JIT - all heavy work happens here
    normalize(buf, n);

    return buf;
}

// JIT layer: typed arrays in, typed scalars/arrays out - no allocation
fn hot normalize(a: [float], n: int) : int {
    let max: float = 0.0;
    let i: int = 0;
    while (i < n) {
        if (a[i] > max) { max = a[i]; }
        i++;
    }
    if (max == 0.0) { return 0; }
    i = 0;
    while (i < n) { a[i] = a[i] / max; i++; }
    return 1;
}
```

Key points:

- The interpreter function creates the `[float]` array and fills it - that allocation lives outside the hot path
- The hot function receives the array as a parameter and works on it in-place - no allocation, no boxing
- Multiple hot functions can call each other directly without re-entering the interpreter (see [JIT fast-call for callbacks](#jit-fast-call-for-callbacks))

### Eligibility and opt-in

A function is **JIT-eligible** (checked statically by the analyzer) when all of the following hold:

- All parameters have explicit type annotations
- The return type is explicitly annotated
- Parameter and return types are: `int`, `float`, `bool`, `char`, or `string`; or typed arrays `[int]`, `[float]`, `[char]`, `[bool]`
- The function body contains **none** of: `try`/`catch`, `yield`, `await`, `new`, member-access (`obj.field`), array literals (`[1, 2, 3]`), object literals (`{k: v}`), string concatenation, or calls to non-`hot` script functions
- `foreach` over a typed-array or `string` parameter/local is allowed; `foreach (v, i in arr)` with an index variable is also supported

Allowed exceptions:

- Calls to `Math.*` and `Char.*` builtins (see tables below) compile to direct native calls
- Calls to other `fn hot` eligible functions compile to direct JIT-to-JIT calls

Being eligible is necessary but not sufficient. To actually have JIT IR emitted, you must also mark the function **`hot`**:

```flaris
fn hot sum_to(n: int) : int {
    let s: int = 0;
    for (let i: int = 1; i <= n; i++) { s = s + i; }
    return s;
}
```

The `hot` keyword is placed immediately after `fn`. Functions that are eligible but not marked `hot` run on the bytecode interpreter. Functions marked `hot` that fail the eligibility check are **silently ignored** - they run on the interpreter with no error or warning. Use `-v` to find out which functions actually passed:

```sh
flarisvm -v myfile.fls
# [JIT] eligible: sum_to (line 20)
# [JIT] eligible: normalize (line 35)
```

If a function you expected to be JIT-compiled does not appear in the `-v` output, look for any of the disqualifiers listed above - string concatenation and member access (`obj.field`) are the most common surprises.

### JIT IR in compiled .flx files

When you compile to a `.flx` bytecode file with `-j`, JIT IR for all `fn hot` eligible functions is embedded alongside the regular bytecode:

```sh
flarisvm -c -j myfile.fls myfile.flx   # compile - embeds JIT IR
flarisvm -j -e myfile.flx              # execute - compiles JIT IR to native on load
flarisvm    -e myfile.flx              # execute - runs bytecode only (JIT IR present but inactive)
```

The JIT IR is architecture-independent. Any platform with a JIT backend can compile it to native code. The `.flx` file format remains fully portable - the JIT IR section is only read into memory when `-j` is active at run time; otherwise it is skipped and functions run on the bytecode interpreter.

### Supported operations

| Category | What is JIT-compiled |
| --- | --- |
| Int arithmetic | `+` `-` `*` `/` `%` on `int` locals |
| Float arithmetic | `+` `-` `*` `/` on `float` locals; unary negation |
| Bitwise / shifts | `&` `\|` `^` `<<` `>>` on `int` |
| Comparisons | `<` `<=` `>` `>=` `==` `!=` (int and float) |
| Logical | `&&` and `\|\|` with short-circuit evaluation |
| Bool operations | `bool` params/return; `true`/`false` literals; bool in `if` |
| Control flow | `if`/`else`, `while`, `for`, `iter`, `foreach`, `return` |
| Switch | `switch` on `int`, `bool`, or `char` expression with literal case values; `break` and fallthrough supported |
| Counter ops | `i++` `i--` `i += n` `i -= n` (also `*=`, `/=`, `%=`, `&=`, `\|=`, `^=`, `<<=`, `>>=`) |
| Type conversions | `float(n)` widens int; `int(x)` truncates float |
| Int array reads | `a[i]` and `a[constant]` where `a: [int]` |
| Int array writes | `a[i] = expr` and `a[constant] = expr` where `a: [int]` |
| Float array reads | `a[i]` and `a[constant]` where `a: [float]` (returns unboxed double) |
| Float array writes | `a[i] = expr` and `a[constant] = expr` where `a: [float]` |
| Char array reads | `a[i]` and `a[constant]` where `a: [char]` (returns unboxed codepoint) |
| Char array writes | `a[i] = expr` and `a[constant] = expr` where `a: [char]` |
| Bool array reads | `a[i]` and `a[constant]` where `a: [bool]` (returns unboxed 0/1) |
| Bool array writes | `a[i] = expr` and `a[constant] = expr` where `a: [bool]` |
| Array returns | Functions may return a typed array (`[int]`, `[float]`, etc.); the array object is passed through unboxed |
| Logical NOT | `!expr` where expr is a bool or bool-typed local |
| String char reads | `s[i]` and `s[constant]` where `s: string` (returns codepoint) |
| String char writes | `s[i] = expr` and `s[constant] = expr` where `s: string` |
| Array length | `len(a)` where `a` is any array or string |
| Foreach - int/char/bool array | `foreach (v in a)` and `foreach (v, i in a)` where `a: [int]`/`[char]`/`[bool]`; element is unboxed |
| Foreach - float array | `foreach (v in a)` where `a: [float]`; element is unboxed double |
| Foreach - string | `foreach (c in s)` where `s: string`; yields unboxed codepoints |
| Math builtins | `Math.Sin`, `Math.Sqrt`, etc. - see table below |
| Char builtins | `Char.IsAlpha`, `Char.ToUpper`, etc. - see table below |
| Math int builtins | `Math.BitCount`, `Math.LeadingZeros`, `Math.TrailingZeros`, `Math.BitLength` |

Out-of-bounds array or string accesses in JIT-compiled functions return `nil` safely.

**`switch` in JIT functions - requirements and limitations:**

- The switch expression must have a static type of `int`, `bool`, or `char`
- All `case` values must be integer, bool, or char literals - computed case expressions disqualify the function
- Multiple values per case use stacked `case` labels: `case 1: case 2: body` (not `case 1, 2:`)
- `break` inside a case body works correctly
- Fallthrough (no `break` between cases) is supported
- Nested `break` (e.g., `break` inside an `if` inside a `case`) is **not** supported in the JIT - it will be emitted but will not break out of the enclosing switch

> **Fiber scheduling:** JIT-compiled functions run atomically - the cooperative fiber scheduler does not yield within a JIT function call. The per-loop quantum checkpoint is a no-op in the JIT backend. If a `fn hot` function runs for a long time, other fibers will not be scheduled until it returns. Design hot functions to do bounded work, or split long computations across multiple calls.
>
> **Operator precedence note:** In Flaris, bitwise `&` has higher precedence than comparison operators (`>=`, `<=`, etc.). Use `&&` (logical AND) to combine comparison results: `x >= lo && x <= hi`.

### Math builtins in hot functions

`fn hot` functions may call the following `Math.*` functions directly. The JIT compiler recognises them by name and emits a native `BLR` call to the corresponding C stdlib function - no interpreter overhead, no boxing:

| Call | C function | Args |
| --- | --- | --- |
| `Math.Sqrt(x)` | `sqrt` | 1 |
| `Math.Sin(x)` | `sin` | 1 |
| `Math.Cos(x)` | `cos` | 1 |
| `Math.Tan(x)` | `tan` | 1 |
| `Math.Asin(x)` | `asin` | 1 |
| `Math.Acos(x)` | `acos` | 1 |
| `Math.Atan(x)` | `atan` | 1 |
| `Math.Atan2(y, x)` | `atan2` | 2 |
| `Math.Pow(x, y)` | `pow` | 2 |
| `Math.Exp(x)` | `exp` | 1 |
| `Math.Expm1(x)` | `expm1` | 1 |
| `Math.Log(x)` | `log` | 1 |
| `Math.Log10(x)` | `log10` | 1 |
| `Math.Log2(x)` | `log2` | 1 |
| `Math.Log1p(x)` | `log1p` | 1 |
| `Math.AbsF(x)` | `fabs` | 1 |
| `Math.Floor(x)` | `floor` | 1 |
| `Math.Ceil(x)` | `ceil` | 1 |
| `Math.Round(x)` | `round` | 1 |
| `Math.Trunc(x)` | `trunc` | 1 |
| `Math.Cbrt(x)` | `cbrt` | 1 |
| `Math.Sinh(x)` | `sinh` | 1 |
| `Math.Cosh(x)` | `cosh` | 1 |
| `Math.Tanh(x)` | `tanh` | 1 |
| `Math.Hypot(x, y)` | `hypot` | 2 |
| `Math.MinF(x, y)` | `fmin` | 2 |
| `Math.MaxF(x, y)` | `fmax` | 2 |
| `Math.IEEERemainder(x, y)` | `remainder` | 2 |
| `Math.CopySign(x, y)` | `copysign` | 2 |
| `Math.Asinh(x)` | `asinh` | 1 |
| `Math.Acosh(x)` | `acosh` | 1 |
| `Math.Atanh(x)` | `atanh` | 1 |

All parameters and the return value must be `float`. The fused multiply-add form `a * b + c` is also detected and emitted as a single `FMADD` instruction.

```js
fn hot gaussian(x: float, mu: float, sigma: float): float {
    let d: float = (x - mu) / sigma;
    return Math.Exp(-0.5 * d * d) / (sigma * Math.Sqrt(6.283185307));
}
```

### Math integer intrinsics in hot functions

The following `Math.*` functions take and return `int` and compile to a single native instruction or a direct C call:

| Call | Description |
| --- | --- |
| `Math.BitCount(n)` | Number of set bits (popcount) |
| `Math.LeadingZeros(n)` | Count of leading zero bits (64-bit) |
| `Math.TrailingZeros(n)` | Count of trailing zero bits (64-bit) |
| `Math.BitLength(n)` | Bit length: floor(log2(abs(n))) + 1, or 0 for 0 |

### Char builtins in hot functions

`fn hot` functions may call the following `Char.*` functions. The JIT compiler emits a direct native call - no boxing, no interpreter:

| Call | Description | Returns |
| --- | --- | --- |
| `Char.IsAlpha(c)` | True if `c` is an alphabetic character | `bool` |
| `Char.IsDigit(c)` | True if `c` is a decimal digit | `bool` |
| `Char.IsAlNum(c)` | True if `c` is alphanumeric | `bool` |
| `Char.IsUpper(c)` | True if `c` is uppercase | `bool` |
| `Char.IsLower(c)` | True if `c` is lowercase | `bool` |
| `Char.IsSpace(c)` | True if `c` is whitespace | `bool` |
| `Char.IsPunct(c)` | True if `c` is punctuation (ASCII) | `bool` |
| `Char.IsXDigit(c)` | True if `c` is a hex digit (ASCII) | `bool` |
| `Char.IsPrint(c)` | True if `c` is printable | `bool` |
| `Char.IsControl(c)` | True if `c` is a control character | `bool` |
| `Char.IsAscii(c)` | True if `c` < 128 | `bool` |
| `Char.IsCased(c)` | True if `c` has case (upper/lower) | `bool` |
| `Char.IsNewline(c)` | True if `c` is a line-ending character | `bool` |
| `Char.ToUpper(c)` | Uppercase mapping of `c` | `char` |
| `Char.ToLower(c)` | Lowercase mapping of `c` | `char` |
| `Char.SwapCase(c)` | Toggle case of `c` | `char` |
| `Char.Utf8Len(c)` | Number of UTF-8 bytes needed to encode `c` | `int` |
| `Char.DigitValue(c)` | Numeric digit value of `c` (0–9, -1 if not a digit) | `int` |

Parameters and return values are unboxed `char` or `int` inside the JIT. The function signature must use `char` or `int` as appropriate.

```js
fn hot count_upper(s: string, n: int): int {
    let count: int = 0;
    let i: int = 0;
    while (i < n) {
        if (Char.IsUpper(s[i])) { count = count + 1; }
        i++;
    }
    return count;
}
```

### JIT fast-call for callbacks

When a `fn hot` JIT-egible and compiled function is passed as a callback to one of the builtins below, the builtin bypasses the bytecode interpreter and invokes the native machine code directly. This eliminates the per-element interpreter overhead for high-throughput loops.

**Array methods** - callback receives the element (and index where noted):

| Method | Callback signature |
| --- | --- |
| `Array.ForEach(arr, fn)` | `fn(elem: T): any` |
| `Array.Map(arr, fn)` | `fn(elem: T): U` |
| `Array.Filter(arr, fn)` | `fn(elem: T): bool` |
| `Array.Reduce(arr, fn, init)` | `fn(acc: T, elem: T): T` |
| `Array.Any(arr, fn)` | `fn(elem: T): bool` |
| `Array.All(arr, fn)` | `fn(elem: T): bool` |
| `Array.Process(arr, fn)` | `fn(elem: T): T` - mutates in place |
| `Array.ProcessIdx(arr, fn)` | `fn(elem: T, idx: int): T` - mutates in place |
| `Array.GroupBy(arr, fn)` | `fn(elem: T): key` |
| `Array.SortBy(arr, fn)` | `fn(elem: T): key` |
| `Array.FlatMap(arr, fn)` | `fn(elem: T): array\|T` |
| `Array.CountBy(arr, fn)` | `fn(elem: T): key` |
| `Array.Partition(arr, fn)` | `fn(elem: T): bool` |
| `Array.MinBy(arr, fn)` | `fn(elem: T): key` |
| `Array.MaxBy(arr, fn)` | `fn(elem: T): key` |
| `Array.UniqueBy(arr, fn)` | `fn(elem: T): key` |
| `Array.Sort(arr, fn)` | `fn(a: T, b: T): bool` - comparator; return true if a < b |

**Other builtins:**

| Method | Callback signature |
| --- | --- |
| `Memory.Process(addr, count, size, fn)` | `fn(val: int, idx: int): int` - rewrites each element |
| `Gfx.Filter(x, y, w, h, fn)` | `fn(pixel: int): int` - rewrites each ARGB pixel |

The fast-call path is selected automatically at runtime when `-j` is active and the passed function was compiled with `fn hot`. If the function is not JIT-compiled (e.g. `-j` is absent, or the function is not marked `hot`), the builtin falls back to the normal interpreter path transparently.

```js
fn hot double_pixel(px: int): int {
    let r: int = (px >> 16) & 0xFF;
    let g: int = (px >> 8)  & 0xFF;
    let b: int =  px        & 0xFF;
    let a: int = (px >> 24) & 0xFF;
    // Brighten RGB channels, clamp to 255
    if (r * 2 > 255) { r = 255; } else { r = r * 2; }
    if (g * 2 > 255) { g = 255; } else { g = g * 2; }
    if (b * 2 > 255) { b = 255; } else { b = b * 2; }
    return (a << 24) | (r << 16) | (g << 8) | b;
}

let img = Gfx.Create(800, 600);
// ...draw something...
img.Filter(0, 0, 800, 600, double_pixel);  // native ARM64 per-pixel loop
```

### Calling convention and memory

JIT-compiled functions share the same call sites as interpreter functions - callers use the normal path. The compiled function pointer is stored and invoked directly once available.

Machine code is allocated with `mmap`/`mprotect` (no entitlement required on macOS).

### Full workflow example

```sh
# 1. Compile with JIT IR embedded
flarisvm -c -j myapp.fls myapp.flx

# 2. Verify which functions are eligible (structural check, -j not required here)
flarisvm -v myapp.fls

# 3. Run with JIT active
flarisvm -j -e myapp.flx

# 4. Run without JIT (fallback to bytecode interpreter, even if JIT IR present)
flarisvm -e myapp.flx

# 5. Source file directly: compile+run in one step with JIT
flarisvm -j myapp.fls
```

### Benchmark reference

| Workload                        | JIT      | Interpreter | Speedup |
|---------------------------------|----------|-------------|---------|
| Gaussian sum to 10 M            | ~9.5 ms  | ~63 ms      | 6.6×    |
| sum_range(10 000) × 10 000 reps | ~54 ms   | ~606 ms     | 11×     |
| count_down(10 000) × 10 000 reps| ~53 ms   | ~954 ms     | 18×     |

Results are wall-clock time on a single fiber. Workloads with tight integer loops show the largest gains; workloads dominated by string or object operations are unaffected.
