Skip to content

Commit

Permalink
add strbasics.strip (nim-lang#16280)
Browse files Browse the repository at this point in the history
  • Loading branch information
ringabout committed Mar 22, 2021
1 parent 38b4cd7 commit 867b739
Show file tree
Hide file tree
Showing 4 changed files with 226 additions and 0 deletions.
2 changes: 2 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions lib/pure/strutils.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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 "
Expand Down
97 changes: 97 additions & 0 deletions lib/std/strbasics.nim
Original file line number Diff line number Diff line change
@@ -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))
126 changes: 126 additions & 0 deletions tests/stdlib/tstrbasics.nim
Original file line number Diff line number Diff line change
@@ -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()

0 comments on commit 867b739

Please sign in to comment.