summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorflywind <43030857+xflywind@users.noreply.github.com>2021-01-05 10:52:26 -0600
committerGitHub <noreply@github.com>2021-01-05 17:52:26 +0100
commitc04f305bf791ee5ecfd17f5a40d009d9c6b6f07a (patch)
tree5c0a24f50e8fd073448ff98634d7d4c01e01f1da
parent0c4bd65e8d2d81a5e52624215e864f7846eb320b (diff)
downloadNim-c04f305bf791ee5ecfd17f5a40d009d9c6b6f07a.tar.gz
make cstrutils work in VM (#16590)
* make cstrutils work in VM

* more
-rw-r--r--lib/pure/cstrutils.nim180
-rw-r--r--lib/pure/strutils.nim15
-rw-r--r--lib/std/private/strimpl.nim29
-rw-r--r--tests/stdlib/tcstrutils.nim12
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()