Skip to content

Commit b6395f4

Browse files
committed
Add support for lua.pushFString()
1 parent 8015e16 commit b6395f4

File tree

3 files changed

+43
-14
lines changed

3 files changed

+43
-14
lines changed

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ lua.doString(
7979

8080
| API | Support |
8181
|---|---|
82-
| Lua C API (`lua_*`) | 65% available (60/92) |
82+
| Lua C API (`lua_*`) | 68% available (63/92) |
8383
| Auxilary Library (`luaL_*`) | 6% available (3/48) |
8484
| LuaJIT Extensions | *No plans to implement.* |
8585

@@ -149,9 +149,9 @@ pattern has changed, such as using the Zig `init()` function pattern instead of
149149
| `lua_pcall`| ☑️📢 `lua.protectedCall()` |
150150
| `lua_pop`| ☑️ `lua.pop()` |
151151
| `lua_pushboolean`| ☑️ `lua.pushBoolean()` |
152-
| `lua_pushcclosure`||
153-
| `lua_pushcfunction`||
154-
| `lua_pushfstring`||
152+
| `lua_pushcclosure`| ☑️ `lua.pushCClosure()` |
153+
| `lua_pushcfunction`| ☑️ `lua.pushCFunction()` |
154+
| `lua_pushfstring`| ☑️ `lua.pushFString()` |
155155
| `lua_pushinteger`| ☑️ `lua.pushInteger()`|
156156
| `lua_pushlightuserdata`||
157157
| `lua_pushliteral`| 🆖 use `lua.pushLString()` |

src/lua_api.zig

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -112,16 +112,6 @@ pub fn pushThread(lua: *Lua) bool;
112112
/// Stack Behavior: `[-0, +1, -]`
113113
pub fn pushValue(lua: *Lua, index: i32) void;
114114

115-
/// Pushes onto the stack a formatted string and returns a pointer to this string. Similar to the C function
116-
/// sprintf, but with important differences: memory allocation is handled by Lua via garbage collection,
117-
/// and conversion specifiers are restricted to: '%%' (%), '%s' (zero-terminated string), '%f' (lua_Number),
118-
/// '%p' (pointer as hex), '%d' (int), and '%c' (int as character).
119-
///
120-
/// From: `const char *lua_pushfstring(lua_State *L, const char *fmt, ...);`
121-
/// Refer to: https://www.lua.org/manual/5.1/manual.html#lua_pushfstring
122-
/// Stack Behavior: `[-0, +1, m]`
123-
pub fn pushFString(lua: *Lua, comptime fmt: []const u8, ...) []const u8;
124-
125115
/// Equivalent to pushFString, except that it receives a va_list instead of a variable number of arguments.
126116
///
127117
/// From: `const char *lua_pushvfstring(lua_State *L, const char *fmt, va_list argp);`

src/root.zig

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -770,6 +770,35 @@ pub const Lua = opaque {
770770
}
771771
}
772772

773+
/// Pushes onto the stack a formatted string and returns a pointer to this string. Memory allocation is handled
774+
/// by Lua via garbage collection, callers do NOT own the returned slice.
775+
///
776+
/// String format specifiers are restricted to the following options:
777+
/// * '%%' - Insert a literal '%' character in the string.
778+
/// * '%s' - Insert a zero-terminated string,
779+
/// * '%f' - Insert a `Lua.Number`, usually an `f64`,
780+
/// * '%p' - Insert a pointer-width ineger formatted as hexadecimal,
781+
/// * '%d' - Insert a `Lua.Integer`, usually an `i64`, and
782+
/// * '%c' - Insert a single character represented by a number.
783+
///
784+
/// **Usage of this function is discouraged**, consider instead using Zig `std.fmt` primitives in combination with
785+
/// `lua.pushString()` or `lua.pushLString()`.
786+
///
787+
/// From: `const char *lua_pushfstring(lua_State *L, const char *fmt, ...);`
788+
/// Refer to: https://www.lua.org/manual/5.1/manual.html#lua_pushfstring
789+
/// Stack Behavior: `[-0, +1, m]`
790+
pub fn pushFString(lua: *Lua, comptime format: []const u8, args: anytype) [:0]const u8 {
791+
const string: ?[*:0]const u8 = @call(.auto, c.lua_pushfstring, .{ asState(lua), format.ptr } ++ args);
792+
if (string) |s| {
793+
// NOTE: This seems dangerous. I don't really like this solution, but it doesn't look like there is any other option.
794+
// We are making a strong assumption that Lua returns a well-behaved zero-terminated string.
795+
const len = std.mem.indexOfSentinel(u8, 0, s);
796+
return s[0..len :0];
797+
} else {
798+
std.debug.panic("Received unexpected NULL response from lua.pushFString(\"{s}, ...\")", .{format});
799+
}
800+
}
801+
773802
fn typeIsNotString(t: Lua.Type) NotStringError {
774803
return switch (t) {
775804
.number, .string => unreachable,
@@ -1703,6 +1732,16 @@ test "slices strings" {
17031732
lua.pop(1);
17041733
}
17051734

1735+
test "string formatting with pushFString" {
1736+
const lua = try Lua.init(std.testing.allocator);
1737+
defer lua.deinit();
1738+
1739+
const expected: [:0]const u8 = "abc%_FOO_42";
1740+
const actual = lua.pushFString("abc%%_%s_%d", .{ "FOO", @as(i32, 42) });
1741+
1742+
try std.testing.expectEqualSlices(u8, expected, actual);
1743+
}
1744+
17061745
test "slices strings to terminated strings" {
17071746
const lua = try Lua.init(std.testing.allocator);
17081747
defer lua.deinit();

0 commit comments

Comments
 (0)