summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--changelog.md2
-rw-r--r--lib/pure/strutils.nim1
-rw-r--r--lib/std/strbasics.nim97
-rw-r--r--tests/stdlib/tstrbasics.nim126
4 files changed, 226 insertions, 0 deletions
diff --git a/changelog.md b/changelog.md
index 17844e09a..dd92c32ee 100644
--- a/changelog.md
+++ b/changelog.md
@@ -171,6 +171,8 @@ provided by the operating system.
   dumping (on select signals) and notifying the parent process about the cause
   of termination.
 
+- Added `strip` and `setSlice` to `std/strbasics`.
+
 
 ## Language changes
 
diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim
index da1a0a59b..d04c972d4 100644
--- a/lib/pure/strutils.nim
+++ b/lib/pure/strutils.nim
@@ -2753,6 +2753,7 @@ func strip*(s: string, leading = true, trailing = true,
   ## If both are false, the string is returned unchanged.
   ##
   ## See also:
+  ## * `strip proc<strbasics.html#strip,string,set[char]>`_ Inplace version.
   ## * `stripLineEnd func<#stripLineEnd,string>`_
   runnableExamples:
     let a = "  vhellov   "
diff --git a/lib/std/strbasics.nim b/lib/std/strbasics.nim
new file mode 100644
index 000000000..ce061adca
--- /dev/null
+++ b/lib/std/strbasics.nim
@@ -0,0 +1,97 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2021 Nim Contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module provides some high performance string operations.
+
+const whitespaces = {' ', '\t', '\v', '\r', '\l', '\f'}
+
+func stripSlice(s: openArray[char], leading = true, trailing = true, chars: set[char] = whitespaces): Slice[int] =
+  ## Returns the slice range of `s` which is stripped `chars`.
+  runnableExamples:
+    assert stripSlice(" abc  ") == 1 .. 3
+  var
+    first = 0
+    last = high(s)
+  if leading:
+    while first <= last and s[first] in chars: inc(first)
+  if trailing:
+    while last >= first and s[last] in chars: dec(last)
+  result = first .. last
+
+func setSlice*(s: var string, slice: Slice[int]) =
+  ## Inplace version of `substr`.
+  runnableExamples:
+    import std/sugar
+
+    var a = "Hello, Nim!"
+    doassert a.dup(setSlice(7 .. 9)) == "Nim"
+    doAssert a.dup(setSlice(0 .. 0)) == "H"
+    doAssert a.dup(setSlice(0 .. 1)) == "He"
+    doAssert a.dup(setSlice(0 .. 10)) == a
+    doAssert a.dup(setSlice(1 .. 0)).len == 0
+    doAssert a.dup(setSlice(20 .. -1)).len == 0
+
+
+    doAssertRaises(AssertionDefect):
+      discard a.dup(setSlice(-1 .. 1))
+
+    doAssertRaises(AssertionDefect):
+      discard a.dup(setSlice(1 .. 11))
+
+
+  let first = slice.a
+  let last = slice.b
+
+  assert first >= 0
+  assert last <= s.high
+
+  if first > last:
+    s.setLen(0)
+    return
+  template impl =
+    for index in first .. last:
+      s[index - first] = s[index]
+  if first > 0:
+    when nimvm: impl()
+    else:
+      # not JS and not Nimscript
+      when not declared(moveMem):
+        impl()
+      else:
+        moveMem(addr s[0], addr s[first], last - first + 1)
+  s.setLen(last - first + 1)
+
+func strip*(a: var string, leading = true, trailing = true, chars: set[char] = whitespaces) {.inline.} =
+  ## Inplace version of `strip`. Strips leading or 
+  ## trailing `chars` (default: whitespace characters).
+  ##
+  ## If `leading` is true (default), leading `chars` are stripped.
+  ## If `trailing` is true (default), trailing `chars` are stripped.
+  ## If both are false, the string is unchanged.
+  runnableExamples:
+    var a = "  vhellov   "
+    strip(a)
+    assert a == "vhellov"
+
+    a = "  vhellov   "
+    a.strip(leading = false)
+    assert a == "  vhellov"
+
+    a = "  vhellov   "
+    a.strip(trailing = false)
+    assert a == "vhellov   "
+
+    var c = "blaXbla"
+    c.strip(chars = {'b', 'a'})
+    assert c == "laXbl"
+    c = "blaXbla"
+    c.strip(chars = {'b', 'a', 'l'})
+    assert c == "X"
+
+  setSlice(a, stripSlice(a, leading, trailing, chars))
diff --git a/tests/stdlib/tstrbasics.nim b/tests/stdlib/tstrbasics.nim
new file mode 100644
index 000000000..4dd232351
--- /dev/null
+++ b/tests/stdlib/tstrbasics.nim
@@ -0,0 +1,126 @@
+discard """
+  targets: "c cpp js"
+"""
+
+import std/[strbasics, sugar]
+
+
+proc teststrip() =
+  var a = "  vhellov   "
+  strip(a)
+  doAssert a == "vhellov"
+
+  a = "  vhellov   "
+  a.strip(leading = false)
+  doAssert a == "  vhellov"
+
+  a = "  vhellov   "
+  a.strip(trailing = false)
+  doAssert a == "vhellov   "
+
+  a.strip()
+  a.strip(chars = {'v'})
+  doAssert a == "hello"
+
+  a = "  vhellov   "
+  a.strip()
+  a.strip(leading = false, chars = {'v'})
+  doAssert a == "vhello"
+
+  var c = "blaXbla"
+  c.strip(chars = {'b', 'a'})
+  doAssert c == "laXbl"
+  c = "blaXbla"
+  c.strip(chars = {'b', 'a', 'l'})
+  doAssert c == "X"
+
+  block:
+    var a = "xxxxxx"
+    a.strip(chars={'x'})
+    doAssert a.len == 0
+
+  block:
+    var a = "x"
+    a.strip(chars={'x'})
+    doAssert a.len == 0
+  
+  block:
+    var a = "x"
+    a.strip(chars={'1'})
+    doAssert a.len == 1
+
+  block:
+    var a = ""
+    a.strip(chars={'x'})
+    doAssert a.len == 0
+
+  block:
+    var a = "xxx xxx"
+    a.strip(chars={'x'})
+    doAssert a == " "
+
+  block:
+    var a = "xxx  wind"
+    a.strip(chars={'x'})
+    doAssert a == "  wind"
+
+  block:
+    var a = "xxx  iii"
+    a.strip(chars={'i'})
+    doAssert a == "xxx  "
+
+  block:
+    var a = "xxx  iii"
+    doAssert a.dup(strip(chars = {'i'})) == "xxx  "
+    doAssert a.dup(strip(chars = {' '})) == "xxx  iii"
+    doAssert a.dup(strip(chars = {'x'})) == "  iii"
+    doAssert a.dup(strip(chars = {'x', ' '})) == "iii"
+    doAssert a.dup(strip(chars = {'x', 'i'})) == "  "
+    doAssert a.dup(strip(chars = {'x', 'i', ' '})).len == 0
+
+  block:
+    var a = "x  i"
+    doAssert a.dup(strip(chars = {'i'})) == "x  "
+    doAssert a.dup(strip(chars = {' '})) == "x  i"
+    doAssert a.dup(strip(chars = {'x'})) == "  i"
+    doAssert a.dup(strip(chars = {'x', ' '})) == "i"
+    doAssert a.dup(strip(chars = {'x', 'i'})) == "  "
+    doAssert a.dup(strip(chars = {'x', 'i', ' '})).len == 0
+
+  block:
+    var a = ""
+    doAssert a.dup(strip(chars = {'i'})).len == 0
+    doAssert a.dup(strip(chars = {' '})).len == 0
+    doAssert a.dup(strip(chars = {'x'})).len == 0
+    doAssert a.dup(strip(chars = {'x', ' '})).len == 0
+    doAssert a.dup(strip(chars = {'x', 'i'})).len == 0
+    doAssert a.dup(strip(chars = {'x', 'i', ' '})).len == 0
+
+  block:
+    var a = " "
+    doAssert a.dup(strip(chars = {'i'})) == " "
+    doAssert a.dup(strip(chars = {' '})).len == 0
+    doAssert a.dup(strip(chars = {'x'})) == " "
+    doAssert a.dup(strip(chars = {'x', ' '})).len == 0
+    doAssert a.dup(strip(chars = {'x', 'i'})) == " "
+    doAssert a.dup(strip(chars = {'x', 'i', ' '})).len == 0
+
+
+  block:
+    var a = "Hello, Nim!"
+    doassert a.dup(setSlice(7 .. 9)) == "Nim"
+    doAssert a.dup(setSlice(0 .. 0)) == "H"
+    doAssert a.dup(setSlice(0 .. 1)) == "He"
+    doAssert a.dup(setSlice(0 .. 10)) == a
+    doAssert a.dup(setSlice(1 .. 0)).len == 0
+    doAssert a.dup(setSlice(20 .. -1)).len == 0
+
+
+    doAssertRaises(AssertionDefect):
+      discard a.dup(setSlice(-1 .. 1))
+
+    doAssertRaises(AssertionDefect):
+      discard a.dup(setSlice(1 .. 11))
+
+static: teststrip()
+teststrip()