summaryrefslogtreecommitdiff
path: root/src/lua_bridge.zig
diff options
context:
space:
mode:
authorT <t@tjp.lol>2026-05-26 20:14:37 -0600
committerT <t@tjp.lol>2026-05-27 06:26:36 -0600
commit1f0915edbe0213e8bc134922f10933468d35a172 (patch)
tree11834769555c7037f3393ef8d98bf5841846144a /src/lua_bridge.zig
parentb788eb05c6d194b91fdc141b6655e61ccaa76ddb (diff)
finish lua runtime makeover
- new multi-tool registration via ToolSource - thread per source-or-standalone-tool - switched to zig 0.16 Io threading interface - cli: include `luv` package and run concurrent lua tools via libuv - one single long-lived lua_State for the whole cli program
Diffstat (limited to 'src/lua_bridge.zig')
-rw-r--r--src/lua_bridge.zig53
1 files changed, 34 insertions, 19 deletions
diff --git a/src/lua_bridge.zig b/src/lua_bridge.zig
index 3d4a24f..eca5661 100644
--- a/src/lua_bridge.zig
+++ b/src/lua_bridge.zig
@@ -1,7 +1,7 @@
//! Lua C-API bridge for the panto CLI.
//!
-//! Exposes a `panto` global table inside any `lua_State` we construct, with
-//! a single function:
+//! Exposes a `panto` global table inside any `lua_State` we construct,
+//! with a single function:
//!
//! panto.register_tool {
//! name = "...",
@@ -10,26 +10,29 @@
//! handler = function(input) ... end,
//! }
//!
-//! The single-table-argument form is idiomatic Lua "named arguments". It's
-//! also forward-compatible: future optional fields (examples, version, etc.)
-//! can be added without breaking existing extensions.
+//! The single-table-argument form is idiomatic Lua "named arguments".
+//! It's also forward-compatible: future optional fields (examples,
+//! version, etc.) can be added without breaking existing extensions.
//!
-//! Each call records a registration in a Lua-side table at a fixed registry
-//! slot. The Zig side then reads that table to decide what to do with it:
+//! Each call records a registration in a Lua-side table at a fixed
+//! registry slot (`registrations_key`). The Zig side reads that table
+//! to decide what to do with the entries:
//!
-//! - **Discovery** (`harvestRegistrations`): runs an extension script once at
-//! startup to learn the *names*, *descriptions*, and *schemas* of every
-//! tool it declares. The handler functions are discarded — that throwaway
-//! state will be closed immediately.
+//! - The long-lived `LuaRuntime` (`src/lua_runtime.zig`) loads each
+//! extension into a single `lua_State`, then walks the registrations
+//! table once per loaded script (between loads it calls
+//! `resetRegistrations`). For each entry it copies name /
+//! description / schema, `luaL_ref`s the handler function into the
+//! Lua registry, and records the ref under the tool name.
//!
-//! - **Invocation** (`fetchHandler` + `runHandler`): per tool call, we open
-//! a fresh `lua_State`, re-run the script, then look up the handler by
-//! name in the same registry table.
+//! - On dispatch, the runtime spawns a coroutine, pushes the handler
+//! onto it via `lua_rawgeti(LUA_REGISTRYINDEX, ref)`, pushes the
+//! parsed JSON input, and `lua_resume`s. Top-level extension code
+//! never runs again — only handler bodies.
//!
-//! No `lua_State` pooling, no shared mutable state across calls. Every
-//! `LuaTool.invoke` builds and tears down its own state. This is slow per-
-//! call (~ms of Lua startup) but mechanically the simplest model: there is
-//! nothing that can leak between invocations.
+//! This bridge module itself is stateless beyond the public
+//! `registrations_key` address; it cooperates with whatever runtime
+//! owns the `lua_State`.
const std = @import("std");
const Allocator = std.mem.Allocator;
@@ -69,7 +72,10 @@ pub const BridgeError = error{
/// The key under which we stash the registrations table in
/// `LUA_REGISTRYINDEX`. Any unique pointer works — we use the address of a
/// module-level `u8` so multiple states all use the same key value.
-var registrations_key: u8 = 0;
+///
+/// Public so the long-lived runtime can poke at it directly when it
+/// needs to harvest entries.
+pub var registrations_key: u8 = 0;
/// A single declared tool, as harvested from a script's top-level call to
/// `panto.register_tool`. All slices reference Lua-owned strings on the
@@ -103,6 +109,15 @@ pub fn install(L: *c.lua_State) void {
c.lua_setglobal(L, "panto");
}
+/// Replace the registrations table with a fresh empty one. Used by the
+/// long-lived runtime between loading distinct extension scripts so it
+/// can harvest only the registrations made by the script just loaded
+/// (not accumulated from prior loads).
+pub fn resetRegistrations(L: *c.lua_State) void {
+ c.lua_createtable(L, 0, 0);
+ c.lua_rawsetp(L, LUA_REGISTRYINDEX, &registrations_key);
+}
+
/// Load and execute a Lua source file in the given state. The file's
/// top-level code typically calls `panto.register_tool(...)` one or more
/// times, populating the registrations table.