From 4eb8b9518b42179d55cb38bb079b8afeb68a5ccc Mon Sep 17 00:00:00 2001 From: Stephen Leitnick Date: Tue, 3 Dec 2019 17:12:56 -0800 Subject: [PATCH 1/9] Add Assign function to TableUtil --- src/ReplicatedStorage/Aero/Shared/TableUtil.lua | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/ReplicatedStorage/Aero/Shared/TableUtil.lua b/src/ReplicatedStorage/Aero/Shared/TableUtil.lua index 4836b4d..e5ea689 100644 --- a/src/ReplicatedStorage/Aero/Shared/TableUtil.lua +++ b/src/ReplicatedStorage/Aero/Shared/TableUtil.lua @@ -12,6 +12,7 @@ TableUtil.Map(Table tbl, Function callback) TableUtil.Filter(Table tbl, Function callback) TableUtil.Reduce(Table tbl, Function callback [, Number initialValue]) + TableUtil.Assign(Table target, ...Table sources) TableUtil.IndexOf(Table tbl, Variant item) TableUtil.Reverse(Table tbl) TableUtil.Shuffle(Table tbl) @@ -278,6 +279,17 @@ local function Reduce(t, f, init) end +-- tableUtil.Assign(Table target, ...Table sources) +local function Assign(target, ...) + for _,src in pairs({...}) do + for k,v in pairs(src) do + target[k] = v + end + end + return target +end + + local function Print(tbl, label, deepPrint) assert(type(tbl) == "table", "First argument must be a table") @@ -398,6 +410,7 @@ TableUtil.Print = Print TableUtil.Map = Map TableUtil.Filter = Filter TableUtil.Reduce = Reduce +TableUtil.Assign = Assign TableUtil.IndexOf = IndexOf TableUtil.Reverse = Reverse TableUtil.Shuffle = Shuffle From ae1d8e64b7d79719c6a5c33a8c1fc5201e9e88d1 Mon Sep 17 00:00:00 2001 From: Stephen Leitnick Date: Tue, 3 Dec 2019 17:21:15 -0800 Subject: [PATCH 2/9] Add example for TableUtil.Assign() --- .../Aero/Shared/TableUtil.lua | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/ReplicatedStorage/Aero/Shared/TableUtil.lua b/src/ReplicatedStorage/Aero/Shared/TableUtil.lua index e5ea689..f0e5c46 100644 --- a/src/ReplicatedStorage/Aero/Shared/TableUtil.lua +++ b/src/ReplicatedStorage/Aero/Shared/TableUtil.lua @@ -125,6 +125,38 @@ print(tblSum) -- > 130 + Assign: + + This allows you to assign values from multiple tables into one. The + Assign function is very similar to JavaScript's Object.Assign() and + is useful for things such as composition-designed systems. + + local function Driver() + return { + Drive = function(self) self.Speed = 10 end; + } + end + + local function Teleporter() + return { + Teleport = function(self, pos) self.Position = pos end; + } + end + + local function CreateCar() + local state = { + Speed = 0; + Position = Vector3.new(); + } + -- Assign the Driver and Teleporter components to the car: + return TableUtil.Assign({}, Driver(), Teleporter()) + end + + local car = CreateCar() + car:Drive() + car:Teleport(Vector3.new(0, 10, 0)) + + IndexOf: Returns the index of the given item in the table. If not found, this From fb5a634eda1478d73122f735f8eefa6225f691c9 Mon Sep 17 00:00:00 2001 From: Stephen Leitnick Date: Tue, 3 Dec 2019 17:32:49 -0800 Subject: [PATCH 3/9] Use ipairs --- src/ReplicatedStorage/Aero/Shared/TableUtil.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ReplicatedStorage/Aero/Shared/TableUtil.lua b/src/ReplicatedStorage/Aero/Shared/TableUtil.lua index f0e5c46..3a46be8 100644 --- a/src/ReplicatedStorage/Aero/Shared/TableUtil.lua +++ b/src/ReplicatedStorage/Aero/Shared/TableUtil.lua @@ -313,7 +313,7 @@ end -- tableUtil.Assign(Table target, ...Table sources) local function Assign(target, ...) - for _,src in pairs({...}) do + for _,src in ipairs({...}) do for k,v in pairs(src) do target[k] = v end From 535406e15a0fcc1630201c243039b98c0d689952 Mon Sep 17 00:00:00 2001 From: Stephen Leitnick Date: Wed, 4 Dec 2019 15:54:46 -0800 Subject: [PATCH 4/9] Add StringUtil module --- .../Aero/Shared/StringUtil.lua | 145 ++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 src/ReplicatedStorage/Aero/Shared/StringUtil.lua diff --git a/src/ReplicatedStorage/Aero/Shared/StringUtil.lua b/src/ReplicatedStorage/Aero/Shared/StringUtil.lua new file mode 100644 index 0000000..8f0141f --- /dev/null +++ b/src/ReplicatedStorage/Aero/Shared/StringUtil.lua @@ -0,0 +1,145 @@ +-- String Util +-- Stephen Leitnick +-- December 3, 2019 + +--[[ + + StringUtil.Trim(String str) + StringUtil.TrimStart(String str) + StringUtil.TrimEnd(String str) + StringUtil.EqualsIgnoreCase(String str, String compare) + StringUtil.RemoveWhitespace(String str) + StringUtil.RemoveExcessWhitespace(String str) + StringUtil.EndsWith(String str, String endsWith) + StringUtil.StartsWith(String str, String startsWith) + StringUtil.Contains(String str, String contains) + StringUtil.ToCharArray(String str) + StringUtil.ToByteArray(String str) + StringUtil.ToCamelCase(String str) + StringUtil.ToPascalCase(String str) + StringUtil.ToSnakeCase(String str [, uppercase]) + StringUtil.ToKebabCase(String str [, uppercase]) + StringUtil.StringBuilder() + +--]] + + +local StringUtil = {} + + +function StringUtil.Escape(str) + local escaped = str:gsub("([%.%$%^%(%)%[%]%+%-%*%?%%])", "%%%1") + return escaped +end + + +function StringUtil.Trim(str) + return str:match("^%s*(.-)%s*$") +end + + +function StringUtil.TrimStart(str) + return str:match("^%s*(.+)") +end + + +function StringUtil.TrimEnd(str) + return str:match("(.-)%s*$") +end + + +function StringUtil.RemoveExcessWhitespace(str) + return str:gsub("%s+", " ") +end + + +function StringUtil.RemoveWhitespace(str) + return str:gsub("%s+", "") +end + + +function StringUtil.EndsWith(str, ends) + return str:match(StringUtil.Escape(ends) .. "$") ~= nil +end + + +function StringUtil.StartsWith(str, starts) + return str:match("^" .. StringUtil.Escape(starts)) ~= nil +end + + +function StringUtil.Contains(str, contains) + return str:find(contains) ~= nil +end + + +function StringUtil.StringBuilder() + local sb = {} + local str = {} + function sb:Append(s) + str[#str + 1] = s + end + function sb:Prepend(s) + table.insert(str, 1, s) + end + function sb:ToString() + return table.concat(str, "") + end + setmetatable(sb, {__tostring=sb.ToString}) + return sb +end + + +function StringUtil.ToCharArray(str) + local chars = {} + for i = 1,#str do + chars[i] = str:sub(1, 1) + end + return chars +end + + +function StringUtil.ToByteArray(str) + local bytes = {} + for i = 1,#str do + bytes[i] = str:sub(1, 1):byte() + end + return bytes +end + + +function StringUtil.EqualsIgnoreCase(str1, str2) + return (str1:lower() == str2:lower()) +end + + +function StringUtil.ToCamelCase(str) + str = str:gsub("[%-_]+([^%-_])", function(s) return s:upper() end) + return str:sub(1, 1):lower() .. str:sub(2) +end + + +function StringUtil.ToPascalCase(str) + str = StringUtil.ToCamelCase(str) + return str:sub(1, 1):upper() .. str:sub(2) +end + + +function StringUtil.ToSnakeCase(str, uppercase) + str = str:gsub("[%-_]+", "_"):gsub("([^%u%-_])(%u)", function(s1, s2) return s1 .. "_" .. s2:lower() end) + if (uppercase) then str = str:upper() else str = str:lower() end + return str +end + + +function StringUtil.ToKebabCase(str, uppercase) + str = str:gsub("[%-_]+", "-"):gsub("([^%u%-_])(%u)", function(s1, s2) return s1 .. "-" .. s2:lower() end) + if (uppercase) then str = str:upper() else str = str:lower() end + return str +end + + +setmetatable(StringUtil, {__index = string}) + + +return StringUtil \ No newline at end of file From 597107f48f02b6988a9bd9497d2e092434986ad5 Mon Sep 17 00:00:00 2001 From: Stephen Leitnick Date: Wed, 4 Dec 2019 16:09:24 -0800 Subject: [PATCH 5/9] Add StringUtil documentation --- .../Aero/Shared/StringUtil.lua | 128 +++++++++++++++++- 1 file changed, 126 insertions(+), 2 deletions(-) diff --git a/src/ReplicatedStorage/Aero/Shared/StringUtil.lua b/src/ReplicatedStorage/Aero/Shared/StringUtil.lua index 8f0141f..a474522 100644 --- a/src/ReplicatedStorage/Aero/Shared/StringUtil.lua +++ b/src/ReplicatedStorage/Aero/Shared/StringUtil.lua @@ -21,6 +21,130 @@ StringUtil.ToKebabCase(String str [, uppercase]) StringUtil.StringBuilder() + EXAMPLES: + + Trim: + + Trims whitespace from the start and end of the string. + + StringUtil.Trim(" hello world ") == "hello world" + + + TrimStart: + + The same as Trim, but only trims the start of the string: + + StringUtil.TrimStart(" hello world ") == "hello world " + + + TrimEnd: + + The same as Trim, but only trims the end of the string: + + StringUtil.TrimEnd(" hello world ") == " hello world" + + + EqualsIgnoreCase: + + Checks if two strings are equal, but ignores their case: + + StringUtil.EqualsIgnoreCase("HELLo woRLD", "hEllo wORLd") == true + + + RemoveWhitespace: + + Removes all whitespace from a string: + + StringUtil.RemoveWhitespace(" hello World!\n") == "helloWorld!" + + + RemoveExcessWhitespace: + + Replaces all whitespace with a single space. This does not trim the string: + + StringUtil.RemoveExcessWhitespace("This is a \n test") == "This is a test" + + + EndsWith: + + Checks if a string ends with a certain string: + + StringUtil.EndsWith("Hello world", "rld") == true + + + StartsWith: + + Checks if a string starts with a certain string: + + StringUtil.StartsWith("Hello world", "He") == true + + + Contains: + + Checks if a string contains another string: + + StringUtil.Contains("Hello world", "lo wor") == true + + + ToCharArray: + + Returns a table of all the characters in the string: + + StringUtil.ToCharArray("Hello") >>> {"H","e","l","l","o"} + + + ToByteArray: + + Returns a table of all the bytes of each character in the string: + + StringUtil.ToByteArray("Hello") >>> {72,101,108,108,111} + + + ToCamelCase: + + Returns a string in camelCase: + + StringUtil.ToCamelCase("Hello_world-abc") == "helloWorldAbc" + + + ToPascalCase: + + Returns a string in PascalCase: + + StringUtil.ToPascalCase("Hello_world-abc") == "HelloWorldAbc" + + + ToSnakeCase: + + Returns a string in snake_case or SNAKE_CASE: + + StringUtil.ToPascalCase("Hello_world-abc") == "hello_world_abc" + StringUtil.ToPascalCase("Hello_world-abc", true) == "HELLO_WORLD_ABC" + + + ToKebabCase: + + Returns a string in kebab-case or KEBAB-CASE: + + StringUtil.ToKebabCase("Hello_world-abc") == "hello-world-abc" + StringUtil.ToKebabCase("Hello_world-abc", true) == "HELLO-WORLD-ABC" + + + StringBuilder: + + Creates a StringBuilder object that can be used to build a string. This + is useful when a large string needs to be concatenated. Traditional + concatenation of a string using ".." can be a performance issue, and thus + StringBuilders can be used to store the pieces of the string in a table + and then concatenate them all at once: + + local builder = StringUtil.StringBuilder() + + builder:Append("world") + builder:Prepend("Hello ") + builder:ToString() == "Hello world" + tostring(builder) == "Hello world" + --]] @@ -93,7 +217,7 @@ end function StringUtil.ToCharArray(str) local chars = {} for i = 1,#str do - chars[i] = str:sub(1, 1) + chars[i] = str:sub(i, i) end return chars end @@ -102,7 +226,7 @@ end function StringUtil.ToByteArray(str) local bytes = {} for i = 1,#str do - bytes[i] = str:sub(1, 1):byte() + bytes[i] = str:sub(i, i):byte() end return bytes end From 9572fdb0903b6c61f9bad2b20dec5580339ff558 Mon Sep 17 00:00:00 2001 From: Stephen Leitnick Date: Wed, 4 Dec 2019 16:14:47 -0800 Subject: [PATCH 6/9] Document Escape function in StringUtil --- src/ReplicatedStorage/Aero/Shared/StringUtil.lua | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/ReplicatedStorage/Aero/Shared/StringUtil.lua b/src/ReplicatedStorage/Aero/Shared/StringUtil.lua index a474522..6fbd4bc 100644 --- a/src/ReplicatedStorage/Aero/Shared/StringUtil.lua +++ b/src/ReplicatedStorage/Aero/Shared/StringUtil.lua @@ -19,6 +19,7 @@ StringUtil.ToPascalCase(String str) StringUtil.ToSnakeCase(String str [, uppercase]) StringUtil.ToKebabCase(String str [, uppercase]) + StringUtil.Escape(str) StringUtil.StringBuilder() EXAMPLES: @@ -130,6 +131,15 @@ StringUtil.ToKebabCase("Hello_world-abc", true) == "HELLO-WORLD-ABC" + Escape: + + Escapes a string from pattern characters. In other words, it prefixes + any special pattern characters with a %. For example, the dollar + sign $ would become %$. Example below: + + StringUtil.Escape("Hello. World$ ^-^") == "Hello%. World%$ %^%-%^" + + StringBuilder: Creates a StringBuilder object that can be used to build a string. This From b2f9a516568ef072fd38a261275b8f32790c4323 Mon Sep 17 00:00:00 2001 From: Stephen Leitnick Date: Wed, 4 Dec 2019 18:29:41 -0800 Subject: [PATCH 7/9] Add ByteArrayToString to StringUtil --- .../Aero/Shared/StringUtil.lua | 39 +++++++++++++++++-- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/src/ReplicatedStorage/Aero/Shared/StringUtil.lua b/src/ReplicatedStorage/Aero/Shared/StringUtil.lua index 6fbd4bc..9942525 100644 --- a/src/ReplicatedStorage/Aero/Shared/StringUtil.lua +++ b/src/ReplicatedStorage/Aero/Shared/StringUtil.lua @@ -15,6 +15,7 @@ StringUtil.Contains(String str, String contains) StringUtil.ToCharArray(String str) StringUtil.ToByteArray(String str) + StringUtil.ByteArrayToString(Table bytes) StringUtil.ToCamelCase(String str) StringUtil.ToPascalCase(String str) StringUtil.ToSnakeCase(String str [, uppercase]) @@ -101,6 +102,13 @@ StringUtil.ToByteArray("Hello") >>> {72,101,108,108,111} + ByteArrayToString: + + Transforms an array of bytes into a string: + + StringUtil.ByteArrayToString({97, 98, 99}) == "abc" + + ToCamelCase: Returns a string in camelCase: @@ -160,6 +168,8 @@ local StringUtil = {} +local MAX_TUPLE = 7997 + function StringUtil.Escape(str) local escaped = str:gsub("([%.%$%^%(%)%[%]%+%-%*%?%%])", "%%%1") @@ -225,8 +235,9 @@ end function StringUtil.ToCharArray(str) - local chars = {} - for i = 1,#str do + local len = #str + local chars = table.create(len) + for i = 1,len do chars[i] = str:sub(i, i) end return chars @@ -234,14 +245,34 @@ end function StringUtil.ToByteArray(str) - local bytes = {} - for i = 1,#str do + local len = #str + if (len == 0) then return {} end + if (len <= MAX_TUPLE) then + return table.pack(str:byte(1, #str)) + end + local bytes = table.create(len) + for i = 1,len do bytes[i] = str:sub(i, i):byte() end return bytes end +function StringUtil.ByteArrayToString(bytes) + local size = #bytes + if (size <= MAX_TUPLE) then + return string.char(table.unpack(bytes)) + end + local numChunks = math.ceil(size / MAX_TUPLE) + local stringBuild = table.create(numChunks) + for i = 1, numChunks do + local chunk = string.char(table.unpack(bytes, ((i - 1) * MAX_TUPLE) + 1, math.min(size, ((i - 1) * MAX_TUPLE) + MAX_TUPLE))) + stringBuild[i] = chunk + end + return table.concat(stringBuild, "") +end + + function StringUtil.EqualsIgnoreCase(str1, str2) return (str1:lower() == str2:lower()) end From 93508b3bb6a2bba5cccdacd99d46009646202c75 Mon Sep 17 00:00:00 2001 From: Stephen Leitnick Date: Wed, 4 Dec 2019 18:48:58 -0800 Subject: [PATCH 8/9] TableUtil improvements --- .../Aero/Shared/TableUtil.lua | 34 +++++++++++++++---- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/src/ReplicatedStorage/Aero/Shared/TableUtil.lua b/src/ReplicatedStorage/Aero/Shared/TableUtil.lua index 3a46be8..4b2d59a 100644 --- a/src/ReplicatedStorage/Aero/Shared/TableUtil.lua +++ b/src/ReplicatedStorage/Aero/Shared/TableUtil.lua @@ -5,6 +5,7 @@ --[[ TableUtil.Copy(Table tbl) + TableUtil.CopyShallow(Table tbl) TableUtil.Sync(Table tbl, Table templateTbl) TableUtil.Print(Table tbl, String label, Boolean deepPrint) TableUtil.FastRemove(Table tbl, Number index) @@ -24,12 +25,24 @@ Copy: - Performs a deep copy of the given table. + Performs a deep copy of the given table. In other words, + all nested tables will also get copied. local tbl = {"a", "b", "c"} local tblCopy = TableUtil.Copy(tbl) + CopyShallow: + + Performs a shallow copy of the given table. In other words, + all nested tables will not be copied, but only moved by + reference. Thus, a nested table in both the original and + the copy will be the same. + + local tbl = {"a", "b", "c"} + local tblCopy = TableUtil.CopyShallow(tbl) + + Sync: Synchronizes a table to a template table. If the table does not have an @@ -196,7 +209,7 @@ local http = game:GetService("HttpService") local function CopyTable(t) assert(type(t) == "table", "First argument must be a table") - local tCopy = {} + local tCopy = table.create(#t) for k,v in pairs(t) do if (type(v) == "table") then tCopy[k] = CopyTable(v) @@ -208,6 +221,13 @@ local function CopyTable(t) end +local function CopyTableShallow(t) + local tCopy = table.create(#t) + for k,v in pairs(t) do tCopy[k] = v end + return tCopy +end + + local function Sync(tbl, templateTbl) assert(type(tbl) == "table", "First argument must be a table") @@ -267,7 +287,7 @@ end local function Map(t, f) assert(type(t) == "table", "First argument must be a table") assert(type(f) == "function", "Second argument must be an array") - local newT = {} + local newT = table.create(#t) for k,v in pairs(t) do newT[k] = f(v, k, t) end @@ -278,7 +298,7 @@ end local function Filter(t, f) assert(type(t) == "table", "First argument must be a table") assert(type(f) == "function", "Second argument must be an array") - local newT = {} + local newT = table.create(#t) if (#t > 0) then local n = 0 for i = 1,#t do @@ -391,8 +411,8 @@ end local function Reverse(tbl) - local tblRev = {} local n = #tbl + local tblRev = table.create(n) for i = 1,n do tblRev[i] = tbl[n - i + 1] end @@ -402,8 +422,9 @@ end local function Shuffle(tbl) assert(type(tbl) == "table", "First argument must be a table") + local rng = Random.new() for i = #tbl, 2, -1 do - local j = math.random(i) + local j = rng:NextInteger(1, i) tbl[i], tbl[j] = tbl[j], tbl[i] end end @@ -435,6 +456,7 @@ end TableUtil.Copy = CopyTable +TableUtil.CopyShallow = CopyTableShallow TableUtil.Sync = Sync TableUtil.FastRemove = FastRemove TableUtil.FastRemoveFirstValue = FastRemoveFirstValue From 08a6656a9b8e54a2ec1ff4147142d7d7a2ad8a53 Mon Sep 17 00:00:00 2001 From: Stephen Leitnick Date: Wed, 4 Dec 2019 18:50:22 -0800 Subject: [PATCH 9/9] StringUtil doc format --- .../Aero/Shared/StringUtil.lua | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/ReplicatedStorage/Aero/Shared/StringUtil.lua b/src/ReplicatedStorage/Aero/Shared/StringUtil.lua index 9942525..c43e453 100644 --- a/src/ReplicatedStorage/Aero/Shared/StringUtil.lua +++ b/src/ReplicatedStorage/Aero/Shared/StringUtil.lua @@ -34,98 +34,98 @@ TrimStart: - The same as Trim, but only trims the start of the string: + The same as Trim, but only trims the start of the string. StringUtil.TrimStart(" hello world ") == "hello world " TrimEnd: - The same as Trim, but only trims the end of the string: + The same as Trim, but only trims the end of the string. StringUtil.TrimEnd(" hello world ") == " hello world" EqualsIgnoreCase: - Checks if two strings are equal, but ignores their case: + Checks if two strings are equal, but ignores their case. StringUtil.EqualsIgnoreCase("HELLo woRLD", "hEllo wORLd") == true RemoveWhitespace: - Removes all whitespace from a string: + Removes all whitespace from a string. StringUtil.RemoveWhitespace(" hello World!\n") == "helloWorld!" RemoveExcessWhitespace: - Replaces all whitespace with a single space. This does not trim the string: + Replaces all whitespace with a single space. This does not trim the string. StringUtil.RemoveExcessWhitespace("This is a \n test") == "This is a test" EndsWith: - Checks if a string ends with a certain string: + Checks if a string ends with a certain string. StringUtil.EndsWith("Hello world", "rld") == true StartsWith: - Checks if a string starts with a certain string: + Checks if a string starts with a certain string. StringUtil.StartsWith("Hello world", "He") == true Contains: - Checks if a string contains another string: + Checks if a string contains another string. StringUtil.Contains("Hello world", "lo wor") == true ToCharArray: - Returns a table of all the characters in the string: + Returns a table of all the characters in the string. StringUtil.ToCharArray("Hello") >>> {"H","e","l","l","o"} ToByteArray: - Returns a table of all the bytes of each character in the string: + Returns a table of all the bytes of each character in the string. StringUtil.ToByteArray("Hello") >>> {72,101,108,108,111} ByteArrayToString: - Transforms an array of bytes into a string: + Transforms an array of bytes into a string. StringUtil.ByteArrayToString({97, 98, 99}) == "abc" ToCamelCase: - Returns a string in camelCase: + Returns a string in camelCase. StringUtil.ToCamelCase("Hello_world-abc") == "helloWorldAbc" ToPascalCase: - Returns a string in PascalCase: + Returns a string in PascalCase. StringUtil.ToPascalCase("Hello_world-abc") == "HelloWorldAbc" ToSnakeCase: - Returns a string in snake_case or SNAKE_CASE: + Returns a string in snake_case or SNAKE_CASE. StringUtil.ToPascalCase("Hello_world-abc") == "hello_world_abc" StringUtil.ToPascalCase("Hello_world-abc", true) == "HELLO_WORLD_ABC" @@ -133,7 +133,7 @@ ToKebabCase: - Returns a string in kebab-case or KEBAB-CASE: + Returns a string in kebab-case or KEBAB-CASE. StringUtil.ToKebabCase("Hello_world-abc") == "hello-world-abc" StringUtil.ToKebabCase("Hello_world-abc", true) == "HELLO-WORLD-ABC" @@ -143,7 +143,7 @@ Escapes a string from pattern characters. In other words, it prefixes any special pattern characters with a %. For example, the dollar - sign $ would become %$. Example below: + sign $ would become %$. See the example below. StringUtil.Escape("Hello. World$ ^-^") == "Hello%. World%$ %^%-%^" @@ -154,7 +154,7 @@ is useful when a large string needs to be concatenated. Traditional concatenation of a string using ".." can be a performance issue, and thus StringBuilders can be used to store the pieces of the string in a table - and then concatenate them all at once: + and then concatenate them all at once. local builder = StringUtil.StringBuilder()