Nitpick Built-in Collections & Data Structures
Nitpick provides several built-in, highly optimized data structures directly mapped as compiler intrinsics. This ensures guaranteed bounds checking, error handling, and deterministic behavior without needing an external standard library.
1. User Stack Builtins
(astack)
A fast, memory-budgeted scope-bound LIFO stack accessed
via opaque handles. Separate from the hardware call stack
and the stack memory allocation keyword.
astack(capacity?)
Initializes a new stack in the current function scope. -
capacity: optional int64, default
256 slots if omitted - Returns an opaque int64
handle representing the stack. - Multiple stacks can be
instantiated in the same scope.
int64:st1 = astack(64i64); // 64-slot stack
int64:st2 = astack(); // 256-slot default
apush(handle, value)
Push a typed value onto the specified stack. - Accepts
standard primitive types and pointers. - Type tag stored
automatically (no manual tag management). - Returns
0 on success, or -1 on
overflow.
drop apush(st1, 42i64);
drop apush(st2, 3.14f64);
apop(handle)
Pop the top value from the specified stack. Destination
type inferred from assignment context. - Returns the value
directly (not wrapped in Result<T>). - On
underflow (empty stack), returns the unknown
sentinel state. Must be checked via ok(). - On
type mismatch, it triggers failsafe()
(e.g. USR_DATA_CORRUPT) rather than blindly
bypassing it.
int64:n = apop(st1);
if (!(n is unknown)) {
n = ok(n);
// safe to use n
}
apeek(handle)
Non-destructive read of the top value. - Same
context-typed semantics and error behavior as
apop(). - Value remains on stack.
int64:top = apeek(st1); // reads top without removing
Auto-Cleanup
Stacks are automatically destroyed on function return (explicit return or fallthrough). No manual cleanup needed.
2. Array List Builtins
(alist)
A fast, memory-budgeted scope-bound dynamic array list accessed via opaque handles.
alist(capacity?)
Initializes a new array list in the current function
scope. - capacity: optional int64,
default 256 slots if omitted - Returns an opaque
int64 handle representing the list.
Modification
alpush(handle, value): Appends to the end. Returns0on success,-1on overflow.alinsert(handle, index, value): Inserts at index. Returns0on success,-1on bounds error or overflow.alset(handle, index, value): Replaces at index. Returns0on success,-1on bounds error.alremove(handle, index): Removes at index. Returns0on success,-1on bounds error.
Retrieval
alpop(handle): Removes and returns the last value. Returnsunknownon underflow.alget(handle, index): Returns the value at index. Returnsunknownon out-of-bounds access.- Type Mismatch: As with
astack, attempting to retrieve with the wrong type triggersfailsafe().
Utility
alsize(handle): Returns the current number of elements in the list.
int64:lst = alist();
drop alpush(lst, 100i32);
int32:val = alget(lst, 0i64);
3. Built-in Hash Table
(ahash)
A fast, memory-budgeted hash table accessed via opaque handles.
int64:ht = ahash(cap);: Creates a table with a byte budgetcap. (0 = unbounded).ahsize(ht): Current size in bytes of all entries.ahget(ht, "key"): Retrieves value at key. Returnsunknownif not found or on error. Type mismatch triggers failsafe!ahset(ht, "key", val): Sets key to value. Returns0on success,-1on overflow.ahcount(ht): Number of keys in the table.ahfits(ht, val): Returns1ifvalfits in remaining capacity,0otherwise.ahtype(ht, "key"): Returns the type tag of the value at key, or-1.
int64:ht = ahash(1024i64);
ahset(ht, "username", "admin");
string:name = ahget(ht, "username");
if (!(name is unknown)) {
name = ok(name);
// safe to use name
}
4.
Generational Handles (Handle<T> and
arena<T>)
Nitpick rejects raw pointer use for dynamic graphs (which lead to Use-After-Free crashes) by enforcing Generational Indexes over Arena Allocators.
Handle<T>is a 12-byte struct containing anindexand agenerationcounter.- When a dynamically-sized
arena<T>grows/reallocates, it automatically increments all generation counters. - Accessing a stale
Handle<T>(e.g.arena.get(h)) returns anERRthrough theResult<T>system rather than causing a segmentation fault.
arena<Node>:my_arena;
Handle<Node>:h = my_arena.alloc(Node{val: 10i32});
// Safely access, resolving to Result<Node>
Result<Node>:n = my_arena.get(h);