From 17e3fddad0be52e88023bdb01424f8c04e408bd3 Mon Sep 17 00:00:00 2001 From: Nathan Craddock Date: Wed, 24 Jan 2024 22:42:44 -0700 Subject: [PATCH 1/5] Combine Lua 54 and 53 The biggest change is that the Uservalue functions in lua 54 no longer have Index in the name. --- build.zig | 18 +- src/{lib54.zig => lib.zig} | 339 +++++- src/lib53.zig | 2055 ------------------------------------ src/tests.zig | 12 +- 4 files changed, 295 insertions(+), 2129 deletions(-) rename src/{lib54.zig => lib.zig} (89%) delete mode 100644 src/lib53.zig diff --git a/build.zig b/build.zig index ddb23aa..53ebf17 100644 --- a/build.zig +++ b/build.zig @@ -30,14 +30,7 @@ pub fn build(b: *Build) void { // Zig module const ziglua = b.addModule("ziglua", .{ - .root_source_file = switch (lang) { - .lua51 => .{ .path = "src/lib51.zig" }, - .lua52 => .{ .path = "src/lib52.zig" }, - .lua53 => .{ .path = "src/lib53.zig" }, - .lua54 => .{ .path = "src/lib54.zig" }, - .luajit => .{ .path = "src/lib51.zig" }, - .luau => .{ .path = "src/libluau.zig" }, - }, + .root_source_file = .{ .path = "src/lib.zig" }, }); // Expose build configuration to the ziglua module @@ -116,14 +109,7 @@ pub fn build(b: *Build) void { } const docs = b.addTest(.{ - .root_source_file = switch (lang) { - .lua51 => .{ .path = "src/lib51.zig" }, - .lua52 => .{ .path = "src/lib52.zig" }, - .lua53 => .{ .path = "src/lib53.zig" }, - .lua54 => .{ .path = "src/lib54.zig" }, - .luajit => .{ .path = "src/lib51.zig" }, - .luau => .{ .path = "src/libluau.zig" }, - }, + .root_source_file = .{ .path = "src/lib.zig" }, }); docs.root_module.addOptions("config", config); diff --git a/src/lib54.zig b/src/lib.zig similarity index 89% rename from src/lib54.zig rename to src/lib.zig index ddf8466..d890dae 100644 --- a/src/lib54.zig +++ b/src/lib.zig @@ -1,15 +1,15 @@ -//! complete bindings around the Lua C API version 5.4.6 -//! exposes all Lua functionality, with additional Zig helper functions - const std = @import("std"); const c = @cImport({ + @cInclude("luaconf.h"); @cInclude("lua.h"); @cInclude("lualib.h"); @cInclude("lauxlib.h"); }); const config = @import("config"); + +/// Lua language version targeted pub const lang = config.lang; const Allocator = std.mem.Allocator; @@ -58,8 +58,60 @@ pub const CompareOperator = enum(u2) { /// The internal Lua debug structure const Debug = c.lua_Debug; +const DebugInfo53 = struct { + source: [:0]const u8 = undefined, + src_len: usize = 0, + short_src: [c.LUA_IDSIZE:0]u8 = undefined, + + name: ?[:0]const u8 = undefined, + name_what: NameType = undefined, + what: FnType = undefined, + + current_line: ?i32 = null, + first_line_defined: ?i32 = null, + last_line_defined: ?i32 = null, + + num_upvalues: u8 = 0, + num_params: u8 = 0, + + is_vararg: bool = false, + is_tail_call: bool = false, + + private: *anyopaque = undefined, + + pub const NameType = enum { global, local, method, field, upvalue, other }; + + pub const FnType = enum { lua, c, main }; + + pub const Options = packed struct { + @">": bool = false, + f: bool = false, + l: bool = false, + n: bool = false, + S: bool = false, + t: bool = false, + u: bool = false, + L: bool = false, + + fn toString(options: Options) [10:0]u8 { + var str = [_:0]u8{0} ** 10; + var index: u8 = 0; + + inline for (std.meta.fields(Options)) |field| { + if (@field(options, field.name)) { + str[index] = field.name[0]; + index += 1; + } + } + while (index < str.len) : (index += 1) str[index] = 0; + + return str; + } + }; +}; + /// The Lua debug interface structure -pub const DebugInfo = struct { +const DebugInfo54 = struct { source: [:0]const u8 = undefined, src_len: usize = 0, short_src: [c.LUA_IDSIZE:0]u8 = undefined, @@ -115,6 +167,11 @@ pub const DebugInfo = struct { }; }; +pub const DebugInfo = switch (lang) { + .lua54 => DebugInfo54, + else => DebugInfo53, +}; + /// The superset of all errors returned from ziglua pub const Error = error{ /// A generic failure (used when a function can only fail in one way) @@ -129,6 +186,12 @@ pub const Error = error{ MsgHandler, /// A file-releated error File, +} || switch (lang) { + .lua53 => error{ + /// A memory in a __gc metamethod + GCMetaMethod, + }, + else => error{}, }; /// The type of event that triggers a hook @@ -296,6 +359,11 @@ const StatusCode = struct { pub const err_syntax = c.LUA_ERRSYNTAX; pub const err_memory = c.LUA_ERRMEM; pub const err_error = c.LUA_ERRERR; + + pub const err_gcmm = switch (lang) { + .lua53 => c.LUA_ERRGCMM, + else => @compileError("err_gcm not defined"), + }; }; // Only used in loadFileX, so no need to group with Status @@ -308,7 +376,10 @@ pub const Stream = c.luaL_Stream; pub const Unsigned = c.lua_Unsigned; /// The type of warning functions used by Lua to emit warnings -pub const CWarnFn = *const fn (data: ?*anyopaque, msg: [*c]const u8, to_cont: c_int) callconv(.C) void; +pub const CWarnFn = switch (lang) { + .lua54 => *const fn (data: ?*anyopaque, msg: [*c]const u8, to_cont: c_int) callconv(.C) void, + else => @compileError("CWarnFn not defined"), +}; /// The type of the writer function used by `Lua.dump()` pub const CWriterFn = *const fn (state: ?*LuaState, buf: ?*const anyopaque, size: usize, data: ?*anyopaque) callconv(.C) c_int; @@ -433,12 +504,17 @@ pub const Lua = struct { c.lua_close(lua.state); } + fn closeSlot54(lua: *Lua, index: i32) void { + c.lua_closeslot(lua.state, index); + } + /// Close the to-be-closed slot at the given index and set the value to nil /// The index must be the last index previously marked to be closed with toClose /// See https://www.lua.org/manual/5.4/manual.html#lua_closeslot - pub fn closeSlot(lua: *Lua, index: i32) void { - c.lua_closeslot(lua.state, index); - } + pub const closeSlot = switch(lang) { + .lua54 => closeSlot54, + else => @compileError("closeSlot not available"), + }; /// Resets a thread, cleaning its call stack and closing all pending to-be-closed variables. /// Returns a status code: LUA_OK for no errors in the thread, or an error status otherwise. @@ -501,43 +577,70 @@ pub const Lua = struct { /// Perform a full garbage-collection cycle /// See https://www.lua.org/manual/5.4/manual.html#lua_gc pub fn gcCollect(lua: *Lua) void { - _ = c.lua_gc(lua.state, c.LUA_GCCOLLECT); + _ = switch (lang) { + .lua54 => c.lua_gc(lua.state, c.LUA_GCCOLLECT), + else => c.lua_gc(lua.state, c.LUA_GCCOLLECT, 0), + }; } /// Stops the garbage collector /// See https://www.lua.org/manual/5.4/manual.html#lua_gc pub fn gcStop(lua: *Lua) void { - _ = c.lua_gc(lua.state, c.LUA_GCSTOP); + _ = switch (lang) { + .lua54 => c.lua_gc(lua.state, c.LUA_GCSTOP), + else => c.lua_gc(lua.state, c.LUA_GCSTOP, 0), + }; } /// Restarts the garbage collector /// See https://www.lua.org/manual/5.4/manual.html#lua_gc pub fn gcRestart(lua: *Lua) void { - _ = c.lua_gc(lua.state, c.LUA_GCRESTART); + _ = switch (lang) { + .lua54 => c.lua_gc(lua.state, c.LUA_GCRESTART), + else => c.lua_gc(lua.state, c.LUA_GCRESTART, 0), + }; } - /// Performs an incremental step of garbage collection corresponding to the allocation of step_size Kbytes - /// See https://www.lua.org/manual/5.4/manual.html#lua_gc - pub fn gcStep(lua: *Lua, step_size: i32) void { + fn gcStep54(lua: *Lua, step_size: i32) void { _ = c.lua_gc(lua.state, c.LUA_GCSTEP, step_size); } + fn gcStep53(lua: *Lua) void { + _ = c.lua_gc(lua.state, c.LUA_GCSTEP, 0); + } + + /// Performs an incremental step of garbage collection corresponding to the allocation of step_size Kbytes + /// See https://www.lua.org/manual/5.4/manual.html#lua_gc + pub const gcStep = switch (lang) { + .lua54 => gcStep54, + else => gcStep53, + }; + /// Returns the current amount of memory (in Kbytes) in use by Lua /// See https://www.lua.org/manual/5.4/manual.html#lua_gc pub fn gcCount(lua: *Lua) i32 { - return c.lua_gc(lua.state, c.LUA_GCCOUNT); + return switch (lang) { + .lua54 => c.lua_gc(lua.state, c.LUA_GCCOUNT), + else => c.lua_gc(lua.state, c.LUA_GCCOUNT, 0), + }; } /// Returns the remainder of dividing the current amount of bytes of memory in use by Lua by 1024 /// See https://www.lua.org/manual/5.4/manual.html#lua_gc pub fn gcCountB(lua: *Lua) i32 { - return c.lua_gc(lua.state, c.LUA_GCCOUNTB); + return switch (lang) { + .lua54 => c.lua_gc(lua.state, c.LUA_GCCOUNTB), + else => c.lua_gc(lua.state, c.LUA_GCCOUNTB, 0), + }; } /// Returns a boolean that tells whether the garbage collector is running /// See https://www.lua.org/manual/5.4/manual.html#lua_gc pub fn gcIsRunning(lua: *Lua) bool { - return c.lua_gc(lua.state, c.LUA_GCISRUNNING) != 0; + return switch (lang) { + .lua54 => c.lua_gc(lua.state, c.LUA_GCISRUNNING) != 0, + else => c.lua_gc(lua.state, c.LUA_GCISRUNNING, 0) != 0, + }; } /// Changes the collector to incremental mode @@ -554,6 +657,20 @@ pub const Lua = struct { return c.lua_gc(lua.state, c.LUA_GCGEN, minor_mul, major_mul) == c.LUA_GCINC; } + /// Sets `pause` as the new value for the pause of the collector + /// Returns the previous value of the pause + /// See https://www.lua.org/manual/5.3/manual.html#lua_gc + pub fn gcSetPause(lua: *Lua, pause: i32) i32 { + return c.lua_gc(lua.state, c.LUA_GCSETPAUSE, pause); + } + + /// Sets `multiplier` as the new value for the step multiplier of the collector + /// Returns the previous value of the step multiplier + /// See https://www.lua.org/manual/5.3/manual.html#lua_gc + pub fn gcSetStepMul(lua: *Lua, multiplier: i32) i32 { + return c.lua_gc(lua.state, c.LUA_GCSETSTEPMUL, multiplier); + } + /// Returns the memory allocation function of a given state /// If data is not null, it is set to the opaque pointer given when the allocator function was set /// See https://www.lua.org/manual/5.4/manual.html#lua_getallocf @@ -593,16 +710,25 @@ pub const Lua = struct { return @enumFromInt(c.lua_geti(lua.state, index, i)); } - /// Pushes onto the stack the nth user value associated with the full userdata at the given index - /// Returns the type of the pushed value or an error if the userdata does not have that value - /// Pushes nil if the userdata does not have that value - /// See https://www.lua.org/manual/5.4/manual.html#lua_getiuservalue - pub fn getIndexUserValue(lua: *Lua, index: i32, n: i32) !LuaType { + fn getUserValue53(lua: *Lua, index: i32) LuaType { + return @enumFromInt(c.lua_getuservalue(lua.state, index)); + } + + fn getUserValue54(lua: *Lua, index: i32, n: i32) !LuaType { const val_type: LuaType = @enumFromInt(c.lua_getiuservalue(lua.state, index, n)); if (val_type == .none) return error.Fail; return val_type; } + /// Pushes onto the stack the nth user value associated with the full userdata at the given index + /// Returns the type of the pushed value or an error if the userdata does not have that value + /// Pushes nil if the userdata does not have that value + /// See https://www.lua.org/manual/5.4/manual.html#lua_getiuservalue + pub const getUserValue = switch (lang) { + .lua54 => getUserValue54, + else => getUserValue53, + }; + /// If the value at the given index has a metatable, the function pushes that metatable onto the stack /// Otherwise an error is returned /// See https://www.lua.org/manual/5.4/manual.html#lua_getmetatable @@ -733,15 +859,28 @@ pub const Lua = struct { .binary_text => "bt", }; const ret = c.lua_load(lua.state, reader, data, chunk_name.ptr, mode_str.ptr); - switch (ret) { - StatusCode.ok => return, - StatusCode.err_syntax => return error.Syntax, - StatusCode.err_memory => return error.Memory, - // lua_load runs pcall, so can also return any result of a pcall error - StatusCode.err_runtime => return error.Runtime, - StatusCode.err_error => return error.MsgHandler, - else => unreachable, - } + + return switch (lang) { + .lua54 => switch (ret) { + StatusCode.ok => {}, + StatusCode.err_syntax => error.Syntax, + StatusCode.err_memory => error.Memory, + // lua_load runs pcall, so can also return any result of a pcall error + StatusCode.err_runtime => error.Runtime, + StatusCode.err_error => error.MsgHandler, + else => unreachable, + }, + else => switch (ret) { + StatusCode.ok => {}, + StatusCode.err_syntax => error.Syntax, + StatusCode.err_memory => error.Memory, + // lua_load runs pcall, so can also return any result of a pcall error + StatusCode.err_runtime => error.Runtime, + StatusCode.err_error => error.MsgHandler, + StatusCode.err_gcmm => error.GCMetaMethod, + else => unreachable, + }, + }; } /// Creates a new independent state and returns its main thread @@ -769,22 +908,51 @@ pub const Lua = struct { /// This function allocates a new userdata of the given type with user_values associated Lua values. /// Returns a pointer to the Lua-owned data /// See https://www.lua.org/manual/5.4/manual.html#lua_newuserdatauv - pub fn newUserdata(lua: *Lua, comptime T: type, user_values: i32) *T { + fn newUserdata54(lua: *Lua, comptime T: type, user_values: i32) *T { // safe to .? because this function throws a Lua error on out of memory // so the returned pointer should never be null const ptr = c.lua_newuserdatauv(lua.state, @sizeOf(T), user_values).?; return opaqueCast(T, ptr); } + /// This function allocates a new userdata of the given type. + /// Returns a pointer to the Lua-owned data + /// See https://www.lua.org/manual/5.3/manual.html#lua_newuserdata + fn newUserdata53(lua: *Lua, comptime T: type) *T { + // safe to .? because this function throws a Lua error on out of memory + // so the returned pointer should never be null + const ptr = c.lua_newuserdata(lua.state, @sizeOf(T)).?; + return opaqueCast(T, ptr); + } + + pub const newUserdata = switch (lang) { + .lua54 => newUserdata54, + else => newUserdata53, + }; + /// This function creates and pushes a slice of full userdata onto the stack with user_values associated Lua values. /// Returns a slice to the Lua-owned data. /// See https://www.lua.org/manual/5.4/manual.html#lua_newuserdatauv - pub fn newUserdataSlice(lua: *Lua, comptime T: type, size: usize, user_values: i32) []T { + fn newUserdataSlice54(lua: *Lua, comptime T: type, size: usize, user_values: i32) []T { // safe to .? because this function throws a Lua error on out of memory const ptr = c.lua_newuserdatauv(lua.state, @sizeOf(T) * size, user_values).?; return @as([*]T, @ptrCast(@alignCast(ptr)))[0..size]; } + /// This function creates and pushes a slice of full userdata onto the stack. + /// Returns a slice to the Lua-owned data. + /// See https://www.lua.org/manual/5.3/manual.html#lua_newuserdata + fn newUserdataSlice53(lua: *Lua, comptime T: type, size: usize) []T { + // safe to .? because this function throws a Lua error on out of memory + const ptr = c.lua_newuserdata(lua.state, @sizeOf(T) * size).?; + return @as([*]T, @ptrCast(@alignCast(ptr)))[0..size]; + } + + pub const newUserdataSlice = switch (lang) { + .lua54 => newUserdataSlice54, + else => newUserdataSlice53, + }; + /// Pops a key from the stack, and pushes a key-value pair from the table at the given index /// See https://www.lua.org/manual/5.4/manual.html#lua_next pub fn next(lua: *Lua, index: i32) bool { @@ -809,26 +977,48 @@ pub const Lua = struct { // The translate-c version of lua_pcall does not type-check so we must rewrite it // (macros don't always translate well with translate-c) const ret = c.lua_pcallk(lua.state, num_args, num_results, msg_handler, 0, null); - switch (ret) { - StatusCode.ok => return, - StatusCode.err_runtime => return error.Runtime, - StatusCode.err_memory => return error.Memory, - StatusCode.err_error => return error.MsgHandler, - else => unreachable, - } + + return switch (lang) { + .lua54 => switch (ret) { + StatusCode.ok => return, + StatusCode.err_runtime => return error.Runtime, + StatusCode.err_memory => return error.Memory, + StatusCode.err_error => return error.MsgHandler, + else => unreachable, + }, + else => switch (ret) { + StatusCode.ok => return, + StatusCode.err_runtime => return error.Runtime, + StatusCode.err_memory => return error.Memory, + StatusCode.err_error => return error.MsgHandler, + StatusCode.err_gcmm => return error.GCMetaMethod, + else => unreachable, + }, + }; } /// Behaves exactly like `Lua.protectedCall()` except that it allows the called function to yield /// See https://www.lua.org/manual/5.4/manual.html#lua_pcallk pub fn protectedCallCont(lua: *Lua, num_args: i32, num_results: i32, msg_handler: i32, ctx: Context, k: CContFn) !void { const ret = c.lua_pcallk(lua.state, num_args, num_results, msg_handler, ctx, k); - switch (ret) { - StatusCode.ok => return, - StatusCode.err_runtime => return error.Runtime, - StatusCode.err_memory => return error.Memory, - StatusCode.err_error => return error.MsgHandler, - else => unreachable, - } + + return switch (lang) { + .lua54 => switch (ret) { + StatusCode.ok => return, + StatusCode.err_runtime => return error.Runtime, + StatusCode.err_memory => return error.Memory, + StatusCode.err_error => return error.MsgHandler, + else => unreachable, + }, + else => switch (ret) { + StatusCode.ok => return, + StatusCode.err_runtime => return error.Runtime, + StatusCode.err_memory => return error.Memory, + StatusCode.err_error => return error.MsgHandler, + StatusCode.err_gcmm => return error.GCMetaMethod, + else => unreachable, + }, + }; } /// Pops `n` elements from the top of the stack @@ -1013,9 +1203,23 @@ pub const Lua = struct { return lua.closeThread(null); } + /// Starts and resumes a coroutine in the given thread + /// See https://www.lua.org/manual/5.3/manual.html#lua_resume + fn resumeThread53(lua: *Lua, from: ?Lua, num_args: i32) !ResumeStatus { + const from_state = if (from) |from_val| from_val.state else null; + const thread_status = c.lua_resume(lua.state, from_state, num_args); + switch (thread_status) { + StatusCode.err_runtime => return error.Runtime, + StatusCode.err_memory => return error.Memory, + StatusCode.err_error => return error.MsgHandler, + StatusCode.err_gcmm => return error.GCMetaMethod, + else => return @enumFromInt(thread_status), + } + } + /// Starts and resumes a coroutine in the given thread /// See https://www.lua.org/manual/5.4/manual.html#lua_resume - pub fn resumeThread(lua: *Lua, from: ?Lua, num_args: i32, num_results: *i32) !ResumeStatus { + fn resumeThread54(lua: *Lua, from: ?Lua, num_args: i32, num_results: *i32) !ResumeStatus { const from_state = if (from) |from_val| from_val.state else null; const thread_status = c.lua_resume(lua.state, from_state, num_args, num_results); switch (thread_status) { @@ -1026,6 +1230,11 @@ pub const Lua = struct { } } + pub const resumeThread = switch (lang) { + .lua54 => resumeThread54, + else => resumeThread53, + }; + /// Rotates the stack elements between the valid `index` and the top of the stack /// The elements are rotated `n` positions in the direction of the top for positive `n`, /// and `n` positions in the direction of the bottom for negative `n` @@ -1060,14 +1269,27 @@ pub const Lua = struct { c.lua_seti(lua.state, index, n); } + /// Pops a value from the stack and sets it as the user value associated to + /// the full userdata at the given index + /// Returns an error if the userdata does not have that value + /// See https://www.lua.org/manual/5.3/manual.html#lua_setuservalue + fn setUserValue53(lua: *Lua, index: i32) !void { + c.lua_setuservalue(lua.state, index); + } + /// Pops a value from the stack and sets it as the new `n`th user value associated to /// the full userdata at the given index /// Returns an error if the userdata does not have that value /// See https://www.lua.org/manual/5.4/manual.html#lua_setiuservalue - pub fn setIndexUserValue(lua: *Lua, index: i32, n: i32) !void { + fn setUserValue54(lua: *Lua, index: i32, n: i32) !void { if (c.lua_setiuservalue(lua.state, index, n) == 0) return error.Fail; } + pub const setUserValue = switch (lang) { + .lua54 => setUserValue54, + else => setUserValue53, + }; + /// Pops a table or nil from the stack and sets that value as the new metatable for the /// value at the given `index` /// See https://www.lua.org/manual/5.4/manual.html#lua_setmetatable @@ -1230,12 +1452,25 @@ pub const Lua = struct { return c.lua_upvalueindex(i); } + /// Returns the version number of this core + /// When `caller_version` is true it returns the address of the version running the call + /// See https://www.lua.org/manual/5.3/manual.html#lua_version + fn version53(lua: *Lua, caller_version: bool) *const Number { + if (caller_version) return c.lua_version(null); + return c.lua_version(lua.state); + } + /// Returns the version number of this core /// See https://www.lua.org/manual/5.4/manual.html#lua_version - pub fn version(lua: *Lua) Number { + fn version54(lua: *Lua) Number { return c.lua_version(lua.state); } + pub const version = switch (lang) { + .lua54 => version54, + else => version53, + }; + /// Emits a warning with the given `msg` /// A message with `to_cont` as true should be continued in a subsequent call to the function /// See https://www.lua.org/manual/5.4/manual.html#lua_warning @@ -1318,7 +1553,7 @@ pub const Lua = struct { unreachable; }; } - if (options.r) { + if (lang == .lua54 and options.r) { info.first_transfer = ar.ftransfer; info.num_transfer = ar.ntransfer; } @@ -1841,7 +2076,7 @@ pub const Lua = struct { /// to expose to the global table rather than all of them /// See https://www.lua.org/manual/5.4/manual.html#luaL_openlibs pub fn open(lua: *Lua, libs: Libs) void { - if (libs.base) lua.requireF(c.LUA_GNAME, c.luaopen_base, true); + if (libs.base) lua.requireF("_G", c.luaopen_base, true); if (libs.coroutine) lua.requireF(c.LUA_COLIBNAME, c.luaopen_coroutine, true); if (libs.package) lua.requireF(c.LUA_LOADLIBNAME, c.luaopen_package, true); if (libs.string) lua.requireF(c.LUA_STRLIBNAME, c.luaopen_string, true); diff --git a/src/lib53.zig b/src/lib53.zig deleted file mode 100644 index 4cbed3b..0000000 --- a/src/lib53.zig +++ /dev/null @@ -1,2055 +0,0 @@ -//! complete bindings around the Lua C API version 5.3.6 -//! exposes all Lua functionality, with additional Zig helper functions - -const std = @import("std"); - -const c = @cImport({ - @cInclude("lua.h"); - @cInclude("lualib.h"); - @cInclude("lauxlib.h"); -}); - -const config = @import("config"); -pub const lang = config.lang; - -const Allocator = std.mem.Allocator; - -// Types -// -// Lua constants and types are declared below in alphabetical order -// For constants that have a logical grouping (like Operators), Zig enums are used for type safety - -/// The type of function that Lua uses for all internal allocations and frees -/// `data` is an opaque pointer to any data (the allocator), `ptr` is a pointer to the block being alloced/realloced/freed -/// `osize` is the original size or a code, and `nsize` is the new size -/// -/// See https://www.lua.org/manual/5.3/manual.html#lua_Alloc for more details -pub const AllocFn = *const fn (data: ?*anyopaque, ptr: ?*anyopaque, osize: usize, nsize: usize) callconv(.C) ?*anyopaque; - -/// Operations supported by `Lua.arith()` -pub const ArithOperator = enum(u4) { - add = c.LUA_OPADD, - sub = c.LUA_OPSUB, - mul = c.LUA_OPMUL, - div = c.LUA_OPDIV, - int_div = c.LUA_OPIDIV, - mod = c.LUA_OPMOD, - pow = c.LUA_OPPOW, - negate = c.LUA_OPUNM, - bnot = c.LUA_OPBNOT, - band = c.LUA_OPBAND, - bor = c.LUA_OPBOR, - bxor = c.LUA_OPBXOR, - shl = c.LUA_OPSHL, - shr = c.LUA_OPSHR, -}; - -/// Type for C functions -/// See https://www.lua.org/manual/5.3/manual.html#lua_CFunction for the protocol -pub const CFn = *const fn (state: ?*LuaState) callconv(.C) c_int; - -/// Operations supported by `Lua.compare()` -pub const CompareOperator = enum(u2) { - eq = c.LUA_OPEQ, - lt = c.LUA_OPLT, - le = c.LUA_OPLE, -}; - -/// The internal Lua debug structure -const Debug = c.lua_Debug; - -/// The Lua debug interface structure -pub const DebugInfo = struct { - source: [:0]const u8 = undefined, - src_len: usize = 0, - short_src: [c.LUA_IDSIZE:0]u8 = undefined, - - name: ?[:0]const u8 = undefined, - name_what: NameType = undefined, - what: FnType = undefined, - - current_line: ?i32 = null, - first_line_defined: ?i32 = null, - last_line_defined: ?i32 = null, - - num_upvalues: u8 = 0, - num_params: u8 = 0, - - is_vararg: bool = false, - is_tail_call: bool = false, - - private: *anyopaque = undefined, - - pub const NameType = enum { global, local, method, field, upvalue, other }; - - pub const FnType = enum { lua, c, main }; - - pub const Options = packed struct { - @">": bool = false, - f: bool = false, - l: bool = false, - n: bool = false, - S: bool = false, - t: bool = false, - u: bool = false, - L: bool = false, - - fn toString(options: Options) [10:0]u8 { - var str = [_:0]u8{0} ** 10; - var index: u8 = 0; - - inline for (std.meta.fields(Options)) |field| { - if (@field(options, field.name)) { - str[index] = field.name[0]; - index += 1; - } - } - while (index < str.len) : (index += 1) str[index] = 0; - - return str; - } - }; -}; - -/// The superset of all errors returned from ziglua -pub const Error = error{ - /// A generic failure (used when a function can only fail in one way) - Fail, - /// A runtime error - Runtime, - /// A syntax error during precompilation - Syntax, - /// A memory allocation error - Memory, - /// A memory in a __gc metamethod - GCMetaMethod, - /// An error while running the message handler - MsgHandler, - /// A file-releated error - File, -}; - -/// The type of event that triggers a hook -pub const Event = enum(u3) { - call = c.LUA_HOOKCALL, - ret = c.LUA_HOOKRET, - line = c.LUA_HOOKLINE, - count = c.LUA_HOOKCOUNT, - tail_call = c.LUA_HOOKTAILCALL, -}; - -/// Type for arrays of functions to be registered -pub const FnReg = struct { - name: [:0]const u8, - func: ?CFn, -}; - -/// Type for debugging hook functions -pub const CHookFn = *const fn (state: ?*LuaState, ar: ?*Debug) callconv(.C) void; - -/// Specifies on which events the hook will be called -pub const HookMask = packed struct { - call: bool = false, - ret: bool = false, - line: bool = false, - count: bool = false, - - /// Converts a HookMask to an integer bitmask - pub fn toInt(mask: HookMask) i32 { - var bitmask: i32 = 0; - if (mask.call) bitmask |= mask_call; - if (mask.ret) bitmask |= mask_ret; - if (mask.line) bitmask |= mask_line; - if (mask.count) bitmask |= mask_count; - return bitmask; - } - - /// Converts an integer bitmask into a HookMask - pub fn fromInt(mask: i32) HookMask { - return .{ - .call = (mask & mask_call) != 0, - .ret = (mask & mask_ret) != 0, - .line = (mask & mask_line) != 0, - .count = (mask & mask_count) != 0, - }; - } -}; - -/// Hook event codes -pub const hook_call = c.LUA_HOOKCALL; -pub const hook_count = c.LUA_HOOKCOUNT; -pub const hook_line = c.LUA_HOOKLINE; -pub const hook_ret = c.LUA_HOOKRET; -pub const hook_tail_call = c.LUA_HOOKTAILCALL; - -/// Type of integers in Lua (typically an i64) -pub const Integer = c.lua_Integer; - -/// Type for continuation-function contexts (usually isize) -pub const Context = isize; - -/// Type for continuation functions -pub const CContFn = *const fn (state: ?*LuaState, status: c_int, ctx: Context) callconv(.C) c_int; - -/// Bitflag for the Lua standard libraries -pub const Libs = packed struct { - base: bool = false, - coroutine: bool = false, - package: bool = false, - string: bool = false, - utf8: bool = false, - table: bool = false, - math: bool = false, - io: bool = false, - os: bool = false, - debug: bool = false, -}; - -/// The type of the opaque structure that points to a thread and the state of a Lua interpreter -pub const LuaState = c.lua_State; - -/// Lua types -/// Must be a signed integer because LuaType.none is -1 -pub const LuaType = enum(i5) { - none = c.LUA_TNONE, - nil = c.LUA_TNIL, - boolean = c.LUA_TBOOLEAN, - light_userdata = c.LUA_TLIGHTUSERDATA, - number = c.LUA_TNUMBER, - string = c.LUA_TSTRING, - table = c.LUA_TTABLE, - function = c.LUA_TFUNCTION, - userdata = c.LUA_TUSERDATA, - thread = c.LUA_TTHREAD, -}; - -/// Modes used for `Lua.load()` -pub const Mode = enum(u2) { binary, text, binary_text }; - -/// Event masks -pub const mask_call = c.LUA_MASKCALL; -pub const mask_count = c.LUA_MASKCOUNT; -pub const mask_line = c.LUA_MASKLINE; -pub const mask_ret = c.LUA_MASKRET; - -/// The maximum integer value that `Integer` can store -pub const max_integer = c.LUA_MAXINTEGER; - -/// The minimum integer value that `Integer` can store -pub const min_integer = c.LUA_MININTEGER; - -/// The minimum Lua stack available to a function -pub const min_stack = c.LUA_MINSTACK; - -/// Option for multiple returns in `Lua.protectedCall()` and `Lua.call()` -pub const mult_return = c.LUA_MULTRET; - -/// Type of floats in Lua (typically an f64) -pub const Number = c.lua_Number; - -/// The type of the reader function used by `Lua.load()` -pub const CReaderFn = *const fn (state: ?*LuaState, data: ?*anyopaque, size: [*c]usize) callconv(.C) [*c]const u8; - -/// The possible status of a call to `Lua.resumeThread` -pub const ResumeStatus = enum(u1) { - ok = StatusCode.ok, - yield = StatusCode.yield, -}; - -/// Reference constants -pub const ref_nil = c.LUA_REFNIL; -pub const ref_no = c.LUA_NOREF; - -/// Index of the regsitry in the stack (pseudo-index) -pub const registry_index = c.LUA_REGISTRYINDEX; - -/// Index of globals in the registry -pub const ridx_globals = c.LUA_RIDX_GLOBALS; - -/// Index of the main thread in the registry -pub const ridx_mainthread = c.LUA_RIDX_MAINTHREAD; - -/// Status that a thread can be in -/// Usually errors are reported by a Zig error rather than a status enum value -pub const Status = enum(u3) { - ok = StatusCode.ok, - yield = StatusCode.yield, - err_runtime = StatusCode.err_runtime, - err_syntax = StatusCode.err_syntax, - err_memory = StatusCode.err_memory, - err_error = StatusCode.err_error, -}; - -/// Status codes -/// Not public, because typically Status.ok is returned from a function implicitly; -/// Any function that returns an error usually returns a Zig error, and a void return -/// is an implicit Status.ok. -/// In the rare case that the status code is required from a function, an enum is -/// used for that specific function's return type -const StatusCode = struct { - pub const ok = c.LUA_OK; - pub const yield = c.LUA_YIELD; - pub const err_runtime = c.LUA_ERRRUN; - pub const err_syntax = c.LUA_ERRSYNTAX; - pub const err_memory = c.LUA_ERRMEM; - pub const err_gcmm = c.LUA_ERRGCMM; - pub const err_error = c.LUA_ERRERR; -}; - -// Only used in loadFileX, so no need to group with Status -pub const err_file = c.LUA_ERRFILE; - -/// The standard representation for file handles used by the standard IO library -pub const Stream = c.luaL_Stream; - -/// The unsigned version of Integer -pub const Unsigned = c.lua_Unsigned; - -/// The type of the writer function used by `Lua.dump()` -pub const CWriterFn = *const fn (state: ?*LuaState, buf: ?*const anyopaque, size: usize, data: ?*anyopaque) callconv(.C) c_int; - -/// A Zig wrapper around the Lua C API -/// Represents a Lua state or thread and contains the entire state of the Lua interpreter -pub const Lua = struct { - state: *LuaState, - - const alignment = @alignOf(std.c.max_align_t); - - /// Allows Lua to allocate memory using a Zig allocator passed in via data. - /// See https://www.lua.org/manual/5.3/manual.html#lua_Alloc for more details - fn alloc(data: ?*anyopaque, ptr: ?*anyopaque, osize: usize, nsize: usize) callconv(.C) ?*align(alignment) anyopaque { - // just like malloc() returns a pointer "which is suitably aligned for any built-in type", - // the memory allocated by this function should also be aligned for any type that Lua may - // desire to allocate. use the largest alignment for the target - const allocator_ptr = opaqueCast(Allocator, data.?); - - if (@as(?[*]align(alignment) u8, @ptrCast(@alignCast(ptr)))) |prev_ptr| { - const prev_slice = prev_ptr[0..osize]; - - // when nsize is zero the allocator must behave like free and return null - if (nsize == 0) { - allocator_ptr.free(prev_slice); - return null; - } - - // when nsize is not zero the allocator must behave like realloc - const new_ptr = allocator_ptr.realloc(prev_slice, nsize) catch return null; - return new_ptr.ptr; - } else if (nsize == 0) { - return null; - } else { - // ptr is null, allocate a new block of memory - const new_ptr = allocator_ptr.alignedAlloc(u8, alignment, nsize) catch return null; - return new_ptr.ptr; - } - } - - /// Initialize a Lua state with the given allocator - /// See https://www.lua.org/manual/5.3/manual.html#lua_newstate - pub fn init(allocator_ptr: *const Allocator) !Lua { - // @constCast() is safe here because Lua does not mutate the pointer internally - const state = c.lua_newstate(alloc, @constCast(allocator_ptr)) orelse return error.Memory; - return Lua{ .state = state }; - } - - /// Deinitialize a Lua state and free all memory - pub fn deinit(lua: *Lua) void { - lua.close(); - } - - /// Returns the std.mem.Allocator used to initialize this Lua state - /// - /// This function is not safe to use on Lua states created without a Zig allocator. - /// If the user data passed to Lua was null, this function will panic. Otherwise use - /// of the returned Allocator is undefined and will likely cause a segfault. - pub fn allocator(lua: *Lua) Allocator { - var data: ?*Allocator = undefined; - _ = lua.getAllocFn(@ptrCast(&data)); - - if (data) |allocator_ptr| { - // Although the Allocator is passed to Lua as a pointer, return a - // copy to make use more convenient. - return allocator_ptr.*; - } - - @panic("Lua.allocator() invalid on Lua states created without a Zig allocator"); - } - - // Library functions - // - // Library functions are included in alphabetical order. - // Each is kept similar to the original C API function while also making it easy to use from Zig - - /// Returns the acceptable index index converted into an equivalent absolute index - /// See https://www.lua.org/manual/5.3/manual.html#lua_absindex - pub fn absIndex(lua: *Lua, index: i32) i32 { - return c.lua_absindex(lua.state, index); - } - - /// Performs an arithmetic or bitwise operation over the value(s) at the top of the stack, - /// with the value at the top being the second operand. Pushes the result of the operation. - /// This function follows the semantics of the corresponding Lua operator and may call metamethods - /// See https://www.lua.org/manual/5.3/manual.html#lua_arith - pub fn arith(lua: *Lua, op: ArithOperator) void { - c.lua_arith(lua.state, @intFromEnum(op)); - } - - /// Sets a new panic function and returns the old one - /// See https://www.lua.org/manual/5.3/manual.html#lua_atpanic - pub fn atPanic(lua: *Lua, panic_fn: CFn) ?CFn { - return c.lua_atpanic(lua.state, panic_fn); - } - - /// Calls a function (or any callable value) - /// First push the function to be called onto the stack. Then push any arguments onto the stack. - /// Then call this function. All arguments and the function value are popped, and any results - /// are pushed onto the stack. - /// See https://www.lua.org/manual/5.3/manual.html#lua_call - pub fn call(lua: *Lua, num_args: i32, num_results: i32) void { - lua.callCont(num_args, num_results, 0, null); - } - - /// Like call, but allows the called function to yield - /// See https://www.lua.org/manual/5.3/manual.html#lua_callk - pub fn callCont(lua: *Lua, num_args: i32, num_results: i32, ctx: Context, k: ?CContFn) void { - c.lua_callk(lua.state, num_args, num_results, ctx, k); - } - - /// Ensures that the stack has space for at least n extra arguments - /// Returns an error if more stack space cannot be allocated - /// Never shrinks the stack - /// See https://www.lua.org/manual/5.3/manual.html#lua_checkstack - pub fn checkStack(lua: *Lua, n: i32) !void { - if (c.lua_checkstack(lua.state, n) == 0) return error.Fail; - } - - /// Release all Lua objects in the state and free all dynamic memory - /// See https://www.lua.org/manual/5.3/manual.html#lua_close - pub fn close(lua: *Lua) void { - c.lua_close(lua.state); - } - - /// Compares two Lua values - /// Returns true if the value at index1 satisisfies the comparison with the value at index2 - /// Returns false otherwise, or if any index is not valid - /// See https://www.lua.org/manual/5.3/manual.html#lua_compare - pub fn compare(lua: *Lua, index1: i32, index2: i32, op: CompareOperator) bool { - return c.lua_compare(lua.state, index1, index2, @intFromEnum(op)) != 0; - } - - /// Concatenates the n values at the top of the stack, pops them, and leaves the result at the top - /// If the number of values is 1, the result is a single value on the stack (nothing changes) - /// If the number of values is 0, the result is the empty string - /// See https://www.lua.org/manual/5.3/manual.html#lua_concat - pub fn concat(lua: *Lua, n: i32) void { - c.lua_concat(lua.state, n); - } - - /// Copies the element at from_index to the valid index to_index, replacing the value at that position - /// See https://www.lua.org/manual/5.3/manual.html#lua_copy - pub fn copy(lua: *Lua, from_index: i32, to_index: i32) void { - c.lua_copy(lua.state, from_index, to_index); - } - - /// Creates a new empty table and pushes onto the stack - /// num_arr is a hint for how many elements the table will have as a sequence - /// num_rec is a hint for how many other elements the table will have - /// Lua may preallocate memory for the table based on the hints - /// See https://www.lua.org/manual/5.3/manual.html#lua_createtable - pub fn createTable(lua: *Lua, num_arr: i32, num_rec: i32) void { - c.lua_createtable(lua.state, num_arr, num_rec); - } - - /// Dumps a function as a binary chunk - /// Data is a pointer passed to the writer function - /// Returns an error if writing was unsuccessful - /// See https://www.lua.org/manual/5.3/manual.html#lua_dump - pub fn dump(lua: *Lua, writer: CWriterFn, data: *anyopaque, strip: bool) !void { - if (c.lua_dump(lua.state, writer, data, @intFromBool(strip)) != 0) return error.Fail; - } - - /// Raises a Lua error using the value at the top of the stack as the error object - /// Does a longjump and therefore never returns - /// See https://www.lua.org/manual/5.3/manual.html#lua_error - pub fn raiseError(lua: *Lua) noreturn { - _ = c.lua_error(lua.state); - unreachable; - } - - /// Perform a full garbage-collection cycle - /// See https://www.lua.org/manual/5.3/manual.html#lua_gc - pub fn gcCollect(lua: *Lua) void { - _ = c.lua_gc(lua.state, c.LUA_GCCOLLECT, 0); - } - - /// Stops the garbage collector - /// See https://www.lua.org/manual/5.3/manual.html#lua_gc - pub fn gcStop(lua: *Lua) void { - _ = c.lua_gc(lua.state, c.LUA_GCSTOP, 0); - } - - /// Restarts the garbage collector - /// See https://www.lua.org/manual/5.3/manual.html#lua_gc - pub fn gcRestart(lua: *Lua) void { - _ = c.lua_gc(lua.state, c.LUA_GCRESTART, 0); - } - - /// Performs an incremental step of garbage collection corresponding to the allocation of step_size Kbytes - /// See https://www.lua.org/manual/5.3/manual.html#lua_gc - pub fn gcStep(lua: *Lua) void { - _ = c.lua_gc(lua.state, c.LUA_GCSTEP, 0); - } - - /// Returns the current amount of memory (in Kbytes) in use by Lua - /// See https://www.lua.org/manual/5.3/manual.html#lua_gc - pub fn gcCount(lua: *Lua) i32 { - return c.lua_gc(lua.state, c.LUA_GCCOUNT, 0); - } - - /// Returns the remainder of dividing the current amount of bytes of memory in use by Lua by 1024 - /// See https://www.lua.org/manual/5.3/manual.html#lua_gc - pub fn gcCountB(lua: *Lua) i32 { - return c.lua_gc(lua.state, c.LUA_GCCOUNTB, 0); - } - - /// Returns a boolean that tells whether the garbage collector is running - /// See https://www.lua.org/manual/5.3/manual.html#lua_gc - pub fn gcIsRunning(lua: *Lua) bool { - return c.lua_gc(lua.state, c.LUA_GCISRUNNING, 0) != 0; - } - - /// Sets `pause` as the new value for the pause of the collector - /// Returns the previous value of the pause - /// See https://www.lua.org/manual/5.3/manual.html#lua_gc - pub fn gcSetPause(lua: *Lua, pause: i32) i32 { - return c.lua_gc(lua.state, c.LUA_GCSETPAUSE, pause); - } - - /// Sets `multiplier` as the new value for the step multiplier of the collector - /// Returns the previous value of the step multiplier - /// See https://www.lua.org/manual/5.3/manual.html#lua_gc - pub fn gcSetStepMul(lua: *Lua, multiplier: i32) i32 { - return c.lua_gc(lua.state, c.LUA_GCSETSTEPMUL, multiplier); - } - - /// Returns the memory allocation function of a given state - /// If data is not null, it is set to the opaque pointer given when the allocator function was set - /// See https://www.lua.org/manual/5.3/manual.html#lua_getallocf - pub fn getAllocFn(lua: *Lua, data: ?**anyopaque) AllocFn { - // Assert cannot be null because it is impossible (and not useful) to pass null - // to the functions that set the allocator (setallocf and newstate) - return c.lua_getallocf(lua.state, @ptrCast(data)).?; - } - - /// Returns a slice of a raw memory area associated with the given Lua state - /// The application may use this area for any purpose; Lua does not use it for anything - /// This area has a size of a pointer to void - /// See https://www.lua.org/manual/5.3/manual.html#lua_getextraspace - pub fn getExtraSpace(lua: *Lua) []u8 { - return @as([*]u8, @ptrCast(c.lua_getextraspace(lua.state).?))[0..@sizeOf(isize)]; - } - - /// Pushes onto the stack the value t[key] where t is the value at the given index - /// See https://www.lua.org/manual/5.3/manual.html#lua_getfield - pub fn getField(lua: *Lua, index: i32, key: [:0]const u8) LuaType { - return @enumFromInt(c.lua_getfield(lua.state, index, key.ptr)); - } - - /// Pushes onto the stack the value of the global name and returns the type of that value - /// Returns an error if the global does not exist (is nil) - /// See https://www.lua.org/manual/5.3/manual.html#lua_getglobal - pub fn getGlobal(lua: *Lua, name: [:0]const u8) !LuaType { - const lua_type: LuaType = @enumFromInt(c.lua_getglobal(lua.state, name.ptr)); - if (lua_type == .nil) return error.Fail; - return lua_type; - } - - /// Pushes onto the stack the value t[i] where t is the value at the given index - /// Returns the type of the pushed value - /// See https://www.lua.org/manual/5.3/manual.html#lua_geti - pub fn getIndex(lua: *Lua, index: i32, i: Integer) LuaType { - return @enumFromInt(c.lua_geti(lua.state, index, i)); - } - - /// Pushes onto the stack the Lua value associated with the full userdata at the given index. - /// Returns the type of the pushed value. - /// See https://www.lua.org/manual/5.3/manual.html#lua_getuservalue - pub fn getUserValue(lua: *Lua, index: i32) LuaType { - return @enumFromInt(c.lua_getuservalue(lua.state, index)); - } - - /// If the value at the given index has a metatable, the function pushes that metatable onto the stack - /// Otherwise an error is returned - /// See https://www.lua.org/manual/5.3/manual.html#lua_getmetatable - pub fn getMetatable(lua: *Lua, index: i32) !void { - if (c.lua_getmetatable(lua.state, index) == 0) return error.Fail; - } - - /// Pushes onto the stack the value t[k] where t is the value at the given index and k is the value on the top of the stack - /// Returns the type of the pushed value - /// See https://www.lua.org/manual/5.3/manual.html#lua_gettable - pub fn getTable(lua: *Lua, index: i32) LuaType { - return @enumFromInt(c.lua_gettable(lua.state, index)); - } - - /// Returns the index of the top element in the stack - /// Because indices start at 1, the result is also equal to the number of elements in the stack - /// See https://www.lua.org/manual/5.3/manual.html#lua_gettop - pub fn getTop(lua: *Lua) i32 { - return c.lua_gettop(lua.state); - } - - /// Moves the top element into the given valid `index` shifting up any elements to make room - /// See https://www.lua.org/manual/5.3/manual.html#lua_insert - pub fn insert(lua: *Lua, index: i32) void { - // translate-c cannot translate this macro correctly - // c.lua_insert(lua.state, index); - lua.rotate(index, 1); - } - - /// Returns true if the value at the given index is a boolean - /// See https://www.lua.org/manual/5.3/manual.html#lua_isboolean - pub fn isBoolean(lua: *Lua, index: i32) bool { - return c.lua_isboolean(lua.state, index); - } - - /// Returns true if the value at the given index is a CFn - /// See https://www.lua.org/manual/5.3/manual.html#lua_iscfunction - pub fn isCFunction(lua: *Lua, index: i32) bool { - return c.lua_iscfunction(lua.state, index) != 0; - } - - /// Returns true if the value at the given index is a function (C or Lua) - /// See https://www.lua.org/manual/5.3/manual.html#lua_isfunction - pub fn isFunction(lua: *Lua, index: i32) bool { - return c.lua_isfunction(lua.state, index); - } - - /// Returns true if the value at the given index is an integer - /// See https://www.lua.org/manual/5.3/manual.html#lua_isinteger - pub fn isInteger(lua: *Lua, index: i32) bool { - return c.lua_isinteger(lua.state, index) != 0; - } - - /// Returns true if the value at the given index is a light userdata - /// See https://www.lua.org/manual/5.3/manual.html#lua_islightuserdata - pub fn isLightUserdata(lua: *Lua, index: i32) bool { - return c.lua_islightuserdata(lua.state, index); - } - - /// Returns true if the value at the given index is nil - /// See https://www.lua.org/manual/5.3/manual.html#lua_isnil - pub fn isNil(lua: *Lua, index: i32) bool { - return c.lua_isnil(lua.state, index); - } - - /// Returns true if the given index is not valid - /// See https://www.lua.org/manual/5.3/manual.html#lua_isnone - pub fn isNone(lua: *Lua, index: i32) bool { - return c.lua_isnone(lua.state, index); - } - - /// Returns true if the given index is not valid or if the value at the index is nil - /// See https://www.lua.org/manual/5.3/manual.html#lua_isnoneornil - pub fn isNoneOrNil(lua: *Lua, index: i32) bool { - return c.lua_isnoneornil(lua.state, index); - } - - /// Returns true if the value at the given index is a number - /// See https://www.lua.org/manual/5.3/manual.html#lua_isnumber - pub fn isNumber(lua: *Lua, index: i32) bool { - return c.lua_isnumber(lua.state, index) != 0; - } - - /// Returns true if the value at the given index is a string - /// See https://www.lua.org/manual/5.3/manual.html#lua_isstring - pub fn isString(lua: *Lua, index: i32) bool { - return c.lua_isstring(lua.state, index) != 0; - } - - /// Returns true if the value at the given index is a table - /// See https://www.lua.org/manual/5.3/manual.html#lua_istable - pub fn isTable(lua: *Lua, index: i32) bool { - return c.lua_istable(lua.state, index); - } - - /// Returns true if the value at the given index is a thread - /// See https://www.lua.org/manual/5.3/manual.html#lua_isthread - pub fn isThread(lua: *Lua, index: i32) bool { - return c.lua_isthread(lua.state, index); - } - - /// Returns true if the value at the given index is a userdata (full or light) - /// See https://www.lua.org/manual/5.3/manual.html#lua_isuserdata - pub fn isUserdata(lua: *Lua, index: i32) bool { - return c.lua_isuserdata(lua.state, index) != 0; - } - - /// Returns true if the given coroutine can yield - /// See https://www.lua.org/manual/5.3/manual.html#lua_isyieldable - pub fn isYieldable(lua: *Lua) bool { - return c.lua_isyieldable(lua.state) != 0; - } - - /// Pushes the length of the value at the given index onto the stack - /// Equivalent to the # operator in Lua - /// See https://www.lua.org/manual/5.3/manual.html#lua_len - pub fn len(lua: *Lua, index: i32) void { - c.lua_len(lua.state, index); - } - - /// Loads a Lua chunk without running it - /// If there are no errors, pushes the compiled chunk on the top of the stack as a function - /// See https://www.lua.org/manual/5.3/manual.html#lua_load - pub fn load(lua: *Lua, reader: CReaderFn, data: *anyopaque, chunk_name: [:0]const u8, mode: Mode) !void { - const mode_str = switch (mode) { - .binary => "b", - .text => "t", - .binary_text => "bt", - }; - const ret = c.lua_load(lua.state, reader, data, chunk_name.ptr, mode_str.ptr); - switch (ret) { - StatusCode.ok => return, - StatusCode.err_syntax => return error.Syntax, - StatusCode.err_memory => return error.Memory, - // lua_load runs pcall, so can also return any result of a pcall error - StatusCode.err_runtime => return error.Runtime, - StatusCode.err_error => return error.MsgHandler, - StatusCode.err_gcmm => return error.GCMetaMethod, - else => unreachable, - } - } - - /// Creates a new independent state and returns its main thread - /// See https://www.lua.org/manual/5.3/manual.html#lua_newstate - pub fn newState(alloc_fn: AllocFn, data: ?*const anyopaque) !Lua { - const state = c.lua_newstate(alloc_fn, @constCast(data)) orelse return error.Memory; - return Lua{ .state = state }; - } - - /// Creates a new empty table and pushes it onto the stack - /// Equivalent to createTable(0, 0) - /// See https://www.lua.org/manual/5.3/manual.html#lua_newtable - pub fn newTable(lua: *Lua) void { - c.lua_newtable(lua.state); - } - - /// Creates a new thread, pushes it on the stack, and returns a Lua state that represents the new thread - /// The new thread shares the global environment but has a separate execution stack - /// See https://www.lua.org/manual/5.3/manual.html#lua_newthread - pub fn newThread(lua: *Lua) Lua { - const state = c.lua_newthread(lua.state).?; - return .{ .state = state }; - } - - /// This function allocates a new userdata of the given type. - /// Returns a pointer to the Lua-owned data - /// See https://www.lua.org/manual/5.3/manual.html#lua_newuserdata - pub fn newUserdata(lua: *Lua, comptime T: type) *T { - // safe to .? because this function throws a Lua error on out of memory - // so the returned pointer should never be null - const ptr = c.lua_newuserdata(lua.state, @sizeOf(T)).?; - return opaqueCast(T, ptr); - } - - /// This function creates and pushes a slice of full userdata onto the stack. - /// Returns a slice to the Lua-owned data. - /// See https://www.lua.org/manual/5.3/manual.html#lua_newuserdata - pub fn newUserdataSlice(lua: *Lua, comptime T: type, size: usize) []T { - // safe to .? because this function throws a Lua error on out of memory - const ptr = c.lua_newuserdata(lua.state, @sizeOf(T) * size).?; - return @as([*]T, @ptrCast(@alignCast(ptr)))[0..size]; - } - - /// Pops a key from the stack, and pushes a key-value pair from the table at the given index - /// See https://www.lua.org/manual/5.3/manual.html#lua_next - pub fn next(lua: *Lua, index: i32) bool { - return c.lua_next(lua.state, index) != 0; - } - - /// Tries to convert a Lua float into a Lua integer - /// Returns an error if the conversion was unsuccessful - /// See https://www.lua.org/manual/5.3/manual.html#lua_numbertointeger - pub fn numberToInteger(n: Number, i: *Integer) !void { - // translate-c failure - // return c.lua_numbertointeger(n, i) != 0; - const min_float: Number = @floatFromInt(min_integer); - if (n >= min_float and n < -min_float) { - i.* = @intFromFloat(n); - } else return error.Fail; - } - - /// Calls a function (or callable object) in protected mode - /// See https://www.lua.org/manual/5.3/manual.html#lua_pcall - pub fn protectedCall(lua: *Lua, num_args: i32, num_results: i32, msg_handler: i32) !void { - // The translate-c version of lua_pcall does not type-check so we must rewrite it - // (macros don't always translate well with translate-c) - const ret = c.lua_pcallk(lua.state, num_args, num_results, msg_handler, 0, null); - switch (ret) { - StatusCode.ok => return, - StatusCode.err_runtime => return error.Runtime, - StatusCode.err_memory => return error.Memory, - StatusCode.err_error => return error.MsgHandler, - StatusCode.err_gcmm => return error.GCMetaMethod, - else => unreachable, - } - } - - /// Behaves exactly like `Lua.protectedCall()` except that it allows the called function to yield - /// See https://www.lua.org/manual/5.3/manual.html#lua_pcallk - pub fn protectedCallCont(lua: *Lua, num_args: i32, num_results: i32, msg_handler: i32, ctx: Context, k: CContFn) !void { - const ret = c.lua_pcallk(lua.state, num_args, num_results, msg_handler, ctx, k); - switch (ret) { - StatusCode.ok => return, - StatusCode.err_runtime => return error.Runtime, - StatusCode.err_memory => return error.Memory, - StatusCode.err_error => return error.MsgHandler, - StatusCode.err_gcmm => return error.GCMetaMethod, - else => unreachable, - } - } - - /// Pops `n` elements from the top of the stack - /// See https://www.lua.org/manual/5.3/manual.html#lua_pop - pub fn pop(lua: *Lua, n: i32) void { - lua.setTop(-n - 1); - } - - /// Pushes a boolean value with value `b` onto the stack - /// See https://www.lua.org/manual/5.3/manual.html#lua_pushboolean - pub fn pushBoolean(lua: *Lua, b: bool) void { - c.lua_pushboolean(lua.state, @intFromBool(b)); - } - - /// Pushes a new Closure onto the stack - /// `n` tells how many upvalues this function will have - /// See https://www.lua.org/manual/5.3/manual.html#lua_pushcclosure - pub fn pushClosure(lua: *Lua, c_fn: CFn, n: i32) void { - c.lua_pushcclosure(lua.state, c_fn, n); - } - - /// Pushes a function onto the stack. - /// Equivalent to pushClosure with no upvalues - /// See https://www.lua.org/manual/5.3/manual.html#lua_pushcfunction - pub fn pushFunction(lua: *Lua, c_fn: CFn) void { - lua.pushClosure(c_fn, 0); - } - - /// Push a formatted string onto the stack and return a pointer to the string - /// See https://www.lua.org/manual/5.3/manual.html#lua_pushfstring - pub fn pushFString(lua: *Lua, fmt: [:0]const u8, args: anytype) [*:0]const u8 { - return @call(.auto, c.lua_pushfstring, .{ lua.state, fmt.ptr } ++ args); - } - - /// Pushes the global environment onto the stack - /// See https://www.lua.org/manual/5.3/manual.html#lua_pushglobaltable - pub fn pushGlobalTable(lua: *Lua) void { - // lua_pushglobaltable is a macro and c-translate assumes it returns opaque - // so just reimplement the macro here - // c.lua_pushglobaltable(lua.state); - _ = lua.rawGetIndex(registry_index, ridx_globals); - } - - /// Pushes an integer with value `n` onto the stack - /// See https://www.lua.org/manual/5.3/manual.html#lua_pushinteger - pub fn pushInteger(lua: *Lua, n: Integer) void { - c.lua_pushinteger(lua.state, n); - } - - /// Pushes a light userdata onto the stack - /// See https://www.lua.org/manual/5.3/manual.html#lua_pushlightuserdata - pub fn pushLightUserdata(lua: *Lua, ptr: *anyopaque) void { - c.lua_pushlightuserdata(lua.state, ptr); - } - - /// Pushes the bytes onto the stack. Returns a slice pointing to Lua's internal copy of the string - /// See https://www.lua.org/manual/5.3/manual.html#lua_pushlstring - pub fn pushBytes(lua: *Lua, bytes: []const u8) []const u8 { - return c.lua_pushlstring(lua.state, bytes.ptr, bytes.len)[0..bytes.len]; - } - - /// Pushes a nil value onto the stack - /// See https://www.lua.org/manual/5.3/manual.html#lua_pushnil - pub fn pushNil(lua: *Lua) void { - c.lua_pushnil(lua.state); - } - - /// Pushes a float with value `n` onto the stack - /// See https://www.lua.org/manual/5.3/manual.html#lua_pushnumber - pub fn pushNumber(lua: *Lua, n: Number) void { - c.lua_pushnumber(lua.state, n); - } - - /// Pushes a zero-terminated string onto the stack - /// Lua makes a copy of the string so `str` may be freed immediately after return - /// Returns a pointer to the internal Lua string - /// See https://www.lua.org/manual/5.3/manual.html#lua_pushstring - pub fn pushString(lua: *Lua, str: [:0]const u8) [:0]const u8 { - return c.lua_pushstring(lua.state, str.ptr)[0..str.len :0]; - } - - /// Pushes this thread onto the stack - /// Returns true if this thread is the main thread of its state - /// See https://www.lua.org/manual/5.3/manual.html#lua_pushthread - pub fn pushThread(lua: *Lua) bool { - return c.lua_pushthread(lua.state) != 0; - } - - /// Pushes a copy of the element at the given index onto the stack - /// See https://www.lua.org/manual/5.3/manual.html#lua_pushvalue - pub fn pushValue(lua: *Lua, index: i32) void { - c.lua_pushvalue(lua.state, index); - } - - /// Returns true if the two values in indices `index1` and `index2` are primitively equal - /// Bypasses __eq metamethods - /// Returns false if not equal, or if any index is invalid - /// See https://www.lua.org/manual/5.3/manual.html#lua_rawequal - pub fn rawEqual(lua: *Lua, index1: i32, index2: i32) bool { - return c.lua_rawequal(lua.state, index1, index2) != 0; - } - - /// Similar to `Lua.getTable()` but does a raw access (without metamethods) - /// See https://www.lua.org/manual/5.3/manual.html#lua_rawget - pub fn rawGetTable(lua: *Lua, index: i32) LuaType { - return @enumFromInt(c.lua_rawget(lua.state, index)); - } - - /// Pushes onto the stack the value t[n], where `t` is the table at the given `index` - /// Returns the `LuaType` of the pushed value - /// See https://www.lua.org/manual/5.3/manual.html#lua_rawgeti - pub fn rawGetIndex(lua: *Lua, index: i32, n: Integer) LuaType { - return @enumFromInt(c.lua_rawgeti(lua.state, index, n)); - } - - /// Pushes onto the stack the value t[k] where t is the table at the given `index` and - /// k is the pointer `p` represented as a light userdata - /// See https://www.lua.org/manual/5.3/manual.html#lua_rawgetp - pub fn rawGetPtr(lua: *Lua, index: i32, p: *const anyopaque) LuaType { - return @enumFromInt(c.lua_rawgetp(lua.state, index, p)); - } - - /// Returns the raw length of the value at the given index - /// For strings it is the length; for tables it is the result of the `#` operator - /// For userdata it is the size of the block of memory - /// For other values the call returns 0 - /// See https://www.lua.org/manual/5.3/manual.html#lua_rawlen - pub fn rawLen(lua: *Lua, index: i32) Unsigned { - return c.lua_rawlen(lua.state, index); - } - - /// Similar to `Lua.setTable()` but does a raw assignment (without metamethods) - /// See https://www.lua.org/manual/5.3/manual.html#lua_rawset - pub fn rawSetTable(lua: *Lua, index: i32) void { - c.lua_rawset(lua.state, index); - } - - /// Does the equivalent of t[`i`] = v where t is the table at the given `index` - /// and v is the value at the top of the stack - /// Pops the value from the stack. Does not use __newindex metavalue - /// See https://www.lua.org/manual/5.3/manual.html#lua_rawseti - pub fn rawSetIndex(lua: *Lua, index: i32, i: Integer) void { - c.lua_rawseti(lua.state, index, i); - } - - /// Does the equivalent of t[p] = v where t is the table at the given `index` - /// `p` is encoded as a light user data, and v is the value at the top of the stack - /// Pops the value from the stack. Does not use __newindex metavalue - /// See https://www.lua.org/manual/5.3/manual.html#lua_rawsetp - pub fn rawSetPtr(lua: *Lua, index: i32, p: *const anyopaque) void { - c.lua_rawsetp(lua.state, index, p); - } - - /// Sets the C function f as the new value of global name - /// See https://www.lua.org/manual/5.3/manual.html#lua_register - pub fn register(lua: *Lua, name: [:0]const u8, c_fn: CFn) void { - c.lua_register(lua.state, name.ptr, c_fn); - } - - /// Removes the element at the given valid `index` shifting down elements to fill the gap - /// See https://www.lua.org/manual/5.3/manual.html#lua_remove - pub fn remove(lua: *Lua, index: i32) void { - // translate-c cannot translate this macro correctly - // c.lua_remove(lua.state, index); - lua.rotate(index, -1); - lua.pop(1); - } - - /// Moves the top element into the given valid `index` without shifting any elements, - /// then pops the top element - /// See https://www.lua.org/manual/5.3/manual.html#lua_replace - pub fn replace(lua: *Lua, index: i32) void { - // translate-c cannot translate this macro correctly - // c.lua_replace(lua.state, index); - lua.copy(-1, index); - lua.pop(1); - } - - /// Starts and resumes a coroutine in the given thread - /// See https://www.lua.org/manual/5.3/manual.html#lua_resume - pub fn resumeThread(lua: *Lua, from: ?Lua, num_args: i32) !ResumeStatus { - const from_state = if (from) |from_val| from_val.state else null; - const thread_status = c.lua_resume(lua.state, from_state, num_args); - switch (thread_status) { - StatusCode.err_runtime => return error.Runtime, - StatusCode.err_memory => return error.Memory, - StatusCode.err_error => return error.MsgHandler, - StatusCode.err_gcmm => return error.GCMetaMethod, - else => return @enumFromInt(thread_status), - } - } - - /// Rotates the stack elements between the valid `index` and the top of the stack - /// The elements are rotated `n` positions in the direction of the top for positive `n`, - /// and `n` positions in the direction of the bottom for negative `n` - /// See https://www.lua.org/manual/5.3/manual.html#lua_rotate - pub fn rotate(lua: *Lua, index: i32, n: i32) void { - c.lua_rotate(lua.state, index, n); - } - - /// Changes the allocator function of a given state to `alloc_fn` with userdata `data` - /// See https://www.lua.org/manual/5.3/manual.html#lua_setallocf - pub fn setAllocF(lua: *Lua, alloc_fn: AllocFn, data: ?*anyopaque) void { - c.lua_setallocf(lua.state, alloc_fn, data); - } - - /// Does the equivalent to t[`k`] = v where t is the value at the given `index` - /// and v is the value on the top of the stack - /// See https://www.lua.org/manual/5.3/manual.html#lua_setfield - pub fn setField(lua: *Lua, index: i32, k: [:0]const u8) void { - c.lua_setfield(lua.state, index, k.ptr); - } - - /// Pops a value from the stack and sets it as the new value of global `name` - /// See https://www.lua.org/manual/5.3/manual.html#lua_setglobal - pub fn setGlobal(lua: *Lua, name: [:0]const u8) void { - c.lua_setglobal(lua.state, name.ptr); - } - - /// Does the equivalent to t[`n`] = v where t is the value at the given `index` - /// and v is the value on the top of the stack. Pops the value from the stack - /// See https://www.lua.org/manual/5.3/manual.html#lua_seti - pub fn setIndex(lua: *Lua, index: i32, n: Integer) void { - c.lua_seti(lua.state, index, n); - } - - /// Pops a value from the stack and sets it as the user value associated to - /// the full userdata at the given index - /// Returns an error if the userdata does not have that value - /// See https://www.lua.org/manual/5.3/manual.html#lua_setuservalue - pub fn setUserValue(lua: *Lua, index: i32) !void { - c.lua_setuservalue(lua.state, index); - } - - /// Pops a table or nil from the stack and sets that value as the new metatable for the - /// value at the given `index` - /// See https://www.lua.org/manual/5.3/manual.html#lua_setmetatable - pub fn setMetatable(lua: *Lua, index: i32) void { - // lua_setmetatable always returns 1 so is safe to ignore - _ = c.lua_setmetatable(lua.state, index); - } - - /// Does the equivalent to t[k] = v, where t is the value at the given `index` - /// v is the value on the top of the stack, and k is the value just below the top - /// See https://www.lua.org/manual/5.3/manual.html#lua_settable - pub fn setTable(lua: *Lua, index: i32) void { - c.lua_settable(lua.state, index); - } - - /// Sets the top of the stack to `index` - /// If the new top is greater than the old, new elements are filled with nil - /// If `index` is 0 all stack elements are removed - /// See https://www.lua.org/manual/5.3/manual.html#lua_settop - pub fn setTop(lua: *Lua, index: i32) void { - c.lua_settop(lua.state, index); - } - - /// Returns the status of this thread - /// See https://www.lua.org/manual/5.3/manual.html#lua_status - pub fn status(lua: *Lua) Status { - return @enumFromInt(c.lua_status(lua.state)); - } - - /// Converts the zero-terminated string `str` to a number, pushes that number onto the stack, - /// Returns an error if conversion failed - /// See https://www.lua.org/manual/5.3/manual.html#lua_stringtonumber - pub fn stringToNumber(lua: *Lua, str: [:0]const u8) !void { - const size = c.lua_stringtonumber(lua.state, str.ptr); - if (size == 0) return error.Fail; - } - - /// Converts the Lua value at the given `index` into a boolean - /// The Lua value at the index will be considered true unless it is false or nil - /// See https://www.lua.org/manual/5.3/manual.html#lua_toboolean - pub fn toBoolean(lua: *Lua, index: i32) bool { - return c.lua_toboolean(lua.state, index) != 0; - } - - /// Converts a value at the given `index` into a CFn - /// Returns an error if the value is not a CFn - /// See https://www.lua.org/manual/5.3/manual.html#lua_tocfunction - pub fn toCFunction(lua: *Lua, index: i32) !CFn { - return c.lua_tocfunction(lua.state, index) orelse return error.Fail; - } - - /// Converts the Lua value at the given `index` to a signed integer - /// The Lua value must be an integer, or a number, or a string convertible to an integer otherwise toIntegerX returns 0 - /// Returns an error if the conversion failed - /// See https://www.lua.org/manual/5.3/manual.html#lua_tointegerx - pub fn toInteger(lua: *Lua, index: i32) !Integer { - var success: c_int = undefined; - const result = c.lua_tointegerx(lua.state, index, &success); - if (success == 0) return error.Fail; - return result; - } - - /// Returns a slice of bytes at the given index - /// If the value is not a string or number, returns an error - /// If the value was a number the actual value in the stack will be changed to a string - /// See https://www.lua.org/manual/5.3/manual.html#lua_tolstring - pub fn toBytes(lua: *Lua, index: i32) ![:0]const u8 { - var length: usize = undefined; - if (c.lua_tolstring(lua.state, index, &length)) |ptr| return ptr[0..length :0]; - return error.Fail; - } - - /// Converts the Lua value at the given `index` to a float - /// The Lua value must be a number or a string convertible to a number otherwise toNumberX returns 0 - /// Returns an error if the conversion failed - /// See https://www.lua.org/manual/5.3/manual.html#lua_tonumberx - pub fn toNumber(lua: *Lua, index: i32) !Number { - var success: c_int = undefined; - const result = c.lua_tonumberx(lua.state, index, &success); - if (success == 0) return error.Fail; - return result; - } - - /// Converts the value at the given `index` to an opaque pointer - /// See https://www.lua.org/manual/5.3/manual.html#lua_topointer - pub fn toPointer(lua: *Lua, index: i32) !*const anyopaque { - if (c.lua_topointer(lua.state, index)) |ptr| return ptr; - return error.Fail; - } - - /// Converts the Lua value at the given `index` to a zero-terminated many-itemed-pointer (string) - /// Returns an error if the conversion failed - /// If the value was a number the actual value in the stack will be changed to a string - /// See https://www.lua.org/manual/5.3/manual.html#lua_tostring - pub fn toString(lua: *Lua, index: i32) ![*:0]const u8 { - var length: usize = undefined; - if (c.lua_tolstring(lua.state, index, &length)) |str| return str; - return error.Fail; - } - - /// Converts the value at the given `index` to a Lua thread (wrapped with a `Lua` struct) - /// The thread does _not_ contain an allocator because it is not the main thread and should therefore not be used with `deinit()` - /// Returns an error if the value is not a thread - /// See https://www.lua.org/manual/5.3/manual.html#lua_tothread - pub fn toThread(lua: *Lua, index: i32) !Lua { - const thread = c.lua_tothread(lua.state, index); - if (thread) |thread_ptr| return Lua{ .state = thread_ptr }; - return error.Fail; - } - - /// Returns a Lua-owned userdata pointer of the given type at the given index. - /// Works for both light and full userdata. - /// Returns an error if the value is not a userdata. - /// See https://www.lua.org/manual/5.3/manual.html#lua_touserdata - pub fn toUserdata(lua: *Lua, comptime T: type, index: i32) !*T { - if (c.lua_touserdata(lua.state, index)) |ptr| return opaqueCast(T, ptr); - return error.Fail; - } - - /// Returns a Lua-owned userdata slice of the given type at the given index. - /// Returns an error if the value is not a userdata. - /// See https://www.lua.org/manual/5.3/manual.html#lua_touserdata - pub fn toUserdataSlice(lua: *Lua, comptime T: type, index: i32) ![]T { - if (c.lua_touserdata(lua.state, index)) |ptr| { - const size = lua.rawLen(index) / @sizeOf(T); - return @as([*]T, @ptrCast(@alignCast(ptr)))[0..size]; - } - return error.Fail; - } - - /// Returns the `LuaType` of the value at the given index - /// Note that this is equivalent to lua_type but because type is a Zig primitive it is renamed to `typeOf` - /// See https://www.lua.org/manual/5.3/manual.html#lua_typeof - pub fn typeOf(lua: *Lua, index: i32) LuaType { - return @enumFromInt(c.lua_type(lua.state, index)); - } - - /// Returns the name of the given `LuaType` as a null-terminated slice - /// See https://www.lua.org/manual/5.3/manual.html#lua_typename - pub fn typeName(lua: *Lua, t: LuaType) [:0]const u8 { - return std.mem.span(c.lua_typename(lua.state, @intFromEnum(t))); - } - - /// Returns the pseudo-index that represents the `i`th upvalue of the running function - /// See https://www.lua.org/manual/5.3/manual.html#lua_upvalueindex - pub fn upvalueIndex(i: i32) i32 { - return c.lua_upvalueindex(i); - } - - /// Returns the version number of this core - /// When `caller_version` is true it returns the address of the version running the call - /// See https://www.lua.org/manual/5.3/manual.html#lua_version - pub fn version(lua: *Lua, caller_version: bool) *const Number { - if (caller_version) return c.lua_version(null); - return c.lua_version(lua.state); - } - - /// Pops `num` values from the current stack and pushes onto the stack of `to` - /// See https://www.lua.org/manual/5.3/manual.html#lua_xmove - pub fn xMove(lua: *Lua, to: Lua, num: i32) void { - c.lua_xmove(lua.state, to.state, num); - } - - /// This function is equivalent to `Lua.yieldCont()` but has no continuation - /// This function never returns - /// NOTE: look into the lua_yieldk docs about this and debug hooks and noreturn - /// See https://www.lua.org/manual/5.3/manual.html#lua_yield - pub fn yield(lua: *Lua, num_results: i32) noreturn { - // translate-c failed to pass NULL correctly - _ = c.lua_yieldk(lua.state, num_results, 0, null); - unreachable; - } - - /// Yields this coroutine (thread) - /// This function never returns - /// See https://www.lua.org/manual/5.3/manual.html#lua_yieldk - pub fn yieldCont(lua: *Lua, num_results: i32, ctx: Context, k: CContFn) noreturn { - _ = c.lua_yieldk(lua.state, num_results, ctx, k); - unreachable; - } - - // Debug library functions - // - // The debug interface functions are included in alphabetical order - // Each is kept similar to the original C API function while also making it easy to use from Zig - - /// Returns the current hook function - /// See https://www.lua.org/manual/5.3/manual.html#lua_gethook - pub fn getHook(lua: *Lua) ?CHookFn { - return c.lua_gethook(lua.state); - } - - /// Returns the current hook count - /// See https://www.lua.org/manual/5.3/manual.html#lua_gethookcount - pub fn getHookCount(lua: *Lua) i32 { - return c.lua_gethookcount(lua.state); - } - - /// Returns the current hook mask - /// See https://www.lua.org/manual/5.3/manual.html#lua_gethookmask - pub fn getHookMask(lua: *Lua) HookMask { - return HookMask.fromInt(c.lua_gethookmask(lua.state)); - } - - /// Gets information about a specific function or function invocation - /// Returns an error if an invalid option was given, but the valid options - /// are still handled - /// See https://www.lua.org/manual/5.3/manual.html#lua_getinfo - pub fn getInfo(lua: *Lua, options: DebugInfo.Options, info: *DebugInfo) void { - const str = options.toString(); - - var ar: Debug = undefined; - ar.i_ci = @ptrCast(info.private); - - // should never fail because we are controlling options with the struct param - _ = c.lua_getinfo(lua.state, &str, &ar); - // std.debug.assert( != 0); - - // copy data into a struct - if (options.l) info.current_line = if (ar.currentline == -1) null else ar.currentline; - if (options.n) { - info.name = if (ar.name != null) std.mem.span(ar.name) else null; - info.name_what = blk: { - const what = std.mem.span(ar.namewhat); - if (std.mem.eql(u8, "global", what)) break :blk .global; - if (std.mem.eql(u8, "local", what)) break :blk .local; - if (std.mem.eql(u8, "method", what)) break :blk .method; - if (std.mem.eql(u8, "field", what)) break :blk .field; - if (std.mem.eql(u8, "upvalue", what)) break :blk .upvalue; - if (what.len == 0) break :blk .other; - unreachable; - }; - } - if (options.S) { - info.source = std.mem.span(ar.source); - @memcpy(&info.short_src, &ar.short_src); - info.first_line_defined = ar.linedefined; - info.last_line_defined = ar.lastlinedefined; - info.what = blk: { - const what = std.mem.span(ar.what); - if (std.mem.eql(u8, "Lua", what)) break :blk .lua; - if (std.mem.eql(u8, "C", what)) break :blk .c; - if (std.mem.eql(u8, "main", what)) break :blk .main; - unreachable; - }; - } - if (options.t) info.is_tail_call = ar.istailcall != 0; - if (options.u) { - info.num_upvalues = ar.nups; - info.num_params = ar.nparams; - info.is_vararg = ar.isvararg != 0; - } - } - - /// Gets information about a local variable - /// Returns the name of the local variable - /// See https://www.lua.org/manual/5.3/manual.html#lua_getlocal - pub fn getLocal(lua: *Lua, info: *DebugInfo, n: i32) ![:0]const u8 { - var ar: Debug = undefined; - ar.i_ci = @ptrCast(info.private); - if (c.lua_getlocal(lua.state, &ar, n)) |name| { - return std.mem.span(name); - } - return error.Fail; - } - - /// Gets information about the interpreter runtime stack - /// See https://www.lua.org/manual/5.3/manual.html#lua_getstack - pub fn getStack(lua: *Lua, level: i32) !DebugInfo { - var ar: Debug = undefined; - if (c.lua_getstack(lua.state, level, &ar) == 0) return error.Fail; - return DebugInfo{ .private = ar.i_ci.? }; - } - - /// Gets information about the `n`th upvalue of the closure at index `func_index` - /// See https://www.lua.org/manual/5.3/manual.html#lua_getupvalue - pub fn getUpvalue(lua: *Lua, func_index: i32, n: i32) ![:0]const u8 { - if (c.lua_getupvalue(lua.state, func_index, n)) |name| { - return std.mem.span(name); - } - return error.Fail; - } - - /// Sets the debugging hook function - /// See https://www.lua.org/manual/5.3/manual.html#lua_sethook - pub fn setHook(lua: *Lua, hook_fn: CHookFn, mask: HookMask, count: i32) void { - const hook_mask = HookMask.toInt(mask); - c.lua_sethook(lua.state, hook_fn, hook_mask, count); - } - - /// Sets the value of a local variable - /// Returns an error when the index is greater than the number of active locals - /// Returns the name of the local variable - /// See https://www.lua.org/manual/5.3/manual.html#lua_setlocal - pub fn setLocal(lua: *Lua, info: *DebugInfo, n: i32) ![:0]const u8 { - var ar: Debug = undefined; - ar.i_ci = @ptrCast(info.private); - if (c.lua_setlocal(lua.state, &ar, n)) |name| { - return std.mem.span(name); - } - return error.Fail; - } - - /// Sets the value of a closure's upvalue - /// Returns the name of the upvalue or an error if the upvalue does not exist - /// See https://www.lua.org/manual/5.3/manual.html#lua_setupvalue - pub fn setUpvalue(lua: *Lua, func_index: i32, n: i32) ![:0]const u8 { - if (c.lua_setupvalue(lua.state, func_index, n)) |name| { - return std.mem.span(name); - } - return error.Fail; - } - - /// Returns a unique identifier for the upvalue numbered `n` from the closure index `func_index` - /// See https://www.lua.org/manual/5.3/manual.html#lua_upvalueid - pub fn upvalueId(lua: *Lua, func_index: i32, n: i32) !*anyopaque { - if (c.lua_upvalueid(lua.state, func_index, n)) |ptr| return ptr; - return error.Fail; - } - - /// Make the `n1`th upvalue of the Lua closure at index `func_index1` refer to the `n2`th upvalue - /// of the Lua closure at index `func_index2` - /// See https://www.lua.org/manual/5.3/manual.html#lua_upvaluejoin - pub fn upvalueJoin(lua: *Lua, func_index1: i32, n1: i32, func_index2: i32, n2: i32) void { - c.lua_upvaluejoin(lua.state, func_index1, n1, func_index2, n2); - } - - // Auxiliary library functions - // - // Auxiliary library functions are included in alphabetical order. - // Each is kept similar to the original C API function while also making it easy to use from Zig - - /// Checks whether `cond` is true. Raises an error using `Lua.argError()` if not - /// Possibly never returns - /// See https://www.lua.org/manual/5.3/manual.html#luaL_argcheck - pub fn argCheck(lua: *Lua, cond: bool, arg: i32, extra_msg: [:0]const u8) void { - // translate-c failed - if (!cond) lua.argError(arg, extra_msg); - } - - /// Raises an error reporting a problem with argument `arg` of the C function that called it - /// See https://www.lua.org/manual/5.3/manual.html#luaL_argerror - pub fn argError(lua: *Lua, arg: i32, extra_msg: [*:0]const u8) noreturn { - _ = c.luaL_argerror(lua.state, arg, extra_msg); - unreachable; - } - - /// Calls a metamethod - /// See https://www.lua.org/manual/5.3/manual.html#luaL_callmeta - pub fn callMeta(lua: *Lua, obj: i32, field: [:0]const u8) !void { - if (c.luaL_callmeta(lua.state, obj, field.ptr) == 0) return error.Fail; - } - - /// Checks whether the function has an argument of any type at position `arg` - /// See https://www.lua.org/manual/5.3/manual.html#luaL_checkany - pub fn checkAny(lua: *Lua, arg: i32) void { - c.luaL_checkany(lua.state, arg); - } - - /// Checks whether the function argument `arg` is an integer (or can be converted to an integer) and returns the integer - /// See https://www.lua.org/manual/5.3/manual.html#luaL_checkinteger - pub fn checkInteger(lua: *Lua, arg: i32) Integer { - return c.luaL_checkinteger(lua.state, arg); - } - - /// Checks whether the function argument `arg` is a slice of bytes and returns the slice - /// See https://www.lua.org/manual/5.3/manual.html#luaL_checklstring - pub fn checkBytes(lua: *Lua, arg: i32) [:0]const u8 { - var length: usize = 0; - const str = c.luaL_checklstring(lua.state, arg, &length); - // luaL_checklstring never returns null (throws lua error) - return str[0..length :0]; - } - - /// Checks whether the function argument `arg` is a number and returns the number - /// See https://www.lua.org/manual/5.3/manual.html#luaL_checknumber - pub fn checkNumber(lua: *Lua, arg: i32) Number { - return c.luaL_checknumber(lua.state, arg); - } - - /// Checks whether the function argument `arg` is a string and searches for the enum value with the same name in `T`. - /// `default` is used as a default value when not null - /// Returns the enum value found - /// Useful for mapping Lua strings to Zig enums - /// See https://www.lua.org/manual/5.3/manual.html#luaL_checkoption - pub fn checkOption(lua: *Lua, comptime T: type, arg: i32, default: ?T) T { - const name = blk: { - if (default) |defaultName| { - break :blk lua.optBytes(arg, @tagName(defaultName)); - } else { - break :blk lua.checkBytes(arg); - } - }; - - inline for (std.meta.fields(T)) |field| { - if (std.mem.eql(u8, field.name, name)) { - return @enumFromInt(field.value); - } - } - - return lua.argError(arg, lua.pushFString("invalid option '%s'", .{name.ptr})); - } - - /// Grows the stack size to top + `size` elements, raising an error if the stack cannot grow to that size - /// `msg` is an additional text to go into the error message - /// See https://www.lua.org/manual/5.3/manual.html#luaL_checkstack - pub fn checkStackErr(lua: *Lua, size: i32, msg: ?[*:0]const u8) void { - c.luaL_checkstack(lua.state, size, msg); - } - - /// Checks whether the function argument `arg` is a string and returns the string - /// See https://www.lua.org/manual/5.3/manual.html#luaL_checkstring - pub fn checkString(lua: *Lua, arg: i32) [*:0]const u8 { - return c.luaL_checklstring(lua.state, arg, null); - } - - /// Checks whether the function argument `arg` has type `t` - /// See https://www.lua.org/manual/5.3/manual.html#luaL_checktype - pub fn checkType(lua: *Lua, arg: i32, t: LuaType) void { - c.luaL_checktype(lua.state, arg, @intFromEnum(t)); - } - - /// Checks whether the function argument `arg` is a userdata of the type `name` - /// Returns a pointer to the userdata - /// See https://www.lua.org/manual/5.3/manual.html#luaL_checkudata - pub fn checkUserdata(lua: *Lua, comptime T: type, arg: i32, name: [:0]const u8) *T { - // the returned pointer will not be null - return opaqueCast(T, c.luaL_checkudata(lua.state, arg, name.ptr).?); - } - - /// Checks whether the function argument `arg` is a userdata of the type `name` - /// Returns a Lua-owned userdata slice - /// See https://www.lua.org/manual/5.3/manual.html#luaL_checkudata - pub fn checkUserdataSlice(lua: *Lua, comptime T: type, arg: i32, name: [:0]const u8) []T { - // the returned pointer will not be null - const ptr = c.luaL_checkudata(lua.state, arg, name.ptr).?; - const size = lua.rawLen(arg) / @sizeOf(T); - return @as([*]T, @ptrCast(@alignCast(ptr)))[0..size]; - } - - /// Checks whether the code making the call and the Lua library being called are using - /// the same version of Lua and the same numeric types. - /// See https://www.lua.org/manual/5.3/manual.html#luaL_checkversion - pub fn checkVersion(lua: *Lua) void { - return c.luaL_checkversion_(lua.state, c.LUA_VERSION_NUM, c.LUAL_NUMSIZES); - } - - /// Loads and runs the given file - /// See https://www.lua.org/manual/5.3/manual.html#luaL_dofile - pub fn doFile(lua: *Lua, file_name: [:0]const u8) !void { - // translate-c failure - try lua.loadFile(file_name, .binary_text); - try lua.protectedCall(0, mult_return, 0); - } - - /// Loads and runs the given string - /// See https://www.lua.org/manual/5.3/manual.html#luaL_dostring - pub fn doString(lua: *Lua, str: [:0]const u8) !void { - // trnaslate-c failure - try lua.loadString(str); - try lua.protectedCall(0, mult_return, 0); - } - - /// Raises an error - /// See https://www.lua.org/manual/5.3/manual.html#luaL_error - pub fn raiseErrorStr(lua: *Lua, fmt: [:0]const u8, args: anytype) noreturn { - _ = @call(.auto, c.luaL_error, .{ lua.state, fmt.ptr } ++ args); - unreachable; - } - - /// This function produces the return values for process-related functions in the standard library - /// See https://www.lua.org/manual/5.3/manual.html#luaL_execresult - pub fn execResult(lua: *Lua, stat: i32) i32 { - return c.luaL_execresult(lua.state, stat); - } - - /// This function produces the return values for file-related functions in the standard library - /// See https://www.lua.org/manual/5.3/manual.html#luaL_fileresult - pub fn fileResult(lua: *Lua, stat: i32, file_name: [:0]const u8) i32 { - return c.luaL_fileresult(lua.state, stat, file_name.ptr); - } - - /// Pushes onto the stack the field `e` from the metatable of the object at index `obj` - /// and returns the type of the pushed value - /// TODO: possibly return an error if nil - /// See https://www.lua.org/manual/5.3/manual.html#luaL_getmetafield - pub fn getMetaField(lua: *Lua, obj: i32, field: [:0]const u8) !LuaType { - const val_type: LuaType = @enumFromInt(c.luaL_getmetafield(lua.state, obj, field.ptr)); - if (val_type == .nil) return error.Fail; - return val_type; - } - - /// Pushes onto the stack the metatable associated with the name `type_name` in the registry - /// or nil if there is no metatable associated with that name. Returns the type of the pushed value - /// See https://www.lua.org/manual/5.3/manual.html#luaL_getmetatable - pub fn getMetatableRegistry(lua: *Lua, table_name: [:0]const u8) LuaType { - return @enumFromInt(c.luaL_getmetatable(lua.state, table_name.ptr)); - } - - /// Ensures that the value t[`field`], where t is the value at `index`, is a table, and pushes that table onto the stack. - /// See https://www.lua.org/manual/5.3/manual.html#luaL_getsubtable - pub fn getSubtable(lua: *Lua, index: i32, field: [:0]const u8) !void { - if (c.luaL_getsubtable(lua.state, index, field.ptr) == 0) return error.Fail; - } - - /// Creates a copy of string `str`, replacing any occurrence of the string `pat` with the string `rep` - /// Pushes the resulting string on the stack and returns it. - /// See https://www.lua.org/manual/5.3/manual.html#luaL_gsub - pub fn globalSub(lua: *Lua, str: [:0]const u8, pat: [:0]const u8, rep: [:0]const u8) [:0]const u8 { - return std.mem.span(c.luaL_gsub(lua.state, str.ptr, pat.ptr, rep.ptr)); - } - - /// Returns the "length" of the value at the given index as a number - /// it is equivalent to the '#' operator in Lua - /// See https://www.lua.org/manual/5.3/manual.html#luaL_len - pub fn lenRaiseErr(lua: *Lua, index: i32) i64 { - return c.luaL_len(lua.state, index); - } - - /// Loads a buffer as a Lua chunk - /// See https://www.lua.org/manual/5.3/manual.html#luaL_loadbufferx - pub fn loadBuffer(lua: *Lua, buf: []const u8, name: [:0]const u8, mode: Mode) !void { - const mode_str = switch (mode) { - .binary => "b", - .text => "t", - .binary_text => "bt", - }; - switch (c.luaL_loadbufferx(lua.state, buf.ptr, buf.len, name.ptr, mode_str.ptr)) { - StatusCode.ok => return, - StatusCode.err_syntax => return error.Syntax, - StatusCode.err_memory => return error.Memory, - else => unreachable, - } - } - - /// Loads a file as a Lua chunk - /// See https://www.lua.org/manual/5.3/manual.html#luaL_loadfilex - pub fn loadFile(lua: *Lua, file_name: [:0]const u8, mode: Mode) !void { - const mode_str = switch (mode) { - .binary => "b", - .text => "t", - .binary_text => "bt", - }; - const ret = c.luaL_loadfilex(lua.state, file_name.ptr, mode_str.ptr); - switch (ret) { - StatusCode.ok => return, - StatusCode.err_syntax => return error.Syntax, - StatusCode.err_memory => return error.Memory, - err_file => return error.File, - // NOTE: the docs mention possible other return types, but I couldn't figure them out - else => unreachable, - } - } - - /// Loads a string as a Lua chunk - /// See https://www.lua.org/manual/5.3/manual.html#luaL_loadstring - pub fn loadString(lua: *Lua, str: [:0]const u8) !void { - const ret = c.luaL_loadstring(lua.state, str.ptr); - switch (ret) { - StatusCode.ok => return, - StatusCode.err_syntax => return error.Syntax, - StatusCode.err_memory => return error.Memory, - // loadstring runs lua_load which runs pcall, so can also return any result of an pcall error - StatusCode.err_runtime => return error.Runtime, - StatusCode.err_error => return error.MsgHandler, - else => unreachable, - } - } - - /// Creates a new table and registers there the functions in `list` - /// See https://www.lua.org/manual/5.3/manual.html#luaL_newlib - pub fn newLib(lua: *Lua, list: []const FnReg) void { - // translate-c failure - lua.checkVersion(); - lua.newLibTable(list); - lua.setFuncs(list, 0); - } - - /// Creates a new table with a size optimized to store all entries in the array `list` - /// See https://www.lua.org/manual/5.3/manual.html#luaL_newlibtable - pub fn newLibTable(lua: *Lua, list: []const FnReg) void { - // translate-c failure - lua.createTable(0, @intCast(list.len)); - } - - /// If the registry already has the key `key`, returns an error - /// Otherwise, creates a new table to be used as a metatable for userdata - /// See https://www.lua.org/manual/5.3/manual.html#luaL_newmetatable - pub fn newMetatable(lua: *Lua, key: [:0]const u8) !void { - if (c.luaL_newmetatable(lua.state, key.ptr) == 0) return error.Fail; - } - - /// Creates a new Lua state with an allocator using the default libc allocator - /// See https://www.lua.org/manual/5.3/manual.html#luaL_newstate - pub fn newStateLibc() !Lua { - const state = c.luaL_newstate() orelse return error.Memory; - return Lua{ .state = state }; - } - - // luaL_opt (a macro) really isn't that useful, so not going to implement for now - - /// If the function argument `arg` is an integer, returns the integer - /// If the argument is absent or nil returns `default` - /// See https://www.lua.org/manual/5.3/manual.html#luaL_optinteger - pub fn optInteger(lua: *Lua, arg: i32, default: Integer) Integer { - return c.luaL_optinteger(lua.state, arg, default); - } - - /// If the function argument `arg` is a slice of bytes, returns the slice - /// If the argument is absent or nil returns `default` - /// See https://www.lua.org/manual/5.3/manual.html#luaL_optlstring - pub fn optBytes(lua: *Lua, arg: i32, default: [:0]const u8) [:0]const u8 { - var length: usize = 0; - // will never return null because default cannot be null - const ret: [*]const u8 = c.luaL_optlstring(lua.state, arg, default.ptr, &length); - if (ret == default.ptr) return default; - return ret[0..length :0]; - } - - /// If the function argument `arg` is a number, returns the number - /// If the argument is absent or nil returns `default` - /// See https://www.lua.org/manual/5.3/manual.html#luaL_optnumber - pub fn optNumber(lua: *Lua, arg: i32, default: Number) Number { - return c.luaL_optnumber(lua.state, arg, default); - } - - /// If the function argument `arg` is a string, returns the string - /// If the argment is absent or nil returns `default` - /// See https://www.lua.org/manual/5.3/manual.html#luaL_optstring - pub fn optString(lua: *Lua, arg: i32, default: [:0]const u8) [*:0]const u8 { - // translate-c error - return c.luaL_optlstring(lua.state, arg, default.ptr, null); - } - - /// Creates and returns a reference in the table at index `index` for the object on the top of the stack - /// Pops the object - /// See https://www.lua.org/manual/5.3/manual.html#luaL_ref - pub fn ref(lua: *Lua, index: i32) !i32 { - const ret = c.luaL_ref(lua.state, index); - return if (ret == ref_nil) error.Fail else ret; - } - - /// If package.loaded[`mod_name`] is not true, calls the function `open_fn` with `mod_name` - /// as an argument and sets the call result to package.loaded[`mod_name`] - /// See https://www.lua.org/manual/5.3/manual.html#luaL_requiref - pub fn requireF(lua: *Lua, mod_name: [:0]const u8, open_fn: CFn, global: bool) void { - c.luaL_requiref(lua.state, mod_name.ptr, open_fn, @intFromBool(global)); - } - - /// Registers all functions in the array `fns` into the table on the top of the stack - /// All functions are created with `num_upvalues` upvalues - /// See https://www.lua.org/manual/5.3/manual.html#luaL_setfuncs - pub fn setFuncs(lua: *Lua, funcs: []const FnReg, num_upvalues: i32) void { - lua.checkStackErr(num_upvalues, "too many upvalues"); - for (funcs) |f| { - if (f.func) |func| { - var i: i32 = 0; - // copy upvalues to the top - while (i < num_upvalues) : (i += 1) lua.pushValue(-num_upvalues); - lua.pushClosure(func, num_upvalues); - } else lua.pushBoolean(false); // register a placeholder - lua.setField(-(num_upvalues + 2), f.name); - } - lua.pop(num_upvalues); - } - - /// Sets the metatable of the object on the top of the stack as the metatable associated - /// with `table_name` in the registry - /// See https://www.lua.org/manual/5.3/manual.html#luaL_setmetatable - pub fn setMetatableRegistry(lua: *Lua, table_name: [:0]const u8) void { - c.luaL_setmetatable(lua.state, table_name.ptr); - } - - /// This function works like `Lua.checkUserdata()` except it returns a Zig error instead of raising a Lua error on fail - /// See https://www.lua.org/manual/5.3/manual.html#luaL_testudata - pub fn testUserdata(lua: *Lua, comptime T: type, arg: i32, name: [:0]const u8) !*T { - if (c.luaL_testudata(lua.state, arg, name.ptr)) |ptr| { - return opaqueCast(T, ptr); - } else return error.Fail; - } - - /// This function works like `Lua.checkUserdataSlice()` except it returns a Zig error instead of raising a Lua error on fail - /// See https://www.lua.org/manual/5.3/manual.html#luaL_checkudata - pub fn testUserdataSlice(lua: *Lua, comptime T: type, arg: i32, name: [:0]const u8) ![]T { - if (c.luaL_testudata(lua.state, arg, name.ptr)) |ptr| { - const size = lua.rawLen(arg) / @sizeOf(T); - return @as([*]T, @ptrCast(@alignCast(ptr)))[0..size]; - } else return error.Fail; - } - - /// Converts any Lua value at the given index into a string in a reasonable format - /// See https://www.lua.org/manual/5.3/manual.html#luaL_tolstring - pub fn toBytesFmt(lua: *Lua, index: i32) [:0]const u8 { - var length: usize = undefined; - const ptr = c.luaL_tolstring(lua.state, index, &length); - return ptr[0..length :0]; - } - - /// Creates and pushes a traceback of the stack of `other` - /// See https://www.lua.org/manual/5.3/manual.html#luaL_traceback - pub fn traceback(lua: *Lua, other: *Lua, msg: [:0]const u8, level: i32) void { - c.luaL_traceback(lua.state, other.state, msg.ptr, level); - } - - /// Returns the name of the type of the value at the given `index` - /// See https://www.lua.org/manual/5.3/manual.html#luaL_typename - pub fn typeNameIndex(lua: *Lua, index: i32) [:0]const u8 { - return std.mem.span(c.luaL_typename(lua.state, index)); - } - - /// Releases the reference `r` from the table at index `index` - /// See https://www.lua.org/manual/5.3/manual.html#luaL_unref - pub fn unref(lua: *Lua, index: i32, r: i32) void { - c.luaL_unref(lua.state, index, r); - } - - /// Pushes onto the stack a string identifying the current position of the control - /// at the call stack `level` - /// See https://www.lua.org/manual/5.3/manual.html#luaL_where - pub fn where(lua: *Lua, level: i32) void { - c.luaL_where(lua.state, level); - } - - // Standard library loading functions - - /// Opens the specified standard library functions - /// Behaves like openLibs, but allows specifying which libraries - /// to expose to the global table rather than all of them - /// See https://www.lua.org/manual/5.3/manual.html#luaL_openlibs - pub fn open(lua: *Lua, libs: Libs) void { - if (libs.base) lua.requireF("_G", c.luaopen_base, true); - if (libs.coroutine) lua.requireF(c.LUA_COLIBNAME, c.luaopen_coroutine, true); - if (libs.package) lua.requireF(c.LUA_LOADLIBNAME, c.luaopen_package, true); - if (libs.string) lua.requireF(c.LUA_STRLIBNAME, c.luaopen_string, true); - if (libs.utf8) lua.requireF(c.LUA_UTF8LIBNAME, c.luaopen_utf8, true); - if (libs.table) lua.requireF(c.LUA_TABLIBNAME, c.luaopen_table, true); - if (libs.math) lua.requireF(c.LUA_MATHLIBNAME, c.luaopen_math, true); - if (libs.io) lua.requireF(c.LUA_IOLIBNAME, c.luaopen_io, true); - if (libs.os) lua.requireF(c.LUA_OSLIBNAME, c.luaopen_os, true); - if (libs.debug) lua.requireF(c.LUA_DBLIBNAME, c.luaopen_debug, true); - } - - /// Open all standard libraries - /// See https://www.lua.org/manual/5.3/manual.html#luaL_openlibs - pub fn openLibs(lua: *Lua) void { - c.luaL_openlibs(lua.state); - } - - /// Open the basic standard library - pub fn openBase(lua: *Lua) void { - _ = c.luaopen_base(lua.state); - } - - /// Open the coroutine standard library - pub fn openCoroutine(lua: *Lua) void { - _ = c.luaopen_coroutine(lua.state); - } - - /// Open the package standard library - pub fn openPackage(lua: *Lua) void { - _ = c.luaopen_package(lua.state); - } - - /// Open the string standard library - pub fn openString(lua: *Lua) void { - _ = c.luaopen_string(lua.state); - } - - /// Open the UTF-8 standard library - pub fn openUtf8(lua: *Lua) void { - _ = c.luaopen_utf8(lua.state); - } - - /// Open the table standard library - pub fn openTable(lua: *Lua) void { - _ = c.luaopen_table(lua.state); - } - - /// Open the math standard library - pub fn openMath(lua: *Lua) void { - _ = c.luaopen_math(lua.state); - } - - /// Open the io standard library - pub fn openIO(lua: *Lua) void { - _ = c.luaopen_io(lua.state); - } - - /// Open the os standard library - pub fn openOS(lua: *Lua) void { - _ = c.luaopen_os(lua.state); - } - - /// Open the debug standard library - pub fn openDebug(lua: *Lua) void { - _ = c.luaopen_debug(lua.state); - } -}; - -/// A string buffer allowing for Zig code to build Lua strings piecemeal -/// All LuaBuffer functions are wrapped in this struct to make the API more convenient to use -pub const Buffer = struct { - b: LuaBuffer = undefined, - - /// Initialize a Lua string buffer - /// See https://www.lua.org/manual/5.3/manual.html#luaL_buffinit - pub fn init(buf: *Buffer, lua: Lua) void { - c.luaL_buffinit(lua.state, &buf.b); - } - - /// Initialize a Lua string buffer with an initial size - /// See https://www.lua.org/manual/5.3/manual.html#luaL_buffinitsize - pub fn initSize(buf: *Buffer, lua: Lua, size: usize) []u8 { - return c.luaL_buffinitsize(lua.state, &buf.b, size)[0..size]; - } - - /// Internal Lua type for a string buffer - pub const LuaBuffer = c.luaL_Buffer; - - pub const buffer_size = c.LUAL_BUFFERSIZE; - - /// Adds `byte` to the buffer - /// See https://www.lua.org/manual/5.3/manual.html#luaL_addchar - pub fn addChar(buf: *Buffer, byte: u8) void { - // could not be translated by translate-c - var lua_buf = &buf.b; - if (lua_buf.n >= lua_buf.size) _ = buf.prepSize(1); - lua_buf.b[lua_buf.n] = byte; - lua_buf.n += 1; - } - - /// Adds the string to the buffer - /// See https://www.lua.org/manual/5.3/manual.html#luaL_addlstring - pub fn addBytes(buf: *Buffer, str: []const u8) void { - c.luaL_addlstring(&buf.b, str.ptr, str.len); - } - - /// Adds to the buffer a string of `length` previously copied to the buffer area - /// See https://www.lua.org/manual/5.3/manual.html#luaL_addsize - pub fn addSize(buf: *Buffer, length: usize) void { - // another function translate-c couldn't handle - // c.luaL_addsize(&buf.b, length); - var lua_buf = &buf.b; - lua_buf.n += length; - } - - /// Adds the zero-terminated string pointed to by `str` to the buffer - /// See https://www.lua.org/manual/5.3/manual.html#luaL_addstring - pub fn addString(buf: *Buffer, str: [:0]const u8) void { - c.luaL_addstring(&buf.b, str.ptr); - } - - /// Adds the value on the top of the stack to the buffer - /// Pops the value - /// See https://www.lua.org/manual/5.3/manual.html#luaL_addvalue - pub fn addValue(buf: *Buffer) void { - c.luaL_addvalue(&buf.b); - } - - /// Equivalent to prepSize with a buffer size of Buffer.buffer_size - /// See https://www.lua.org/manual/5.3/manual.html#luaL_prepbuffer - pub fn prep(buf: *Buffer) []u8 { - return buf.prepSize(buffer_size)[0..buffer_size]; - } - - /// Returns an address to a space of `size` where you can copy a string - /// to be added to the buffer - /// you must call `Buffer.addSize` to actually add it to the buffer - /// See https://www.lua.org/manual/5.3/manual.html#luaL_prepbuffsize - pub fn prepSize(buf: *Buffer, size: usize) []u8 { - return c.luaL_prepbuffsize(&buf.b, size)[0..size]; - } - - /// Finishes the use of the buffer leaving the final string on the top of the stack - /// See https://www.lua.org/manual/5.3/manual.html#luaL_pushresult - pub fn pushResult(buf: *Buffer) void { - c.luaL_pushresult(&buf.b); - } - - /// Equivalent to `Buffer.addSize()` followed by `Buffer.pushResult()` - /// See https://www.lua.org/manual/5.3/manual.html#luaL_pushresultsize - pub fn pushResultSize(buf: *Buffer, size: usize) void { - c.luaL_pushresultsize(&buf.b, size); - } -}; - -// Helper functions to make the ziglua API easier to use - -/// Casts the opaque pointer to a pointer of the given type with the proper alignment -/// Useful for casting pointers from the Lua API like userdata or other data -pub inline fn opaqueCast(comptime T: type, ptr: *anyopaque) *T { - return @ptrCast(@alignCast(ptr)); -} - -pub const ZigFn = fn (lua: *Lua) i32; -pub const ZigHookFn = fn (lua: *Lua, event: Event, info: *DebugInfo) void; -pub const ZigContFn = fn (lua: *Lua, status: Status, ctx: Context) i32; -pub const ZigReaderFn = fn (lua: *Lua, data: *anyopaque) ?[]const u8; -pub const ZigWriterFn = fn (lua: *Lua, buf: []const u8, data: *anyopaque) bool; - -fn TypeOfWrap(comptime T: type) type { - return switch (T) { - LuaState => Lua, - ZigFn => CFn, - ZigHookFn => CHookFn, - ZigContFn => CContFn, - ZigReaderFn => CReaderFn, - ZigWriterFn => CWriterFn, - else => @compileError("unsupported type given to wrap: '" ++ @typeName(T) ++ "'"), - }; -} - -/// Wraps the given value for use in the Lua API -/// Supports the following: -/// * `LuaState` => `Lua` -pub fn wrap(comptime value: anytype) TypeOfWrap(@TypeOf(value)) { - const T = @TypeOf(value); - return switch (T) { - LuaState => Lua{ .state = value }, - ZigFn => wrapZigFn(value), - ZigHookFn => wrapZigHookFn(value), - ZigContFn => wrapZigContFn(value), - ZigReaderFn => wrapZigReaderFn(value), - ZigWriterFn => wrapZigWriterFn(value), - else => @compileError("unsupported type given to wrap: '" ++ @typeName(T) ++ "'"), - }; -} - -/// Wrap a ZigFn in a CFn for passing to the API -fn wrapZigFn(comptime f: ZigFn) CFn { - return struct { - fn inner(state: ?*LuaState) callconv(.C) c_int { - // this is called by Lua, state should never be null - var lua: Lua = .{ .state = state.? }; - return @call(.always_inline, f, .{&lua}); - } - }.inner; -} - -/// Wrap a ZigHookFn in a CHookFn for passing to the API -fn wrapZigHookFn(comptime f: ZigHookFn) CHookFn { - return struct { - fn inner(state: ?*LuaState, ar: ?*Debug) callconv(.C) void { - // this is called by Lua, state should never be null - var lua: Lua = .{ .state = state.? }; - var info: DebugInfo = .{ - .current_line = if (ar.?.currentline == -1) null else ar.?.currentline, - .private = @ptrCast(ar.?.i_ci), - }; - @call(.always_inline, f, .{ &lua, @as(Event, @enumFromInt(ar.?.event)), &info }); - } - }.inner; -} - -/// Wrap a ZigContFn in a CContFn for passing to the API -fn wrapZigContFn(comptime f: ZigContFn) CContFn { - return struct { - fn inner(state: ?*LuaState, status: c_int, ctx: Context) callconv(.C) c_int { - // this is called by Lua, state should never be null - var lua: Lua = .{ .state = state.? }; - return @call(.always_inline, f, .{ &lua, @as(Status, @enumFromInt(status)), ctx }); - } - }.inner; -} - -/// Wrap a ZigReaderFn in a CReaderFn for passing to the API -fn wrapZigReaderFn(comptime f: ZigReaderFn) CReaderFn { - return struct { - fn inner(state: ?*LuaState, data: ?*anyopaque, size: [*c]usize) callconv(.C) [*c]const u8 { - var lua: Lua = .{ .state = state.? }; - if (@call(.always_inline, f, .{ &lua, data.? })) |buffer| { - size.* = buffer.len; - return buffer.ptr; - } else { - size.* = 0; - return null; - } - } - }.inner; -} - -/// Wrap a ZigWriterFn in a CWriterFn for passing to the API -fn wrapZigWriterFn(comptime f: ZigWriterFn) CWriterFn { - return struct { - fn inner(state: ?*LuaState, buf: ?*const anyopaque, size: usize, data: ?*anyopaque) callconv(.C) c_int { - // this is called by Lua, state should never be null - var lua: Lua = .{ .state = state.? }; - const buffer = @as([*]const u8, @ptrCast(buf))[0..size]; - const result = @call(.always_inline, f, .{ &lua, buffer, data.? }); - // it makes more sense for the inner writer function to return false for failure, - // so negate the result here - return @intFromBool(!result); - } - }.inner; -} - -/// Export a Zig function to be used as a the entry point to a Lua module -/// -/// Exported as luaopen_[name] -pub fn exportFn(comptime name: []const u8, comptime func: ZigFn) CFn { - return struct { - fn luaopen(state: ?*LuaState) callconv(.C) c_int { - const declaration = comptime wrap(func); - - return @call(.always_inline, declaration, .{state}); - } - - comptime { - @export(luaopen, .{ .name = "luaopen_" ++ name }); - } - }.luaopen; -} diff --git a/src/tests.zig b/src/tests.zig index 569d68b..e64aef6 100644 --- a/src/tests.zig +++ b/src/tests.zig @@ -1107,18 +1107,18 @@ test "userdata and uservalues" { } else if (ziglua.lang == .lua54) { // assign the user values lua.pushNumber(1234.56); - try lua.setIndexUserValue(1, 1); + try lua.setUserValue(1, 1); _ = lua.pushString("test string"); - try lua.setIndexUserValue(1, 2); + try lua.setUserValue(1, 2); - try expectEqual(.number, try lua.getIndexUserValue(1, 1)); + try expectEqual(.number, try lua.getUserValue(1, 1)); try expectEqual(1234.56, try lua.toNumber(-1)); - try expectEqual(.string, try lua.getIndexUserValue(1, 2)); + try expectEqual(.string, try lua.getUserValue(1, 2)); try expectEqualStrings("test string", try lua.toBytes(-1)); - try expectError(error.Fail, lua.setIndexUserValue(1, 3)); - try expectError(error.Fail, lua.getIndexUserValue(1, 3)); + try expectError(error.Fail, lua.setUserValue(1, 3)); + try expectError(error.Fail, lua.getUserValue(1, 3)); } } From ef665e52b4e66ea46ce28554657415793650cee6 Mon Sep 17 00:00:00 2001 From: Nathan Craddock Date: Sat, 27 Jan 2024 20:29:47 -0700 Subject: [PATCH 2/5] Merge lib52.zig into lib.zig --- src/lib.zig | 340 +++++++-- src/lib52.zig | 2018 ------------------------------------------------- src/tests.zig | 52 +- 3 files changed, 298 insertions(+), 2112 deletions(-) delete mode 100644 src/lib52.zig diff --git a/src/lib.zig b/src/lib.zig index d890dae..da90e3b 100644 --- a/src/lib.zig +++ b/src/lib.zig @@ -26,8 +26,17 @@ const Allocator = std.mem.Allocator; /// See https://www.lua.org/manual/5.4/manual.html#lua_Alloc for more details pub const AllocFn = *const fn (data: ?*anyopaque, ptr: ?*anyopaque, osize: usize, nsize: usize) callconv(.C) ?*anyopaque; -/// Operations supported by `Lua.arith()` -pub const ArithOperator = enum(u4) { +const ArithOperator52 = enum(u4) { + add = c.LUA_OPADD, + sub = c.LUA_OPSUB, + mul = c.LUA_OPMUL, + div = c.LUA_OPDIV, + mod = c.LUA_OPMOD, + pow = c.LUA_OPPOW, + negate = c.LUA_OPUNM, +}; + +const ArithOperator53 = enum(u4) { add = c.LUA_OPADD, sub = c.LUA_OPSUB, mul = c.LUA_OPMUL, @@ -44,6 +53,12 @@ pub const ArithOperator = enum(u4) { shr = c.LUA_OPSHR, }; +/// Operations supported by `Lua.arith()` +pub const ArithOperator = switch (lang) { + .lua53, .lua54 => ArithOperator53, + else => ArithOperator52, +}; + /// Type for C functions /// See https://www.lua.org/manual/5.4/manual.html#lua_CFunction for the protocol pub const CFn = *const fn (state: ?*LuaState) callconv(.C) c_int; @@ -58,7 +73,7 @@ pub const CompareOperator = enum(u2) { /// The internal Lua debug structure const Debug = c.lua_Debug; -const DebugInfo53 = struct { +const DebugInfo52 = struct { source: [:0]const u8 = undefined, src_len: usize = 0, short_src: [c.LUA_IDSIZE:0]u8 = undefined, @@ -169,7 +184,7 @@ const DebugInfo54 = struct { pub const DebugInfo = switch (lang) { .lua54 => DebugInfo54, - else => DebugInfo53, + else => DebugInfo52, }; /// The superset of all errors returned from ziglua @@ -187,7 +202,7 @@ pub const Error = error{ /// A file-releated error File, } || switch (lang) { - .lua53 => error{ + .lua52, .lua53 => error{ /// A memory in a __gc metamethod GCMetaMethod, }, @@ -256,8 +271,20 @@ pub const Context = isize; /// Type for continuation functions pub const CContFn = *const fn (state: ?*LuaState, status: c_int, ctx: Context) callconv(.C) c_int; -/// Bitflag for the Lua standard libraries -pub const Libs = packed struct { +pub const Libs52 = packed struct { + base: bool = false, + coroutine: bool = false, + package: bool = false, + string: bool = false, + table: bool = false, + math: bool = false, + io: bool = false, + os: bool = false, + debug: bool = false, + bit: bool = false, +}; + +pub const Libs53 = packed struct { base: bool = false, coroutine: bool = false, package: bool = false, @@ -270,6 +297,12 @@ pub const Libs = packed struct { debug: bool = false, }; +/// Bitflag for the Lua standard libraries +pub const Libs = switch(lang) { + .lua53, .lua54 => Libs53, + else => Libs52, +}; + /// The type of the opaque structure that points to a thread and the state of a Lua interpreter pub const LuaState = c.lua_State; @@ -361,7 +394,7 @@ const StatusCode = struct { pub const err_error = c.LUA_ERRERR; pub const err_gcmm = switch (lang) { - .lua53 => c.LUA_ERRGCMM, + .lua52, .lua53 => c.LUA_ERRGCMM, else => @compileError("err_gcm not defined"), }; }; @@ -484,12 +517,21 @@ pub const Lua = struct { lua.callCont(num_args, num_results, 0, null); } - /// Like call, but allows the called function to yield - /// See https://www.lua.org/manual/5.4/manual.html#lua_callk - pub fn callCont(lua: *Lua, num_args: i32, num_results: i32, ctx: Context, k: ?CContFn) void { + fn callCont52(lua: *Lua, num_args: i32, num_results: i32, ctx: i32, k: ?CFn) void { c.lua_callk(lua.state, num_args, num_results, ctx, k); } + fn callCont53(lua: *Lua, num_args: i32, num_results: i32, ctx: Context, k: ?CContFn) void { + c.lua_callk(lua.state, num_args, num_results, ctx, k); + } + + /// Like call, but allows the called function to yield + /// See https://www.lua.org/manual/5.4/manual.html#lua_callk + pub const callCont = switch (lang) { + .lua53, .lua54 => callCont53, + else => callCont52, + }; + /// Ensures that the stack has space for at least n extra arguments /// Returns an error if more stack space cannot be allocated /// Never shrinks the stack @@ -558,13 +600,22 @@ pub const Lua = struct { c.lua_createtable(lua.state, num_arr, num_rec); } + fn dump52(lua: *Lua, writer: CWriterFn, data: *anyopaque) !void { + if (c.lua_dump(lua.state, writer, data) != 0) return error.Fail; + } + + fn dump53(lua: *Lua, writer: CWriterFn, data: *anyopaque, strip: bool) !void { + if (c.lua_dump(lua.state, writer, data, @intFromBool(strip)) != 0) return error.Fail; + } + /// Dumps a function as a binary chunk /// Data is a pointer passed to the writer function /// Returns an error if writing was unsuccessful /// See https://www.lua.org/manual/5.4/manual.html#lua_dump - pub fn dump(lua: *Lua, writer: CWriterFn, data: *anyopaque, strip: bool) !void { - if (c.lua_dump(lua.state, writer, data, @intFromBool(strip)) != 0) return error.Fail; - } + pub const dump = switch (lang) { + .lua53, .lua54 => dump53, + else => dump52, + }; /// Raises a Lua error using the value at the top of the stack as the error object /// Does a longjump and therefore never returns @@ -650,12 +701,22 @@ pub const Lua = struct { return c.lua_gc(lua.state, c.LUA_GCINC, pause, step_mul, step_size) == c.LUA_GCGEN; } + fn gcSetGenerational52(lua: *Lua) void { + _ = c.lua_gc(lua.state, c.LUA_GCGEN, 0); + } + + fn gcSetGenerational54(lua: *Lua, minor_mul: i32, major_mul: i32) bool { + return c.lua_gc(lua.state, c.LUA_GCGEN, minor_mul, major_mul) == c.LUA_GCINC; + } + /// Changes the collector to generational mode /// Returns true if the previous mode was incremental /// See https://www.lua.org/manual/5.4/manual.html#lua_gc - pub fn gcSetGenerational(lua: *Lua, minor_mul: i32, major_mul: i32) bool { - return c.lua_gc(lua.state, c.LUA_GCGEN, minor_mul, major_mul) == c.LUA_GCINC; - } + pub const gcSetGenerational = switch (lang) { + .lua52 => gcSetGenerational52, + .lua54 => gcSetGenerational54, + else => @compileError("gcSetGenerational() not available"), + }; /// Sets `pause` as the new value for the pause of the collector /// Returns the previous value of the pause @@ -680,6 +741,21 @@ pub const Lua = struct { return c.lua_getallocf(lua.state, @ptrCast(data)).?; } + /// Called by a continuation function to retrieve the status of the thread and context information + /// See https://www.lua.org/manual/5.2/manual.html#lua_getctx + pub fn getContext(lua: *Lua) !?i32 { + var ctx: i32 = undefined; + const ret = c.lua_getctx(lua.state, &ctx); + switch (ret) { + StatusCode.ok => return null, + StatusCode.yield => return ctx, + StatusCode.err_runtime => return error.Runtime, + StatusCode.err_memory => return error.Memory, + StatusCode.err_error => return error.MsgHandler, + else => unreachable, + } + } + /// Returns a slice of a raw memory area associated with the given Lua state /// The application may use this area for any purpose; Lua does not use it for anything /// This area has a size of a pointer to void @@ -691,14 +767,29 @@ pub const Lua = struct { /// Pushes onto the stack the value t[key] where t is the value at the given index /// See https://www.lua.org/manual/5.4/manual.html#lua_getfield pub fn getField(lua: *Lua, index: i32, key: [:0]const u8) LuaType { - return @enumFromInt(c.lua_getfield(lua.state, index, key.ptr)); + switch (lang) { + .lua53, .lua54 => return @enumFromInt(c.lua_getfield(lua.state, index, key.ptr)), + else => { + c.lua_getfield(lua.state, index, key.ptr); + return lua.typeOf(-1); + } + } } /// Pushes onto the stack the value of the global name and returns the type of that value /// Returns an error if the global does not exist (is nil) /// See https://www.lua.org/manual/5.4/manual.html#lua_getglobal pub fn getGlobal(lua: *Lua, name: [:0]const u8) !LuaType { - const lua_type: LuaType = @enumFromInt(c.lua_getglobal(lua.state, name.ptr)); + const lua_type: LuaType = blk: { + switch (lang) { + .lua53, .lua54 => break :blk @enumFromInt(c.lua_getglobal(lua.state, name.ptr)), + else => { + c.lua_getglobal(lua.state, name.ptr); + break :blk lua.typeOf(-1); + }, + } + }; + if (lua_type == .nil) return error.Fail; return lua_type; } @@ -710,6 +801,11 @@ pub const Lua = struct { return @enumFromInt(c.lua_geti(lua.state, index, i)); } + pub fn getUserValue52(lua: *Lua, index: i32) void { + c.lua_getuservalue(lua.state, index); + } + + // TODO: should all versions of getUserValue possibly fail? fn getUserValue53(lua: *Lua, index: i32) LuaType { return @enumFromInt(c.lua_getuservalue(lua.state, index)); } @@ -725,8 +821,9 @@ pub const Lua = struct { /// Pushes nil if the userdata does not have that value /// See https://www.lua.org/manual/5.4/manual.html#lua_getiuservalue pub const getUserValue = switch (lang) { + .lua53 => getUserValue53, .lua54 => getUserValue54, - else => getUserValue53, + else => getUserValue52, }; /// If the value at the given index has a metatable, the function pushes that metatable onto the stack @@ -740,7 +837,13 @@ pub const Lua = struct { /// Returns the type of the pushed value /// See https://www.lua.org/manual/5.4/manual.html#lua_gettable pub fn getTable(lua: *Lua, index: i32) LuaType { - return @enumFromInt(c.lua_gettable(lua.state, index)); + switch (lang) { + .lua53, .lua54 => return @enumFromInt(c.lua_gettable(lua.state, index)), + else => { + c.lua_gettable(lua.state, index); + return lua.typeOf(-1); + } + } } /// Returns the index of the top element in the stack @@ -753,9 +856,7 @@ pub const Lua = struct { /// Moves the top element into the given valid `index` shifting up any elements to make room /// See https://www.lua.org/manual/5.4/manual.html#lua_insert pub fn insert(lua: *Lua, index: i32) void { - // translate-c cannot translate this macro correctly - // c.lua_insert(lua.state, index); - lua.rotate(index, 1); + c.lua_insert(lua.state, index); } /// Returns true if the value at the given index is a boolean @@ -918,7 +1019,7 @@ pub const Lua = struct { /// This function allocates a new userdata of the given type. /// Returns a pointer to the Lua-owned data /// See https://www.lua.org/manual/5.3/manual.html#lua_newuserdata - fn newUserdata53(lua: *Lua, comptime T: type) *T { + fn newUserdata52(lua: *Lua, comptime T: type) *T { // safe to .? because this function throws a Lua error on out of memory // so the returned pointer should never be null const ptr = c.lua_newuserdata(lua.state, @sizeOf(T)).?; @@ -927,7 +1028,7 @@ pub const Lua = struct { pub const newUserdata = switch (lang) { .lua54 => newUserdata54, - else => newUserdata53, + else => newUserdata52, }; /// This function creates and pushes a slice of full userdata onto the stack with user_values associated Lua values. @@ -942,7 +1043,7 @@ pub const Lua = struct { /// This function creates and pushes a slice of full userdata onto the stack. /// Returns a slice to the Lua-owned data. /// See https://www.lua.org/manual/5.3/manual.html#lua_newuserdata - fn newUserdataSlice53(lua: *Lua, comptime T: type, size: usize) []T { + fn newUserdataSlice52(lua: *Lua, comptime T: type, size: usize) []T { // safe to .? because this function throws a Lua error on out of memory const ptr = c.lua_newuserdata(lua.state, @sizeOf(T) * size).?; return @as([*]T, @ptrCast(@alignCast(ptr)))[0..size]; @@ -950,7 +1051,7 @@ pub const Lua = struct { pub const newUserdataSlice = switch (lang) { .lua54 => newUserdataSlice54, - else => newUserdataSlice53, + else => newUserdataSlice52, }; /// Pops a key from the stack, and pushes a key-value pair from the table at the given index @@ -997,9 +1098,19 @@ pub const Lua = struct { }; } - /// Behaves exactly like `Lua.protectedCall()` except that it allows the called function to yield - /// See https://www.lua.org/manual/5.4/manual.html#lua_pcallk - pub fn protectedCallCont(lua: *Lua, num_args: i32, num_results: i32, msg_handler: i32, ctx: Context, k: CContFn) !void { + fn protectedCallCont52(lua: *Lua, num_args: i32, num_results: i32, msg_handler: i32, ctx: i32, k: CFn) !void { + const ret = c.lua_pcallk(lua.state, num_args, num_results, msg_handler, ctx, k); + switch (ret) { + StatusCode.ok => return, + StatusCode.err_runtime => return error.Runtime, + StatusCode.err_memory => return error.Memory, + StatusCode.err_error => return error.MsgHandler, + StatusCode.err_gcmm => return error.GCMetaMethod, + else => unreachable, + } + } + + fn protectedCallCont53(lua: *Lua, num_args: i32, num_results: i32, msg_handler: i32, ctx: Context, k: CContFn) !void { const ret = c.lua_pcallk(lua.state, num_args, num_results, msg_handler, ctx, k); return switch (lang) { @@ -1021,6 +1132,13 @@ pub const Lua = struct { }; } + /// Behaves exactly like `Lua.protectedCall()` except that it allows the called function to yield + /// See https://www.lua.org/manual/5.4/manual.html#lua_pcallk + pub const protectedCallCont = switch (lang) { + .lua53, .lua54 => protectedCallCont53, + else => protectedCallCont52, + }; + /// Pops `n` elements from the top of the stack /// See https://www.lua.org/manual/5.4/manual.html#lua_pop pub fn pop(lua: *Lua, n: i32) void { @@ -1107,6 +1225,12 @@ pub const Lua = struct { return c.lua_pushthread(lua.state) != 0; } + /// Pushes a number with value n onto the stack + /// See https://www.lua.org/manual/5.2/manual.html#lua_pushunsigned + pub fn pushUnsigned(lua: *Lua, n: Unsigned) void { + return c.lua_pushunsigned(lua.state, n); + } + /// Pushes a copy of the element at the given index onto the stack /// See https://www.lua.org/manual/5.4/manual.html#lua_pushvalue pub fn pushValue(lua: *Lua, index: i32) void { @@ -1123,22 +1247,46 @@ pub const Lua = struct { /// Similar to `Lua.getTable()` but does a raw access (without metamethods) /// See https://www.lua.org/manual/5.4/manual.html#lua_rawget + /// TODO: should this be renamed to getTableRaw (seems more logical)? pub fn rawGetTable(lua: *Lua, index: i32) LuaType { - return @enumFromInt(c.lua_rawget(lua.state, index)); + switch (lang) { + .lua53, .lua54 => return @enumFromInt(c.lua_rawget(lua.state, index)), + else => { + c.lua_rawget(lua.state, index); + return lua.typeOf(-1); + } + } } + const RawGetIndexNType = switch (lang) { + .lua52 => i32, + else => Integer, + }; + /// Pushes onto the stack the value t[n], where `t` is the table at the given `index` /// Returns the `LuaType` of the pushed value /// See https://www.lua.org/manual/5.4/manual.html#lua_rawgeti - pub fn rawGetIndex(lua: *Lua, index: i32, n: Integer) LuaType { - return @enumFromInt(c.lua_rawgeti(lua.state, index, n)); + pub fn rawGetIndex(lua: *Lua, index: i32, n: RawGetIndexNType) LuaType { + switch (lang) { + .lua53, .lua54 => return @enumFromInt(c.lua_rawgeti(lua.state, index, n)), + else => { + c.lua_rawgeti(lua.state, index, n); + return lua.typeOf(-1); + } + } } /// Pushes onto the stack the value t[k] where t is the table at the given `index` and /// k is the pointer `p` represented as a light userdata /// rawgetp pub fn rawGetPtr(lua: *Lua, index: i32, p: *const anyopaque) LuaType { - return @enumFromInt(c.lua_rawgetp(lua.state, index, p)); + switch (lang) { + .lua53, .lua54 => return @enumFromInt(c.lua_rawgetp(lua.state, index, p)), + else => { + c.lua_rawgetp(lua.state, index, p); + return lua.typeOf(-1); + } + } } /// Returns the raw length of the value at the given index @@ -1146,7 +1294,10 @@ pub const Lua = struct { /// For userdata it is the size of the block of memory /// For other values the call returns 0 /// See https://www.lua.org/manual/5.4/manual.html#lua_rawlen - pub fn rawLen(lua: *Lua, index: i32) Unsigned { + pub fn rawLen(lua: *Lua, index: i32) switch (lang) { + .lua52, .lua53 => usize, + else => Unsigned, + } { return c.lua_rawlen(lua.state, index); } @@ -1156,11 +1307,16 @@ pub const Lua = struct { c.lua_rawset(lua.state, index); } + const RawSetIndexIType = switch (lang) { + .lua52 => i32, + else => Integer, + }; + /// Does the equivalent of t[`i`] = v where t is the table at the given `index` /// and v is the value at the top of the stack /// Pops the value from the stack. Does not use __newindex metavalue /// See https://www.lua.org/manual/5.4/manual.html#lua_rawseti - pub fn rawSetIndex(lua: *Lua, index: i32, i: Integer) void { + pub fn rawSetIndex(lua: *Lua, index: i32, i: RawSetIndexIType) void { c.lua_rawseti(lua.state, index, i); } @@ -1181,10 +1337,7 @@ pub const Lua = struct { /// Removes the element at the given valid `index` shifting down elements to fill the gap /// See https://www.lua.org/manual/5.4/manual.html#lua_remove pub fn remove(lua: *Lua, index: i32) void { - // translate-c cannot translate this macro correctly - // c.lua_remove(lua.state, index); - lua.rotate(index, -1); - lua.pop(1); + c.lua_remove(lua.state, index); } /// Moves the top element into the given valid `index` without shifting any elements, @@ -1205,7 +1358,7 @@ pub const Lua = struct { /// Starts and resumes a coroutine in the given thread /// See https://www.lua.org/manual/5.3/manual.html#lua_resume - fn resumeThread53(lua: *Lua, from: ?Lua, num_args: i32) !ResumeStatus { + fn resumeThread52(lua: *Lua, from: ?Lua, num_args: i32) !ResumeStatus { const from_state = if (from) |from_val| from_val.state else null; const thread_status = c.lua_resume(lua.state, from_state, num_args); switch (thread_status) { @@ -1232,7 +1385,7 @@ pub const Lua = struct { pub const resumeThread = switch (lang) { .lua54 => resumeThread54, - else => resumeThread53, + else => resumeThread52, }; /// Rotates the stack elements between the valid `index` and the top of the stack @@ -1273,7 +1426,7 @@ pub const Lua = struct { /// the full userdata at the given index /// Returns an error if the userdata does not have that value /// See https://www.lua.org/manual/5.3/manual.html#lua_setuservalue - fn setUserValue53(lua: *Lua, index: i32) !void { + fn setUserValue52(lua: *Lua, index: i32) !void { c.lua_setuservalue(lua.state, index); } @@ -1287,7 +1440,7 @@ pub const Lua = struct { pub const setUserValue = switch (lang) { .lua54 => setUserValue54, - else => setUserValue53, + else => setUserValue52, }; /// Pops a table or nil from the stack and sets that value as the new metatable for the @@ -1413,6 +1566,16 @@ pub const Lua = struct { return error.Fail; } + /// Converts the Lua value at the given index to an unsigned integer + /// The Lua value must be a number or a string convertible to a number otherwise an error is returned + /// See https://www.lua.org/manual/5.2/manual.html#lua_tounsignedx + pub fn toUnsigned(lua: *Lua, index: i32) !Unsigned { + var success: c_int = undefined; + const result = c.lua_tounsignedx(lua.state, index, &success); + if (success == 0) return error.Fail; + return result; + } + /// Returns a Lua-owned userdata pointer of the given type at the given index. /// Works for both light and full userdata. /// Returns an error if the value is not a userdata. @@ -1455,7 +1618,7 @@ pub const Lua = struct { /// Returns the version number of this core /// When `caller_version` is true it returns the address of the version running the call /// See https://www.lua.org/manual/5.3/manual.html#lua_version - fn version53(lua: *Lua, caller_version: bool) *const Number { + fn version52(lua: *Lua, caller_version: bool) *const Number { if (caller_version) return c.lua_version(null); return c.lua_version(lua.state); } @@ -1468,7 +1631,7 @@ pub const Lua = struct { pub const version = switch (lang) { .lua54 => version54, - else => version53, + else => version52, }; /// Emits a warning with the given `msg` @@ -1493,14 +1656,24 @@ pub const Lua = struct { unreachable; } - /// Yields this coroutine (thread) - /// This function never returns - /// See https://www.lua.org/manual/5.4/manual.html#lua_yieldk - pub fn yieldCont(lua: *Lua, num_results: i32, ctx: Context, k: CContFn) noreturn { + fn yieldCont52(lua: *Lua, num_results: i32, ctx: i32, k: CFn) noreturn { _ = c.lua_yieldk(lua.state, num_results, ctx, k); unreachable; } + fn yieldCont53(lua: *Lua, num_results: i32, ctx: Context, k: CContFn) noreturn { + _ = c.lua_yieldk(lua.state, num_results, ctx, k); + unreachable; + } + + /// Yields this coroutine (thread) + /// This function never returns + /// See https://www.lua.org/manual/5.4/manual.html#lua_yieldk + pub const yieldCont = switch (lang) { + .lua53, .lua54 => yieldCont53, + else => yieldCont52, + }; + // Debug library functions // // The debug interface functions are included in alphabetical order @@ -1611,7 +1784,8 @@ pub const Lua = struct { /// See https://www.lua.org/manual/5.4/manual.html#lua_sethook pub fn setHook(lua: *Lua, hook_fn: CHookFn, mask: HookMask, count: i32) void { const hook_mask = HookMask.toInt(mask); - c.lua_sethook(lua.state, hook_fn, hook_mask, count); + // Lua 5.1 and 5.2 always return 1. Other versions return void + _ = c.lua_sethook(lua.state, hook_fn, hook_mask, count); } /// Sets the value of a local variable @@ -1691,6 +1865,13 @@ pub const Lua = struct { c.luaL_checkany(lua.state, arg); } + /// Checks whether the function argument `arg` is a number and returns this number cast to an i32 + /// See https://www.lua.org/manual/5.2/manual.html#luaL_checkint + /// TODO: is this ever useful? + pub fn checkInt(lua: *Lua, arg: i32) i32 { + return c.luaL_checkint(lua.state, arg); + } + /// Checks whether the function argument `arg` is an integer (or can be converted to an integer) and returns the integer /// See https://www.lua.org/manual/5.4/manual.html#luaL_checkinteger pub fn checkInteger(lua: *Lua, arg: i32) Integer { @@ -1772,12 +1953,27 @@ pub const Lua = struct { return @as([*]T, @ptrCast(@alignCast(ptr)))[0..size]; } + /// Checks whether the function argument arg is a number and returns this number cast to an unsigned + /// See https://www.lua.org/manual/5.2/manual.html#luaL_checkunsigned + pub fn checkUnsigned(lua: *Lua, arg: i32) Unsigned { + return c.luaL_checkunsigned(lua.state, arg); + } + + fn checkVersion52(lua: *Lua) void { + return c.luaL_checkversion_(lua.state, c.LUA_VERSION_NUM); + } + + fn checkVersion53(lua: *Lua) void { + return c.luaL_checkversion_(lua.state, c.LUA_VERSION_NUM, c.LUAL_NUMSIZES); + } + /// Checks whether the code making the call and the Lua library being called are using /// the same version of Lua and the same numeric types. /// See https://www.lua.org/manual/5.4/manual.html#luaL_checkversion - pub fn checkVersion(lua: *Lua) void { - return c.luaL_checkversion_(lua.state, c.LUA_VERSION_NUM, c.LUAL_NUMSIZES); - } + pub const checkVersion = switch (lang) { + .lua53, .lua54 => checkVersion53, + else => checkVersion52, + }; /// Loads and runs the given file /// See https://www.lua.org/manual/5.4/manual.html#luaL_dofile @@ -1829,7 +2025,13 @@ pub const Lua = struct { /// TODO: return error when type is nil? /// See https://www.lua.org/manual/5.4/manual.html#luaL_getmetatable pub fn getMetatableRegistry(lua: *Lua, table_name: [:0]const u8) LuaType { - return @enumFromInt(c.luaL_getmetatable(lua.state, table_name.ptr)); + switch (lang) { + .lua53, .lua54 => return @enumFromInt(c.luaL_getmetatable(lua.state, table_name.ptr)), + else => { + c.luaL_getmetatable(lua.state, table_name.ptr); + return lua.typeOf(-1); + }, + } } /// Ensures that the value t[`field`], where t is the value at `index`, is a table, and pushes that table onto the stack. @@ -1934,6 +2136,14 @@ pub const Lua = struct { // luaL_opt (a macro) really isn't that useful, so not going to implement for now + /// If the function argument `arg` is a number, returns this number cast to an i32. + /// If the argument is absent or nil returns `default` + /// See https://www.lua.org/manual/5.2/manual.html#luaL_optint + /// TODO: just like checkInt, is this ever useful? + pub fn optInt(lua: *Lua, arg: i32, default: i32) i32 { + return c.luaL_optint(lua.state, arg, default); + } + /// If the function argument `arg` is an integer, returns the integer /// If the argument is absent or nil returns `default` /// See https://www.lua.org/manual/5.4/manual.html#luaL_optinteger @@ -1967,6 +2177,13 @@ pub const Lua = struct { return c.luaL_optlstring(lua.state, arg, default.ptr, null); } + /// If the function argument is a number, returns this number as an unsigned + /// If the argument is absent or nil returns default, otherwise raises an error + /// See https://www.lua.org/manual/5.2/manual.html#luaL_optunsigned + pub fn optUnsigned(lua: *Lua, arg: i32, default: Unsigned) Unsigned { + return c.luaL_optunsigned(lua.state, arg, default); + } + /// Pushes the fail value onto the stack /// See https://www.lua.org/manual/5.4/manual.html#luaL_pushfail pub fn pushFail(lua: *Lua) void { @@ -2080,12 +2297,15 @@ pub const Lua = struct { if (libs.coroutine) lua.requireF(c.LUA_COLIBNAME, c.luaopen_coroutine, true); if (libs.package) lua.requireF(c.LUA_LOADLIBNAME, c.luaopen_package, true); if (libs.string) lua.requireF(c.LUA_STRLIBNAME, c.luaopen_string, true); - if (libs.utf8) lua.requireF(c.LUA_UTF8LIBNAME, c.luaopen_utf8, true); if (libs.table) lua.requireF(c.LUA_TABLIBNAME, c.luaopen_table, true); if (libs.math) lua.requireF(c.LUA_MATHLIBNAME, c.luaopen_math, true); if (libs.io) lua.requireF(c.LUA_IOLIBNAME, c.luaopen_io, true); if (libs.os) lua.requireF(c.LUA_OSLIBNAME, c.luaopen_os, true); if (libs.debug) lua.requireF(c.LUA_DBLIBNAME, c.luaopen_debug, true); + + if (lang != .lua52 and libs.utf8) lua.requireF(c.LUA_UTF8LIBNAME, c.luaopen_utf8, true); + + if (lang == .lua52 and libs.bit) lua.requireF(c.LUA_BITLIBNAME, c.luaopen_bit32, true); } /// Open all standard libraries @@ -2143,6 +2363,11 @@ pub const Lua = struct { pub fn openDebug(lua: *Lua) void { _ = c.luaopen_debug(lua.state); } + + /// Open the bit32 standard library + pub fn openBit32(lua: *Lua) void { + _ = c.luaopen_bit32(lua.state); + } }; /// A string buffer allowing for Zig code to build Lua strings piecemeal @@ -2296,7 +2521,6 @@ fn TypeOfWrap(comptime T: type) type { pub fn wrap(comptime value: anytype) TypeOfWrap(@TypeOf(value)) { const T = @TypeOf(value); return switch (T) { - LuaState => Lua{ .state = value }, ZigFn => wrapZigFn(value), ZigHookFn => wrapZigHookFn(value), ZigContFn => wrapZigContFn(value), diff --git a/src/lib52.zig b/src/lib52.zig deleted file mode 100644 index 1b77535..0000000 --- a/src/lib52.zig +++ /dev/null @@ -1,2018 +0,0 @@ -//! complete bindings around the Lua C API version 5.2.4 -//! exposes all Lua functionality, with additional Zig helper functions - -const std = @import("std"); - -const c = @cImport({ - @cInclude("lua.h"); - @cInclude("lualib.h"); - @cInclude("lauxlib.h"); -}); - -const config = @import("config"); -pub const lang = config.lang; - -const Allocator = std.mem.Allocator; - -// Types -// -// Lua constants and types are declared below in alphabetical order -// For constants that have a logical grouping (like Operators), Zig enums are used for type safety - -/// The type of function that Lua uses for all internal allocations and frees -/// `data` is an opaque pointer to any data (the allocator), `ptr` is a pointer to the block being alloced/realloced/freed -/// `osize` is the original size or a code, and `nsize` is the new size -/// -/// See https://www.lua.org/manual/5.2/manual.html#lua_Alloc for more details -pub const AllocFn = *const fn (data: ?*anyopaque, ptr: ?*anyopaque, osize: usize, nsize: usize) callconv(.C) ?*anyopaque; - -/// Operations supported by `Lua.arith()` -pub const ArithOperator = enum(u4) { - add = c.LUA_OPADD, - sub = c.LUA_OPSUB, - mul = c.LUA_OPMUL, - div = c.LUA_OPDIV, - mod = c.LUA_OPMOD, - pow = c.LUA_OPPOW, - negate = c.LUA_OPUNM, -}; - -/// Type for C functions -/// See https://www.lua.org/manual/5.2/manual.html#lua_CFunction for the protocol -pub const CFn = *const fn (state: ?*LuaState) callconv(.C) c_int; - -/// Operations supported by `Lua.compare()` -pub const CompareOperator = enum(u2) { - eq = c.LUA_OPEQ, - lt = c.LUA_OPLT, - le = c.LUA_OPLE, -}; - -/// The internal Lua debug structure -const Debug = c.lua_Debug; - -/// The Lua debug interface structure -pub const DebugInfo = struct { - source: [:0]const u8 = undefined, - src_len: usize = 0, - short_src: [c.LUA_IDSIZE:0]u8 = undefined, - - name: ?[:0]const u8 = undefined, - name_what: NameType = undefined, - what: FnType = undefined, - - current_line: ?i32 = null, - first_line_defined: ?i32 = null, - last_line_defined: ?i32 = null, - - num_upvalues: u8 = 0, - num_params: u8 = 0, - - is_vararg: bool = false, - is_tail_call: bool = false, - - private: *anyopaque = undefined, - - pub const NameType = enum { global, local, method, field, upvalue, other }; - - pub const FnType = enum { lua, c, main }; - - pub const Options = packed struct { - @">": bool = false, - f: bool = false, - l: bool = false, - n: bool = false, - S: bool = false, - t: bool = false, - u: bool = false, - L: bool = false, - - fn toString(options: Options) [10:0]u8 { - var str = [_:0]u8{0} ** 10; - var index: u8 = 0; - - inline for (std.meta.fields(Options)) |field| { - if (@field(options, field.name)) { - str[index] = field.name[0]; - index += 1; - } - } - while (index < str.len) : (index += 1) str[index] = 0; - - return str; - } - }; -}; - -/// The superset of all errors returned from ziglua -pub const Error = error{ - /// A generic failure (used when a function can only fail in one way) - Fail, - /// A runtime error - Runtime, - /// A syntax error during precompilation - Syntax, - /// A memory allocation error - Memory, - /// An error while running the message handler - MsgHandler, - /// A file-releated error - File, -}; - -/// The type of event that triggers a hook -pub const Event = enum(u3) { - call = c.LUA_HOOKCALL, - ret = c.LUA_HOOKRET, - line = c.LUA_HOOKLINE, - count = c.LUA_HOOKCOUNT, - tail_call = c.LUA_HOOKTAILCALL, -}; - -/// Type for arrays of functions to be registered -pub const FnReg = struct { - name: [:0]const u8, - func: ?CFn, -}; - -/// Type for debugging hook functions -pub const CHookFn = *const fn (state: ?*LuaState, ar: ?*Debug) callconv(.C) void; - -/// Specifies on which events the hook will be called -pub const HookMask = packed struct { - call: bool = false, - ret: bool = false, - line: bool = false, - count: bool = false, - - /// Converts a HookMask to an integer bitmask - pub fn toInt(mask: HookMask) i32 { - var bitmask: i32 = 0; - if (mask.call) bitmask |= mask_call; - if (mask.ret) bitmask |= mask_ret; - if (mask.line) bitmask |= mask_line; - if (mask.count) bitmask |= mask_count; - return bitmask; - } - - /// Converts an integer bitmask into a HookMask - pub fn fromInt(mask: i32) HookMask { - return .{ - .call = (mask & mask_call) != 0, - .ret = (mask & mask_ret) != 0, - .line = (mask & mask_line) != 0, - .count = (mask & mask_count) != 0, - }; - } -}; - -/// Hook event codes -pub const hook_call = c.LUA_HOOKCALL; -pub const hook_count = c.LUA_HOOKCOUNT; -pub const hook_line = c.LUA_HOOKLINE; -pub const hook_ret = c.LUA_HOOKRET; -pub const hook_tail_call = c.LUA_HOOKTAILCALL; - -/// Type of integers in Lua (typically an i64) -pub const Integer = c.lua_Integer; - -/// Bitflag for the Lua standard libraries -pub const Libs = packed struct { - base: bool = false, - coroutine: bool = false, - package: bool = false, - string: bool = false, - utf8: bool = false, - table: bool = false, - math: bool = false, - io: bool = false, - os: bool = false, - debug: bool = false, - bit: bool = false, -}; - -/// The type of the opaque structure that points to a thread and the state of a Lua interpreter -pub const LuaState = c.lua_State; - -/// Lua types -/// Must be a signed integer because LuaType.none is -1 -pub const LuaType = enum(i5) { - none = c.LUA_TNONE, - nil = c.LUA_TNIL, - boolean = c.LUA_TBOOLEAN, - light_userdata = c.LUA_TLIGHTUSERDATA, - number = c.LUA_TNUMBER, - string = c.LUA_TSTRING, - table = c.LUA_TTABLE, - function = c.LUA_TFUNCTION, - userdata = c.LUA_TUSERDATA, - thread = c.LUA_TTHREAD, -}; - -/// Modes used for `Lua.load()` -pub const Mode = enum(u2) { binary, text, binary_text }; - -/// Event masks -pub const mask_call = c.LUA_MASKCALL; -pub const mask_count = c.LUA_MASKCOUNT; -pub const mask_line = c.LUA_MASKLINE; -pub const mask_ret = c.LUA_MASKRET; - -/// The maximum integer value that `Integer` can store -pub const max_integer = c.LUA_MAXINTEGER; - -/// The minimum Lua stack available to a function -pub const min_stack = c.LUA_MINSTACK; - -/// Option for multiple returns in `Lua.protectedCall()` and `Lua.call()` -pub const mult_return = c.LUA_MULTRET; - -/// Type of floats in Lua (typically an f64) -pub const Number = c.lua_Number; - -/// The type of the reader function used by `Lua.load()` -pub const CReaderFn = *const fn (state: ?*LuaState, data: ?*anyopaque, size: [*c]usize) callconv(.C) [*c]const u8; - -/// The possible status of a call to `Lua.resumeThread` -pub const ResumeStatus = enum(u1) { - ok = StatusCode.ok, - yield = StatusCode.yield, -}; - -/// Reference constants -pub const ref_nil = c.LUA_REFNIL; -pub const ref_no = c.LUA_NOREF; - -/// Index of the regsitry in the stack (pseudo-index) -pub const registry_index = c.LUA_REGISTRYINDEX; - -/// Index of globals in the registry -pub const ridx_globals = c.LUA_RIDX_GLOBALS; - -/// Index of the main thread in the registry -pub const ridx_mainthread = c.LUA_RIDX_MAINTHREAD; - -/// Status that a thread can be in -/// Usually errors are reported by a Zig error rather than a status enum value -pub const Status = enum(u3) { - ok = StatusCode.ok, - yield = StatusCode.yield, - err_runtime = StatusCode.err_runtime, - err_syntax = StatusCode.err_syntax, - err_memory = StatusCode.err_memory, - err_error = StatusCode.err_error, -}; - -/// Status codes -/// Not public, because typically Status.ok is returned from a function implicitly; -/// Any function that returns an error usually returns a Zig error, and a void return -/// is an implicit Status.ok. -/// In the rare case that the status code is required from a function, an enum is -/// used for that specific function's return type -const StatusCode = struct { - pub const ok = c.LUA_OK; - pub const yield = c.LUA_YIELD; - pub const err_runtime = c.LUA_ERRRUN; - pub const err_syntax = c.LUA_ERRSYNTAX; - pub const err_memory = c.LUA_ERRMEM; - pub const err_gcmm = c.LUA_ERRGCMM; - pub const err_error = c.LUA_ERRERR; -}; - -// Only used in loadFileX, so no need to group with Status -pub const err_file = c.LUA_ERRFILE; - -/// The standard representation for file handles used by the standard IO library -pub const Stream = c.luaL_Stream; - -/// The unsigned version of Integer -pub const Unsigned = c.lua_Unsigned; - -/// The type of the writer function used by `Lua.dump()` -pub const CWriterFn = *const fn (state: ?*LuaState, buf: ?*const anyopaque, size: usize, data: ?*anyopaque) callconv(.C) c_int; - -/// A Zig wrapper around the Lua C API -/// Represents a Lua state or thread and contains the entire state of the Lua interpreter -pub const Lua = struct { - state: *LuaState, - - const alignment = @alignOf(std.c.max_align_t); - - /// Allows Lua to allocate memory using a Zig allocator passed in via data. - /// See https://www.lua.org/manual/5.2/manual.html#lua_Alloc for more details - fn alloc(data: ?*anyopaque, ptr: ?*anyopaque, osize: usize, nsize: usize) callconv(.C) ?*align(alignment) anyopaque { - // just like malloc() returns a pointer "which is suitably aligned for any built-in type", - // the memory allocated by this function should also be aligned for any type that Lua may - // desire to allocate. use the largest alignment for the target - const allocator_ptr = opaqueCast(Allocator, data.?); - - if (@as(?[*]align(alignment) u8, @ptrCast(@alignCast(ptr)))) |prev_ptr| { - const prev_slice = prev_ptr[0..osize]; - - // when nsize is zero the allocator must behave like free and return null - if (nsize == 0) { - allocator_ptr.free(prev_slice); - return null; - } - - // when nsize is not zero the allocator must behave like realloc - const new_ptr = allocator_ptr.realloc(prev_slice, nsize) catch return null; - return new_ptr.ptr; - } else if (nsize == 0) { - return null; - } else { - // ptr is null, allocate a new block of memory - const new_ptr = allocator_ptr.alignedAlloc(u8, alignment, nsize) catch return null; - return new_ptr.ptr; - } - } - - /// Initialize a Lua state with the given allocator - /// See https://www.lua.org/manual/5.2/manual.html#lua_newstate - pub fn init(allocator_ptr: *const Allocator) !Lua { - // @constCast() is safe here because Lua does not mutate the pointer internally - const state = c.lua_newstate(alloc, @constCast(allocator_ptr)) orelse return error.Memory; - return Lua{ .state = state }; - } - - /// Deinitialize a Lua state and free all memory - pub fn deinit(lua: *Lua) void { - lua.close(); - } - - /// Returns the std.mem.Allocator used to initialize this Lua state - /// - /// This function is not safe to use on Lua states created without a Zig allocator. - /// If the user data passed to Lua was null, this function will panic. Otherwise use - /// of the returned Allocator is undefined and will likely cause a segfault. - pub fn allocator(lua: *Lua) Allocator { - var data: ?*Allocator = undefined; - _ = lua.getAllocFn(@ptrCast(&data)); - - if (data) |allocator_ptr| { - // Although the Allocator is passed to Lua as a pointer, return a - // copy to make use more convenient. - return allocator_ptr.*; - } - - @panic("Lua.allocator() invalid on Lua states created without a Zig allocator"); - } - - // Library functions - // - // Library functions are included in alphabetical order. - // Each is kept similar to the original C API function while also making it easy to use from Zig - - /// Returns the acceptable index index converted into an equivalent absolute index - /// See https://www.lua.org/manual/5.2/manual.html#lua_absindex - pub fn absIndex(lua: *Lua, index: i32) i32 { - return c.lua_absindex(lua.state, index); - } - - /// Performs an arithmetic operation over the value(s) at the top of the stack, - /// with the value at the top being the second operand. Pushes the result of the operation. - /// This function follows the semantics of the corresponding Lua operator and may call metamethods - /// See https://www.lua.org/manual/5.2/manual.html#lua_arith - pub fn arith(lua: *Lua, op: ArithOperator) void { - c.lua_arith(lua.state, @intFromEnum(op)); - } - - /// Sets a new panic function and returns the old one - /// See https://www.lua.org/manual/5.2/manual.html#lua_atpanic - pub fn atPanic(lua: *Lua, panic_fn: CFn) ?CFn { - return c.lua_atpanic(lua.state, panic_fn); - } - - /// Calls a function (or any callable value) - /// First push the function to be called onto the stack. Then push any arguments onto the stack. - /// Then call this function. All arguments and the function value are popped, and any results - /// are pushed onto the stack. - /// See https://www.lua.org/manual/5.2/manual.html#lua_call - pub fn call(lua: *Lua, num_args: i32, num_results: i32) void { - lua.callCont(num_args, num_results, 0, null); - } - - /// Like call, but allows the called function to yield - /// See https://www.lua.org/manual/5.2/manual.html#lua_callk - pub fn callCont(lua: *Lua, num_args: i32, num_results: i32, ctx: i32, k: ?CFn) void { - c.lua_callk(lua.state, num_args, num_results, ctx, k); - } - - /// Ensures that the stack has space for at least n extra arguments - /// Returns an error if more stack space cannot be allocated - /// Never shrinks the stack - /// See https://www.lua.org/manual/5.2/manual.html#lua_checkstack - pub fn checkStack(lua: *Lua, n: i32) !void { - if (c.lua_checkstack(lua.state, n) == 0) return error.Fail; - } - - /// Release all Lua objects in the state and free all dynamic memory - /// See https://www.lua.org/manual/5.2/manual.html#lua_close - pub fn close(lua: *Lua) void { - c.lua_close(lua.state); - } - - /// Compares two Lua values - /// Returns true if the value at index1 satisisfies the comparison with the value at index2 - /// Returns false otherwise, or if any index is not valid - /// See https://www.lua.org/manual/5.2/manual.html#lua_compare - pub fn compare(lua: *Lua, index1: i32, index2: i32, op: CompareOperator) bool { - return c.lua_compare(lua.state, index1, index2, @intFromEnum(op)) != 0; - } - - /// Concatenates the n values at the top of the stack, pops them, and leaves the result at the top - /// If the number of values is 1, the result is a single value on the stack (nothing changes) - /// If the number of values is 0, the result is the empty string - /// See https://www.lua.org/manual/5.2/manual.html#lua_concat - pub fn concat(lua: *Lua, n: i32) void { - c.lua_concat(lua.state, n); - } - - /// Copies the element at from_index to the valid index to_index, replacing the value at that position - /// See https://www.lua.org/manual/5.2/manual.html#lua_copy - pub fn copy(lua: *Lua, from_index: i32, to_index: i32) void { - c.lua_copy(lua.state, from_index, to_index); - } - - /// Creates a new empty table and pushes onto the stack - /// num_arr is a hint for how many elements the table will have as a sequence - /// num_rec is a hint for how many other elements the table will have - /// Lua may preallocate memory for the table based on the hints - /// See https://www.lua.org/manual/5.2/manual.html#lua_createtable - pub fn createTable(lua: *Lua, num_arr: i32, num_rec: i32) void { - c.lua_createtable(lua.state, num_arr, num_rec); - } - - /// Dumps a function as a binary chunk - /// Data is a pointer passed to the writer function - /// Returns an error if writing was unsuccessful - /// See https://www.lua.org/manual/5.2/manual.html#lua_dump - pub fn dump(lua: *Lua, writer: CWriterFn, data: *anyopaque) !void { - if (c.lua_dump(lua.state, writer, data) != 0) return error.Fail; - } - - /// Raises a Lua error using the value at the top of the stack as the error object - /// Does a longjump and therefore never returns - /// See https://www.lua.org/manual/5.2/manual.html#lua_error - pub fn raiseError(lua: *Lua) noreturn { - _ = c.lua_error(lua.state); - unreachable; - } - - /// Perform a full garbage-collection cycle - /// See https://www.lua.org/manual/5.2/manual.html#lua_gc - pub fn gcCollect(lua: *Lua) void { - _ = c.lua_gc(lua.state, c.LUA_GCCOLLECT, 0); - } - - /// Stops the garbage collector - /// See https://www.lua.org/manual/5.2/manual.html#lua_gc - pub fn gcStop(lua: *Lua) void { - _ = c.lua_gc(lua.state, c.LUA_GCSTOP, 0); - } - - /// Restarts the garbage collector - /// See https://www.lua.org/manual/5.2/manual.html#lua_gc - pub fn gcRestart(lua: *Lua) void { - _ = c.lua_gc(lua.state, c.LUA_GCRESTART, 0); - } - - /// Performs an incremental step of garbage collection corresponding to the allocation of step_size Kbytes - /// See https://www.lua.org/manual/5.2/manual.html#lua_gc - pub fn gcStep(lua: *Lua) void { - _ = c.lua_gc(lua.state, c.LUA_GCSTEP, 0); - } - - /// Returns the current amount of memory (in Kbytes) in use by Lua - /// See https://www.lua.org/manual/5.2/manual.html#lua_gc - pub fn gcCount(lua: *Lua) i32 { - return c.lua_gc(lua.state, c.LUA_GCCOUNT, 0); - } - - /// Returns the remainder of dividing the current amount of bytes of memory in use by Lua by 1024 - /// See https://www.lua.org/manual/5.2/manual.html#lua_gc - pub fn gcCountB(lua: *Lua) i32 { - return c.lua_gc(lua.state, c.LUA_GCCOUNTB, 0); - } - - /// Returns a boolean that tells whether the garbage collector is running - /// See https://www.lua.org/manual/5.2/manual.html#lua_gc - pub fn gcIsRunning(lua: *Lua) bool { - return c.lua_gc(lua.state, c.LUA_GCISRUNNING, 0) != 0; - } - - /// Sets `pause` as the new value for the pause of the collector - /// Returns the previous value of the pause - /// See https://www.lua.org/manual/5.2/manual.html#lua_gc - pub fn gcSetPause(lua: *Lua, pause: i32) i32 { - return c.lua_gc(lua.state, c.LUA_GCSETPAUSE, pause); - } - - /// Sets `multiplier` as the new value for the step multiplier of the collector - /// Returns the previous value of the step multiplier - /// See https://www.lua.org/manual/5.2/manual.html#lua_gc - pub fn gcSetStepMul(lua: *Lua, multiplier: i32) i32 { - return c.lua_gc(lua.state, c.LUA_GCSETSTEPMUL, multiplier); - } - - /// Changes the collector to generational mode - /// See https://www.lua.org/manual/5.2/manual.html#lua_gc - pub fn gcSetGenerational(lua: *Lua) void { - _ = c.lua_gc(lua.state, c.LUA_GCGEN, 0); - } - - /// Changes the collector to incremental mode. This is the default mode - /// See https://www.lua.org/manual/5.2/manual.html#lua_gc - pub fn gcSetIncremental(lua: *Lua) void { - _ = c.lua_gc(lua.state, c.LUA_GCINC, 0); - } - - /// Returns the memory allocation function of a given state - /// If data is not null, it is set to the opaque pointer given when the allocator function was set - /// See https://www.lua.org/manual/5.2/manual.html#lua_getallocf - pub fn getAllocFn(lua: *Lua, data: ?**anyopaque) AllocFn { - // Assert cannot be null because it is impossible (and not useful) to pass null - // to the functions that set the allocator (setallocf and newstate) - return c.lua_getallocf(lua.state, @ptrCast(data)).?; - } - - /// Called by a continuation function to retrieve the status of the thread and context information - /// See https://www.lua.org/manual/5.2/manual.html#lua_getctx - pub fn getContext(lua: *Lua) !?i32 { - var ctx: i32 = undefined; - const ret = c.lua_getctx(lua.state, &ctx); - switch (ret) { - StatusCode.ok => return null, - StatusCode.yield => return ctx, - StatusCode.err_runtime => return error.Runtime, - StatusCode.err_memory => return error.Memory, - StatusCode.err_error => return error.MsgHandler, - else => unreachable, - } - } - - /// Pushes onto the stack the value t[key] where t is the value at the given index - /// See https://www.lua.org/manual/5.2/manual.html#lua_getfield - pub fn getField(lua: *Lua, index: i32, key: [:0]const u8) void { - c.lua_getfield(lua.state, index, key.ptr); - } - - /// Pushes onto the stack the value of the global name - /// See https://www.lua.org/manual/5.2/manual.html#lua_getglobal - pub fn getGlobal(lua: *Lua, name: [:0]const u8) void { - c.lua_getglobal(lua.state, name.ptr); - } - - /// Pushes onto the stack the Lua value associated with the full userdata at the given index. - /// See https://www.lua.org/manual/5.2/manual.html#lua_getuservalue - pub fn getUserValue(lua: *Lua, index: i32) void { - c.lua_getuservalue(lua.state, index); - } - - /// If the value at the given index has a metatable, the function pushes that metatable onto the stack - /// Otherwise an error is returned - /// See https://www.lua.org/manual/5.2/manual.html#lua_getmetatable - pub fn getMetatable(lua: *Lua, index: i32) !void { - if (c.lua_getmetatable(lua.state, index) == 0) return error.Fail; - } - - /// Pushes onto the stack the value t[k] where t is the value at the given index and k is the value on the top of the stack - /// See https://www.lua.org/manual/5.2/manual.html#lua_gettable - pub fn getTable(lua: *Lua, index: i32) void { - c.lua_gettable(lua.state, index); - } - - /// Returns the index of the top element in the stack - /// Because indices start at 1, the result is also equal to the number of elements in the stack - /// See https://www.lua.org/manual/5.2/manual.html#lua_gettop - pub fn getTop(lua: *Lua) i32 { - return c.lua_gettop(lua.state); - } - - /// Moves the top element into the given valid `index` shifting up any elements to make room - /// See https://www.lua.org/manual/5.2/manual.html#lua_insert - pub fn insert(lua: *Lua, index: i32) void { - c.lua_insert(lua.state, index); - } - - /// Returns true if the value at the given index is a boolean - /// See https://www.lua.org/manual/5.2/manual.html#lua_isboolean - pub fn isBoolean(lua: *Lua, index: i32) bool { - return c.lua_isboolean(lua.state, index); - } - - /// Returns true if the value at the given index is a CFn - /// See https://www.lua.org/manual/5.2/manual.html#lua_iscfunction - pub fn isCFunction(lua: *Lua, index: i32) bool { - return c.lua_iscfunction(lua.state, index) != 0; - } - - /// Returns true if the value at the given index is a function (C or Lua) - /// See https://www.lua.org/manual/5.2/manual.html#lua_isfunction - pub fn isFunction(lua: *Lua, index: i32) bool { - return c.lua_isfunction(lua.state, index); - } - - /// Returns true if the value at the given index is a light userdata - /// See https://www.lua.org/manual/5.2/manual.html#lua_islightuserdata - pub fn isLightUserdata(lua: *Lua, index: i32) bool { - return c.lua_islightuserdata(lua.state, index); - } - - /// Returns true if the value at the given index is nil - /// See https://www.lua.org/manual/5.2/manual.html#lua_isnil - pub fn isNil(lua: *Lua, index: i32) bool { - return c.lua_isnil(lua.state, index); - } - - /// Returns true if the given index is not valid - /// See https://www.lua.org/manual/5.2/manual.html#lua_isnone - pub fn isNone(lua: *Lua, index: i32) bool { - return c.lua_isnone(lua.state, index); - } - - /// Returns true if the given index is not valid or if the value at the index is nil - /// See https://www.lua.org/manual/5.2/manual.html#lua_isnoneornil - pub fn isNoneOrNil(lua: *Lua, index: i32) bool { - return c.lua_isnoneornil(lua.state, index); - } - - /// Returns true if the value at the given index is a number - /// See https://www.lua.org/manual/5.2/manual.html#lua_isnumber - pub fn isNumber(lua: *Lua, index: i32) bool { - return c.lua_isnumber(lua.state, index) != 0; - } - - /// Returns true if the value at the given index is a string - /// See https://www.lua.org/manual/5.2/manual.html#lua_isstring - pub fn isString(lua: *Lua, index: i32) bool { - return c.lua_isstring(lua.state, index) != 0; - } - - /// Returns true if the value at the given index is a table - /// See https://www.lua.org/manual/5.2/manual.html#lua_istable - pub fn isTable(lua: *Lua, index: i32) bool { - return c.lua_istable(lua.state, index); - } - - /// Returns true if the value at the given index is a thread - /// See https://www.lua.org/manual/5.2/manual.html#lua_isthread - pub fn isThread(lua: *Lua, index: i32) bool { - return c.lua_isthread(lua.state, index); - } - - /// Returns true if the value at the given index is a userdata (full or light) - /// See https://www.lua.org/manual/5.2/manual.html#lua_isuserdata - pub fn isUserdata(lua: *Lua, index: i32) bool { - return c.lua_isuserdata(lua.state, index) != 0; - } - - /// Pushes the length of the value at the given index onto the stack - /// Equivalent to the # operator in Lua - /// See https://www.lua.org/manual/5.2/manual.html#lua_len - pub fn len(lua: *Lua, index: i32) void { - c.lua_len(lua.state, index); - } - - /// Loads a Lua chunk without running it - /// If there are no errors, pushes the compiled chunk on the top of the stack as a function - /// See https://www.lua.org/manual/5.2/manual.html#lua_load - pub fn load(lua: *Lua, reader: CReaderFn, data: *anyopaque, chunk_name: [:0]const u8, mode: Mode) !void { - const mode_str = switch (mode) { - .binary => "b", - .text => "t", - .binary_text => "bt", - }; - const ret = c.lua_load(lua.state, reader, data, chunk_name.ptr, mode_str.ptr); - switch (ret) { - StatusCode.ok => return, - StatusCode.err_syntax => return error.Syntax, - StatusCode.err_memory => return error.Memory, - // lua_load runs pcall, so can also return any result of a pcall error - StatusCode.err_runtime => return error.Runtime, - StatusCode.err_error => return error.MsgHandler, - StatusCode.err_gcmm => return error.GCMetaMethod, - else => unreachable, - } - } - - /// Creates a new independent state and returns its main thread - /// See https://www.lua.org/manual/5.2/manual.html#lua_newstate - pub fn newState(alloc_fn: AllocFn, data: ?*const anyopaque) !Lua { - const state = c.lua_newstate(alloc_fn, @constCast(data)) orelse return error.Memory; - return Lua{ .state = state }; - } - - /// Creates a new empty table and pushes it onto the stack - /// Equivalent to createTable(0, 0) - /// See https://www.lua.org/manual/5.2/manual.html#lua_newtable - pub fn newTable(lua: *Lua) void { - c.lua_newtable(lua.state); - } - - /// Creates a new thread, pushes it on the stack, and returns a Lua state that represents the new thread - /// The new thread shares the global environment but has a separate execution stack - /// See https://www.lua.org/manual/5.2/manual.html#lua_newthread - pub fn newThread(lua: *Lua) Lua { - const state = c.lua_newthread(lua.state).?; - return .{ .state = state }; - } - - /// This function allocates a new userdata of the given type - /// Returns a pointer to the Lua-owned data - /// See https://www.lua.org/manual/5.2/manual.html#lua_newuserdata - pub fn newUserdata(lua: *Lua, comptime T: type) *T { - // safe to .? because this function throws a Lua error on out of memory - // so the returned pointer should never be null - const ptr = c.lua_newuserdata(lua.state, @sizeOf(T)).?; - return opaqueCast(T, ptr); - } - - /// This function creates and pushes a slice of full userdata onto the stack. - /// Returns a slice to the Lua-owned data. - /// See https://www.lua.org/manual/5.2/manual.html#lua_newuserdata - pub fn newUserdataSlice(lua: *Lua, comptime T: type, size: usize) []T { - // safe to .? because this function throws a Lua error on out of memory - const ptr = c.lua_newuserdata(lua.state, @sizeOf(T) * size).?; - return @as([*]T, @ptrCast(@alignCast(ptr)))[0..size]; - } - - /// Pops a key from the stack, and pushes a key-value pair from the table at the given index - /// See https://www.lua.org/manual/5.2/manual.html#lua_next - pub fn next(lua: *Lua, index: i32) bool { - return c.lua_next(lua.state, index) != 0; - } - - /// Calls a function (or callable object) in protected mode - /// See https://www.lua.org/manual/5.2/manual.html#lua_pcall - pub fn protectedCall(lua: *Lua, num_args: i32, num_results: i32, msg_handler: i32) !void { - // The translate-c version of lua_pcall does not type-check so we must rewrite it - const ret = c.lua_pcallk(lua.state, num_args, num_results, msg_handler, 0, null); - switch (ret) { - StatusCode.ok => return, - StatusCode.err_runtime => return error.Runtime, - StatusCode.err_memory => return error.Memory, - StatusCode.err_error => return error.MsgHandler, - StatusCode.err_gcmm => return error.GCMetaMethod, - else => unreachable, - } - } - - /// Behaves exactly like `Lua.protectedCall()` except that it allows the called function to yield - /// See https://www.lua.org/manual/5.2/manual.html#lua_pcallk - pub fn protectedCallCont(lua: *Lua, num_args: i32, num_results: i32, msg_handler: i32, ctx: i32, k: CFn) !void { - const ret = c.lua_pcallk(lua.state, num_args, num_results, msg_handler, ctx, k); - switch (ret) { - StatusCode.ok => return, - StatusCode.err_runtime => return error.Runtime, - StatusCode.err_memory => return error.Memory, - StatusCode.err_error => return error.MsgHandler, - StatusCode.err_gcmm => return error.GCMetaMethod, - else => unreachable, - } - } - - /// Pops `n` elements from the top of the stack - /// See https://www.lua.org/manual/5.2/manual.html#lua_pop - pub fn pop(lua: *Lua, n: i32) void { - lua.setTop(-n - 1); - } - - /// Pushes a boolean value with value `b` onto the stack - /// See https://www.lua.org/manual/5.2/manual.html#lua_pushboolean - pub fn pushBoolean(lua: *Lua, b: bool) void { - c.lua_pushboolean(lua.state, @intFromBool(b)); - } - - /// Pushes a new Closure onto the stack - /// `n` tells how many upvalues this function will have - /// See https://www.lua.org/manual/5.2/manual.html#lua_pushcclosure - pub fn pushClosure(lua: *Lua, c_fn: CFn, n: i32) void { - c.lua_pushcclosure(lua.state, c_fn, n); - } - - /// Pushes a function onto the stack. - /// Equivalent to pushClosure with no upvalues - /// See https://www.lua.org/manual/5.2/manual.html#lua_pushcfunction - pub fn pushFunction(lua: *Lua, c_fn: CFn) void { - lua.pushClosure(c_fn, 0); - } - - /// Push a formatted string onto the stack and return a pointer to the string - /// See https://www.lua.org/manual/5.2/manual.html#lua_pushfstring - pub fn pushFString(lua: *Lua, fmt: [:0]const u8, args: anytype) [*:0]const u8 { - return @call(.auto, c.lua_pushfstring, .{ lua.state, fmt.ptr } ++ args); - } - - /// Pushes the global environment onto the stack - /// See https://www.lua.org/manual/5.2/manual.html#lua_pushglobaltable - pub fn pushGlobalTable(lua: *Lua) void { - // lua_pushglobaltable is a macro and c-translate assumes it returns opaque - // so just reimplement the macro here - // c.lua_pushglobaltable(lua.state); - _ = lua.rawGetIndex(registry_index, ridx_globals); - } - - /// Pushes an integer with value `n` onto the stack - /// See https://www.lua.org/manual/5.2/manual.html#lua_pushinteger - pub fn pushInteger(lua: *Lua, n: Integer) void { - c.lua_pushinteger(lua.state, n); - } - - /// Pushes a light userdata onto the stack - /// See https://www.lua.org/manual/5.2/manual.html#lua_lightuserdata - pub fn pushLightUserdata(lua: *Lua, ptr: *anyopaque) void { - c.lua_pushlightuserdata(lua.state, ptr); - } - - /// Pushes the bytes onto the stack. Returns a slice pointing to Lua's internal copy of the string - /// See https://www.lua.org/manual/5.2/manual.html#lua_pushlstring - pub fn pushBytes(lua: *Lua, bytes: []const u8) []const u8 { - return c.lua_pushlstring(lua.state, bytes.ptr, bytes.len)[0..bytes.len]; - } - - /// Pushes a nil value onto the stack - /// See https://www.lua.org/manual/5.2/manual.html#lua_pushnil - pub fn pushNil(lua: *Lua) void { - c.lua_pushnil(lua.state); - } - - /// Pushes a float with value `n` onto the stack - /// See https://www.lua.org/manual/5.2/manual.html#lua_pushnumber - pub fn pushNumber(lua: *Lua, n: Number) void { - c.lua_pushnumber(lua.state, n); - } - - /// Pushes a zero-terminated string onto the stack - /// Lua makes a copy of the string so `str` may be freed immediately after return - /// Returns a pointer to the internal Lua string - /// See https://www.lua.org/manual/5.2/manual.html#lua_pushstring - pub fn pushString(lua: *Lua, str: [:0]const u8) [:0]const u8 { - return c.lua_pushstring(lua.state, str.ptr)[0..str.len :0]; - } - - /// Pushes this thread onto the stack - /// Returns true if this thread is the main thread of its state - /// See https://www.lua.org/manual/5.2/manual.html#lua_pushthread - pub fn pushThread(lua: *Lua) bool { - return c.lua_pushthread(lua.state) != 0; - } - - /// Pushes a number with value n onto the stack - /// See https://www.lua.org/manual/5.2/manual.html#lua_pushunsigned - pub fn pushUnsigned(lua: *Lua, n: Unsigned) void { - return c.lua_pushunsigned(lua.state, n); - } - - /// Pushes a copy of the element at the given index onto the stack - /// See https://www.lua.org/manual/5.2/manual.html#lua_pushvalue - pub fn pushValue(lua: *Lua, index: i32) void { - c.lua_pushvalue(lua.state, index); - } - - /// Returns true if the two values in indices `index1` and `index2` are primitively equal - /// Bypasses __eq metamethods - /// Returns false if not equal, or if any index is invalid - /// See https://www.lua.org/manual/5.2/manual.html#lua_rawequal - pub fn rawEqual(lua: *Lua, index1: i32, index2: i32) bool { - return c.lua_rawequal(lua.state, index1, index2) != 0; - } - - /// Similar to `Lua.getTable()` but does a raw access (without metamethods) - /// See https://www.lua.org/manual/5.2/manual.html#lua_rawget - pub fn rawGetTable(lua: *Lua, index: i32) void { - c.lua_rawget(lua.state, index); - } - - /// Pushes onto the stack the value t[n], where `t` is the table at the given `index` - /// Returns the `LuaType` of the pushed value - /// See https://www.lua.org/manual/5.2/manual.html#lua_rawgeti - pub fn rawGetIndex(lua: *Lua, index: i32, n: i32) void { - c.lua_rawgeti(lua.state, index, n); - } - - /// Pushes onto the stack the value t[k] where t is the table at the given `index` and - /// k is the pointer `p` represented as a light userdata - /// See https://www.lua.org/manual/5.2/manual.html#lua_rawgetp - pub fn rawGetPtr(lua: *Lua, index: i32, p: *const anyopaque) void { - c.lua_rawgetp(lua.state, index, p); - } - - /// Returns the raw length of the value at the given index - /// For strings it is the length; for tables it is the result of the `#` operator - /// For userdata it is the size of the block of memory - /// For other values the call returns 0 - /// See https://www.lua.org/manual/5.2/manual.html#lua_rawlen - pub fn rawLen(lua: *Lua, index: i32) usize { - return c.lua_rawlen(lua.state, index); - } - - /// Similar to `Lua.setTable()` but does a raw assignment (without metamethods) - /// See https://www.lua.org/manual/5.2/manual.html#lua_rawset - pub fn rawSetTable(lua: *Lua, index: i32) void { - c.lua_rawset(lua.state, index); - } - - /// Does the equivalent of t[`i`] = v where t is the table at the given `index` - /// and v is the value at the top of the stack - /// Pops the value from the stack. Does not use __newindex metavalue - /// See https://www.lua.org/manual/5.2/manual.html#lua_rawseti - pub fn rawSetIndex(lua: *Lua, index: i32, i: i32) void { - c.lua_rawseti(lua.state, index, i); - } - - /// Does the equivalent of t[p] = v where t is the table at the given `index` - /// `p` is encoded as a light user data, and v is the value at the top of the stack - /// Pops the value from the stack. Does not use __newindex metavalue - /// See https://www.lua.org/manual/5.2/manual.html#lua_rawsetp - pub fn rawSetPtr(lua: *Lua, index: i32, p: *const anyopaque) void { - c.lua_rawsetp(lua.state, index, p); - } - - /// Sets the C function f as the new value of global name - /// See https://www.lua.org/manual/5.2/manual.html#lua_register - pub fn register(lua: *Lua, name: [:0]const u8, c_fn: CFn) void { - c.lua_register(lua.state, name.ptr, c_fn); - } - - /// Removes the element at the given valid `index` shifting down elements to fill the gap - /// See https://www.lua.org/manual/5.2/manual.html#lua_remove - pub fn remove(lua: *Lua, index: i32) void { - c.lua_remove(lua.state, index); - } - - /// Moves the top element into the given valid `index` without shifting any elements, - /// then pops the top element - /// See https://www.lua.org/manual/5.2/manual.html#lua_replace - pub fn replace(lua: *Lua, index: i32) void { - // translate-c cannot translate this macro correctly - // c.lua_replace(lua.state, index); - lua.copy(-1, index); - lua.pop(1); - } - - /// Starts and resumes a coroutine in the given thread - /// See https://www.lua.org/manual/5.2/manual.html#lua_resume - pub fn resumeThread(lua: *Lua, from: ?Lua, num_args: i32) !ResumeStatus { - const from_state = if (from) |from_val| from_val.state else null; - const thread_status = c.lua_resume(lua.state, from_state, num_args); - switch (thread_status) { - StatusCode.err_runtime => return error.Runtime, - StatusCode.err_memory => return error.Memory, - StatusCode.err_error => return error.MsgHandler, - StatusCode.err_gcmm => return error.GCMetaMethod, - else => return @enumFromInt(thread_status), - } - } - - /// Changes the allocator function of a given state to `alloc_fn` with userdata `data` - /// See https://www.lua.org/manual/5.2/manual.html#lua_setallocf - pub fn setAllocF(lua: *Lua, alloc_fn: AllocFn, data: ?*anyopaque) void { - c.lua_setallocf(lua.state, alloc_fn, data); - } - - /// Does the equivalent to t[`k`] = v where t is the value at the given `index` - /// and v is the value on the top of the stack - /// See https://www.lua.org/manual/5.2/manual.html#lua_setfield - pub fn setField(lua: *Lua, index: i32, k: [:0]const u8) void { - c.lua_setfield(lua.state, index, k.ptr); - } - - /// Pops a value from the stack and sets it as the new value of global `name` - /// See https://www.lua.org/manual/5.2/manual.html#lua_setglobal - pub fn setGlobal(lua: *Lua, name: [:0]const u8) void { - c.lua_setglobal(lua.state, name.ptr); - } - - /// Pops a value from the stack and sets it as the user value associated to the full userdata at the given index - /// Returns an error if the userdata does not have that value - /// See https://www.lua.org/manual/5.2/manual.html#lua_setuservalue - pub fn setUserValue(lua: *Lua, index: i32) !void { - c.lua_setuservalue(lua.state, index); - } - - /// Pops a table or nil from the stack and sets that value as the new metatable for the value at the given `index` - /// See https://www.lua.org/manual/5.2/manual.html#lua_setmetatable - pub fn setMetatable(lua: *Lua, index: i32) void { - // lua_setmetatable always returns 1 so is safe to ignore - _ = c.lua_setmetatable(lua.state, index); - } - - /// Does the equivalent to t[k] = v, where t is the value at the given `index` - /// v is the value on the top of the stack, and k is the value just below the top - /// See https://www.lua.org/manual/5.2/manual.html#lua_settable - pub fn setTable(lua: *Lua, index: i32) void { - c.lua_settable(lua.state, index); - } - - /// Sets the top of the stack to `index` - /// If the new top is greater than the old, new elements are filled with nil - /// If `index` is 0 all stack elements are removed - /// See https://www.lua.org/manual/5.2/manual.html#lua_settop - pub fn setTop(lua: *Lua, index: i32) void { - c.lua_settop(lua.state, index); - } - - /// Returns the status of this thread - /// See https://www.lua.org/manual/5.2/manual.html#lua_status - pub fn status(lua: *Lua) Status { - return @enumFromInt(c.lua_status(lua.state)); - } - - /// Converts the Lua value at the given `index` into a boolean - /// The Lua value at the index will be considered true unless it is false or nil - /// See https://www.lua.org/manual/5.2/manual.html#lua_toboolean - pub fn toBoolean(lua: *Lua, index: i32) bool { - return c.lua_toboolean(lua.state, index) != 0; - } - - /// Converts a value at the given `index` into a CFn - /// Returns an error if the value is not a CFn - /// See https://www.lua.org/manual/5.2/manual.html#lua_tocfunction - pub fn toCFunction(lua: *Lua, index: i32) !CFn { - return c.lua_tocfunction(lua.state, index) orelse return error.Fail; - } - - /// Converts the Lua value at the given `index` to a signed integer - /// The Lua value must be an integer, or a number, or a string convertible to an integer otherwise toIntegerX returns 0 - /// Returns an error if the conversion failed - /// See https://www.lua.org/manual/5.2/manual.html#lua_tointegerx - pub fn toInteger(lua: *Lua, index: i32) !Integer { - var success: c_int = undefined; - const result = c.lua_tointegerx(lua.state, index, &success); - if (success == 0) return error.Fail; - return result; - } - - /// Returns a slice of bytes at the given index - /// If the value is not a string or number, returns an error - /// If the value was a number the actual value in the stack will be changed to a string - /// See https://www.lua.org/manual/5.2/manual.html#lua_tolstring - pub fn toBytes(lua: *Lua, index: i32) ![:0]const u8 { - var length: usize = undefined; - if (c.lua_tolstring(lua.state, index, &length)) |ptr| return ptr[0..length :0]; - return error.Fail; - } - - /// Converts the Lua value at the given `index` to a float - /// The Lua value must be a number or a string convertible to a number otherwise toNumberX returns 0 - /// Returns an error if the conversion failed - /// See https://www.lua.org/manual/5.2/manual.html#lua_tonumberx - pub fn toNumber(lua: *Lua, index: i32) !Number { - var success: c_int = undefined; - const result = c.lua_tonumberx(lua.state, index, &success); - if (success == 0) return error.Fail; - return result; - } - - /// Converts the value at the given `index` to an opaque pointer - /// See https://www.lua.org/manual/5.2/manual.html#lua_topointer - pub fn toPointer(lua: *Lua, index: i32) !*const anyopaque { - if (c.lua_topointer(lua.state, index)) |ptr| return ptr; - return error.Fail; - } - - /// Converts the Lua value at the given `index` to a zero-terminated many-itemed-pointer (string) - /// Returns an error if the conversion failed - /// If the value was a number the actual value in the stack will be changed to a string - /// See https://www.lua.org/manual/5.2/manual.html#lua_tostring - pub fn toString(lua: *Lua, index: i32) ![*:0]const u8 { - var length: usize = undefined; - if (c.lua_tolstring(lua.state, index, &length)) |str| return str; - return error.Fail; - } - - /// Converts the value at the given `index` to a Lua thread (wrapped with a `Lua` struct) - /// The thread does _not_ contain an allocator because it is not the main thread and should therefore not be used with `deinit()` - /// Returns an error if the value is not a thread - /// See https://www.lua.org/manual/5.2/manual.html#lua_tothread - pub fn toThread(lua: *Lua, index: i32) !Lua { - const thread = c.lua_tothread(lua.state, index); - if (thread) |thread_ptr| return Lua{ .state = thread_ptr }; - return error.Fail; - } - - /// Converts the Lua value at the given index to an unsigned integer - /// The Lua value must be a number or a string convertible to a number otherwise an error is returned - /// See https://www.lua.org/manual/5.2/manual.html#lua_tounsignedx - pub fn toUnsigned(lua: *Lua, index: i32) !Unsigned { - var success: c_int = undefined; - const result = c.lua_tounsignedx(lua.state, index, &success); - if (success == 0) return error.Fail; - return result; - } - - /// Returns a Lua-owned userdata pointer of the given type at the given index. - /// Works for both light and full userdata. - /// Returns an error if the value is not a userdata. - /// See https://www.lua.org/manual/5.2/manual.html#lua_touserdata - pub fn toUserdata(lua: *Lua, comptime T: type, index: i32) !*T { - if (c.lua_touserdata(lua.state, index)) |ptr| return opaqueCast(T, ptr); - return error.Fail; - } - - /// Returns a Lua-owned userdata slice of the given type at the given index. - /// Returns an error if the value is not a userdata. - /// See https://www.lua.org/manual/5.2/manual.html#lua_touserdata - pub fn toUserdataSlice(lua: *Lua, comptime T: type, index: i32) ![]T { - if (c.lua_touserdata(lua.state, index)) |ptr| { - const size = lua.rawLen(index) / @sizeOf(T); - return @as([*]T, @ptrCast(@alignCast(ptr)))[0..size]; - } - return error.Fail; - } - - /// Returns the `LuaType` of the value at the given index - /// Note that this is equivalent to lua_type but because type is a Zig primitive it is renamed to `typeOf` - /// See https://www.lua.org/manual/5.2/manual.html#lua_type - pub fn typeOf(lua: *Lua, index: i32) LuaType { - return @enumFromInt(c.lua_type(lua.state, index)); - } - - /// Returns the name of the given `LuaType` as a null-terminated slice - /// See https://www.lua.org/manual/5.2/manual.html#lua_typename - pub fn typeName(lua: *Lua, t: LuaType) [:0]const u8 { - return std.mem.span(c.lua_typename(lua.state, @intFromEnum(t))); - } - - /// Returns the pseudo-index that represents the `i`th upvalue of the running function - /// See https://www.lua.org/manual/5.2/manual.html#lua_upvalueindex - pub fn upvalueIndex(i: i32) i32 { - return c.lua_upvalueindex(i); - } - - /// Returns the version number of this core - /// When `caller_version` is true it returns the address of the version running the call - /// See https://www.lua.org/manual/5.2/manual.html#lua_version - pub fn version(lua: *Lua, caller_version: bool) *const Number { - if (caller_version) return c.lua_version(null); - return c.lua_version(lua.state); - } - - /// Pops `num` values from the current stack and pushes onto the stack of `to` - /// See https://www.lua.org/manual/5.2/manual.html#lua_xmove - pub fn xMove(lua: *Lua, to: Lua, num: i32) void { - c.lua_xmove(lua.state, to.state, num); - } - - /// This function is equivalent to `Lua.yieldCont()` but has no continuation - /// This function never returns - /// See https://www.lua.org/manual/5.2/manual.html#lua_yield - pub fn yield(lua: *Lua, num_results: i32) noreturn { - // translate-c failed to pass NULL correctly - _ = c.lua_yieldk(lua.state, num_results, 0, null); - unreachable; - } - - /// Yields this coroutine (thread) - /// This function never returns - /// See https://www.lua.org/manual/5.2/manual.html#lua_yieldk - pub fn yieldCont(lua: *Lua, num_results: i32, ctx: i32, k: CFn) noreturn { - _ = c.lua_yieldk(lua.state, num_results, ctx, k); - unreachable; - } - - // Debug library functions - // - // The debug interface functions are included in alphabetical order - // Each is kept similar to the original C API function while also making it easy to use from Zig - - /// Returns the current hook function - /// See https://www.lua.org/manual/5.2/manual.html#lua_gethook - pub fn getHook(lua: *Lua) ?CHookFn { - return c.lua_gethook(lua.state); - } - - /// Returns the current hook count - /// See https://www.lua.org/manual/5.2/manual.html#lua_gethookcount - pub fn getHookCount(lua: *Lua) i32 { - return c.lua_gethookcount(lua.state); - } - - /// Returns the current hook mask - /// See https://www.lua.org/manual/5.2/manual.html#lua_gethookmask - pub fn getHookMask(lua: *Lua) HookMask { - return HookMask.fromInt(c.lua_gethookmask(lua.state)); - } - - /// Gets information about a specific function or function invocation - /// Returns an error if an invalid option was given, but the valid options - /// are still handled - /// See https://www.lua.org/manual/5.2/manual.html#lua_getinfo - pub fn getInfo(lua: *Lua, options: DebugInfo.Options, info: *DebugInfo) void { - const str = options.toString(); - - var ar: Debug = undefined; - ar.i_ci = @ptrCast(info.private); - - // should never fail because we are controlling options with the struct param - _ = c.lua_getinfo(lua.state, &str, &ar); - // std.debug.assert( != 0); - - // copy data into a struct - if (options.l) info.current_line = if (ar.currentline == -1) null else ar.currentline; - if (options.n) { - info.name = if (ar.name != null) std.mem.span(ar.name) else null; - info.name_what = blk: { - const what = std.mem.span(ar.namewhat); - if (std.mem.eql(u8, "global", what)) break :blk .global; - if (std.mem.eql(u8, "local", what)) break :blk .local; - if (std.mem.eql(u8, "method", what)) break :blk .method; - if (std.mem.eql(u8, "field", what)) break :blk .field; - if (std.mem.eql(u8, "upvalue", what)) break :blk .upvalue; - if (what.len == 0) break :blk .other; - unreachable; - }; - } - if (options.S) { - info.source = std.mem.span(ar.source); - @memcpy(&info.short_src, &ar.short_src); - info.first_line_defined = ar.linedefined; - info.last_line_defined = ar.lastlinedefined; - info.what = blk: { - const what = std.mem.span(ar.what); - if (std.mem.eql(u8, "Lua", what)) break :blk .lua; - if (std.mem.eql(u8, "C", what)) break :blk .c; - if (std.mem.eql(u8, "main", what)) break :blk .main; - unreachable; - }; - } - if (options.t) info.is_tail_call = ar.istailcall != 0; - if (options.u) { - info.num_upvalues = ar.nups; - info.num_params = ar.nparams; - info.is_vararg = ar.isvararg != 0; - } - } - - /// Gets information about a local variable - /// Returns the name of the local variable - /// See https://www.lua.org/manual/5.2/manual.html#lua_getlocal - pub fn getLocal(lua: *Lua, info: *DebugInfo, n: i32) ![:0]const u8 { - var ar: Debug = undefined; - ar.i_ci = @ptrCast(info.private); - if (c.lua_getlocal(lua.state, &ar, n)) |name| { - return std.mem.span(name); - } - return error.Fail; - } - - /// Gets information about the interpreter runtime stack - /// See https://www.lua.org/manual/5.2/manual.html#lua_getstack - pub fn getStack(lua: *Lua, level: i32) !DebugInfo { - var ar: Debug = undefined; - if (c.lua_getstack(lua.state, level, &ar) == 0) return error.Fail; - return DebugInfo{ .private = ar.i_ci.? }; - } - - /// Gets information about the `n`th upvalue of the closure at index `func_index` - /// See https://www.lua.org/manual/5.2/manual.html#lua_getupvalue - pub fn getUpvalue(lua: *Lua, func_index: i32, n: i32) ![:0]const u8 { - if (c.lua_getupvalue(lua.state, func_index, n)) |name| { - return std.mem.span(name); - } - return error.Fail; - } - - /// Sets the debugging hook function - /// See https://www.lua.org/manual/5.2/manual.html#lua_sethook - pub fn setHook(lua: *Lua, hook_fn: CHookFn, mask: HookMask, count: i32) void { - const hook_mask = HookMask.toInt(mask); - _ = c.lua_sethook(lua.state, hook_fn, hook_mask, count); - } - - /// Sets the value of a local variable - /// Returns an error when the index is greater than the number of active locals - /// Returns the name of the local variable - /// See https://www.lua.org/manual/5.2/manual.html#lua_setlocal - pub fn setLocal(lua: *Lua, info: *DebugInfo, n: i32) ![:0]const u8 { - var ar: Debug = undefined; - ar.i_ci = @ptrCast(info.private); - if (c.lua_setlocal(lua.state, &ar, n)) |name| { - return std.mem.span(name); - } - return error.Fail; - } - - /// Sets the value of a closure's upvalue - /// Returns the name of the upvalue or an error if the upvalue does not exist - /// See https://www.lua.org/manual/5.2/manual.html#lua_setupvalue - pub fn setUpvalue(lua: *Lua, func_index: i32, n: i32) ![:0]const u8 { - if (c.lua_setupvalue(lua.state, func_index, n)) |name| { - return std.mem.span(name); - } - return error.Fail; - } - - /// Returns a unique identifier for the upvalue numbered `n` from the closure index `func_index` - /// See https://www.lua.org/manual/5.2/manual.html#lua_upvalueid - pub fn upvalueId(lua: *Lua, func_index: i32, n: i32) !*anyopaque { - if (c.lua_upvalueid(lua.state, func_index, n)) |ptr| return ptr; - return error.Fail; - } - - /// Make the `n1`th upvalue of the Lua closure at index `func_index1` refer to the `n2`th upvalue - /// of the Lua closure at index `func_index2` - /// See https://www.lua.org/manual/5.2/manual.html#lua_upvaluejoin - pub fn upvalueJoin(lua: *Lua, func_index1: i32, n1: i32, func_index2: i32, n2: i32) void { - c.lua_upvaluejoin(lua.state, func_index1, n1, func_index2, n2); - } - - // Auxiliary library functions - // - // Auxiliary library functions are included in alphabetical order. - // Each is kept similar to the original C API function while also making it easy to use from Zig - - /// Checks whether `cond` is true. Raises an error using `Lua.argError()` if not - /// Possibly never returns - /// See https://www.lua.org/manual/5.2/manual.html#lua_argcheck - pub fn argCheck(lua: *Lua, cond: bool, arg: i32, extra_msg: [:0]const u8) void { - // translate-c failed - if (!cond) lua.argError(arg, extra_msg); - } - - /// Raises an error reporting a problem with argument `arg` of the C function that called it - /// See https://www.lua.org/manual/5.2/manual.html#luaL_argerror - pub fn argError(lua: *Lua, arg: i32, extra_msg: [*:0]const u8) noreturn { - _ = c.luaL_argerror(lua.state, arg, extra_msg); - unreachable; - } - - /// Calls a metamethod - /// See https://www.lua.org/manual/5.2/manual.html#luaL_callmeta - pub fn callMeta(lua: *Lua, obj: i32, field: [:0]const u8) !void { - if (c.luaL_callmeta(lua.state, obj, field.ptr) == 0) return error.Fail; - } - - /// Checks whether the function has an argument of any type at position `arg` - /// See https://www.lua.org/manual/5.2/manual.html#luaL_checkany - pub fn checkAny(lua: *Lua, arg: i32) void { - c.luaL_checkany(lua.state, arg); - } - - /// Checks whether the function argument `arg` is a number and returns this number cast to an i32 - /// See https://www.lua.org/manual/5.2/manual.html#luaL_checkint - pub fn checkInt(lua: *Lua, arg: i32) i32 { - return c.luaL_checkint(lua.state, arg); - } - - /// Checks whether the function argument `arg` is an integer (or can be converted to an integer) and returns the integer - /// See https://www.lua.org/manual/5.2/manual.html#luaL_checkinteger - pub fn checkInteger(lua: *Lua, arg: i32) Integer { - return c.luaL_checkinteger(lua.state, arg); - } - - /// Checks whether the function argument `arg` is a slice of bytes and returns the slice - /// See https://www.lua.org/manual/5.2/manual.html#luaL_checklstring - pub fn checkBytes(lua: *Lua, arg: i32) [:0]const u8 { - var length: usize = 0; - const str = c.luaL_checklstring(lua.state, arg, &length); - // luaL_checklstring never returns null (throws lua error) - return str[0..length :0]; - } - - /// Checks whether the function argument `arg` is a number and returns the number - /// See https://www.lua.org/manual/5.2/manual.html#luaL_checknumber - pub fn checkNumber(lua: *Lua, arg: i32) Number { - return c.luaL_checknumber(lua.state, arg); - } - - /// Checks whether the function argument `arg` is a string and searches for the enum value with the same name in `T`. - /// `default` is used as a default value when not null - /// Returns the enum value found - /// Useful for mapping Lua strings to Zig enums - /// See https://www.lua.org/manual/5.2/manual.html#luaL_checkoption - pub fn checkOption(lua: *Lua, comptime T: type, arg: i32, default: ?T) T { - const name = blk: { - if (default) |defaultName| { - break :blk lua.optBytes(arg, @tagName(defaultName)); - } else { - break :blk lua.checkBytes(arg); - } - }; - - inline for (std.meta.fields(T)) |field| { - if (std.mem.eql(u8, field.name, name)) { - return @enumFromInt(field.value); - } - } - - return lua.argError(arg, lua.pushFString("invalid option '%s'", .{name.ptr})); - } - - /// Grows the stack size to top + `size` elements, raising an error if the stack cannot grow to that size - /// `msg` is an additional text to go into the error message - /// See https://www.lua.org/manual/5.2/manual.html#luaL_checkstack - pub fn checkStackErr(lua: *Lua, size: i32, msg: ?[*:0]const u8) void { - c.luaL_checkstack(lua.state, size, msg); - } - - /// Checks whether the function argument `arg` is a string and returns the string - /// See https://www.lua.org/manual/5.2/manual.html#luaL_checkstring - pub fn checkString(lua: *Lua, arg: i32) [*:0]const u8 { - return c.luaL_checklstring(lua.state, arg, null); - } - - /// Checks whether the function argument `arg` has type `t` - /// See https://www.lua.org/manual/5.2/manual.html#luaL_checktype - pub fn checkType(lua: *Lua, arg: i32, t: LuaType) void { - c.luaL_checktype(lua.state, arg, @intFromEnum(t)); - } - - /// Checks whether the function argument `arg` is a userdata of the type `name` - /// Returns the userdata's memory-block address - /// See https://www.lua.org/manual/5.2/manual.html#luaL_checkudata - pub fn checkUserdata(lua: *Lua, comptime T: type, arg: i32, name: [:0]const u8) *T { - // the returned pointer will not be null - return opaqueCast(T, c.luaL_checkudata(lua.state, arg, name.ptr).?); - } - - /// Checks whether the function argument `arg` is a userdata of the type `name` - /// Returns a Lua-owned userdata slice - /// See https://www.lua.org/manual/5.2/manual.html#luaL_checkudata - pub fn checkUserdataSlice(lua: *Lua, comptime T: type, arg: i32, name: [:0]const u8) []T { - // the returned pointer will not be null - const ptr = c.luaL_checkudata(lua.state, arg, name.ptr).?; - const size = lua.rawLen(arg) / @sizeOf(T); - return @as([*]T, @ptrCast(@alignCast(ptr)))[0..size]; - } - - /// Checks whether the function argument arg is a number and returns this number cast to an unsigned - /// See https://www.lua.org/manual/5.2/manual.html#luaL_checkunsigned - pub fn checkUnsigned(lua: *Lua, arg: i32) Unsigned { - return c.luaL_checkunsigned(lua.state, arg); - } - - /// Checks whether the code making the call and the Lua library being called are using - /// the same version of Lua and the same numeric types. - /// See https://www.lua.org/manual/5.2/manual.html#luaL_checkversion - pub fn checkVersion(lua: *Lua) void { - return c.luaL_checkversion_(lua.state, c.LUA_VERSION_NUM); - } - - /// Loads and runs the given file - /// See https://www.lua.org/manual/5.2/manual.html#luaL_dofile - pub fn doFile(lua: *Lua, file_name: [:0]const u8) !void { - // translate-c failure - try lua.loadFile(file_name, .binary_text); - try lua.protectedCall(0, mult_return, 0); - } - - /// Loads and runs the given string - /// See https://www.lua.org/manual/5.2/manual.html#luaL_dostring - pub fn doString(lua: *Lua, str: [:0]const u8) !void { - // trnaslate-c failure - try lua.loadString(str); - try lua.protectedCall(0, mult_return, 0); - } - - /// Raises an error - /// See https://www.lua.org/manual/5.2/manual.html#luaL_error - pub fn raiseErrorStr(lua: *Lua, fmt: [:0]const u8, args: anytype) noreturn { - _ = @call(.auto, c.luaL_error, .{ lua.state, fmt.ptr } ++ args); - unreachable; - } - - /// This function produces the return values for process-related functions in the standard library - /// See https://www.lua.org/manual/5.2/manual.html#luaL_execresult - pub fn execResult(lua: *Lua, stat: i32) i32 { - return c.luaL_execresult(lua.state, stat); - } - - /// This function produces the return values for file-related functions in the standard library - /// See https://www.lua.org/manual/5.2/manual.html#luaL_fileresult - pub fn fileResult(lua: *Lua, stat: i32, file_name: [:0]const u8) i32 { - return c.luaL_fileresult(lua.state, stat, file_name.ptr); - } - - /// Pushes onto the stack the field `e` from the metatable of the object at index `obj` - /// and returns the type of the pushed value - /// TODO: possibly return an error if nil - /// See https://www.lua.org/manual/5.2/manual.html#luaL_getmetafield - pub fn getMetaField(lua: *Lua, obj: i32, field: [:0]const u8) !LuaType { - const val_type: LuaType = @enumFromInt(c.luaL_getmetafield(lua.state, obj, field.ptr)); - if (val_type == .nil) return error.Fail; - return val_type; - } - - /// Pushes onto the stack the metatable associated with the name `type_name` in the registry - /// or nil if there is no metatable associated with that name. - /// See https://www.lua.org/manual/5.2/manual.html#luaL_getmetatable - pub fn getMetatableRegistry(lua: *Lua, table_name: [:0]const u8) void { - c.luaL_getmetatable(lua.state, table_name.ptr); - } - - /// Ensures that the value t[`field`], where t is the value at `index`, is a table, and pushes that table onto the stack. - /// See https://www.lua.org/manual/5.2/manual.html#luaL_getsubtable - pub fn getSubtable(lua: *Lua, index: i32, field: [:0]const u8) !void { - if (c.luaL_getsubtable(lua.state, index, field.ptr) == 0) return error.Fail; - } - - /// Creates a copy of string `str`, replacing any occurrence of the string `pat` with the string `rep` - /// Pushes the resulting string on the stack and returns it. - /// See https://www.lua.org/manual/5.2/manual.html#luaL_gsub - pub fn globalSub(lua: *Lua, str: [:0]const u8, pat: [:0]const u8, rep: [:0]const u8) [:0]const u8 { - return std.mem.span(c.luaL_gsub(lua.state, str.ptr, pat.ptr, rep.ptr)); - } - - /// Returns the "length" of the value at the given index as a number - /// it is equivalent to the '#' operator in Lua - /// See https://www.lua.org/manual/5.2/manual.html#luaL_len - pub fn lenRaiseErr(lua: *Lua, index: i32) i64 { - return c.luaL_len(lua.state, index); - } - - /// Loads a buffer as a Lua chunk - /// See https://www.lua.org/manual/5.2/manual.html#luaL_loadbufferx - pub fn loadBuffer(lua: *Lua, buf: []const u8, name: [:0]const u8, mode: Mode) !void { - const mode_str = switch (mode) { - .binary => "b", - .text => "t", - .binary_text => "bt", - }; - switch (c.luaL_loadbufferx(lua.state, buf.ptr, buf.len, name.ptr, mode_str.ptr)) { - StatusCode.ok => return, - StatusCode.err_syntax => return error.Syntax, - StatusCode.err_memory => return error.Memory, - else => unreachable, - } - } - - /// Loads a file as a Lua chunk - /// See https://www.lua.org/manual/5.2/manual.html#luaL_loadfilex - pub fn loadFile(lua: *Lua, file_name: [:0]const u8, mode: Mode) !void { - const mode_str = switch (mode) { - .binary => "b", - .text => "t", - .binary_text => "bt", - }; - const ret = c.luaL_loadfilex(lua.state, file_name.ptr, mode_str.ptr); - switch (ret) { - StatusCode.ok => return, - StatusCode.err_syntax => return error.Syntax, - StatusCode.err_memory => return error.Memory, - err_file => return error.File, - // NOTE: the docs mention possible other return types, but I couldn't figure them out - else => unreachable, - } - } - - /// Loads a string as a Lua chunk - /// See https://www.lua.org/manual/5.2/manual.html#luaL_loadstring - pub fn loadString(lua: *Lua, str: [:0]const u8) !void { - const ret = c.luaL_loadstring(lua.state, str.ptr); - switch (ret) { - StatusCode.ok => return, - StatusCode.err_syntax => return error.Syntax, - StatusCode.err_memory => return error.Memory, - // loadstring runs lua_load which runs pcall, so can also return any result of an pcall error - StatusCode.err_runtime => return error.Runtime, - StatusCode.err_error => return error.MsgHandler, - else => unreachable, - } - } - - /// Creates a new table and registers there the functions in `list` - /// See https://www.lua.org/manual/5.2/manual.html#luaL_newlib - pub fn newLib(lua: *Lua, list: []const FnReg) void { - // translate-c failure - lua.checkVersion(); - lua.newLibTable(list); - lua.setFuncs(list, 0); - } - - /// Creates a new table with a size optimized to store all entries in the array `list` - /// See https://www.lua.org/manual/5.2/manual.html#luaL_newlibtable - pub fn newLibTable(lua: *Lua, list: []const FnReg) void { - // translate-c failure - lua.createTable(0, @intCast(list.len)); - } - - /// If the registry already has the key `key`, returns an error - /// Otherwise, creates a new table to be used as a metatable for userdata - /// See https://www.lua.org/manual/5.2/manual.html#luaL_newmetatable - pub fn newMetatable(lua: *Lua, key: [:0]const u8) !void { - if (c.luaL_newmetatable(lua.state, key.ptr) == 0) return error.Fail; - } - - /// Creates a new Lua state with an allocator using the default libc allocator - /// See https://www.lua.org/manual/5.2/manual.html#luaL_newstate - pub fn newStateLibc() !Lua { - const state = c.luaL_newstate() orelse return error.Memory; - return Lua{ .state = state }; - } - - // luaL_opt (a macro) really isn't that useful, so not going to implement for now - - /// If the function argument `arg` is a number, returns this number cast to an i32. - /// If the argument is absent or nil returns `default` - /// /// See https://www.lua.org/manual/5.2/manual.html#luaL_optint - pub fn optInt(lua: *Lua, arg: i32, default: i32) i32 { - return c.luaL_optint(lua.state, arg, default); - } - - /// If the function argument `arg` is an integer, returns the integer - /// If the argument is absent or nil returns `default` - /// See https://www.lua.org/manual/5.2/manual.html#luaL_optinteger - pub fn optInteger(lua: *Lua, arg: i32, default: Integer) Integer { - return c.luaL_optinteger(lua.state, arg, default); - } - - /// If the function argument `arg` is a slice of bytes, returns the slice - /// If the argument is absent or nil returns `default` - /// See https://www.lua.org/manual/5.2/manual.html#luaL_optlstring - pub fn optBytes(lua: *Lua, arg: i32, default: [:0]const u8) [:0]const u8 { - var length: usize = 0; - // will never return null because default cannot be null - const ret: [*]const u8 = c.luaL_optlstring(lua.state, arg, default.ptr, &length); - if (ret == default.ptr) return default; - return ret[0..length :0]; - } - - /// If the function argument `arg` is a number, returns the number - /// If the argument is absent or nil returns `default` - /// See https://www.lua.org/manual/5.2/manual.html#luaL_optnumber - pub fn optNumber(lua: *Lua, arg: i32, default: Number) Number { - return c.luaL_optnumber(lua.state, arg, default); - } - - /// If the function argument `arg` is a string, returns the string - /// If the argment is absent or nil returns `default` - /// See https://www.lua.org/manual/5.2/manual.html#luaL_optlstring - pub fn optString(lua: *Lua, arg: i32, default: [:0]const u8) [*:0]const u8 { - // translate-c error - return c.luaL_optlstring(lua.state, arg, default.ptr, null); - } - - /// If the function argument is a number, returns this number as an unsigned - /// If the argument is absent or nil returns default, otherwise raises an error - /// See https://www.lua.org/manual/5.2/manual.html#luaL_optunsigned - pub fn optUnsigned(lua: *Lua, arg: i32, default: Unsigned) Unsigned { - return c.luaL_optunsigned(lua.state, arg, default); - } - - /// Creates and returns a reference in the table at index `index` for the object on the top of the stack - /// Pops the object - /// See https://www.lua.org/manual/5.2/manual.html#luaL_ref - pub fn ref(lua: *Lua, index: i32) !i32 { - const ret = c.luaL_ref(lua.state, index); - return if (ret == ref_nil) error.Fail else ret; - } - - /// If package.loaded[`mod_name`] is not true, calls the function `open_fn` with `mod_name` - /// as an argument and sets the call result to package.loaded[`mod_name`] - /// See https://www.lua.org/manual/5.2/manual.html#luaL_requiref - pub fn requireF(lua: *Lua, mod_name: [:0]const u8, open_fn: CFn, global: bool) void { - c.luaL_requiref(lua.state, mod_name.ptr, open_fn, @intFromBool(global)); - } - - /// Registers all functions in the array `fns` into the table on the top of the stack - /// All functions are created with `num_upvalues` upvalues - /// See https://www.lua.org/manual/5.2/manual.html#luaL_setfuncs - pub fn setFuncs(lua: *Lua, funcs: []const FnReg, num_upvalues: i32) void { - lua.checkStackErr(num_upvalues, "too many upvalues"); - for (funcs) |f| { - if (f.func) |func| { - var i: i32 = 0; - // copy upvalues to the top - while (i < num_upvalues) : (i += 1) lua.pushValue(-num_upvalues); - lua.pushClosure(func, num_upvalues); - } else lua.pushBoolean(false); // register a placeholder - lua.setField(-(num_upvalues + 2), f.name); - } - lua.pop(num_upvalues); - } - - /// Sets the metatable of the object on the top of the stack as the metatable associated - /// with `table_name` in the registry - /// See https://www.lua.org/manual/5.2/manual.html#luaL_setmetatable - pub fn setMetatableRegistry(lua: *Lua, table_name: [:0]const u8) void { - c.luaL_setmetatable(lua.state, table_name.ptr); - } - - /// This function works like `Lua.checkUserdata()` except it returns a Zig error instead of raising a Lua error on fail - /// See https://www.lua.org/manual/5.2/manual.html#luaL_testudata - pub fn testUserdata(lua: *Lua, comptime T: type, arg: i32, name: [:0]const u8) !*T { - if (c.luaL_testudata(lua.state, arg, name.ptr)) |ptr| { - return opaqueCast(T, ptr); - } else return error.Fail; - } - - /// This function works like `Lua.checkUserdataSlice()` except it returns a Zig error instead of raising a Lua error on fail - /// See https://www.lua.org/manual/5.2/manual.html#luaL_checkudata - pub fn testUserdataSlice(lua: *Lua, comptime T: type, arg: i32, name: [:0]const u8) ![]T { - if (c.luaL_testudata(lua.state, arg, name.ptr)) |ptr| { - const size = lua.rawLen(arg) / @sizeOf(T); - return @as([*]T, @ptrCast(@alignCast(ptr)))[0..size]; - } else return error.Fail; - } - - /// Converts any Lua value at the given index into a string in a reasonable format - /// See https://www.lua.org/manual/5.2/manual.html#luaL_tolstring - pub fn toBytesFmt(lua: *Lua, index: i32) [:0]const u8 { - var length: usize = undefined; - const ptr = c.luaL_tolstring(lua.state, index, &length); - return ptr[0..length :0]; - } - - /// Creates and pushes a traceback of the stack of `other` - /// See https://www.lua.org/manual/5.2/manual.html#luaL_traceback - pub fn traceback(lua: *Lua, other: *Lua, msg: [:0]const u8, level: i32) void { - c.luaL_traceback(lua.state, other.state, msg.ptr, level); - } - - /// Returns the name of the type of the value at the given `index` - /// See https://www.lua.org/manual/5.2/manual.html#luaL_typename - pub fn typeNameIndex(lua: *Lua, index: i32) [:0]const u8 { - return std.mem.span(c.luaL_typename(lua.state, index)); - } - - /// Releases the reference `r` from the table at index `index` - /// See https://www.lua.org/manual/5.2/manual.html#luaL_unref - pub fn unref(lua: *Lua, index: i32, r: i32) void { - c.luaL_unref(lua.state, index, r); - } - - /// Pushes onto the stack a string identifying the current position of the control - /// See https://www.lua.org/manual/5.2/manual.html#luaL_where - /// at the call stack `level` - pub fn where(lua: *Lua, level: i32) void { - c.luaL_where(lua.state, level); - } - - // Standard library loading functions - - /// Opens the specified standard library functions - /// Behaves like openLibs, but allows specifying which libraries - /// to expose to the global table rather than all of them - /// See https://www.lua.org/manual/5.2/manual.html#luaL_openlibs - pub fn open(lua: *Lua, libs: Libs) void { - if (libs.base) lua.requireF("_G", c.luaopen_base, true); - if (libs.coroutine) lua.requireF(c.LUA_COLIBNAME, c.luaopen_coroutine, true); - if (libs.package) lua.requireF(c.LUA_LOADLIBNAME, c.luaopen_package, true); - if (libs.string) lua.requireF(c.LUA_STRLIBNAME, c.luaopen_string, true); - if (libs.table) lua.requireF(c.LUA_TABLIBNAME, c.luaopen_table, true); - if (libs.math) lua.requireF(c.LUA_MATHLIBNAME, c.luaopen_math, true); - if (libs.io) lua.requireF(c.LUA_IOLIBNAME, c.luaopen_io, true); - if (libs.os) lua.requireF(c.LUA_OSLIBNAME, c.luaopen_os, true); - if (libs.debug) lua.requireF(c.LUA_DBLIBNAME, c.luaopen_debug, true); - if (libs.bit) lua.requireF(c.LUA_BITLIBNAME, c.luaopen_bit32, true); - } - - /// Open all standard libraries - /// See https://www.lua.org/manual/5.2/manual.html#luaL_openlibs - pub fn openLibs(lua: *Lua) void { - c.luaL_openlibs(lua.state); - } - - /// Open the basic standard library - pub fn openBase(lua: *Lua) void { - _ = c.luaopen_base(lua.state); - } - - /// Open the coroutine standard library - pub fn openCoroutine(lua: *Lua) void { - _ = c.luaopen_coroutine(lua.state); - } - - /// Open the package standard library - pub fn openPackage(lua: *Lua) void { - _ = c.luaopen_package(lua.state); - } - - /// Open the string standard library - pub fn openString(lua: *Lua) void { - _ = c.luaopen_string(lua.state); - } - - /// Open the table standard library - pub fn openTable(lua: *Lua) void { - _ = c.luaopen_table(lua.state); - } - - /// Open the math standard library - pub fn openMath(lua: *Lua) void { - _ = c.luaopen_math(lua.state); - } - - /// Open the io standard library - pub fn openIO(lua: *Lua) void { - _ = c.luaopen_io(lua.state); - } - - /// Open the os standard library - pub fn openOS(lua: *Lua) void { - _ = c.luaopen_os(lua.state); - } - - /// Open the debug standard library - pub fn openDebug(lua: *Lua) void { - _ = c.luaopen_debug(lua.state); - } - - /// Open the bit32 standard library - pub fn openBit32(lua: *Lua) void { - _ = c.luaopen_bit32(lua.state); - } -}; - -/// A string buffer allowing for Zig code to build Lua strings piecemeal -/// All LuaBuffer functions are wrapped in this struct to make the API more convenient to use -pub const Buffer = struct { - b: LuaBuffer = undefined, - - /// Initialize a Lua string buffer - /// See https://www.lua.org/manual/5.2/manual.html#luaL_buffinit - pub fn init(buf: *Buffer, lua: Lua) void { - c.luaL_buffinit(lua.state, &buf.b); - } - - /// Initialize a Lua string buffer with an initial size - /// See https://www.lua.org/manual/5.2/manual.html#luaL_buffinitsize - pub fn initSize(buf: *Buffer, lua: Lua, size: usize) []u8 { - return c.luaL_buffinitsize(lua.state, &buf.b, size)[0..size]; - } - - /// Internal Lua type for a string buffer - pub const LuaBuffer = c.luaL_Buffer; - - pub const buffer_size = c.LUAL_BUFFERSIZE; - - /// Adds `byte` to the buffer - /// See https://www.lua.org/manual/5.2/manual.html#luaL_addchar - pub fn addChar(buf: *Buffer, byte: u8) void { - // could not be translated by translate-c - var lua_buf = &buf.b; - if (lua_buf.n >= lua_buf.size) _ = buf.prepSize(1); - lua_buf.b[lua_buf.n] = byte; - lua_buf.n += 1; - } - - /// Adds the string to the buffer - /// See https://www.lua.org/manual/5.2/manual.html#luaL_addlstring - pub fn addBytes(buf: *Buffer, str: []const u8) void { - c.luaL_addlstring(&buf.b, str.ptr, str.len); - } - - /// Adds to the buffer a string of `length` previously copied to the buffer area - /// See https://www.lua.org/manual/5.2/manual.html#luaL_addsize - pub fn addSize(buf: *Buffer, length: usize) void { - // another function translate-c couldn't handle - // c.luaL_addsize(&buf.b, length); - var lua_buf = &buf.b; - lua_buf.n += length; - } - - /// Adds the zero-terminated string pointed to by `str` to the buffer - /// See https://www.lua.org/manual/5.2/manual.html#luaL_addstring - pub fn addString(buf: *Buffer, str: [:0]const u8) void { - c.luaL_addstring(&buf.b, str.ptr); - } - - /// Adds the value on the top of the stack to the buffer - /// Pops the value - /// See https://www.lua.org/manual/5.2/manual.html#luaL_addvalue - pub fn addValue(buf: *Buffer) void { - c.luaL_addvalue(&buf.b); - } - - /// Equivalent to prepSize with a buffer size of Buffer.buffer_size - /// See https://www.lua.org/manual/5.2/manual.html#luaL_prepbuffer - pub fn prep(buf: *Buffer) []u8 { - return buf.prepSize(buffer_size)[0..buffer_size]; - } - - /// Returns an address to a space of `size` where you can copy a string - /// to be added to the buffer - /// you must call `Buffer.addSize` to actually add it to the buffer - /// See https://www.lua.org/manual/5.2/manual.html#luaL_prepbuffsize - pub fn prepSize(buf: *Buffer, size: usize) []u8 { - return c.luaL_prepbuffsize(&buf.b, size)[0..size]; - } - - /// Finishes the use of the buffer leaving the final string on the top of the stack - /// See https://www.lua.org/manual/5.2/manual.html#luaL_pushresult - pub fn pushResult(buf: *Buffer) void { - c.luaL_pushresult(&buf.b); - } - - /// Equivalent to `Buffer.addSize()` followed by `Buffer.pushResult()` - /// See https://www.lua.org/manual/5.2/manual.html#luaL_pushresultsize - pub fn pushResultSize(buf: *Buffer, size: usize) void { - c.luaL_pushresultsize(&buf.b, size); - } -}; - -// Helper functions to make the ziglua API easier to use - -/// Casts the opaque pointer to a pointer of the given type with the proper alignment -/// Useful for casting pointers from the Lua API like userdata or other data -pub inline fn opaqueCast(comptime T: type, ptr: *anyopaque) *T { - return @ptrCast(@alignCast(ptr)); -} - -pub const ZigFn = fn (lua: *Lua) i32; -pub const ZigHookFn = fn (lua: *Lua, event: Event, info: *DebugInfo) void; -pub const ZigContFn = fn (lua: *Lua, status: Status, ctx: i32) i32; -pub const ZigReaderFn = fn (lua: *Lua, data: *anyopaque) ?[]const u8; -pub const ZigWriterFn = fn (lua: *Lua, buf: []const u8, data: *anyopaque) bool; - -fn TypeOfWrap(comptime T: type) type { - return switch (T) { - LuaState => Lua, - ZigFn => CFn, - ZigHookFn => CHookFn, - ZigReaderFn => CReaderFn, - ZigWriterFn => CWriterFn, - else => @compileError("unsupported type given to wrap: '" ++ @typeName(T) ++ "'"), - }; -} - -/// Wraps the given value for use in the Lua API -/// Supports the following: -/// * `LuaState` => `Lua` -pub fn wrap(comptime value: anytype) TypeOfWrap(@TypeOf(value)) { - const T = @TypeOf(value); - return switch (T) { - LuaState => Lua{ .state = value }, - ZigFn => wrapZigFn(value), - ZigHookFn => wrapZigHookFn(value), - ZigReaderFn => wrapZigReaderFn(value), - ZigWriterFn => wrapZigWriterFn(value), - else => @compileError("unsupported type given to wrap: '" ++ @typeName(T) ++ "'"), - }; -} - -/// Wrap a ZigFn in a CFn for passing to the API -fn wrapZigFn(comptime f: ZigFn) CFn { - return struct { - fn inner(state: ?*LuaState) callconv(.C) c_int { - // this is called by Lua, state should never be null - var lua: Lua = .{ .state = state.? }; - return @call(.always_inline, f, .{&lua}); - } - }.inner; -} - -/// Wrap a ZigHookFn in a CHookFn for passing to the API -fn wrapZigHookFn(comptime f: ZigHookFn) CHookFn { - return struct { - fn inner(state: ?*LuaState, ar: ?*Debug) callconv(.C) void { - // this is called by Lua, state should never be null - var lua: Lua = .{ .state = state.? }; - var info: DebugInfo = .{ - .current_line = if (ar.?.currentline == -1) null else ar.?.currentline, - .private = @ptrCast(ar.?.i_ci), - }; - @call(.always_inline, f, .{ &lua, @as(Event, @enumFromInt(ar.?.event)), &info }); - } - }.inner; -} - -/// Wrap a ZigReaderFn in a CReaderFn for passing to the API -fn wrapZigReaderFn(comptime f: ZigReaderFn) CReaderFn { - return struct { - fn inner(state: ?*LuaState, data: ?*anyopaque, size: [*c]usize) callconv(.C) [*c]const u8 { - var lua: Lua = .{ .state = state.? }; - if (@call(.always_inline, f, .{ &lua, data.? })) |buffer| { - size.* = buffer.len; - return buffer.ptr; - } else { - size.* = 0; - return null; - } - } - }.inner; -} - -/// Wrap a ZigWriterFn in a CWriterFn for passing to the API -fn wrapZigWriterFn(comptime f: ZigWriterFn) CWriterFn { - return struct { - fn inner(state: ?*LuaState, buf: ?*const anyopaque, size: usize, data: ?*anyopaque) callconv(.C) c_int { - // this is called by Lua, state should never be null - var lua: Lua = .{ .state = state.? }; - const buffer = @as([*]const u8, @ptrCast(buf))[0..size]; - const result = @call(.always_inline, f, .{ &lua, buffer, data.? }); - // it makes more sense for the inner writer function to return false for failure, - // so negate the result here - return @intFromBool(!result); - } - }.inner; -} - -/// Export a Zig function to be used as a the entry point to a Lua module -/// -/// Exported as luaopen_[name] -pub fn exportFn(comptime name: []const u8, comptime func: ZigFn) CFn { - return struct { - fn luaopen(state: ?*LuaState) callconv(.C) c_int { - const declaration = comptime wrap(func); - - return @call(.always_inline, declaration, .{state}); - } - - comptime { - @export(luaopen, .{ .name = "luaopen_" ++ name }); - } - }.luaopen; -} diff --git a/src/tests.zig b/src/tests.zig index e64aef6..89acec4 100644 --- a/src/tests.zig +++ b/src/tests.zig @@ -26,6 +26,8 @@ fn expectStringContains(actual: []const u8, expected_contains: []const u8) !void // toInteger differ enough to require these helper functions to handle the differences // to keep the test code more readable +// TODO: implement as much of this as is reasonable in the shared lib.zig and remove these + /// Return true if ziglua.lang matches any of the given langs inline fn langIn(langs: anytype) bool { inline for (langs) |lang| if (ziglua.lang == lang) return true; @@ -48,7 +50,7 @@ inline fn toNumber(lua: *Lua, index: i32) !ziglua.Number { /// getGlobal that always returns an error union inline fn getGlobal(lua: *Lua, name: [:0]const u8) !ziglua.LuaType { - if (langIn(.{ .lua51, .lua52, .luajit })) { + if (langIn(.{ .lua51, .luajit })) { lua.getGlobal(name); return lua.typeOf(-1); } @@ -66,7 +68,7 @@ inline fn getIndex(lua: *Lua, index: i32, i: ziglua.Integer) ziglua.LuaType { /// getTagle that always returns a LuaType inline fn getTable(lua: *Lua, index: i32) ziglua.LuaType { - if (langIn(.{ .lua53, .lua54, .luau })) { + if (langIn(.{ .lua52, .lua53, .lua54, .luau })) { return lua.getTable(index); } lua.getTable(index); @@ -75,7 +77,7 @@ inline fn getTable(lua: *Lua, index: i32) ziglua.LuaType { /// rawGetTable that always returns a LuaType inline fn rawGetTable(lua: *Lua, index: i32) ziglua.LuaType { - if (langIn(.{ .lua53, .lua54, .luau })) { + if (langIn(.{ .lua52, .lua53, .lua54, .luau })) { return lua.rawGetTable(index); } lua.rawGetTable(index); @@ -674,42 +676,20 @@ test "global table" { lua.open(.{ .math = true, .base = true }); lua.pushGlobalTable(); - if (ziglua.lang == .lua52) { - // find the print function - _ = lua.pushString("print"); - lua.getTable(-2); - try expectEqual(.function, lua.typeOf(-1)); - - // index the global table in the global table - lua.getField(-2, "_G"); - try expectEqual(.table, lua.typeOf(-1)); - - // find pi in the math table - lua.getField(-1, "math"); - try expectEqual(.table, lua.typeOf(-1)); - lua.getField(-1, "pi"); - try expectEqual(.number, lua.typeOf(-1)); - - // but the string table should be nil - lua.pop(2); - lua.getField(-1, "string"); - try expectEqual(.nil, lua.typeOf(-1)); - } else { - // find the print function - _ = lua.pushString("print"); - try expectEqual(.function, lua.getTable(-2)); + // find the print function + _ = lua.pushString("print"); + try expectEqual(.function, lua.getTable(-2)); - // index the global table in the global table - try expectEqual(.table, lua.getField(-2, "_G")); + // index the global table in the global table + try expectEqual(.table, lua.getField(-2, "_G")); - // find pi in the math table - try expectEqual(.table, lua.getField(-1, "math")); - try expectEqual(.number, lua.getField(-1, "pi")); + // find pi in the math table + try expectEqual(.table, lua.getField(-1, "math")); + try expectEqual(.number, lua.getField(-1, "pi")); - // but the string table should be nil - lua.pop(2); - try expectEqual(.nil, lua.getField(-1, "string")); - } + // but the string table should be nil + lua.pop(2); + try expectEqual(.nil, lua.getField(-1, "string")); } const sub = struct { From 19c1a0ba883dfd289d98d25577dd08b01fc9cdb8 Mon Sep 17 00:00:00 2001 From: Nathan Craddock Date: Sat, 27 Jan 2024 22:06:36 -0700 Subject: [PATCH 3/5] Merge lib51.zig into lib.zig Now lua 5.1 and luajit work. The last thing is Luau --- src/lib.zig | 531 +++++++++++++--- src/lib51.zig | 1692 ------------------------------------------------- src/tests.zig | 93 +-- 3 files changed, 461 insertions(+), 1855 deletions(-) delete mode 100644 src/lib51.zig diff --git a/src/lib.zig b/src/lib.zig index da90e3b..7b185b2 100644 --- a/src/lib.zig +++ b/src/lib.zig @@ -5,6 +5,8 @@ const c = @cImport({ @cInclude("lua.h"); @cInclude("lualib.h"); @cInclude("lauxlib.h"); + + if (lang == .luajit) @cInclude("luajit.h"); }); const config = @import("config"); @@ -73,6 +75,53 @@ pub const CompareOperator = enum(u2) { /// The internal Lua debug structure const Debug = c.lua_Debug; +pub const DebugInfo51 = struct { + source: [:0]const u8 = undefined, + src_len: usize = 0, + short_src: [c.LUA_IDSIZE:0]u8 = undefined, + + name: ?[:0]const u8 = undefined, + name_what: NameType = undefined, + what: FnType = undefined, + + current_line: ?i32 = null, + first_line_defined: ?i32 = null, + last_line_defined: ?i32 = null, + + is_vararg: bool = false, + + private: c_int = undefined, + + pub const NameType = enum { global, local, method, field, upvalue, other }; + + pub const FnType = enum { lua, c, main }; + + pub const Options = packed struct { + @">": bool = false, + f: bool = false, + l: bool = false, + n: bool = false, + S: bool = false, + u: bool = false, + L: bool = false, + + fn toString(options: Options) [10:0]u8 { + var str = [_:0]u8{0} ** 10; + var index: u8 = 0; + + inline for (std.meta.fields(Options)) |field| { + if (@field(options, field.name)) { + str[index] = field.name[0]; + index += 1; + } + } + while (index < str.len) : (index += 1) str[index] = 0; + + return str; + } + }; +}; + const DebugInfo52 = struct { source: [:0]const u8 = undefined, src_len: usize = 0, @@ -183,8 +232,9 @@ const DebugInfo54 = struct { }; pub const DebugInfo = switch (lang) { + .lua52, .lua53 => DebugInfo52, .lua54 => DebugInfo54, - else => DebugInfo52, + else => DebugInfo51, }; /// The superset of all errors returned from ziglua @@ -203,14 +253,21 @@ pub const Error = error{ File, } || switch (lang) { .lua52, .lua53 => error{ - /// A memory in a __gc metamethod + /// A memory error in a __gc metamethod GCMetaMethod, }, else => error{}, }; +const Event51 = enum(u3) { + call = c.LUA_HOOKCALL, + ret = c.LUA_HOOKRET, + line = c.LUA_HOOKLINE, + count = c.LUA_HOOKCOUNT, +}; + /// The type of event that triggers a hook -pub const Event = enum(u3) { +const Event52 = enum(u3) { call = c.LUA_HOOKCALL, ret = c.LUA_HOOKRET, line = c.LUA_HOOKLINE, @@ -218,12 +275,20 @@ pub const Event = enum(u3) { tail_call = c.LUA_HOOKTAILCALL, }; +pub const Event = switch (lang) { + .lua51, .luajit => Event51, + else => Event52, +}; + /// Type for arrays of functions to be registered pub const FnReg = struct { name: [:0]const u8, func: ?CFn, }; +/// The index of the global environment table +pub const globals_index = c.LUA_GLOBALSINDEX; + /// Type for debugging hook functions pub const CHookFn = *const fn (state: ?*LuaState, ar: ?*Debug) callconv(.C) void; @@ -271,6 +336,18 @@ pub const Context = isize; /// Type for continuation functions pub const CContFn = *const fn (state: ?*LuaState, status: c_int, ctx: Context) callconv(.C) c_int; +pub const Libs51 = packed struct { + base: bool = false, + package: bool = false, + string: bool = false, + utf8: bool = false, + table: bool = false, + math: bool = false, + io: bool = false, + os: bool = false, + debug: bool = false, +}; + pub const Libs52 = packed struct { base: bool = false, coroutine: bool = false, @@ -298,9 +375,10 @@ pub const Libs53 = packed struct { }; /// Bitflag for the Lua standard libraries -pub const Libs = switch(lang) { +pub const Libs = switch (lang) { + .lua52 => Libs52, .lua53, .lua54 => Libs53, - else => Libs52, + else => Libs51, }; /// The type of the opaque structure that points to a thread and the state of a Lua interpreter @@ -386,7 +464,10 @@ pub const Status = enum(u3) { /// used for that specific function's return type /// TODO: see where this is used and check if a null can be used instead const StatusCode = struct { - pub const ok = c.LUA_OK; + pub const ok = switch (@hasDecl(c, "LUA_OK")) { + true => c.LUA_OK, + false => 0, + }; pub const yield = c.LUA_YIELD; pub const err_runtime = c.LUA_ERRRUN; pub const err_syntax = c.LUA_ERRSYNTAX; @@ -395,7 +476,7 @@ const StatusCode = struct { pub const err_gcmm = switch (lang) { .lua52, .lua53 => c.LUA_ERRGCMM, - else => @compileError("err_gcm not defined"), + else => unreachable, }; }; @@ -514,7 +595,10 @@ pub const Lua = struct { /// are pushed onto the stack. /// See https://www.lua.org/manual/5.4/manual.html#lua_call pub fn call(lua: *Lua, num_args: i32, num_results: i32) void { - lua.callCont(num_args, num_results, 0, null); + switch (lang) { + .lua51, .luajit => c.lua_call(lua.state, num_args, num_results), + else => lua.callCont(num_args, num_results, 0, null), + } } fn callCont52(lua: *Lua, num_args: i32, num_results: i32, ctx: i32, k: ?CFn) void { @@ -528,8 +612,9 @@ pub const Lua = struct { /// Like call, but allows the called function to yield /// See https://www.lua.org/manual/5.4/manual.html#lua_callk pub const callCont = switch (lang) { + .lua52 => callCont52, .lua53, .lua54 => callCont53, - else => callCont52, + else => @compileError("callCont() not defined"), }; /// Ensures that the stack has space for at least n extra arguments @@ -553,7 +638,7 @@ pub const Lua = struct { /// Close the to-be-closed slot at the given index and set the value to nil /// The index must be the last index previously marked to be closed with toClose /// See https://www.lua.org/manual/5.4/manual.html#lua_closeslot - pub const closeSlot = switch(lang) { + pub const closeSlot = switch (lang) { .lua54 => closeSlot54, else => @compileError("closeSlot not available"), }; @@ -585,6 +670,20 @@ pub const Lua = struct { c.lua_concat(lua.state, n); } + /// Calls the C function c_fn in protected mode. The function starts with only one element on its + /// stack, the userdata given to this function. + /// See https://www.lua.org/manual/5.1/manual.html#lua_cpcall + pub fn cProtectedCall(lua: *Lua, c_fn: CFn, userdata: *anyopaque) !void { + const ret = c.lua_cpcall(lua.state, c_fn, userdata); + switch (ret) { + StatusCode.ok => return, + StatusCode.err_runtime => return error.Runtime, + StatusCode.err_memory => return error.Memory, + StatusCode.err_error => return error.MsgHandler, + else => unreachable, + } + } + /// Copies the element at from_index to the valid index to_index, replacing the value at that position /// See https://www.lua.org/manual/5.4/manual.html#lua_copy pub fn copy(lua: *Lua, from_index: i32, to_index: i32) void { @@ -600,7 +699,7 @@ pub const Lua = struct { c.lua_createtable(lua.state, num_arr, num_rec); } - fn dump52(lua: *Lua, writer: CWriterFn, data: *anyopaque) !void { + fn dump51(lua: *Lua, writer: CWriterFn, data: *anyopaque) !void { if (c.lua_dump(lua.state, writer, data) != 0) return error.Fail; } @@ -614,9 +713,16 @@ pub const Lua = struct { /// See https://www.lua.org/manual/5.4/manual.html#lua_dump pub const dump = switch (lang) { .lua53, .lua54 => dump53, - else => dump52, + else => dump51, }; + /// Returns true if the two values at the indexes are equal following the semantics of the + /// Lua == operator. + /// See https://www.lua.org/manual/5.1/manual.html#lua_equal + pub fn equal(lua: *Lua, index1: i32, index2: i32) bool { + return c.lua_equal(lua.state, index1, index2) == 1; + } + /// Raises a Lua error using the value at the top of the stack as the error object /// Does a longjump and therefore never returns /// See https://www.lua.org/manual/5.4/manual.html#lua_error @@ -656,7 +762,7 @@ pub const Lua = struct { _ = c.lua_gc(lua.state, c.LUA_GCSTEP, step_size); } - fn gcStep53(lua: *Lua) void { + fn gcStep51(lua: *Lua) void { _ = c.lua_gc(lua.state, c.LUA_GCSTEP, 0); } @@ -664,7 +770,7 @@ pub const Lua = struct { /// See https://www.lua.org/manual/5.4/manual.html#lua_gc pub const gcStep = switch (lang) { .lua54 => gcStep54, - else => gcStep53, + else => gcStep51, }; /// Returns the current amount of memory (in Kbytes) in use by Lua @@ -764,6 +870,12 @@ pub const Lua = struct { return @as([*]u8, @ptrCast(c.lua_getextraspace(lua.state).?))[0..@sizeOf(isize)]; } + /// Pushes onto the stack the environment table of the value at the given index. + /// See https://www.lua.org/manual/5.1/manual.html#lua_getfenv + pub fn getFnEnvironment(lua: *Lua, index: i32) void { + c.lua_getfenv(lua.state, index); + } + /// Pushes onto the stack the value t[key] where t is the value at the given index /// See https://www.lua.org/manual/5.4/manual.html#lua_getfield pub fn getField(lua: *Lua, index: i32, key: [:0]const u8) LuaType { @@ -772,7 +884,7 @@ pub const Lua = struct { else => { c.lua_getfield(lua.state, index, key.ptr); return lua.typeOf(-1); - } + }, } } @@ -781,7 +893,7 @@ pub const Lua = struct { /// See https://www.lua.org/manual/5.4/manual.html#lua_getglobal pub fn getGlobal(lua: *Lua, name: [:0]const u8) !LuaType { const lua_type: LuaType = blk: { - switch (lang) { + switch (lang) { .lua53, .lua54 => break :blk @enumFromInt(c.lua_getglobal(lua.state, name.ptr)), else => { c.lua_getglobal(lua.state, name.ptr); @@ -842,7 +954,7 @@ pub const Lua = struct { else => { c.lua_gettable(lua.state, index); return lua.typeOf(-1); - } + }, } } @@ -950,10 +1062,25 @@ pub const Lua = struct { c.lua_len(lua.state, index); } - /// Loads a Lua chunk without running it - /// If there are no errors, pushes the compiled chunk on the top of the stack as a function - /// See https://www.lua.org/manual/5.4/manual.html#lua_load - pub fn load(lua: *Lua, reader: CReaderFn, data: *anyopaque, chunk_name: [:0]const u8, mode: Mode) !void { + /// Returns true if the value at index1 is smaller than the value at index2, following the + /// semantics of the Lua < operator. + /// See https://www.lua.org/manual/5.4/manual.html#lua_lessthan + /// TODO: maybe implement these using compare somehow? + pub fn lessThan(lua: *Lua, index1: i32, index2: i32) bool { + return c.lua_lessthan(lua.state, index1, index2) == 1; + } + + fn load51(lua: *Lua, reader: CReaderFn, data: *anyopaque, chunk_name: [:0]const u8) !void { + const ret = c.lua_load(lua.state, reader, data, chunk_name.ptr); + switch (ret) { + StatusCode.ok => return, + StatusCode.err_syntax => return error.Syntax, + StatusCode.err_memory => return error.Memory, + else => unreachable, + } + } + + fn load52(lua: *Lua, reader: CReaderFn, data: *anyopaque, chunk_name: [:0]const u8, mode: Mode) !void { const mode_str = switch (mode) { .binary => "b", .text => "t", @@ -964,26 +1091,34 @@ pub const Lua = struct { return switch (lang) { .lua54 => switch (ret) { StatusCode.ok => {}, - StatusCode.err_syntax => error.Syntax, - StatusCode.err_memory => error.Memory, + StatusCode.err_syntax => error.Syntax, + StatusCode.err_memory => error.Memory, // lua_load runs pcall, so can also return any result of a pcall error - StatusCode.err_runtime => error.Runtime, - StatusCode.err_error => error.MsgHandler, + StatusCode.err_runtime => error.Runtime, + StatusCode.err_error => error.MsgHandler, else => unreachable, }, else => switch (ret) { StatusCode.ok => {}, - StatusCode.err_syntax => error.Syntax, - StatusCode.err_memory => error.Memory, + StatusCode.err_syntax => error.Syntax, + StatusCode.err_memory => error.Memory, // lua_load runs pcall, so can also return any result of a pcall error - StatusCode.err_runtime => error.Runtime, - StatusCode.err_error => error.MsgHandler, - StatusCode.err_gcmm => error.GCMetaMethod, + StatusCode.err_runtime => error.Runtime, + StatusCode.err_error => error.MsgHandler, + StatusCode.err_gcmm => error.GCMetaMethod, else => unreachable, }, }; } + /// Loads a Lua chunk without running it + /// If there are no errors, pushes the compiled chunk on the top of the stack as a function + /// See https://www.lua.org/manual/5.4/manual.html#lua_load + pub const load = switch (lang) { + .lua51, .luajit => load51, + else => load52, + }; + /// Creates a new independent state and returns its main thread /// See https://www.lua.org/manual/5.4/manual.html#lua_newstate pub fn newState(alloc_fn: AllocFn, data: ?*const anyopaque) !Lua { @@ -1006,52 +1141,52 @@ pub const Lua = struct { return .{ .state = state }; } - /// This function allocates a new userdata of the given type with user_values associated Lua values. + /// This function allocates a new userdata of the given type. /// Returns a pointer to the Lua-owned data - /// See https://www.lua.org/manual/5.4/manual.html#lua_newuserdatauv - fn newUserdata54(lua: *Lua, comptime T: type, user_values: i32) *T { + /// See https://www.lua.org/manual/5.3/manual.html#lua_newuserdata + fn newUserdata51(lua: *Lua, comptime T: type) *T { // safe to .? because this function throws a Lua error on out of memory // so the returned pointer should never be null - const ptr = c.lua_newuserdatauv(lua.state, @sizeOf(T), user_values).?; + const ptr = c.lua_newuserdata(lua.state, @sizeOf(T)).?; return opaqueCast(T, ptr); } - /// This function allocates a new userdata of the given type. + /// This function allocates a new userdata of the given type with user_values associated Lua values. /// Returns a pointer to the Lua-owned data - /// See https://www.lua.org/manual/5.3/manual.html#lua_newuserdata - fn newUserdata52(lua: *Lua, comptime T: type) *T { + /// See https://www.lua.org/manual/5.4/manual.html#lua_newuserdatauv + fn newUserdata54(lua: *Lua, comptime T: type, user_values: i32) *T { // safe to .? because this function throws a Lua error on out of memory // so the returned pointer should never be null - const ptr = c.lua_newuserdata(lua.state, @sizeOf(T)).?; + const ptr = c.lua_newuserdatauv(lua.state, @sizeOf(T), user_values).?; return opaqueCast(T, ptr); } pub const newUserdata = switch (lang) { .lua54 => newUserdata54, - else => newUserdata52, + else => newUserdata51, }; - /// This function creates and pushes a slice of full userdata onto the stack with user_values associated Lua values. + /// This function creates and pushes a slice of full userdata onto the stack. /// Returns a slice to the Lua-owned data. - /// See https://www.lua.org/manual/5.4/manual.html#lua_newuserdatauv - fn newUserdataSlice54(lua: *Lua, comptime T: type, size: usize, user_values: i32) []T { + /// See https://www.lua.org/manual/5.3/manual.html#lua_newuserdata + fn newUserdataSlice51(lua: *Lua, comptime T: type, size: usize) []T { // safe to .? because this function throws a Lua error on out of memory - const ptr = c.lua_newuserdatauv(lua.state, @sizeOf(T) * size, user_values).?; + const ptr = c.lua_newuserdata(lua.state, @sizeOf(T) * size).?; return @as([*]T, @ptrCast(@alignCast(ptr)))[0..size]; } - /// This function creates and pushes a slice of full userdata onto the stack. + /// This function creates and pushes a slice of full userdata onto the stack with user_values associated Lua values. /// Returns a slice to the Lua-owned data. - /// See https://www.lua.org/manual/5.3/manual.html#lua_newuserdata - fn newUserdataSlice52(lua: *Lua, comptime T: type, size: usize) []T { + /// See https://www.lua.org/manual/5.4/manual.html#lua_newuserdatauv + fn newUserdataSlice54(lua: *Lua, comptime T: type, size: usize, user_values: i32) []T { // safe to .? because this function throws a Lua error on out of memory - const ptr = c.lua_newuserdata(lua.state, @sizeOf(T) * size).?; + const ptr = c.lua_newuserdatauv(lua.state, @sizeOf(T) * size, user_values).?; return @as([*]T, @ptrCast(@alignCast(ptr)))[0..size]; } pub const newUserdataSlice = switch (lang) { .lua54 => newUserdataSlice54, - else => newUserdataSlice52, + else => newUserdataSlice51, }; /// Pops a key from the stack, and pushes a key-value pair from the table at the given index @@ -1072,9 +1207,27 @@ pub const Lua = struct { } else return error.Fail; } - /// Calls a function (or callable object) in protected mode - /// See https://www.lua.org/manual/5.4/manual.html#lua_pcall - pub fn protectedCall(lua: *Lua, num_args: i32, num_results: i32, msg_handler: i32) !void { + /// Returns the length of the value at the given index + /// See https://www.lua.org/manual/5.1/manual.html#lua_objlen + /// TODO: this might be nice to map to "len" + pub fn objectLen(lua: *Lua, index: i32) usize { + return c.lua_objlen(lua.state, index); + } + + fn protectedCall51(lua: *Lua, num_args: i32, num_results: i32, err_func: i32) !void { + // The translate-c version of lua_pcall does not type-check so we must rewrite it + // (macros don't always translate well with translate-c) + const ret = c.lua_pcall(lua.state, num_args, num_results, err_func); + switch (ret) { + StatusCode.ok => return, + StatusCode.err_runtime => return error.Runtime, + StatusCode.err_memory => return error.Memory, + StatusCode.err_error => return error.MsgHandler, + else => unreachable, + } + } + + fn protectedCall52(lua: *Lua, num_args: i32, num_results: i32, msg_handler: i32) !void { // The translate-c version of lua_pcall does not type-check so we must rewrite it // (macros don't always translate well with translate-c) const ret = c.lua_pcallk(lua.state, num_args, num_results, msg_handler, 0, null); @@ -1098,6 +1251,13 @@ pub const Lua = struct { }; } + /// Calls a function (or callable object) in protected mode + /// See https://www.lua.org/manual/5.4/manual.html#lua_pcall + pub const protectedCall = switch (lang) { + .lua51, .luajit => protectedCall51, + else => protectedCall52, + }; + fn protectedCallCont52(lua: *Lua, num_args: i32, num_results: i32, msg_handler: i32, ctx: i32, k: CFn) !void { const ret = c.lua_pcallk(lua.state, num_args, num_results, msg_handler, ctx, k); switch (ret) { @@ -1135,8 +1295,9 @@ pub const Lua = struct { /// Behaves exactly like `Lua.protectedCall()` except that it allows the called function to yield /// See https://www.lua.org/manual/5.4/manual.html#lua_pcallk pub const protectedCallCont = switch (lang) { + .lua52 => protectedCallCont52, .lua53, .lua54 => protectedCallCont53, - else => protectedCallCont52, + else => @compileError("protectedCallCont() not implemented"), }; /// Pops `n` elements from the top of the stack @@ -1192,10 +1353,20 @@ pub const Lua = struct { c.lua_pushlightuserdata(lua.state, ptr); } + const BytesResult = switch (lang) { + .lua51, .luajit => void, + else => []const u8, + }; + /// Pushes the bytes onto the stack. Returns a slice pointing to Lua's internal copy of the string /// See https://www.lua.org/manual/5.4/manual.html#lua_pushlstring - pub fn pushBytes(lua: *Lua, bytes: []const u8) []const u8 { - return c.lua_pushlstring(lua.state, bytes.ptr, bytes.len)[0..bytes.len]; + pub fn pushBytes(lua: *Lua, bytes: []const u8) BytesResult { + switch (lang) { + .lua51, .luajit => { + c.lua_pushlstring(lua.state, bytes.ptr, bytes.len); + }, + else => return c.lua_pushlstring(lua.state, bytes.ptr, bytes.len)[0..bytes.len], + } } /// Pushes a nil value onto the stack @@ -1210,12 +1381,22 @@ pub const Lua = struct { c.lua_pushnumber(lua.state, n); } + const StringResult = switch (lang) { + .lua51, .luajit => void, + else => [:0]const u8, + }; + /// Pushes a zero-terminated string onto the stack /// Lua makes a copy of the string so `str` may be freed immediately after return /// Returns a pointer to the internal Lua string /// See https://www.lua.org/manual/5.4/manual.html#lua_pushstring - pub fn pushString(lua: *Lua, str: [:0]const u8) [:0]const u8 { - return c.lua_pushstring(lua.state, str.ptr)[0..str.len :0]; + pub fn pushString(lua: *Lua, str: [:0]const u8) StringResult { + switch (lang) { + .lua51, .luajit => { + c.lua_pushstring(lua.state, str.ptr); + }, + else => return c.lua_pushstring(lua.state, str.ptr)[0..str.len :0], + } } /// Pushes this thread onto the stack @@ -1254,12 +1435,12 @@ pub const Lua = struct { else => { c.lua_rawget(lua.state, index); return lua.typeOf(-1); - } + }, } } const RawGetIndexNType = switch (lang) { - .lua52 => i32, + .lua51, .lua52, .luajit => i32, else => Integer, }; @@ -1272,7 +1453,7 @@ pub const Lua = struct { else => { c.lua_rawgeti(lua.state, index, n); return lua.typeOf(-1); - } + }, } } @@ -1285,7 +1466,7 @@ pub const Lua = struct { else => { c.lua_rawgetp(lua.state, index, p); return lua.typeOf(-1); - } + }, } } @@ -1308,7 +1489,7 @@ pub const Lua = struct { } const RawSetIndexIType = switch (lang) { - .lua52 => i32, + .lua51, .lua52, .luajit => i32, else => Integer, }; @@ -1344,10 +1525,7 @@ pub const Lua = struct { /// then pops the top element /// See https://www.lua.org/manual/5.4/manual.html#lua_replace pub fn replace(lua: *Lua, index: i32) void { - // translate-c cannot translate this macro correctly - // c.lua_replace(lua.state, index); - lua.copy(-1, index); - lua.pop(1); + c.lua_replace(lua.state, index); } /// This function is deprecated; it is equivalent to closeThread() with from being null. @@ -1356,6 +1534,16 @@ pub const Lua = struct { return lua.closeThread(null); } + pub fn resumeThread51(lua: *Lua, num_args: i32) !ResumeStatus { + const thread_status = c.lua_resume(lua.state, num_args); + switch (thread_status) { + StatusCode.err_runtime => return error.Runtime, + StatusCode.err_memory => return error.Memory, + StatusCode.err_error => return error.MsgHandler, + else => return @enumFromInt(thread_status), + } + } + /// Starts and resumes a coroutine in the given thread /// See https://www.lua.org/manual/5.3/manual.html#lua_resume fn resumeThread52(lua: *Lua, from: ?Lua, num_args: i32) !ResumeStatus { @@ -1384,8 +1572,9 @@ pub const Lua = struct { } pub const resumeThread = switch (lang) { + .lua52, .lua53 => resumeThread52, .lua54 => resumeThread54, - else => resumeThread52, + else => resumeThread51, }; /// Rotates the stack elements between the valid `index` and the top of the stack @@ -1402,6 +1591,13 @@ pub const Lua = struct { c.lua_setallocf(lua.state, alloc_fn, data); } + /// Pops a table from the stack and sets it as the new environment for the value at the + /// given index. Returns an error if the value at that index is not a function or thread or userdata. + /// See https://www.lua.org/manual/5.1/manual.html#lua_setfenv + pub fn setFnEnvironment(lua: *Lua, index: i32) !void { + if (c.lua_setfenv(lua.state, index) == 0) return error.Fail; + } + /// Does the equivalent to t[`k`] = v where t is the value at the given `index` /// and v is the value on the top of the stack /// See https://www.lua.org/manual/5.4/manual.html#lua_setfield @@ -1507,17 +1703,26 @@ pub const Lua = struct { c.lua_toclose(lua.state, index); } - /// Converts the Lua value at the given `index` to a signed integer - /// The Lua value must be an integer, or a number, or a string convertible to an integer otherwise toIntegerX returns 0 - /// Returns an error if the conversion failed - /// See https://www.lua.org/manual/5.4/manual.html#lua_tointeger - pub fn toInteger(lua: *Lua, index: i32) !Integer { + fn toInteger51(lua: *Lua, index: i32) Integer { + return c.lua_tointeger(lua.state, index); + } + + fn toInteger52(lua: *Lua, index: i32) !Integer { var success: c_int = undefined; const result = c.lua_tointegerx(lua.state, index, &success); if (success == 0) return error.Fail; return result; } + /// Converts the Lua value at the given `index` to a signed integer + /// The Lua value must be an integer, or a number, or a string convertible to an integer otherwise toIntegerX returns 0 + /// Returns an error if the conversion failed + /// See https://www.lua.org/manual/5.4/manual.html#lua_tointeger + pub const toInteger = switch (lang) { + .lua51, .luajit => toInteger51, + else => toInteger52, + }; + /// Returns a slice of bytes at the given index /// If the value is not a string or number, returns an error /// If the value was a number the actual value in the stack will be changed to a string @@ -1528,17 +1733,26 @@ pub const Lua = struct { return error.Fail; } - /// Converts the Lua value at the given `index` to a float - /// The Lua value must be a number or a string convertible to a number otherwise toNumberX returns 0 - /// Returns an error if the conversion failed - /// See https://www.lua.org/manual/5.4/manual.html#lua_tonumber - pub fn toNumber(lua: *Lua, index: i32) !Number { + fn toNumber51(lua: *Lua, index: i32) Number { + return c.lua_tonumber(lua.state, index); + } + + fn toNumber52(lua: *Lua, index: i32) !Number { var success: c_int = undefined; const result = c.lua_tonumberx(lua.state, index, &success); if (success == 0) return error.Fail; return result; } + /// Converts the Lua value at the given `index` to a float + /// The Lua value must be a number or a string convertible to a number otherwise toNumberX returns 0 + /// Returns an error if the conversion failed + /// See https://www.lua.org/manual/5.4/manual.html#lua_tonumber + pub const toNumber = switch (lang) { + .lua51, .luajit => toNumber51, + else => toNumber52, + }; + /// Converts the value at the given `index` to an opaque pointer /// See https://www.lua.org/manual/5.4/manual.html#lua_topointer pub fn toPointer(lua: *Lua, index: i32) !*const anyopaque { @@ -1590,7 +1804,10 @@ pub const Lua = struct { /// See https://www.lua.org/manual/5.4/manual.html#lua_touserdata pub fn toUserdataSlice(lua: *Lua, comptime T: type, index: i32) ![]T { if (c.lua_touserdata(lua.state, index)) |ptr| { - const size = lua.rawLen(index) / @sizeOf(T); + const size = switch (lang) { + .lua51, .luajit => lua.objectLen(index) / @sizeOf(T), + else => lua.rawLen(index) / @sizeOf(T), + }; return @as([*]T, @ptrCast(@alignCast(ptr)))[0..size]; } return error.Fail; @@ -1647,13 +1864,22 @@ pub const Lua = struct { c.lua_xmove(lua.state, to.state, num); } + const YieldReturn = switch (lang) { + .lua51, .luajit => i32, + else => noreturn, + }; + /// This function is equivalent to `Lua.yieldCont()` but has no continuation /// This function never returns /// See https://www.lua.org/manual/5.4/manual.html#lua_yield - pub fn yield(lua: *Lua, num_results: i32) noreturn { - // translate-c failed to pass NULL correctly - _ = c.lua_yieldk(lua.state, num_results, 0, null); - unreachable; + pub fn yield(lua: *Lua, num_results: i32) YieldReturn { + switch (lang) { + .lua51, .luajit => return c.lua_yield(lua.state, num_results), + else => { + _ = c.lua_yield(lua.state, num_results); + unreachable; + }, + } } fn yieldCont52(lua: *Lua, num_results: i32, ctx: i32, k: CFn) noreturn { @@ -1705,7 +1931,11 @@ pub const Lua = struct { const str = options.toString(); var ar: Debug = undefined; - ar.i_ci = @ptrCast(info.private); + + switch (lang) { + .lua51, .luajit => ar.i_ci = info.private, + else => ar.i_ci = @ptrCast(info.private), + } // should never fail because we are controlling options with the struct param _ = c.lua_getinfo(lua.state, &str, &ar); @@ -1743,6 +1973,8 @@ pub const Lua = struct { unreachable; }; } + if (lang == .lua51 or lang == .luajit) return; + if (options.t) info.is_tail_call = ar.istailcall != 0; if (options.u) { info.num_upvalues = ar.nups; @@ -1756,7 +1988,12 @@ pub const Lua = struct { /// See https://www.lua.org/manual/5.4/manual.html#lua_getlocal pub fn getLocal(lua: *Lua, info: *DebugInfo, n: i32) ![:0]const u8 { var ar: Debug = undefined; - ar.i_ci = @ptrCast(info.private); + + switch (lang) { + .lua51, .luajit => ar.i_ci = info.private, + else => ar.i_ci = @ptrCast(info.private), + } + if (c.lua_getlocal(lua.state, &ar, n)) |name| { return std.mem.span(name); } @@ -1768,7 +2005,12 @@ pub const Lua = struct { pub fn getStack(lua: *Lua, level: i32) !DebugInfo { var ar: Debug = undefined; if (c.lua_getstack(lua.state, level, &ar) == 0) return error.Fail; - return DebugInfo{ .private = ar.i_ci.? }; + return DebugInfo{ + .private = switch (lang) { + .lua51, .luajit => ar.i_ci, + else => ar.i_ci.?, + }, + }; } /// Gets information about the `n`th upvalue of the closure at index `func_index` @@ -1794,7 +2036,12 @@ pub const Lua = struct { /// See https://www.lua.org/manual/5.4/manual.html#lua_setlocal pub fn setLocal(lua: *Lua, info: *DebugInfo, n: i32) ![:0]const u8 { var ar: Debug = undefined; - ar.i_ci = @ptrCast(info.private); + + switch (lang) { + .lua51, .luajit => ar.i_ci = info.private, + else => ar.i_ci = @ptrCast(info.private), + } + if (c.lua_setlocal(lua.state, &ar, n)) |name| { return std.mem.span(name); } @@ -1949,7 +2196,10 @@ pub const Lua = struct { pub fn checkUserdataSlice(lua: *Lua, comptime T: type, arg: i32, name: [:0]const u8) []T { // the returned pointer will not be null const ptr = c.luaL_checkudata(lua.state, arg, name.ptr).?; - const size = lua.rawLen(arg) / @sizeOf(T); + const size = switch (lang) { + .lua51, .luajit => lua.objectLen(arg) / @sizeOf(T), + else => lua.rawLen(arg) / @sizeOf(T), + }; return @as([*]T, @ptrCast(@alignCast(ptr)))[0..size]; } @@ -2054,9 +2304,16 @@ pub const Lua = struct { return c.luaL_len(lua.state, index); } - /// Loads a buffer as a Lua chunk - /// See https://www.lua.org/manual/5.4/manual.html#luaL_loadbufferx - pub fn loadBuffer(lua: *Lua, buf: []const u8, name: [:0]const u8, mode: Mode) !void { + fn loadBuffer51(lua: *Lua, buf: []const u8, name: [:0]const u8) !void { + switch (c.luaL_loadbuffer(lua.state, buf.ptr, buf.len, name.ptr)) { + StatusCode.ok => return, + StatusCode.err_syntax => return error.Syntax, + StatusCode.err_memory => return error.Memory, + else => unreachable, + } + } + + fn loadBuffer52(lua: *Lua, buf: []const u8, name: [:0]const u8, mode: Mode) !void { const mode_str = switch (mode) { .binary => "b", .text => "t", @@ -2070,9 +2327,25 @@ pub const Lua = struct { } } - /// Loads a file as a Lua chunk - /// See https://www.lua.org/manual/5.4/manual.html#luaL_loadfilex - pub fn loadFile(lua: *Lua, file_name: [:0]const u8, mode: Mode) !void { + /// Loads a buffer as a Lua chunk + /// See https://www.lua.org/manual/5.4/manual.html#luaL_loadbufferx + pub const loadBuffer = switch (lang) { + .lua51, .luajit => loadBuffer51, + else => loadBuffer52, + }; + + fn loadFile51(lua: *Lua, file_name: [:0]const u8) !void { + const ret = c.luaL_loadfile(lua.state, file_name.ptr); + switch (ret) { + StatusCode.ok => return, + StatusCode.err_syntax => return error.Syntax, + StatusCode.err_memory => return error.Memory, + err_file => return error.File, + else => unreachable, + } + } + + fn loadFile52(lua: *Lua, file_name: [:0]const u8, mode: Mode) !void { const mode_str = switch (mode) { .binary => "b", .text => "t", @@ -2089,6 +2362,13 @@ pub const Lua = struct { } } + /// Loads a file as a Lua chunk + /// See https://www.lua.org/manual/5.4/manual.html#luaL_loadfilex + pub const loadFile = switch (lang) { + .lua51, .luajit => loadFile51, + else => loadFile52, + }; + /// Loads a string as a Lua chunk /// See https://www.lua.org/manual/5.4/manual.html#luaL_loadstring pub fn loadString(lua: *Lua, str: [:0]const u8) !void { @@ -2198,10 +2478,43 @@ pub const Lua = struct { return if (ret == ref_nil) error.Fail else ret; } + /// Opens a library + /// See https://www.lua.org/manual/5.1/manual.html#luaL_register + pub fn registerFns(lua: *Lua, libname: ?[:0]const u8, funcs: []const FnReg) void { + // translated from the implementation of luaI_openlib so we can use a slice of + // FnReg without requiring a sentinel end value + if (libname) |name| { + _ = c.luaL_findtable(lua.state, registry_index, "_LOADED", 1); + _ = lua.getField(-1, name); + if (!lua.isTable(-1)) { + lua.pop(1); + if (c.luaL_findtable(lua.state, globals_index, name, @intCast(funcs.len))) |_| { + lua.raiseErrorStr("name conflict for module " ++ c.LUA_QS, .{name.ptr}); + } + lua.pushValue(-1); + lua.setField(-3, name); + } + lua.remove(-2); + lua.insert(-1); + } + for (funcs) |f| { + // TODO: handle null functions + lua.pushFunction(f.func.?); + lua.setField(-2, f.name); + } + } + /// If package.loaded[`mod_name`] is not true, calls the function `open_fn` with `mod_name` /// as an argument and sets the call result to package.loaded[`mod_name`] /// See https://www.lua.org/manual/5.4/manual.html#luaL_requiref pub fn requireF(lua: *Lua, mod_name: [:0]const u8, open_fn: CFn, global: bool) void { + if (lang == .lua51 or lang == .luajit) { + lua.pushFunction(open_fn); + lua.pushString(mod_name); + lua.call(1, 0); + return; + } + c.luaL_requiref(lua.state, mod_name.ptr, open_fn, @intFromBool(global)); } @@ -2294,7 +2607,6 @@ pub const Lua = struct { /// See https://www.lua.org/manual/5.4/manual.html#luaL_openlibs pub fn open(lua: *Lua, libs: Libs) void { if (libs.base) lua.requireF("_G", c.luaopen_base, true); - if (libs.coroutine) lua.requireF(c.LUA_COLIBNAME, c.luaopen_coroutine, true); if (libs.package) lua.requireF(c.LUA_LOADLIBNAME, c.luaopen_package, true); if (libs.string) lua.requireF(c.LUA_STRLIBNAME, c.luaopen_string, true); if (libs.table) lua.requireF(c.LUA_TABLIBNAME, c.luaopen_table, true); @@ -2303,7 +2615,9 @@ pub const Lua = struct { if (libs.os) lua.requireF(c.LUA_OSLIBNAME, c.luaopen_os, true); if (libs.debug) lua.requireF(c.LUA_DBLIBNAME, c.luaopen_debug, true); - if (lang != .lua52 and libs.utf8) lua.requireF(c.LUA_UTF8LIBNAME, c.luaopen_utf8, true); + if (lang != .lua51 and lang != .luajit and libs.coroutine) lua.requireF(c.LUA_COLIBNAME, c.luaopen_coroutine, true); + + if ((lang == .lua53 or lang == .lua54) and libs.utf8) lua.requireF(c.LUA_UTF8LIBNAME, c.luaopen_utf8, true); if (lang == .lua52 and libs.bit) lua.requireF(c.LUA_BITLIBNAME, c.luaopen_bit32, true); } @@ -2397,9 +2711,19 @@ pub const Buffer = struct { pub fn addChar(buf: *Buffer, byte: u8) void { // could not be translated by translate-c var lua_buf = &buf.b; - if (lua_buf.n >= lua_buf.size) _ = buf.prepSize(1); - lua_buf.b[lua_buf.n] = byte; - lua_buf.n += 1; + + switch (lang) { + .lua51, .luajit => { + if (lua_buf.p > &lua_buf.buffer[buffer_size - 1]) _ = buf.prep(); + lua_buf.p.* = byte; + lua_buf.p += 1; + }, + else => { + if (lua_buf.n >= lua_buf.size) _ = buf.prepSize(1); + lua_buf.b[lua_buf.n] = byte; + lua_buf.n += 1; + }, + } } /// Adds a copy of the string `str` to the buffer @@ -2420,7 +2744,11 @@ pub const Buffer = struct { // another function translate-c couldn't handle // c.luaL_addsize(&buf.b, length); var lua_buf = &buf.b; - lua_buf.n += length; + + switch (lang) { + .lua51, .luajit => lua_buf.p += length, + else => lua_buf.n += length, + } } /// Adds the zero-terminated string pointed to by `str` to the buffer @@ -2463,7 +2791,7 @@ pub const Buffer = struct { /// Equivalent to prepSize with a buffer size of Buffer.buffer_size /// See https://www.lua.org/manual/5.4/manual.html#luaL_prepbuffer pub fn prep(buf: *Buffer) []u8 { - return buf.prepSize(buffer_size)[0..buffer_size]; + return c.luaL_prepbuffer(&buf.b)[0..buffer_size]; } /// Returns an address to a space of `size` where you can copy a string @@ -2550,7 +2878,10 @@ fn wrapZigHookFn(comptime f: ZigHookFn) CHookFn { var lua: Lua = .{ .state = state.? }; var info: DebugInfo = .{ .current_line = if (ar.?.currentline == -1) null else ar.?.currentline, - .private = @ptrCast(ar.?.i_ci), + .private = switch (lang) { + .lua51, .luajit => ar.?.i_ci, + else => @ptrCast(ar.?.i_ci), + }, }; @call(.always_inline, f, .{ &lua, @as(Event, @enumFromInt(ar.?.event)), &info }); } diff --git a/src/lib51.zig b/src/lib51.zig deleted file mode 100644 index 3f67dd7..0000000 --- a/src/lib51.zig +++ /dev/null @@ -1,1692 +0,0 @@ -//! complete bindings around the Lua C API version 5.1.5 -//! exposes all Lua functionality, with additional Zig helper functions - -const std = @import("std"); - -const c = @cImport({ - @cInclude("lua.h"); - @cInclude("lualib.h"); - @cInclude("lauxlib.h"); - - if (lang == .luajit) @cInclude("luajit.h"); -}); - -const config = @import("config"); -pub const lang = config.lang; - -const Allocator = std.mem.Allocator; - -// Types -// -// Lua constants and types are declared below in alphabetical order -// For constants that have a logical grouping (like Operators), Zig enums are used for type safety - -/// The type of function that Lua uses for all internal allocations and frees -/// `data` is an opaque pointer to any data (the allocator), `ptr` is a pointer to the block being alloced/realloced/freed -/// `osize` is the original size or a code, and `nsize` is the new size -/// -/// See https://www.lua.org/manual/5.1/manual.html#lua_Alloc for more details -pub const AllocFn = *const fn (data: ?*anyopaque, ptr: ?*anyopaque, osize: usize, nsize: usize) callconv(.C) ?*anyopaque; - -/// Type for C functions -/// See https://www.lua.org/manual/5.1/manual.html#lua_CFunction for the protocol -pub const CFn = *const fn (state: ?*LuaState) callconv(.C) c_int; - -/// The internal Lua debug structure -/// See https://www.lua.org/manual/5.1/manual.html#lua_Debug -const Debug = c.lua_Debug; - -/// The Lua debug interface structure -pub const DebugInfo = struct { - source: [:0]const u8 = undefined, - src_len: usize = 0, - short_src: [c.LUA_IDSIZE:0]u8 = undefined, - - name: ?[:0]const u8 = undefined, - name_what: NameType = undefined, - what: FnType = undefined, - - current_line: ?i32 = null, - first_line_defined: ?i32 = null, - last_line_defined: ?i32 = null, - - is_vararg: bool = false, - - private: c_int = undefined, - - pub const NameType = enum { global, local, method, field, upvalue, other }; - - pub const FnType = enum { lua, c, main }; - - pub const Options = packed struct { - @">": bool = false, - f: bool = false, - l: bool = false, - n: bool = false, - S: bool = false, - u: bool = false, - L: bool = false, - - fn toString(options: Options) [10:0]u8 { - var str = [_:0]u8{0} ** 10; - var index: u8 = 0; - - inline for (std.meta.fields(Options)) |field| { - if (@field(options, field.name)) { - str[index] = field.name[0]; - index += 1; - } - } - while (index < str.len) : (index += 1) str[index] = 0; - - return str; - } - }; -}; - -/// The superset of all errors returned from ziglua -pub const Error = error{ - /// A generic failure (used when a function can only fail in one way) - Fail, - /// A runtime error - Runtime, - /// A syntax error during precompilation - Syntax, - /// A memory allocation error - Memory, - /// An error while running the message handler - MsgHandler, - /// A file-releated error - File, -}; - -/// The type of event that triggers a hook -pub const Event = enum(u3) { - call = c.LUA_HOOKCALL, - ret = c.LUA_HOOKRET, - line = c.LUA_HOOKLINE, - count = c.LUA_HOOKCOUNT, -}; - -/// Type for arrays of functions to be registered -pub const FnReg = struct { - name: [:0]const u8, - func: CFn, -}; - -/// The index of the global environment table -pub const globals_index = c.LUA_GLOBALSINDEX; - -/// Type for debugging hook functions -/// See https://www.lua.org/manual/5.1/manual.html#lua_Hook -pub const CHookFn = *const fn (state: ?*LuaState, ar: ?*Debug) callconv(.C) void; - -/// Specifies on which events the hook will be called -pub const HookMask = packed struct { - call: bool = false, - ret: bool = false, - line: bool = false, - count: bool = false, - - /// Converts a HookMask to an integer bitmask - pub fn toInt(mask: HookMask) i32 { - var bitmask: i32 = 0; - if (mask.call) bitmask |= mask_call; - if (mask.ret) bitmask |= mask_ret; - if (mask.line) bitmask |= mask_line; - if (mask.count) bitmask |= mask_count; - return bitmask; - } - - /// Converts an integer bitmask into a HookMask - pub fn fromInt(mask: i32) HookMask { - return .{ - .call = (mask & mask_call) != 0, - .ret = (mask & mask_ret) != 0, - .line = (mask & mask_line) != 0, - .count = (mask & mask_count) != 0, - }; - } -}; - -/// Hook event codes -pub const hook_call = c.LUA_HOOKCALL; -pub const hook_count = c.LUA_HOOKCOUNT; -pub const hook_line = c.LUA_HOOKLINE; -pub const hook_ret = c.LUA_HOOKRET; -pub const hook_tail_call = c.LUA_HOOKTAILCALL; - -/// Type of integers in Lua (typically a ptrdiff_t) -pub const Integer = c.lua_Integer; - -/// Bitflag for the Lua standard libraries -pub const Libs = packed struct { - base: bool = false, - package: bool = false, - string: bool = false, - utf8: bool = false, - table: bool = false, - math: bool = false, - io: bool = false, - os: bool = false, - debug: bool = false, -}; - -/// The type of the opaque structure that points to a thread and the state of a Lua interpreter -pub const LuaState = c.lua_State; - -/// Lua types -/// Must be a signed integer because LuaType.none is -1 -pub const LuaType = enum(i5) { - none = c.LUA_TNONE, - nil = c.LUA_TNIL, - boolean = c.LUA_TBOOLEAN, - light_userdata = c.LUA_TLIGHTUSERDATA, - number = c.LUA_TNUMBER, - string = c.LUA_TSTRING, - table = c.LUA_TTABLE, - function = c.LUA_TFUNCTION, - userdata = c.LUA_TUSERDATA, - thread = c.LUA_TTHREAD, -}; - -/// Event masks -pub const mask_call = c.LUA_MASKCALL; -pub const mask_count = c.LUA_MASKCOUNT; -pub const mask_line = c.LUA_MASKLINE; -pub const mask_ret = c.LUA_MASKRET; - -/// The maximum integer value that `Integer` can store -pub const max_integer = c.LUA_MAXINTEGER; - -/// The minimum Lua stack available to a function -pub const min_stack = c.LUA_MINSTACK; - -/// Option for multiple returns in `Lua.protectedCall()` and `Lua.call()` -pub const mult_return = c.LUA_MULTRET; - -/// Type of floats in Lua (typically an f64) -pub const Number = c.lua_Number; - -/// The type of the reader function used by `Lua.load()` -pub const CReaderFn = *const fn (state: ?*LuaState, data: ?*anyopaque, size: [*c]usize) callconv(.C) [*c]const u8; - -/// The possible status of a call to `Lua.resumeThread` -pub const ResumeStatus = enum(u1) { - ok = StatusCode.ok, - yield = StatusCode.yield, -}; - -/// Reference constants -pub const ref_nil = c.LUA_REFNIL; -pub const ref_no = c.LUA_NOREF; - -/// Index of the regsitry in the stack (pseudo-index) -pub const registry_index = c.LUA_REGISTRYINDEX; - -/// Index of the main thread in the registry -pub const ridx_mainthread = c.LUA_RIDX_MAINTHREAD; - -/// Status that a thread can be in -/// Usually errors are reported by a Zig error rather than a status enum value -pub const Status = enum(u3) { - ok = StatusCode.ok, - yield = StatusCode.yield, - err_runtime = StatusCode.err_runtime, - err_syntax = StatusCode.err_syntax, - err_memory = StatusCode.err_memory, - err_error = StatusCode.err_error, -}; - -/// Status codes -/// Not public, because typically Status.ok is returned from a function implicitly; -/// Any function that returns an error usually returns a Zig error, and a void return -/// is an implicit Status.ok. -/// In the rare case that the status code is required from a function, an enum is -/// used for that specific function's return type -const StatusCode = struct { - // Lua 5.1 doesn't have an explicit variable, but 0 represents OK - pub const ok = 0; - pub const yield = c.LUA_YIELD; - pub const err_runtime = c.LUA_ERRRUN; - pub const err_syntax = c.LUA_ERRSYNTAX; - pub const err_memory = c.LUA_ERRMEM; - pub const err_error = c.LUA_ERRERR; -}; - -// Only used in loadFileX, so no need to group with Status -pub const err_file = c.LUA_ERRFILE; - -/// The standard representation for file handles used by the standard IO library -pub const Stream = c.luaL_Stream; - -/// The type of the writer function used by `Lua.dump()` -pub const CWriterFn = *const fn (state: ?*LuaState, buf: ?*const anyopaque, size: usize, data: ?*anyopaque) callconv(.C) c_int; - -/// A Zig wrapper around the Lua C API -/// Represents a Lua state or thread and contains the entire state of the Lua interpreter -pub const Lua = struct { - state: *LuaState, - - const alignment = @alignOf(std.c.max_align_t); - - /// Allows Lua to allocate memory using a Zig allocator passed in via data. - /// See https://www.lua.org/manual/5.1/manual.html#lua_Alloc for more details - fn alloc(data: ?*anyopaque, ptr: ?*anyopaque, osize: usize, nsize: usize) callconv(.C) ?*align(alignment) anyopaque { - // just like malloc() returns a pointer "which is suitably aligned for any built-in type", - // the memory allocated by this function should also be aligned for any type that Lua may - // desire to allocate. use the largest alignment for the target - const allocator_ptr = opaqueCast(Allocator, data.?); - - if (@as(?[*]align(alignment) u8, @ptrCast(@alignCast(ptr)))) |prev_ptr| { - const prev_slice = prev_ptr[0..osize]; - - // when nsize is zero the allocator must behave like free and return null - if (nsize == 0) { - allocator_ptr.free(prev_slice); - return null; - } - - // when nsize is not zero the allocator must behave like realloc - const new_ptr = allocator_ptr.realloc(prev_slice, nsize) catch return null; - return new_ptr.ptr; - } else if (nsize == 0) { - return null; - } else { - // ptr is null, allocate a new block of memory - const new_ptr = allocator_ptr.alignedAlloc(u8, alignment, nsize) catch return null; - return new_ptr.ptr; - } - } - - /// Initialize a Lua state with the given allocator - /// See https://www.lua.org/manual/5.1/manual.html#lua_newstate - pub fn init(allocator_ptr: *const Allocator) !Lua { - // @constCast() is safe here because Lua does not mutate the pointer internally - const state = c.lua_newstate(alloc, @constCast(allocator_ptr)) orelse return error.Memory; - return Lua{ .state = state }; - } - - /// Deinitialize a Lua state and free all memory - pub fn deinit(lua: *Lua) void { - lua.close(); - } - - /// Returns the std.mem.Allocator used to initialize this Lua state - /// - /// This function is not safe to use on Lua states created without a Zig allocator. - /// If the user data passed to Lua was null, this function will panic. Otherwise use - /// of the returned Allocator is undefined and will likely cause a segfault. - pub fn allocator(lua: *Lua) Allocator { - var data: ?*Allocator = undefined; - _ = lua.getAllocFn(@ptrCast(&data)); - - if (data) |allocator_ptr| { - // Although the Allocator is passed to Lua as a pointer, return a - // copy to make use more convenient. - return allocator_ptr.*; - } - - @panic("Lua.allocator() invalid on Lua states created without a Zig allocator"); - } - - // Library functions - // - // Library functions are included in alphabetical order. - // Each is kept similar to the original C API function while also making it easy to use from Zig - - /// Sets a new panic function and returns the old one - /// See https://www.lua.org/manual/5.1/manual.html#lua_atpanic - pub fn atPanic(lua: *Lua, panic_fn: CFn) ?CFn { - return c.lua_atpanic(lua.state, panic_fn); - } - - /// Calls a function (or any callable value) - /// First push the function to be called onto the stack. Then push any arguments onto the stack. - /// Then call this function. All arguments and the function value are popped, and any results - /// are pushed onto the stack. - /// See https://www.lua.org/manual/5.1/manual.html#lua_call - pub fn call(lua: *Lua, num_args: i32, num_results: i32) void { - c.lua_call(lua.state, num_args, num_results); - } - - /// Ensures that the stack has space for at least n extra arguments - /// Returns an error if more stack space cannot be allocated - /// Never shrinks the stack - /// See https://www.lua.org/manual/5.1/manual.html#lua_checkstack - pub fn checkStack(lua: *Lua, n: i32) !void { - if (c.lua_checkstack(lua.state, n) == 0) return error.Fail; - } - - /// Release all Lua objects in the state and free all dynamic memory - /// See https://www.lua.org/manual/5.1/manual.html#lua_close - pub fn close(lua: *Lua) void { - c.lua_close(lua.state); - } - - /// Concatenates the n values at the top of the stack, pops them, and leaves the result at the top - /// If the number of values is 1, the result is a single value on the stack (nothing changes) - /// If the number of values is 0, the result is the empty string - /// See https://www.lua.org/manual/5.1/manual.html#lua_concat - pub fn concat(lua: *Lua, n: i32) void { - c.lua_concat(lua.state, n); - } - - /// Calls the C function c_fn in protected mode. The function starts with only one element on its - /// stack, the userdata given to this function. - /// See https://www.lua.org/manual/5.1/manual.html#lua_cpcall - pub fn cProtectedCall(lua: *Lua, c_fn: CFn, userdata: *anyopaque) !void { - const ret = c.lua_cpcall(lua.state, c_fn, userdata); - switch (ret) { - StatusCode.ok => return, - StatusCode.err_runtime => return error.Runtime, - StatusCode.err_memory => return error.Memory, - StatusCode.err_error => return error.MsgHandler, - else => unreachable, - } - } - - /// Creates a new empty table and pushes onto the stack - /// num_arr is a hint for how many elements the table will have as a sequence - /// num_rec is a hint for how many other elements the table will have - /// Lua may preallocate memory for the table based on the hints - /// See https://www.lua.org/manual/5.1/manual.html#lua_createtable - pub fn createTable(lua: *Lua, num_arr: i32, num_rec: i32) void { - c.lua_createtable(lua.state, num_arr, num_rec); - } - - /// Dumps a function as a binary chunk - /// Data is a pointer passed to the writer function - /// Returns an error if writing was unsuccessful - /// See https://www.lua.org/manual/5.1/manual.html#lua_dump - pub fn dump(lua: *Lua, writer: CWriterFn, data: *anyopaque) !void { - if (c.lua_dump(lua.state, writer, data) != 0) return error.Fail; - } - - /// Returns true if the two values at the indexes are equal following the semantics of the - /// Lua == operator. - /// See https://www.lua.org/manual/5.1/manual.html#lua_equal - pub fn equal(lua: *Lua, index1: i32, index2: i32) bool { - return c.lua_equal(lua.state, index1, index2) == 1; - } - - /// Raises a Lua error using the value at the top of the stack as the error object - /// Does a longjump and therefore never returns - /// See https://www.lua.org/manual/5.1/manual.html#lua_error - pub fn raiseError(lua: *Lua) noreturn { - _ = c.lua_error(lua.state); - unreachable; - } - - /// Perform a full garbage-collection cycle - /// See https://www.lua.org/manual/5.1/manual.html#lua_gc - pub fn gcCollect(lua: *Lua) void { - _ = c.lua_gc(lua.state, c.LUA_GCCOLLECT, 0); - } - - /// Stops the garbage collector - /// See https://www.lua.org/manual/5.1/manual.html#lua_gc - pub fn gcStop(lua: *Lua) void { - _ = c.lua_gc(lua.state, c.LUA_GCSTOP, 0); - } - - /// Restarts the garbage collector - /// See https://www.lua.org/manual/5.1/manual.html#lua_gc - pub fn gcRestart(lua: *Lua) void { - _ = c.lua_gc(lua.state, c.LUA_GCRESTART, 0); - } - - /// Performs an incremental step of garbage collection corresponding to the allocation of step_size Kbytes - /// See https://www.lua.org/manual/5.1/manual.html#lua_gc - pub fn gcStep(lua: *Lua) void { - _ = c.lua_gc(lua.state, c.LUA_GCSTEP, 0); - } - - /// Returns the current amount of memory (in Kbytes) in use by Lua - /// See https://www.lua.org/manual/5.1/manual.html#lua_gc - pub fn gcCount(lua: *Lua) i32 { - return c.lua_gc(lua.state, c.LUA_GCCOUNT, 0); - } - - /// Returns the remainder of dividing the current amount of bytes of memory in use by Lua by 1024 - /// See https://www.lua.org/manual/5.1/manual.html#lua_gc - pub fn gcCountB(lua: *Lua) i32 { - return c.lua_gc(lua.state, c.LUA_GCCOUNTB, 0); - } - - /// Sets `pause` as the new value for the pause of the collector - /// Returns the previous value of the pause - /// See https://www.lua.org/manual/5.1/manual.html#lua_gc - pub fn gcSetPause(lua: *Lua, pause: i32) i32 { - return c.lua_gc(lua.state, c.LUA_GCSETPAUSE, pause); - } - - /// Sets `multiplier` as the new value for the step multiplier of the collector - /// Returns the previous value of the step multiplier - /// See https://www.lua.org/manual/5.1/manual.html#lua_gc - pub fn gcSetStepMul(lua: *Lua, multiplier: i32) i32 { - return c.lua_gc(lua.state, c.LUA_GCSETSTEPMUL, multiplier); - } - - /// Returns the memory allocation function of a given state - /// If data is not null, it is set to the opaque pointer given when the allocator function was set - /// See https://www.lua.org/manual/5.1/manual.html#lua_getallocf - pub fn getAllocFn(lua: *Lua, data: ?**anyopaque) AllocFn { - // Assert cannot be null because it is impossible (and not useful) to pass null - // to the functions that set the allocator (setallocf and newstate) - return c.lua_getallocf(lua.state, @ptrCast(data)).?; - } - - /// Pushes onto the stack the environment table of the value at the given index. - /// See https://www.lua.org/manual/5.1/manual.html#lua_getfenv - pub fn getFnEnvironment(lua: *Lua, index: i32) void { - c.lua_getfenv(lua.state, index); - } - - /// Pushes onto the stack the value t[key] where t is the value at the given index - /// See https://www.lua.org/manual/5.1/manual.html#lua_getfield - pub fn getField(lua: *Lua, index: i32, key: [:0]const u8) void { - c.lua_getfield(lua.state, index, key.ptr); - } - - /// Pushes onto the stack the value of the global name - /// See https://www.lua.org/manual/5.1/manual.html#lua_getglobal - pub fn getGlobal(lua: *Lua, name: [:0]const u8) void { - c.lua_getglobal(lua.state, name.ptr); - } - - /// If the value at the given index has a metatable, the function pushes that metatable onto the stack - /// Otherwise an error is returned - /// See https://www.lua.org/manual/5.1/manual.html#lua_getmetatable - pub fn getMetatable(lua: *Lua, index: i32) !void { - if (c.lua_getmetatable(lua.state, index) == 0) return error.Fail; - } - - /// Pushes onto the stack the value t[k] where t is the value at the given index and k is the value on the top of the stack - /// See https://www.lua.org/manual/5.1/manual.html#lua_gettable - pub fn getTable(lua: *Lua, index: i32) void { - c.lua_gettable(lua.state, index); - } - - /// Returns the index of the top element in the stack - /// Because indices start at 1, the result is also equal to the number of elements in the stack - /// See https://www.lua.org/manual/5.1/manual.html#lua_gettop - pub fn getTop(lua: *Lua) i32 { - return c.lua_gettop(lua.state); - } - - /// Moves the top element into the given valid `index` shifting up any elements to make room - /// See https://www.lua.org/manual/5.1/manual.html#lua_insert - pub fn insert(lua: *Lua, index: i32) void { - // translate-c cannot translate this macro correctly - c.lua_insert(lua.state, index); - } - - /// Returns true if the value at the given index is a boolean - /// See https://www.lua.org/manual/5.1/manual.html#lua_isboolean - pub fn isBoolean(lua: *Lua, index: i32) bool { - return c.lua_isboolean(lua.state, index); - } - - /// Returns true if the value at the given index is a CFn - /// See https://www.lua.org/manual/5.1/manual.html#lua_iscfunction - pub fn isCFunction(lua: *Lua, index: i32) bool { - return c.lua_iscfunction(lua.state, index) != 0; - } - - /// Returns true if the value at the given index is a function (C or Lua) - /// See https://www.lua.org/manual/5.1/manual.html#lua_isfunction - pub fn isFunction(lua: *Lua, index: i32) bool { - return c.lua_isfunction(lua.state, index); - } - - /// Returns true if the value at the given index is a light userdata - /// See https://www.lua.org/manual/5.1/manual.html#lua_islightuserdata - pub fn isLightUserdata(lua: *Lua, index: i32) bool { - return c.lua_islightuserdata(lua.state, index); - } - - /// Returns true if the value at the given index is nil - /// See https://www.lua.org/manual/5.1/manual.html#lua_isnil - pub fn isNil(lua: *Lua, index: i32) bool { - return c.lua_isnil(lua.state, index); - } - - /// Returns true if the given index is not valid - /// See https://www.lua.org/manual/5.1/manual.html#lua_isnone - pub fn isNone(lua: *Lua, index: i32) bool { - return c.lua_isnone(lua.state, index); - } - - /// Returns true if the given index is not valid or if the value at the index is nil - /// See https://www.lua.org/manual/5.1/manual.html#lua_isnoneornil - pub fn isNoneOrNil(lua: *Lua, index: i32) bool { - return c.lua_isnoneornil(lua.state, index); - } - - /// Returns true if the value at the given index is a number - /// See https://www.lua.org/manual/5.1/manual.html#lua_isnumber - pub fn isNumber(lua: *Lua, index: i32) bool { - return c.lua_isnumber(lua.state, index) != 0; - } - - /// Returns true if the value at the given index is a string - /// See https://www.lua.org/manual/5.1/manual.html#lua_isstring - pub fn isString(lua: *Lua, index: i32) bool { - return c.lua_isstring(lua.state, index) != 0; - } - - /// Returns true if the value at the given index is a table - /// See https://www.lua.org/manual/5.1/manual.html#lua_istable - pub fn isTable(lua: *Lua, index: i32) bool { - return c.lua_istable(lua.state, index); - } - - /// Returns true if the value at the given index is a thread - /// See https://www.lua.org/manual/5.1/manual.html#lua_isthread - pub fn isThread(lua: *Lua, index: i32) bool { - return c.lua_isthread(lua.state, index); - } - - /// Returns true if the value at the given index is a userdata (full or light) - /// See https://www.lua.org/manual/5.1/manual.html#lua_isuserdata - pub fn isUserdata(lua: *Lua, index: i32) bool { - return c.lua_isuserdata(lua.state, index) != 0; - } - - /// Returns true if the value at index1 is smaller than the value at index2, following the - /// semantics of the Lua < operator. - /// See https://www.lua.org/manual/5.4/manual.html#lua_lessthan - pub fn lessThan(lua: *Lua, index1: i32, index2: i32) bool { - return c.lua_lessthan(lua.state, index1, index2) == 1; - } - - /// Loads a Lua chunk without running it - /// If there are no errors, pushes the compiled chunk on the top of the stack as a function - /// See https://www.lua.org/manual/5.1/manual.html#lua_load - pub fn load(lua: *Lua, reader: CReaderFn, data: *anyopaque, chunk_name: [:0]const u8) !void { - const ret = c.lua_load(lua.state, reader, data, chunk_name.ptr); - switch (ret) { - StatusCode.ok => return, - StatusCode.err_syntax => return error.Syntax, - StatusCode.err_memory => return error.Memory, - else => unreachable, - } - } - - /// Creates a new independent state and returns its main thread - /// See https://www.lua.org/manual/5.1/manual.html#lua_newstate - pub fn newState(alloc_fn: AllocFn, data: ?*const anyopaque) !Lua { - const state = c.lua_newstate(alloc_fn, @constCast(data)) orelse return error.Memory; - return Lua{ .state = state }; - } - - /// Creates a new empty table and pushes it onto the stack - /// Equivalent to createTable(0, 0) - /// See https://www.lua.org/manual/5.1/manual.html#lua_newtable - pub fn newTable(lua: *Lua) void { - c.lua_newtable(lua.state); - } - - /// Creates a new thread, pushes it on the stack, and returns a Lua state that represents the new thread - /// The new thread shares the global environment but has a separate execution stack - /// See https://www.lua.org/manual/5.1/manual.html#lua_newthread - pub fn newThread(lua: *Lua) Lua { - const state = c.lua_newthread(lua.state).?; - return .{ .state = state }; - } - - /// This function allocates a new userdata of the given type. - /// Returns a pointer to the Lua-owned data - /// See https://www.lua.org/manual/5.1/manual.html#lua_newuserdata - pub fn newUserdata(lua: *Lua, comptime T: type) *T { - // safe to .? because this function throws a Lua error on out of memory - // so the returned pointer should never be null - const ptr = c.lua_newuserdata(lua.state, @sizeOf(T)).?; - return opaqueCast(T, ptr); - } - - /// This function creates and pushes a slice of full userdata onto the stack. - /// Returns a slice to the Lua-owned data. - /// See https://www.lua.org/manual/5.1/manual.html#lua_newuserdata - pub fn newUserdataSlice(lua: *Lua, comptime T: type, size: usize) []T { - // safe to .? because this function throws a Lua error on out of memory - const ptr = c.lua_newuserdata(lua.state, @sizeOf(T) * size).?; - return @as([*]T, @ptrCast(@alignCast(ptr)))[0..size]; - } - - /// Pops a key from the stack, and pushes a key-value pair from the table at the given index. - /// See https://www.lua.org/manual/5.1/manual.html#lua_next - pub fn next(lua: *Lua, index: i32) bool { - return c.lua_next(lua.state, index) != 0; - } - - /// Returns the length of the value at the given index - /// See https://www.lua.org/manual/5.1/manual.html#lua_objlen - pub fn objectLen(lua: *Lua, index: i32) usize { - return c.lua_objlen(lua.state, index); - } - - /// Calls a function (or callable object) in protected mode - /// See https://www.lua.org/manual/5.1/manual.html#lua_pcall - pub fn protectedCall(lua: *Lua, num_args: i32, num_results: i32, err_func: i32) !void { - // The translate-c version of lua_pcall does not type-check so we must rewrite it - // (macros don't always translate well with translate-c) - const ret = c.lua_pcall(lua.state, num_args, num_results, err_func); - switch (ret) { - StatusCode.ok => return, - StatusCode.err_runtime => return error.Runtime, - StatusCode.err_memory => return error.Memory, - StatusCode.err_error => return error.MsgHandler, - else => unreachable, - } - } - - /// Pops `n` elements from the top of the stack - /// See https://www.lua.org/manual/5.1/manual.html#lua_pop - pub fn pop(lua: *Lua, n: i32) void { - lua.setTop(-n - 1); - } - - /// Pushes a boolean value with value `b` onto the stack - /// See https://www.lua.org/manual/5.1/manual.html#lua_pushboolean - pub fn pushBoolean(lua: *Lua, b: bool) void { - c.lua_pushboolean(lua.state, @intFromBool(b)); - } - - /// Pushes a new Closure onto the stack - /// `n` tells how many upvalues this function will have - /// See https://www.lua.org/manual/5.1/manual.html#lua_pushcclosure - pub fn pushClosure(lua: *Lua, c_fn: CFn, n: i32) void { - c.lua_pushcclosure(lua.state, c_fn, n); - } - - /// Pushes a function onto the stack. - /// Equivalent to pushClosure with no upvalues - /// See https://www.lua.org/manual/5.1/manual.html#lua_pushfunction - pub fn pushFunction(lua: *Lua, c_fn: CFn) void { - lua.pushClosure(c_fn, 0); - } - - /// Push a formatted string onto the stack and return a pointer to the string - /// See https://www.lua.org/manual/5.1/manual.html#lua_pushfstring - pub fn pushFString(lua: *Lua, fmt: [:0]const u8, args: anytype) [*:0]const u8 { - return @call(.auto, c.lua_pushfstring, .{ lua.state, fmt.ptr } ++ args); - } - - /// Pushes an integer with value `n` onto the stack - /// See https://www.lua.org/manual/5.1/manual.html#lua_pushinteger - pub fn pushInteger(lua: *Lua, n: Integer) void { - c.lua_pushinteger(lua.state, n); - } - - /// Pushes a light userdata onto the stack - /// See https://www.lua.org/manual/5.1/manual.html#lua_pushlightuserdata - pub fn pushLightUserdata(lua: *Lua, ptr: *anyopaque) void { - c.lua_pushlightuserdata(lua.state, ptr); - } - - /// Pushes the bytes onto the stack - /// See https://www.lua.org/manual/5.1/manual.html#lua_pushlstring - pub fn pushBytes(lua: *Lua, bytes: []const u8) void { - c.lua_pushlstring(lua.state, bytes.ptr, bytes.len); - } - - /// Pushes a nil value onto the stack - /// See https://www.lua.org/manual/5.1/manual.html#lua_pushnil - pub fn pushNil(lua: *Lua) void { - c.lua_pushnil(lua.state); - } - - /// Pushes a float with value `n` onto the stack - /// See https://www.lua.org/manual/5.1/manual.html#lua_pushnumber - pub fn pushNumber(lua: *Lua, n: Number) void { - c.lua_pushnumber(lua.state, n); - } - - /// Pushes a zero-terminated string onto the stack - /// Lua makes a copy of the string so `str` may be freed immediately after return - /// See https://www.lua.org/manual/5.1/manual.html#lua_pushstring - pub fn pushString(lua: *Lua, str: [:0]const u8) void { - c.lua_pushstring(lua.state, str.ptr); - } - - /// Pushes this thread onto the stack - /// Returns true if this thread is the main thread of its state - /// See https://www.lua.org/manual/5.1/manual.html#lua_pushthread - pub fn pushThread(lua: *Lua) bool { - return c.lua_pushthread(lua.state) != 0; - } - - /// Pushes a copy of the element at the given index onto the stack - /// See https://www.lua.org/manual/5.1/manual.html#lua_pushvalue - pub fn pushValue(lua: *Lua, index: i32) void { - c.lua_pushvalue(lua.state, index); - } - - /// Returns true if the two values in indices `index1` and `index2` are primitively equal - /// Bypasses __eq metamethods - /// Returns false if not equal, or if any index is invalid - /// See https://www.lua.org/manual/5.1/manual.html#lua_rawequal - pub fn rawEqual(lua: *Lua, index1: i32, index2: i32) bool { - return c.lua_rawequal(lua.state, index1, index2) != 0; - } - - /// Similar to `Lua.getTable()` but does a raw access (without metamethods) - /// See https://www.lua.org/manual/5.1/manual.html#lua_rawget - pub fn rawGetTable(lua: *Lua, index: i32) void { - c.lua_rawget(lua.state, index); - } - - /// Pushes onto the stack the value t[n], where `t` is the table at the given `index` - /// Returns the `LuaType` of the pushed value - /// See https://www.lua.org/manual/5.1/manual.html#lua_rawgeti - pub fn rawGetIndex(lua: *Lua, index: i32, n: i32) void { - c.lua_rawgeti(lua.state, index, n); - } - - /// Similar to `Lua.setTable()` but does a raw assignment (without metamethods) - /// See https://www.lua.org/manual/5.1/manual.html#lua_rawset - pub fn rawSetTable(lua: *Lua, index: i32) void { - c.lua_rawset(lua.state, index); - } - - /// Does the equivalent of t[`i`] = v where t is the table at the given `index` - /// and v is the value at the top of the stack - /// Pops the value from the stack. Does not use __newindex metavalue - /// See https://www.lua.org/manual/5.1/manual.html#lua_rawseti - pub fn rawSetIndex(lua: *Lua, index: i32, i: i32) void { - c.lua_rawseti(lua.state, index, i); - } - - /// Sets the C function f as the new value of global name - /// See https://www.lua.org/manual/5.1/manual.html#lua_register - pub fn register(lua: *Lua, name: [:0]const u8, c_fn: CFn) void { - // translate-c failure - // c.lua_register(lua.state, name, c_fn); - lua.pushFunction(c_fn); - lua.setGlobal(name); - } - - /// Removes the element at the given valid `index` shifting down elements to fill the gap - /// See https://www.lua.org/manual/5.1/manual.html#lua_remove - pub fn remove(lua: *Lua, index: i32) void { - c.lua_remove(lua.state, index); - } - - /// Moves the top element into the given valid `index` without shifting any elements, - /// then pops the top element - /// See https://www.lua.org/manual/5.1/manual.html#lua_replace - pub fn replace(lua: *Lua, index: i32) void { - c.lua_replace(lua.state, index); - } - - /// Starts and resumes a coroutine in the thread - /// See https://www.lua.org/manual/5.1/manual.html#lua_resume - pub fn resumeThread(lua: *Lua, num_args: i32) !ResumeStatus { - const thread_status = c.lua_resume(lua.state, num_args); - switch (thread_status) { - StatusCode.err_runtime => return error.Runtime, - StatusCode.err_memory => return error.Memory, - StatusCode.err_error => return error.MsgHandler, - else => return @enumFromInt(thread_status), - } - } - - /// Changes the allocator function of a given state to `alloc_fn` with userdata `data` - /// See https://www.lua.org/manual/5.1/manual.html#lua_setallocf - pub fn setAllocF(lua: *Lua, alloc_fn: AllocFn, data: ?*anyopaque) void { - c.lua_setallocf(lua.state, alloc_fn, data); - } - - /// Pops a table from the stack and sets it as the new environment for the value at the - /// given index. Returns an error if the value at that index is not a function or thread or userdata. - /// See https://www.lua.org/manual/5.1/manual.html#lua_setfenv - pub fn setFnEnvironment(lua: *Lua, index: i32) !void { - if (c.lua_setfenv(lua.state, index) == 0) return error.Fail; - } - - /// Does the equivalent to t[`k`] = v where t is the value at the given `index` - /// and v is the value on the top of the stack - /// See https://www.lua.org/manual/5.1/manual.html#lua_setfield - pub fn setField(lua: *Lua, index: i32, k: [:0]const u8) void { - c.lua_setfield(lua.state, index, k.ptr); - } - - /// Pops a value from the stack and sets it as the new value of global `name` - /// See https://www.lua.org/manual/5.1/manual.html#lua_setglobal - pub fn setGlobal(lua: *Lua, name: [:0]const u8) void { - c.lua_setglobal(lua.state, name.ptr); - } - - /// Pops a table or nil from the stack and sets that value as the new metatable for the - /// value at the given `index` - /// See https://www.lua.org/manual/5.1/manual.html#lua_setmetatable - pub fn setMetatable(lua: *Lua, index: i32) void { - // lua_setmetatable always returns 1 so is safe to ignore - _ = c.lua_setmetatable(lua.state, index); - } - - /// Does the equivalent to t[k] = v, where t is the value at the given `index` - /// v is the value on the top of the stack, and k is the value just below the top - /// See https://www.lua.org/manual/5.1/manual.html#lua_settable - pub fn setTable(lua: *Lua, index: i32) void { - c.lua_settable(lua.state, index); - } - - /// Sets the top of the stack to `index` - /// If the new top is greater than the old, new elements are filled with nil - /// If `index` is 0 all stack elements are removed - /// See https://www.lua.org/manual/5.1/manual.html#lua_settop - pub fn setTop(lua: *Lua, index: i32) void { - c.lua_settop(lua.state, index); - } - - /// Returns the status of this thread - /// See https://www.lua.org/manual/5.1/manual.html#lua_status - pub fn status(lua: *Lua) Status { - return @enumFromInt(c.lua_status(lua.state)); - } - - /// Converts the Lua value at the given `index` into a boolean - /// The Lua value at the index will be considered true unless it is false or nil - /// See https://www.lua.org/manual/5.1/manual.html#lua_toboolean - pub fn toBoolean(lua: *Lua, index: i32) bool { - return c.lua_toboolean(lua.state, index) != 0; - } - - /// Converts a value at the given `index` into a CFn - /// Returns an error if the value is not a CFn - /// See https://www.lua.org/manual/5.1/manual.html#lua_tocfunction - pub fn toCFunction(lua: *Lua, index: i32) !CFn { - return c.lua_tocfunction(lua.state, index) orelse return error.Fail; - } - - /// Converts the Lua value at the given `index` to a signed integer - /// The Lua value must be an integer, or a number, or a string convertible to an integer otherwise toInteger returns 0 - /// See https://www.lua.org/manual/5.1/manual.html#lua_tointeger - pub fn toInteger(lua: *Lua, index: i32) Integer { - return c.lua_tointeger(lua.state, index); - } - - /// Returns a slice of bytes at the given index - /// If the value is not a string or number, returns an error - /// If the value was a number the actual value in the stack will be changed to a string - /// See https://www.lua.org/manual/5.1/manual.html#lua_tolstring - pub fn toBytes(lua: *Lua, index: i32) ![:0]const u8 { - var length: usize = undefined; - if (c.lua_tolstring(lua.state, index, &length)) |ptr| return ptr[0..length :0]; - return error.Fail; - } - - /// Converts the Lua value at the given `index` to a float - /// The Lua value must be a number or a string convertible to a number otherwise toNumber returns 0 - /// See https://www.lua.org/manual/5.1/manual.html#lua_tonumber - pub fn toNumber(lua: *Lua, index: i32) Number { - return c.lua_tonumber(lua.state, index); - } - - /// Converts the value at the given `index` to an opaque pointer - /// See https://www.lua.org/manual/5.1/manual.html#lua_topointer - pub fn toPointer(lua: *Lua, index: i32) !*const anyopaque { - if (c.lua_topointer(lua.state, index)) |ptr| return ptr; - return error.Fail; - } - - /// Converts the Lua value at the given `index` to a zero-terminated many-itemed-pointer (string) - /// Returns an error if the conversion failed - /// If the value was a number the actual value in the stack will be changed to a string - /// See https://www.lua.org/manual/5.1/manual.html#lua_tolstring - pub fn toString(lua: *Lua, index: i32) ![*:0]const u8 { - var length: usize = undefined; - if (c.lua_tolstring(lua.state, index, &length)) |str| return str; - return error.Fail; - } - - /// Converts the value at the given `index` to a Lua thread (wrapped with a `Lua` struct) - /// The thread does _not_ contain an allocator because it is not the main thread and should therefore not be used with `deinit()` - /// Returns an error if the value is not a thread - /// See https://www.lua.org/manual/5.1/manual.html#lua_tothread - pub fn toThread(lua: *Lua, index: i32) !Lua { - const thread = c.lua_tothread(lua.state, index); - if (thread) |thread_ptr| return Lua{ .state = thread_ptr }; - return error.Fail; - } - - /// Returns a Lua-owned userdata pointer of the given type at the given index. - /// Works for both light and full userdata. - /// Returns an error if the value is not a userdata. - /// See https://www.lua.org/manual/5.1/manual.html#lua_touserdata - pub fn toUserdata(lua: *Lua, comptime T: type, index: i32) !*T { - if (c.lua_touserdata(lua.state, index)) |ptr| return opaqueCast(T, ptr); - return error.Fail; - } - - /// Returns a Lua-owned userdata slice of the given type at the given index. - /// Returns an error if the value is not a userdata. - /// See https://www.lua.org/manual/5.1/manual.html#lua_touserdata - pub fn toUserdataSlice(lua: *Lua, comptime T: type, index: i32) ![]T { - if (c.lua_touserdata(lua.state, index)) |ptr| { - const size = lua.objectLen(index) / @sizeOf(T); - return @as([*]T, @ptrCast(@alignCast(ptr)))[0..size]; - } - return error.Fail; - } - - /// Returns the `LuaType` of the value at the given index - /// Note that this is equivalent to lua_type but because type is a Zig primitive it is renamed to `typeOf` - /// See https://www.lua.org/manual/5.1/manual.html#lua_type - pub fn typeOf(lua: *Lua, index: i32) LuaType { - return @enumFromInt(c.lua_type(lua.state, index)); - } - - /// Returns the name of the given `LuaType` as a null-terminated slice - /// See https://www.lua.org/manual/5.1/manual.html#lua_typename - pub fn typeName(lua: *Lua, t: LuaType) [:0]const u8 { - return std.mem.span(c.lua_typename(lua.state, @intFromEnum(t))); - } - - /// Returns the pseudo-index that represents the `i`th upvalue of the running function - pub fn upvalueIndex(i: i32) i32 { - return c.lua_upvalueindex(i); - } - - /// Pops `num` values from the current stack and pushes onto the stack of `to` - /// See https://www.lua.org/manual/5.1/manual.html#lua_xmove - pub fn xMove(lua: *Lua, to: Lua, num: i32) void { - c.lua_xmove(lua.state, to.state, num); - } - - /// Yields a coroutine - /// This function must be used as the return expression of a function - /// See https://www.lua.org/manual/5.1/manual.html#lua_yield - pub fn yield(lua: *Lua, num_results: i32) i32 { - return c.lua_yield(lua.state, num_results); - } - - // Debug library functions - // - // The debug interface functions are included in alphabetical order - // Each is kept similar to the original C API function while also making it easy to use from Zig - - /// Returns the current hook function - /// See https://www.lua.org/manual/5.1/manual.html#lua_gethook - pub fn getHook(lua: *Lua) ?CHookFn { - return c.lua_gethook(lua.state); - } - - /// Returns the current hook count - /// See https://www.lua.org/manual/5.1/manual.html#lua_gethookcount - pub fn getHookCount(lua: *Lua) i32 { - return c.lua_gethookcount(lua.state); - } - - /// Returns the current hook mask - /// See https://www.lua.org/manual/5.1/manual.html#lua_gethookmask - pub fn getHookMask(lua: *Lua) HookMask { - return HookMask.fromInt(c.lua_gethookmask(lua.state)); - } - - /// Gets information about a specific function or function invocation. - /// See https://www.lua.org/manual/5.1/manual.html#lua_getinfo - pub fn getInfo(lua: *Lua, options: DebugInfo.Options, info: *DebugInfo) void { - const str = options.toString(); - - var ar: Debug = undefined; - ar.i_ci = info.private; - - // should never fail because we are controlling options with the struct param - _ = c.lua_getinfo(lua.state, &str, &ar); - // std.debug.assert( != 0); - - // copy data into a struct - if (options.l) info.current_line = if (ar.currentline == -1) null else ar.currentline; - if (options.n) { - info.name = if (ar.name != null) std.mem.span(ar.name) else null; - info.name_what = blk: { - const what = std.mem.span(ar.namewhat); - if (std.mem.eql(u8, "global", what)) break :blk .global; - if (std.mem.eql(u8, "local", what)) break :blk .local; - if (std.mem.eql(u8, "method", what)) break :blk .method; - if (std.mem.eql(u8, "field", what)) break :blk .field; - if (std.mem.eql(u8, "upvalue", what)) break :blk .upvalue; - if (what.len == 0) break :blk .other; - unreachable; - }; - } - if (options.S) { - info.source = std.mem.span(ar.source); - @memcpy(&info.short_src, &ar.short_src); - info.first_line_defined = ar.linedefined; - info.last_line_defined = ar.lastlinedefined; - info.what = blk: { - const what = std.mem.span(ar.what); - if (std.mem.eql(u8, "Lua", what)) break :blk .lua; - if (std.mem.eql(u8, "C", what)) break :blk .c; - if (std.mem.eql(u8, "main", what)) break :blk .main; - unreachable; - }; - } - } - - /// Gets information about a local variable - /// Returns the name of the local variable - /// See https://www.lua.org/manual/5.1/manual.html#lua_getlocal - pub fn getLocal(lua: *Lua, info: *DebugInfo, n: i32) ![:0]const u8 { - var ar: Debug = undefined; - ar.i_ci = info.private; - if (c.lua_getlocal(lua.state, &ar, n)) |name| { - return std.mem.span(name); - } - return error.Fail; - } - - /// Gets information about the interpreter runtime stack - /// See https://www.lua.org/manual/5.1/manual.html#lua_getstack - pub fn getStack(lua: *Lua, level: i32) !DebugInfo { - var ar: Debug = undefined; - if (c.lua_getstack(lua.state, level, &ar) == 0) return error.Fail; - return DebugInfo{ .private = ar.i_ci }; - } - - /// Gets information about the `n`th upvalue of the closure at index `func_index` - /// See https://www.lua.org/manual/5.1/manual.html#lua_getupvaule - pub fn getUpvalue(lua: *Lua, func_index: i32, n: i32) ![:0]const u8 { - if (c.lua_getupvalue(lua.state, func_index, n)) |name| { - return std.mem.span(name); - } - return error.Fail; - } - - /// Sets the debugging hook function - /// See https://www.lua.org/manual/5.1/manual.html#lua_sethook - pub fn setHook(lua: *Lua, hook_fn: CHookFn, mask: HookMask, count: i32) void { - const hook_mask = HookMask.toInt(mask); - _ = c.lua_sethook(lua.state, hook_fn, hook_mask, count); - } - - /// Sets the value of a local variable - /// Returns an error when the index is greater than the number of active locals - /// Returns the name of the local variable - /// See https://www.lua.org/manual/5.1/manual.html#lua_setlocal - pub fn setLocal(lua: *Lua, info: *DebugInfo, n: i32) ![:0]const u8 { - var ar: Debug = undefined; - ar.i_ci = info.private; - if (c.lua_setlocal(lua.state, &ar, n)) |name| { - return std.mem.span(name); - } - return error.Fail; - } - - /// Sets the value of a closure's upvalue - /// Returns the name of the upvalue or an error if the upvalue does not exist - /// See https://www.lua.org/manual/5.1/manual.html#lua_setupvalue - pub fn setUpvalue(lua: *Lua, func_index: i32, n: i32) ![:0]const u8 { - if (c.lua_setupvalue(lua.state, func_index, n)) |name| { - return std.mem.span(name); - } - return error.Fail; - } - - // Auxiliary library functions - // - // Auxiliary library functions are included in alphabetical order. - // Each is kept similar to the original C API function while also making it easy to use from Zig - - /// Checks whether `cond` is true. Raises an error using `Lua.argError()` if not - /// Possibly never returns - /// See https://www.lua.org/manual/5.1/manual.html#luaL_argcheck - pub fn argCheck(lua: *Lua, cond: bool, arg: i32, extra_msg: [:0]const u8) void { - // translate-c failed - if (!cond) lua.argError(arg, extra_msg); - } - - /// Raises an error reporting a problem with argument `arg` of the C function that called it - /// See https://www.lua.org/manual/5.1/manual.html#luaL_argerror - pub fn argError(lua: *Lua, arg: i32, extra_msg: [*:0]const u8) noreturn { - _ = c.luaL_argerror(lua.state, arg, extra_msg); - unreachable; - } - - /// Calls a metamethod - /// See https://www.lua.org/manual/5.1/manual.html#luaL_callmeta - pub fn callMeta(lua: *Lua, obj: i32, field: [:0]const u8) !void { - if (c.luaL_callmeta(lua.state, obj, field.ptr) == 0) return error.Fail; - } - - /// Checks whether the function has an argument of any type at position `arg` - /// See https://www.lua.org/manual/5.1/manual.html#luaL_checkany - pub fn checkAny(lua: *Lua, arg: i32) void { - c.luaL_checkany(lua.state, arg); - } - - /// Checks whether the function argument `arg` is a number and returns this number cast to an i32 - /// See https://www.lua.org/manual/5.1/manual.html#luaL_checkint - pub fn checkInt(lua: *Lua, arg: i32) i32 { - return c.luaL_checkint(lua.state, arg); - } - - /// Checks whether the function argument `arg` is a number and returns the number cast to an Integer - /// See https://www.lua.org/manual/5.1/manual.html#luaL_checkinteger - pub fn checkInteger(lua: *Lua, arg: i32) Integer { - return c.luaL_checkinteger(lua.state, arg); - } - - /// Checks whether the function argument `arg` is a slice of bytes and returns the slice - /// See https://www.lua.org/manual/5.1/manual.html#luaL_checklstring - pub fn checkBytes(lua: *Lua, arg: i32) [:0]const u8 { - var length: usize = 0; - const str = c.luaL_checklstring(lua.state, arg, &length); - // luaL_checklstring never returns null (throws lua error) - return str[0..length :0]; - } - - /// Checks whether the function argument `arg` is a number and returns the number - /// See https://www.lua.org/manual/5.1/manual.html#luaL_checknumber - pub fn checkNumber(lua: *Lua, arg: i32) Number { - return c.luaL_checknumber(lua.state, arg); - } - - /// Checks whether the function argument `arg` is a string and searches for the enum value with the same name in `T`. - /// `default` is used as a default value when not null - /// Returns the enum value found - /// Useful for mapping Lua strings to Zig enums - /// See https://www.lua.org/manual/5.1/manual.html#luaL_checkoption - pub fn checkOption(lua: *Lua, comptime T: type, arg: i32, default: ?T) T { - const name = blk: { - if (default) |defaultName| { - break :blk lua.optBytes(arg, @tagName(defaultName)); - } else { - break :blk lua.checkBytes(arg); - } - }; - - inline for (std.meta.fields(T)) |field| { - if (std.mem.eql(u8, field.name, name)) { - return @enumFromInt(field.value); - } - } - - return lua.argError(arg, lua.pushFString("invalid option '%s'", .{name.ptr})); - } - - /// Grows the stack size to top + `size` elements, raising an error if the stack cannot grow to that size - /// `msg` is an additional text to go into the error message - /// See https://www.lua.org/manual/5.1/manual.html#luaL_checkstack - pub fn checkStackErr(lua: *Lua, size: i32, msg: ?[*:0]const u8) void { - c.luaL_checkstack(lua.state, size, msg); - } - - /// Checks whether the function argument `arg` is a string and returns the string - /// See https://www.lua.org/manual/5.1/manual.html#luaL_checkstring - pub fn checkString(lua: *Lua, arg: i32) [*:0]const u8 { - return c.luaL_checklstring(lua.state, arg, null); - } - - /// Checks whether the function argument `arg` has type `t` - /// See https://www.lua.org/manual/5.1/manual.html#luaL_checktype - pub fn checkType(lua: *Lua, arg: i32, t: LuaType) void { - c.luaL_checktype(lua.state, arg, @intFromEnum(t)); - } - - /// Checks whether the function argument `arg` is a userdata of the type `name` - /// Returns the userdata's memory-block address - /// See https://www.lua.org/manual/5.1/manual.html#luaL_checkudata - pub fn checkUserdata(lua: *Lua, comptime T: type, arg: i32, name: [:0]const u8) *T { - // the returned pointer will not be null - return opaqueCast(T, c.luaL_checkudata(lua.state, arg, name.ptr).?); - } - - /// Checks whether the function argument `arg` is a userdata of the type `name` - /// Returns a Lua-owned userdata slice - /// See https://www.lua.org/manual/5.1/manual.html#luaL_checkudata - pub fn checkUserdataSlice(lua: *Lua, comptime T: type, arg: i32, name: [:0]const u8) []T { - // the returned pointer will not be null - const ptr = c.luaL_checkudata(lua.state, arg, name.ptr).?; - const size = lua.objectLen(arg) / @sizeOf(T); - return @as([*]T, @ptrCast(@alignCast(ptr)))[0..size]; - } - - /// Loads and runs the given file - /// See https://www.lua.org/manual/5.1/manual.html#luaL_dofile - pub fn doFile(lua: *Lua, file_name: [:0]const u8) !void { - // translate-c failure - try lua.loadFile(file_name); - try lua.protectedCall(0, mult_return, 0); - } - - /// Loads and runs the given string - /// See https://www.lua.org/manual/5.1/manual.html#luaL_dostring - pub fn doString(lua: *Lua, str: [:0]const u8) !void { - // trnaslate-c failure - try lua.loadString(str); - try lua.protectedCall(0, mult_return, 0); - } - - /// Raises an error - /// See https://www.lua.org/manual/5.1/manual.html#luaL_error - pub fn raiseErrorStr(lua: *Lua, fmt: [:0]const u8, args: anytype) noreturn { - _ = @call(.auto, c.luaL_error, .{ lua.state, fmt.ptr } ++ args); - unreachable; - } - - /// Pushes onto the stack the field `e` from the metatable of the object at index `obj` - /// and returns the type of the pushed value - /// See https://www.lua.org/manual/5.1/manual.html#luaL_getmetafield - pub fn getMetaField(lua: *Lua, obj: i32, field: [:0]const u8) !LuaType { - const val_type: LuaType = @enumFromInt(c.luaL_getmetafield(lua.state, obj, field.ptr)); - if (val_type == .nil) return error.Fail; - return val_type; - } - - /// Pushes onto the stack the metatable associated with the name `type_name` in the registry - /// or nil if there is no metatable associated with that name. Returns the type of the pushed value - /// See https://www.lua.org/manual/5.1/manual.html#luaL_getmetatable - pub fn getMetatableRegistry(lua: *Lua, table_name: [:0]const u8) void { - c.luaL_getmetatable(lua.state, table_name); - } - - /// Creates a copy of string `str`, replacing any occurrence of the string `pat` with the string `rep` - /// Pushes the resulting string on the stack and returns it. - /// See https://www.lua.org/manual/5.1/manual.html#luaL_gsub - pub fn globalSub(lua: *Lua, str: [:0]const u8, pat: [:0]const u8, rep: [:0]const u8) [:0]const u8 { - return std.mem.span(c.luaL_gsub(lua.state, str.ptr, pat.ptr, rep.ptr)); - } - - /// Loads a buffer as a Lua chunk - /// See https://www.lua.org/manual/5.1/manual.html#luaL_loadbuffer - pub fn loadBuffer(lua: *Lua, buf: []const u8, name: [:0]const u8) !void { - switch (c.luaL_loadbuffer(lua.state, buf.ptr, buf.len, name.ptr)) { - StatusCode.ok => return, - StatusCode.err_syntax => return error.Syntax, - StatusCode.err_memory => return error.Memory, - else => unreachable, - } - } - - /// Loads a file as a Lua chunk - /// See https://www.lua.org/manual/5.1/manual.html#luaL_loadfile - pub fn loadFile(lua: *Lua, file_name: [:0]const u8) !void { - const ret = c.luaL_loadfile(lua.state, file_name.ptr); - switch (ret) { - StatusCode.ok => return, - StatusCode.err_syntax => return error.Syntax, - StatusCode.err_memory => return error.Memory, - err_file => return error.File, - else => unreachable, - } - } - - /// Loads a string as a Lua chunk - /// See https://www.lua.org/manual/5.1/manual.html#luaL_loadstring - pub fn loadString(lua: *Lua, str: [:0]const u8) !void { - const ret = c.luaL_loadstring(lua.state, str.ptr); - switch (ret) { - StatusCode.ok => return, - StatusCode.err_syntax => return error.Syntax, - StatusCode.err_memory => return error.Memory, - // loadstring runs lua_load which runs pcall, so can also return any result of an pcall error - StatusCode.err_runtime => return error.Runtime, - StatusCode.err_error => return error.MsgHandler, - else => unreachable, - } - } - - /// If the registry already has the key `key`, returns an error - /// Otherwise, creates a new table to be used as a metatable for userdata - /// See https://www.lua.org/manual/5.1/manual.html#luaL_newmetatable - pub fn newMetatable(lua: *Lua, key: [:0]const u8) !void { - if (c.luaL_newmetatable(lua.state, key.ptr) == 0) return error.Fail; - } - - /// Creates a new Lua state with an allocator using the default libc allocator - /// See https://www.lua.org/manual/5.1/manual.html#luaL_newstate - pub fn newStateLibc() !Lua { - const state = c.luaL_newstate() orelse return error.Memory; - return Lua{ .state = state }; - } - - // luaL_opt (a macro) really isn't that useful, so not going to implement for now - - /// If the function argument `arg` is a number, returns this number cast to an i32. - /// If the argument is absent or nil returns `default` - /// /// See https://www.lua.org/manual/5.1/manual.html#luaL_optint - pub fn optInt(lua: *Lua, arg: i32, default: i32) i32 { - return c.luaL_optint(lua.state, arg, default); - } - - /// If the function argument `arg` is an integer, returns the integer - /// If the argument is absent or nil returns `default` - /// See https://www.lua.org/manual/5.1/manual.html#luaL_optinteger - pub fn optInteger(lua: *Lua, arg: i32, default: Integer) Integer { - return c.luaL_optinteger(lua.state, arg, default); - } - - /// If the function argument `arg` is a slice of bytes, returns the slice - /// If the argument is absent or nil returns `default` - /// See https://www.lua.org/manual/5.1/manual.html#luaL_optlstring - pub fn optBytes(lua: *Lua, arg: i32, default: [:0]const u8) [:0]const u8 { - var length: usize = 0; - // will never return null because default cannot be null - const ret: [*]const u8 = c.luaL_optlstring(lua.state, arg, default.ptr, &length); - if (ret == default.ptr) return default; - return ret[0..length :0]; - } - - /// If the function argument `arg` is a number, returns the number - /// If the argument is absent or nil returns `default` - /// See https://www.lua.org/manual/5.1/manual.html#luaL_optnumber - pub fn optNumber(lua: *Lua, arg: i32, default: Number) Number { - return c.luaL_optnumber(lua.state, arg, default); - } - - /// If the function argument `arg` is a string, returns the string - /// If the argment is absent or nil returns `default` - /// See https://www.lua.org/manual/5.1/manual.html#luaL_optstring - pub fn optString(lua: *Lua, arg: i32, default: [:0]const u8) [*:0]const u8 { - // translate-c error - return c.luaL_optlstring(lua.state, arg, default.ptr, null); - } - - /// Creates and returns a reference in the table at index `index` for the object on the top of the stack - /// Pops the object - /// See https://www.lua.org/manual/5.1/manual.html#luaL_ref - pub fn ref(lua: *Lua, index: i32) !i32 { - const ret = c.luaL_ref(lua.state, index); - return if (ret == ref_nil) error.Fail else ret; - } - - /// Opens a library - /// See https://www.lua.org/manual/5.1/manual.html#luaL_register - pub fn registerFns(lua: *Lua, libname: ?[:0]const u8, funcs: []const FnReg) void { - // translated from the implementation of luaI_openlib so we can use a slice of - // FnReg without requiring a sentinel end value - if (libname) |name| { - _ = c.luaL_findtable(lua.state, registry_index, "_LOADED", 1); - lua.getField(-1, name); - if (!lua.isTable(-1)) { - lua.pop(1); - if (c.luaL_findtable(lua.state, globals_index, name, @intCast(funcs.len))) |_| { - lua.raiseErrorStr("name conflict for module " ++ c.LUA_QS, .{name.ptr}); - } - lua.pushValue(-1); - lua.setField(-3, name); - } - lua.remove(-2); - lua.insert(-1); - } - for (funcs) |f| { - lua.pushFunction(f.func); - lua.setField(-2, f.name); - } - } - - /// Returns the name of the type of the value at the given `index` - /// See https://www.lua.org/manual/5.1/manual.html#luaL_typename - pub fn typeNameIndex(lua: *Lua, index: i32) [:0]const u8 { - return std.mem.span(c.luaL_typename(lua.state, index)); - } - - /// Releases the reference `r` from the table at index `index` - /// See https://www.lua.org/manual/5.1/manual.html#luaL_unref - pub fn unref(lua: *Lua, index: i32, r: i32) void { - c.luaL_unref(lua.state, index, r); - } - - /// Pushes onto the stack a string identifying the current position of the control - /// at the call stack `level` - /// See https://www.lua.org/manual/5.1/manual.html#luaL_where - pub fn where(lua: *Lua, level: i32) void { - c.luaL_where(lua.state, level); - } - - // Standard library loading functions - - /// Opens the specified standard library functions - /// Behaves like openLibs, but allows specifying which libraries - /// to expose to the global table rather than all of them - /// See https://www.lua.org/manual/5.1/manual.html#luaL_openlibs - pub fn open(lua: *Lua, libs: Libs) void { - if (libs.base) lua.requireF("", c.luaopen_base); - if (libs.package) lua.requireF(c.LUA_LOADLIBNAME, c.luaopen_package); - if (libs.string) lua.requireF(c.LUA_STRLIBNAME, c.luaopen_string); - if (libs.table) lua.requireF(c.LUA_TABLIBNAME, c.luaopen_table); - if (libs.math) lua.requireF(c.LUA_MATHLIBNAME, c.luaopen_math); - if (libs.io) lua.requireF(c.LUA_IOLIBNAME, c.luaopen_io); - if (libs.os) lua.requireF(c.LUA_OSLIBNAME, c.luaopen_os); - if (libs.debug) lua.requireF(c.LUA_DBLIBNAME, c.luaopen_debug); - } - - fn requireF(lua: *Lua, name: [:0]const u8, func: CFn) void { - lua.pushFunction(func); - lua.pushString(name); - lua.call(1, 0); - } - - /// Open all standard libraries - /// See https://www.lua.org/manual/5.1/manual.html#luaL_openlibs - pub fn openLibs(lua: *Lua) void { - c.luaL_openlibs(lua.state); - } - - /// Open the basic standard library - pub fn openBase(lua: *Lua) void { - _ = c.luaopen_base(lua.state); - } - - /// Open the package standard library - pub fn openPackage(lua: *Lua) void { - _ = c.luaopen_package(lua.state); - } - - /// Open the string standard library - pub fn openString(lua: *Lua) void { - _ = c.luaopen_string(lua.state); - } - - /// Open the table standard library - pub fn openTable(lua: *Lua) void { - _ = c.luaopen_table(lua.state); - } - - /// Open the math standard library - pub fn openMath(lua: *Lua) void { - _ = c.luaopen_math(lua.state); - } - - /// Open the io standard library - pub fn openIO(lua: *Lua) void { - _ = c.luaopen_io(lua.state); - } - - /// Open the os standard library - pub fn openOS(lua: *Lua) void { - _ = c.luaopen_os(lua.state); - } - - /// Open the debug standard library - pub fn openDebug(lua: *Lua) void { - _ = c.luaopen_debug(lua.state); - } -}; - -/// A string buffer allowing for Zig code to build Lua strings piecemeal -/// All LuaBuffer functions are wrapped in this struct to make the API more convenient to use -/// See https://www.lua.org/manual/5.1/manual.html#luaL_Buffer -pub const Buffer = struct { - b: LuaBuffer = undefined, - - /// Initialize a Lua string buffer - /// See https://www.lua.org/manual/5.1/manual.html#luaL_buffinit - pub fn init(buf: *Buffer, lua: Lua) void { - c.luaL_buffinit(lua.state, &buf.b); - } - - /// Internal Lua type for a string buffer - pub const LuaBuffer = c.luaL_Buffer; - - pub const buffer_size = c.LUAL_BUFFERSIZE; - - /// Adds `byte` to the buffer - /// See https://www.lua.org/manual/5.1/manual.html#luaL_addchar - pub fn addChar(buf: *Buffer, byte: u8) void { - // could not be translated by translate-c - var lua_buf = &buf.b; - if (lua_buf.p > &lua_buf.buffer[buffer_size - 1]) _ = buf.prep(); - lua_buf.p.* = byte; - lua_buf.p += 1; - } - - /// Adds the string to the buffer - /// See https://www.lua.org/manual/5.1/manual.html#luaL_addlstring - pub fn addBytes(buf: *Buffer, str: []const u8) void { - c.luaL_addlstring(&buf.b, str.ptr, str.len); - } - - /// Adds to the buffer a string of `length` previously copied to the buffer area - /// See https://www.lua.org/manual/5.1/manual.html#luaL_addsize - pub fn addSize(buf: *Buffer, length: usize) void { - // another function translate-c couldn't handle - // c.luaL_addsize(&buf.b, length); - var lua_buf = &buf.b; - lua_buf.p += length; - } - - /// Adds the zero-terminated string pointed to by `str` to the buffer - /// See https://www.lua.org/manual/5.1/manual.html#luaL_addstring - pub fn addString(buf: *Buffer, str: [:0]const u8) void { - c.luaL_addstring(&buf.b, str.ptr); - } - - /// Adds the value on the top of the stack to the buffer and pops the value - /// See https://www.lua.org/manual/5.1/manual.html#luaL_addvalue - pub fn addValue(buf: *Buffer) void { - c.luaL_addvalue(&buf.b); - } - - /// Equivalent to prepSize with a buffer size of Buffer.buffer_size - /// See https://www.lua.org/manual/5.1/manual.html#luaL_prepbuffer - pub fn prep(buf: *Buffer) []u8 { - return c.luaL_prepbuffer(&buf.b)[0..buffer_size]; - } - - /// Finishes the use of the buffer leaving the final string on the top of the stack - /// See https://www.lua.org/manual/5.1/manual.html#luaL_pushresult - pub fn pushResult(buf: *Buffer) void { - c.luaL_pushresult(&buf.b); - } -}; - -// Helper functions to make the ziglua API easier to use - -/// Casts the opaque pointer to a pointer of the given type with the proper alignment -/// Useful for casting pointers from the Lua API like userdata or other data -pub inline fn opaqueCast(comptime T: type, ptr: *anyopaque) *T { - return @ptrCast(@alignCast(ptr)); -} - -pub const ZigFn = fn (lua: *Lua) i32; -pub const ZigHookFn = fn (lua: *Lua, event: Event, info: *DebugInfo) void; -pub const ZigContFn = fn (lua: *Lua, status: Status, ctx: i32) i32; -pub const ZigReaderFn = fn (lua: *Lua, data: *anyopaque) ?[]const u8; -pub const ZigWriterFn = fn (lua: *Lua, buf: []const u8, data: *anyopaque) bool; - -fn TypeOfWrap(comptime T: type) type { - return switch (T) { - LuaState => Lua, - ZigFn => CFn, - ZigHookFn => CHookFn, - ZigReaderFn => CReaderFn, - ZigWriterFn => CWriterFn, - else => @compileError("unsupported type given to wrap: '" ++ @typeName(T) ++ "'"), - }; -} - -/// Wraps the given value for use in the Lua API -/// Supports the following: -/// * `LuaState` => `Lua` -pub fn wrap(comptime value: anytype) TypeOfWrap(@TypeOf(value)) { - const T = @TypeOf(value); - return switch (T) { - LuaState => Lua{ .state = value }, - ZigFn => wrapZigFn(value), - ZigHookFn => wrapZigHookFn(value), - ZigReaderFn => wrapZigReaderFn(value), - ZigWriterFn => wrapZigWriterFn(value), - else => @compileError("unsupported type given to wrap: '" ++ @typeName(T) ++ "'"), - }; -} - -/// Wrap a ZigFn in a CFn for passing to the API -fn wrapZigFn(comptime f: ZigFn) CFn { - return struct { - fn inner(state: ?*LuaState) callconv(.C) c_int { - // this is called by Lua, state should never be null - var lua: Lua = .{ .state = state.? }; - return @call(.always_inline, f, .{&lua}); - } - }.inner; -} - -/// Wrap a ZigHookFn in a CHookFn for passing to the API -fn wrapZigHookFn(comptime f: ZigHookFn) CHookFn { - return struct { - fn inner(state: ?*LuaState, ar: ?*Debug) callconv(.C) void { - // this is called by Lua, state should never be null - var lua: Lua = .{ .state = state.? }; - var info: DebugInfo = .{ - .current_line = if (ar.?.currentline == -1) null else ar.?.currentline, - .private = ar.?.i_ci, - }; - @call(.always_inline, f, .{ &lua, @as(Event, @enumFromInt(ar.?.event)), &info }); - } - }.inner; -} - -/// Wrap a ZigReaderFn in a CReaderFn for passing to the API -fn wrapZigReaderFn(comptime f: ZigReaderFn) CReaderFn { - return struct { - fn inner(state: ?*LuaState, data: ?*anyopaque, size: [*c]usize) callconv(.C) [*c]const u8 { - var lua: Lua = .{ .state = state.? }; - if (@call(.always_inline, f, .{ &lua, data.? })) |buffer| { - size.* = buffer.len; - return buffer.ptr; - } else { - size.* = 0; - return null; - } - } - }.inner; -} - -/// Wrap a ZigWriterFn in a CWriterFn for passing to the API -fn wrapZigWriterFn(comptime f: ZigWriterFn) CWriterFn { - return struct { - fn inner(state: ?*LuaState, buf: ?*const anyopaque, size: usize, data: ?*anyopaque) callconv(.C) c_int { - // this is called by Lua, state should never be null - var lua: Lua = .{ .state = state.? }; - const buffer = @as([*]const u8, @ptrCast(buf))[0..size]; - const result = @call(.always_inline, f, .{ &lua, buffer, data.? }); - // it makes more sense for the inner writer function to return false for failure, - // so negate the result here - return @intFromBool(!result); - } - }.inner; -} - -/// Export a Zig function to be used as a the entry point to a Lua module -/// -/// Exported as luaopen_[name] -pub fn exportFn(comptime name: []const u8, comptime func: ZigFn) CFn { - return struct { - fn luaopen(state: ?*LuaState) callconv(.C) c_int { - const declaration = comptime wrap(func); - - return @call(.always_inline, declaration, .{state}); - } - - comptime { - @export(luaopen, .{ .name = "luaopen_" ++ name }); - } - }.luaopen; -} diff --git a/src/tests.zig b/src/tests.zig index 89acec4..0791711 100644 --- a/src/tests.zig +++ b/src/tests.zig @@ -48,42 +48,6 @@ inline fn toNumber(lua: *Lua, index: i32) !ziglua.Number { } else return try lua.toNumber(index); } -/// getGlobal that always returns an error union -inline fn getGlobal(lua: *Lua, name: [:0]const u8) !ziglua.LuaType { - if (langIn(.{ .lua51, .luajit })) { - lua.getGlobal(name); - return lua.typeOf(-1); - } - return try lua.getGlobal(name); -} - -/// getGlobal that always returns a LuaType -inline fn getIndex(lua: *Lua, index: i32, i: ziglua.Integer) ziglua.LuaType { - if (ziglua.lang == .lua53 or ziglua.lang == .lua54) { - return lua.getIndex(index, i); - } - _ = lua.rawGetIndex(index, i); - return lua.typeOf(-1); -} - -/// getTagle that always returns a LuaType -inline fn getTable(lua: *Lua, index: i32) ziglua.LuaType { - if (langIn(.{ .lua52, .lua53, .lua54, .luau })) { - return lua.getTable(index); - } - lua.getTable(index); - return lua.typeOf(-1); -} - -/// rawGetTable that always returns a LuaType -inline fn rawGetTable(lua: *Lua, index: i32) ziglua.LuaType { - if (langIn(.{ .lua52, .lua53, .lua54, .luau })) { - return lua.rawGetTable(index); - } - lua.rawGetTable(index); - return lua.typeOf(-1); -} - /// pushFunction that sets the name for Luau inline fn pushFunction(lua: *Lua, c_fn: ziglua.CFn) void { if (ziglua.lang == .luau) return lua.pushFunction(c_fn, ""); @@ -446,7 +410,7 @@ test "executing string contents" { try lua.loadString("a = f(2)"); try lua.protectedCall(0, 0, 0); - try expectEqual(.number, try getGlobal(&lua, "a")); + try expectEqual(.number, try lua.getGlobal("a")); try expectEqual(12, try toInteger(&lua, 1)); try expectError(if (ziglua.lang == .luau) error.Fail else error.Syntax, lua.loadString("bad syntax")); @@ -558,7 +522,7 @@ test "calling a function" { lua.register("zigadd", ziglua.wrap(add)); - _ = try getGlobal(&lua, "zigadd"); + _ = try lua.getGlobal("zigadd"); lua.pushInteger(10); lua.pushInteger(32); @@ -724,7 +688,7 @@ test "function registration" { lua.registerFns("testlib", &funcs); // testlib.add(1, 2) - _ = try getGlobal(&lua, "testlib"); + _ = try lua.getGlobal("testlib"); _ = lua.getField(-1, "add"); lua.pushInteger(1); lua.pushInteger(2); @@ -875,22 +839,25 @@ test "table access" { defer lua.deinit(); try lua.doString("a = { [1] = 'first', key = 'value', ['other one'] = 1234 }"); - _ = try getGlobal(&lua, "a"); + _ = try lua.getGlobal("a"); if (ziglua.lang == .lua53 or ziglua.lang == .lua54) { try expectEqual(.string, lua.rawGetIndex(1, 1)); try expectEqualStrings("first", try lua.toBytes(-1)); } - try expectEqual(.string, getIndex(&lua, 1, 1)); + try expectEqual(.string, switch (ziglua.lang) { + .lua53, .lua54 => lua.getIndex(1, 1), + else => lua.rawGetIndex(1, 1), + }); try expectEqualStrings("first", try lua.toBytes(-1)); _ = lua.pushString("key"); - try expectEqual(.string, getTable(&lua, 1)); + try expectEqual(.string, lua.getTable(1)); try expectEqualStrings("value", try lua.toBytes(-1)); _ = lua.pushString("other one"); - try expectEqual(.number, rawGetTable(&lua, 1)); + try expectEqual(.number, lua.rawGetTable(1)); try expectEqual(1234, try toInteger(&lua, -1)); // a.name = "ziglua" @@ -923,7 +890,7 @@ test "table access" { lua.setField(1, "bool"); try lua.doString("b = a.bool"); - try expectEqual(.boolean, try getGlobal(&lua, "b")); + try expectEqual(.boolean, try lua.getGlobal("b")); try expect(lua.toBoolean(-1)); // create array [1, 2, 3, 4, 5] @@ -993,7 +960,7 @@ test "dump and load" { // store a function in a global try lua.doString("f = function(x) return function(n) return n + x end end"); // put the function on the stack - _ = try getGlobal(&lua, "f"); + _ = try lua.getGlobal("f"); const writer = struct { fn inner(l: *Lua, buf: []const u8, data: *anyopaque) bool { @@ -1126,7 +1093,7 @@ test "upvalues" { // call the function repeatedly, each time ensuring the result increases by one var expected: i32 = 1; while (expected <= 10) : (expected += 1) { - _ = try getGlobal(&lua, "counter"); + _ = try lua.getGlobal("counter"); lua.call(0, 1); try expectEqual(expected, try toInteger(&lua, -1)); lua.pop(1); @@ -1138,7 +1105,7 @@ test "table traversal" { defer lua.deinit(); try lua.doString("t = { key = 'value', second = true, third = 1 }"); - _ = try getGlobal(&lua, "t"); + _ = try lua.getGlobal("t"); lua.pushNil(); @@ -1371,7 +1338,7 @@ test "resuming" { \\ return "done" \\end ); - _ = try getGlobal(&thread, "counter"); + _ = try thread.getGlobal("counter"); var i: i32 = 1; while (i <= 5) : (i += 1) { @@ -1587,7 +1554,7 @@ test "loadBuffer" { } else _ = try lua.loadBuffer("global = 10", "chunkname", .text); try lua.protectedCall(0, ziglua.mult_return, 0); - _ = try getGlobal(&lua, "global"); + _ = try lua.getGlobal("global"); try expectEqual(10, try toInteger(&lua, -1)); } @@ -1610,7 +1577,7 @@ test "where" { \\ret = whereFn() ); - _ = try getGlobal(&lua, "ret"); + _ = try lua.getGlobal("ret"); try expectEqualStrings("[string \"...\"]:2: ", try lua.toBytes(-1)); } @@ -1669,7 +1636,7 @@ test "metatables" { } // set the len metamethod to the function f - _ = try getGlobal(&lua, "f"); + _ = try lua.getGlobal("f"); lua.setField(1, "__len"); lua.newTable(); @@ -1739,7 +1706,7 @@ test "traceback" { lua.setGlobal("tracebackFn"); try lua.doString("res = tracebackFn()"); - _ = try getGlobal(&lua, "res"); + _ = try lua.getGlobal("res"); try expectEqualStrings("\nstack traceback:\n\t[string \"res = tracebackFn()\"]:1: in main chunk", try lua.toBytes(-1)); } @@ -1754,7 +1721,7 @@ test "getSubtable" { \\ b = {}, \\} ); - _ = try getGlobal(&lua, "a"); + _ = try lua.getGlobal("a"); // get the subtable a.b try lua.getSubtable(-1, "b"); @@ -1891,13 +1858,13 @@ test "function environments" { lua.pushInteger(10); lua.setGlobal("x"); - _ = try getGlobal(&lua, "test"); + _ = try lua.getGlobal("test"); try lua.protectedCall(0, 1, 0); try testing.expectEqual(10, lua.toInteger(1)); lua.pop(1); // now set the functions table to have a different value of x - _ = try getGlobal(&lua, "test"); + _ = try lua.getGlobal("test"); lua.newTable(); lua.pushInteger(20); lua.setField(2, "x"); @@ -1907,7 +1874,7 @@ test "function environments" { try testing.expectEqual(20, lua.toInteger(1)); lua.pop(1); - _ = try getGlobal(&lua, "test"); + _ = try lua.getGlobal("test"); lua.getFnEnvironment(1); _ = lua.getField(2, "x"); try testing.expectEqual(20, lua.toInteger(3)); @@ -1938,7 +1905,7 @@ test "debug interface" { \\ return x + y \\end ); - _ = try getGlobal(&lua, "f"); + _ = try lua.getGlobal("f"); var info: DebugInfo = undefined; lua.getInfo(.{ @@ -1997,7 +1964,7 @@ test "debug interface" { try expectEqual(ziglua.wrap(hook), lua.getHook()); try expectEqual(ziglua.HookMask{ .call = true, .line = true, .ret = true }, lua.getHookMask()); - _ = try getGlobal(&lua, "f"); + _ = try lua.getGlobal("f"); lua.pushNumber(3); try lua.protectedCall(1, 1, 0); } @@ -2015,7 +1982,7 @@ test "debug interface Lua 5.1 and Luau" { \\ return x + y \\end ); - _ = try getGlobal(&lua, "f"); + _ = try lua.getGlobal("f"); var info: DebugInfo = undefined; @@ -2087,7 +2054,7 @@ test "debug interface Lua 5.1 and Luau" { try expectEqual(@as(?ziglua.CHookFn, ziglua.wrap(hook)), lua.getHook()); try expectEqual(ziglua.HookMask{ .call = true, .line = true, .ret = true }, lua.getHookMask()); - lua.getGlobal("f"); + _ = try lua.getGlobal("f"); lua.pushNumber(3); try lua.protectedCall(1, 1, 0); } @@ -2104,7 +2071,7 @@ test "debug upvalues" { \\end \\addone = f(1) ); - _ = try getGlobal(&lua, "addone"); + _ = try lua.getGlobal("addone"); // index doesn't exist try expectError(error.Fail, lua.getUpvalue(1, 2)); @@ -2134,8 +2101,8 @@ test "debug upvalues" { \\addthree = f(3) ); - _ = try getGlobal(&lua, "addone"); - _ = try getGlobal(&lua, "addthree"); + _ = try lua.getGlobal("addone"); + _ = try lua.getGlobal("addthree"); // now addone and addthree share the same upvalue lua.upvalueJoin(-2, 1, -1, 1); From da14ff259738c4a1529334cfa5f923f421178b9b Mon Sep 17 00:00:00 2001 From: Nathan Craddock Date: Mon, 29 Jan 2024 17:56:07 -0700 Subject: [PATCH 4/5] Merge liblua.zig into lib.zig --- src/lib.zig | 601 ++++++++++++++--- src/libluau.zig | 1683 ----------------------------------------------- src/tests.zig | 2 +- 3 files changed, 505 insertions(+), 1781 deletions(-) delete mode 100644 src/libluau.zig diff --git a/src/lib.zig b/src/lib.zig index 7b185b2..7b4d04f 100644 --- a/src/lib.zig +++ b/src/lib.zig @@ -4,8 +4,9 @@ const c = @cImport({ @cInclude("luaconf.h"); @cInclude("lua.h"); @cInclude("lualib.h"); - @cInclude("lauxlib.h"); + if (lang != .luau) @cInclude("lauxlib.h"); + if (lang == .luau) @cInclude("luacode.h"); if (lang == .luajit) @cInclude("luajit.h"); }); @@ -14,6 +15,15 @@ const config = @import("config"); /// Lua language version targeted pub const lang = config.lang; +/// The length of Luau vector values, either 3 or 4. +pub const luau_vector_size = if (config.luau_use_4_vector) 4 else 3; + +/// This function is defined in luau.cpp and must be called to define the assertion printer +extern "c" fn zig_registerAssertionHandler() void; + +/// This function is defined in luau.cpp and ensures Zig uses the correct free when compiling luau code +extern "c" fn zig_luau_free(ptr: *anyopaque) void; + const Allocator = std.mem.Allocator; // Types @@ -72,6 +82,12 @@ pub const CompareOperator = enum(u2) { le = c.LUA_OPLE, }; +/// Type for C userdata destructors +pub const CUserdataDtorFn = *const fn (userdata: *anyopaque) callconv(.C) void; + +/// Type for C useratom callback +pub const CUserAtomCallbackFn = *const fn (str: [*c]const u8, len: usize) callconv(.C) i16; + /// The internal Lua debug structure const Debug = c.lua_Debug; @@ -231,10 +247,53 @@ const DebugInfo54 = struct { }; }; +pub const DebugInfoLuau = struct { + source: [:0]const u8 = undefined, + src_len: usize = 0, + short_src: [c.LUA_IDSIZE:0]u8 = undefined, + + name: ?[:0]const u8 = undefined, + what: FnType = undefined, + + current_line: ?i32 = null, + first_line_defined: ?i32 = null, + + is_vararg: bool = false, + + pub const NameType = enum { global, local, method, field, upvalue, other }; + + pub const FnType = enum { lua, c, main, tail }; + + pub const Options = packed struct { + f: bool = false, + l: bool = false, + n: bool = false, + s: bool = false, + u: bool = false, + L: bool = false, + + fn toString(options: Options) [10:0]u8 { + var str = [_:0]u8{0} ** 10; + var index: u8 = 0; + + inline for (std.meta.fields(Options)) |field| { + if (@field(options, field.name)) { + str[index] = field.name[0]; + index += 1; + } + } + while (index < str.len) : (index += 1) str[index] = 0; + + return str; + } + }; +}; + pub const DebugInfo = switch (lang) { + .lua51, .luajit => DebugInfo51, .lua52, .lua53 => DebugInfo52, .lua54 => DebugInfo54, - else => DebugInfo51, + .luau => DebugInfoLuau, }; /// The superset of all errors returned from ziglua @@ -277,7 +336,9 @@ const Event52 = enum(u3) { pub const Event = switch (lang) { .lua51, .luajit => Event51, - else => Event52, + .lua52, .lua53, .lua54 => Event52, + // TODO: probably something better than void here + .luau => void, }; /// Type for arrays of functions to be registered @@ -376,9 +437,9 @@ pub const Libs53 = packed struct { /// Bitflag for the Lua standard libraries pub const Libs = switch (lang) { + .lua51, .luajit => Libs51, .lua52 => Libs52, - .lua53, .lua54 => Libs53, - else => Libs51, + .lua53, .lua54, .luau => Libs53, }; /// The type of the opaque structure that points to a thread and the state of a Lua interpreter @@ -386,17 +447,32 @@ pub const LuaState = c.lua_State; /// Lua types /// Must be a signed integer because LuaType.none is -1 -pub const LuaType = enum(i5) { - none = c.LUA_TNONE, - nil = c.LUA_TNIL, - boolean = c.LUA_TBOOLEAN, - light_userdata = c.LUA_TLIGHTUSERDATA, - number = c.LUA_TNUMBER, - string = c.LUA_TSTRING, - table = c.LUA_TTABLE, - function = c.LUA_TFUNCTION, - userdata = c.LUA_TUSERDATA, - thread = c.LUA_TTHREAD, +pub const LuaType = switch (lang) { + .luau => enum(i5) { + none = c.LUA_TNONE, + nil = c.LUA_TNIL, + boolean = c.LUA_TBOOLEAN, + light_userdata = c.LUA_TLIGHTUSERDATA, + number = c.LUA_TNUMBER, + vector = c.LUA_TVECTOR, + string = c.LUA_TSTRING, + table = c.LUA_TTABLE, + function = c.LUA_TFUNCTION, + userdata = c.LUA_TUSERDATA, + thread = c.LUA_TTHREAD, + }, + else => enum(i5) { + none = c.LUA_TNONE, + nil = c.LUA_TNIL, + boolean = c.LUA_TBOOLEAN, + light_userdata = c.LUA_TLIGHTUSERDATA, + number = c.LUA_TNUMBER, + string = c.LUA_TSTRING, + table = c.LUA_TTABLE, + function = c.LUA_TFUNCTION, + userdata = c.LUA_TUSERDATA, + thread = c.LUA_TTHREAD, + }, }; /// Modes used for `Lua.load()` @@ -464,10 +540,7 @@ pub const Status = enum(u3) { /// used for that specific function's return type /// TODO: see where this is used and check if a null can be used instead const StatusCode = struct { - pub const ok = switch (@hasDecl(c, "LUA_OK")) { - true => c.LUA_OK, - false => 0, - }; + pub const ok = if (@hasDecl(c, "LUA_OK")) c.LUA_OK else 0; pub const yield = c.LUA_YIELD; pub const err_runtime = c.LUA_ERRRUN; pub const err_syntax = c.LUA_ERRSYNTAX; @@ -536,6 +609,8 @@ pub const Lua = struct { /// Initialize a Lua state with the given allocator pub fn init(allocator_ptr: *const Allocator) !Lua { + if (lang == .luau) zig_registerAssertionHandler(); + // @constCast() is safe here because Lua does not mutate the pointer internally const state = c.lua_newstate(alloc, @constCast(allocator_ptr)) orelse return error.Memory; return Lua{ .state = state }; @@ -596,7 +671,7 @@ pub const Lua = struct { /// See https://www.lua.org/manual/5.4/manual.html#lua_call pub fn call(lua: *Lua, num_args: i32, num_results: i32) void { switch (lang) { - .lua51, .luajit => c.lua_call(lua.state, num_args, num_results), + .lua51, .luajit, .luau => c.lua_call(lua.state, num_args, num_results), else => lua.callCont(num_args, num_results, 0, null), } } @@ -815,6 +890,14 @@ pub const Lua = struct { return c.lua_gc(lua.state, c.LUA_GCGEN, minor_mul, major_mul) == c.LUA_GCINC; } + pub fn gcSetGoal(lua: *Lua, goal: i32) i32 { + return c.lua_gc(lua.state, c.LUA_GCSETGOAL, goal); + } + + pub fn gcSetStepSize(lua: *Lua, size: i32) i32 { + return c.lua_gc(lua.state, c.LUA_GCSETSTEPSIZE, size); + } + /// Changes the collector to generational mode /// Returns true if the previous mode was incremental /// See https://www.lua.org/manual/5.4/manual.html#lua_gc @@ -880,7 +963,7 @@ pub const Lua = struct { /// See https://www.lua.org/manual/5.4/manual.html#lua_getfield pub fn getField(lua: *Lua, index: i32, key: [:0]const u8) LuaType { switch (lang) { - .lua53, .lua54 => return @enumFromInt(c.lua_getfield(lua.state, index, key.ptr)), + .lua53, .lua54, .luau => return @enumFromInt(c.lua_getfield(lua.state, index, key.ptr)), else => { c.lua_getfield(lua.state, index, key.ptr); return lua.typeOf(-1); @@ -894,7 +977,7 @@ pub const Lua = struct { pub fn getGlobal(lua: *Lua, name: [:0]const u8) !LuaType { const lua_type: LuaType = blk: { switch (lang) { - .lua53, .lua54 => break :blk @enumFromInt(c.lua_getglobal(lua.state, name.ptr)), + .lua53, .lua54, .luau => break :blk @enumFromInt(c.lua_getglobal(lua.state, name.ptr)), else => { c.lua_getglobal(lua.state, name.ptr); break :blk lua.typeOf(-1); @@ -950,7 +1033,7 @@ pub const Lua = struct { /// See https://www.lua.org/manual/5.4/manual.html#lua_gettable pub fn getTable(lua: *Lua, index: i32) LuaType { switch (lang) { - .lua53, .lua54 => return @enumFromInt(c.lua_gettable(lua.state, index)), + .lua53, .lua54, .luau => return @enumFromInt(c.lua_gettable(lua.state, index)), else => { c.lua_gettable(lua.state, index); return lua.typeOf(-1); @@ -965,6 +1048,14 @@ pub const Lua = struct { return c.lua_gettop(lua.state); } + pub fn setReadonly(lua: *Lua, idx: i32, enabled: bool) void { + c.lua_setreadonly(lua.state, idx, @intFromBool(enabled)); + } + + pub fn getReadonly(lua: *Lua, idx: i32) bool { + return c.lua_getreadonly(lua.state, idx) != 0; + } + /// Moves the top element into the given valid `index` shifting up any elements to make room /// See https://www.lua.org/manual/5.4/manual.html#lua_insert pub fn insert(lua: *Lua, index: i32) void { @@ -1049,6 +1140,11 @@ pub const Lua = struct { return c.lua_isuserdata(lua.state, index) != 0; } + /// Returns true if the value at the given index is a vector + pub fn isVector(lua: *Lua, index: i32) bool { + return c.lua_isvector(lua.state, index); + } + /// Returns true if the given coroutine can yield /// See https://www.lua.org/manual/5.4/manual.html#lua_isyieldable pub fn isYieldable(lua: *Lua) bool { @@ -1122,6 +1218,8 @@ pub const Lua = struct { /// Creates a new independent state and returns its main thread /// See https://www.lua.org/manual/5.4/manual.html#lua_newstate pub fn newState(alloc_fn: AllocFn, data: ?*const anyopaque) !Lua { + if (lang == .luau) zig_registerAssertionHandler(); + const state = c.lua_newstate(alloc_fn, @constCast(data)) orelse return error.Memory; return Lua{ .state = state }; } @@ -1189,6 +1287,36 @@ pub const Lua = struct { else => newUserdataSlice51, }; + pub fn newUserdataTagged(lua: *Lua, comptime T: type, tag: i32) *T { + const UTAG_PROXY = c.LUA_UTAG_LIMIT + 1; // not exposed in headers + std.debug.assert((tag >= 0 and tag < c.LUA_UTAG_LIMIT) or tag == UTAG_PROXY); // Luau will do the same assert, this is easier to debug + // safe to .? because this function throws a Lua error on out of memory + // so the returned pointer should never be null + const ptr = c.lua_newuserdatatagged(lua.state, @sizeOf(T), tag).?; + return opaqueCast(T, ptr); + } + + /// Returns the tag of a userdata at the given index + /// TODO: rename to getUserdataTag? + pub fn userdataTag(lua: *Lua, index: i32) !i32 { + const tag = c.lua_userdatatag(lua.state, index); + if (tag == -1) return error.Fail; + return tag; + } + + /// This function allocates a new userdata of the given type with an associated + /// destructor callback. + /// + /// Returns a pointer to the Lua-owned data + /// + /// Note: Luau doesn't support the usual Lua __gc metatable destructor. Use this instead. + pub fn newUserdataDtor(lua: *Lua, comptime T: type, dtor_fn: CUserdataDtorFn) *T { + // safe to .? because this function throws a Lua error on out of memory + // so the returned pointer should never be null + const ptr = c.lua_newuserdatadtor(lua.state, @sizeOf(T), @ptrCast(dtor_fn)).?; + return opaqueCast(T, ptr); + } + /// Pops a key from the stack, and pushes a key-value pair from the table at the given index /// See https://www.lua.org/manual/5.4/manual.html#lua_next pub fn next(lua: *Lua, index: i32) bool { @@ -1210,7 +1338,10 @@ pub const Lua = struct { /// Returns the length of the value at the given index /// See https://www.lua.org/manual/5.1/manual.html#lua_objlen /// TODO: this might be nice to map to "len" - pub fn objectLen(lua: *Lua, index: i32) usize { + pub fn objectLen(lua: *Lua, index: i32) switch (lang) { + .luau => i32, + else => usize, + } { return c.lua_objlen(lua.state, index); } @@ -1254,7 +1385,7 @@ pub const Lua = struct { /// Calls a function (or callable object) in protected mode /// See https://www.lua.org/manual/5.4/manual.html#lua_pcall pub const protectedCall = switch (lang) { - .lua51, .luajit => protectedCall51, + .lua51, .luajit, .luau => protectedCall51, else => protectedCall52, }; @@ -1312,24 +1443,40 @@ pub const Lua = struct { c.lua_pushboolean(lua.state, @intFromBool(b)); } - /// Pushes a new Closure onto the stack - /// `n` tells how many upvalues this function will have - /// See https://www.lua.org/manual/5.4/manual.html#lua_pushcclosure - pub fn pushClosure(lua: *Lua, c_fn: CFn, n: i32) void { + fn pushClosureLua(lua: *Lua, c_fn: CFn, n: i32) void { c.lua_pushcclosure(lua.state, c_fn, n); } - /// Pushes a function onto the stack. - /// Equivalent to pushClosure with no upvalues - /// See https://www.lua.org/manual/5.4/manual.html#lua_pushcfunction - pub fn pushFunction(lua: *Lua, c_fn: CFn) void { + fn pushClosureLuau(lua: *Lua, c_fn: CFn, name: [:0]const u8, n: i32) void { + c.lua_pushcclosurek(lua.state, c_fn, name, n, null); + } + + /// Pushes a new Closure onto the stack + /// `n` tells how many upvalues this function will have + /// See https://www.lua.org/manual/5.1/manual.html#lua_pushcclosure + pub const pushClosure = if (lang == .luau) pushClosureLuau else pushClosureLua; + + fn pushFunctionLua(lua: *Lua, c_fn: CFn) void { lua.pushClosure(c_fn, 0); } + fn pushFunctionLuau(lua: *Lua, c_fn: CFn, name: [:0]const u8) void { + lua.pushClosure(c_fn, name, 0); + } + + /// Pushes a new Closure onto the stack + /// `n` tells how many upvalues this function will have + /// See https://www.lua.org/manual/5.4/manual.html#lua_pushcclosure + pub const pushFunction = if (lang == .luau) pushFunctionLuau else pushFunctionLua; + /// Push a formatted string onto the stack and return a pointer to the string /// See https://www.lua.org/manual/5.4/manual.html#lua_pushfstring pub fn pushFString(lua: *Lua, fmt: [:0]const u8, args: anytype) [*:0]const u8 { - return @call(.auto, c.lua_pushfstring, .{ lua.state, fmt.ptr } ++ args); + return @call( + .auto, + if (lang == .luau) c.lua_pushfstringL else c.lua_pushfstring, + .{ lua.state, fmt.ptr } ++ args, + ); } /// Pushes the global environment onto the stack @@ -1354,7 +1501,7 @@ pub const Lua = struct { } const BytesResult = switch (lang) { - .lua51, .luajit => void, + .lua51, .luajit, .luau => void, else => []const u8, }; @@ -1362,7 +1509,7 @@ pub const Lua = struct { /// See https://www.lua.org/manual/5.4/manual.html#lua_pushlstring pub fn pushBytes(lua: *Lua, bytes: []const u8) BytesResult { switch (lang) { - .lua51, .luajit => { + .lua51, .luajit, .luau => { c.lua_pushlstring(lua.state, bytes.ptr, bytes.len); }, else => return c.lua_pushlstring(lua.state, bytes.ptr, bytes.len)[0..bytes.len], @@ -1382,7 +1529,7 @@ pub const Lua = struct { } const StringResult = switch (lang) { - .lua51, .luajit => void, + .lua51, .luajit, .luau => void, else => [:0]const u8, }; @@ -1392,7 +1539,7 @@ pub const Lua = struct { /// See https://www.lua.org/manual/5.4/manual.html#lua_pushstring pub fn pushString(lua: *Lua, str: [:0]const u8) StringResult { switch (lang) { - .lua51, .luajit => { + .lua51, .luajit, .luau => { c.lua_pushstring(lua.state, str.ptr); }, else => return c.lua_pushstring(lua.state, str.ptr)[0..str.len :0], @@ -1418,6 +1565,17 @@ pub const Lua = struct { c.lua_pushvalue(lua.state, index); } + fn pushVector3(lua: *Lua, x: f32, y: f32, z: f32) void { + c.lua_pushvector(lua.state, x, y, z); + } + + fn pushVector4(lua: *Lua, x: f32, y: f32, z: f32, w: f32) void { + c.lua_pushvector(lua.state, x, y, z, w); + } + + /// Pushes a floating point 3-vector (or 4-vector if configured) `v` onto the stack + pub const pushVector = if (luau_vector_size == 3) pushVector3 else pushVector4; + /// Returns true if the two values in indices `index1` and `index2` are primitively equal /// Bypasses __eq metamethods /// Returns false if not equal, or if any index is invalid @@ -1431,7 +1589,7 @@ pub const Lua = struct { /// TODO: should this be renamed to getTableRaw (seems more logical)? pub fn rawGetTable(lua: *Lua, index: i32) LuaType { switch (lang) { - .lua53, .lua54 => return @enumFromInt(c.lua_rawget(lua.state, index)), + .lua53, .lua54, .luau => return @enumFromInt(c.lua_rawget(lua.state, index)), else => { c.lua_rawget(lua.state, index); return lua.typeOf(-1); @@ -1440,7 +1598,7 @@ pub const Lua = struct { } const RawGetIndexNType = switch (lang) { - .lua51, .lua52, .luajit => i32, + .lua51, .lua52, .luajit, .luau => i32, else => Integer, }; @@ -1449,7 +1607,7 @@ pub const Lua = struct { /// See https://www.lua.org/manual/5.4/manual.html#lua_rawgeti pub fn rawGetIndex(lua: *Lua, index: i32, n: RawGetIndexNType) LuaType { switch (lang) { - .lua53, .lua54 => return @enumFromInt(c.lua_rawgeti(lua.state, index, n)), + .lua53, .lua54, .luau => return @enumFromInt(c.lua_rawgeti(lua.state, index, n)), else => { c.lua_rawgeti(lua.state, index, n); return lua.typeOf(-1); @@ -1489,7 +1647,7 @@ pub const Lua = struct { } const RawSetIndexIType = switch (lang) { - .lua51, .lua52, .luajit => i32, + .lua51, .lua52, .luajit, .luau => i32, else => Integer, }; @@ -1512,7 +1670,13 @@ pub const Lua = struct { /// Sets the C function f as the new value of global name /// See https://www.lua.org/manual/5.4/manual.html#lua_register pub fn register(lua: *Lua, name: [:0]const u8, c_fn: CFn) void { - c.lua_register(lua.state, name.ptr, c_fn); + switch (lang) { + .luau => { + lua.pushFunction(c_fn, name); + lua.setGlobal(name); + }, + else => c.lua_register(lua.state, name.ptr, c_fn), + } } /// Removes the element at the given valid `index` shifting down elements to fill the gap @@ -1571,10 +1735,22 @@ pub const Lua = struct { } } + pub fn resumeThreadLuau(lua: *Lua, from: ?Lua, num_args: i32) !ResumeStatus { + const from_state = if (from) |from_val| from_val.state else null; + const thread_status = c.lua_resume(lua.state, from_state, num_args); + switch (thread_status) { + StatusCode.err_runtime => return error.Runtime, + StatusCode.err_memory => return error.Memory, + StatusCode.err_error => return error.MsgHandler, + else => return @enumFromInt(thread_status), + } + } + pub const resumeThread = switch (lang) { + .lua51, .luajit => resumeThread51, .lua52, .lua53 => resumeThread52, .lua54 => resumeThread54, - else => resumeThread51, + .luau => resumeThreadLuau, }; /// Rotates the stack elements between the valid `index` and the top of the stack @@ -1662,6 +1838,12 @@ pub const Lua = struct { c.lua_settop(lua.state, index); } + /// Set userdata tag at the given index + pub fn setUserdataTag(lua: *Lua, index: i32, tag: i32) void { + std.debug.assert((tag >= 0 and tag < c.LUA_UTAG_LIMIT)); // Luau will do the same assert, this is easier to debug + c.lua_setuserdatatag(lua.state, index, tag); + } + /// Sets the warning function to be used by Lua to emit warnings /// The `data` parameter sets the value `data` passed to the warning function /// See https://www.lua.org/manual/5.4/manual.html#lua_setwarnf @@ -1806,6 +1988,7 @@ pub const Lua = struct { if (c.lua_touserdata(lua.state, index)) |ptr| { const size = switch (lang) { .lua51, .luajit => lua.objectLen(index) / @sizeOf(T), + .luau => @as(u32, @intCast(lua.objectLen(index))) / @sizeOf(T), else => lua.rawLen(index) / @sizeOf(T), }; return @as([*]T, @ptrCast(@alignCast(ptr)))[0..size]; @@ -1813,6 +1996,45 @@ pub const Lua = struct { return error.Fail; } + pub fn toUserdataTagged(lua: *Lua, comptime T: type, index: i32, tag: i32) !*T { + if (c.lua_touserdatatagged(lua.state, index, tag)) |ptr| return opaqueCast(T, ptr); + return error.Fail; + } + + /// Converts the Lua value at the given `index` to a 3- or 4-vector. + /// The Lua value must be a vector. + pub fn toVector(lua: *Lua, index: i32) ![luau_vector_size]f32 { + const res = c.lua_tovector(lua.state, index); + if (res) |r| { + switch (luau_vector_size) { + 3 => return [_]f32{ r[0], r[1], r[2] }, + 4 => return [_]f32{ r[0], r[1], r[2], r[3] }, + else => @compileError("invalid luau_vector_size - should not happen"), + } + } + return error.Fail; + } + + /// Converts the Lua string at the given `index` to a string atom. + /// The Lua value must be a string. + pub fn toStringAtom(lua: *Lua, index: i32) !struct { i32, [:0]const u8 } { + var atom: c_int = undefined; + if (c.lua_tostringatom(lua.state, index, &atom)) |ptr| { + return .{ atom, std.mem.span(ptr) }; + } + return error.Fail; + } + + /// Retrieve the user atom index and name for the method being + /// invoked in a namecall. + pub fn namecallAtom(lua: *Lua) !struct { i32, [:0]const u8 } { + var atom: c_int = undefined; + if (c.lua_namecallatom(lua.state, &atom)) |ptr| { + return .{ atom, std.mem.span(ptr) }; + } + return error.Fail; + } + /// Returns the `LuaType` of the value at the given index /// Note that this is equivalent to lua_type but because type is a Zig primitive it is renamed to `typeOf` /// See https://www.lua.org/manual/5.4/manual.html#lua_type @@ -1864,17 +2086,15 @@ pub const Lua = struct { c.lua_xmove(lua.state, to.state, num); } - const YieldReturn = switch (lang) { - .lua51, .luajit => i32, - else => noreturn, - }; - /// This function is equivalent to `Lua.yieldCont()` but has no continuation /// This function never returns /// See https://www.lua.org/manual/5.4/manual.html#lua_yield - pub fn yield(lua: *Lua, num_results: i32) YieldReturn { + pub fn yield(lua: *Lua, num_results: i32) switch (lang) { + .lua51, .luajit, .luau => i32, + else => noreturn, + } { switch (lang) { - .lua51, .luajit => return c.lua_yield(lua.state, num_results), + .lua51, .luajit, .luau => return c.lua_yield(lua.state, num_results), else => { _ = c.lua_yield(lua.state, num_results); unreachable; @@ -1923,11 +2143,7 @@ pub const Lua = struct { return HookMask.fromInt(c.lua_gethookmask(lua.state)); } - /// Gets information about a specific function or function invocation - /// Returns an error if an invalid option was given, but the valid options - /// are still handled - /// See https://www.lua.org/manual/5.4/manual.html#lua_getinfo - pub fn getInfo(lua: *Lua, options: DebugInfo.Options, info: *DebugInfo) void { + fn getInfoLua(lua: *Lua, options: DebugInfo.Options, info: *DebugInfo) void { const str = options.toString(); var ar: Debug = undefined; @@ -1983,10 +2199,43 @@ pub const Lua = struct { } } - /// Gets information about a local variable - /// Returns the name of the local variable - /// See https://www.lua.org/manual/5.4/manual.html#lua_getlocal - pub fn getLocal(lua: *Lua, info: *DebugInfo, n: i32) ![:0]const u8 { + fn getInfoLuau(lua: *Lua, level: i32, options: DebugInfo.Options, info: *DebugInfo) void { + const str = options.toString(); + + var ar: Debug = undefined; + + // should never fail because we are controlling options with the struct param + _ = c.lua_getinfo(lua.state, level, &str, &ar); + // std.debug.assert( != 0); + + // copy data into a struct + if (options.l) info.current_line = if (ar.currentline == -1) null else ar.currentline; + if (options.n) { + info.name = if (ar.name != null) std.mem.span(ar.name) else null; + } + if (options.s) { + info.source = std.mem.span(ar.source); + // TODO: short_src figureit out + @memcpy(&info.short_src, ar.short_src[0..c.LUA_IDSIZE]); + info.first_line_defined = ar.linedefined; + info.what = blk: { + const what = std.mem.span(ar.what); + if (std.mem.eql(u8, "Lua", what)) break :blk .lua; + if (std.mem.eql(u8, "C", what)) break :blk .c; + if (std.mem.eql(u8, "main", what)) break :blk .main; + if (std.mem.eql(u8, "tail", what)) break :blk .tail; + unreachable; + }; + } + } + + /// Gets information about a specific function or function invocation + /// Returns an error if an invalid option was given, but the valid options + /// are still handled + /// See https://www.lua.org/manual/5.4/manual.html#lua_getinfo + pub const getInfo = if (lang == .luau) getInfoLuau else getInfoLua; + + fn getLocalLua(lua: *Lua, info: *DebugInfo, n: i32) ![:0]const u8 { var ar: Debug = undefined; switch (lang) { @@ -2000,6 +2249,18 @@ pub const Lua = struct { return error.Fail; } + fn getLocalLuau(lua: *Lua, level: i32, n: i32) ![:0]const u8 { + if (c.lua_getlocal(lua.state, level, n)) |name| { + return std.mem.span(name); + } + return error.Fail; + } + + /// Gets information about a local variable + /// Returns the name of the local variable + /// See https://www.lua.org/manual/5.4/manual.html#lua_getlocal + pub const getLocal = if (lang == .luau) getLocalLuau else getLocalLua; + /// Gets information about the interpreter runtime stack /// See https://www.lua.org/manual/5.4/manual.html#lua_getstack pub fn getStack(lua: *Lua, level: i32) !DebugInfo { @@ -2030,11 +2291,7 @@ pub const Lua = struct { _ = c.lua_sethook(lua.state, hook_fn, hook_mask, count); } - /// Sets the value of a local variable - /// Returns an error when the index is greater than the number of active locals - /// Returns the name of the local variable - /// See https://www.lua.org/manual/5.4/manual.html#lua_setlocal - pub fn setLocal(lua: *Lua, info: *DebugInfo, n: i32) ![:0]const u8 { + fn setLocalLua(lua: *Lua, info: *DebugInfo, n: i32) ![:0]const u8 { var ar: Debug = undefined; switch (lang) { @@ -2048,6 +2305,19 @@ pub const Lua = struct { return error.Fail; } + fn setLocalLuau(lua: *Lua, level: i32, n: i32) ![:0]const u8 { + if (c.lua_setlocal(lua.state, level, n)) |name| { + return std.mem.span(name); + } + return error.Fail; + } + + /// Sets the value of a local variable + /// Returns an error when the index is greater than the number of active locals + /// Returns the name of the local variable + /// See https://www.lua.org/manual/5.4/manual.html#lua_setlocal + pub const setLocal = if (lang == .luau) setLocalLuau else setLocalLua; + /// Sets the value of a closure's upvalue /// Returns the name of the upvalue or an error if the upvalue does not exist /// See https://www.lua.org/manual/5.4/manual.html#lua_setupvalue @@ -2058,6 +2328,12 @@ pub const Lua = struct { return error.Fail; } + pub fn setUserAtomCallbackFn(lua: *Lua, cb: CUserAtomCallbackFn) void { + if (c.lua_callbacks(lua.state)) |cb_struct| { + cb_struct.*.useratom = cb; + } + } + /// Returns a unique identifier for the upvalue numbered `n` from the closure index `func_index` /// See https://www.lua.org/manual/5.4/manual.html#lua_upvalueid pub fn upvalueId(lua: *Lua, func_index: i32, n: i32) !*anyopaque { @@ -2198,6 +2474,7 @@ pub const Lua = struct { const ptr = c.luaL_checkudata(lua.state, arg, name.ptr).?; const size = switch (lang) { .lua51, .luajit => lua.objectLen(arg) / @sizeOf(T), + .luau => @as(u32, @intCast(lua.objectLen(arg))) / @sizeOf(T), else => lua.rawLen(arg) / @sizeOf(T), }; return @as([*]T, @ptrCast(@alignCast(ptr)))[0..size]; @@ -2209,6 +2486,14 @@ pub const Lua = struct { return c.luaL_checkunsigned(lua.state, arg); } + /// Checks whether the function argument `arg` is a vector and returns the vector as a floating point slice. + pub fn checkVector(lua: *Lua, arg: i32) [luau_vector_size]f32 { + const vec = lua.toVector(arg) catch { + lua.typeError(arg, lua.typeName(LuaType.vector)); + }; + return vec; + } + fn checkVersion52(lua: *Lua) void { return c.luaL_checkversion_(lua.state, c.LUA_VERSION_NUM); } @@ -2244,7 +2529,11 @@ pub const Lua = struct { /// Raises an error /// See https://www.lua.org/manual/5.4/manual.html#luaL_error pub fn raiseErrorStr(lua: *Lua, fmt: [:0]const u8, args: anytype) noreturn { - _ = @call(.auto, c.luaL_error, .{ lua.state, fmt.ptr } ++ args); + _ = @call( + .auto, + if (lang == .luau) c.luaL_errorL else c.luaL_error, + .{ lua.state, fmt.ptr } ++ args, + ); unreachable; } @@ -2276,7 +2565,7 @@ pub const Lua = struct { /// See https://www.lua.org/manual/5.4/manual.html#luaL_getmetatable pub fn getMetatableRegistry(lua: *Lua, table_name: [:0]const u8) LuaType { switch (lang) { - .lua53, .lua54 => return @enumFromInt(c.luaL_getmetatable(lua.state, table_name.ptr)), + .lua53, .lua54, .luau => return @enumFromInt(c.luaL_getmetatable(lua.state, table_name.ptr)), else => { c.luaL_getmetatable(lua.state, table_name.ptr); return lua.typeOf(-1); @@ -2334,6 +2623,13 @@ pub const Lua = struct { else => loadBuffer52, }; + /// Loads bytecode binary (as compiled with f.ex. 'luau-compile --binary') + /// See https://luau-lang.org/getting-started + /// See also condsiderations for binary bytecode compatibility/safety: https://github.com/luau-lang/luau/issues/493#issuecomment-1185054665 + pub fn loadBytecode(lua: *Lua, chunkname: [:0]const u8, bytecode: []const u8) !void { + if (c.luau_load(lua.state, chunkname.ptr, bytecode.ptr, bytecode.len, 0) != 0) return error.Fail; + } + fn loadFile51(lua: *Lua, file_name: [:0]const u8) !void { const ret = c.luaL_loadfile(lua.state, file_name.ptr); switch (ret) { @@ -2372,15 +2668,30 @@ pub const Lua = struct { /// Loads a string as a Lua chunk /// See https://www.lua.org/manual/5.4/manual.html#luaL_loadstring pub fn loadString(lua: *Lua, str: [:0]const u8) !void { - const ret = c.luaL_loadstring(lua.state, str.ptr); - switch (ret) { - StatusCode.ok => return, - StatusCode.err_syntax => return error.Syntax, - StatusCode.err_memory => return error.Memory, - // loadstring runs lua_load which runs pcall, so can also return any result of an pcall error - StatusCode.err_runtime => return error.Runtime, - StatusCode.err_error => return error.MsgHandler, - else => unreachable, + switch (lang) { + .luau => { + var size: usize = 0; + const bytecode = c.luau_compile(str.ptr, str.len, null, &size); + + // Failed to allocate memory for the out buffer + if (bytecode == null) return error.Memory; + + // luau_compile uses malloc to allocate the bytecode on the heap + defer zig_luau_free(bytecode); + try lua.loadBytecode("...", bytecode[0..size]); + }, + else => { + const ret = c.luaL_loadstring(lua.state, str.ptr); + switch (ret) { + StatusCode.ok => return, + StatusCode.err_syntax => return error.Syntax, + StatusCode.err_memory => return error.Memory, + // loadstring runs lua_load which runs pcall, so can also return any result of an pcall error + StatusCode.err_runtime => return error.Runtime, + StatusCode.err_error => return error.MsgHandler, + else => unreachable, + } + }, } } @@ -2410,6 +2721,8 @@ pub const Lua = struct { /// Creates a new Lua state with an allocator using the default libc allocator /// See https://www.lua.org/manual/5.4/manual.html#luaL_newstate pub fn newStateLibc() !Lua { + if (lang == .luau) zig_registerAssertionHandler(); + const state = c.luaL_newstate() orelse return error.Memory; return Lua{ .state = state }; } @@ -2474,7 +2787,7 @@ pub const Lua = struct { /// Pops the object /// See https://www.lua.org/manual/5.4/manual.html#luaL_ref pub fn ref(lua: *Lua, index: i32) !i32 { - const ret = c.luaL_ref(lua.state, index); + const ret = if (lang == .luau) c.lua_ref(lua.state, index) else c.luaL_ref(lua.state, index); return if (ret == ref_nil) error.Fail else ret; } @@ -2489,7 +2802,10 @@ pub const Lua = struct { if (!lua.isTable(-1)) { lua.pop(1); if (c.luaL_findtable(lua.state, globals_index, name, @intCast(funcs.len))) |_| { - lua.raiseErrorStr("name conflict for module " ++ c.LUA_QS, .{name.ptr}); + switch (lang) { + .luau => lua.raiseErrorStr("name conflict for module '%s'", .{name.ptr}), + else => lua.raiseErrorStr("name conflict for module " ++ c.LUA_QS, .{name.ptr}), + } } lua.pushValue(-1); lua.setField(-3, name); @@ -2499,7 +2815,10 @@ pub const Lua = struct { } for (funcs) |f| { // TODO: handle null functions - lua.pushFunction(f.func.?); + switch (lang) { + .luau => lua.pushFunction(f.func.?, f.name), + else => lua.pushFunction(f.func.?), + } lua.setField(-2, f.name); } } @@ -2508,14 +2827,19 @@ pub const Lua = struct { /// as an argument and sets the call result to package.loaded[`mod_name`] /// See https://www.lua.org/manual/5.4/manual.html#luaL_requiref pub fn requireF(lua: *Lua, mod_name: [:0]const u8, open_fn: CFn, global: bool) void { - if (lang == .lua51 or lang == .luajit) { - lua.pushFunction(open_fn); - lua.pushString(mod_name); - lua.call(1, 0); - return; - } + switch (lang) { + .lua51, .luajit, .luau => { + if (lang == .luau) { + lua.pushFunction(open_fn, mod_name); + } else { + lua.pushFunction(open_fn); + } - c.luaL_requiref(lua.state, mod_name.ptr, open_fn, @intFromBool(global)); + _ = lua.pushString(mod_name); + lua.call(1, 0); + }, + else => c.luaL_requiref(lua.state, mod_name.ptr, open_fn, @intFromBool(global)), + } } /// Registers all functions in the array `fns` into the table on the top of the stack @@ -2586,12 +2910,18 @@ pub const Lua = struct { return std.mem.span(c.luaL_typename(lua.state, index)); } - /// Releases the reference `r` from the table at index `index` - /// See https://www.lua.org/manual/5.4/manual.html#luaL_unref - pub fn unref(lua: *Lua, index: i32, r: i32) void { + fn unrefLua(lua: *Lua, index: i32, r: i32) void { c.luaL_unref(lua.state, index, r); } + fn unrefLuau(lua: *Lua, r: i32) void { + c.lua_unref(lua.state, r); + } + + /// Releases the reference `r` from the table at index `index` + /// See https://www.lua.org/manual/5.4/manual.html#luaL_unref + pub const unref = if (lang == .luau) unrefLuau else unrefLua; + /// Pushes onto the stack a string identifying the current position of the control /// at the call stack `level` /// See https://www.lua.org/manual/5.4/manual.html#luaL_where @@ -2607,14 +2937,17 @@ pub const Lua = struct { /// See https://www.lua.org/manual/5.4/manual.html#luaL_openlibs pub fn open(lua: *Lua, libs: Libs) void { if (libs.base) lua.requireF("_G", c.luaopen_base, true); - if (libs.package) lua.requireF(c.LUA_LOADLIBNAME, c.luaopen_package, true); if (libs.string) lua.requireF(c.LUA_STRLIBNAME, c.luaopen_string, true); if (libs.table) lua.requireF(c.LUA_TABLIBNAME, c.luaopen_table, true); if (libs.math) lua.requireF(c.LUA_MATHLIBNAME, c.luaopen_math, true); - if (libs.io) lua.requireF(c.LUA_IOLIBNAME, c.luaopen_io, true); if (libs.os) lua.requireF(c.LUA_OSLIBNAME, c.luaopen_os, true); if (libs.debug) lua.requireF(c.LUA_DBLIBNAME, c.luaopen_debug, true); + if (lang != .luau) { + if (libs.io) lua.requireF(c.LUA_IOLIBNAME, c.luaopen_io, true); + if (libs.package) lua.requireF(c.LUA_LOADLIBNAME, c.luaopen_package, true); + } + if (lang != .lua51 and lang != .luajit and libs.coroutine) lua.requireF(c.LUA_COLIBNAME, c.luaopen_coroutine, true); if ((lang == .lua53 or lang == .lua54) and libs.utf8) lua.requireF(c.LUA_UTF8LIBNAME, c.luaopen_utf8, true); @@ -2704,7 +3037,7 @@ pub const Buffer = struct { /// Internal Lua type for a string buffer pub const LuaBuffer = c.luaL_Buffer; - pub const buffer_size = c.LUAL_BUFFERSIZE; + pub const buffer_size = if (lang == .luau) c.LUA_BUFFERSIZE else c.LUAL_BUFFERSIZE; /// Adds `byte` to the buffer /// See https://www.lua.org/manual/5.4/manual.html#luaL_addchar @@ -2713,7 +3046,7 @@ pub const Buffer = struct { var lua_buf = &buf.b; switch (lang) { - .lua51, .luajit => { + .lua51, .luajit, .luau => { if (lua_buf.p > &lua_buf.buffer[buffer_size - 1]) _ = buf.prep(); lua_buf.p.* = byte; lua_buf.p += 1; @@ -2746,7 +3079,7 @@ pub const Buffer = struct { var lua_buf = &buf.b; switch (lang) { - .lua51, .luajit => lua_buf.p += length, + .lua51, .luajit, .luau => lua_buf.p += length, else => lua_buf.n += length, } } @@ -2754,7 +3087,10 @@ pub const Buffer = struct { /// Adds the zero-terminated string pointed to by `str` to the buffer /// See https://www.lua.org/manual/5.4/manual.html#luaL_addstring pub fn addString(buf: *Buffer, str: [:0]const u8) void { - c.luaL_addstring(&buf.b, str.ptr); + switch (lang) { + .luau => c.luaL_addlstring(&buf.b, str.ptr, str.len), + else => c.luaL_addstring(&buf.b, str.ptr), + } } /// Adds the value on the top of the stack to the buffer @@ -2764,6 +3100,11 @@ pub const Buffer = struct { c.luaL_addvalue(&buf.b); } + /// Adds the value at the given index to the buffer + pub fn addValueAny(buf: *Buffer, idx: i32) void { + c.luaL_addvalueany(&buf.b, idx); + } + /// Returns a slice of the current content of the buffer /// Any changes to the buffer may invalidate this slice /// See https://www.lua.org/manual/5.4/manual.html#luaL_buffaddr @@ -2791,7 +3132,10 @@ pub const Buffer = struct { /// Equivalent to prepSize with a buffer size of Buffer.buffer_size /// See https://www.lua.org/manual/5.4/manual.html#luaL_prepbuffer pub fn prep(buf: *Buffer) []u8 { - return c.luaL_prepbuffer(&buf.b)[0..buffer_size]; + return switch (lang) { + .luau => c.luaL_prepbuffsize(&buf.b, buffer_size)[0..buffer_size], + else => c.luaL_prepbuffer(&buf.b)[0..buffer_size], + }; } /// Returns an address to a space of `size` where you can copy a string @@ -2827,6 +3171,8 @@ pub const ZigFn = fn (lua: *Lua) i32; pub const ZigHookFn = fn (lua: *Lua, event: Event, info: *DebugInfo) void; pub const ZigContFn = fn (lua: *Lua, status: Status, ctx: Context) i32; pub const ZigReaderFn = fn (lua: *Lua, data: *anyopaque) ?[]const u8; +pub const ZigUserdataDtorFn = fn (data: *anyopaque) void; +pub const ZigUserAtomCallbackFn = fn (str: []const u8) i16; pub const ZigWarnFn = fn (data: ?*anyopaque, msg: []const u8, to_cont: bool) void; pub const ZigWriterFn = fn (lua: *Lua, buf: []const u8, data: *anyopaque) bool; @@ -2837,6 +3183,8 @@ fn TypeOfWrap(comptime T: type) type { ZigHookFn => CHookFn, ZigContFn => CContFn, ZigReaderFn => CReaderFn, + ZigUserdataDtorFn => CUserdataDtorFn, + ZigUserAtomCallbackFn => CUserAtomCallbackFn, ZigWarnFn => CWarnFn, ZigWriterFn => CWriterFn, else => @compileError("unsupported type given to wrap: '" ++ @typeName(T) ++ "'"), @@ -2853,6 +3201,8 @@ pub fn wrap(comptime value: anytype) TypeOfWrap(@TypeOf(value)) { ZigHookFn => wrapZigHookFn(value), ZigContFn => wrapZigContFn(value), ZigReaderFn => wrapZigReaderFn(value), + ZigUserdataDtorFn => wrapZigUserdataDtorFn(value), + ZigUserAtomCallbackFn => wrapZigUserAtomCallbackFn(value), ZigWarnFn => wrapZigWarnFn(value), ZigWriterFn => wrapZigWriterFn(value), else => @compileError("unsupported type given to wrap: '" ++ @typeName(T) ++ "'"), @@ -2915,6 +3265,28 @@ fn wrapZigReaderFn(comptime f: ZigReaderFn) CReaderFn { }.inner; } +/// Wrap a ZigFn in a CFn for passing to the API +fn wrapZigUserdataDtorFn(comptime f: ZigUserdataDtorFn) CUserdataDtorFn { + return struct { + fn inner(userdata: *anyopaque) callconv(.C) void { + return @call(.always_inline, f, .{userdata}); + } + }.inner; +} + +/// Wrap a ZigFn in a CFn for passing to the API +fn wrapZigUserAtomCallbackFn(comptime f: ZigUserAtomCallbackFn) CUserAtomCallbackFn { + return struct { + fn inner(str: [*c]const u8, len: usize) callconv(.C) i16 { + if (str) |s| { + const buf = s[0..len]; + return @call(.always_inline, f, .{buf}); + } + return -1; + } + }.inner; +} + /// Wrap a ZigWarnFn in a CWarnFn for passing to the API fn wrapZigWarnFn(comptime f: ZigWarnFn) CWarnFn { return struct { @@ -2941,10 +3313,45 @@ fn wrapZigWriterFn(comptime f: ZigWriterFn) CWriterFn { }.inner; } +/// Zig wrapper for Luau lua_CompileOptions that uses the same defaults as Luau if +/// no compile options is specified. +pub const CompileOptions = struct { + optimization_level: i32 = 1, + debug_level: i32 = 1, + coverage_level: i32 = 0, + /// global builtin to construct vectors; disabled by default (.) + vector_lib: ?[*:0]const u8 = null, + vector_ctor: ?[*:0]const u8 = null, + /// vector type name for type tables; disabled by default + vector_type: ?[*:0]const u8 = null, + /// null-terminated array of globals that are mutable; disables the import optimization for fields accessed through these + mutable_globals: ?[*:null]const ?[*:0]const u8 = null, +}; + +/// Compile luau source into bytecode, return callee owned buffer allocated through the given allocator. +pub fn compile(allocator: Allocator, source: []const u8, options: CompileOptions) ![]const u8 { + var size: usize = 0; + + var opts = c.lua_CompileOptions{ + .optimizationLevel = options.optimization_level, + .debugLevel = options.debug_level, + .coverageLevel = options.coverage_level, + .vectorLib = options.vector_lib, + .vectorCtor = options.vector_ctor, + .mutableGlobals = options.mutable_globals, + }; + const bytecode = c.luau_compile(source.ptr, source.len, &opts, &size); + if (bytecode == null) return error.Memory; + defer zig_luau_free(bytecode); + return try allocator.dupe(u8, bytecode[0..size]); +} + /// Export a Zig function to be used as a the entry point to a Lua module /// /// Exported as luaopen_[name] pub fn exportFn(comptime name: []const u8, comptime func: ZigFn) CFn { + if (lang == .luau) @compileError("Luau does not support compiling or loading shared modules"); + return struct { fn luaopen(state: ?*LuaState) callconv(.C) c_int { const declaration = comptime wrap(func); diff --git a/src/libluau.zig b/src/libluau.zig deleted file mode 100644 index e13a383..0000000 --- a/src/libluau.zig +++ /dev/null @@ -1,1683 +0,0 @@ -//! complete bindings around the Lua C API version 5.1.5 -//! exposes all Lua functionality, with additional Zig helper functions - -const std = @import("std"); - -const c = @cImport({ - @cInclude("lua.h"); - @cInclude("lualib.h"); - @cInclude("luacode.h"); -}); - -const config = @import("config"); -pub const lang = config.lang; - -/// The length of Luau vector values, either 3 or 4. -pub const luau_vector_size = if (config.luau_use_4_vector) 4 else 3; - -/// This function is defined in luau.cpp and must be called to define the assertion printer -extern "c" fn zig_registerAssertionHandler() void; - -/// This function is defined in luau.cpp and ensures Zig uses the correct free when compiling luau code -extern "c" fn zig_luau_free(ptr: *anyopaque) void; - -const Allocator = std.mem.Allocator; - -// Types -// -// Lua constants and types are declared below in alphabetical order -// For constants that have a logical grouping (like Operators), Zig enums are used for type safety - -/// The type of function that Lua uses for all internal allocations and frees -/// `data` is an opaque pointer to any data (the allocator), `ptr` is a pointer to the block being alloced/realloced/freed -/// `osize` is the original size or a code, and `nsize` is the new size -/// -/// See https://www.lua.org/manual/5.1/manual.html#lua_Alloc for more details -pub const AllocFn = *const fn (data: ?*anyopaque, ptr: ?*anyopaque, osize: usize, nsize: usize) callconv(.C) ?*anyopaque; - -/// Type for C functions -/// See https://www.lua.org/manual/5.1/manual.html#lua_CFunction for the protocol -pub const CFn = *const fn (state: ?*LuaState) callconv(.C) c_int; - -/// Type for C userdata destructors -pub const CUserdataDtorFn = *const fn (userdata: *anyopaque) callconv(.C) void; - -/// Type for C useratom callback -pub const CUserAtomCallbackFn = *const fn (str: [*c]const u8, len: usize) callconv(.C) i16; - -/// The internal Lua debug structure -/// See https://www.lua.org/manual/5.1/manual.html#lua_Debug -const Debug = c.lua_Debug; - -/// The Lua debug interface structure -pub const DebugInfo = struct { - source: [:0]const u8 = undefined, - src_len: usize = 0, - short_src: [c.LUA_IDSIZE:0]u8 = undefined, - - name: ?[:0]const u8 = undefined, - what: FnType = undefined, - - current_line: ?i32 = null, - first_line_defined: ?i32 = null, - - is_vararg: bool = false, - - pub const NameType = enum { global, local, method, field, upvalue, other }; - - pub const FnType = enum { lua, c, main, tail }; - - pub const Options = packed struct { - f: bool = false, - l: bool = false, - n: bool = false, - s: bool = false, - u: bool = false, - L: bool = false, - - fn toString(options: Options) [10:0]u8 { - var str = [_:0]u8{0} ** 10; - var index: u8 = 0; - - inline for (std.meta.fields(Options)) |field| { - if (@field(options, field.name)) { - str[index] = field.name[0]; - index += 1; - } - } - while (index < str.len) : (index += 1) str[index] = 0; - - return str; - } - }; -}; - -/// The superset of all errors returned from ziglua -pub const Error = error{ - /// A generic failure (used when a function can only fail in one way) - Fail, - /// A runtime error - Runtime, - /// A syntax error during precompilation - Syntax, - /// A memory allocation error - Memory, - /// An error while running the message handler - MsgHandler, - /// A file-releated error - File, -}; - -/// Type for arrays of functions to be registered -pub const FnReg = struct { - name: [:0]const u8, - func: CFn, -}; - -/// The index of the global environment table -pub const globals_index = c.LUA_GLOBALSINDEX; - -/// Type of integers in Lua (typically a ptrdiff_t) -pub const Integer = c.lua_Integer; - -/// Bitflag for the Lua standard libraries -pub const Libs = packed struct { - base: bool = false, - coroutine: bool = false, - package: bool = false, - string: bool = false, - utf8: bool = false, - table: bool = false, - math: bool = false, - io: bool = false, - os: bool = false, - debug: bool = false, -}; - -/// The type of the opaque structure that points to a thread and the state of a Lua interpreter -pub const LuaState = c.lua_State; - -/// Lua types -/// Must be a signed integer because LuaType.none is -1 -pub const LuaType = enum(i5) { - none = c.LUA_TNONE, - nil = c.LUA_TNIL, - boolean = c.LUA_TBOOLEAN, - light_userdata = c.LUA_TLIGHTUSERDATA, - number = c.LUA_TNUMBER, - vector = c.LUA_TVECTOR, - string = c.LUA_TSTRING, - table = c.LUA_TTABLE, - function = c.LUA_TFUNCTION, - userdata = c.LUA_TUSERDATA, - thread = c.LUA_TTHREAD, -}; - -/// The maximum integer value that `Integer` can store -pub const max_integer = c.LUA_MAXINTEGER; - -/// The minimum Lua stack available to a function -pub const min_stack = c.LUA_MINSTACK; - -/// Option for multiple returns in `Lua.protectedCall()` and `Lua.call()` -pub const mult_return = c.LUA_MULTRET; - -/// Type of floats in Lua (typically an f64) -pub const Number = c.lua_Number; - -/// The type of the reader function used by `Lua.load()` -pub const CReaderFn = *const fn (state: ?*LuaState, data: ?*anyopaque, size: [*c]usize) callconv(.C) [*c]const u8; - -/// The possible status of a call to `Lua.resumeThread` -pub const ResumeStatus = enum(u1) { - ok = StatusCode.ok, - yield = StatusCode.yield, -}; - -/// Reference constants -pub const ref_nil = c.LUA_REFNIL; -pub const ref_no = c.LUA_NOREF; - -/// Index of the regsitry in the stack (pseudo-index) -pub const registry_index = c.LUA_REGISTRYINDEX; - -/// Index of the main thread in the registry -pub const ridx_mainthread = c.LUA_RIDX_MAINTHREAD; - -/// Status that a thread can be in -/// Usually errors are reported by a Zig error rather than a status enum value -pub const Status = enum(u3) { - ok = StatusCode.ok, - yield = StatusCode.yield, - err_runtime = StatusCode.err_runtime, - err_syntax = StatusCode.err_syntax, - err_memory = StatusCode.err_memory, - err_error = StatusCode.err_error, -}; - -/// Status codes -/// Not public, because typically Status.ok is returned from a function implicitly; -/// Any function that returns an error usually returns a Zig error, and a void return -/// is an implicit Status.ok. -/// In the rare case that the status code is required from a function, an enum is -/// used for that specific function's return type -const StatusCode = struct { - // Lua 5.1 doesn't have an explicit variable, but 0 represents OK - pub const ok = 0; - pub const yield = c.LUA_YIELD; - pub const err_runtime = c.LUA_ERRRUN; - pub const err_syntax = c.LUA_ERRSYNTAX; - pub const err_memory = c.LUA_ERRMEM; - pub const err_error = c.LUA_ERRERR; -}; - -// Only used in loadFileX, so no need to group with Status -pub const err_file = c.LUA_ERRFILE; - -/// The standard representation for file handles used by the standard IO library -pub const Stream = c.luaL_Stream; - -/// The type of the writer function used by `Lua.dump()` -pub const CWriterFn = *const fn (state: ?*LuaState, buf: ?*const anyopaque, size: usize, data: ?*anyopaque) callconv(.C) c_int; - -/// A Zig wrapper around the Lua C API -/// Represents a Lua state or thread and contains the entire state of the Lua interpreter -pub const Lua = struct { - state: *LuaState, - - const alignment = @alignOf(std.c.max_align_t); - - /// Allows Lua to allocate memory using a Zig allocator passed in via data. - /// See https://www.lua.org/manual/5.1/manual.html#lua_Alloc for more details - fn alloc(data: ?*anyopaque, ptr: ?*anyopaque, osize: usize, nsize: usize) callconv(.C) ?*align(alignment) anyopaque { - // just like malloc() returns a pointer "which is suitably aligned for any built-in type", - // the memory allocated by this function should also be aligned for any type that Lua may - // desire to allocate. use the largest alignment for the target - const allocator_ptr = opaqueCast(Allocator, data.?); - - if (@as(?[*]align(alignment) u8, @ptrCast(@alignCast(ptr)))) |prev_ptr| { - const prev_slice = prev_ptr[0..osize]; - - // when nsize is zero the allocator must behave like free and return null - if (nsize == 0) { - allocator_ptr.free(prev_slice); - return null; - } - - // when nsize is not zero the allocator must behave like realloc - const new_ptr = allocator_ptr.realloc(prev_slice, nsize) catch return null; - return new_ptr.ptr; - } else if (nsize == 0) { - return null; - } else { - // ptr is null, allocate a new block of memory - const new_ptr = allocator_ptr.alignedAlloc(u8, alignment, nsize) catch return null; - return new_ptr.ptr; - } - } - - /// Initialize a Lua state with the given allocator - /// See https://www.lua.org/manual/5.1/manual.html#lua_newstate - pub fn init(allocator_ptr: *const Allocator) !Lua { - zig_registerAssertionHandler(); - - // @constCast() is safe here because Lua does not mutate the pointer internally - const state = c.lua_newstate(alloc, @constCast(allocator_ptr)) orelse return error.Memory; - return Lua{ .state = state }; - } - - /// Deinitialize a Lua state and free all memory - pub fn deinit(lua: *Lua) void { - lua.close(); - } - - /// Returns the std.mem.Allocator used to initialize this Lua state - /// - /// This function is not safe to use on Lua states created without a Zig allocator. - /// If the user data passed to Lua was null, this function will panic. Otherwise use - /// of the returned Allocator is undefined and will likely cause a segfault. - pub fn allocator(lua: *Lua) Allocator { - var data: ?*Allocator = undefined; - _ = lua.getAllocFn(@ptrCast(&data)); - - if (data) |allocator_ptr| { - // Although the Allocator is passed to Lua as a pointer, return a - // copy to make use more convenient. - return allocator_ptr.*; - } - - @panic("Lua.allocator() invalid on Lua states created without a Zig allocator"); - } - - // Library functions - // - // Library functions are included in alphabetical order. - // Each is kept similar to the original C API function while also making it easy to use from Zig - - /// Calls a function (or any callable value) - /// First push the function to be called onto the stack. Then push any arguments onto the stack. - /// Then call this function. All arguments and the function value are popped, and any results - /// are pushed onto the stack. - /// See https://www.lua.org/manual/5.1/manual.html#lua_call - pub fn call(lua: *Lua, num_args: i32, num_results: i32) void { - c.lua_call(lua.state, num_args, num_results); - } - - /// Ensures that the stack has space for at least n extra arguments - /// Returns an error if more stack space cannot be allocated - /// Never shrinks the stack - /// See https://www.lua.org/manual/5.1/manual.html#lua_checkstack - pub fn checkStack(lua: *Lua, n: i32) !void { - if (c.lua_checkstack(lua.state, n) == 0) return error.Fail; - } - - /// Release all Lua objects in the state and free all dynamic memory - /// See https://www.lua.org/manual/5.1/manual.html#lua_close - pub fn close(lua: *Lua) void { - c.lua_close(lua.state); - } - - /// Concatenates the n values at the top of the stack, pops them, and leaves the result at the top - /// If the number of values is 1, the result is a single value on the stack (nothing changes) - /// If the number of values is 0, the result is the empty string - /// See https://www.lua.org/manual/5.1/manual.html#lua_concat - pub fn concat(lua: *Lua, n: i32) void { - c.lua_concat(lua.state, n); - } - - /// Creates a new empty table and pushes onto the stack - /// num_arr is a hint for how many elements the table will have as a sequence - /// num_rec is a hint for how many other elements the table will have - /// Lua may preallocate memory for the table based on the hints - /// See https://www.lua.org/manual/5.1/manual.html#lua_createtable - pub fn createTable(lua: *Lua, num_arr: i32, num_rec: i32) void { - c.lua_createtable(lua.state, num_arr, num_rec); - } - - pub fn setReadonly(lua: *Lua, idx: i32, enabled: bool) void { - c.lua_setreadonly(lua.state, idx, @intFromBool(enabled)); - } - - pub fn getReadonly(lua: *Lua, idx: i32) bool { - return c.lua_getreadonly(lua.state, idx) != 0; - } - - /// Returns true if the two values at the indexes are equal following the semantics of the - /// Lua == operator. - /// See https://www.lua.org/manual/5.1/manual.html#lua_equal - pub fn equal(lua: *Lua, index1: i32, index2: i32) bool { - return c.lua_equal(lua.state, index1, index2) == 1; - } - - /// Raises a Lua error using the value at the top of the stack as the error object - /// Does a longjump and therefore never returns - /// See https://www.lua.org/manual/5.1/manual.html#lua_error - pub fn raiseError(lua: *Lua) noreturn { - _ = c.lua_error(lua.state); - unreachable; - } - - /// Perform a full garbage-collection cycle - /// See https://www.lua.org/manual/5.1/manual.html#lua_gc - pub fn gcCollect(lua: *Lua) void { - _ = c.lua_gc(lua.state, c.LUA_GCCOLLECT, 0); - } - - /// Stops the garbage collector - /// See https://www.lua.org/manual/5.1/manual.html#lua_gc - pub fn gcStop(lua: *Lua) void { - _ = c.lua_gc(lua.state, c.LUA_GCSTOP, 0); - } - - /// Restarts the garbage collector - /// See https://www.lua.org/manual/5.1/manual.html#lua_gc - pub fn gcRestart(lua: *Lua) void { - _ = c.lua_gc(lua.state, c.LUA_GCRESTART, 0); - } - - /// Performs an incremental step of garbage collection corresponding to the allocation of step_size Kbytes - /// See https://www.lua.org/manual/5.1/manual.html#lua_gc - pub fn gcStep(lua: *Lua) void { - _ = c.lua_gc(lua.state, c.LUA_GCSTEP, 0); - } - - /// Returns the current amount of memory (in Kbytes) in use by Lua - /// See https://www.lua.org/manual/5.1/manual.html#lua_gc - pub fn gcCount(lua: *Lua) i32 { - return c.lua_gc(lua.state, c.LUA_GCCOUNT, 0); - } - - /// Returns the remainder of dividing the current amount of bytes of memory in use by Lua by 1024 - /// See https://www.lua.org/manual/5.1/manual.html#lua_gc - pub fn gcCountB(lua: *Lua) i32 { - return c.lua_gc(lua.state, c.LUA_GCCOUNTB, 0); - } - - /// Sets `multiplier` as the new value for the step multiplier of the collector - /// Returns the previous value of the step multiplier - /// See https://www.lua.org/manual/5.1/manual.html#lua_gc - pub fn gcSetStepMul(lua: *Lua, multiplier: i32) i32 { - return c.lua_gc(lua.state, c.LUA_GCSETSTEPMUL, multiplier); - } - - pub fn gcIsRunning(lua: *Lua) bool { - return c.lua_gc(lua.state, c.LUA_GCISRUNNING, 0) == 1; - } - - pub fn gcSetGoal(lua: *Lua, goal: i32) i32 { - return c.lua_gc(lua.state, c.LUA_GCSETGOAL, goal); - } - - pub fn gcSetStepSize(lua: *Lua, size: i32) i32 { - return c.lua_gc(lua.state, c.LUA_GCSETSTEPSIZE, size); - } - - /// Returns the memory allocation function of a given state - /// If data is not null, it is set to the opaque pointer given when the allocator function was set - /// See https://www.lua.org/manual/5.1/manual.html#lua_getallocf - pub fn getAllocFn(lua: *Lua, data: ?**anyopaque) AllocFn { - // Assert cannot be null because it is impossible (and not useful) to pass null - // to the functions that set the allocator (setallocf and newstate) - return c.lua_getallocf(lua.state, @ptrCast(data)).?; - } - - /// Pushes onto the stack the environment table of the value at the given index. - /// See https://www.lua.org/manual/5.1/manual.html#lua_getfenv - pub fn getFnEnvironment(lua: *Lua, index: i32) void { - c.lua_getfenv(lua.state, index); - } - - /// Pushes onto the stack the value t[key] where t is the value at the given index - /// See https://www.lua.org/manual/5.1/manual.html#lua_getfield - pub fn getField(lua: *Lua, index: i32, key: [:0]const u8) LuaType { - return @enumFromInt(c.lua_getfield(lua.state, index, key.ptr)); - } - - /// Pushes onto the stack the value of the global name - /// See https://www.lua.org/manual/5.1/manual.html#lua_getglobal - pub fn getGlobal(lua: *Lua, name: [:0]const u8) !LuaType { - const lua_type: LuaType = @enumFromInt(c.lua_getglobal(lua.state, name.ptr)); - if (lua_type == .nil) return error.Fail; - return lua_type; - } - - /// If the value at the given index has a metatable, the function pushes that metatable onto the stack - /// Otherwise an error is returned - /// See https://www.lua.org/manual/5.1/manual.html#lua_getmetatable - pub fn getMetatable(lua: *Lua, index: i32) !void { - if (c.lua_getmetatable(lua.state, index) == 0) return error.Fail; - } - - /// Pushes onto the stack the value t[k] where t is the value at the given index and k is the value on the top of the stack - /// See https://www.lua.org/manual/5.1/manual.html#lua_gettable - pub fn getTable(lua: *Lua, index: i32) LuaType { - return @enumFromInt(c.lua_gettable(lua.state, index)); - } - - /// Returns the index of the top element in the stack - /// Because indices start at 1, the result is also equal to the number of elements in the stack - /// See https://www.lua.org/manual/5.1/manual.html#lua_gettop - pub fn getTop(lua: *Lua) i32 { - return c.lua_gettop(lua.state); - } - - /// Moves the top element into the given valid `index` shifting up any elements to make room - /// See https://www.lua.org/manual/5.1/manual.html#lua_insert - pub fn insert(lua: *Lua, index: i32) void { - // translate-c cannot translate this macro correctly - c.lua_insert(lua.state, index); - } - - /// Returns true if the value at the given index is a boolean - /// See https://www.lua.org/manual/5.1/manual.html#lua_isboolean - pub fn isBoolean(lua: *Lua, index: i32) bool { - return c.lua_isboolean(lua.state, index); - } - - /// Returns true if the value at the given index is a CFn - /// See https://www.lua.org/manual/5.1/manual.html#lua_iscfunction - pub fn isCFunction(lua: *Lua, index: i32) bool { - return c.lua_iscfunction(lua.state, index) != 0; - } - - /// Returns true if the value at the given index is a function (C or Lua) - /// See https://www.lua.org/manual/5.1/manual.html#lua_isfunction - pub fn isFunction(lua: *Lua, index: i32) bool { - return c.lua_isfunction(lua.state, index); - } - - /// Returns true if the value at the given index is a light userdata - /// See https://www.lua.org/manual/5.1/manual.html#lua_islightuserdata - pub fn isLightUserdata(lua: *Lua, index: i32) bool { - return c.lua_islightuserdata(lua.state, index); - } - - /// Returns true if the value at the given index is nil - /// See https://www.lua.org/manual/5.1/manual.html#lua_isnil - pub fn isNil(lua: *Lua, index: i32) bool { - return c.lua_isnil(lua.state, index); - } - - /// Returns true if the given index is not valid - /// See https://www.lua.org/manual/5.1/manual.html#lua_isnone - pub fn isNone(lua: *Lua, index: i32) bool { - return c.lua_isnone(lua.state, index); - } - - /// Returns true if the given index is not valid or if the value at the index is nil - /// See https://www.lua.org/manual/5.1/manual.html#lua_isnoneornil - pub fn isNoneOrNil(lua: *Lua, index: i32) bool { - return c.lua_isnoneornil(lua.state, index); - } - - /// Returns true if the value at the given index is a number - /// See https://www.lua.org/manual/5.1/manual.html#lua_isnumber - pub fn isNumber(lua: *Lua, index: i32) bool { - return c.lua_isnumber(lua.state, index) != 0; - } - - /// Returns true if the value at the given index is a string - /// See https://www.lua.org/manual/5.1/manual.html#lua_isstring - pub fn isString(lua: *Lua, index: i32) bool { - return c.lua_isstring(lua.state, index) != 0; - } - - /// Returns true if the value at the given index is a table - /// See https://www.lua.org/manual/5.1/manual.html#lua_istable - pub fn isTable(lua: *Lua, index: i32) bool { - return c.lua_istable(lua.state, index); - } - - /// Returns true if the value at the given index is a thread - /// See https://www.lua.org/manual/5.1/manual.html#lua_isthread - pub fn isThread(lua: *Lua, index: i32) bool { - return c.lua_isthread(lua.state, index); - } - - /// Returns true if the value at the given index is a userdata (full or light) - /// See https://www.lua.org/manual/5.1/manual.html#lua_isuserdata - pub fn isUserdata(lua: *Lua, index: i32) bool { - return c.lua_isuserdata(lua.state, index) != 0; - } - - /// Returns true if the value at the given index is a vector - pub fn isVector(lua: *Lua, index: i32) bool { - return c.lua_isvector(lua.state, index); - } - - /// Returns true if the value at index1 is smaller than the value at index2, following the - /// semantics of the Lua < operator. - /// See https://www.lua.org/manual/5.4/manual.html#lua_lessthan - pub fn lessThan(lua: *Lua, index1: i32, index2: i32) bool { - return c.lua_lessthan(lua.state, index1, index2) == 1; - } - - /// Creates a new independent state and returns its main thread - /// See https://www.lua.org/manual/5.1/manual.html#lua_newstate - pub fn newState(alloc_fn: AllocFn, data: ?*const anyopaque) !Lua { - zig_registerAssertionHandler(); - const state = c.lua_newstate(alloc_fn, @constCast(data)) orelse return error.Memory; - return Lua{ .state = state }; - } - - /// Creates a new empty table and pushes it onto the stack - /// Equivalent to createTable(0, 0) - /// See https://www.lua.org/manual/5.1/manual.html#lua_newtable - pub fn newTable(lua: *Lua) void { - c.lua_newtable(lua.state); - } - - /// Creates a new thread, pushes it on the stack, and returns a Lua state that represents the new thread - /// The new thread shares the global environment but has a separate execution stack - /// See https://www.lua.org/manual/5.1/manual.html#lua_newthread - pub fn newThread(lua: *Lua) Lua { - const state = c.lua_newthread(lua.state).?; - return .{ .state = state }; - } - - /// This function allocates a new userdata of the given type. - /// Returns a pointer to the Lua-owned data - /// See https://www.lua.org/manual/5.1/manual.html#lua_newuserdata - pub fn newUserdata(lua: *Lua, comptime T: type) *T { - // safe to .? because this function throws a Lua error on out of memory - // so the returned pointer should never be null - const ptr = c.lua_newuserdata(lua.state, @sizeOf(T)).?; - return opaqueCast(T, ptr); - } - - /// This function creates and pushes a slice of full userdata onto the stack. - /// Returns a slice to the Lua-owned data. - /// See https://www.lua.org/manual/5.1/manual.html#lua_newuserdata - pub fn newUserdataSlice(lua: *Lua, comptime T: type, size: usize) []T { - // safe to .? because this function throws a Lua error on out of memory - const ptr = c.lua_newuserdata(lua.state, @sizeOf(T) * size).?; - return @as([*]T, @ptrCast(@alignCast(ptr)))[0..size]; - } - - pub fn newUserdataTagged(lua: *Lua, comptime T: type, tag: i32) *T { - const UTAG_PROXY = c.LUA_UTAG_LIMIT + 1; // not exposed in headers - std.debug.assert((tag >= 0 and tag < c.LUA_UTAG_LIMIT) or tag == UTAG_PROXY); // Luau will do the same assert, this is easier to debug - // safe to .? because this function throws a Lua error on out of memory - // so the returned pointer should never be null - const ptr = c.lua_newuserdatatagged(lua.state, @sizeOf(T), tag).?; - return opaqueCast(T, ptr); - } - - /// This function allocates a new userdata of the given type with an associated - /// destructor callback. - /// - /// Returns a pointer to the Lua-owned data - /// - /// Note: Luau doesn't support the usual Lua __gc metatable destructor. Use this instead. - pub fn newUserdataDtor(lua: *Lua, comptime T: type, dtor_fn: CUserdataDtorFn) *T { - // safe to .? because this function throws a Lua error on out of memory - // so the returned pointer should never be null - const ptr = c.lua_newuserdatadtor(lua.state, @sizeOf(T), @ptrCast(dtor_fn)).?; - return opaqueCast(T, ptr); - } - - /// Set userdata tag at the given index - pub fn setUserdataTag(lua: *Lua, index: i32, tag: i32) void { - std.debug.assert((tag >= 0 and tag < c.LUA_UTAG_LIMIT)); // Luau will do the same assert, this is easier to debug - c.lua_setuserdatatag(lua.state, index, tag); - } - - /// Returns the tag of a userdata at the given index - pub fn userdataTag(lua: *Lua, index: i32) !i32 { - const tag = c.lua_userdatatag(lua.state, index); - if (tag == -1) return error.Fail; - return tag; - } - - /// Pops a key from the stack, and pushes a key-value pair from the table at the given index. - /// See https://www.lua.org/manual/5.1/manual.html#lua_next - pub fn next(lua: *Lua, index: i32) bool { - return c.lua_next(lua.state, index) != 0; - } - - /// Returns the length of the value at the given index - /// See https://www.lua.org/manual/5.1/manual.html#lua_objlen - pub fn objectLen(lua: *Lua, index: i32) i32 { - return c.lua_objlen(lua.state, index); - } - - /// Calls a function (or callable object) in protected mode - /// See https://www.lua.org/manual/5.1/manual.html#lua_pcall - pub fn protectedCall(lua: *Lua, num_args: i32, num_results: i32, err_func: i32) !void { - // The translate-c version of lua_pcall does not type-check so we must rewrite it - // (macros don't always translate well with translate-c) - const ret = c.lua_pcall(lua.state, num_args, num_results, err_func); - switch (ret) { - StatusCode.ok => return, - StatusCode.err_runtime => return error.Runtime, - StatusCode.err_memory => return error.Memory, - StatusCode.err_error => return error.MsgHandler, - else => unreachable, - } - } - - /// Pops `n` elements from the top of the stack - /// See https://www.lua.org/manual/5.1/manual.html#lua_pop - pub fn pop(lua: *Lua, n: i32) void { - lua.setTop(-n - 1); - } - - /// Pushes a boolean value with value `b` onto the stack - /// See https://www.lua.org/manual/5.1/manual.html#lua_pushboolean - pub fn pushBoolean(lua: *Lua, b: bool) void { - c.lua_pushboolean(lua.state, @intFromBool(b)); - } - - /// Pushes a new Closure onto the stack - /// `n` tells how many upvalues this function will have - /// See https://www.lua.org/manual/5.1/manual.html#lua_pushcclosure - pub fn pushClosure(lua: *Lua, c_fn: CFn, name: [:0]const u8, n: i32) void { - c.lua_pushcclosurek(lua.state, c_fn, name, n, null); - } - - /// Pushes a function onto the stack. - /// Equivalent to pushClosure with no upvalues - /// See https://www.lua.org/manual/5.1/manual.html#lua_pushfunction - pub fn pushFunction(lua: *Lua, c_fn: CFn, name: [:0]const u8) void { - lua.pushClosure(c_fn, name, 0); - } - - /// Push a formatted string onto the stack and return a pointer to the string - /// See https://www.lua.org/manual/5.1/manual.html#lua_pushfstring - pub fn pushFString(lua: *Lua, fmt: [:0]const u8, args: anytype) [*:0]const u8 { - return @call(.auto, c.lua_pushfstringL, .{ lua.state, fmt.ptr } ++ args); - } - - /// Pushes an integer with value `n` onto the stack - /// See https://www.lua.org/manual/5.1/manual.html#lua_pushinteger - pub fn pushInteger(lua: *Lua, n: Integer) void { - c.lua_pushinteger(lua.state, n); - } - - /// Pushes a light userdata onto the stack - /// See https://www.lua.org/manual/5.1/manual.html#lua_pushlightuserdata - pub fn pushLightUserdata(lua: *Lua, ptr: *anyopaque) void { - c.lua_pushlightuserdata(lua.state, ptr); - } - - /// Pushes the bytes onto the stack - /// See https://www.lua.org/manual/5.1/manual.html#lua_pushlstring - pub fn pushBytes(lua: *Lua, bytes: []const u8) void { - c.lua_pushlstring(lua.state, bytes.ptr, bytes.len); - } - - /// Pushes a nil value onto the stack - /// See https://www.lua.org/manual/5.1/manual.html#lua_pushnil - pub fn pushNil(lua: *Lua) void { - c.lua_pushnil(lua.state); - } - - /// Pushes a float with value `n` onto the stack - /// See https://www.lua.org/manual/5.1/manual.html#lua_pushnumber - pub fn pushNumber(lua: *Lua, n: Number) void { - c.lua_pushnumber(lua.state, n); - } - - /// Pushes a zero-terminated string onto the stack - /// Lua makes a copy of the string so `str` may be freed immediately after return - /// See https://www.lua.org/manual/5.1/manual.html#lua_pushstring - pub fn pushString(lua: *Lua, str: [:0]const u8) void { - c.lua_pushstring(lua.state, str.ptr); - } - - /// Pushes this thread onto the stack - /// Returns true if this thread is the main thread of its state - /// See https://www.lua.org/manual/5.1/manual.html#lua_pushthread - pub fn pushThread(lua: *Lua) bool { - return c.lua_pushthread(lua.state) != 0; - } - - /// Pushes a copy of the element at the given index onto the stack - /// See https://www.lua.org/manual/5.1/manual.html#lua_pushvalue - pub fn pushValue(lua: *Lua, index: i32) void { - c.lua_pushvalue(lua.state, index); - } - - fn pushVector3(lua: *Lua, x: f32, y: f32, z: f32) void { - c.lua_pushvector(lua.state, x, y, z); - } - - fn pushVector4(lua: *Lua, x: f32, y: f32, z: f32, w: f32) void { - c.lua_pushvector(lua.state, x, y, z, w); - } - - /// Pushes a floating point 3-vector (or 4-vector if configured) `v` onto the stack - pub const pushVector = if (luau_vector_size == 3) pushVector3 else pushVector4; - - /// Returns true if the two values in indices `index1` and `index2` are primitively equal - /// Bypasses __eq metamethods - /// Returns false if not equal, or if any index is invalid - /// See https://www.lua.org/manual/5.1/manual.html#lua_rawequal - pub fn rawEqual(lua: *Lua, index1: i32, index2: i32) bool { - return c.lua_rawequal(lua.state, index1, index2) != 0; - } - - /// Similar to `Lua.getTable()` but does a raw access (without metamethods) - /// See https://www.lua.org/manual/5.1/manual.html#lua_rawget - pub fn rawGetTable(lua: *Lua, index: i32) LuaType { - return @enumFromInt(c.lua_rawget(lua.state, index)); - } - - /// Pushes onto the stack the value t[n], where `t` is the table at the given `index` - /// Returns the `LuaType` of the pushed value - /// See https://www.lua.org/manual/5.1/manual.html#lua_rawgeti - pub fn rawGetIndex(lua: *Lua, index: i32, n: i32) LuaType { - return @enumFromInt(c.lua_rawgeti(lua.state, index, n)); - } - - /// Similar to `Lua.setTable()` but does a raw assignment (without metamethods) - /// See https://www.lua.org/manual/5.1/manual.html#lua_rawset - pub fn rawSetTable(lua: *Lua, index: i32) void { - c.lua_rawset(lua.state, index); - } - - /// Does the equivalent of t[`i`] = v where t is the table at the given `index` - /// and v is the value at the top of the stack - /// Pops the value from the stack. Does not use __newindex metavalue - /// See https://www.lua.org/manual/5.1/manual.html#lua_rawseti - pub fn rawSetIndex(lua: *Lua, index: i32, i: i32) void { - c.lua_rawseti(lua.state, index, i); - } - - /// Sets the C function f as the new value of global name - /// See https://www.lua.org/manual/5.1/manual.html#lua_register - pub fn register(lua: *Lua, name: [:0]const u8, c_fn: CFn) void { - // translate-c failure - lua.pushFunction(c_fn, name); - lua.setGlobal(name); - } - - /// Removes the element at the given valid `index` shifting down elements to fill the gap - /// See https://www.lua.org/manual/5.1/manual.html#lua_remove - pub fn remove(lua: *Lua, index: i32) void { - c.lua_remove(lua.state, index); - } - - /// Moves the top element into the given valid `index` without shifting any elements, - /// then pops the top element - /// See https://www.lua.org/manual/5.1/manual.html#lua_replace - pub fn replace(lua: *Lua, index: i32) void { - c.lua_replace(lua.state, index); - } - - /// Starts and resumes a coroutine in the thread - /// See https://www.lua.org/manual/5.1/manual.html#lua_resume - pub fn resumeThread(lua: *Lua, from: ?Lua, num_args: i32) !ResumeStatus { - const from_state = if (from) |from_val| from_val.state else null; - const thread_status = c.lua_resume(lua.state, from_state, num_args); - switch (thread_status) { - StatusCode.err_runtime => return error.Runtime, - StatusCode.err_memory => return error.Memory, - StatusCode.err_error => return error.MsgHandler, - else => return @enumFromInt(thread_status), - } - } - - /// Pops a table from the stack and sets it as the new environment for the value at the - /// given index. Returns an error if the value at that index is not a function or thread or userdata. - /// See https://www.lua.org/manual/5.1/manual.html#lua_setfenv - pub fn setFnEnvironment(lua: *Lua, index: i32) !void { - if (c.lua_setfenv(lua.state, index) == 0) return error.Fail; - } - - /// Does the equivalent to t[`k`] = v where t is the value at the given `index` - /// and v is the value on the top of the stack - /// See https://www.lua.org/manual/5.1/manual.html#lua_setfield - pub fn setField(lua: *Lua, index: i32, k: [:0]const u8) void { - c.lua_setfield(lua.state, index, k.ptr); - } - - /// Pops a value from the stack and sets it as the new value of global `name` - /// See https://www.lua.org/manual/5.1/manual.html#lua_setglobal - pub fn setGlobal(lua: *Lua, name: [:0]const u8) void { - c.lua_setglobal(lua.state, name.ptr); - } - - /// Pops a table or nil from the stack and sets that value as the new metatable for the - /// value at the given `index` - /// See https://www.lua.org/manual/5.1/manual.html#lua_setmetatable - pub fn setMetatable(lua: *Lua, index: i32) void { - // lua_setmetatable always returns 1 so is safe to ignore - _ = c.lua_setmetatable(lua.state, index); - } - - /// Does the equivalent to t[k] = v, where t is the value at the given `index` - /// v is the value on the top of the stack, and k is the value just below the top - /// See https://www.lua.org/manual/5.1/manual.html#lua_settable - pub fn setTable(lua: *Lua, index: i32) void { - c.lua_settable(lua.state, index); - } - - /// Sets the top of the stack to `index` - /// If the new top is greater than the old, new elements are filled with nil - /// If `index` is 0 all stack elements are removed - /// See https://www.lua.org/manual/5.1/manual.html#lua_settop - pub fn setTop(lua: *Lua, index: i32) void { - c.lua_settop(lua.state, index); - } - - /// Returns the status of this thread - /// See https://www.lua.org/manual/5.1/manual.html#lua_status - pub fn status(lua: *Lua) Status { - return @enumFromInt(c.lua_status(lua.state)); - } - - /// Converts the Lua value at the given `index` into a boolean - /// The Lua value at the index will be considered true unless it is false or nil - /// See https://www.lua.org/manual/5.1/manual.html#lua_toboolean - pub fn toBoolean(lua: *Lua, index: i32) bool { - return c.lua_toboolean(lua.state, index) != 0; - } - - /// Converts a value at the given `index` into a CFn - /// Returns an error if the value is not a CFn - /// See https://www.lua.org/manual/5.1/manual.html#lua_tocfunction - pub fn toCFunction(lua: *Lua, index: i32) !CFn { - return c.lua_tocfunction(lua.state, index) orelse return error.Fail; - } - - /// Converts the Lua value at the given `index` to a signed integer - /// The Lua value must be an integer, or a number, or a string convertible to an integer otherwise toInteger returns 0 - /// See https://www.lua.org/manual/5.1/manual.html#lua_tointeger - pub fn toInteger(lua: *Lua, index: i32) !Integer { - var success: c_int = undefined; - const result = c.lua_tointegerx(lua.state, index, &success); - if (success == 0) return error.Fail; - return result; - } - - /// Returns a slice of bytes at the given index - /// If the value is not a string or number, returns an error - /// If the value was a number the actual value in the stack will be changed to a string - /// See https://www.lua.org/manual/5.1/manual.html#lua_tolstring - pub fn toBytes(lua: *Lua, index: i32) ![:0]const u8 { - var length: usize = undefined; - if (c.lua_tolstring(lua.state, index, &length)) |ptr| return ptr[0..length :0]; - return error.Fail; - } - - /// Converts the Lua value at the given `index` to a float - /// The Lua value must be a number or a string convertible to a number otherwise toNumber returns 0 - /// See https://www.lua.org/manual/5.1/manual.html#lua_tonumber - pub fn toNumber(lua: *Lua, index: i32) !Number { - var success: c_int = undefined; - const result = c.lua_tonumberx(lua.state, index, &success); - if (success == 0) return error.Fail; - return result; - } - - /// Converts the value at the given `index` to an opaque pointer - /// See https://www.lua.org/manual/5.1/manual.html#lua_topointer - pub fn toPointer(lua: *Lua, index: i32) !*const anyopaque { - if (c.lua_topointer(lua.state, index)) |ptr| return ptr; - return error.Fail; - } - - /// Converts the Lua value at the given `index` to a zero-terminated many-itemed-pointer (string) - /// Returns an error if the conversion failed - /// If the value was a number the actual value in the stack will be changed to a string - /// See https://www.lua.org/manual/5.1/manual.html#lua_tolstring - pub fn toString(lua: *Lua, index: i32) ![*:0]const u8 { - var length: usize = undefined; - if (c.lua_tolstring(lua.state, index, &length)) |str| return str; - return error.Fail; - } - - /// Converts the value at the given `index` to a Lua thread (wrapped with a `Lua` struct) - /// The thread does _not_ contain an allocator because it is not the main thread and should therefore not be used with `deinit()` - /// Returns an error if the value is not a thread - /// See https://www.lua.org/manual/5.1/manual.html#lua_tothread - pub fn toThread(lua: *Lua, index: i32) !Lua { - const thread = c.lua_tothread(lua.state, index); - if (thread) |thread_ptr| return Lua{ .state = thread_ptr }; - return error.Fail; - } - - /// Returns a Lua-owned userdata pointer of the given type at the given index. - /// Works for both light and full userdata. - /// Returns an error if the value is not a userdata. - /// See https://www.lua.org/manual/5.1/manual.html#lua_touserdata - pub fn toUserdata(lua: *Lua, comptime T: type, index: i32) !*T { - if (c.lua_touserdata(lua.state, index)) |ptr| return opaqueCast(T, ptr); - return error.Fail; - } - - /// Returns a Lua-owned userdata slice of the given type at the given index. - /// Returns an error if the value is not a userdata. - /// See https://www.lua.org/manual/5.1/manual.html#lua_touserdata - pub fn toUserdataSlice(lua: *Lua, comptime T: type, index: i32) ![]T { - if (c.lua_touserdata(lua.state, index)) |ptr| { - const size = @as(u32, @intCast(lua.objectLen(index))) / @sizeOf(T); - return @as([*]T, @ptrCast(@alignCast(ptr)))[0..size]; - } - return error.Fail; - } - - pub fn toUserdataTagged(lua: *Lua, comptime T: type, index: i32, tag: i32) !*T { - if (c.lua_touserdatatagged(lua.state, index, tag)) |ptr| return opaqueCast(T, ptr); - return error.Fail; - } - - /// Converts the Lua value at the given `index` to a 3- or 4-vector. - /// The Lua value must be a vector. - pub fn toVector(lua: *Lua, index: i32) ![luau_vector_size]f32 { - const res = c.lua_tovector(lua.state, index); - if (res) |r| { - switch (luau_vector_size) { - 3 => return [_]f32{ r[0], r[1], r[2] }, - 4 => return [_]f32{ r[0], r[1], r[2], r[3] }, - else => @compileError("invalid luau_vector_size - should not happen"), - } - } - return error.Fail; - } - - /// Converts the Lua string at the given `index` to a string atom. - /// The Lua value must be a string. - pub fn toStringAtom(lua: *Lua, index: i32) !struct { i32, [:0]const u8 } { - var atom: c_int = undefined; - if (c.lua_tostringatom(lua.state, index, &atom)) |ptr| { - return .{ atom, std.mem.span(ptr) }; - } - return error.Fail; - } - - /// Retrieve the user atom index and name for the method being - /// invoked in a namecall. - pub fn namecallAtom(lua: *Lua) !struct { i32, [:0]const u8 } { - var atom: c_int = undefined; - if (c.lua_namecallatom(lua.state, &atom)) |ptr| { - return .{ atom, std.mem.span(ptr) }; - } - return error.Fail; - } - - /// Returns the `LuaType` of the value at the given index - /// Note that this is equivalent to lua_type but because type is a Zig primitive it is renamed to `typeOf` - /// See https://www.lua.org/manual/5.1/manual.html#lua_type - pub fn typeOf(lua: *Lua, index: i32) LuaType { - return @enumFromInt(c.lua_type(lua.state, index)); - } - - /// Returns the name of the given `LuaType` as a null-terminated slice - /// See https://www.lua.org/manual/5.1/manual.html#lua_typename - pub fn typeName(lua: *Lua, t: LuaType) [:0]const u8 { - return std.mem.span(c.lua_typename(lua.state, @intFromEnum(t))); - } - - /// Returns the pseudo-index that represents the `i`th upvalue of the running function - pub fn upvalueIndex(i: i32) i32 { - return c.lua_upvalueindex(i); - } - - /// Pops `num` values from the current stack and pushes onto the stack of `to` - /// See https://www.lua.org/manual/5.1/manual.html#lua_xmove - pub fn xMove(lua: *Lua, to: Lua, num: i32) void { - c.lua_xmove(lua.state, to.state, num); - } - - /// Yields a coroutine - /// This function must be used as the return expression of a function - /// See https://www.lua.org/manual/5.1/manual.html#lua_yield - pub fn yield(lua: *Lua, num_results: i32) i32 { - return c.lua_yield(lua.state, num_results); - } - - // Debug library functions - // - // The debug interface functions are included in alphabetical order - // Each is kept similar to the original C API function while also making it easy to use from Zig - - /// Gets information about a specific function or function invocation. - /// See https://www.lua.org/manual/5.1/manual.html#lua_getinfo - pub fn getInfo(lua: *Lua, level: i32, options: DebugInfo.Options, info: *DebugInfo) void { - const str = options.toString(); - - var ar: Debug = undefined; - - // should never fail because we are controlling options with the struct param - _ = c.lua_getinfo(lua.state, level, &str, &ar); - // std.debug.assert( != 0); - - // copy data into a struct - if (options.l) info.current_line = if (ar.currentline == -1) null else ar.currentline; - if (options.n) { - info.name = if (ar.name != null) std.mem.span(ar.name) else null; - } - if (options.s) { - info.source = std.mem.span(ar.source); - // TODO: short_src figureit out - @memcpy(&info.short_src, ar.short_src[0..c.LUA_IDSIZE]); - info.first_line_defined = ar.linedefined; - info.what = blk: { - const what = std.mem.span(ar.what); - if (std.mem.eql(u8, "Lua", what)) break :blk .lua; - if (std.mem.eql(u8, "C", what)) break :blk .c; - if (std.mem.eql(u8, "main", what)) break :blk .main; - if (std.mem.eql(u8, "tail", what)) break :blk .tail; - unreachable; - }; - } - } - - /// Gets information about a local variable - /// Returns the name of the local variable - /// See https://www.lua.org/manual/5.1/manual.html#lua_getlocal - pub fn getLocal(lua: *Lua, level: i32, n: i32) ![:0]const u8 { - if (c.lua_getlocal(lua.state, level, n)) |name| { - return std.mem.span(name); - } - return error.Fail; - } - - /// Gets information about the `n`th upvalue of the closure at index `func_index` - /// See https://www.lua.org/manual/5.1/manual.html#lua_getupvaule - pub fn getUpvalue(lua: *Lua, func_index: i32, n: i32) ![:0]const u8 { - if (c.lua_getupvalue(lua.state, func_index, n)) |name| { - return std.mem.span(name); - } - return error.Fail; - } - - /// Sets the value of a local variable - /// Returns an error when the index is greater than the number of active locals - /// Returns the name of the local variable - /// See https://www.lua.org/manual/5.1/manual.html#lua_setlocal - pub fn setLocal(lua: *Lua, level: i32, n: i32) ![:0]const u8 { - if (c.lua_setlocal(lua.state, level, n)) |name| { - return std.mem.span(name); - } - return error.Fail; - } - - /// Sets the value of a closure's upvalue - /// Returns the name of the upvalue or an error if the upvalue does not exist - /// See https://www.lua.org/manual/5.1/manual.html#lua_setupvalue - pub fn setUpvalue(lua: *Lua, func_index: i32, n: i32) ![:0]const u8 { - if (c.lua_setupvalue(lua.state, func_index, n)) |name| { - return std.mem.span(name); - } - return error.Fail; - } - - pub fn setUserAtomCallbackFn(lua: *Lua, cb: CUserAtomCallbackFn) void { - if (c.lua_callbacks(lua.state)) |cb_struct| { - cb_struct.*.useratom = cb; - } - } - - // Auxiliary library functions - // - // Auxiliary library functions are included in alphabetical order. - // Each is kept similar to the original C API function while also making it easy to use from Zig - - /// Checks whether `cond` is true. Raises an error using `Lua.argError()` if not - /// Possibly never returns - /// See https://www.lua.org/manual/5.1/manual.html#luaL_argcheck - pub fn argCheck(lua: *Lua, cond: bool, arg: i32, extra_msg: [:0]const u8) void { - // translate-c failed - if (!cond) lua.argError(arg, extra_msg); - } - - /// Raises an error reporting a problem with argument `arg` of the C function that called it - /// See https://www.lua.org/manual/5.1/manual.html#luaL_argerror - pub fn argError(lua: *Lua, arg: i32, extra_msg: [*:0]const u8) noreturn { - _ = c.luaL_argerror(lua.state, arg, extra_msg); - unreachable; - } - - /// Raises a type error for the argument arg of the C function that called it, using a standard message; tname is a "name" for the expected type. This function never returns. - /// See https://www.lua.org/manual/5.4/manual.html#luaL_typeerror - pub fn typeError(lua: *Lua, arg: i32, type_name: [:0]const u8) noreturn { - _ = c.luaL_typeerror(lua.state, arg, type_name.ptr); - unreachable; - } - - /// Calls a metamethod - /// See https://www.lua.org/manual/5.1/manual.html#luaL_callmeta - pub fn callMeta(lua: *Lua, obj: i32, field: [:0]const u8) !void { - if (c.luaL_callmeta(lua.state, obj, field.ptr) == 0) return error.Fail; - } - - /// Checks whether the function has an argument of any type at position `arg` - /// See https://www.lua.org/manual/5.1/manual.html#luaL_checkany - pub fn checkAny(lua: *Lua, arg: i32) void { - c.luaL_checkany(lua.state, arg); - } - - /// Checks whether the function argument `arg` is a number and returns the number cast to an Integer - /// See https://www.lua.org/manual/5.1/manual.html#luaL_checkinteger - pub fn checkInteger(lua: *Lua, arg: i32) Integer { - return c.luaL_checkinteger(lua.state, arg); - } - - /// Checks whether the function argument `arg` is a slice of bytes and returns the slice - /// See https://www.lua.org/manual/5.1/manual.html#luaL_checklstring - pub fn checkBytes(lua: *Lua, arg: i32) [:0]const u8 { - var length: usize = 0; - const str = c.luaL_checklstring(lua.state, arg, &length); - // luaL_checklstring never returns null (throws lua error) - return str[0..length :0]; - } - - /// Checks whether the function argument `arg` is a number and returns the number - /// See https://www.lua.org/manual/5.1/manual.html#luaL_checknumber - pub fn checkNumber(lua: *Lua, arg: i32) Number { - return c.luaL_checknumber(lua.state, arg); - } - - /// Checks whether the function argument `arg` is a string and searches for the enum value with the same name in `T`. - /// `default` is used as a default value when not null - /// Returns the enum value found - /// Useful for mapping Lua strings to Zig enums - /// See https://www.lua.org/manual/5.1/manual.html#luaL_checkoption - pub fn checkOption(lua: *Lua, comptime T: type, arg: i32, default: ?T) T { - const name = blk: { - if (default) |defaultName| { - break :blk lua.optBytes(arg, @tagName(defaultName)); - } else { - break :blk lua.checkBytes(arg); - } - }; - - inline for (std.meta.fields(T)) |field| { - if (std.mem.eql(u8, field.name, name)) { - return @enumFromInt(field.value); - } - } - - return lua.argError(arg, lua.pushFString("invalid option '%s'", .{name.ptr})); - } - - /// Grows the stack size to top + `size` elements, raising an error if the stack cannot grow to that size - /// `msg` is an additional text to go into the error message - /// See https://www.lua.org/manual/5.1/manual.html#luaL_checkstack - pub fn checkStackErr(lua: *Lua, size: i32, msg: ?[*:0]const u8) void { - c.luaL_checkstack(lua.state, size, msg); - } - - /// Checks whether the function argument `arg` is a string and returns the string - /// See https://www.lua.org/manual/5.1/manual.html#luaL_checkstring - pub fn checkString(lua: *Lua, arg: i32) [*:0]const u8 { - return c.luaL_checklstring(lua.state, arg, null); - } - - /// Checks whether the function argument `arg` has type `t` - /// See https://www.lua.org/manual/5.1/manual.html#luaL_checktype - pub fn checkType(lua: *Lua, arg: i32, t: LuaType) void { - c.luaL_checktype(lua.state, arg, @intFromEnum(t)); - } - - /// Checks whether the function argument `arg` is a userdata of the type `name` - /// Returns the userdata's memory-block address - /// See https://www.lua.org/manual/5.1/manual.html#luaL_checkudata - pub fn checkUserdata(lua: *Lua, comptime T: type, arg: i32, name: [:0]const u8) *T { - // the returned pointer will not be null - return opaqueCast(T, c.luaL_checkudata(lua.state, arg, name.ptr).?); - } - - /// Checks whether the function argument `arg` is a userdata of the type `name` - /// Returns a Lua-owned userdata slice - /// See https://www.lua.org/manual/5.1/manual.html#luaL_checkudata - pub fn checkUserdataSlice(lua: *Lua, comptime T: type, arg: i32, name: [:0]const u8) []T { - // the returned pointer will not be null - const ptr = c.luaL_checkudata(lua.state, arg, name.ptr).?; - const size = @as(u32, @intCast(lua.objectLen(arg))) / @sizeOf(T); - return @as([*]T, @ptrCast(@alignCast(ptr)))[0..size]; - } - - /// Checks whether the function argument `arg` is a vector and returns the vector as a floating point slice. - pub fn checkVector(lua: *Lua, arg: i32) [luau_vector_size]f32 { - const vec = lua.toVector(arg) catch { - lua.typeError(arg, lua.typeName(LuaType.vector)); - }; - return vec; - } - - /// Loads and runs the given string - /// See https://www.lua.org/manual/5.1/manual.html#luaL_dostring - /// TODO: does it make sense to have this in Luau? - pub fn doString(lua: *Lua, str: [:0]const u8) !void { - try lua.loadString(str); - try lua.protectedCall(0, mult_return, 0); - } - - /// Raises an error - /// See https://www.lua.org/manual/5.1/manual.html#luaL_error - pub fn raiseErrorStr(lua: *Lua, fmt: [:0]const u8, args: anytype) noreturn { - _ = @call(.auto, c.luaL_errorL, .{ lua.state, fmt.ptr } ++ args); - unreachable; - } - - /// Pushes onto the stack the field `e` from the metatable of the object at index `obj` - /// and returns the type of the pushed value - /// See https://www.lua.org/manual/5.1/manual.html#luaL_getmetafield - pub fn getMetaField(lua: *Lua, obj: i32, field: [:0]const u8) !LuaType { - const val_type: LuaType = @enumFromInt(c.luaL_getmetafield(lua.state, obj, field.ptr)); - if (val_type == .nil) return error.Fail; - return val_type; - } - - /// Pushes onto the stack the metatable associated with the name `type_name` in the registry - /// or nil if there is no metatable associated with that name. Returns the type of the pushed value - /// See https://www.lua.org/manual/5.1/manual.html#luaL_getmetatable - pub fn getMetatableRegistry(lua: *Lua, table_name: [:0]const u8) LuaType { - return @enumFromInt(c.luaL_getmetatable(lua.state, table_name)); - } - - /// Loads a string as a Lua chunk - /// See https://www.lua.org/manual/5.1/manual.html#luaL_loadstring - /// TODO: does it make sense to have this in Luau? - pub fn loadString(lua: *Lua, str: [:0]const u8) !void { - var size: usize = 0; - const bytecode = c.luau_compile(str.ptr, str.len, null, &size); - - // Failed to allocate memory for the out buffer - if (bytecode == null) return error.Memory; - - // luau_compile uses malloc to allocate the bytecode on the heap - defer zig_luau_free(bytecode); - try lua.loadBytecode("...", bytecode[0..size]); - } - - /// Loads bytecode binary (as compiled with f.ex. 'luau-compile --binary') - /// See https://luau-lang.org/getting-started - /// See also condsiderations for binary bytecode compatibility/safety: https://github.com/luau-lang/luau/issues/493#issuecomment-1185054665 - pub fn loadBytecode(lua: *Lua, chunkname: [:0]const u8, bytecode: []const u8) !void { - if (c.luau_load(lua.state, chunkname.ptr, bytecode.ptr, bytecode.len, 0) != 0) return error.Fail; - } - - /// If the registry already has the key `key`, returns an error - /// Otherwise, creates a new table to be used as a metatable for userdata - /// See https://www.lua.org/manual/5.1/manual.html#luaL_newmetatable - pub fn newMetatable(lua: *Lua, key: [:0]const u8) !void { - if (c.luaL_newmetatable(lua.state, key.ptr) == 0) return error.Fail; - } - - /// Creates a new Lua state with an allocator using the default libc allocator - /// See https://www.lua.org/manual/5.1/manual.html#luaL_newstate - pub fn newStateLibc() !Lua { - zig_registerAssertionHandler(); - const state = c.luaL_newstate() orelse return error.Memory; - return Lua{ .state = state }; - } - - // luaL_opt (a macro) really isn't that useful, so not going to implement for now - - /// If the function argument `arg` is an integer, returns the integer - /// If the argument is absent or nil returns `default` - /// See https://www.lua.org/manual/5.1/manual.html#luaL_optinteger - pub fn optInteger(lua: *Lua, arg: i32, default: Integer) Integer { - return c.luaL_optinteger(lua.state, arg, default); - } - - /// If the function argument `arg` is a slice of bytes, returns the slice - /// If the argument is absent or nil returns `default` - /// See https://www.lua.org/manual/5.1/manual.html#luaL_optlstring - pub fn optBytes(lua: *Lua, arg: i32, default: [:0]const u8) [:0]const u8 { - var length: usize = 0; - // will never return null because default cannot be null - const ret: [*]const u8 = c.luaL_optlstring(lua.state, arg, default.ptr, &length); - if (ret == default.ptr) return default; - return ret[0..length :0]; - } - - /// If the function argument `arg` is a number, returns the number - /// If the argument is absent or nil returns `default` - /// See https://www.lua.org/manual/5.1/manual.html#luaL_optnumber - pub fn optNumber(lua: *Lua, arg: i32, default: Number) Number { - return c.luaL_optnumber(lua.state, arg, default); - } - - /// If the function argument `arg` is a string, returns the string - /// If the argment is absent or nil returns `default` - /// See https://www.lua.org/manual/5.1/manual.html#luaL_optstring - pub fn optString(lua: *Lua, arg: i32, default: [:0]const u8) [*:0]const u8 { - // translate-c error - return c.luaL_optlstring(lua.state, arg, default.ptr, null); - } - - /// Creates and returns a reference in the table at index `index` for the object on the top of the stack - /// Pops the object - /// See https://www.lua.org/manual/5.1/manual.html#luaL_ref - pub fn ref(lua: *Lua, index: i32) !i32 { - const ret = c.lua_ref(lua.state, index); - return if (ret == ref_nil) error.Fail else ret; - } - - /// Opens a library - /// See https://www.lua.org/manual/5.1/manual.html#luaL_register - pub fn registerFns(lua: *Lua, libname: ?[:0]const u8, funcs: []const FnReg) void { - // translated from the implementation of luaI_openlib so we can use a slice of - // FnReg without requiring a sentinel end value - if (libname) |name| { - _ = c.luaL_findtable(lua.state, registry_index, "_LOADED", 1); - _ = lua.getField(-1, name); - if (!lua.isTable(-1)) { - lua.pop(1); - if (c.luaL_findtable(lua.state, globals_index, name, @intCast(funcs.len))) |_| { - lua.raiseErrorStr("name conflict for module '%s'", .{name.ptr}); - } - lua.pushValue(-1); - lua.setField(-3, name); - } - lua.remove(-2); - lua.insert(-1); - } - for (funcs) |f| { - lua.pushFunction(f.func, f.name); - lua.setField(-2, f.name); - } - } - - /// Returns the name of the type of the value at the given `index` - /// See https://www.lua.org/manual/5.1/manual.html#luaL_typename - pub fn typeNameIndex(lua: *Lua, index: i32) [:0]const u8 { - return std.mem.span(c.luaL_typename(lua.state, index)); - } - - /// Releases the reference `r` from the table at index `index` - /// See https://www.lua.org/manual/5.1/manual.html#luaL_unref - pub fn unref(lua: *Lua, r: i32) void { - c.lua_unref(lua.state, r); - } - - /// Pushes onto the stack a string identifying the current position of the control - /// at the call stack `level` - /// See https://www.lua.org/manual/5.1/manual.html#luaL_where - pub fn where(lua: *Lua, level: i32) void { - c.luaL_where(lua.state, level); - } - - // Standard library loading functions - - /// Opens the specified standard library functions - /// Behaves like openLibs, but allows specifying which libraries - /// to expose to the global table rather than all of them - /// See https://www.lua.org/manual/5.1/manual.html#luaL_openlibs - pub fn open(lua: *Lua, libs: Libs) void { - if (libs.base) lua.requireF("", c.luaopen_base); - if (libs.coroutine) lua.requireF(c.LUA_COLIBNAME, c.luaopen_coroutine); - if (libs.string) lua.requireF(c.LUA_STRLIBNAME, c.luaopen_string); - if (libs.table) lua.requireF(c.LUA_TABLIBNAME, c.luaopen_table); - if (libs.math) lua.requireF(c.LUA_MATHLIBNAME, c.luaopen_math); - if (libs.os) lua.requireF(c.LUA_OSLIBNAME, c.luaopen_os); - if (libs.debug) lua.requireF(c.LUA_DBLIBNAME, c.luaopen_debug); - } - - fn requireF(lua: *Lua, name: [:0]const u8, func: CFn) void { - lua.pushFunction(func, name); - lua.pushString(name); - lua.call(1, 0); - } - - /// Open all standard libraries - /// See https://www.lua.org/manual/5.1/manual.html#luaL_openlibs - pub fn openLibs(lua: *Lua) void { - c.luaL_openlibs(lua.state); - } - - /// Open the basic standard library - pub fn openBase(lua: *Lua) void { - _ = c.luaopen_base(lua.state); - } - - /// Open the coroutine standard library - pub fn openCoroutine(lua: *Lua) void { - _ = c.luaopen_coroutine(lua.state); - } - - /// Open the string standard library - pub fn openString(lua: *Lua) void { - _ = c.luaopen_string(lua.state); - } - - /// Open the UTF-8 standard library - pub fn openUtf8(lua: *Lua) void { - _ = c.luaopen_utf8(lua.state); - } - - /// Open the table standard library - pub fn openTable(lua: *Lua) void { - _ = c.luaopen_table(lua.state); - } - - /// Open the math standard library - pub fn openMath(lua: *Lua) void { - _ = c.luaopen_math(lua.state); - } - - /// Open the os standard library - pub fn openOS(lua: *Lua) void { - _ = c.luaopen_os(lua.state); - } - - /// Open the debug standard library - pub fn openDebug(lua: *Lua) void { - _ = c.luaopen_debug(lua.state); - } -}; - -/// A string buffer allowing for Zig code to build Lua strings piecemeal -/// All LuaBuffer functions are wrapped in this struct to make the API more convenient to use -/// See https://www.lua.org/manual/5.1/manual.html#luaL_Buffer -pub const Buffer = struct { - b: LuaBuffer = undefined, - - /// Initialize a Lua string buffer - /// See https://www.lua.org/manual/5.1/manual.html#luaL_buffinit - pub fn init(buf: *Buffer, lua: Lua) void { - c.luaL_buffinit(lua.state, &buf.b); - } - - /// TODO: buffinitsize - /// Internal Lua type for a string buffer - pub const LuaBuffer = c.luaL_Strbuf; - - pub const buffer_size = c.LUA_BUFFERSIZE; - - /// Adds `byte` to the buffer - /// See https://www.lua.org/manual/5.1/manual.html#luaL_addchar - pub fn addChar(buf: *Buffer, byte: u8) void { - // could not be translated by translate-c - var lua_buf = &buf.b; - if (lua_buf.p > &lua_buf.buffer[buffer_size - 1]) _ = buf.prep(); - lua_buf.p.* = byte; - lua_buf.p += 1; - } - - /// Adds the string to the buffer - /// See https://www.lua.org/manual/5.1/manual.html#luaL_addlstring - pub fn addBytes(buf: *Buffer, str: []const u8) void { - c.luaL_addlstring(&buf.b, str.ptr, str.len); - } - - /// Adds to the buffer a string of `length` previously copied to the buffer area - /// See https://www.lua.org/manual/5.1/manual.html#luaL_addsize - pub fn addSize(buf: *Buffer, length: usize) void { - // another function translate-c couldn't handle - // c.luaL_addsize(&buf.b, length); - var lua_buf = &buf.b; - lua_buf.p += length; - } - - /// Adds the zero-terminated string pointed to by `str` to the buffer - /// See https://www.lua.org/manual/5.1/manual.html#luaL_addstring - pub fn addString(buf: *Buffer, str: [:0]const u8) void { - c.luaL_addlstring(&buf.b, str.ptr, str.len); - } - - /// Adds the value on the top of the stack to the buffer and pops the value - /// See https://www.lua.org/manual/5.1/manual.html#luaL_addvalue - pub fn addValue(buf: *Buffer) void { - c.luaL_addvalue(&buf.b); - } - - /// Adds the value at the given index to the buffer - pub fn addValueAny(buf: *Buffer, idx: i32) void { - c.luaL_addvalueany(&buf.b, idx); - } - - /// Equivalent to prepSize with a buffer size of Buffer.buffer_size - /// See https://www.lua.org/manual/5.1/manual.html#luaL_prepbuffer - pub fn prep(buf: *Buffer) []u8 { - return c.luaL_prepbuffsize(&buf.b, buffer_size)[0..buffer_size]; - } - - /// Finishes the use of the buffer leaving the final string on the top of the stack - /// See https://www.lua.org/manual/5.1/manual.html#luaL_pushresult - pub fn pushResult(buf: *Buffer) void { - c.luaL_pushresult(&buf.b); - } - - /// Equivalent to `Buffer.addSize()` followed by `Buffer.pushResult()` - /// See https://www.lua.org/manual/5.2/manual.html#luaL_pushresultsize - pub fn pushResultSize(buf: *Buffer, size: usize) void { - c.luaL_pushresultsize(&buf.b, size); - } -}; - -// Helper functions to make the ziglua API easier to use - -/// Casts the opaque pointer to a pointer of the given type with the proper alignment -/// Useful for casting pointers from the Lua API like userdata or other data -pub inline fn opaqueCast(comptime T: type, ptr: *anyopaque) *T { - return @ptrCast(@alignCast(ptr)); -} - -pub const ZigFn = fn (lua: *Lua) i32; -pub const ZigContFn = fn (lua: *Lua, status: Status, ctx: i32) i32; -pub const ZigReaderFn = fn (lua: *Lua, data: *anyopaque) ?[]const u8; -pub const ZigWriterFn = fn (lua: *Lua, buf: []const u8, data: *anyopaque) bool; -pub const ZigUserdataDtorFn = fn (data: *anyopaque) void; -pub const ZigUserAtomCallbackFn = fn (str: []const u8) i16; - -fn TypeOfWrap(comptime T: type) type { - return switch (T) { - LuaState => Lua, - ZigFn => CFn, - ZigReaderFn => CReaderFn, - ZigWriterFn => CWriterFn, - ZigUserdataDtorFn => CUserdataDtorFn, - ZigUserAtomCallbackFn => CUserAtomCallbackFn, - else => @compileError("unsupported type given to wrap: '" ++ @typeName(T) ++ "'"), - }; -} - -/// Wraps the given value for use in the Lua API -/// Supports the following: -/// * `LuaState` => `Lua` -pub fn wrap(comptime value: anytype) TypeOfWrap(@TypeOf(value)) { - const T = @TypeOf(value); - return switch (T) { - LuaState => Lua{ .state = value }, - ZigFn => wrapZigFn(value), - ZigReaderFn => wrapZigReaderFn(value), - ZigWriterFn => wrapZigWriterFn(value), - ZigUserdataDtorFn => wrapZigUserdataDtorFn(value), - ZigUserAtomCallbackFn => wrapZigUserAtomCallbackFn(value), - else => @compileError("unsupported type given to wrap: '" ++ @typeName(T) ++ "'"), - }; -} - -/// Wrap a ZigFn in a CFn for passing to the API -fn wrapZigFn(comptime f: ZigFn) CFn { - return struct { - fn inner(state: ?*LuaState) callconv(.C) c_int { - // this is called by Lua, state should never be null - var lua: Lua = .{ .state = state.? }; - return @call(.always_inline, f, .{&lua}); - } - }.inner; -} - -/// Wrap a ZigFn in a CFn for passing to the API -fn wrapZigUserdataDtorFn(comptime f: ZigUserdataDtorFn) CUserdataDtorFn { - return struct { - fn inner(userdata: *anyopaque) callconv(.C) void { - return @call(.always_inline, f, .{userdata}); - } - }.inner; -} - -/// Wrap a ZigFn in a CFn for passing to the API -fn wrapZigUserAtomCallbackFn(comptime f: ZigUserAtomCallbackFn) CUserAtomCallbackFn { - return struct { - fn inner(str: [*c]const u8, len: usize) callconv(.C) i16 { - if (str) |s| { - const buf = s[0..len]; - return @call(.always_inline, f, .{buf}); - } - return -1; - } - }.inner; -} - -/// Wrap a ZigReaderFn in a CReaderFn for passing to the API -fn wrapZigReaderFn(comptime f: ZigReaderFn) CReaderFn { - return struct { - fn inner(state: ?*LuaState, data: ?*anyopaque, size: [*c]usize) callconv(.C) [*c]const u8 { - var lua: Lua = .{ .state = state.? }; - if (@call(.always_inline, f, .{ &lua, data.? })) |buffer| { - size.* = buffer.len; - return buffer.ptr; - } else { - size.* = 0; - return null; - } - } - }.inner; -} - -/// Wrap a ZigWriterFn in a CWriterFn for passing to the API -fn wrapZigWriterFn(comptime f: ZigWriterFn) CWriterFn { - return struct { - fn inner(state: ?*LuaState, buf: ?*const anyopaque, size: usize, data: ?*anyopaque) callconv(.C) c_int { - // this is called by Lua, state should never be null - var lua: Lua = .{ .state = state.? }; - const buffer = @as([*]const u8, @ptrCast(buf))[0..size]; - const result = @call(.always_inline, f, .{ &lua, buffer, data.? }); - // it makes more sense for the inner writer function to return false for failure, - // so negate the result here - return @intFromBool(!result); - } - }.inner; -} - -/// Zig wrapper for Luau lua_CompileOptions that uses the same defaults as Luau if -/// no compile options is specified. -pub const CompileOptions = struct { - optimization_level: i32 = 1, - debug_level: i32 = 1, - coverage_level: i32 = 0, - /// global builtin to construct vectors; disabled by default (.) - vector_lib: ?[*:0]const u8 = null, - vector_ctor: ?[*:0]const u8 = null, - /// vector type name for type tables; disabled by default - vector_type: ?[*:0]const u8 = null, - /// null-terminated array of globals that are mutable; disables the import optimization for fields accessed through these - mutable_globals: ?[*:null]const ?[*:0]const u8 = null, -}; - -/// Compile luau source into bytecode, return callee owned buffer allocated through the given allocator. -pub fn compile(allocator: Allocator, source: []const u8, options: CompileOptions) ![]const u8 { - var size: usize = 0; - - var opts = c.lua_CompileOptions{ - .optimizationLevel = options.optimization_level, - .debugLevel = options.debug_level, - .coverageLevel = options.coverage_level, - .vectorLib = options.vector_lib, - .vectorCtor = options.vector_ctor, - .mutableGlobals = options.mutable_globals, - }; - const bytecode = c.luau_compile(source.ptr, source.len, &opts, &size); - if (bytecode == null) return error.Memory; - defer zig_luau_free(bytecode); - return try allocator.dupe(u8, bytecode[0..size]); -} diff --git a/src/tests.zig b/src/tests.zig index 0791711..db64883 100644 --- a/src/tests.zig +++ b/src/tests.zig @@ -169,7 +169,7 @@ test "standard library loading" { .lua51 => lua.open(.{ .base = true, .package = true, .string = true, .table = true, .math = true, .io = true, .os = true, .debug = true }), .lua52 => lua.open(.{ .base = true, .coroutine = true, .package = true, .string = true, .table = true, .math = true, .io = true, .os = true, .debug = true, .bit = true }), .lua53, .lua54 => lua.open(.{ .base = true, .coroutine = true, .package = true, .string = true, .utf8 = true, .table = true, .math = true, .io = true, .os = true, .debug = true }), - .luau => lua.open(.{ .base = true, .coroutine = true, .package = true, .string = true, .utf8 = true, .table = true, .math = true, .io = true, .os = true, .debug = true }), + .luau => lua.open(.{ .base = true, .coroutine = true, .string = true, .utf8 = true, .table = true, .math = true, .io = true, .os = true, .debug = true }), .luajit => { // TODO: why do tests crash? }, From 863b81b5741e2d957028f9f0c208648f358e3aba Mon Sep 17 00:00:00 2001 From: Nathan Craddock Date: Mon, 29 Jan 2024 17:56:19 -0700 Subject: [PATCH 5/5] Run zig fmt --- build.zig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build.zig b/build.zig index 53ebf17..2a7d405 100644 --- a/build.zig +++ b/build.zig @@ -257,7 +257,7 @@ fn buildLuaJIT(b: *Build, target: Build.ResolvedTarget, optimize: std.builtin.Op }); minilua.linkLibC(); minilua.root_module.sanitize_c = false; - minilua.addCSourceFile(.{.file = upstream.path("src/host/minilua.c")}); + minilua.addCSourceFile(.{ .file = upstream.path("src/host/minilua.c") }); // Generate the buildvm_arch.h file using minilua const dynasm_run = b.addRunArtifact(minilua); @@ -282,7 +282,7 @@ fn buildLuaJIT(b: *Build, target: Build.ResolvedTarget, optimize: std.builtin.Op dynasm_run.addArg("-o"); const buildvm_arch_h = dynasm_run.addOutputFileArg("buildvm_arch.h"); - dynasm_run.addFileArg(upstream.path(switch(target.result.cpu.arch) { + dynasm_run.addFileArg(upstream.path(switch (target.result.cpu.arch) { .x86 => "src/vm_x86.dasc", .x86_64 => "src/vm_x64.dasc", .arm, .armeb => "src/vm_arm.dasc", @@ -469,7 +469,7 @@ const lua_54_source_files = lua_base_source_files ++ [_][]const u8{ "src/lutf8lib.c", }; -const luajit_lib = [_][]const u8 { +const luajit_lib = [_][]const u8{ "src/lib_base.c", "src/lib_math.c", "src/lib_bit.c",