From d2eb8fe6ecf8f884ad8dbf96962b04363c9efee4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matou=C5=A1=20Jan=20Fialka?= Date: Fri, 20 Feb 2026 07:31:32 +0100 Subject: [PATCH] [SpaceLua] Use `luaFormatNumber` for number-to-string coercion in `..` and `table.concat` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace raw `String(v)` calls with `luaFormatNumber` in the concatenation operator and `table.concat`, so that floats are formatted according to Lua semantics rather than JavaScript's. This fixes cases like `10.8*22..""` producing `237.60000000000002` instead of `237.6`, and ensures integer-valued floats carry the `.0` suffix as Lua requires. Signed-off-by: Matouš Jan Fialka --- client/space_lua/arithmetic_test.lua | 31 ++++++++++++++++++++++++++ client/space_lua/eval.ts | 5 +++-- client/space_lua/stdlib/table.ts | 5 +++-- client/space_lua/stdlib/table_test.lua | 30 +++++++++++++++++++++++++ 4 files changed, 67 insertions(+), 4 deletions(-) diff --git a/client/space_lua/arithmetic_test.lua b/client/space_lua/arithmetic_test.lua index e576cbc8..daf69e0e 100644 --- a/client/space_lua/arithmetic_test.lua +++ b/client/space_lua/arithmetic_test.lua @@ -1741,3 +1741,34 @@ do assertEquals(math.type(sum_f), 'float', 'float sum stays float') end end + +-- 24. Concatenation number-to-string coercion + +-- 24.1. Integer formatting must not get decimal point +assertEquals(1 .. "", "1", ".. int 1") +assertEquals(0 .. "", "0", ".. int 0") +assertEquals(-5 .. "", "-5", ".. int -5") + +-- 24.2. Integer-valued floats must show .0 suffix +assertEquals(1.0 .. "", "1.0", ".. float 1.0") +assertEquals(2.0 .. "", "2.0", ".. float 2.0") +assertEquals(-3.0 .. "", "-3.0", ".. float -3.0") + +-- 24.3. 10.8*22 must produce Lua's "237.6" and not "237.60000000000002" +local r = 10.8 * 22 +assertEquals(r .. "", "237.6", ".. 10.8*22 formats correctly") + +-- 24.4. Special float values +assertEquals(1/0.0 .. "", "inf", ".. +inf") +assertEquals(-1/0.0 .. "", "-inf", ".. -inf") +assertEquals(0.0/0.0 .. "", "-nan", ".. nan") + +-- 24.5. Non-integer floats +assertEquals(0.5 .. "", "0.5", ".. float 0.5") +assertEquals(3.14 .. "", "3.14", ".. float 3.14") + +-- 24.6. LHS and RHS number coercion +assertEquals("x=" .. 42, "x=42", ".. string..int") +assertEquals("x=" .. 1.5, "x=1.5", ".. string..float") +assertEquals(99 .. "!", "99!", ".. int..string") +assertEquals(0.0 .. "!", "0.0!", ".. float..string") diff --git a/client/space_lua/eval.ts b/client/space_lua/eval.ts index 18e9928b..a709f30c 100644 --- a/client/space_lua/eval.ts +++ b/client/space_lua/eval.ts @@ -19,6 +19,7 @@ import { luaEnsureCloseStack, LuaEnv, luaEquals, + luaFormatNumber, LuaFunction, luaGet, luaIndexValue, @@ -333,10 +334,10 @@ function luaOp( return v as string; } if (typeof v === "number") { - return String(v); + return luaFormatNumber(v); } if (isTaggedFloat(v)) { - return String(v.value); + return luaFormatNumber(v.value, "float"); } const t = luaTypeName(v); throw new LuaRuntimeError( diff --git a/client/space_lua/stdlib/table.ts b/client/space_lua/stdlib/table.ts index c0aba789..c0d4e5e7 100644 --- a/client/space_lua/stdlib/table.ts +++ b/client/space_lua/stdlib/table.ts @@ -5,6 +5,7 @@ import { luaCall, type LuaEnv, luaEquals, + luaFormatNumber, luaGet, LuaMultiRes, LuaRuntimeError, @@ -77,10 +78,10 @@ export const tableApi = new LuaTable({ return v; } if (typeof v === "number") { - return String(v); + return luaFormatNumber(v); } if (isTaggedFloat(v)) { - return String(v.value); + return luaFormatNumber(v.value, "float"); } const ty = typeof v === "object" && v instanceof LuaTable diff --git a/client/space_lua/stdlib/table_test.lua b/client/space_lua/stdlib/table_test.lua index b0e09e2b..4cef98af 100644 --- a/client/space_lua/stdlib/table_test.lua +++ b/client/space_lua/stdlib/table_test.lua @@ -255,3 +255,33 @@ do local c = a assertTrue(a == c) end + +-- table.concat +do + assertEqual(table.concat( + { "Hello", "world" }, " "), "Hello world") + assertEqual(table.concat( + { "Hello", "world", "three" }, " ", 2, 3), "world three") + + assertEqual(table.concat({ "a", "b", "c" }), "abc") + assertEqual(table.concat({ "a", "b", "c" }, "", 2, 1), "") + + assertError(function() table.concat({ "a", {} }, "") end) + + -- number coercion: integers must not get a decimal point + assertEqual(table.concat({ 1, 2, 3 }, ","), "1,2,3") + + -- integer-valued floats must show .0 suffix (Lua semantics) + assertEqual(table.concat({ 1.0, 2.0 }, ","), "1.0,2.0") + + -- motivating case: 10.8*22 must produce "237.6" not "237.60000000000002" + assertEqual(table.concat({ 10.8 * 22 }, ""), "237.6") + + -- special float values + assertEqual(table.concat({ 1/0.0 }, ""), "inf") + assertEqual(table.concat({ -1/0.0 }, ""), "-inf") + assertEqual(table.concat({ 0.0/0.0 }, ""), "-nan") + + -- mixed strings and numbers + assertEqual(table.concat({ "a", 1, "b", 2.5 }, "-"), "a-1-b-2.5") +end