From a69e24573aac6104d0f071e8c6d7583dd2dfae1b Mon Sep 17 00:00:00 2001 From: Validark Date: Mon, 15 Apr 2019 19:17:11 -0600 Subject: [PATCH] Add string.split implementation (#190) * init * remove .history files * fix bad test and add Roblox's internal tests * change formatting to please linter --- lib/libs/init.lua | 3 +- lib/libs/string.lua | 36 +++++++++++++++ lib/libs/string_spec.lua | 98 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 lib/libs/string.lua create mode 100644 lib/libs/string_spec.lua diff --git a/lib/libs/init.lua b/lib/libs/init.lua index 79abf14..f2ae7e4 100644 --- a/lib/libs/init.lua +++ b/lib/libs/init.lua @@ -1,5 +1,6 @@ local names = { "math", + "string", } local libs = {} @@ -8,4 +9,4 @@ for _, name in ipairs(names) do libs[name] = import("./" .. name) end -return libs \ No newline at end of file +return libs diff --git a/lib/libs/string.lua b/lib/libs/string.lua new file mode 100644 index 0000000..4f0d972 --- /dev/null +++ b/lib/libs/string.lua @@ -0,0 +1,36 @@ +local rbxString = {} + +for key, value in pairs(string) do + rbxString[key] = value +end + +rbxString.split = function(str, sep) + local result = {} + + if sep == "" then + for i = 1, #str do + result[i] = str:sub(i, i) + end + else + if sep == nil then + sep = "," + end + + local count = 1 + local pos = 1 + local a, b = str:find(sep, pos, true) + + while a do + result[count] = str:sub(pos, a - 1) + count = count + 1 + pos = b + 1 + a, b = str:find(sep, pos, true) + end + + result[count] = str:sub(pos) + end + + return result +end + +return rbxString diff --git a/lib/libs/string_spec.lua b/lib/libs/string_spec.lua new file mode 100644 index 0000000..f042a8a --- /dev/null +++ b/lib/libs/string_spec.lua @@ -0,0 +1,98 @@ +local string = import("./string") + +describe("libs.string", function() + describe("split", function() + it("should be a function", function() + assert.is_function(string.split) + end) + + it("should return an array of comma separated strings if sep is nil", function() + assert.are.same({"Hello", "world", "and", "lemur"}, string.split("Hello,world,and,lemur")) + end) + + it("should return an array of all characters in a string if sep is the empty string", function() + assert.are.same({ + "H", + "e", + "l", + "l", + "o", + ",", + "w", + "o", + "r", + "l", + "d", + ",", + "a", + "n", + "d", + ",", + "l", + "e", + "m", + "u", + "r", + }, string.split("Hello,world,and,lemur", "")) + end) + + it("should return an empty table if the string and sep is the empty string", function() + assert.are.same({}, string.split("", "")) + end) + + it("should return the original string in a table if no sep is matched", function() + assert.are.same({"Hello, world"}, string.split("Hello, world", "K")) + assert.are.same({""}, string.split("", " ")) + end) + + it("should return empty strings at the front and back when seps are present there", function() + assert.are.same({"", "Validark", "Osyris", "Vorlias", ""}, string.split("/Validark/Osyris/Vorlias/", "/")) + assert.are.same({"", "Validark", "Osyris", "Vorlias"}, string.split("/Validark/Osyris/Vorlias", "/")) + assert.are.same({"Validark", "Osyris", "Vorlias", ""}, string.split("Validark/Osyris/Vorlias/", "/")) + assert.are.same({"Validark", "Osyris", "Vorlias"}, string.split("Validark/Osyris/Vorlias", "/")) + end) + + it("should allow multi-character separators", function() + assert.are.same({"Hello", "world"}, string.split("Hello, world", ", ")) + end) + + it("should literally interpret Lua character classes", function() + assert.are.same({"Hello, world"}, string.split("Hello, world", "%l")) + assert.are.same({"Hel", "o, world"}, string.split("Hel%lo, world", "%l")) + end) + + it("should match Roblox's internal tests", function() + -- Provided by tiffany352 at https://github.com/LPGhatguy/lemur/pull/190 + local char = string.char + local ZWJ = char(0xe2, 0x80, 0x8d) + assert.are.same({ "" }, string.split("", ",")) + assert.are.same({ "foo", "", "bar" }, string.split("foo,,bar", ",")) + assert.are.same({ "", "foo" }, string.split(",foo", ",")) + assert.are.same({ "foo", "" }, string.split("foo,", ",")) + assert.are.same({ "", "" }, string.split(",", ",")) + assert.are.same({ "", "", "" }, string.split(",,", ",")) + assert.are.same({ "" }, string.split("", "~~~")) + assert.are.same({ "~~" }, string.split("~~", "~~~")) + assert.are.same({ "~~ ~~" }, string.split("~~ ~~", "~~~")) + assert.are.same({ "foo", "bar" }, string.split("foo~~~bar", "~~~")) + assert.are.same({ "foo", "", "bar" }, string.split("foo~~~~~~bar", "~~~")) + assert.are.same({ "", "foo" }, string.split("~~~foo", "~~~")) + assert.are.same({ "foo", "" }, string.split("foo~~~", "~~~")) + assert.are.same({ "", "" }, string.split("~~~", "~~~")) + assert.are.same({ "", "", "" }, string.split("~~~~~~", "~~~")) + assert.are.same({ "", "", "O" }, string.split("OOOOO", "OO")) + assert.are.same({ " ws " }, string.split(" ws ", ",")) + assert.are.same({ "foo ", " bar" }, string.split("foo , bar", ",")) + assert.are.same({ "我很高兴", "你呢?" }, string.split("我很高兴,你呢?", ",")) + assert.are.same({ "👩", "👩", "👧", "👧" }, string.split("👩‍👩‍👧‍👧", ZWJ)) + assert.are.same({ "foo", "bar" }, string.split("foo\0bar", "\0")) + assert.are.same({ "foo", "bar", "" }, string.split("foo\0bar\0", "\0")) + assert.are.same({ "foo", "bar" }, string.split("foo\0\0bar", "\0\0")) + assert.are.same({ "foo\0" }, string.split("foo\0", "\0\0")) + assert.are.same({ "foo", "\0" }, string.split("foo\0\0\0", "\0\0")) + assert.are.same({ }, string.split("", "")) + assert.are.same({ "a", "b", "c" }, string.split("abc", "")) + assert.are.same({ char(0xef), char(0xbc), char(0x9f) }, string.split("?", "")) + end) + end) +end)