make cstrutils work in VM (#16590)

* make cstrutils work in VM

* more
This commit is contained in:
flywind
2021-01-05 10:52:26 -06:00
committed by GitHub
parent 0c4bd65e8d
commit c04f305bf7
4 changed files with 132 additions and 104 deletions

View File

@@ -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 =
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`.
##
## 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
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)
func cmpIgnoreCase*(a, b: cstring): int =
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)
# 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 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")
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
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)
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)

View File

@@ -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".} =

View File

@@ -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

View File

@@ -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()