Skip to content

Commit 951e44a

Browse files
committed
Add support for lua.registerLibrary() and renamed lua_register wrapper to lua.registerFunction()
1 parent 63edf1f commit 951e44a

File tree

3 files changed

+168
-50
lines changed

3 files changed

+168
-50
lines changed

README.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ you.
8686
| API | Support |
8787
|-----------------------------|---------------------------------------|
8888
| Lua C API (`lua_*`) | 🎉 100% coverage<sup>†</sup> (92/92) |
89-
| Auxilary Library (`luaL_*`) | 48% coverage (24/48) |
89+
| Auxilary Library (`luaL_*`) | 52% coverage (26/48) |
9090
| LuaJIT Extensions | *No plans to implement.* |
9191

9292
*†: Coroutine yield/resume is not yet part of the public `zig-luajit` Zig API, see [#6][ISSUE-6].*
@@ -183,7 +183,7 @@ This section describes the current status of Zig language bindings ("the Zig API
183183
| `lua_rawget` | ☑️📢 `lua.getTableRaw()` |
184184
| `lua_rawseti` | ☑️📢 `lua.setTableIndexRaw()` |
185185
| `lua_rawset` | ☑️📢 `lua.setTableRaw()` |
186-
| `lua_register` | ☑️ `lua.register()` |
186+
| `lua_register` | ☑️📢 `lua.registerFunction()` |
187187
| `lua_remove` | ☑️ `lua.remove()` |
188188
| `lua_replace` | ☑️ `lua.replace()` |
189189
| `lua_resume` | ➖ Hidden, see [Issue #6][ISSUE-6] |
@@ -216,13 +216,13 @@ The `zig-luajit` project has not yet reached the 1.0 release, the API is subject
216216

217217
### Auxilary Library Coverage (`luaL_`)
218218

219-
| C Type Definition | Available in `zig-luajit` |
220-
|----------------------------|---------------------------------------------------------------------|
219+
| C Type Definition | Available in `zig-luajit` |
220+
|----------------------------|-------------------------------------|
221221
| `luaL_Buffer` ||
222-
| `luaL_Reg` ||
222+
| `luaL_Reg` | ☑️ `Lua.Reg` and `Lua.RegEnd` |
223223

224-
| C API Symbol | Available in `zig-luajit` |
225-
|--------------|---------------------------|
224+
| C API Symbol | Available in `zig-luajit` |
225+
|----------------------------|-------------------------------------|
226226
| `luaL_addchar` ||
227227
| `luaL_addlstring` ||
228228
| `luaL_addsize` ||
@@ -265,7 +265,7 @@ The `zig-luajit` project has not yet reached the 1.0 release, the API is subject
265265
| `luaL_pushresult` ||
266266
| `luaL_ref` | ☑️ `lua.ref()` |
267267
| `luaL_unref` | ☑️ `lua.unref()` |
268-
| `luaL_register` ||
268+
| `luaL_register` | ☑️📢 `lua.registerLibrary()` |
269269
| `luaL_typename` | ☑️ `lua.getTypeNameAt()` |
270270
| `luaL_typerror` ||
271271
| `luaL_where` ||

src/lual_api.zig

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -173,30 +173,6 @@ pub fn prepBuffer(buffer: *Buffer) [*]u8;
173173
/// Stack Behavior: `[-?, +1, m]`
174174
pub fn pushResult(buffer: *LuaBuffer) void;
175175

176-
/// Opens a library. When called with libname equal to null, it simply registers all functions in the list l
177-
/// into the table on the top of the stack. When called with a non-null libname, creates a new table t,
178-
/// sets it as the value of the global variable libname, sets it as the value of package.loaded[libname],
179-
/// and registers on it all functions in the list l. If there is a table in package.loaded[libname] or in
180-
/// variable libname, reuses this table instead of creating a new one. In any case the function leaves
181-
/// the table on the top of the stack.
182-
///
183-
/// From: `void luaL_register(lua_State *L, const char *libname, const luaL_Reg *l);`
184-
/// Refer to: https://www.lua.org/manual/5.1/manual.html#luaL_register
185-
/// Stack Behavior: `[-(0|1), +1, m]`
186-
pub fn register(lua: *Lua, lib_name: ?[:0]const u8, funcs: []const LuaReg) void;
187-
188-
/// Type for arrays of functions to be registered by `luaL_register`.
189-
/// `name` is the function name and `func` is a pointer to the function.
190-
/// Any array of `luaL_Reg` must end with a sentinel entry in which both
191-
/// `name` and `func` are `null`.
192-
///
193-
/// From: `typedef struct luaL_Reg { const char *name; lua_CFunction func; } luaL_Reg;`
194-
/// Refer to: https://www.lua.org/manual/5.1/manual.html#luaL_Reg
195-
pub const Reg = extern struct {
196-
name: ?[*:0]const u8,
197-
func: ?*const fn (state: *Lua) callconv(.C) c_int,
198-
};
199-
200176
/// Generates an error with a message like "location: bad argument narg to 'func' (tname expected, got rt)",
201177
/// where location is produced by luaL_where, func is the name of the current function,
202178
/// and rt is the type name of the actual argument.

src/root.zig

Lines changed: 160 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1317,15 +1317,6 @@ pub const Lua = opaque {
13171317
return c.lua_replace(asState(lua), index);
13181318
}
13191319

1320-
/// Sets the `CFunction` `f` as the new value of global `name`.
1321-
///
1322-
/// From: `void lua_register(lua_State *L, const char *name, lua_CFunction f);`
1323-
/// Refer to: https://www.lua.org/manual/5.1/manual.html#lua_register
1324-
/// Stack Behavior: `[-0, +0, e]`
1325-
pub fn register(lua: *Lua, name: [:0]const u8, f: CFunction) void {
1326-
return c.lua_register(asState(lua), asCString(name), asCFn(f));
1327-
}
1328-
13291320
/// Returns whether the two values in given acceptable indices are equal, following the semantics of the Lua `==`
13301321
/// operator, which may call metamethods.
13311322
///
@@ -2201,6 +2192,67 @@ pub const Lua = opaque {
22012192
_ = c.luaL_argerror(asState(lua), arg_n, ptr);
22022193
}
22032194
}
2195+
2196+
/// Represents named functions that belong to a library that can be registered by a call to the `registerLibrary()`
2197+
/// function.
2198+
///
2199+
/// Any array of this type **MUST** be terminated by a sentinel value in which both `name` and `func` are set to
2200+
/// `null`. Refer to `RegEnd`.
2201+
///
2202+
/// From: `typedef struct luaL_Reg { const char *name; lua_CFunction func; } luaL_Reg;`
2203+
/// Refer to: https://www.lua.org/manual/5.1/manual.html#luaL_Reg
2204+
pub const Reg = struct {
2205+
name: ?[*:0]const u8,
2206+
func: ?Lua.CFunction,
2207+
};
2208+
pub const RegEnd: Reg = .{
2209+
.name = null,
2210+
.func = null,
2211+
};
2212+
2213+
/// Sets the given function as the value of a new global variable named `name`.
2214+
///
2215+
/// From: `void lua_register(lua_State *L, const char *name, lua_CFunction f);`
2216+
/// Refer to: https://www.lua.org/manual/5.1/manual.html#lua_register
2217+
/// Stack Behavior: `[-0, +0, e]`
2218+
pub fn registerFunction(lua: *Lua, name: [:0]const u8, function: CFunction) void {
2219+
return c.lua_register(asState(lua), asCString(name), asCFn(function));
2220+
}
2221+
2222+
/// Opens a library.
2223+
///
2224+
/// When called with `name` equal to null, registers all functions in the list `functions` into the table on the
2225+
/// top of the stack. The list of functions *MUST* be terminated by the `Lua.RegEnd`
2226+
///
2227+
/// When called with a non-null `name`:
2228+
/// * creates a new table `t`,
2229+
/// * sets `t` as the value of the global variable `name`,
2230+
/// * sets `t` as the value of package.loaded[libname],
2231+
/// * and registers on it all functions in the list `functions`.
2232+
///
2233+
/// If there is a table in package.loaded[libname] or in variable libname, reuses this table instead of creating
2234+
/// a new one.
2235+
///
2236+
/// Calls to `registerLibrary()` always leave the library table on the top of the stack.
2237+
///
2238+
/// From: `void luaL_register(lua_State *L, const char *libname, const luaL_Reg *l);`
2239+
/// Refer to: https://www.lua.org/manual/5.1/manual.html#luaL_register
2240+
/// Stack Behavior: `[-(0|1), +1, m]`
2241+
pub fn registerLibrary(lua: *Lua, name: ?[:0]const u8, functions: []const Lua.Reg) void {
2242+
if (isSafeBuildTarget) {
2243+
if (name == null) {
2244+
assert(lua.isTable(-1));
2245+
}
2246+
assert(functions[functions.len - 1].name == null); // When calling `lua.registerLibrary(name, functions)`, the functions must be terminated by `RegEnd`.
2247+
assert(functions[functions.len - 1].func == null); // When calling `lua.registerLibrary(name, functions)`, the functions must be terminated by `RegEnd`.
2248+
}
2249+
2250+
return c.luaL_register(
2251+
asState(lua),
2252+
if (name) |p| @ptrCast(p.ptr) else null,
2253+
@ptrCast(functions.ptr),
2254+
);
2255+
}
22042256
};
22052257

22062258
test "Lua can be initialized with an allocator" {
@@ -3251,16 +3303,18 @@ test "getTableIndexRaw" {
32513303
try std.testing.expectEqual(2, lua.getTop());
32523304
}
32533305

3254-
fn registeredFn(lua: *Lua) callconv(.c) i32 {
3255-
lua.pushLString("Galt, John");
3256-
return 1;
3257-
}
3258-
32593306
test "registering named functions" {
32603307
const lua = try Lua.init(std.testing.allocator);
32613308
defer lua.deinit();
32623309

3263-
lua.register("regREG", registeredFn);
3310+
const T = struct {
3311+
fn registeredFn(l: *Lua) callconv(.c) i32 {
3312+
l.pushLString("Galt, John");
3313+
return 1;
3314+
}
3315+
};
3316+
3317+
lua.registerFunction("regREG", T.registeredFn);
32643318
try lua.doString(
32653319
\\actual = regREG()
32663320
\\if actual == 'Galt, John' then
@@ -3272,6 +3326,80 @@ test "registering named functions" {
32723326
try std.testing.expectEqual(1, lua.toIntegerStrict(-1));
32733327
}
32743328

3329+
test "registering a library to the global namespace" {
3330+
const lua = try Lua.init(std.testing.allocator);
3331+
defer lua.deinit();
3332+
3333+
const T = struct {
3334+
fn john(l: *Lua) callconv(.c) i32 {
3335+
l.pushLString("Galt");
3336+
return 1;
3337+
}
3338+
fn hank(l: *Lua) callconv(.c) i32 {
3339+
l.pushLString("Reardon");
3340+
return 1;
3341+
}
3342+
};
3343+
3344+
lua.registerLibrary(
3345+
"foo",
3346+
&[_]Lua.Reg{
3347+
.{ .name = "john", .func = &T.john },
3348+
.{ .name = "hank", .func = &T.hank },
3349+
Lua.RegEnd,
3350+
},
3351+
);
3352+
try lua.doString(
3353+
\\john, hank = foo.john(), foo.hank()
3354+
\\if (john == 'Galt' and hank == 'Reardon') then
3355+
\\ return 1
3356+
\\end
3357+
\\return 0
3358+
);
3359+
try std.testing.expect(lua.isInteger(-1));
3360+
try std.testing.expectEqual(1, lua.toIntegerStrict(-1));
3361+
}
3362+
3363+
test "registering a library to a table" {
3364+
const lua = try Lua.init(std.testing.allocator);
3365+
defer lua.deinit();
3366+
3367+
const T = struct {
3368+
fn john(l: *Lua) callconv(.c) i32 {
3369+
l.pushLString("Galt");
3370+
return 1;
3371+
}
3372+
fn hank(l: *Lua) callconv(.c) i32 {
3373+
l.pushLString("Reardon");
3374+
return 1;
3375+
}
3376+
};
3377+
3378+
try lua.doString(
3379+
\\return function(lib)
3380+
\\ john, hank = lib.john(), lib.hank()
3381+
\\ if (john == 'Galt' and hank == 'Reardon') then
3382+
\\ return 1
3383+
\\ end
3384+
\\ return 0
3385+
\\end
3386+
);
3387+
3388+
lua.newTable();
3389+
lua.registerLibrary(
3390+
null,
3391+
&[_]Lua.Reg{
3392+
.{ .name = "john", .func = &T.john },
3393+
.{ .name = "hank", .func = &T.hank },
3394+
Lua.RegEnd,
3395+
},
3396+
);
3397+
3398+
try lua.callProtected(1, 1, 0);
3399+
try std.testing.expect(lua.isInteger(-1));
3400+
try std.testing.expectEqual(1, lua.toIntegerStrict(-1));
3401+
}
3402+
32753403
test "threads should share global state and not share local state" {
32763404
const lua = try Lua.init(std.testing.allocator);
32773405
defer lua.deinit();
@@ -3488,10 +3616,17 @@ test "toCFunction should return expected function" {
34883616
const lua = try Lua.init(std.testing.allocator);
34893617
defer lua.deinit();
34903618

3491-
lua.pushCFunction(registeredFn);
3619+
const T = struct {
3620+
fn registeredFn(l: *Lua) callconv(.c) i32 {
3621+
l.pushLString("Galt, John");
3622+
return 1;
3623+
}
3624+
};
3625+
3626+
lua.pushCFunction(T.registeredFn);
34923627
const actual = lua.toCFunction(-1);
34933628
try std.testing.expect(actual != null);
3494-
try std.testing.expectEqual(registeredFn, actual);
3629+
try std.testing.expectEqual(T.registeredFn, actual);
34953630
lua.pop(1);
34963631
}
34973632

@@ -3516,6 +3651,13 @@ test "toPointer should return a non-null pointer for supported types" {
35163651
const lua = try Lua.init(std.testing.allocator);
35173652
defer lua.deinit();
35183653

3654+
const T = struct {
3655+
fn registeredFn(l: *Lua) callconv(.c) i32 {
3656+
l.pushLString("Galt, John");
3657+
return 1;
3658+
}
3659+
};
3660+
35193661
lua.pushLString("ASDF");
35203662
try std.testing.expect(lua.toPointer(-1) != null);
35213663
lua.pop(1);
@@ -3524,7 +3666,7 @@ test "toPointer should return a non-null pointer for supported types" {
35243666
try std.testing.expect(lua.toPointer(-1) != null);
35253667
lua.pop(1);
35263668

3527-
lua.pushCFunction(registeredFn);
3669+
lua.pushCFunction(T.registeredFn);
35283670
try std.testing.expect(lua.toPointer(-1) != null);
35293671
lua.pop(1);
35303672

0 commit comments

Comments
 (0)