Skip to content

Commit 4f78a1e

Browse files
committed
Add support for lua.getMetatable() and lua.setMetatable()
1 parent c9196a1 commit 4f78a1e

File tree

2 files changed

+93
-29
lines changed

2 files changed

+93
-29
lines changed

src/lua_api.zig

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,6 @@ pub fn getField(lua: *Lua, index: i32, k: [:0]const u8) LuaType;
7676
/// Stack Behavior: `[-0, +1, e]`
7777
pub fn getGlobal(lua: *Lua, name: [*:0]const u8) LuaType;
7878

79-
/// Pushes onto the stack the metatable of the value at the given acceptable index. If the index is not valid,
80-
/// or if the value does not have a metatable, the function returns 0 and pushes nothing on the stack.
81-
///
82-
/// From: `int lua_getmetatable(lua_State *L, int index);`
83-
/// Refer to: https://www.lua.org/manual/5.1/manual.html#lua_getmetatable
84-
/// Stack Behavior: `[-0, +(0|1), -]`
85-
pub fn getMetatable(lua: *Lua, index: i32) bool;
86-
8779
/// The type used by the Lua API to represent integral values.
8880
/// By default it is a signed integral type that the machine handles "comfortably".
8981
///
@@ -292,21 +284,6 @@ pub fn setField(lua: *Lua, index: i32, key: [:0]const u8) void;
292284
/// Stack Behavior: `[-1, +0, e]`
293285
pub fn setGlobal(lua: *Lua, name: [:0]const u8) void;
294286

295-
/// Pops a table from the stack and sets it as the new metatable for the value at the given acceptable index.
296-
///
297-
/// From: `int lua_setmetatable(lua_State *L, int index);`
298-
/// Refer to: https://www.lua.org/manual/5.1/manual.html#lua_setmetatable
299-
/// Stack Behavior: `[-1, +0, -]`
300-
pub fn setMetatable(lua: *Lua, index: i32) i32;
301-
302-
/// Accepts any acceptable index, or 0, and sets the stack top to this index. If the new top is larger
303-
/// than the old one, then the new elements are filled with nil. If index is 0, then all stack elements are removed.
304-
///
305-
/// From: `void lua_settop(lua_State *L, int index);`
306-
/// Refer to: https://www.lua.org/manual/5.1/manual.html#lua_settop
307-
/// Stack Behavior: `[-?, +?, -]`
308-
pub fn setTop(lua: *Lua, index: i32) void;
309-
310287
/// Opaque structure that keeps the whole state of a Lua interpreter. The Lua library is fully reentrant:
311288
/// it has no global variables. All information about a state is kept in this structure. A pointer
312289
/// to this state must be passed as the first argument to every function in the library, except

src/root.zig

Lines changed: 93 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -806,12 +806,13 @@ pub const Lua = opaque {
806806
/// Stack Behavior: `[-1, +1, e]`
807807
pub fn getTable(lua: *Lua, index: i32) Lua.Type {
808808
lua.validateStackIndex(index);
809+
assert(lua.isTable(index));
809810

810811
c.lua_gettable(asState(lua), index);
811812
return lua.typeOf(-1);
812813
}
813814

814-
/// Similar to lua_gettable, but does a raw access (i.e., without metamethods).
815+
/// Similar to `getTable()`, but this implementation will not invoke any metamethods.
815816
///
816817
/// Note: This function was renamed from `rawget`.
817818
///
@@ -825,6 +826,19 @@ pub const Lua = opaque {
825826
return lua.typeOf(-1);
826827
}
827828

829+
/// Pushes onto the stack the metatable of the value at the given acceptable index. If the index is not
830+
/// valid, or if the value does not have a metatable, the function returns `false` and pushes nothing on
831+
/// the stack.
832+
///
833+
/// From: `int lua_getmetatable(lua_State *L, int index);`
834+
/// Refer to: https://www.lua.org/manual/5.1/manual.html#lua_getmetatable
835+
/// Stack Behavior: `[-0, +(0|1), -]`
836+
pub fn getMetatable(lua: *Lua, index: i32) bool {
837+
lua.validateStackIndex(index);
838+
839+
return 1 == c.lua_getmetatable(asState(lua), index);
840+
}
841+
828842
/// Does the equivalent of `t[k] = v`, where `t` is the acceptable index of the table on the stack, `v` is
829843
/// the value at the top of the stack, and `k` is the value just below the top. This function pops both the
830844
/// key and the value from the stack. As in Lua, this function may trigger a metamethod for the "newindex"
@@ -850,6 +864,20 @@ pub const Lua = opaque {
850864
return c.lua_settable(asState(lua), index);
851865
}
852866

867+
/// Pops a table from the top of the stack and sets it as the metatable for the value at the
868+
/// given acceptable index.
869+
///
870+
/// From: `int lua_setmetatable(lua_State *L, int index);`
871+
/// Refer to: https://www.lua.org/manual/5.1/manual.html#lua_setmetatable
872+
/// Stack Behavior: `[-1, +0, -]`
873+
pub fn setMetatable(lua: *Lua, index: i32) void {
874+
lua.validateStackIndex(index);
875+
assert(lua.isTable(index));
876+
877+
const res = c.lua_setmetatable(asState(lua), index);
878+
assert(1 == res);
879+
}
880+
853881
/// Pops `n` elements from the stack.
854882
///
855883
/// From: `void lua_pop(lua_State *L, int n);`
@@ -871,6 +899,19 @@ pub const Lua = opaque {
871899
return c.lua_gettop(asState(lua));
872900
}
873901

902+
/// Accepts any acceptable index, or 0, and sets the stack top to this index. If the new top is
903+
/// larger than the old one, then the new elements are filled with nil. If index is 0, then all
904+
/// stack elements are removed.
905+
///
906+
/// From: `void lua_settop(lua_State *L, int index);`
907+
/// Refer to: https://www.lua.org/manual/5.1/manual.html#lua_settop
908+
/// Stack Behavior: `[-?, +?, -]`
909+
pub fn setTop(lua: *Lua, index: i32) void {
910+
assert(index >= 0);
911+
912+
return c.lua_settop(asState(lua), index);
913+
}
914+
874915
/// Moves the top element into the given valid index, shifting up the elements above this index to open space.
875916
/// Cannot be called with a pseudo-index, because a pseudo-index is not an actual stack position.
876917
///
@@ -1069,13 +1110,13 @@ pub const Lua = opaque {
10691110
assert(Status.is_status(res)); // Expected the status to be one of the "thread status" values defined in lua.h
10701111

10711112
const s: Status = @enumFromInt(res);
1072-
return switch (s) {
1113+
switch (s) {
10731114
.ok => return,
1074-
.runtime_error => error.Runtime,
1075-
.memory_error => error.OutOfMemory,
1076-
.error_handling_error => error.ErrorHandlerFailure,
1115+
.runtime_error => return error.Runtime,
1116+
.memory_error => return error.OutOfMemory,
1117+
.error_handling_error => return error.ErrorHandlerFailure,
10771118
else => std.debug.panic("Lua returned unexpected status code from protected call: {d}\n", .{res}),
1078-
};
1119+
}
10791120
}
10801121

10811122
/// Opens all standard Lua libraries into the given state. Callers may prefer to open individual
@@ -1939,3 +1980,49 @@ test "concat should follow expected semantics" {
19391980
try std.testing.expectEqualSlices(u8, "42-84-Boof", try lua.toLString(-1));
19401981
lua.pop(1);
19411982
}
1983+
1984+
fn always42AddMetamethod(lua: *Lua) callconv(.c) i32 {
1985+
lua.pushInteger(42);
1986+
return 1;
1987+
}
1988+
1989+
test "metatables can be set" {
1990+
const lua = try Lua.init(std.testing.allocator);
1991+
defer lua.deinit();
1992+
1993+
lua.openBaseLib();
1994+
1995+
lua.newTable();
1996+
lua.newTable();
1997+
lua.pushLString("__add");
1998+
lua.pushCFunction(always42AddMetamethod);
1999+
lua.setTable(-3);
2000+
lua.setMetatable(-2);
2001+
2002+
try std.testing.expect(lua.getMetatable(-1));
2003+
try std.testing.expect(lua.isTable(-1));
2004+
lua.setTop(0);
2005+
}
2006+
2007+
test "metatables can be accessed" {
2008+
const lua = try Lua.init(std.testing.allocator);
2009+
defer lua.deinit();
2010+
2011+
lua.openBaseLib();
2012+
2013+
try lua.doString(
2014+
\\f = {}
2015+
\\return setmetatable(f, {
2016+
\\ __add = function (l, r)
2017+
\\ return 0
2018+
\\ end
2019+
\\})
2020+
);
2021+
try std.testing.expect(lua.isTable(-1));
2022+
try std.testing.expect(lua.getMetatable(-1));
2023+
try std.testing.expect(lua.isTable(-1));
2024+
2025+
lua.pushLString("__add");
2026+
try std.testing.expectEqual(Lua.Type.function, lua.getTable(-2));
2027+
lua.setTop(0);
2028+
}

0 commit comments

Comments
 (0)