mirror of
https://github.com/luau-lang/luau.git
synced 2024-12-12 21:10:37 +00:00
Add %* format specifier (#619)
RFC: https://github.com/Roblox/luau/pull/165
This commit is contained in:
parent
2c12badb5c
commit
4658219df2
6 changed files with 72 additions and 3 deletions
|
@ -1433,7 +1433,7 @@ private:
|
|||
const char* checkStringFormat(const char* data, size_t size)
|
||||
{
|
||||
const char* flags = "-+ #0";
|
||||
const char* options = "cdiouxXeEfgGqs";
|
||||
const char* options = "cdiouxXeEfgGqs*";
|
||||
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
{
|
||||
|
|
|
@ -1058,7 +1058,7 @@ ConstrainedTypeVarIterator end(const ConstrainedTypeVar* ctv)
|
|||
|
||||
static std::vector<TypeId> parseFormatString(TypeChecker& typechecker, const char* data, size_t size)
|
||||
{
|
||||
const char* options = "cdiouxXeEfgGqs";
|
||||
const char* options = "cdiouxXeEfgGqs*";
|
||||
|
||||
std::vector<TypeId> result;
|
||||
|
||||
|
@ -1072,7 +1072,7 @@ static std::vector<TypeId> parseFormatString(TypeChecker& typechecker, const cha
|
|||
continue;
|
||||
|
||||
// we just ignore all characters (including flags/precision) up until first alphabetic character
|
||||
while (i < size && !(data[i] > 0 && isalpha(data[i])))
|
||||
while (i < size && !(data[i] > 0 && (isalpha(data[i]) || data[i] == '*')))
|
||||
i++;
|
||||
|
||||
if (i == size)
|
||||
|
@ -1080,6 +1080,8 @@ static std::vector<TypeId> parseFormatString(TypeChecker& typechecker, const cha
|
|||
|
||||
if (data[i] == 'q' || data[i] == 's')
|
||||
result.push_back(typechecker.stringType);
|
||||
else if (data[i] == '*')
|
||||
result.push_back(typechecker.unknownType);
|
||||
else if (strchr(options, data[i]))
|
||||
result.push_back(typechecker.numberType);
|
||||
else
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
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));
|
||||
|
|
|
@ -291,6 +291,8 @@ TEST_CASE("Clear")
|
|||
|
||||
TEST_CASE("Strings")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauTostringFormatSpecifier", true};
|
||||
|
||||
runConformance("strings.lua");
|
||||
}
|
||||
|
||||
|
|
|
@ -556,6 +556,29 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "string_format_correctly_ordered_types")
|
|||
CHECK_EQ(tm->givenType, typeChecker.numberType);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "string_format_tostring_specifier")
|
||||
{
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
string.format("%* %* %* %*", "string", 1, true, function() end)
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "string_format_tostring_specifier_type_constraint")
|
||||
{
|
||||
CheckResult result = check(R"(
|
||||
local function f(x): string
|
||||
local _ = string.format("%*", x)
|
||||
return x
|
||||
end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
CHECK_EQ("(string) -> string", toString(requireType("f")));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "xpcall")
|
||||
{
|
||||
CheckResult result = check(R"(
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue