diff --git a/changelog.md b/changelog.md index 17844e09a165..dd92c32ee406 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 da1a0a59baf0..d04c972d4c4f 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`_ 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 000000000000..ce061adca59a --- /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 000000000000..4dd232351ecb --- /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()