@@ -6,6 +6,7 @@ const testing = std.testing;
66const assert = std .debug .assert ;
77
88const mode = @import ("builtin" ).mode ;
9+ const isSafeBuildTarget : bool = mode == .ReleaseSafe or mode == .Debug ;
910
1011const c = @import ("c" );
1112fn asState (lua : * Lua ) * c.lua_State {
@@ -214,7 +215,7 @@ pub const Lua = opaque {
214215 assert (index != 0 );
215216
216217 // Make sure we only run these checks in safety-checked build modes.
217- if (mode == .ReleaseSafe or mode == .Debug ) {
218+ if (isSafeBuildTarget ) {
218219 if (index <= c .LUA_REGISTRYINDEX ) {
219220 const max_upvalues_count = 255 ;
220221 assert (@as (i32 , @intCast (c .LUA_GLOBALSINDEX - max_upvalues_count )) <= index ); // Safety check failed: pseudo-index exceeds maximum number of upvalues (255). This can also happen if your stack index has been corrupted and become a very large negative number.
@@ -914,6 +915,23 @@ pub const Lua = opaque {
914915 return 1 == c .lua_lessthan (asState (lua ), index_left , index_right );
915916 }
916917
918+ /// Concatenates the n values at the top of the stack, pops them, and leaves the result at the top.
919+ /// If n is 1, the result is the single value on the stack (that is, the function does nothing);
920+ /// if n is 0, the result is the empty string. Concatenation is performed following the usual
921+ /// semantics of the lua concat `..` operator (see https://www.lua.org/manual/5.1/manual.html#2.5.4).
922+ ///
923+ /// In Debug or ReleaseSafe builds, the values to be concatenated are checked to be strings, numbers
924+ /// or types with the `__concat` metamethod defined.
925+ ///
926+ /// From: `void lua_concat(lua_State *L, int n);`
927+ /// Refer to: https://www.lua.org/manual/5.1/manual.html#lua_concat
928+ /// Stack Behavior: `[-n, +1, e]`
929+ pub fn concat (lua : * Lua , n : i32 ) void {
930+ assert (n >= 0 );
931+
932+ return c .lua_concat (asState (lua ), n );
933+ }
934+
917935 /// Returns the current status of the thread. The status will be `Status.ok` for a normal thread, an error
918936 /// code if the thread finished its execution with an error, or `status.yield` if the thread is suspended.
919937 ///
@@ -1889,3 +1907,31 @@ test "lessthan should follow expected semantics" {
18891907 try std .testing .expect (! lua .lessThan (-1 , -2 ));
18901908 lua .pop (2 );
18911909}
1910+
1911+ test "concat should follow expected semantics" {
1912+ const lua = try Lua .init (std .testing .allocator );
1913+ defer lua .deinit ();
1914+
1915+ lua .concat (0 );
1916+ try std .testing .expectEqualSlices (u8 , "" , try lua .toLString (-1 ));
1917+ lua .pop (1 );
1918+
1919+ lua .pushInteger (42 );
1920+ lua .concat (1 );
1921+ try std .testing .expectEqualSlices (u8 , "42" , try lua .toLString (-1 ));
1922+ lua .pop (1 );
1923+
1924+ lua .pushNumber (13.1 );
1925+ lua .pushInteger (13 );
1926+ lua .concat (2 );
1927+ try std .testing .expectEqualSlices (u8 , "13.113" , try lua .toLString (-1 ));
1928+ lua .pop (1 );
1929+
1930+ lua .pushInteger (42 );
1931+ lua .pushLString ("-" );
1932+ lua .pushInteger (84 );
1933+ lua .pushLString ("-Boof" );
1934+ lua .concat (4 );
1935+ try std .testing .expectEqualSlices (u8 , "42-84-Boof" , try lua .toLString (-1 ));
1936+ lua .pop (1 );
1937+ }
0 commit comments