Built-in Namespaces
Serez Code ships with built-in namespaces available everywhere without imports. OS/hardware namespaces (Terminal, OS, Env, Time, System) require explicit permission declarations.
Math
All math functions are called as Math.functionName(args):
out Math.PI // → 3.141592653589793
out Math.E // → 2.718281828459045
// Rounding
out Math.floor(3.9) // → 3
out Math.ceil(3.1) // → 4
out Math.round(3.5) // → 4
out Math.trunc(-3.9) // → -3 (toward zero)
// Basic
out Math.abs(-7) // → 7
out Math.sqrt(16.0) // → 4.0
out Math.pow(2.0, 10.0) // → 1024.0
// Min / max / clamp
out Math.min(3, 1, 4, 1, 5) // → 1
out Math.max(3, 1, 4, 1, 5) // → 5
out Math.clamp(15, 0, 10) // → 10
// Logarithms
out Math.log(Math.E) // → 1.0
out Math.log2(8.0) // → 3.0
out Math.log10(1000.0) // → 3.0
// Random — decimal in [0, 1)
out Math.random()Trigonometry
All trig functions use radians:
out Math.sin(Math.PI / 2.0) // → 1.0
out Math.cos(0.0) // → 1.0
out Math.tan(Math.PI / 4.0) // → ~1.0
out Math.atan2(1.0, 1.0) // → 0.785... (π/4)
// Degrees → radians helper
let deg = 90.0
let rad = deg * Math.PI / 180.0
out Math.sin(rad) // → 1.0File I/O
Read and write files with the File namespace:
// Write a file (creates it if it doesn't exist, overwrites if it does)
File.write("data.txt", "Hello, world!")
// Check if it exists
out File.exists("data.txt") // → true
// Read the whole file as a string
let content = File.read("data.txt")
out content // → Hello, world!
// Create an empty file (no-op if it already exists)
File.create("log.txt")Binary files
// Read raw bytes — returns [int] where each value is 0-255
let bytes = File.read_asBinary("image.png")
out bytes.length // number of bytes
// Write raw bytes
File.write_asBinary("copy.png", bytes)Extended file system methods
// List directory contents
let entries = File.listDir("./src")
out entries // → [main.sz, utils.sz, ...]
// Create a directory (including parent dirs)
File.mkdir("output/logs")
// File metadata
let stat = File.stat("data.txt")
out stat.size // → bytes
out stat.isDir // → false
out stat.modified // → Unix ms timestamp
// Rename / move (requires unsafe)
unsafe {
File.rename("old.txt", "new.txt")
}
// Delete file or directory (requires unsafe)
unsafe {
File.delete("temp_dir")
}Practical example — save and load JSON data
let data <string, any> = (
{"name", "Sergio"},
{"score", 42},
{"active", true}
)
// Save
File.write("save.json", JSON.stringify(data))
// Load
let loaded = JSON.parse(File.read("save.json"))
out loaded["name"] // → SergioJSON
Serialize any value to JSON and parse it back:
// Stringify — works with any value
out JSON.stringify(42) // → "42"
out JSON.stringify(true) // → "true"
out JSON.stringify([1, 2, 3]) // → "[1,2,3]"
let user <string, any> = ({"name", "Sergio"}, {"age", 28})
out JSON.stringify(user)
// → {"name":"Sergio","age":28}
// Parse — returns the equivalent Serez value
let json = '{"x": 10, "y": 20}'
let obj = JSON.parse(json)
out obj["x"] // → 10
// Round-trip
let original = [1, "hello", true, null]
let json_str = JSON.stringify(original)
let parsed = JSON.parse(json_str)
out parsed[1] // → helloUse JSON.pretty(value, [indent]) for indented, human-readable output — great for inspecting a fetch response in the console. The indent (spaces per level) defaults to 2; an indent of 0 falls back to compact. If the value is a raw JSON string (such as a fetch body), it is parsed first and then re-indented.
native fn string fetch(string url)
let body = fetch("https://api.example.com/data")
// Pretty-print the raw response body (2-space indent by default)
out JSON.pretty(body)
// → {
// "name": "Sergio",
// "age": 28
// }
out JSON.pretty(body, 4) // 4-space indent
// Works on structured values too
out JSON.pretty(user)Networking (fetch)
fetch is a general-purpose HTTP client. Declare it once as a native fn, then call it. Only http:// and https:// URLs are allowed, and URLs/headers with control characters are rejected (CRLF / header-injection safe).
native fn string fetch(string url)
let body = fetch("https://pokeapi.co/api/v2/pokemon/ditto")
out JSON.pretty(body) // pretty-print the JSON responseSignature: fetch(url, [method], [body], [options]). Arguments after the url are sniffed by type — the first string is the method, the second is the body, and a dict is the options.
Default headers: fetch always sends User-Agent: Serez-Code/<version> unless you set your own (without it, some CDNs/WAFs reply 503). When a body is sent it also defaults Content-Type: application/json unless you set one. Any caller-provided header wins.
// POST with a JSON body (build it with JSON.stringify — no raw braces)
let payload <string, any> = ({"name", "serez"}, {"stars", 10})
let res = fetch("https://example.com/api", "POST", JSON.stringify(payload))
// Custom headers (override the default User-Agent, add auth, ...)
let headers <string, string> = ({"Authorization", "Bearer TOKEN"}, {"User-Agent", "my-app/1.0"})
let opts <string, any> = ({"headers", headers}, {"timeout", 10})
let r = fetch("https://example.com/api", opts)Options dict (a <string, any> dict): headers (a <string, string> dict), timeout (seconds, default 60), full(return a structured response and don't throw on HTTP status), and binary (return the body as a byte array [int] for images/zips/PDFs).
native fn any fetch(string url, any options)
// full mode → { status, ok, statusText, headers, body }, never throws on status
let opts <string, any> = ({"full", true})
let r = fetch("https://pokeapi.co/api/v2/pokemon/ditto", opts)
if (r["ok"] == true) {
out "status " + r["status"] // 200
out JSON.pretty(r["body"]) // headers keyed by lowercased name; missing key → null
}Default mode (no full) returns the body string and throws on status ≥ 400 (the response body is embedded in the error), so wrap calls in try / catch:
try {
let body = fetch("https://pokeapi.co/api/v2/pokemon/ditto")
out body.length()
} catch (e) {
out "request failed: " + e
}Socket (TCP & WebSocket)
Raw TCP client/server sockets over the standard library, plus RFC 6455 WebSocket text frames. These are the low-level networking primitives — for a full HTTP/WebSocket server with routing, use the serez-http package. Requires the Socket permission — declare it in your serez.json or at the top of your script:
// serez.json
{ "permissions": ["Socket"] }// or inline
use permissions { Socket }TCP client
use permissions { Socket }
// Connect, send a request, read the reply
let sock = Socket.connect("example.com", 80) // → socket id (int)
Socket.send(sock, "GET / HTTP/1.0\r\nHost: example.com\r\n\r\n")
let reply = Socket.recv(sock, 4096) // read up to 4096 bytes → string
out reply
Socket.close(sock)TCP server
use permissions { Socket }
// Listen, accept a connection, echo a message back
let server = Socket.listen(8080) // → listener id (int)
let conn = Socket.accept(server) // blocks until a client connects → socket id
let msg = Socket.recv(conn, 1024)
Socket.send(conn, "echo: " + msg)
Socket.close(conn)
Socket.close(server)WebSocket frames
After a connection is established, exchange WebSocket text frames. recvWsFrame returns the decoded payload, or nullon a close frame / end of connection. Frames larger than 16 MiB are rejected (DoS-safe).
Socket.sendWsFrame(conn, "ping") // encode + send a text frame → null
let frame = Socket.recvWsFrame(conn) // → text payload, or null on close
if (frame != null) {
out "received: " + frame
}| Method | Returns | Description |
|---|---|---|
Socket.connect(host, port) | int | Open a TCP connection → socket id |
Socket.send(id, data) | int | Send a string → bytes written |
Socket.recv(id, max_bytes) | string | Read up to max_bytes |
Socket.listen(port) | int | Bind + listen → listener id |
Socket.accept(listener_id) | int | Accept a connection (blocks) → socket id |
Socket.close(id) | null | Close a socket or listener |
Socket.sendWsFrame(id, data) | null | Send a WebSocket text frame |
Socket.recvWsFrame(id) | string | null | Read one WebSocket text frame |
Permissions
OS/hardware namespaces are sandboxed by default. Grant access with a three-level permission model:
// Level 1 — serez.json (project-wide)
// { "permissions": ["Terminal", "OS", "Env"] }
// Level 2 — file-level
use permissions { OS, Time }
// Level 3 — operation-level (unsafe required for destructive ops)
unsafe {
OS.exec("git", ["status"])
File.delete("temp.txt")
}Terminal
Interact with the terminal emulator — keyboard, mouse, cursor, raw mode. Requires use permissions { Terminal }.
use permissions { Terminal }
// Terminal size
let size = Terminal.getSize()
out "Cols: {size[0]}, Rows: {size[1]}"
// Clear screen and move cursor
Terminal.clear()
Terminal.setCursor(0, 0)
// Write raw byte to stdout (e.g. ANSI ESC = 27)
Terminal.writeByte(27)
// Raw mode + keyboard + mouse (all require unsafe)
unsafe {
Terminal.setRawMode(true)
Terminal.enableMouse(true)
let evt = Terminal.readEvent()
if (evt.type == "key") {
out "Key: {evt.code}" // "a", "Enter", "Esc", "F1", ...
out "Mods: {evt.modifiers}" // ["ctrl"], ["shift"], ...
} else if (evt.type == "mouse") {
out "Mouse {evt.kind} at {evt.col},{evt.row}"
out "Button: {evt.button}" // "left", "right", "middle"
} else if (evt.type == "resize") {
out "New size: {evt.cols}x{evt.rows}"
}
Terminal.enableMouse(false)
Terminal.setRawMode(false)
}Autodiff
Reverse-mode automatic differentiation tape. Record operations, run backward, retrieve gradients. All tensor operations performed while the tape is active are tracked automatically.
Tape control
Autodiff.tape() // start recording
Autodiff.backward(loss) // backpropagate from scalar tensor
Autodiff.gradient(tensor) // retrieve gradient tensor
Autodiff.clear() // clear tape and gradients
Autodiff.isRecording() // → boolWeight initialization
// Xavier (Glorot) — good for tanh / sigmoid
let w = Autodiff.xavierUniform([fan_in, fan_out])
let w = Autodiff.xavierNormal([fan_in, fan_out])
// He — good for ReLU networks
let w = Autodiff.heUniform([fan_in, fan_out])
let w = Autodiff.heNormal([fan_in, fan_out])Optimizers
All optimizer steps are pure functions — they return updated parameters without modifying the tape.
// Adam — returns [new_param, new_m, new_v]
let result = Autodiff.adamStep(param, grad, m, v, step, lr)
let result = Autodiff.adamStep(param, grad, m, v, step, lr, beta1, beta2, eps)
// AdamW — Adam with decoupled weight decay
let result = Autodiff.adamwStep(param, grad, m, v, step, lr, wd)
// SGD with momentum — returns [new_param, new_velocity]
let result = Autodiff.sgdStep(param, grad, velocity, lr)
let result = Autodiff.sgdStep(param, grad, velocity, lr, momentum, weight_decay)
// RMSprop — returns [new_param, new_sq_avg]
let result = Autodiff.rmspropStep(param, grad, sq_avg, lr)
let result = Autodiff.rmspropStep(param, grad, sq_avg, lr, alpha, eps)
// Usage pattern:
let w = Autodiff.heNormal([128, 64])
let m = Tensor.zeros([128, 64])
let v = Tensor.zeros([128, 64])
let step = 0
// Training loop:
step++
Autodiff.tape()
let loss = Autodiff.mseLoss(w.matmul(x), target)
Autodiff.backward(loss)
let grad = Autodiff.gradient(w)
let res = Autodiff.adamStep(w, grad, m, v, step, 0.001)
w = res[0]; m = res[1]; v = res[2]Loss functions
// All loss functions are tracked on the tape
let loss = Autodiff.mseLoss(pred, target) // Mean Squared Error
let loss = Autodiff.maeLoss(pred, target) // Mean Absolute Error
let loss = Autodiff.bceLoss(pred, target) // Binary Cross-Entropy (probs in [0,1])
let loss = Autodiff.crossEntropyLoss(logits, idx) // Cross-Entropy (raw logits + class indices)Layers
// BatchNorm — normalizes [N, C] tensor per feature
let out = Autodiff.batchNorm(x, gamma, beta, training)
let out = Autodiff.batchNorm(x, gamma, beta, training, eps)
// Dropout — zeros random activations during training
let out = Autodiff.dropout(x, p) // p = drop probability
let out = Autodiff.dropout(x, p, training) // training=false → no-op
// Embedding — lookup rows from weight matrix
// indices: Array of int or integer Tensor
// weight: [vocab_size, emb_dim] Tensor
// returns: [seq_len, emb_dim] Tensor
let out = Autodiff.embedding(indices, weight)Gradient utilities
// Clip a single gradient tensor by norm
let clipped = Autodiff.clipGrad(grad, max_norm)
// Clip an array of gradients by global norm
let clipped = Autodiff.clipGradNorm([g1, g2, g3], max_norm)
// Detach a tensor from the tape (stop gradient flow)
let detached = Autodiff.stopGrad(tensor)
let detached = Autodiff.detach(tensor)Weight persistence
// Save an array of tensors to a .szw binary file
Autodiff.saveWeights("model.szw", [w1, b1, w2, b2])
// Load tensors back — returns Array in same order
let weights = Autodiff.loadWeights("model.szw")
let w1 = weights[0]
let b1 = weights[1]Tensor
Multi-dimensional arrays with automatic differentiation support. All operations are tracked when a tape is active.
Creation
Tensor.zeros([rows, cols])
Tensor.ones([rows, cols])
Tensor.eye(n) // identity matrix
Tensor.from([[1.0, 2.0], [3.0, 4.0]]) // from a (nested) array
Random.normalTensor([rows, cols], 0.0, 0.1) // gaussian-filledShape manipulation
t.shape() // → [rows, cols, ...]
t.ndim() // → number of dimensions
t.size() // → total number of elements
t.reshape([2,3]) // reshape (same total elements)
t.flatten() // → 1D tensor
t.transpose() // 2D transpose
t.permute([0,2,1]) // N-D generalized transpose
t.unsqueeze(dim) // insert size-1 dim at position
t.squeeze() // remove all size-1 dims
t.squeeze(dim) // remove specific size-1 dimMath operations
// Element-wise (all tracked)
t.add(other) t.sub(other) t.mul(other) t.div(other)
t.neg() t.abs() t.sqrt() t.exp()
t.log() t.pow(exp) t.scale(s)
t.sign() t.reciprocal()
t.sin() t.cos()
t.round() t.floor() t.ceil()
t.clamp(min, max)
t.maximum(other) t.minimum(other)
// Matrix operations (tracked)
t.dot(other) // 1D dot product
t.matmul(other) // 2D matrix multiply
t.outer(other) // outer product
t.bmm(other) // batch matmul [B,N,M] @ [B,M,K] → [B,N,K]
// Reductions
t.sum() t.mean() t.max() t.min()
t.sumAxis(axis) t.meanAxis(axis)
t.reduceSum(axis) t.reduceMean(axis) t.reduceMax(axis)
t.variance() t.std()
t.cumsum() t.norm(order)
// N-D broadcasting
t.broadcastTo([shape]) // expand to target shape
t.broadcastAdd(bias) // 2D + 1D (tracked)
t.broadcastAddNd(other) // N-D broadcast add
t.broadcastMulNd(other) // N-D broadcast multiplyActivation functions (all tracked)
t.relu()
t.sigmoid()
t.tanh()
t.softmax()
t.gelu()
t.leaky_relu(alpha)
t.elu(alpha) // ELU (alpha default 1.0)
t.swish() // swish(x) = x * sigmoid(x)
t.silu() // alias for swish
t.mish() // mish(x) = x * tanh(softplus(x))
t.softplus() // log(1 + exp(x))
t.hardsigmoid() // clamp((x+3)/6, 0, 1)
t.hardswish() // x * hardsigmoid(x)Convolution & pooling (all tracked)
// Input shape: [N, H, W, C_in]
// Weights shape: [C_in * kernel^2, C_out]
t.conv2d(weights, bias, kernel, stride)
t.max_pool2d(kernel, stride)
t.avg_pool2d(kernel, stride) // average poolingRecurrent layers (all tracked)
// LSTM — input [seq_len, input_size], returns [1, hidden_size]
t.lstm(wx, wh, b, h0, c0)
// GRU — input [seq_len, input_size], returns [1, hidden_size]
t.gru(wx, wh, b, h0)
// Multi-head attention — input [seq_len, d_model]
t.mha(wq, wk, wv, wo, n_heads)
// Layer normalization — tracked
t.layer_norm(gamma, beta, eps)Utilities
t.toArray() // convert to serez array
t.toString() // human-readable string
t.get(i, j) // element access
t.set(i, j, val) // element mutation
t.slice(start, end) // flat slice
t.concat(other, axis)
t.argmax() t.argmin()
t.stopGrad() // alias: t.detach() — detach from tapeGPU
CPU-backed compute buffers with a GPU-shaped API. Buffers are flat decimal arrays; the create / upload / dispatch / readback / free pattern mirrors real GPU compute so a future backend can swap in actual GPU calls. Buffers are not garbage-collected — free them with GPU.freeBuffer when done. No permission declaration is required.
// Upload data, run element-wise + reduction, read back
let src = GPU.createBufferFromArray([1.0, 2.0, 3.0, 4.0]) // → buffer id
let doubled = GPU.map(src, x => x * 2.0) // element-wise → new buffer
let sum = GPU.reduce(src, (acc, x) => acc + x, 0.0) // → 10.0
let product = GPU.reduce(src, (acc, x) => acc * x, 1.0) // → 24.0
// Linear algebra
let d = GPU.dot(src, doubled) // dot product → decimal
let r = GPU.axpy(2.0, src, doubled) // 2*src + doubled → new buffer
// Matrix multiply: [2×2] @ [2×2]
let I = GPU.createBufferFromArray([1.0, 0.0, 0.0, 1.0])
let M = GPU.createBufferFromArray([5.0, 6.0, 7.0, 8.0])
let C = GPU.matmul(I, 2, 2, M, 2, 2)
out GPU.readBuffer(C) // → [5.0, 6.0, 7.0, 8.0]
// Always free buffers you created
GPU.freeBuffer(src)
GPU.freeBuffer(doubled)| Method | Returns | Description |
|---|---|---|
GPU.createBuffer(size) | int | Allocate a zero-filled buffer → id |
GPU.createBufferFromArray(arr) | int | Allocate from a Serez array → id |
GPU.readBuffer(id) | [decimal] | Copy a buffer back to a Serez array |
GPU.freeBuffer(id) | null | Release a buffer |
GPU.fill(id, value) | null | Set every element to value |
GPU.size(id) | int | Number of elements |
GPU.map(id, fn) | int | Element-wise fn → new buffer |
GPU.reduce(id, fn, initial) | decimal | Fold over the buffer |
GPU.dot(id_a, id_b) | decimal | Dot product of two buffers |
GPU.axpy(alpha, id_x, id_y) | int | alpha*x + y → new buffer |
GPU.matmul(id_a, ra, ca, id_b, rb, cb) | int | Matrix multiply → new buffer |
OS
Process and operating system information. Requires use permissions { OS }.
use permissions { OS }
out OS.platform() // → "windows" | "linux" | "macos"
out OS.pid() // → current process ID
// Execute external command (requires unsafe)
let result = null
unsafe {
result = OS.exec("git", ["log", "--oneline", "-5"])
}
out result.stdout // command output
out result.code // exit code (0 = success)
// Kill a process by PID (requires unsafe)
unsafe {
OS.kill(1234)
}Env
Environment variables and command-line arguments. Requires use permissions { Env }.
use permissions { Env }
out Env.get("HOME") // → "/Users/sergio" or null if not set
out Env.get("PATH") // → full PATH string
let args = Env.args() // command-line args including program name
out args.length
// Set env var (requires unsafe — not thread-safe)
unsafe {
Env.set("MY_VAR", "hello")
}
out Env.get("MY_VAR") // → helloTime
Timestamps and sleep. Requires use permissions { Time }.
use permissions { Time }
let t1 = Time.now() // Unix timestamp in milliseconds
Time.sleep(500) // pause 500ms
let t2 = Time.now()
out t2 - t1 // → ~500DateTime
Immutable calendar date/time. DateTime.now() / utcNow() read the clock and require use permissions { Time }; from() / fromEpoch() and every field/arithmetic/format operation are pure and need no permission.
let d = DateTime.from(2026, 1, 31, 9, 30, 0)
// fields act as ints, but carry immutable add/reduce/remove
out d.day + 5 // 36
out d.month.add(1) // 2026-02-28T09:30:00 (day clamped to month end)
out d.day.reduce(20) // 2026-01-11T09:30:00
// formatting (moment.js-style; [text] is literal)
out d.format("YYYY-MM-DD HH:mm") // 2026-01-31 09:30
out d.format("D/M/YYYY h:mm A") // 31/1/2026 9:30 AM
out d.weekday // 6 (1=Mon … 7=Sun)
// object-destructuring exposes calendar fields as ints
const {day, month, year} = DateTime.from(2026, 6, 20)
out year + "-" + month + "-" + day // 2026-6-20Members: fields year month day hour minute second ms (each a DateField with .add/.reduce/.remove(n)), read-only weekday dayOfYear daysInMonth, and format(p) toString() iso() timestamp() isLeapYear() isUtc(). Two dates compare by instant.
System
Read-only system information. Requires use permissions { System }.
use permissions { System }
out System.cpuCount() // → 15 (logical cores)
out System.totalMemory() // → 34279034880 (bytes)
out System.freeMemory() // → 13000000000 (bytes)
out System.hostname() // → "DESKTOP-XYZ"
out System.uptime() // → 168517 (seconds since boot)Task (Concurrencia)
Execute background scripts in native threads to prevent blocking the main thread (highly critical to keep GUI apps running smoothly). Requires use permissions { Task }.
use permissions { Task, Time }
// 1. Spawns the worker script in the background
let taskId = Task.run("worker.sz", "Serez Developer")
// 2. Poll for completion (non-blocking)
while (!Task.isDone(taskId)) {
Time.sleep(10)
}
// 3. Retrieve the final result
let result = Task.poll(taskId)
out resultInside the worker script (e.g. worker.sz), use Task.message() to retrieve the input argument, and Task.reply(result) to return the response and terminate the worker:
// worker.sz
use permissions { Task }
let input = Task.message()
let response = "Hello, " + input
Task.reply(response)| Method | Returns | Description |
|---|---|---|
Task.run(script_path, arg) | int | Spawns a background worker thread → task id |
Task.message() | string | (Worker only) Retrieves the input argument |
Task.reply(result) | null | (Worker only) Sends the result and terminates the worker |
Task.isDone(id) | bool | Checks if the task has finished or failed |
Task.poll(id) | string | null | Returns the result if finished, null if running, or error message |
Media (Audio)
Play and control audio files (WAV, MP3, FLAC, Vorbis) asynchronously.Requires use permissions { Media }.
use permissions { Media, Time }
// Play audio asynchronously (returns an audio instance id)
let audioId = Media.playSound("sound.mp3")
// Control volume (scale: 0 to 200, representing 0% to 200%)
Media.setVolume(audioId, 150)
// Check if playing
if (Media.isPlaying(audioId)) {
out "Audio is playing!"
}
// Pause, resume or stop
Media.pause(audioId)
Time.sleep(1000)
Media.resume(audioId)
// Stop a specific instance or stop all audio
Media.stop(audioId)
Media.stopAll()Audio operations can throw catchable errors: IOError if the file is missing, or MediaError if the format is invalid or no audio device is found. Permission denial is fatal (sec_media_no_permission).
| Method | Returns | Description |
|---|---|---|
Media.playSound(path) | int | Starts playing a sound file asynchronously → audio id |
Media.stop(id) | null | Stops the specified audio instance |
Media.stopAll() | null | Stops all playing audio instances |
Media.pause(id) | null | Pauses the specified audio instance |
Media.resume(id) | null | Resumes the specified paused audio instance |
Media.setVolume(id, volume) | null | Sets the volume of the audio instance (0 to 200) |
Media.isPlaying(id) | bool | Checks if the specified audio instance is currently playing |
Media.playingCount() | int | Returns the total number of playing audio instances |