Something went wrong. Try again.
A general purpose .. programming language written in zig ijadux2.tngl.sh/Rinnescript
zig language
Something went wrong. Try again.
14 kB · 405 lines
Zig
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406const std = @import("std");
/// Element type of a tensor. Kept minimal for v1: no quantized/bf16 types.pub const DType = enum { f32, f64, i32, i64, u8, boolean,
pub fn size(self: DType) usize { return switch (self) { .f32 => 4, .f64 => 8, .i32 => 4, .i64 => 8, .u8 => 1, .boolean => 1, }; }
pub fn name(self: DType) []const u8 { return switch (self) { .boolean => "bool", else => @tagName(self), }; }};
pub const TensorDevice = enum { cpu, cuda, rocm };
/// Shared owner of one memory-mapped file region. Several zero-copy tensor/// views reference a single mapping; each view's `deinit` drops a reference,/// and the LAST one unmaps the file. The owner itself is allocated from the/// VM's arena allocator, so abandoning views without `deinit` only leaks the/// OS mapping until process exit (never miscounted Zig memory).pub const MapOwner = struct { map: []align(std.heap.page_size_min) u8, refs: usize,
/// Drops one reference; unmaps the file when the last view releases. pub fn release(self: *MapOwner) void { self.refs -= 1; if (self.refs == 0) { // Unreachable on Windows: ai loaders refuse the platform before // any mapping exists. if (@import("builtin").os.tag == .windows) return; std.posix.munmap(self.map); } }};
/// Loader-side descriptor of one tensor discovered in a model file/// (safetensors / GGUF). `off`/`len` are absolute byte ranges within the/// mapped file; the VM turns these into read-only tensor views over a single/// shared MapOwner. `name` and `shape` are owned by the parse allocator and/// transfer to the created TensorObj.pub const TensorView = struct { name: []u8, dtype: DType, shape: []u64, off: u64, len: u64,};
/// Architecture hyperparameters for a RinneScript LLM (GGUF kv values).pub const LlmConfig = struct { dim: u64, hidden_dim: u64, n_layers: u64, n_heads: u64, n_kv_heads: u64, vocab_size: u64, seq_len: u64,
pub fn headSize(self: LlmConfig) u64 { return self.dim / self.n_heads; } pub fn kvDim(self: LlmConfig) u64 { return self.n_kv_heads * self.headSize(); }};
/// One transformer layer's weights (all F32, row-major flat slices).pub const LlmLayerWeights = struct { attn_norm: []f32, // [dim] attn_q: []f32, // [dim * dim] attn_k: []f32, // [kv_dim * dim] attn_v: []f32, // [kv_dim * dim] attn_o: []f32, // [dim * dim] ffn_norm: []f32, // [dim] ffn_gate: []f32, // [hidden_dim * dim] ffn_down: []f32, // [dim * hidden_dim] ffn_up: []f32, // [hidden_dim * dim]};
/// Heap handle for a dynamically loaded shared library (dlopen FFI),/// referenced by `Value.lib_val`. Pointer semantics only — the embedded/// DynLib is not copyable.pub const LibObj = struct { path: []const u8, /// Opaque dlopen() handle (POSIX); closed by dlclose on deinit. handle: ?*anyopaque, dead: bool = false,};
/// TCP listener or connected stream (std.Io.net). `kind` decides which field/// is active; `close` sets `dead`, after which further use is a runtime error.pub const NetObj = struct { kind: enum { listener, stream }, server: ?std.Io.net.Server = null, stream: ?std.Io.net.Stream = null, dead: bool = false,};
/// Heap map (string-keyed dictionary) referenced by `Value.map_val`./// Keys are duplicated into the map; values are dynamic. Arena-owned in/// practice — no deep deinit is provided.pub const MapObj = struct { map: std.StringHashMap(Value), dead: bool = false,
pub fn create(allocator: std.mem.Allocator) !*MapObj { const m = try allocator.create(MapObj); m.* = .{ .map = std.StringHashMap(Value).init(allocator) }; return m; }};
/// Heap LLM object referenced by `Value.llm_val`. Weights are F32 views over/// an mmap'd GGUF file (zero-copy); the KV cache is heap-allocated per model/// and reset at the start of each forward pass. `deinit` is idempotent.pub const LlmModel = struct { cfg: LlmConfig, tok_embd: []f32, // [vocab_size * dim] output_norm: []f32, // [dim] output: []f32, // [vocab_size * dim] layers: []LlmLayerWeights, /// Keeps the mapped weight file alive while any model references it. owner: ?*MapOwner = null, /// KV cache fill level (tokens already processed this forward pass). pos: usize = 0, /// Per-layer caches shaped [seq_len * kv_dim], allocated lazily. key_cache: [][]f32 = &.{}, value_cache: [][]f32 = &.{}, dead: bool = false,};
/// Heap tensor object referenced by `Value.tensor_val`. Storage is contiguous/// row-major; owned buffers come from `@tensor(...)`, while ai-library loaders/// produce read-only views into an mmap'd model file. `deinit` is idempotent:/// after it runs, `dead` is set and any further use is a runtime error.pub const TensorObj = struct { name: []const u8 = "", shape: []u64, dtype: DType, device: TensorDevice, storage: union(enum) { owned: []u8, /// Zero-copy window into `owner.map`; writes are rejected. mapped: struct { owner: *MapOwner, bytes: []u8, }, }, dead: bool = false,
/// Raw byte window holding this tensor's elements. pub fn bytes(self: *const TensorObj) []u8 { return switch (self.storage) { .owned => |b| b, .mapped => |mv| mv.bytes, }; }
/// True when element writes are permitted (owned storage only). pub fn isMutable(self: *const TensorObj) bool { return self.storage == .owned; }
/// Total element count (shape product); errors on overflow. pub fn numel(self: *const TensorObj) !u64 { var n: u64 = 1; for (self.shape) |dim| { n = std.math.mul(u64, n, dim) catch return error.Overflow; } return n; }
/// Decodes element `i` (linear index) into a dynamic Value. pub fn readElem(self: *const TensorObj, i: usize) Value { const buf = self.bytes(); const base = i * self.dtype.size(); return switch (self.dtype) { .f32 => .{ .float_val = @as(f32, @bitCast(std.mem.readInt(u32, buf[base..][0..4], .little))) }, .f64 => .{ .float_val = @bitCast(std.mem.readInt(u64, buf[base..][0..8], .little)) }, .i32 => .{ .int_val = std.mem.readInt(i32, buf[base..][0..4], .little) }, .i64 => .{ .int_val = std.mem.readInt(i64, buf[base..][0..8], .little) }, .u8 => .{ .int_val = buf[base] }, .boolean => .{ .bool_val = buf[base] != 0 }, }; }
pub const WriteError = error{ReadOnlyTensor};
/// Encodes `v` into element `i`, coercing int/float/bool sources. /// Fails on memory-mapped (read-only) tensors; callers translate this /// into a runtime diagnostic naming the tensor. pub fn writeElem(self: *TensorObj, i: usize, v: Value) WriteError!void { if (self.storage == .mapped) return error.ReadOnlyTensor; const buf = self.bytes(); const base = i * self.dtype.size(); switch (self.dtype) { .f32 => { const f: f32 = @floatCast(v.toF64()); std.mem.writeInt(u32, buf[base..][0..4], @bitCast(f), .little); }, .f64 => std.mem.writeInt(u64, buf[base..][0..8], @bitCast(v.toF64()), .little), .i32 => std.mem.writeInt(i32, buf[base..][0..4], lossyI32(v), .little), .i64 => std.mem.writeInt(i64, buf[base..][0..8], lossyI64(v), .little), .u8 => buf[base] = @truncate(@as(u64, @bitCast(lossyI64(v)))), .boolean => buf[base] = if (v.truthy()) 1 else 0, } }
fn lossyI64(v: Value) i64 { return switch (v) { .int_val => |x| x, .float_val => |f| std.math.lossyCast(i64, @trunc(f)), .bool_val => |b| if (b) 1 else 0, else => 0, }; }
fn lossyI32(v: Value) i32 { return @truncate(lossyI64(v)); }};
/// Dynamic value type for the RinneScript bytecode VM.pub const Value = union(enum) { int_val: i64, float_val: f64, str_val: []const u8, bool_val: bool, null_val, void_val, array_val: []Value, tensor_val: *TensorObj, llm_val: *LlmModel, map_val: *MapObj, net_val: *NetObj, lib_val: *LibObj,
pub fn isNumber(self: Value) bool { return self == .int_val or self == .float_val; }
pub fn truthy(self: Value) bool { return switch (self) { .bool_val => |b| b, .int_val => |v| v != 0, .float_val => |v| v != 0, .str_val => |s| s.len > 0, .array_val => |a| a.len > 0, .tensor_val => |t| !t.dead and ((t.numel() catch 0) > 0), .llm_val => |m| !m.dead, .map_val => |mp| !mp.dead and mp.map.count() > 0, .net_val => |n| !n.dead, .lib_val => |l| !l.dead, .null_val, .void_val => false, }; }
/// Deep equality (string and array contents compared by value). pub fn eql(a: Value, b: Value) bool { if (@as(std.meta.Tag(Value), a) != @as(std.meta.Tag(Value), b)) { // int/float numeric cross-comparison if (a.isNumber() and b.isNumber()) { return a.toF64() == b.toF64(); } return false; } return switch (a) { .int_val => |v| v == b.int_val, .float_val => |v| v == b.float_val, .str_val => |s| std.mem.eql(u8, s, b.str_val), .bool_val => |v| v == b.bool_val, .null_val => true, .void_val => true, .tensor_val => |t| t == b.tensor_val, .llm_val => |m| m == b.llm_val, .map_val => |mp| mp == b.map_val, .net_val => |n| n == b.net_val, .lib_val => |l| l == b.lib_val, .array_val => |arr| blk: { if (arr.len != b.array_val.len) break :blk false; for (arr, b.array_val) |x, y| { if (!Value.eql(x, y)) break :blk false; } break :blk true; }, }; }
pub fn toF64(self: Value) f64 { return switch (self) { .int_val => |v| @floatFromInt(v), .float_val => |v| v, .bool_val => |b| if (b) 1.0 else 0.0, else => 0, }; }
pub const FormatError = error{OutOfMemory};
/// Human-readable rendering used by @stdout/@stderr/@log. pub fn format(self: Value, writer: *std.Io.Writer) std.Io.Writer.Error!void { switch (self) { .int_val => |v| try writer.print("{d}", .{v}), .float_val => |v| try writer.print("{d}", .{v}), .str_val => |s| try writer.writeAll(s), .bool_val => |b| try writer.writeAll(if (b) "true" else "false"), .null_val => try writer.writeAll("null"), .void_val => try writer.writeAll("void"), .tensor_val => |t| { if (t.dead) { try writer.writeAll("tensor<dead>"); return; } try writer.print("tensor<{s}[", .{t.dtype.name()}); for (t.shape, 0..) |dim, i| { if (i > 0) try writer.writeAll("x"); try writer.print("{d}", .{dim}); } try writer.writeAll("]>"); }, .llm_val => |m| { if (m.dead) { try writer.writeAll("llm<dead>"); return; } try writer.print("llm<dim={d}, layers={d}>", .{ m.cfg.dim, m.cfg.n_layers }); }, .map_val => |mp| { if (mp.dead) { try writer.writeAll("map<dead>"); return; } try writer.writeAll("{"); var mit = mp.map.iterator(); var mn: usize = 0; while (mit.next()) |e| { if (mn > 0) try writer.writeAll(", "); try writer.print("{s}: ", .{e.key_ptr.*}); try e.value_ptr.format(writer); mn += 1; } try writer.writeAll("}"); }, .net_val => |n| { if (n.dead) { try writer.writeAll("net<closed>"); return; } switch (n.kind) { .listener => { const port = if (n.server) |*srv| srv.socket.address.getPort() else 0; try writer.print("tcp-listener<port={d}>", .{port}); }, .stream => try writer.writeAll("tcp-stream"), } }, .lib_val => |l| { if (l.dead) { try writer.writeAll("lib<dead>"); return; } try writer.print("lib<{s}>", .{l.path}); }, .array_val => |arr| { try writer.writeAll("["); for (arr, 0..) |item, i| { if (i > 0) try writer.writeAll(", "); try item.format(writer); } try writer.writeAll("]"); }, } }
pub fn display(self: Value, allocator: std.mem.Allocator) FormatError![]u8 { var buf: std.Io.Writer.Allocating = .init(allocator); self.format(&buf.writer) catch return error.OutOfMemory; return buf.toOwnedSlice() catch return error.OutOfMemory; }};
test "value equality and truthiness" { try std.testing.expect(Value.eql(.{ .int_val = 1 }, .{ .int_val = 1 })); try std.testing.expect(!Value.eql(.{ .str_val = "a" }, .{ .str_val = "b" })); try std.testing.expect((Value{ .int_val = 0 }).truthy() == false); try std.testing.expect((Value{ .str_val = "x" }).truthy());}