Skip to content

Commit df0c1aa

Browse files
T1nk3r1VisenDev
andauthored
Add support for tagged unions, vectors and arrays in pushAny and toAny (#66)
* Add support for tagged unions in pushAny and toAny * Add new test cases for tagged unions * Add support for void in pushAny and toAny * New tests * Make pushAny arrays and slices one-based * Add support for vectors and arrays in pushAny and toAny * Replace getIndex with more compatible code in toAny * Fix tests --------- Co-authored-by: Robert Burnett <dr.slate2@gmail.com>
1 parent 67fc4a9 commit df0c1aa

File tree

2 files changed

+186
-11
lines changed

2 files changed

+186
-11
lines changed

src/lib.zig

Lines changed: 76 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3055,7 +3055,7 @@ pub const Lua = struct {
30553055

30563056
/// Pushes any valid zig value onto the stack,
30573057
/// Works with ints, floats, booleans, structs,
3058-
/// optionals, and strings
3058+
/// tagged unions, optionals, and strings
30593059
pub fn pushAny(lua: *Lua, value: anytype) !void {
30603060
switch (@typeInfo(@TypeOf(value))) {
30613061
.Int, .ComptimeInt => {
@@ -3079,7 +3079,7 @@ pub const Lua = struct {
30793079
.C, .Many, .Slice => {
30803080
lua.createTable(0, 0);
30813081
for (value, 0..) |index_value, i| {
3082-
try lua.pushAny(i);
3082+
try lua.pushAny(i + 1);
30833083
try lua.pushAny(index_value);
30843084
lua.setTable(-3);
30853085
}
@@ -3089,11 +3089,14 @@ pub const Lua = struct {
30893089
.Array => {
30903090
lua.createTable(0, 0);
30913091
for (value, 0..) |index_value, i| {
3092-
try lua.pushAny(i);
3092+
try lua.pushAny(i + 1);
30933093
try lua.pushAny(index_value);
30943094
lua.setTable(-3);
30953095
}
30963096
},
3097+
.Vector => |info| {
3098+
try lua.pushAny(@as([info.len]info.child, value));
3099+
},
30973100
.Bool => {
30983101
lua.pushBoolean(value);
30993102
},
@@ -3115,10 +3118,25 @@ pub const Lua = struct {
31153118
lua.setTable(-3);
31163119
}
31173120
},
3121+
.Union => |info| {
3122+
if (info.tag_type == null) @compileError("Parameter type is not a tagged union");
3123+
lua.createTable(0, 0);
3124+
errdefer lua.pop(1);
3125+
try lua.pushAnyString(@tagName(value));
3126+
3127+
inline for (info.fields) |field| {
3128+
if (std.mem.eql(u8, field.name, @tagName(value))) {
3129+
try lua.pushAny(@field(value, field.name));
3130+
}
3131+
}
3132+
lua.setTable(-3);
3133+
},
31183134
.Fn => {
31193135
lua.autoPushFunction(value);
31203136
},
3121-
.Void => {},
3137+
.Void => {
3138+
lua.createTable(0, 0);
3139+
},
31223140
else => {
31233141
@compileLog(value);
31243142
@compileError("Invalid type given");
@@ -3189,6 +3207,28 @@ pub const Lua = struct {
31893207
},
31903208
}
31913209
},
3210+
.Array, .Vector => {
3211+
const child = std.meta.Child(T);
3212+
const arr_len = switch (@typeInfo(T)) {
3213+
inline else => |i| i.len,
3214+
};
3215+
var result: T = undefined;
3216+
lua.pushValue(index);
3217+
defer lua.pop(1);
3218+
3219+
for (0..arr_len) |i| {
3220+
if (lua.getMetaField(-1, "__index")) |_| {
3221+
lua.pushValue(-2);
3222+
lua.pushInteger(@intCast(i + 1));
3223+
lua.call(2, 1);
3224+
} else |_| {
3225+
_ = lua.rawGetIndex(-1, @intCast(i + 1));
3226+
}
3227+
defer lua.pop(1);
3228+
result[i] = try lua.toAny(child, -1);
3229+
}
3230+
return result;
3231+
},
31923232
.Pointer => |info| {
31933233
if (comptime isTypeString(info)) {
31943234
const string: [*:0]const u8 = try lua.toString(index);
@@ -3226,13 +3266,43 @@ pub const Lua = struct {
32263266
.Struct => {
32273267
return try lua.toStruct(T, a, allow_alloc, index);
32283268
},
3269+
.Union => |u| {
3270+
if (u.tag_type == null) @compileError("Parameter type is not a tagged union");
3271+
if (!lua.isTable(index)) return error.ValueIsNotATable;
3272+
3273+
lua.pushValue(index);
3274+
defer lua.pop(1);
3275+
lua.pushNil();
3276+
if (lua.next(-2)) {
3277+
defer lua.pop(2);
3278+
const key = try lua.toAny([]const u8, -2);
3279+
inline for (u.fields) |field| {
3280+
if (std.mem.eql(u8, key, field.name)) {
3281+
return @unionInit(T, field.name, try lua.toAny(field.type, -1));
3282+
}
3283+
}
3284+
return error.InvalidTagName;
3285+
}
3286+
return error.TableIsEmpty;
3287+
},
32293288
.Optional => {
32303289
if (lua.isNil(index)) {
32313290
return null;
32323291
} else {
32333292
return try lua.toAnyInternal(@typeInfo(T).Optional.child, a, allow_alloc, index);
32343293
}
32353294
},
3295+
.Void => {
3296+
if (!lua.isTable(index)) return error.ValueIsNotATable;
3297+
lua.pushValue(index);
3298+
defer lua.pop(1);
3299+
lua.pushNil();
3300+
if (lua.next(-2)) {
3301+
lua.pop(2);
3302+
return error.VoidTableIsNotEmpty;
3303+
}
3304+
return void{};
3305+
},
32363306
else => {
32373307
@compileError("Invalid parameter type");
32383308
},
@@ -3253,8 +3323,8 @@ pub const Lua = struct {
32533323
for (1..size + 1) |i| {
32543324
_ = try lua.pushAny(i);
32553325
_ = lua.getTable(index);
3326+
defer lua.pop(1);
32563327
result[i - 1] = try lua.toAnyInternal(ChildType, a, true, -1);
3257-
lua.pop(1);
32583328
}
32593329

32603330
return result;
@@ -3270,7 +3340,6 @@ pub const Lua = struct {
32703340
if (!lua.isTable(index)) {
32713341
return error.ValueNotATable;
32723342
}
3273-
std.debug.assert(lua.typeOf(index) == .table);
32743343

32753344
var result: T = undefined;
32763345

@@ -3279,19 +3348,18 @@ pub const Lua = struct {
32793348
_ = lua.pushString(field_name);
32803349

32813350
const lua_field_type = lua.getTable(index);
3351+
defer lua.pop(1);
32823352
if (lua_field_type == .nil) {
32833353
if (field.default_value) |default_value| {
32843354
@field(result, field.name) = @as(*const field.type, @ptrCast(@alignCast(default_value))).*;
32853355
} else {
3286-
lua.pop(1);
32873356
return error.LuaTableMissingValue;
32883357
}
32893358
} else {
32903359
const stack_size_before_call = lua.getTop();
32913360
@field(result, field.name) = try lua.toAnyInternal(field.type, a, allow_alloc, -1);
32923361
std.debug.assert(stack_size_before_call == lua.getTop());
32933362
}
3294-
lua.pop(1); //pop the value off the stack
32953363
}
32963364

32973365
return result;

src/tests.zig

Lines changed: 110 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2437,6 +2437,13 @@ test "toAny" {
24372437
_ = lua.pushString("hello");
24382438
const my_enum = try lua.toAny(MyEnumType, -1);
24392439
try testing.expect(my_enum == MyEnumType.hello);
2440+
2441+
//void
2442+
try lua.doString("value = {}\nvalue_err = {a = 5}");
2443+
_ = try lua.getGlobal("value");
2444+
try testing.expectEqual(void{}, try lua.toAny(void, -1));
2445+
_ = try lua.getGlobal("value_err");
2446+
try testing.expectError(error.VoidTableIsNotEmpty, lua.toAny(void, -1));
24402447
}
24412448

24422449
test "toAny struct" {
@@ -2472,7 +2479,7 @@ test "toAny struct recursive" {
24722479
try lua.doString(
24732480
\\value = {
24742481
\\ ["foo"] = 10,
2475-
\\ ["bar"] = true,
2482+
\\ ["bar"] = false,
24762483
\\ ["bizz"] = "hi",
24772484
\\ ["meep"] = {
24782485
\\ ["a"] = nil
@@ -2482,7 +2489,43 @@ test "toAny struct recursive" {
24822489

24832490
_ = try lua.getGlobal("value");
24842491
const my_struct = try lua.toAny(MyType, -1);
2485-
_ = my_struct;
2492+
try testing.expectEqualDeep(MyType{}, my_struct);
2493+
}
2494+
2495+
test "toAny tagged union" {
2496+
var lua = try Lua.init(&testing.allocator);
2497+
defer lua.deinit();
2498+
2499+
const MyType = union(enum) {
2500+
a: i32,
2501+
b: bool,
2502+
c: []const u8,
2503+
d: struct { t0: f64, t1: f64 },
2504+
};
2505+
2506+
try lua.doString(
2507+
\\value0 = {
2508+
\\ ["c"] = "Hello, world!",
2509+
\\}
2510+
\\value1 = {
2511+
\\ ["d"] = {t0 = 5.0, t1 = -3.0},
2512+
\\}
2513+
\\value2 = {
2514+
\\ ["a"] = 1000,
2515+
\\}
2516+
);
2517+
2518+
_ = try lua.getGlobal("value0");
2519+
const my_struct0 = try lua.toAny(MyType, -1);
2520+
try testing.expectEqualDeep(MyType{ .c = "Hello, world!" }, my_struct0);
2521+
2522+
_ = try lua.getGlobal("value1");
2523+
const my_struct1 = try lua.toAny(MyType, -1);
2524+
try testing.expectEqualDeep(MyType{ .d = .{ .t0 = 5.0, .t1 = -3.0 } }, my_struct1);
2525+
2526+
_ = try lua.getGlobal("value2");
2527+
const my_struct2 = try lua.toAny(MyType, -1);
2528+
try testing.expectEqualDeep(MyType{ .a = 1000 }, my_struct2);
24862529
}
24872530

24882531
test "toAny slice" {
@@ -2502,6 +2545,34 @@ test "toAny slice" {
25022545
);
25032546
}
25042547

2548+
test "toAny array" {
2549+
var lua = try Lua.init(&testing.allocator);
2550+
defer lua.deinit();
2551+
2552+
const arr: [5]?u32 = .{ 1, 2, null, 4, 5 };
2553+
const program =
2554+
\\array= {1, 2, nil, 4, 5}
2555+
;
2556+
try lua.doString(program);
2557+
_ = try lua.getGlobal("array");
2558+
const array = try lua.toAny([5]?u32, -1);
2559+
try testing.expectEqual(arr, array);
2560+
}
2561+
2562+
test "toAny vector" {
2563+
var lua = try Lua.init(&testing.allocator);
2564+
defer lua.deinit();
2565+
2566+
const vec = @Vector(4, bool){ true, false, false, true };
2567+
const program =
2568+
\\vector= {true, false, false, true}
2569+
;
2570+
try lua.doString(program);
2571+
_ = try lua.getGlobal("vector");
2572+
const vector = try lua.toAny(@Vector(4, bool), -1);
2573+
try testing.expectEqual(vec, vector);
2574+
}
2575+
25052576
test "pushAny" {
25062577
var lua = try Lua.init(&testing.allocator);
25072578
defer lua.deinit();
@@ -2541,6 +2612,10 @@ test "pushAny" {
25412612
try lua.pushAny(MyEnumType.goodbye);
25422613
const my_enum = try lua.toAny(MyEnumType, -1);
25432614
try testing.expect(my_enum == MyEnumType.goodbye);
2615+
2616+
//void
2617+
try lua.pushAny(void{});
2618+
try testing.expectEqual(void{}, try lua.toAny(void, -1));
25442619
}
25452620

25462621
test "pushAny struct" {
@@ -2559,14 +2634,46 @@ test "pushAny struct" {
25592634
try testing.expect(value.bar == (MyType{}).bar);
25602635
}
25612636

2562-
test "pushAny slice/array" {
2637+
test "pushAny tagged union" {
2638+
var lua = try Lua.init(&testing.allocator);
2639+
defer lua.deinit();
2640+
2641+
const MyType = union(enum) {
2642+
a: i32,
2643+
b: bool,
2644+
c: []const u8,
2645+
d: struct { t0: f64, t1: f64 },
2646+
};
2647+
2648+
const t0 = MyType{ .d = .{ .t0 = 5.0, .t1 = -3.0 } };
2649+
try lua.pushAny(t0);
2650+
const value0 = try lua.toAny(MyType, -1);
2651+
try testing.expectEqualDeep(t0, value0);
2652+
2653+
const t1 = MyType{ .c = "Hello, world!" };
2654+
try lua.pushAny(t1);
2655+
const value1 = try lua.toAny(MyType, -1);
2656+
try testing.expectEqualDeep(t1, value1);
2657+
}
2658+
2659+
test "pushAny toAny slice/array/vector" {
25632660
var lua = try Lua.init(&testing.allocator);
25642661
defer lua.deinit();
25652662

25662663
var my_array = [_]u32{ 1, 2, 3, 4, 5 };
25672664
const my_slice: []u32 = my_array[0..];
2665+
const my_vector: @Vector(5, u32) = .{ 1, 2, 3, 4, 5 };
25682666
try lua.pushAny(my_slice);
25692667
try lua.pushAny(my_array);
2668+
try lua.pushAny(my_vector);
2669+
const vector = try lua.toAny(@TypeOf(my_vector), -1);
2670+
const array = try lua.toAny(@TypeOf(my_array), -2);
2671+
const slice = try lua.toAnyAlloc(@TypeOf(my_slice), -3);
2672+
defer slice.deinit();
2673+
2674+
try testing.expectEqual(my_array, array);
2675+
try testing.expectEqualDeep(my_slice, slice.value);
2676+
try testing.expectEqual(my_vector, vector);
25702677
}
25712678

25722679
fn foo(a: i32, b: i32) i32 {

0 commit comments

Comments
 (0)