From 63e40cf3cb33ab0ced3cfe3ba6de82dcbb6159ab Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Wed, 24 Jul 2024 22:18:50 +0100 Subject: [PATCH] Add a cc.strings.split method This is largely copied from metis, with the documentation updated. --- .../lua/rom/modules/main/cc/strings.lua | 56 +++++++++++++++++++ .../test-rom/spec/modules/cc/strings_spec.lua | 29 ++++++++++ 2 files changed, 85 insertions(+) diff --git a/projects/core/src/main/resources/data/computercraft/lua/rom/modules/main/cc/strings.lua b/projects/core/src/main/resources/data/computercraft/lua/rom/modules/main/cc/strings.lua index 09fc057e7..44a9e5ce7 100644 --- a/projects/core/src/main/resources/data/computercraft/lua/rom/modules/main/cc/strings.lua +++ b/projects/core/src/main/resources/data/computercraft/lua/rom/modules/main/cc/strings.lua @@ -110,7 +110,63 @@ local function ensure_width(line, width) return line end +--[[- Split a string into parts, each separated by a deliminator. + +For instance, splitting the string `"a b c"` with the deliminator `" "`, would +return a table with three strings: `"a"`, `"b"`, and `"c"`. + +By default, the deliminator is given as a [Lua pattern][pattern]. Passing `true` +to the `plain` argument will cause the deliminator to be treated as a litteral +string. + +[pattern]: https://www.lua.org/manual/5.3/manual.html#6.4.1 + +@tparam string str The string to split. +@tparam string deliminator The pattern to split this string on. +@tparam[opt=false] boolean plain Treat the deliminator as a plain string, rather than a pattern. +@tparam[opt] number limit The maximum number of elements in the returned list. +@treturn { string... } The list of split strings. + +@usage Split a string into words. + + require "cc.strings".split("This is a sentence.", "%s+") + +@usage Split a string by "-" into at most 3 elements. + + require "cc.strings".split("a-separated-string-of-sorts", "-", true, 3) + +@see table.concat To join strings together. + +@since 1.112.0 +]] +local function split(str, deliminator, plain, limit) + expect(1, str, "string") + expect(2, deliminator, "string") + expect(3, plain, "boolean", "nil") + expect(4, limit, "number", "nil") + + local out, out_n, pos = {}, 0, 1 + while not limit or out_n < limit - 1 do + local start, finish = str:find(deliminator, pos, plain) + if not start then break end + + out_n = out_n + 1 + out[out_n] = str:sub(pos, start - 1) + + -- Require us to advance by at least one character. + if finish < start then error("separator is empty", 2) end + + pos = finish + 1 + end + + if pos == 1 then return { str } end + + out[out_n + 1] = str:sub(pos) + return out +end + return { wrap = wrap, ensure_width = ensure_width, + split = split, } diff --git a/projects/core/src/test/resources/test-rom/spec/modules/cc/strings_spec.lua b/projects/core/src/test/resources/test-rom/spec/modules/cc/strings_spec.lua index 32d26d514..3cde011e2 100644 --- a/projects/core/src/test/resources/test-rom/spec/modules/cc/strings_spec.lua +++ b/projects/core/src/test/resources/test-rom/spec/modules/cc/strings_spec.lua @@ -44,4 +44,33 @@ describe("cc.strings", function() expect(str.ensure_width("test string is long", 15)):eq("test string is ") end) end) + + describe("split", function() + it("splits with empty segments", function() + expect(str.split("", "%-")):same { "" } + expect(str.split("-", "%-")):same { "", "" } + expect(str.split("---", "%-")):same { "", "", "", "" } + expect(str.split("-a", "%-")):same { "", "a" } + expect(str.split("a-", "%-")):same { "a", "" } + end) + + it("cannot split with an empty separator", function() + expect.error(str.split, "abc", ""):eq("separator is empty") + end) + + it("splits on patterns", function() + expect(str.split("a.bcd ef", "%W+")):same { "a", "bcd", "ef" } + end) + + it("splits on literal strings", function() + expect(str.split("a-bcd-ef", "-", true)):same { "a", "bcd", "ef" } + end) + + it("accepts a limit", function() + expect(str.split("foo-bar-baz-qux-quyux", "-", true, 3)):same { "foo", "bar", "baz-qux-quyux" } + expect(str.split("foo-bar-baz", "-", true, 5)):same { "foo", "bar", "baz" } + expect(str.split("foo-bar-baz", "-", true, 1)):same { "foo-bar-baz" } + expect(str.split("foo-bar-baz", "-", true, 1)):same { "foo-bar-baz" } + end) + end) end)