first commit
This commit is contained in:
commit
a28b585cd4
7 changed files with 504 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
.zig-cache/
|
||||||
4
README.md
Normal file
4
README.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
**Dependancies**
|
||||||
|
```bash
|
||||||
|
sudo pacman -Sy lua --needed
|
||||||
|
```
|
||||||
34
build.zig
Normal file
34
build.zig
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub fn build(b: *std.Build) void {
|
||||||
|
const target = b.standardTargetOptions(.{});
|
||||||
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
|
|
||||||
|
const mod = b.addModule("zla", .{
|
||||||
|
.root_source_file = b.path("src/root.zig"),
|
||||||
|
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
|
||||||
|
const mod_tests = b.addTest(.{
|
||||||
|
.root_module = mod,
|
||||||
|
});
|
||||||
|
|
||||||
|
const run_mod_tests = b.addRunArtifact(mod_tests);
|
||||||
|
|
||||||
|
const test_step = b.step("test", "Run tests");
|
||||||
|
test_step.dependOn(&run_mod_tests.step);
|
||||||
|
|
||||||
|
//
|
||||||
|
// SYSTEM DEPENDENCIES
|
||||||
|
//
|
||||||
|
|
||||||
|
const luajit = b.addTranslateC(.{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
.root_source_file = b.path("./src/vendors/luajit/luajit.h"),
|
||||||
|
});
|
||||||
|
mod.addImport("luajit", luajit.createModule());
|
||||||
|
mod.linkSystemLibrary("luajit", .{});
|
||||||
|
}
|
||||||
17
build.zig.zon
Normal file
17
build.zig.zon
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
.{
|
||||||
|
.name = .zla,
|
||||||
|
|
||||||
|
.version = "0.0.0",
|
||||||
|
|
||||||
|
.fingerprint = 0x55da41f18abfadcf,
|
||||||
|
|
||||||
|
.minimum_zig_version = "0.16.0",
|
||||||
|
|
||||||
|
.dependencies = .{},
|
||||||
|
|
||||||
|
.paths = .{
|
||||||
|
"build.zig",
|
||||||
|
"build.zig.zon",
|
||||||
|
"src",
|
||||||
|
},
|
||||||
|
}
|
||||||
424
src/root.zig
Normal file
424
src/root.zig
Normal file
|
|
@ -0,0 +1,424 @@
|
||||||
|
//! ----------------------------------------------------
|
||||||
|
//! ----------------------------------------------------
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const lua = @import("luajit");
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
//
|
||||||
|
// FIELDS
|
||||||
|
//
|
||||||
|
|
||||||
|
state: *lua.lua_State,
|
||||||
|
err: ?[*:0]const u8 = null,
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn init() !Self {
|
||||||
|
const state = lua.luaL_newstate() orelse return error.LuaStateFailed;
|
||||||
|
lua.luaL_openlibs(state);
|
||||||
|
return .{ .state = state };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn deinit(self: *Self) void {
|
||||||
|
lua.lua_close(self.state);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// EXECUTION
|
||||||
|
//
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn exec(self: *Self, comptime ReturnType: type, code: []const u8) !ReturnType {
|
||||||
|
if (lua.luaL_loadbuffer(self.state, code.ptr, code.len, "") != 0) {
|
||||||
|
return self.luaError();
|
||||||
|
}
|
||||||
|
return self.pcall(ReturnType, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn execFromFile(self: *Self, comptime ReturnType: type, path: [*:0]const u8) !ReturnType {
|
||||||
|
if (lua.luaL_dofile(self.state, path) != 0) {
|
||||||
|
return self.luaError();
|
||||||
|
}
|
||||||
|
if (ReturnType == void) return {};
|
||||||
|
return self.readReturn(ReturnType);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// GLOBALS
|
||||||
|
//
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn getGlobal(self: *Self, name: [*:0]const u8) void {
|
||||||
|
lua.lua_getglobal(self.state, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn setGlobal(self: *Self, name: [*:0]const u8) void {
|
||||||
|
lua.lua_setglobal(self.state, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// STACK
|
||||||
|
//
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn getTop(self: *Self) c_int {
|
||||||
|
return lua.lua_gettop(self.state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn pop(self: *Self, n: c_int) void {
|
||||||
|
lua.lua_settop(self.state, -n - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn pushNil(self: *Self) void {
|
||||||
|
lua.lua_pushnil(self.state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn push(self: *Self, value: anytype) void {
|
||||||
|
const T = @TypeOf(value);
|
||||||
|
switch (@typeInfo(T)) {
|
||||||
|
.void, .null => lua.lua_pushnil(self.state),
|
||||||
|
.optional => {
|
||||||
|
if (value) |v| {
|
||||||
|
self.push(v);
|
||||||
|
} else {
|
||||||
|
lua.lua_pushnil(self.state);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.bool => lua.lua_pushboolean(self.state, @intFromBool(value)),
|
||||||
|
.int => lua.lua_pushinteger(self.state, @intCast(value)),
|
||||||
|
.float => lua.lua_pushnumber(self.state, @floatCast(value)),
|
||||||
|
.pointer => |p| {
|
||||||
|
if (p.child == u8) {
|
||||||
|
if (p.sentinel != null) {
|
||||||
|
lua.lua_pushstring(self.state, @ptrCast(value.ptr));
|
||||||
|
} else {
|
||||||
|
lua.lua_pushlstring(self.state, value.ptr, value.len);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const child_info = @typeInfo(p.child);
|
||||||
|
if (child_info == .array and child_info.array.child == u8 and child_info.array.sentinel_ptr != null) {
|
||||||
|
lua.lua_pushstring(self.state, @ptrCast(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
else => @compileError("unsupported push type: " ++ @typeName(T)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn add(self: *Self, values: anytype) void {
|
||||||
|
inline for (values) |v| {
|
||||||
|
self.push(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// READING
|
||||||
|
//
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn to(self: *Self, comptime T: type, idx: c_int) T {
|
||||||
|
return switch (T) {
|
||||||
|
bool => lua.lua_toboolean(self.state, idx) != 0,
|
||||||
|
i8, i16, i32, i64, u8, u16, u32, u64 => @intCast(lua.lua_tointeger(self.state, idx)),
|
||||||
|
f32, f64 => @floatCast(lua.lua_tonumber(self.state, idx)),
|
||||||
|
[:0]const u8 => ptr: {
|
||||||
|
const ptr = lua.lua_tolstring(self.state, idx, null) orelse break :ptr "";
|
||||||
|
break :ptr std.mem.sliceTo(ptr, 0);
|
||||||
|
},
|
||||||
|
[]const u8 => ptr: {
|
||||||
|
var len: usize = 0;
|
||||||
|
const ptr = lua.lua_tolstring(self.state, idx, &len) orelse break :ptr "";
|
||||||
|
break :ptr ptr[0..len];
|
||||||
|
},
|
||||||
|
else => @compileError("unsupported to type: " ++ @typeName(T)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn toOwned(self: *Self, comptime T: type, alloc: std.mem.Allocator, idx: c_int) !T {
|
||||||
|
return switch (T) {
|
||||||
|
[]const u8 => {
|
||||||
|
var len: usize = 0;
|
||||||
|
const ptr = lua.lua_tolstring(self.state, idx, &len) orelse return error.InvalidValue;
|
||||||
|
return try alloc.dupe(u8, ptr[0..len]);
|
||||||
|
},
|
||||||
|
[:0]const u8 => {
|
||||||
|
const ptr = lua.lua_tolstring(self.state, idx, null) orelse return error.InvalidValue;
|
||||||
|
return try alloc.dupeZ(u8, std.mem.sliceTo(ptr, 0));
|
||||||
|
},
|
||||||
|
else => self.to(T, idx),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// CALLING
|
||||||
|
//
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
fn readReturn(self: *Self, comptime ReturnType: type) ReturnType {
|
||||||
|
const info = @typeInfo(ReturnType);
|
||||||
|
if (info == .@"struct" and info.@"struct".is_tuple) {
|
||||||
|
const n: c_int = @intCast(info.@"struct".fields.len);
|
||||||
|
var result: ReturnType = undefined;
|
||||||
|
inline for (info.@"struct".fields, 0..) |field, i| {
|
||||||
|
result[i] = self.to(field.type, -n + @as(c_int, @intCast(i)));
|
||||||
|
}
|
||||||
|
self.pop(n);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
const result = self.to(ReturnType, -1);
|
||||||
|
self.pop(1);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn pcall(self: *Self, comptime ReturnType: type, nargs: u32) !ReturnType {
|
||||||
|
const nresults: c_int = if (ReturnType == void) 0 else switch (@typeInfo(ReturnType)) {
|
||||||
|
.@"struct" => |s| if (s.is_tuple) @intCast(s.fields.len) else @as(c_int, 1),
|
||||||
|
else => 1,
|
||||||
|
};
|
||||||
|
if (lua.lua_pcall(self.state, @intCast(nargs), nresults, 0) != 0) {
|
||||||
|
return self.luaError();
|
||||||
|
}
|
||||||
|
if (ReturnType == void) return {};
|
||||||
|
return self.readReturn(ReturnType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn call(
|
||||||
|
self: *Self,
|
||||||
|
comptime ReturnType: type,
|
||||||
|
name: [*:0]const u8,
|
||||||
|
args: anytype,
|
||||||
|
) !ReturnType {
|
||||||
|
self.getGlobal(name);
|
||||||
|
inline for (args) |arg| {
|
||||||
|
self.push(arg);
|
||||||
|
}
|
||||||
|
return self.pcall(ReturnType, args.len);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// TABLES
|
||||||
|
//
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn tableGet(self: *Self, idx: c_int, key: anytype) void {
|
||||||
|
self.push(key);
|
||||||
|
lua.lua_gettable(self.state, if (idx < 0) idx - 1 else idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn tableSet(self: *Self, idx: c_int, key: anytype, value: anytype) void {
|
||||||
|
self.push(key);
|
||||||
|
self.push(value);
|
||||||
|
lua.lua_settable(self.state, if (idx < 0) idx - 2 else idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// REGISTRATION
|
||||||
|
//
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn register(self: *Self, name: [*:0]const u8, comptime func: anytype) void {
|
||||||
|
const fn_info = @typeInfo(@TypeOf(func)).@"fn";
|
||||||
|
const Return = fn_info.return_type orelse void;
|
||||||
|
|
||||||
|
const Wrapper = struct {
|
||||||
|
fn luaFunc(state: ?*lua.lua_State) callconv(.c) c_int {
|
||||||
|
var zl = Self{ .state = state.? };
|
||||||
|
|
||||||
|
const args: std.meta.ArgsTuple(@TypeOf(func)) = args: {
|
||||||
|
var a: std.meta.ArgsTuple(@TypeOf(func)) = undefined;
|
||||||
|
inline for (fn_info.params, 0..) |param, i| {
|
||||||
|
a[i] = zl.to(param.type.?, @intCast(i + 1));
|
||||||
|
}
|
||||||
|
break :args a;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ret = @call(.auto, func, args);
|
||||||
|
if (Return == void) return 0;
|
||||||
|
zl.push(ret);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
lua.lua_pushcfunction(self.state, Wrapper.luaFunc);
|
||||||
|
lua.lua_setglobal(self.state, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn registerWithCtx(
|
||||||
|
self: *Self,
|
||||||
|
name: [*:0]const u8,
|
||||||
|
comptime func: anytype,
|
||||||
|
ctx: anytype,
|
||||||
|
) void {
|
||||||
|
const CtxType = @TypeOf(ctx);
|
||||||
|
const fn_info = @typeInfo(@TypeOf(func)).@"fn";
|
||||||
|
const Return = fn_info.return_type orelse void;
|
||||||
|
|
||||||
|
const Wrapper = struct {
|
||||||
|
fn luaFunc(state: ?*lua.lua_State) callconv(.c) c_int {
|
||||||
|
var zl = Self{ .state = state.? };
|
||||||
|
|
||||||
|
const ctx_ptr: CtxType = @ptrCast(@alignCast(
|
||||||
|
lua.lua_touserdata(state, lua.lua_upvalueindex(1)),
|
||||||
|
));
|
||||||
|
|
||||||
|
const args: std.meta.ArgsTuple(@TypeOf(func)) = args: {
|
||||||
|
var a: std.meta.ArgsTuple(@TypeOf(func)) = undefined;
|
||||||
|
inline for (fn_info.params, 0..) |param, i| {
|
||||||
|
if (i == 0) {
|
||||||
|
a[i] = ctx_ptr;
|
||||||
|
} else {
|
||||||
|
a[i] = zl.to(param.type.?, @intCast(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break :args a;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ret = @call(.auto, func, args);
|
||||||
|
if (Return == void) return 0;
|
||||||
|
zl.push(ret);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
lua.lua_pushlightuserdata(self.state, @ptrCast(@constCast(ctx)));
|
||||||
|
lua.lua_pushcclosure(self.state, Wrapper.luaFunc, 1);
|
||||||
|
lua.lua_setglobal(self.state, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn registerMany(self: *Self, values: anytype) void {
|
||||||
|
inline for (values) |value| {
|
||||||
|
self.register(value.name, value.func);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// ERRORS
|
||||||
|
//
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn lastError(self: *Self) ?[:0]const u8 {
|
||||||
|
if (self.err) |p| {
|
||||||
|
return std.mem.sliceTo(p, 0);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
fn luaError(self: *Self) error{LuaError} {
|
||||||
|
self.err = lua.lua_tolstring(self.state, -1, null);
|
||||||
|
if (self.err) |p| {
|
||||||
|
std.debug.print("lua error: {s}\n", .{p[0..std.mem.len(p)]});
|
||||||
|
}
|
||||||
|
lua.lua_pop(self.state, 1);
|
||||||
|
return error.LuaError;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// TESTS
|
||||||
|
//
|
||||||
|
|
||||||
|
fn spawnEntity(name: []const u8, x: f64, y: f64) void {
|
||||||
|
std.debug.print("spawned '{s}' at ({d:.1}, {d:.1})\n", .{ name, x, y });
|
||||||
|
}
|
||||||
|
|
||||||
|
const Ctx = struct {
|
||||||
|
counter: i64 = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn increment(ctx: *Ctx) i64 {
|
||||||
|
ctx.counter += 1;
|
||||||
|
return ctx.counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
test "exec" {
|
||||||
|
// --- ZLA ---
|
||||||
|
var zl = try Self.init();
|
||||||
|
defer zl.deinit();
|
||||||
|
|
||||||
|
// --- REGISTER FUNCTIONS TO LUA ---
|
||||||
|
zl.register("spawnEntity", spawnEntity);
|
||||||
|
|
||||||
|
// --- EXEC FILE ---
|
||||||
|
try zl.exec(void, @embedFile("scripts/basic.lua"));
|
||||||
|
// try zl.execFromFile(void, "src/scripts/basic.lua");
|
||||||
|
|
||||||
|
// --- CALL ---
|
||||||
|
try zl.call(void, "hello", .{"World"});
|
||||||
|
try zl.call(void, "hello", .{"World"});
|
||||||
|
|
||||||
|
// --- CALL RETURN ---
|
||||||
|
const sum = try zl.call(i64, "add", .{ @as(i64, 3), @as(i64, 4) });
|
||||||
|
std.debug.print("Result: {}\n", .{sum});
|
||||||
|
|
||||||
|
// --- MULTI RETURN ---
|
||||||
|
const coords = try zl.call(struct { f64, f64 }, "getCoords", .{});
|
||||||
|
std.debug.print("coords: ({d:.1}, {d:.1})\n", .{ coords[0], coords[1] });
|
||||||
|
|
||||||
|
// --- EXEC WITH RETURN ---
|
||||||
|
const val = try zl.exec(i64, "return 6 * 7");
|
||||||
|
try std.testing.expectEqual(@as(i64, 42), val);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Nicer tables / generic
|
||||||
|
|
||||||
|
// test "tables" {
|
||||||
|
// // --- ZLA ---
|
||||||
|
// var zl = try Self.init();
|
||||||
|
// defer zl.deinit();
|
||||||
|
//
|
||||||
|
// var ctx = Ctx{};
|
||||||
|
// zl.registerWithCtx("increment", increment, &ctx);
|
||||||
|
//
|
||||||
|
// // --- TABLE ---
|
||||||
|
// zl.getGlobal("config");
|
||||||
|
// zl.tableGet(-1, "width");
|
||||||
|
// const width = zl.to(i64, -1);
|
||||||
|
// std.debug.print("width: {}\n", .{width});
|
||||||
|
// zl.pop(2);
|
||||||
|
//
|
||||||
|
// // --- CTX ---
|
||||||
|
// const n = try zl.call(i64, "increment", .{});
|
||||||
|
// std.debug.print("counter: {}\n", .{n});
|
||||||
|
//
|
||||||
|
// // --- LAST ERROR ---
|
||||||
|
// zl.exec(void, "nonexistent()") catch {};
|
||||||
|
// const err = zl.lastError();
|
||||||
|
// try std.testing.expect(err != null);
|
||||||
|
// std.debug.print("caught: {s}\n", .{err.?});
|
||||||
|
// }
|
||||||
20
src/scripts/basic.lua
Normal file
20
src/scripts/basic.lua
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
function hello(name)
|
||||||
|
if name ~= nil then
|
||||||
|
io.stderr:write("Hello " .. name .. "\n")
|
||||||
|
else
|
||||||
|
io.stderr:write("Hello nil\n")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function add(a, b)
|
||||||
|
return a + b
|
||||||
|
end
|
||||||
|
|
||||||
|
function getCoords()
|
||||||
|
return 1.5, 2.5
|
||||||
|
end
|
||||||
|
|
||||||
|
config = {
|
||||||
|
width = 800,
|
||||||
|
height = 600,
|
||||||
|
}
|
||||||
4
src/vendors/luajit/luajit.h
vendored
Normal file
4
src/vendors/luajit/luajit.h
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
#include <luajit-2.1/lauxlib.h>
|
||||||
|
#include <luajit-2.1/lua.h>
|
||||||
|
#include <luajit-2.1/lualib.h>
|
||||||
|
#include <luajit-2.1/luajit.h>
|
||||||
Loading…
Add table
Reference in a new issue