feat: add trim, trimEnd, trimStart methods with optional chars argument to String class

Implement six new trimming methods on String: trim(), trim(chars), trimEnd(), trimEnd(chars), trimStart(), trimStart(chars). The core logic uses a private trim_ helper that accepts a chars string, converts it to code points, and iterates from the start and/or end of the string to remove matching characters. Default whitespace set is tab, carriage return, newline, and space. Includes comprehensive test coverage for default and custom character trimming, edge cases (empty strings, all-trimmed strings, 8-bit clean data), and runtime error when chars argument is not a string.
This commit is contained in:
Bob Nystrom
2018-07-15 17:48:56 +00:00
parent e6b216e1b0
commit ffc9ec950c
9 changed files with 209 additions and 20 deletions
+42
View File
@@ -236,6 +236,48 @@ class String is Sequence {
return result
}
trim() { trim_("\t\r\n ", true, true) }
trim(chars) { trim_(chars, true, true) }
trimEnd() { trim_("\t\r\n ", false, true) }
trimEnd(chars) { trim_(chars, false, true) }
trimStart() { trim_("\t\r\n ", true, false) }
trimStart(chars) { trim_(chars, true, false) }
trim_(chars, trimStart, trimEnd) {
if (!(chars is String)) {
Fiber.abort("Characters must be a string.")
}
var codePoints = chars.codePoints.toList
var start
if (trimStart) {
while (start = iterate(start)) {
if (!codePoints.contains(codePointAt_(start))) break
}
if (start == false) return ""
} else {
start = 0
}
var end
if (trimEnd) {
end = byteCount_ - 1
while (end >= start) {
var codePoint = codePointAt_(end)
if (codePoint != -1 && !codePoints.contains(codePoint)) break
end = end - 1
}
if (end < start) return ""
} else {
end = -1
}
return this[start..end]
}
*(count) {
if (!(count is Num) || !count.isInteger || count < 0) {
Fiber.abort("Count must be a non-negative integer.")
+50
View File
@@ -238,6 +238,56 @@ static const char* coreModuleSource =
" return result\n"
" }\n"
"\n"
" trim() { trim_(\"\t\r\n \", true, true) }\n"
"\n"
" trim(chars) { trim_(chars, true, true) }\n"
"\n"
" trimEnd() { trim_(\"\t\r\n \", false, true) }\n"
"\n"
" trimEnd(chars) { trim_(chars, false, true) }\n"
"\n"
" trimStart() { trim_(\"\t\r\n \", true, false) }\n"
"\n"
" trimStart(chars) { trim_(chars, true, false) }\n"
"\n"
" trim_(chars, trimStart, trimEnd) {\n"
" if (!(chars is String)) {\n"
" Fiber.abort(\"Characters must be a string.\")\n"
" }\n"
"\n"
" var codePoints = chars.codePoints.toList\n"
"// System.print(\"code points %(codePoints)\")\n"
"\n"
" var start\n"
" if (trimStart) {\n"
" while (start = iterate(start)) {\n"
" if (!codePoints.contains(codePointAt_(start))) break\n"
" }\n"
"\n"
" if (start == false) return \"\"\n"
" } else {\n"
" start = 0\n"
" }\n"
"\n"
" var end\n"
" if (trimEnd) {\n"
" end = byteCount_ - 1\n"
" while (end >= start) {\n"
" var codePoint = codePointAt_(end)\n"
"// System.print(\"test %(end) : %(codePoint)\")\n"
" if (codePoint != -1 && !codePoints.contains(codePoint)) break\n"
" end = end - 1\n"
" }\n"
"\n"
"// System.print(\"range %(start) %(end)\")\n"
" if (end < start) return \"\"\n"
" } else {\n"
" end = -1\n"
" }\n"
"\n"
" return this[start..end]\n"
" }\n"
"\n"
" *(count) {\n"
" if (!(count is Num) || !count.isInteger || count < 0) {\n"
" Fiber.abort(\"Count must be a non-negative integer.\")\n"