From 2b9c8d9376f3f65a552b9942d0b07a42efaa60a2 Mon Sep 17 00:00:00 2001 From: Spotandjake Date: Mon, 12 Aug 2024 20:31:08 -0400 Subject: [PATCH 1/7] feat(stdlib): Add `String.repeat` to `String` module --- compiler/test/stdlib/string.test.gr | 9 +++++++ stdlib/string.gr | 37 +++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/compiler/test/stdlib/string.test.gr b/compiler/test/stdlib/string.test.gr index 90e83e5c8..855f1210e 100644 --- a/compiler/test/stdlib/string.test.gr +++ b/compiler/test/stdlib/string.test.gr @@ -551,3 +551,12 @@ assert String.toAsciiLowercase("aBc🌾12Y") == "abc🌾12y" // toAsciiUppercase assert String.toAsciiUppercase("aBc🌾12Y") == "ABC🌾12Y" + +// String.repeat +assert String.repeat("=.", 1) == "=." +assert String.repeat("=", 10) == "==========" +assert String.repeat("=.", 10) == "=.=.=.=.=.=.=.=.=.=." +assert String.repeat("=.", 0) == "" +assert String.repeat("", 0) == "" +assert String.repeat("=.", -1) == ".=" +assert String.repeat("=.", -10) == ".=.=.=.=.=.=.=.=.=.=" diff --git a/stdlib/string.gr b/stdlib/string.gr index cf06927cc..5d9695222 100644 --- a/stdlib/string.gr +++ b/stdlib/string.gr @@ -2338,3 +2338,40 @@ provide let toAsciiUppercase = string => { } implode(chars) } + +/** + * Produces a new string by repeating a given substring a given number of times. + * + * @param string: The string to repeat + * @param count: The number of times to repeat, negative values reverse the substring + * @returns A string containing the repeated input string + * + * @example assert String.repeat("=", 5) == "=====" + * @example assert String.repeat("ab", -5) == "bababababa" + * @example assert String.repeat(".", 0) == "" + * + * @since v0.6.7 + */ +@unsafe +provide let repeat = (string, count) => { + use WasmI32.{ (+), (*), (<) } + let mut rawCount = untagSimpleNumber(count) + if (WasmI32.eqz(rawCount)) return "" + let string = if (rawCount < 0n) { + rawCount *= -1n + reverse(string) + } else { + string + } + let mut stringPtr = WasmI32.fromGrain(string) + let byteLength = WasmI32.load(stringPtr, 4n) + if (WasmI32.eqz(byteLength)) return "" + stringPtr += 8n + let strPtr = allocateString(byteLength * rawCount) + let strContentPtr = strPtr + 8n + for (let mut i = 0n; i < rawCount; i += 1n) { + Memory.copy(strContentPtr + byteLength * i, stringPtr, byteLength) + } + ignore(stringPtr) + return WasmI32.toGrain(strPtr): String +} From 38ddc8532042ff33b1a5a677e48ad3862204554b Mon Sep 17 00:00:00 2001 From: Spotandjake Date: Mon, 12 Aug 2024 20:36:50 -0400 Subject: [PATCH 2/7] chore: Generate docs and validate count --- stdlib/string.gr | 7 ++++++- stdlib/string.md | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/stdlib/string.gr b/stdlib/string.gr index 5d9695222..74056d2fc 100644 --- a/stdlib/string.gr +++ b/stdlib/string.gr @@ -2346,6 +2346,8 @@ provide let toAsciiUppercase = string => { * @param count: The number of times to repeat, negative values reverse the substring * @returns A string containing the repeated input string * + * @throws InvalidArgument(String): When the `count` is not an integer + * * @example assert String.repeat("=", 5) == "=====" * @example assert String.repeat("ab", -5) == "bababababa" * @example assert String.repeat(".", 0) == "" @@ -2354,7 +2356,10 @@ provide let toAsciiUppercase = string => { */ @unsafe provide let repeat = (string, count) => { - use WasmI32.{ (+), (*), (<) } + use WasmI32.{ (+), (*), (<), (&), (!=) } + if ((WasmI32.fromGrain(count) & 1n) != 1n) { + throw InvalidArgument("Invalid count value") + } let mut rawCount = untagSimpleNumber(count) if (WasmI32.eqz(rawCount)) return "" let string = if (rawCount < 0n) { diff --git a/stdlib/string.md b/stdlib/string.md index 58ab18066..b31799996 100644 --- a/stdlib/string.md +++ b/stdlib/string.md @@ -1174,3 +1174,49 @@ Examples: assert String.toAsciiUppercase("aBc123") == "ABC123" ``` +### String.**repeat** + +
+Added in next +No other changes yet. +
+ +```grain +repeat : (string: String, count: Number) => String +``` + +Produces a new string by repeating a given substring a given number of times. + +Parameters: + +|param|type|description| +|-----|----|-----------| +|`string`|`String`|The string to repeat| +|`count`|`Number`|The number of times to repeat, negative values reverse the substring| + +Returns: + +|type|description| +|----|-----------| +|`String`|A string containing the repeated input string| + +Throws: + +`InvalidArgument(String)` + +* When the `count` is not an integer + +Examples: + +```grain +assert String.repeat("=", 5) == "=====" +``` + +```grain +assert String.repeat("ab", -5) == "bababababa" +``` + +```grain +assert String.repeat(".", 0) == "" +``` + From 66d6eaa1b88b118975edb47b39defdb7c519df3e Mon Sep 17 00:00:00 2001 From: Spotandjake Date: Thu, 15 Aug 2024 15:47:22 -0400 Subject: [PATCH 3/7] chore: Throw exception on negative values. --- compiler/test/stdlib/string.test.gr | 2 -- stdlib/string.gr | 13 ++++++------- stdlib/string.md | 11 ++++------- 3 files changed, 10 insertions(+), 16 deletions(-) diff --git a/compiler/test/stdlib/string.test.gr b/compiler/test/stdlib/string.test.gr index 855f1210e..2839e1fa2 100644 --- a/compiler/test/stdlib/string.test.gr +++ b/compiler/test/stdlib/string.test.gr @@ -558,5 +558,3 @@ assert String.repeat("=", 10) == "==========" assert String.repeat("=.", 10) == "=.=.=.=.=.=.=.=.=.=." assert String.repeat("=.", 0) == "" assert String.repeat("", 0) == "" -assert String.repeat("=.", -1) == ".=" -assert String.repeat("=.", -10) == ".=.=.=.=.=.=.=.=.=.=" diff --git a/stdlib/string.gr b/stdlib/string.gr index 74056d2fc..5b0da2e85 100644 --- a/stdlib/string.gr +++ b/stdlib/string.gr @@ -38,6 +38,8 @@ provide enum Encoding { exception MalformedUnicode +exception RangeError + /** * Concatenate two strings. * @@ -2343,13 +2345,13 @@ provide let toAsciiUppercase = string => { * Produces a new string by repeating a given substring a given number of times. * * @param string: The string to repeat - * @param count: The number of times to repeat, negative values reverse the substring + * @param count: The number of times to repeat the string * @returns A string containing the repeated input string * * @throws InvalidArgument(String): When the `count` is not an integer + * @throws InvalidArgument(String): When the `count` is negative * * @example assert String.repeat("=", 5) == "=====" - * @example assert String.repeat("ab", -5) == "bababababa" * @example assert String.repeat(".", 0) == "" * * @since v0.6.7 @@ -2362,11 +2364,8 @@ provide let repeat = (string, count) => { } let mut rawCount = untagSimpleNumber(count) if (WasmI32.eqz(rawCount)) return "" - let string = if (rawCount < 0n) { - rawCount *= -1n - reverse(string) - } else { - string + if (rawCount < 0n) { + throw InvalidArgument("Invalid count must be a positive integer or zero") } let mut stringPtr = WasmI32.fromGrain(string) let byteLength = WasmI32.load(stringPtr, 4n) diff --git a/stdlib/string.md b/stdlib/string.md index b31799996..5f87de8b3 100644 --- a/stdlib/string.md +++ b/stdlib/string.md @@ -1182,7 +1182,7 @@ No other changes yet. ```grain -repeat : (string: String, count: Number) => String +repeat : (string: a, count: Number) => String ``` Produces a new string by repeating a given substring a given number of times. @@ -1191,8 +1191,8 @@ Parameters: |param|type|description| |-----|----|-----------| -|`string`|`String`|The string to repeat| -|`count`|`Number`|The number of times to repeat, negative values reverse the substring| +|`string`|`a`|The string to repeat| +|`count`|`Number`|The number of times to repeat the string| Returns: @@ -1205,6 +1205,7 @@ Throws: `InvalidArgument(String)` * When the `count` is not an integer +* When the `count` is negative Examples: @@ -1212,10 +1213,6 @@ Examples: assert String.repeat("=", 5) == "=====" ``` -```grain -assert String.repeat("ab", -5) == "bababababa" -``` - ```grain assert String.repeat(".", 0) == "" ``` From 9bb0a3cdf8bb9748e41ebd61ad33ed46ba026f45 Mon Sep 17 00:00:00 2001 From: Spotandjake Date: Fri, 16 Aug 2024 16:46:11 -0400 Subject: [PATCH 4/7] feat: remove unused `exception` --- stdlib/string.gr | 2 -- 1 file changed, 2 deletions(-) diff --git a/stdlib/string.gr b/stdlib/string.gr index 5b0da2e85..0a3f638f9 100644 --- a/stdlib/string.gr +++ b/stdlib/string.gr @@ -38,8 +38,6 @@ provide enum Encoding { exception MalformedUnicode -exception RangeError - /** * Concatenate two strings. * From 9deb02702d813312e94afdf71a803cad6a3e77ee Mon Sep 17 00:00:00 2001 From: Spotandjake Date: Sat, 17 Aug 2024 11:55:16 -0400 Subject: [PATCH 5/7] chore: Apply suggestions from code review --- compiler/test/stdlib/string.test.gr | 11 ++++++----- stdlib/string.gr | 22 ++++++++++------------ stdlib/string.md | 8 ++++---- 3 files changed, 20 insertions(+), 21 deletions(-) diff --git a/compiler/test/stdlib/string.test.gr b/compiler/test/stdlib/string.test.gr index 2839e1fa2..7e7507c30 100644 --- a/compiler/test/stdlib/string.test.gr +++ b/compiler/test/stdlib/string.test.gr @@ -553,8 +553,9 @@ assert String.toAsciiLowercase("aBc🌾12Y") == "abc🌾12y" assert String.toAsciiUppercase("aBc🌾12Y") == "ABC🌾12Y" // String.repeat -assert String.repeat("=.", 1) == "=." -assert String.repeat("=", 10) == "==========" -assert String.repeat("=.", 10) == "=.=.=.=.=.=.=.=.=.=." -assert String.repeat("=.", 0) == "" -assert String.repeat("", 0) == "" +assert String.repeat(1, "=.") == "=." +assert String.repeat(10, "=") == "==========" +assert String.repeat(10, "=.") == "=.=.=.=.=.=.=.=.=.=." +assert String.repeat(0, "=.") == "" +assert String.repeat(0, "") == "" +assert String.repeat(5, "") == "" diff --git a/stdlib/string.gr b/stdlib/string.gr index 0a3f638f9..6bbe34bc2 100644 --- a/stdlib/string.gr +++ b/stdlib/string.gr @@ -2342,38 +2342,36 @@ provide let toAsciiUppercase = string => { /** * Produces a new string by repeating a given substring a given number of times. * - * @param string: The string to repeat * @param count: The number of times to repeat the string + * @param string: The string to repeat * @returns A string containing the repeated input string * * @throws InvalidArgument(String): When the `count` is not an integer * @throws InvalidArgument(String): When the `count` is negative * - * @example assert String.repeat("=", 5) == "=====" - * @example assert String.repeat(".", 0) == "" + * @example assert String.repeat(5, "=") == "=====" + * @example assert String.repeat(0, ".") == "" * * @since v0.6.7 */ @unsafe -provide let repeat = (string, count) => { +provide let repeat = (count, string: String) => { use WasmI32.{ (+), (*), (<), (&), (!=) } if ((WasmI32.fromGrain(count) & 1n) != 1n) { throw InvalidArgument("Invalid count value") } - let mut rawCount = untagSimpleNumber(count) - if (WasmI32.eqz(rawCount)) return "" + let rawCount = untagSimpleNumber(count) if (rawCount < 0n) { throw InvalidArgument("Invalid count must be a positive integer or zero") } - let mut stringPtr = WasmI32.fromGrain(string) + let stringPtr = WasmI32.fromGrain(string) let byteLength = WasmI32.load(stringPtr, 4n) - if (WasmI32.eqz(byteLength)) return "" - stringPtr += 8n - let strPtr = allocateString(byteLength * rawCount) - let strContentPtr = strPtr + 8n + let stringPtr = stringPtr + 8n + let newStringPtr = allocateString(byteLength * rawCount) + let strContentPtr = newStringPtr + 8n for (let mut i = 0n; i < rawCount; i += 1n) { Memory.copy(strContentPtr + byteLength * i, stringPtr, byteLength) } ignore(stringPtr) - return WasmI32.toGrain(strPtr): String + return WasmI32.toGrain(newStringPtr): String } diff --git a/stdlib/string.md b/stdlib/string.md index 5f87de8b3..c0ee1202d 100644 --- a/stdlib/string.md +++ b/stdlib/string.md @@ -1182,7 +1182,7 @@ No other changes yet. ```grain -repeat : (string: a, count: Number) => String +repeat : (count: Number, string: String) => String ``` Produces a new string by repeating a given substring a given number of times. @@ -1191,8 +1191,8 @@ Parameters: |param|type|description| |-----|----|-----------| -|`string`|`a`|The string to repeat| |`count`|`Number`|The number of times to repeat the string| +|`string`|`String`|The string to repeat| Returns: @@ -1210,10 +1210,10 @@ Throws: Examples: ```grain -assert String.repeat("=", 5) == "=====" +assert String.repeat(5, "=") == "=====" ``` ```grain -assert String.repeat(".", 0) == "" +assert String.repeat(0, ".") == "" ``` From 833e54019b3d38923931047e9c9515610d9a88f5 Mon Sep 17 00:00:00 2001 From: Oscar Spencer Date: Sat, 31 Aug 2024 15:51:01 -0500 Subject: [PATCH 6/7] Apply suggestions from code review --- stdlib/string.gr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/string.gr b/stdlib/string.gr index 6bbe34bc2..84e3aa389 100644 --- a/stdlib/string.gr +++ b/stdlib/string.gr @@ -2340,7 +2340,7 @@ provide let toAsciiUppercase = string => { } /** - * Produces a new string by repeating a given substring a given number of times. + * Produces a new string by repeating a substring a given number of times. * * @param count: The number of times to repeat the string * @param string: The string to repeat @@ -2372,6 +2372,6 @@ provide let repeat = (count, string: String) => { for (let mut i = 0n; i < rawCount; i += 1n) { Memory.copy(strContentPtr + byteLength * i, stringPtr, byteLength) } - ignore(stringPtr) + ignore(string) return WasmI32.toGrain(newStringPtr): String } From 6c8d84659981ebc12f50064a4df54433d57061af Mon Sep 17 00:00:00 2001 From: Spotandjake Date: Sat, 31 Aug 2024 17:01:53 -0400 Subject: [PATCH 7/7] chore: regen graindoc --- stdlib/string.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/string.md b/stdlib/string.md index c0ee1202d..3f7e79ebb 100644 --- a/stdlib/string.md +++ b/stdlib/string.md @@ -1185,7 +1185,7 @@ No other changes yet. repeat : (count: Number, string: String) => String ``` -Produces a new string by repeating a given substring a given number of times. +Produces a new string by repeating a substring a given number of times. Parameters: