379 lines
12 KiB
Zig
379 lines
12 KiB
Zig
pub const c = @import("c.zig").c;
|
|
const std = @import("std");
|
|
pub const pod = @import("pod.zig");
|
|
|
|
pub const LuaStateSettings = struct {
|
|
defaultSetup: bool = true,
|
|
};
|
|
|
|
pub const LuaCFunc = *const fn (?*c.lua_State) callconv(.C) i32;
|
|
pub const LuaZigFunc = *const fn (LuaState) i32;
|
|
pub const LibSpec = []const c.luaL_Reg;
|
|
pub const luaL_Reg = c.luaL_Reg;
|
|
|
|
pub fn CWrap(comptime Func: anytype) LuaCFunc {
|
|
const Wrap = struct {
|
|
pub fn inner(l: ?*c.lua_State) callconv(.C) i32 {
|
|
//return @call(.always_inline, Func, .{.{ .l = l }});
|
|
return Func(.{ .l = l });
|
|
}
|
|
};
|
|
|
|
return Wrap.inner;
|
|
}
|
|
|
|
pub extern fn setupMiniDump(takeFullDump: bool) callconv(.C) void;
|
|
pub extern fn generateAnException() callconv(.C) void;
|
|
pub extern fn takeDump() callconv(.C) void;
|
|
|
|
pub fn WrapZigFunc(comptime baseFunc: anytype) LuaCFunc {
|
|
return CWrap(FuncWrapper(baseFunc).wrapper);
|
|
}
|
|
|
|
var debugEnabled: bool = false;
|
|
|
|
pub fn debugPrints(enable: bool) void {
|
|
debugEnabled = enable;
|
|
}
|
|
|
|
pub fn FuncWrapper(comptime baseFunc: anytype) type {
|
|
return struct {
|
|
pub fn wrapper(state: LuaState) i32 {
|
|
const Args = std.meta.ArgsTuple(@TypeOf(baseFunc));
|
|
|
|
var args: Args = undefined;
|
|
inline for (std.meta.fields(Args), 0..) |field, index| {
|
|
switch (field.type) {
|
|
f32 => {
|
|
args[index] = @as(f32, @floatCast(state.toNumber(index + 1)));
|
|
},
|
|
f64 => {
|
|
args[index] = state.toNumber(index + 1);
|
|
},
|
|
i32 => {
|
|
args[index] = @intFromFloat(state.toNumber(index + 1));
|
|
},
|
|
else => {
|
|
args[index] = state.toUserdata(field.type, index + 1).?.*;
|
|
},
|
|
}
|
|
}
|
|
state.pop(@intCast(args.len));
|
|
|
|
const rv = @call(.always_inline, baseFunc, args);
|
|
switch (@TypeOf(rv)) {
|
|
i32, u32, i64, u64 => {
|
|
state.pushNumber(@floatFromInt(rv));
|
|
},
|
|
f32, f64 => {
|
|
state.pushNumber(@floatCast(rv));
|
|
},
|
|
void => {
|
|
return 0;
|
|
},
|
|
bool => {
|
|
state.pushBool(rv);
|
|
},
|
|
[]const u8 => {
|
|
@panic("not implemented");
|
|
},
|
|
else => {
|
|
const ud = state.newZigUserdata(@TypeOf(rv)) catch @panic("not implemented");
|
|
ud.* = rv;
|
|
},
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
};
|
|
}
|
|
|
|
var gErrorPrinterContext: ?*anyopaque = null;
|
|
var errorPrintFunc: ?*const fn (?*anyopaque, []const u8) void = undefined;
|
|
|
|
pub fn registerLuaErrorPrinter(context: ?*anyopaque, func: *const fn (?*anyopaque, []const u8) void) void {
|
|
errorPrintFunc = func;
|
|
gErrorPrinterContext = context;
|
|
}
|
|
|
|
pub const LuaState = struct {
|
|
l: ?*c.lua_State = null,
|
|
|
|
pub fn init(settings: LuaStateSettings) !@This() {
|
|
const self: @This() = .{
|
|
.l = c.luaL_newstate(),
|
|
};
|
|
|
|
if (self.l == null) {
|
|
return error.OutOfMemory;
|
|
}
|
|
|
|
if (settings.defaultSetup) {
|
|
self.stopGC();
|
|
self.openLibs();
|
|
self.useGenerationalGC();
|
|
} else {
|
|
self.stopGC();
|
|
self.openLimited();
|
|
self.useGenerationalGC();
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
pub fn deinit(self: @This()) void {
|
|
c.lua_close(self.l);
|
|
}
|
|
|
|
pub fn openLimited(self: @This()) void {
|
|
c.open_limitedio_libs(self.l);
|
|
}
|
|
|
|
pub fn openLibs(self: @This()) void {
|
|
c.luaL_openlibs(self.l);
|
|
}
|
|
|
|
pub fn stopGC(self: @This()) void {
|
|
_ = c.lua_gc(self.l, c.LUA_GCSTOP);
|
|
}
|
|
|
|
pub fn useGenerationalGC(self: @This()) void {
|
|
_ = c.lua_gc(self.l, c.LUA_GCRESTART);
|
|
_ = c.lua_gc(self.l, c.LUA_GCGEN, @as(u32, @intCast(0)), @as(u32, @intCast(0)));
|
|
}
|
|
|
|
pub fn loadFile(self: @This(), filename: []const u8) !void {
|
|
const status = c.luaL_loadfilex(self.l, filename.ptr, null);
|
|
if (status != c.LUA_OK) {
|
|
const errorString = c.lua_tolstring(self.l, -1, 0);
|
|
if (errorPrintFunc) |printFunc| {
|
|
printFunc(gErrorPrinterContext, std.mem.span(errorString));
|
|
} else {
|
|
std.debug.print("{s}\n", .{errorString});
|
|
}
|
|
|
|
return error.LoadFileError;
|
|
}
|
|
}
|
|
|
|
pub fn pcallStack(self: @This(), argc: c_int) !void {
|
|
const status = c.lua_pcallk(self.l, argc, 0, 0, 0, null);
|
|
if (status != c.LUA_OK) {
|
|
const errorString = c.lua_tolstring(self.l, -1, 0);
|
|
if (errorPrintFunc) |printFunc| {
|
|
printFunc(gErrorPrinterContext, std.mem.span(errorString));
|
|
} else {
|
|
std.debug.print("{s}\n", .{errorString});
|
|
}
|
|
return error.LuaRuntimeError;
|
|
}
|
|
}
|
|
|
|
pub fn pcall(self: @This()) !void {
|
|
try self.pcallStack(0);
|
|
}
|
|
|
|
pub fn pushFunction(self: @This(), comptime func: LuaZigFunc) !void {
|
|
const Wrap = struct {
|
|
pub fn inner(l: ?*c.lua_State) callconv(.C) i32 {
|
|
return func(.{ .l = l });
|
|
}
|
|
};
|
|
_ = c.lua_pushcfunction(self.l, Wrap.inner);
|
|
}
|
|
|
|
pub fn pushCFunction(self: @This(), comptime func: LuaCFunc) !void {
|
|
_ = c.lua_pushcfunction(self.l, func);
|
|
}
|
|
|
|
pub fn setGlobal(self: @This(), value: []const u8) !void {
|
|
_ = c.lua_setglobal(self.l, value.ptr);
|
|
}
|
|
|
|
pub fn toNumber(self: @This(), index: i32) f64 {
|
|
return c.lua_tonumberx(self.l, index, null);
|
|
}
|
|
|
|
pub fn pop(self: @This(), count: i32) void {
|
|
c.lua_pop(self.l, count);
|
|
}
|
|
|
|
pub fn pushBool(self: @This(), value: bool) void {
|
|
c.lua_pushboolean(self.l, @as(c_int, @intFromBool(value)));
|
|
}
|
|
|
|
pub fn pushNumber(self: @This(), number: f64) void {
|
|
c.lua_pushnumber(self.l, number);
|
|
}
|
|
|
|
pub fn pushZigFunction(self: @This(), func: anytype) !void {
|
|
try self.pushFunction(FuncWrapper(func).wrapper);
|
|
}
|
|
|
|
pub fn pushString(self: @This(), str: []const u8) !void {
|
|
_ = c.lua_pushlstring(self.l, str.ptr, str.len);
|
|
}
|
|
|
|
pub fn toStringL(self: @This(), index: i32) []const u8 {
|
|
const cstr = c.luaL_tolstring(self.l, index, null);
|
|
const len = std.mem.len(cstr);
|
|
|
|
return cstr[0..len];
|
|
}
|
|
|
|
pub fn toString(self: @This(), index: i32) []const u8 {
|
|
const cstr = c.lua_tolstring(self.l, index, null);
|
|
const len = std.mem.len(cstr);
|
|
|
|
return cstr[0..len];
|
|
}
|
|
|
|
pub fn isString(self: @This(), index: i32) bool {
|
|
return c.lua_isstring(self.l, index) > 0;
|
|
}
|
|
|
|
pub inline fn getGlobal(self: @This(), definition: []const u8) i32 {
|
|
return c.lua_getglobal(self.l, definition.ptr);
|
|
}
|
|
|
|
pub inline fn getTop(self: @This()) i32 {
|
|
return c.lua_gettop(self.l);
|
|
}
|
|
|
|
pub fn loadString(self: @This(), string: []const u8) !void {
|
|
const status = c.luaL_loadstring(self.l, string.ptr);
|
|
if (status != c.LUA_OK) {
|
|
return error.LuaRuntimeError;
|
|
}
|
|
}
|
|
|
|
pub fn toUserdata(self: @This(), comptime T: type, index: i32) ?*T {
|
|
if (debugEnabled) {
|
|
// std.debug.print("toUserdata: {s}", .{@typeName(T)});
|
|
}
|
|
return @ptrCast(@alignCast(c.lua_touserdata(self.l, index)));
|
|
}
|
|
|
|
pub fn checkString(self: @This(), index: i32) []const u8 {
|
|
return std.mem.span(c.luaL_checklstring(self.l, index, null));
|
|
}
|
|
|
|
pub fn checkInteger(self: @This(), index: i32) i32 {
|
|
return @intCast(c.luaL_checkinteger(self.l, index));
|
|
}
|
|
|
|
pub fn pushInteger(self: @This(), index: i32) void {
|
|
c.lua_pushinteger(self.l, index);
|
|
}
|
|
|
|
pub fn argCheck(self: @This(), condition: bool, index: i32, message: []const u8) !void {
|
|
if (!condition) {
|
|
_ = c.luaL_argerror(self.l, index, message.ptr);
|
|
return error.BadArgument;
|
|
}
|
|
}
|
|
|
|
pub fn newLib(self: @This(), lib: LibSpec) !void {
|
|
c.luaL_checkversion(self.l);
|
|
c.lua_createtable(self.l, 0, @as(c_int, @intCast(lib.len)) - 1);
|
|
c.luaL_setfuncs(self.l, lib.ptr, 0);
|
|
}
|
|
|
|
pub fn setFuncs(self: @This(), lib: LibSpec, index: i32) !void {
|
|
c.luaL_setfuncs(self.l, lib.ptr, index);
|
|
}
|
|
|
|
pub fn newUserdata(self: @This(), comptime T: type) !*T {
|
|
return @ptrCast(@alignCast(c.lua_newuserdata(self.l, @as(c_int, @intCast(@sizeOf(T))))));
|
|
}
|
|
|
|
pub fn newMetatable(self: @This(), name: [:0]const u8) !void {
|
|
_ = c.luaL_newmetatable(self.l, name);
|
|
}
|
|
|
|
pub fn getMetatable(self: @This(), index: i32) !void {
|
|
if (c.lua_getmetatable(self.l, index) == 0) {
|
|
return error.BadMetatable;
|
|
}
|
|
}
|
|
|
|
pub fn getMetafield(self: @This(), index: i32, name: [:0]const u8) bool {
|
|
if (c.luaL_getmetafield(self.l, index, name) == c.LUA_TNIL) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
pub fn getMetatableByName(self: @This(), name: [:0]const u8) !void {
|
|
if (c.luaL_getmetatable(self.l, name) == 0) {
|
|
return error.BadMetatable;
|
|
}
|
|
}
|
|
|
|
pub fn setMetatable(self: @This(), index: i32) !void {
|
|
_ = c.lua_setmetatable(self.l, index);
|
|
}
|
|
|
|
pub fn pushValue(self: @This(), index: i32) !void {
|
|
_ = c.lua_pushvalue(self.l, index);
|
|
}
|
|
|
|
pub fn getField(self: @This(), index: i32, name: [:0]const u8) !void {
|
|
_ = c.lua_getfield(self.l, index, name);
|
|
if (self.isNil(1)) {
|
|
return error.FieldNotFound;
|
|
}
|
|
}
|
|
|
|
pub fn isNumber(self: @This(), index: i32) bool {
|
|
const typeIndex = c.lua_type(self.l, index);
|
|
return (typeIndex == c.LUA_TNUMBER);
|
|
}
|
|
|
|
pub fn isNil(self: @This(), index: i32) bool {
|
|
const typeIndex = c.lua_type(self.l, index);
|
|
return (typeIndex == c.LUA_TNIL);
|
|
}
|
|
|
|
pub fn isUserdata(self: @This(), index: i32) bool {
|
|
const typeIndex = c.lua_type(self.l, index);
|
|
return (typeIndex == c.LUA_TUSERDATA);
|
|
}
|
|
|
|
pub fn isTable(self: @This(), index: i32) bool {
|
|
return c.lua_type(self.l, index) == c.LUA_TTABLE;
|
|
}
|
|
|
|
pub fn getFieldAsNumber(self: @This(), index: i32, name: [:0]const u8) ?f64 {
|
|
const t = c.lua_getfield(self.l, index, name);
|
|
defer self.pop(1);
|
|
if (t != c.LUA_TNUMBER) {
|
|
return null;
|
|
}
|
|
const n = self.toNumber(self.getTop());
|
|
return n;
|
|
}
|
|
|
|
pub fn newZigUserdata(self: @This(), comptime T: type) !*T {
|
|
const rv = try self.newUserdata(T);
|
|
if (@hasDecl(T, "MetatableName")) {
|
|
try self.getMetatableByName(T.MetatableName);
|
|
} else if (@hasDecl(T, "PodDataTable")) {
|
|
try self.getMetatableByName(@ptrCast(T.PodDataTable.name));
|
|
} else {
|
|
@panic("todo");
|
|
}
|
|
try self.setMetatable(-2);
|
|
return rv;
|
|
}
|
|
|
|
pub fn emitError(self: @This(), errorMessage: [:0]const u8) void {
|
|
_ = c.luaL_error(self.l, errorMessage);
|
|
}
|
|
|
|
pub fn createLibrary(self: @This(), libName: []const u8, spec: LibSpec) !void {
|
|
try self.newLib(spec);
|
|
try self.setGlobal(libName);
|
|
}
|
|
};
|