diff --git a/src/lib.zig b/src/lib.zig index 885cff1..68083ab 100644 --- a/src/lib.zig +++ b/src/lib.zig @@ -3055,7 +3055,7 @@ pub const Lua = struct { /// Pushes any valid zig value onto the stack, /// Works with ints, floats, booleans, structs, - /// optionals, and strings + /// tagged unions, optionals, and strings pub fn pushAny(lua: *Lua, value: anytype) !void { switch (@typeInfo(@TypeOf(value))) { .Int, .ComptimeInt => { @@ -3079,7 +3079,7 @@ pub const Lua = struct { .C, .Many, .Slice => { lua.createTable(0, 0); for (value, 0..) |index_value, i| { - try lua.pushAny(i); + try lua.pushAny(i + 1); try lua.pushAny(index_value); lua.setTable(-3); } @@ -3089,11 +3089,14 @@ pub const Lua = struct { .Array => { lua.createTable(0, 0); for (value, 0..) |index_value, i| { - try lua.pushAny(i); + try lua.pushAny(i + 1); try lua.pushAny(index_value); lua.setTable(-3); } }, + .Vector => |info| { + try lua.pushAny(@as([info.len]info.child, value)); + }, .Bool => { lua.pushBoolean(value); }, @@ -3115,10 +3118,25 @@ pub const Lua = struct { lua.setTable(-3); } }, + .Union => |info| { + if (info.tag_type == null) @compileError("Parameter type is not a tagged union"); + lua.createTable(0, 0); + errdefer lua.pop(1); + try lua.pushAnyString(@tagName(value)); + + inline for (info.fields) |field| { + if (std.mem.eql(u8, field.name, @tagName(value))) { + try lua.pushAny(@field(value, field.name)); + } + } + lua.setTable(-3); + }, .Fn => { lua.autoPushFunction(value); }, - .Void => {}, + .Void => { + lua.createTable(0, 0); + }, else => { @compileLog(value); @compileError("Invalid type given"); @@ -3189,6 +3207,28 @@ pub const Lua = struct { }, } }, + .Array, .Vector => { + const child = std.meta.Child(T); + const arr_len = switch (@typeInfo(T)) { + inline else => |i| i.len, + }; + var result: T = undefined; + lua.pushValue(index); + defer lua.pop(1); + + for (0..arr_len) |i| { + if (lua.getMetaField(-1, "__index")) |_| { + lua.pushValue(-2); + lua.pushInteger(@intCast(i + 1)); + lua.call(2, 1); + } else |_| { + _ = lua.rawGetIndex(-1, @intCast(i + 1)); + } + defer lua.pop(1); + result[i] = try lua.toAny(child, -1); + } + return result; + }, .Pointer => |info| { if (comptime isTypeString(info)) { const string: [*:0]const u8 = try lua.toString(index); @@ -3226,6 +3266,25 @@ pub const Lua = struct { .Struct => { return try lua.toStruct(T, a, allow_alloc, index); }, + .Union => |u| { + if (u.tag_type == null) @compileError("Parameter type is not a tagged union"); + if (!lua.isTable(index)) return error.ValueIsNotATable; + + lua.pushValue(index); + defer lua.pop(1); + lua.pushNil(); + if (lua.next(-2)) { + defer lua.pop(2); + const key = try lua.toAny([]const u8, -2); + inline for (u.fields) |field| { + if (std.mem.eql(u8, key, field.name)) { + return @unionInit(T, field.name, try lua.toAny(field.type, -1)); + } + } + return error.InvalidTagName; + } + return error.TableIsEmpty; + }, .Optional => { if (lua.isNil(index)) { return null; @@ -3233,6 +3292,17 @@ pub const Lua = struct { return try lua.toAnyInternal(@typeInfo(T).Optional.child, a, allow_alloc, index); } }, + .Void => { + if (!lua.isTable(index)) return error.ValueIsNotATable; + lua.pushValue(index); + defer lua.pop(1); + lua.pushNil(); + if (lua.next(-2)) { + lua.pop(2); + return error.VoidTableIsNotEmpty; + } + return void{}; + }, else => { @compileError("Invalid parameter type"); }, @@ -3253,8 +3323,8 @@ pub const Lua = struct { for (1..size + 1) |i| { _ = try lua.pushAny(i); _ = lua.getTable(index); + defer lua.pop(1); result[i - 1] = try lua.toAnyInternal(ChildType, a, true, -1); - lua.pop(1); } return result; @@ -3270,7 +3340,6 @@ pub const Lua = struct { if (!lua.isTable(index)) { return error.ValueNotATable; } - std.debug.assert(lua.typeOf(index) == .table); var result: T = undefined; @@ -3279,11 +3348,11 @@ pub const Lua = struct { _ = lua.pushString(field_name); const lua_field_type = lua.getTable(index); + defer lua.pop(1); if (lua_field_type == .nil) { if (field.default_value) |default_value| { @field(result, field.name) = @as(*const field.type, @ptrCast(@alignCast(default_value))).*; } else { - lua.pop(1); return error.LuaTableMissingValue; } } else { @@ -3291,7 +3360,6 @@ pub const Lua = struct { @field(result, field.name) = try lua.toAnyInternal(field.type, a, allow_alloc, -1); std.debug.assert(stack_size_before_call == lua.getTop()); } - lua.pop(1); //pop the value off the stack } return result; diff --git a/src/tests.zig b/src/tests.zig index c86f2af..f5d8abf 100644 --- a/src/tests.zig +++ b/src/tests.zig @@ -2437,6 +2437,13 @@ test "toAny" { _ = lua.pushString("hello"); const my_enum = try lua.toAny(MyEnumType, -1); try testing.expect(my_enum == MyEnumType.hello); + + //void + try lua.doString("value = {}\nvalue_err = {a = 5}"); + _ = try lua.getGlobal("value"); + try testing.expectEqual(void{}, try lua.toAny(void, -1)); + _ = try lua.getGlobal("value_err"); + try testing.expectError(error.VoidTableIsNotEmpty, lua.toAny(void, -1)); } test "toAny struct" { @@ -2472,7 +2479,7 @@ test "toAny struct recursive" { try lua.doString( \\value = { \\ ["foo"] = 10, - \\ ["bar"] = true, + \\ ["bar"] = false, \\ ["bizz"] = "hi", \\ ["meep"] = { \\ ["a"] = nil @@ -2482,7 +2489,43 @@ test "toAny struct recursive" { _ = try lua.getGlobal("value"); const my_struct = try lua.toAny(MyType, -1); - _ = my_struct; + try testing.expectEqualDeep(MyType{}, my_struct); +} + +test "toAny tagged union" { + var lua = try Lua.init(&testing.allocator); + defer lua.deinit(); + + const MyType = union(enum) { + a: i32, + b: bool, + c: []const u8, + d: struct { t0: f64, t1: f64 }, + }; + + try lua.doString( + \\value0 = { + \\ ["c"] = "Hello, world!", + \\} + \\value1 = { + \\ ["d"] = {t0 = 5.0, t1 = -3.0}, + \\} + \\value2 = { + \\ ["a"] = 1000, + \\} + ); + + _ = try lua.getGlobal("value0"); + const my_struct0 = try lua.toAny(MyType, -1); + try testing.expectEqualDeep(MyType{ .c = "Hello, world!" }, my_struct0); + + _ = try lua.getGlobal("value1"); + const my_struct1 = try lua.toAny(MyType, -1); + try testing.expectEqualDeep(MyType{ .d = .{ .t0 = 5.0, .t1 = -3.0 } }, my_struct1); + + _ = try lua.getGlobal("value2"); + const my_struct2 = try lua.toAny(MyType, -1); + try testing.expectEqualDeep(MyType{ .a = 1000 }, my_struct2); } test "toAny slice" { @@ -2502,6 +2545,34 @@ test "toAny slice" { ); } +test "toAny array" { + var lua = try Lua.init(&testing.allocator); + defer lua.deinit(); + + const arr: [5]?u32 = .{ 1, 2, null, 4, 5 }; + const program = + \\array= {1, 2, nil, 4, 5} + ; + try lua.doString(program); + _ = try lua.getGlobal("array"); + const array = try lua.toAny([5]?u32, -1); + try testing.expectEqual(arr, array); +} + +test "toAny vector" { + var lua = try Lua.init(&testing.allocator); + defer lua.deinit(); + + const vec = @Vector(4, bool){ true, false, false, true }; + const program = + \\vector= {true, false, false, true} + ; + try lua.doString(program); + _ = try lua.getGlobal("vector"); + const vector = try lua.toAny(@Vector(4, bool), -1); + try testing.expectEqual(vec, vector); +} + test "pushAny" { var lua = try Lua.init(&testing.allocator); defer lua.deinit(); @@ -2541,6 +2612,10 @@ test "pushAny" { try lua.pushAny(MyEnumType.goodbye); const my_enum = try lua.toAny(MyEnumType, -1); try testing.expect(my_enum == MyEnumType.goodbye); + + //void + try lua.pushAny(void{}); + try testing.expectEqual(void{}, try lua.toAny(void, -1)); } test "pushAny struct" { @@ -2559,14 +2634,46 @@ test "pushAny struct" { try testing.expect(value.bar == (MyType{}).bar); } -test "pushAny slice/array" { +test "pushAny tagged union" { + var lua = try Lua.init(&testing.allocator); + defer lua.deinit(); + + const MyType = union(enum) { + a: i32, + b: bool, + c: []const u8, + d: struct { t0: f64, t1: f64 }, + }; + + const t0 = MyType{ .d = .{ .t0 = 5.0, .t1 = -3.0 } }; + try lua.pushAny(t0); + const value0 = try lua.toAny(MyType, -1); + try testing.expectEqualDeep(t0, value0); + + const t1 = MyType{ .c = "Hello, world!" }; + try lua.pushAny(t1); + const value1 = try lua.toAny(MyType, -1); + try testing.expectEqualDeep(t1, value1); +} + +test "pushAny toAny slice/array/vector" { var lua = try Lua.init(&testing.allocator); defer lua.deinit(); var my_array = [_]u32{ 1, 2, 3, 4, 5 }; const my_slice: []u32 = my_array[0..]; + const my_vector: @Vector(5, u32) = .{ 1, 2, 3, 4, 5 }; try lua.pushAny(my_slice); try lua.pushAny(my_array); + try lua.pushAny(my_vector); + const vector = try lua.toAny(@TypeOf(my_vector), -1); + const array = try lua.toAny(@TypeOf(my_array), -2); + const slice = try lua.toAnyAlloc(@TypeOf(my_slice), -3); + defer slice.deinit(); + + try testing.expectEqual(my_array, array); + try testing.expectEqualDeep(my_slice, slice.value); + try testing.expectEqual(my_vector, vector); } fn foo(a: i32, b: i32) i32 {