From 9feb7eaa015c88c5ad550d8616f7c55c2a20fec0 Mon Sep 17 00:00:00 2001 From: Kampfkarren Date: Mon, 1 Aug 2022 23:01:44 -0700 Subject: [PATCH] Add %* format specifier --- VM/src/lstrlib.cpp | 22 ++++++++++++++++++++++ tests/Conformance.test.cpp | 2 ++ tests/conformance/strings.lua | 20 ++++++++++++++++++++ 3 files changed, 44 insertions(+) diff --git a/VM/src/lstrlib.cpp b/VM/src/lstrlib.cpp index b0cd4dc2..98bbcd41 100644 --- a/VM/src/lstrlib.cpp +++ b/VM/src/lstrlib.cpp @@ -8,6 +8,8 @@ #include #include +LUAU_FASTFLAGVARIABLE(LuauTostringFormatSpecifier, false); + /* macro to `unsign' a character */ #define uchar(c) ((unsigned char)(c)) @@ -1032,6 +1034,26 @@ static int str_format(lua_State* L) break; } } + case '*': + { + if (!FFlag::LuauTostringFormatSpecifier) + { + luaL_error(L, "invalid option '%%*' to 'format'"); + break; + } + + if (formatItemSize != 1) + { + luaL_error(L, "'%%*' does not take a form"); + } + + size_t length; + const char* string = luaL_tolstring(L, arg, &length); + + luaL_addlstring(&b, string, length); + + continue; /* skip the `addsize' at the end */ + } default: { /* also treat cases `pnLlh' */ luaL_error(L, "invalid option '%%%c' to 'format'", *(strfrmt - 1)); diff --git a/tests/Conformance.test.cpp b/tests/Conformance.test.cpp index 1339fa0c..5758f86c 100644 --- a/tests/Conformance.test.cpp +++ b/tests/Conformance.test.cpp @@ -291,6 +291,8 @@ TEST_CASE("Clear") TEST_CASE("Strings") { + ScopedFastFlag sff{"LuauTostringFormatSpecifier", true}; + runConformance("strings.lua"); } diff --git a/tests/conformance/strings.lua b/tests/conformance/strings.lua index 98a5721a..c87cf15c 100644 --- a/tests/conformance/strings.lua +++ b/tests/conformance/strings.lua @@ -130,6 +130,26 @@ assert(string.format('"-%20s.20s"', string.rep("%", 2000)) == -- longest number that can be formated assert(string.len(string.format('%99.99f', -1e308)) >= 100) +local function return_one_thing() + return "hi" +end +local function return_two_nils() + return nil, nil +end + +assert(string.format("%*", return_one_thing()) == "hi") +assert(string.format("%* %*", return_two_nils()) == "nil nil") +assert(pcall(function() + string.format("%* %* %*", return_two_nils()) +end) == false) + +assert(string.format("%*", "a\0b\0c") == "a\0b\0c") +assert(string.format("%*", string.rep("doge", 3000)) == string.rep("doge", 3000)) + +assert(pcall(function() + string.format("%#*", "bad form") +end) == false) + assert(loadstring("return 1\n--comentário sem EOL no final")() == 1)