diff options
author | flywind <43030857+xflywind@users.noreply.github.com> | 2021-01-05 10:52:26 -0600 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-01-05 17:52:26 +0100 |
commit | c04f305bf791ee5ecfd17f5a40d009d9c6b6f07a (patch) | |
tree | 5c0a24f50e8fd073448ff98634d7d4c01e01f1da | |
parent | 0c4bd65e8d2d81a5e52624215e864f7846eb320b (diff) | |
download | Nim-c04f305bf791ee5ecfd17f5a40d009d9c6b6f07a.tar.gz |
make cstrutils work in VM (#16590)
* make cstrutils work in VM * more
-rw-r--r-- | lib/pure/cstrutils.nim | 180 | ||||
-rw-r--r-- | lib/pure/strutils.nim | 15 | ||||
-rw-r--r-- | lib/std/private/strimpl.nim | 29 | ||||
-rw-r--r-- | tests/stdlib/tcstrutils.nim | 12 |
4 files changed, 132 insertions, 104 deletions
diff --git a/lib/pure/cstrutils.nim b/lib/pure/cstrutils.nim index 390aac00b..cdef7c804 100644 --- a/lib/pure/cstrutils.nim +++ b/lib/pure/cstrutils.nim @@ -16,93 +16,103 @@ import std/private/strimpl when defined(js): - func startsWith*(s, prefix: cstring): bool {.importjs: "#.startsWith(#)".} + func jsStartsWith(s, prefix: cstring): bool {.importjs: "#.startsWith(#)".} + func jsEndsWith(s, suffix: cstring): bool {.importjs: "#.endsWith(#)".} - func endsWith*(s, suffix: cstring): bool {.importjs: "#.endsWith(#)".} - func cmpIgnoreStyle*(a, b: cstring): int = - cmpIgnoreStyleImpl(a, b) - - func cmpIgnoreCase*(a, b: cstring): int = - cmpIgnoreCaseImpl(a, b) - - # JS string has more operations that might warrant its own module: - # https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String -else: - func startsWith*(s, prefix: cstring): bool {.rtl, extern: "csuStartsWith".} = - ## Returns true if `s` starts with `prefix`. - ## - ## If `prefix == ""` true is returned. - ## - ## JS backend uses native `String.prototype.startsWith`. - runnableExamples: - assert startsWith(cstring"Hello, Nimion", cstring"Hello") - assert not startsWith(cstring"Hello, Nimion", cstring"Nimion") - - var i = 0 - while true: - if prefix[i] == '\0': return true - if s[i] != prefix[i]: return false - inc(i) +func startsWith*(s, prefix: cstring): bool {.rtl, extern: "csuStartsWith".} = + ## Returns true if `s` starts with `prefix`. + ## + ## JS backend uses native `String.prototype.startsWith`. + runnableExamples: + assert startsWith(cstring"Hello, Nimion", cstring"Hello") + assert not startsWith(cstring"Hello, Nimion", cstring"Nimion") + assert startsWith(cstring"Hello", cstring"") + when nimvm: + startsWithImpl(s, prefix) + else: + when defined(js): + result = jsStartsWith(s, prefix) + else: + var i = 0 + while true: + if prefix[i] == '\0': return true + if s[i] != prefix[i]: return false + inc(i) - func endsWith*(s, suffix: cstring): bool {.rtl, extern: "csuEndsWith".} = - ## Returns true if `s` ends with `suffix`. - ## - ## If `suffix == ""` true is returned. - ## - ## JS backend uses native `String.prototype.endsWith`. - runnableExamples: - assert endsWith(cstring"Hello, Nimion", cstring"Nimion") - assert not endsWith(cstring"Hello, Nimion", cstring"Hello") +func endsWith*(s, suffix: cstring): bool {.rtl, extern: "csuEndsWith".} = + ## Returns true if `s` ends with `suffix`. + ## + ## JS backend uses native `String.prototype.endsWith`. + runnableExamples: + assert endsWith(cstring"Hello, Nimion", cstring"Nimion") + assert not endsWith(cstring"Hello, Nimion", cstring"Hello") + assert endsWith(cstring"Hello", cstring"") + when nimvm: + endsWithImpl(s, suffix) + else: + when defined(js): + result = jsEndsWith(s, suffix) + else: + let slen = s.len + var i = 0 + var j = slen - len(suffix) + while i+j <% slen: + if s[i+j] != suffix[i]: return false + inc(i) + if suffix[i] == '\0': return true - let slen = s.len - var i = 0 - var j = slen - len(suffix) - while i+j <% slen: - if s[i+j] != suffix[i]: return false - inc(i) - if suffix[i] == '\0': return true - - func cmpIgnoreStyle*(a, b: cstring): int {.rtl, extern: "csuCmpIgnoreStyle".} = - ## Semantically the same as `cmp(normalize($a), normalize($b))`. It - ## is just optimized to not allocate temporary strings. This should - ## NOT be used to compare Nim identifier names. use `macros.eqIdent` - ## for that. Returns: - ## - ## .. code-block:: - ## 0 if a == b - ## < 0 if a < b - ## > 0 if a > b - runnableExamples: - assert cmpIgnoreStyle(cstring"hello", cstring"H_e_L_Lo") == 0 - var i = 0 - var j = 0 - while true: - while a[i] == '_': inc(i) - while b[j] == '_': inc(j) # BUGFIX: typo - var aa = toLowerAscii(a[i]) - var bb = toLowerAscii(b[j]) - result = ord(aa) - ord(bb) - if result != 0 or aa == '\0': break - inc(i) - inc(j) - - func cmpIgnoreCase*(a, b: cstring): int {.rtl, extern: "csuCmpIgnoreCase".} = - ## Compares two strings in a case insensitive manner. Returns: - ## - ## .. code-block:: - ## 0 if a == b - ## < 0 if a < b - ## > 0 if a > b - runnableExamples: - assert cmpIgnoreCase(cstring"hello", cstring"HeLLo") == 0 - assert cmpIgnoreCase(cstring"echo", cstring"hello") < 0 - assert cmpIgnoreCase(cstring"yellow", cstring"hello") > 0 +func cmpIgnoreStyle*(a, b: cstring): int {.rtl, extern: "csuCmpIgnoreStyle".} = + ## Semantically the same as `cmp(normalize($a), normalize($b))`. It + ## is just optimized to not allocate temporary strings. This should + ## NOT be used to compare Nim identifier names. use `macros.eqIdent` + ## for that. Returns: + ## + ## .. code-block:: + ## 0 if a == b + ## < 0 if a < b + ## > 0 if a > b + runnableExamples: + assert cmpIgnoreStyle(cstring"hello", cstring"H_e_L_Lo") == 0 + when nimvm: + cmpIgnoreStyleImpl(a, b) + else: + when defined(js): + cmpIgnoreStyleImpl(a, b) + else: + var i = 0 + var j = 0 + while true: + while a[i] == '_': inc(i) + while b[j] == '_': inc(j) # BUGFIX: typo + var aa = toLowerAscii(a[i]) + var bb = toLowerAscii(b[j]) + result = ord(aa) - ord(bb) + if result != 0 or aa == '\0': break + inc(i) + inc(j) - var i = 0 - while true: - var aa = toLowerAscii(a[i]) - var bb = toLowerAscii(b[i]) - result = ord(aa) - ord(bb) - if result != 0 or aa == '\0': break - inc(i) +func cmpIgnoreCase*(a, b: cstring): int {.rtl, extern: "csuCmpIgnoreCase".} = + ## Compares two strings in a case insensitive manner. Returns: + ## + ## .. code-block:: + ## 0 if a == b + ## < 0 if a < b + ## > 0 if a > b + runnableExamples: + assert cmpIgnoreCase(cstring"hello", cstring"HeLLo") == 0 + assert cmpIgnoreCase(cstring"echo", cstring"hello") < 0 + assert cmpIgnoreCase(cstring"yellow", cstring"hello") > 0 + when nimvm: + cmpIgnoreCaseImpl(a, b) + else: + when defined(js): + cmpIgnoreCaseImpl(a, b) + else: + var i = 0 + while true: + var aa = toLowerAscii(a[i]) + var bb = toLowerAscii(b[i]) + result = ord(aa) - ord(bb) + if result != 0 or aa == '\0': break + inc(i) diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index b1418a3ec..a25df9e42 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -81,7 +81,7 @@ when defined(nimVmExportFixed): include "system/inclrtl" import std/private/since -from std/private/strimpl import cmpIgnoreStyleImpl, cmpIgnoreCaseImpl +from std/private/strimpl import cmpIgnoreStyleImpl, cmpIgnoreCaseImpl, startsWithImpl, endsWithImpl const @@ -1530,11 +1530,7 @@ func startsWith*(s, prefix: string): bool {.rtl, extern: "nsuStartsWith".} = let a = "abracadabra" doAssert a.startsWith("abra") == true doAssert a.startsWith("bra") == false - var i = 0 - while true: - if i >= prefix.len: return true - if i >= s.len or s[i] != prefix[i]: return false - inc(i) + startsWithImpl(s, prefix) func endsWith*(s: string, suffix: char): bool {.inline.} = ## Returns true if `s` ends with `suffix`. @@ -1562,12 +1558,7 @@ func endsWith*(s, suffix: string): bool {.rtl, extern: "nsuEndsWith".} = let a = "abracadabra" doAssert a.endsWith("abra") == true doAssert a.endsWith("dab") == false - var i = 0 - var j = len(s) - len(suffix) - while i+j >= 0 and i+j < s.len: - if s[i+j] != suffix[i]: return false - inc(i) - if i >= suffix.len: return true + endsWithImpl(s, suffix) func continuesWith*(s, substr: string, start: Natural): bool {.rtl, extern: "nsuContinuesWith".} = diff --git a/lib/std/private/strimpl.nim b/lib/std/private/strimpl.nim index ae752165a..3fa0dc1d3 100644 --- a/lib/std/private/strimpl.nim +++ b/lib/std/private/strimpl.nim @@ -4,13 +4,13 @@ func toLowerAscii*(c: char): char {.inline.} = else: result = c -template firstCharCaseSensitiveImpl(a, b: typed, aLen, bLen: int) = +template firstCharCaseSensitiveImpl[T: string | cstring](a, b: T, aLen, bLen: int) = if aLen == 0 or bLen == 0: return aLen - bLen if a[0] != b[0]: return ord(a[0]) - ord(b[0]) -template cmpIgnoreStyleImpl*(a, b: typed, firstCharCaseSensitive: static bool = false) = - # a, b are string or cstring +template cmpIgnoreStyleImpl*[T: string | cstring](a, b: T, + firstCharCaseSensitive: static bool = false) = let aLen = a.len let bLen = b.len var i = 0 @@ -37,8 +37,8 @@ template cmpIgnoreStyleImpl*(a, b: typed, firstCharCaseSensitive: static bool = inc i inc j -template cmpIgnoreCaseImpl*(a, b: typed, firstCharCaseSensitive: static bool = false) = - # a, b are string or cstring +template cmpIgnoreCaseImpl*[T: string | cstring](a, b: T, + firstCharCaseSensitive: static bool = false) = let aLen = a.len let bLen = b.len var i = 0 @@ -51,3 +51,22 @@ template cmpIgnoreCaseImpl*(a, b: typed, firstCharCaseSensitive: static bool = f if result != 0: return inc i result = aLen - bLen + +template startsWithImpl*[T: string | cstring](s, prefix: T) = + let prefixLen = prefix.len + let sLen = s.len + var i = 0 + while true: + if i >= prefixLen: return true + if i >= sLen or s[i] != prefix[i]: return false + inc(i) + +template endsWithImpl*[T: string | cstring](s, suffix: T) = + let suffixLen = suffix.len + let sLen = s.len + var i = 0 + var j = sLen - suffixLen + while i+j >= 0 and i+j < sLen: + if s[i+j] != suffix[i]: return false + inc(i) + if i >= suffixLen: return true diff --git a/tests/stdlib/tcstrutils.nim b/tests/stdlib/tcstrutils.nim index 1daf32aa5..ba3b1de68 100644 --- a/tests/stdlib/tcstrutils.nim +++ b/tests/stdlib/tcstrutils.nim @@ -2,21 +2,25 @@ discard """ targets: "c cpp js" """ -import cstrutils +import std/cstrutils -block tcstrutils: +proc main() = let s = cstring "abcdef" doAssert s.startsWith("a") doAssert not s.startsWith("b") doAssert s.endsWith("f") doAssert not s.endsWith("a") + doAssert s.startsWith("") + doAssert s.endsWith("") let a = cstring "abracadabra" doAssert a.startsWith("abra") doAssert not a.startsWith("bra") doAssert a.endsWith("abra") doAssert not a.endsWith("dab") + doAssert a.startsWith("") + doAssert a.endsWith("") doAssert cmpIgnoreCase(cstring "FooBar", "foobar") == 0 doAssert cmpIgnoreCase(cstring "bar", "Foo") < 0 @@ -28,3 +32,7 @@ block tcstrutils: doAssert cmpIgnoreCase(cstring "", cstring "") == 0 doAssert cmpIgnoreCase(cstring "", cstring "Hello") < 0 doAssert cmpIgnoreCase(cstring "wind", cstring "") > 0 + + +static: main() +main() |