//! The comptime `compiler` library's function bridge — the curated set of the //! compiler's own functions reachable from comptime sx via //! `abi(.zig) extern compiler`. See `current/PLAN-COMPILER-VM.md`. //! //! **This registry IS the safety boundary.** Only the functions registered here //! are bindable from user comptime code; a name not on the export list is //! rejected at declaration (`weldedCompilerFn`), and the interpreter dispatches a //! welded call to the matching Zig handler instead of dlsym. //! //! **Direction note (2026-06-17 pivot).** The byte-weld of TYPES (sx structs whose //! layout was validated to mirror the compiler's Zig records) was stripped — it //! bolted a parallel layout regime + hand-marshaling onto a comptime value model //! that isn't bytes. The replacement is a flat-memory comptime VM where values are //! native bytes, so the compiler-API needs no weld/validation/marshaling (Phase 3 //! of the plan re-homes the type/function exposure on that VM). `intern`/`text_of` //! survive here as the first compiler-call seed: clean scalar host-calls (string in, //! handle out), no weld involved. const std = @import("std"); const types = @import("types.zig"); const interp_mod = @import("interp.zig"); const Value = interp_mod.Value; const Interpreter = interp_mod.Interpreter; const InterpError = interp_mod.InterpError; const StringId = types.StringId; /// The name of the only compiler library. A `fn abi(.zig) extern ` with a /// different `` is rejected — `compiler` is the sole comptime bind source. pub const lib_name = "compiler"; // ── Functions (comptime-only, host-call bridged) ──────────────────────────── /// A welded `compiler` function: dispatched under the comptime interpreter to its /// Zig handler (never dlsym'd). The handler receives the interpreter (for the /// string pool / type table) and the already-evaluated argument `Value`s, and /// returns the result `Value`. pub const FnHandler = *const fn (interp: *Interpreter, args: []const Value) InterpError!Value; pub const BoundFn = struct { sx_name: []const u8, handler: FnHandler, }; /// The compiler-function export list. The `StringId` round-trip readers are the /// seed; the type-table API (lookup / register) is re-homed onto the flat-memory /// VM in Phase 3 of `PLAN-COMPILER-VM.md`. pub const bound_fns = [_]BoundFn{ .{ .sx_name = "intern", .handler = handleIntern }, .{ .sx_name = "text_of", .handler = handleTextOf }, }; /// Look up a compiler function by its sx name. Returns null when the name is not /// on the export list. pub fn findFn(sx_name: []const u8) ?*const BoundFn { for (&bound_fns) |*bf| { if (std.mem.eql(u8, bf.sx_name, sx_name)) return bf; } return null; } /// The comptime type table to intern into: the host's mutable mint target when /// set (the metatype-construction path), else the module's table reached through /// a const-cast — the same access the interp's mint path uses (interp.zig). The /// underlying table is genuinely mutable; the interp merely holds it `const`. fn mintTable(interp: *Interpreter) *types.TypeTable { return interp.mint orelse @constCast(&interp.module.types); } /// `intern(s: string) -> StringId` — intern `s` into the compiler's string pool /// and return its handle. The inverse of `text_of`. fn handleIntern(interp: *Interpreter, args: []const Value) InterpError!Value { if (args.len != 1 or args[0] != .string) return error.TypeError; const id = mintTable(interp).internString(args[0].string); return Value{ .int = @intFromEnum(id) }; } /// `text_of(id: StringId) -> string` — resolve a string handle back to its text. /// The inverse of `intern`. fn handleTextOf(interp: *Interpreter, args: []const Value) InterpError!Value { if (args.len != 1 or args[0] != .int) return error.TypeError; if (args[0].int < 0 or args[0].int > std.math.maxInt(u32)) return error.TypeError; const id: StringId = @enumFromInt(@as(u32, @intCast(args[0].int))); return Value{ .string = interp.module.types.getString(id) }; }