diff options
Diffstat (limited to 'lib/pure')
40 files changed, 903 insertions, 466 deletions
diff --git a/lib/pure/algorithm.nim b/lib/pure/algorithm.nim index 9eee04404..fdf2d7cbb 100644 --- a/lib/pure/algorithm.nim +++ b/lib/pure/algorithm.nim @@ -371,3 +371,126 @@ when isMainModule: for i in 0 .. high(arr1): assert arr1.reversed(0, i) == arr1.reversed()[high(arr1) - i .. high(arr1)] assert arr1.reversed(i, high(arr1)) == arr1.reversed()[0 .. high(arr1) - i] + + +proc rotateInternal[T](arg: var openarray[T]; first, middle, last: int): int = + ## A port of std::rotate from c++. Ported from `this reference <http://www.cplusplus.com/reference/algorithm/rotate/>`_. + result = first + last - middle + + if first == middle or middle == last: + return + + assert first < middle + assert middle < last + + # m prefix for mutable + var + mFirst = first + mMiddle = middle + next = middle + + swap(arg[mFirst], arg[next]) + mFirst += 1 + next += 1 + if mFirst == mMiddle: + mMiddle = next + + while next != last: + swap(arg[mFirst], arg[next]) + mFirst += 1 + next += 1 + if mFirst == mMiddle: + mMiddle = next + + next = mMiddle + while next != last: + swap(arg[mFirst], arg[next]) + mFirst += 1 + next += 1 + if mFirst == mMiddle: + mMiddle = next + elif next == last: + next = mMiddle + +proc rotatedInternal[T](arg: openarray[T]; first, middle, last: int): seq[T] = + result = newSeq[T](arg.len) + for i in 0 ..< first: + result[i] = arg[i] + let N = last - middle + let M = middle - first + for i in 0 ..< N: + result[first+i] = arg[middle+i] + for i in 0 ..< M: + result[first+N+i] = arg[first+i] + for i in last ..< arg.len: + result[i] = arg[i] + +proc rotateLeft*[T](arg: var openarray[T]; slice: HSlice[int, int]; dist: int): int = + ## Performs a left rotation on a range of elements. If you want to rotate right, use a negative ``dist``. + ## Specifically, ``rotateLeft`` rotates the elements at ``slice`` by ``dist`` positions. + ## The element at index ``slice.a + dist`` will be at index ``slice.a``. + ## The element at index ``slice.b`` will be at ``slice.a + dist -1``. + ## The element at index ``slice.a`` will be at ``slice.b + 1 - dist``. + ## The element at index ``slice.a + dist - 1`` will be at ``slice.b``. + # + ## Elements outsize of ``slice`` will be left unchanged. + ## The time complexity is linear to ``slice.b - slice.a + 1``. + ## + ## ``slice`` + ## the indices of the element range that should be rotated. + ## + ## ``dist`` + ## the distance in amount of elements that the data should be rotated. Can be negative, can be any number. + ## + ## .. code-block:: nim + ## var list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + ## list.rotateLeft(1 .. 8, 3) + ## doAssert list == [0, 4, 5, 6, 7, 8, 1, 2, 3, 9, 10] + let sliceLen = slice.b + 1 - slice.a + let distLeft = ((dist mod sliceLen) + sliceLen) mod sliceLen + arg.rotateInternal(slice.a, slice.a+distLeft, slice.b + 1) + +proc rotateLeft*[T](arg: var openarray[T]; dist: int): int = + ## default arguments for slice, so that this procedure operates on the entire + ## ``arg``, and not just on a part of it. + let arglen = arg.len + let distLeft = ((dist mod arglen) + arglen) mod arglen + arg.rotateInternal(0, distLeft, arglen) + +proc rotatedLeft*[T](arg: openarray[T]; slice: HSlice[int, int], dist: int): seq[T] = + ## same as ``rotateLeft``, just with the difference that it does + ## not modify the argument. It creates a new ``seq`` instead + let sliceLen = slice.b + 1 - slice.a + let distLeft = ((dist mod sliceLen) + sliceLen) mod sliceLen + arg.rotatedInternal(slice.a, slice.a+distLeft, slice.b+1) + +proc rotatedLeft*[T](arg: openarray[T]; dist: int): seq[T] = + ## same as ``rotateLeft``, just with the difference that it does + ## not modify the argument. It creates a new ``seq`` instead + let arglen = arg.len + let distLeft = ((dist mod arglen) + arglen) mod arglen + arg.rotatedInternal(0, distLeft, arg.len) + +when isMainModule: + var list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + let list2 = list.rotatedLeft(1 ..< 9, 3) + let expected = [0, 4, 5, 6, 7, 8, 1, 2, 3, 9, 10] + + doAssert list.rotateLeft(1 ..< 9, 3) == 6 + doAssert list == expected + doAssert list2 == @expected + + var s0,s1,s2,s3,s4,s5 = "xxxabcdefgxxx" + + doAssert s0.rotateLeft(3 ..< 10, 3) == 7 + doAssert s0 == "xxxdefgabcxxx" + doAssert s1.rotateLeft(3 ..< 10, 2) == 8 + doAssert s1 == "xxxcdefgabxxx" + doAssert s2.rotateLeft(3 ..< 10, 4) == 6 + doAssert s2 == "xxxefgabcdxxx" + doAssert s3.rotateLeft(3 ..< 10, -3) == 6 + doAssert s3 == "xxxefgabcdxxx" + doAssert s4.rotateLeft(3 ..< 10, -10) == 6 + doAssert s4 == "xxxefgabcdxxx" + doAssert s5.rotateLeft(3 ..< 10, 11) == 6 + doAssert s5 == "xxxefgabcdxxx" diff --git a/lib/pure/asyncmacro.nim b/lib/pure/asyncmacro.nim index 6e7d7993f..981190211 100644 --- a/lib/pure/asyncmacro.nim +++ b/lib/pure/asyncmacro.nim @@ -61,14 +61,14 @@ proc generateExceptionCheck(futSym, else: var exceptionChecks: seq[tuple[cond, body: NimNode]] = @[] let errorNode = newDotExpr(futSym, newIdentNode("error")) - for i in 1 .. <tryStmt.len: + for i in 1 ..< tryStmt.len: let exceptBranch = tryStmt[i] if exceptBranch[0].kind == nnkStmtList: exceptionChecks.add((newIdentNode("true"), exceptBranch[0])) else: var exceptIdentCount = 0 var ifCond: NimNode - for i in 0 .. <exceptBranch.len: + for i in 0 ..< exceptBranch.len: let child = exceptBranch[i] if child.kind == nnkIdent: let cond = infix(errorNode, "of", child) @@ -270,7 +270,7 @@ proc processBody(node, retFutureSym: NimNode, return else: discard - for i in 0 .. <result.len: + for i in 0 ..< result.len: result[i] = processBody(result[i], retFutureSym, subTypeIsVoid, futureVarIdents, nil) @@ -287,7 +287,7 @@ proc getName(node: NimNode): string {.compileTime.} = proc getFutureVarIdents(params: NimNode): seq[NimNode] {.compileTime.} = result = @[] - for i in 1 .. <len(params): + for i in 1 ..< len(params): expectKind(params[i], nnkIdentDefs) if params[i][1].kind == nnkBracketExpr and ($params[i][1][0].ident).normalize == "futurevar": @@ -466,7 +466,7 @@ proc stripAwait(node: NimNode): NimNode = node[0][0] = emptyNoopSym else: discard - for i in 0 .. <result.len: + for i in 0 ..< result.len: result[i] = stripAwait(result[i]) proc splitParamType(paramType: NimNode, async: bool): NimNode = @@ -512,7 +512,7 @@ proc splitProc(prc: NimNode): (NimNode, NimNode) = # Retrieve the `T` inside `Future[T]`. let returnType = stripReturnType(result[0][3][0]) result[0][3][0] = splitParamType(returnType, async=false) - for i in 1 .. <result[0][3].len: + for i in 1 ..< result[0][3].len: # Sync proc (0) -> FormalParams (3) -> IdentDefs, the parameter (i) -> # parameter type (1). result[0][3][i][1] = splitParamType(result[0][3][i][1], async=false) @@ -521,7 +521,7 @@ proc splitProc(prc: NimNode): (NimNode, NimNode) = result[1] = prc.copyNimTree() if result[1][3][0].kind == nnkBracketExpr: result[1][3][0][1] = splitParamType(result[1][3][0][1], async=true) - for i in 1 .. <result[1][3].len: + for i in 1 ..< result[1][3].len: # Async proc (1) -> FormalParams (3) -> IdentDefs, the parameter (i) -> # parameter type (1). result[1][3][i][1] = splitParamType(result[1][3][i][1], async=true) diff --git a/lib/pure/collections/critbits.nim b/lib/pure/collections/critbits.nim index f70a12843..19f1f2e58 100644 --- a/lib/pure/collections/critbits.nim +++ b/lib/pure/collections/critbits.nim @@ -257,7 +257,7 @@ proc allprefixedAux[T](c: CritBitTree[T], key: string; longestMatch: bool): Node p = p.child[dir] if q.byte < key.len: top = p if not longestMatch: - for i in 0 .. <key.len: + for i in 0 ..< key.len: if p.key[i] != key[i]: return result = top diff --git a/lib/pure/collections/deques.nim b/lib/pure/collections/deques.nim index 1bbe9f1ad..1e0cb82d2 100644 --- a/lib/pure/collections/deques.nim +++ b/lib/pure/collections/deques.nim @@ -207,9 +207,9 @@ when isMainModule: assert($deq == "[4, 56, 6, 789]") assert deq[0] == deq.peekFirst and deq.peekFirst == 4 - assert deq[^1] == deq.peekLast and deq.peekLast == 789 + #assert deq[^1] == deq.peekLast and deq.peekLast == 789 deq[0] = 42 - deq[^1] = 7 + deq[deq.len - 1] = 7 assert 6 in deq and 789 notin deq assert deq.find(6) >= 0 diff --git a/lib/pure/collections/lists.nim b/lib/pure/collections/lists.nim index f847ddd58..560273dfa 100644 --- a/lib/pure/collections/lists.nim +++ b/lib/pure/collections/lists.nim @@ -37,6 +37,14 @@ type DoublyLinkedRing*[T] = object ## a doubly linked ring head*: DoublyLinkedNode[T] + SomeLinkedList*[T] = SinglyLinkedList[T] | DoublyLinkedList[T] + + SomeLinkedRing*[T] = SinglyLinkedRing[T] | DoublyLinkedRing[T] + + SomeLinkedCollection*[T] = SomeLinkedList[T] | SomeLinkedRing[T] + + SomeLinkedNode*[T] = SinglyLinkedNode[T] | DoublyLinkedNode[T] + {.deprecated: [TDoublyLinkedNode: DoublyLinkedNodeObj, PDoublyLinkedNode: DoublyLinkedNode, TSinglyLinkedNode: SinglyLinkedNodeObj, @@ -86,137 +94,57 @@ template itemsRingImpl() {.dirty.} = it = it.next if it == L.head: break -template nodesListImpl() {.dirty.} = - var it = L.head - while it != nil: - var nxt = it.next - yield it - it = nxt - -template nodesRingImpl() {.dirty.} = - var it = L.head - if it != nil: - while true: - var nxt = it.next - yield it - it = nxt - if it == L.head: break - -template findImpl() {.dirty.} = - for x in nodes(L): - if x.value == value: return x - -iterator items*[T](L: DoublyLinkedList[T]): T = +iterator items*[T](L: SomeLinkedList[T]): T = ## yields every value of `L`. itemsListImpl() -iterator items*[T](L: SinglyLinkedList[T]): T = - ## yields every value of `L`. - itemsListImpl() - -iterator items*[T](L: SinglyLinkedRing[T]): T = - ## yields every value of `L`. - itemsRingImpl() - -iterator items*[T](L: DoublyLinkedRing[T]): T = +iterator items*[T](L: SomeLinkedRing[T]): T = ## yields every value of `L`. itemsRingImpl() -iterator mitems*[T](L: var DoublyLinkedList[T]): var T = +iterator mitems*[T](L: var SomeLinkedList[T]): var T = ## yields every value of `L` so that you can modify it. itemsListImpl() -iterator mitems*[T](L: var SinglyLinkedList[T]): var T = - ## yields every value of `L` so that you can modify it. - itemsListImpl() - -iterator mitems*[T](L: var SinglyLinkedRing[T]): var T = +iterator mitems*[T](L: var SomeLinkedRing[T]): var T = ## yields every value of `L` so that you can modify it. itemsRingImpl() -iterator mitems*[T](L: var DoublyLinkedRing[T]): var T = - ## yields every value of `L` so that you can modify it. - itemsRingImpl() - -iterator nodes*[T](L: SinglyLinkedList[T]): SinglyLinkedNode[T] = - ## iterates over every node of `x`. Removing the current node from the - ## list during traversal is supported. - nodesListImpl() - -iterator nodes*[T](L: DoublyLinkedList[T]): DoublyLinkedNode[T] = - ## iterates over every node of `x`. Removing the current node from the - ## list during traversal is supported. - nodesListImpl() - -iterator nodes*[T](L: SinglyLinkedRing[T]): SinglyLinkedNode[T] = +iterator nodes*[T](L: SomeLinkedList[T]): SomeLinkedNode[T] = ## iterates over every node of `x`. Removing the current node from the ## list during traversal is supported. - nodesRingImpl() + var it = L.head + while it != nil: + var nxt = it.next + yield it + it = nxt -iterator nodes*[T](L: DoublyLinkedRing[T]): DoublyLinkedNode[T] = +iterator nodes*[T](L: SomeLinkedRing[T]): SomeLinkedNode[T] = ## iterates over every node of `x`. Removing the current node from the ## list during traversal is supported. - nodesRingImpl() + var it = L.head + if it != nil: + while true: + var nxt = it.next + yield it + it = nxt + if it == L.head: break -template dollarImpl() {.dirty.} = +proc `$`*[T](L: SomeLinkedCollection[T]): string = + ## turns a list into its string representation. result = "[" for x in nodes(L): if result.len > 1: result.add(", ") result.add($x.value) result.add("]") -proc `$`*[T](L: SinglyLinkedList[T]): string = - ## turns a list into its string representation. - dollarImpl() - -proc `$`*[T](L: DoublyLinkedList[T]): string = - ## turns a list into its string representation. - dollarImpl() - -proc `$`*[T](L: SinglyLinkedRing[T]): string = - ## turns a list into its string representation. - dollarImpl() - -proc `$`*[T](L: DoublyLinkedRing[T]): string = - ## turns a list into its string representation. - dollarImpl() - -proc find*[T](L: SinglyLinkedList[T], value: T): SinglyLinkedNode[T] = - ## searches in the list for a value. Returns nil if the value does not - ## exist. - findImpl() - -proc find*[T](L: DoublyLinkedList[T], value: T): DoublyLinkedNode[T] = +proc find*[T](L: SomeLinkedCollection[T], value: T): SomeLinkedNode[T] = ## searches in the list for a value. Returns nil if the value does not ## exist. - findImpl() - -proc find*[T](L: SinglyLinkedRing[T], value: T): SinglyLinkedNode[T] = - ## searches in the list for a value. Returns nil if the value does not - ## exist. - findImpl() - -proc find*[T](L: DoublyLinkedRing[T], value: T): DoublyLinkedNode[T] = - ## searches in the list for a value. Returns nil if the value does not - ## exist. - findImpl() - -proc contains*[T](L: SinglyLinkedList[T], value: T): bool {.inline.} = - ## searches in the list for a value. Returns false if the value does not - ## exist, true otherwise. - result = find(L, value) != nil - -proc contains*[T](L: DoublyLinkedList[T], value: T): bool {.inline.} = - ## searches in the list for a value. Returns false if the value does not - ## exist, true otherwise. - result = find(L, value) != nil - -proc contains*[T](L: SinglyLinkedRing[T], value: T): bool {.inline.} = - ## searches in the list for a value. Returns false if the value does not - ## exist, true otherwise. - result = find(L, value) != nil + for x in nodes(L): + if x.value == value: return x -proc contains*[T](L: DoublyLinkedRing[T], value: T): bool {.inline.} = +proc contains*[T](L: SomeLinkedCollection[T], value: T): bool {.inline.} = ## searches in the list for a value. Returns false if the value does not ## exist, true otherwise. result = find(L, value) != nil @@ -266,7 +194,6 @@ proc remove*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) = if n.next != nil: n.next.prev = n.prev if n.prev != nil: n.prev.next = n.next - proc append*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) = ## appends a node `n` to `L`. Efficiency: O(1). if L.head != nil: diff --git a/lib/pure/collections/queues.nim b/lib/pure/collections/queues.nim index 401422162..ce792d6da 100644 --- a/lib/pure/collections/queues.nim +++ b/lib/pure/collections/queues.nim @@ -198,9 +198,8 @@ when isMainModule: assert($q == "[4, 56, 6, 789]") assert q[0] == q.front and q.front == 4 - assert q[^1] == q.back and q.back == 789 q[0] = 42 - q[^1] = 7 + q[q.len - 1] = 7 assert 6 in q and 789 notin q assert q.find(6) >= 0 diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index e8e725aa3..0eb8e6704 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -13,8 +13,9 @@ ## were inspired by functional programming languages. ## ## For functional style programming you may want to pass `anonymous procs -## <manual.html#anonymous-procs>`_ to procs like ``filter`` to reduce typing. -## Anonymous procs can use `the special do notation <manual.html#do-notation>`_ +## <manual.html#procedures-anonymous-procs>`_ to procs like ``filter`` to +## reduce typing. Anonymous procs can use `the special do notation +## <manual.html#procedures-do-notation>`_ ## which is more convenient in certain situations. include "system/inclrtl" @@ -43,8 +44,23 @@ proc concat*[T](seqs: varargs[seq[T]]): seq[T] = result[i] = itm inc(i) -proc cycle*[T](s: seq[T], n: Natural): seq[T] = - ## Returns a new sequence with the items of `s` repeated `n` times. +proc count*[T](s: openArray[T], x: T): int = + ## Returns the number of occurrences of the item `x` in the container `s`. + ## + ## Example: + ## + ## .. code-block:: + ## let + ## s = @[1, 2, 2, 3, 2, 4, 2] + ## c = count(s, 2) + ## assert c == 4 + for itm in items(s): + if itm == x: + inc result + +proc cycle*[T](s: openArray[T], n: Natural): seq[T] = + ## Returns a new sequence with the items of the container `s` repeated + ## `n` times. ## ## Example: ## @@ -56,7 +72,7 @@ proc cycle*[T](s: seq[T], n: Natural): seq[T] = ## assert total == @[1, 2, 3, 1, 2, 3, 1, 2, 3] result = newSeq[T](n * s.len) var o = 0 - for x in 0..<n: + for x in 0 ..< n: for e in s: result[o] = e inc o @@ -72,12 +88,14 @@ proc repeat*[T](x: T, n: Natural): seq[T] = ## total = repeat(5, 3) ## assert total == @[5, 5, 5] result = newSeq[T](n) - for i in 0..<n: + for i in 0 ..< n: result[i] = x -proc deduplicate*[T](seq1: seq[T]): seq[T] = +proc deduplicate*[T](s: openArray[T]): seq[T] = ## Returns a new sequence without duplicates. ## + ## Example: + ## ## .. code-block:: ## let ## dup1 = @[1, 1, 3, 4, 2, 2, 8, 1, 4] @@ -87,17 +105,19 @@ proc deduplicate*[T](seq1: seq[T]): seq[T] = ## assert unique1 == @[1, 3, 4, 2, 8] ## assert unique2 == @["a", "c", "d"] result = @[] - for itm in items(seq1): + for itm in items(s): if not result.contains(itm): result.add(itm) {.deprecated: [distnct: deduplicate].} -proc zip*[S, T](seq1: seq[S], seq2: seq[T]): seq[tuple[a: S, b: T]] = - ## Returns a new sequence with a combination of the two input sequences. +proc zip*[S, T](s1: openArray[S], s2: openArray[T]): seq[tuple[a: S, b: T]] = + ## Returns a new sequence with a combination of the two input containers. ## ## For convenience you can access the returned tuples through the named - ## fields `a` and `b`. If one sequence is shorter, the remaining items in the - ## longer sequence are discarded. Example: + ## fields `a` and `b`. If one container is shorter, the remaining items in + ## the longer container are discarded. + ## + ## Example: ## ## .. code-block:: ## let @@ -110,15 +130,16 @@ proc zip*[S, T](seq1: seq[S], seq2: seq[T]): seq[tuple[a: S, b: T]] = ## assert zip2 == @[(1, "one"), (2, "two"), (3, "three")] ## assert zip1[2].b == 4 ## assert zip2[2].b == "three" - var m = min(seq1.len, seq2.len) + var m = min(s1.len, s2.len) newSeq(result, m) - for i in 0 .. m-1: result[i] = (seq1[i], seq2[i]) + for i in 0 ..< m: + result[i] = (s1[i], s2[i]) proc distribute*[T](s: seq[T], num: Positive, spread = true): seq[seq[T]] = ## Splits and distributes a sequence `s` into `num` sub sequences. ## ## Returns a sequence of `num` sequences. For some input values this is the - ## inverse of the `concat <#concat>`_ proc. The proc will assert in debug + ## inverse of the `concat <#concat>`_ proc. The proc will assert in debug ## builds if `s` is nil or `num` is less than one, and will likely crash on ## release builds. The input sequence `s` can be empty, which will produce ## `num` empty sequences. @@ -159,48 +180,52 @@ proc distribute*[T](s: seq[T], num: Positive, spread = true): seq[seq[T]] = # Use an algorithm which overcounts the stride and minimizes reading limits. if extra > 0: inc(stride) - for i in 0 .. <num: + for i in 0 ..< num: result[i] = newSeq[T]() - for g in first .. <min(s.len, first + stride): + for g in first ..< min(s.len, first + stride): result[i].add(s[g]) first += stride else: # Use an undercounting algorithm which *adds* the remainder each iteration. - for i in 0 .. <num: + for i in 0 ..< num: last = first + stride if extra > 0: extra -= 1 inc(last) result[i] = newSeq[T]() - for g in first .. <last: + for g in first ..< last: result[i].add(s[g]) first = last - -proc map*[T, S](data: openArray[T], op: proc (x: T): S {.closure.}): +proc map*[T, S](s: openArray[T], op: proc (x: T): S {.closure.}): seq[S]{.inline.} = ## Returns a new sequence with the results of `op` applied to every item in - ## `data`. + ## the container `s`. ## ## Since the input is not modified you can use this version of ``map`` to - ## transform the type of the elements in the input sequence. Example: + ## transform the type of the elements in the input container. + ## + ## Example: ## ## .. code-block:: nim ## let ## a = @[1, 2, 3, 4] ## b = map(a, proc(x: int): string = $x) ## assert b == @["1", "2", "3", "4"] - newSeq(result, data.len) - for i in 0..data.len-1: result[i] = op(data[i]) + newSeq(result, s.len) + for i in 0 ..< s.len: + result[i] = op(s[i]) -proc map*[T](data: var openArray[T], op: proc (x: var T) {.closure.}) +proc map*[T](s: var openArray[T], op: proc (x: var T) {.closure.}) {.deprecated.} = - ## Applies `op` to every item in `data` modifying it directly. + ## Applies `op` to every item in `s` modifying it directly. ## ## Note that this version of ``map`` requires your input and output types to - ## be the same, since they are modified in-place. Example: + ## be the same, since they are modified in-place. + ## + ## Example: ## ## .. code-block:: nim ## var a = @["1", "2", "3", "4"] @@ -210,15 +235,16 @@ proc map*[T](data: var openArray[T], op: proc (x: var T) {.closure.}) ## echo repr(a) ## # --> ["142", "242", "342", "442"] ## **Deprecated since version 0.12.0:** Use the ``apply`` proc instead. - for i in 0..data.len-1: op(data[i]) + for i in 0 ..< s.len: op(s[i]) -proc apply*[T](data: var seq[T], op: proc (x: var T) {.closure.}) +proc apply*[T](s: var openArray[T], op: proc (x: var T) {.closure.}) {.inline.} = - ## Applies `op` to every item in `data` modifying it directly. + ## Applies `op` to every item in `s` modifying it directly. ## ## Note that this requires your input and output types to ## be the same, since they are modified in-place. ## The parameter function takes a ``var T`` type parameter. + ## ## Example: ## ## .. code-block:: nim @@ -229,15 +255,16 @@ proc apply*[T](data: var seq[T], op: proc (x: var T) {.closure.}) ## echo repr(a) ## # --> ["142", "242", "342", "442"] ## - for i in 0..data.len-1: op(data[i]) + for i in 0 ..< s.len: op(s[i]) -proc apply*[T](data: var seq[T], op: proc (x: T): T {.closure.}) +proc apply*[T](s: var openArray[T], op: proc (x: T): T {.closure.}) {.inline.} = - ## Applies `op` to every item in `data` modifying it directly. + ## Applies `op` to every item in `s` modifying it directly. ## ## Note that this requires your input and output types to ## be the same, since they are modified in-place. ## The parameter function takes and returns a ``T`` type variable. + ## ## Example: ## ## .. code-block:: nim @@ -248,11 +275,10 @@ proc apply*[T](data: var seq[T], op: proc (x: T): T {.closure.}) ## echo repr(a) ## # --> ["142", "242", "342", "442"] ## - for i in 0..data.len-1: data[i] = op(data[i]) + for i in 0 ..< s.len: s[i] = op(s[i]) - -iterator filter*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): T = - ## Iterates through a sequence and yields every item that fulfills the +iterator filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): T = + ## Iterates through a container and yields every item that fulfills the ## predicate. ## ## Example: @@ -262,11 +288,11 @@ iterator filter*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): T = ## for n in filter(numbers, proc (x: int): bool = x mod 2 == 0): ## echo($n) ## # echoes 4, 8, 4 in separate lines - for i in 0..<seq1.len: - if pred(seq1[i]): - yield seq1[i] + for i in 0 ..< s.len: + if pred(s[i]): + yield s[i] -proc filter*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): seq[T] +proc filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): seq[T] {.inline.} = ## Returns a new sequence with all the items that fulfilled the predicate. ## @@ -280,11 +306,11 @@ proc filter*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): seq[T] ## assert f1 == @["red", "black"] ## assert f2 == @["yellow"] result = newSeq[T]() - for i in 0..<seq1.len: - if pred(seq1[i]): - result.add(seq1[i]) + for i in 0 ..< s.len: + if pred(s[i]): + result.add(s[i]) -proc keepIf*[T](seq1: var seq[T], pred: proc(item: T): bool {.closure.}) +proc keepIf*[T](s: var seq[T], pred: proc(x: T): bool {.closure.}) {.inline.} = ## Keeps the items in the passed sequence if they fulfilled the predicate. ## Same as the ``filter`` proc, but modifies the sequence directly. @@ -296,12 +322,12 @@ proc keepIf*[T](seq1: var seq[T], pred: proc(item: T): bool {.closure.}) ## keepIf(floats, proc(x: float): bool = x > 10) ## assert floats == @[13.0, 12.5, 10.1] var pos = 0 - for i in 0 .. <len(seq1): - if pred(seq1[i]): + for i in 0 ..< len(s): + if pred(s[i]): if pos != i: - shallowCopy(seq1[pos], seq1[i]) + shallowCopy(s[pos], s[i]) inc(pos) - setLen(seq1, pos) + setLen(s, pos) proc delete*[T](s: var seq[T]; first, last: Natural) = ## Deletes in `s` the items at position `first` .. `last`. This modifies @@ -354,11 +380,12 @@ proc insert*[T](dest: var seq[T], src: openArray[T], pos=0) = inc(j) -template filterIt*(seq1, pred: untyped): untyped = +template filterIt*(s, pred: untyped): untyped = ## Returns a new sequence with all the items that fulfilled the predicate. ## ## Unlike the `proc` version, the predicate needs to be an expression using ## the ``it`` variable for testing, like: ``filterIt("abcxyz", it == 'x')``. + ## ## Example: ## ## .. code-block:: @@ -368,8 +395,8 @@ template filterIt*(seq1, pred: untyped): untyped = ## notAcceptable = filterIt(temperatures, it > 50 or it < -10) ## assert acceptable == @[-2.0, 24.5, 44.31] ## assert notAcceptable == @[-272.15, 99.9, -113.44] - var result = newSeq[type(seq1[0])]() - for it {.inject.} in items(seq1): + var result = newSeq[type(s[0])]() + for it {.inject.} in items(s): if pred: result.add(it) result @@ -378,6 +405,7 @@ template keepItIf*(varSeq: seq, pred: untyped) = ## ## Unlike the `proc` version, the predicate needs to be an expression using ## the ``it`` variable for testing, like: ``keepItIf("abcxyz", it == 'x')``. + ## ## Example: ## ## .. code-block:: @@ -385,7 +413,7 @@ template keepItIf*(varSeq: seq, pred: untyped) = ## keepItIf(candidates, it.len == 3 and it[0] == 'b') ## assert candidates == @["bar", "baz"] var pos = 0 - for i in 0 .. <len(varSeq): + for i in 0 ..< len(varSeq): let it {.inject.} = varSeq[i] if pred: if pos != i: @@ -393,8 +421,8 @@ template keepItIf*(varSeq: seq, pred: untyped) = inc(pos) setLen(varSeq, pos) -proc all*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): bool = - ## Iterates through a sequence and checks if every item fulfills the +proc all*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): bool = + ## Iterates through a container and checks if every item fulfills the ## predicate. ## ## Example: @@ -403,12 +431,12 @@ proc all*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): bool = ## let numbers = @[1, 4, 5, 8, 9, 7, 4] ## assert all(numbers, proc (x: int): bool = return x < 10) == true ## assert all(numbers, proc (x: int): bool = return x < 9) == false - for i in seq1: + for i in s: if not pred(i): return false return true -template allIt*(seq1, pred: untyped): bool = +template allIt*(s, pred: untyped): bool = ## Checks if every item fulfills the predicate. ## ## Example: @@ -418,14 +446,14 @@ template allIt*(seq1, pred: untyped): bool = ## assert allIt(numbers, it < 10) == true ## assert allIt(numbers, it < 9) == false var result = true - for it {.inject.} in items(seq1): + for it {.inject.} in items(s): if not pred: result = false break result -proc any*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): bool = - ## Iterates through a sequence and checks if some item fulfills the +proc any*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): bool = + ## Iterates through a container and checks if some item fulfills the ## predicate. ## ## Example: @@ -434,12 +462,12 @@ proc any*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): bool = ## let numbers = @[1, 4, 5, 8, 9, 7, 4] ## assert any(numbers, proc (x: int): bool = return x > 8) == true ## assert any(numbers, proc (x: int): bool = return x > 9) == false - for i in seq1: + for i in s: if pred(i): return true return false -template anyIt*(seq1, pred: untyped): bool = +template anyIt*(s, pred: untyped): bool = ## Checks if some item fulfills the predicate. ## ## Example: @@ -449,7 +477,7 @@ template anyIt*(seq1, pred: untyped): bool = ## assert anyIt(numbers, it > 8) == true ## assert anyIt(numbers, it > 9) == false var result = false - for it {.inject.} in items(seq1): + for it {.inject.} in items(s): if pred: result = true break @@ -493,7 +521,9 @@ template foldl*(sequence, operation: untyped): untyped = ## variables ``a`` and ``b`` for each step of the fold. Since this is a left ## fold, for non associative binary operations like subtraction think that ## the sequence of numbers 1, 2 and 3 will be parenthesized as (((1) - 2) - - ## 3). Example: + ## 3). + ## + ## Example: ## ## .. code-block:: ## let @@ -527,6 +557,7 @@ template foldl*(sequence, operation, first): untyped = ## The ``operation`` parameter should be an expression which uses the variables ## ``a`` and ``b`` for each step of the fold. The ``first`` parameter is the ## start value (the first ``a``) and therefor defines the type of the result. + ## ## Example: ## ## .. code-block:: @@ -555,7 +586,9 @@ template foldr*(sequence, operation: untyped): untyped = ## variables ``a`` and ``b`` for each step of the fold. Since this is a right ## fold, for non associative binary operations like subtraction think that ## the sequence of numbers 1, 2 and 3 will be parenthesized as (1 - (2 - - ## (3))). Example: + ## (3))). + ## + ## Example: ## ## .. code-block:: ## let @@ -580,13 +613,15 @@ template foldr*(sequence, operation: untyped): untyped = result = operation result -template mapIt*(seq1, typ, op: untyped): untyped = +template mapIt*(s, typ, op: untyped): untyped = ## Convenience template around the ``map`` proc to reduce typing. ## ## The template injects the ``it`` variable which you can use directly in an ## expression. You also need to pass as `typ` the type of the expression, ## since the new returned sequence can have a different type than the - ## original. Example: + ## original. + ## + ## Example: ## ## .. code-block:: ## let @@ -596,16 +631,18 @@ template mapIt*(seq1, typ, op: untyped): untyped = ## **Deprecated since version 0.12.0:** Use the ``mapIt(seq1, op)`` ## template instead. var result: seq[typ] = @[] - for it {.inject.} in items(seq1): + for it {.inject.} in items(s): result.add(op) result -template mapIt*(seq1, op: untyped): untyped = +template mapIt*(s, op: untyped): untyped = ## Convenience template around the ``map`` proc to reduce typing. ## ## The template injects the ``it`` variable which you can use directly in an - ## expression. Example: + ## expression. + ## + ## Example: ## ## .. code-block:: ## let @@ -614,19 +651,19 @@ template mapIt*(seq1, op: untyped): untyped = ## assert strings == @["4", "8", "12", "16"] type outType = type(( block: - var it{.inject.}: type(items(seq1)); + var it{.inject.}: type(items(s)); op)) var result: seq[outType] - when compiles(seq1.len): - let s = seq1 + when compiles(s.len): + let t = s var i = 0 result = newSeq[outType](s.len) - for it {.inject.} in s: + for it {.inject.} in t: result[i] = op i += 1 else: result = @[] - for it {.inject.} in seq1: + for it {.inject.} in s: result.add(op) result @@ -635,20 +672,23 @@ template applyIt*(varSeq, op: untyped) = ## ## The template injects the ``it`` variable which you can use directly in an ## expression. The expression has to return the same type as the sequence you - ## are mutating. Example: + ## are mutating. + ## + ## Example: ## ## .. code-block:: ## var nums = @[1, 2, 3, 4] ## nums.applyIt(it * 3) ## assert nums[0] + nums[3] == 15 - for i in 0 .. <varSeq.len: + for i in 0 ..< varSeq.len: let it {.inject.} = varSeq[i] varSeq[i] = op - template newSeqWith*(len: int, init: untyped): untyped = - ## creates a new sequence, calling `init` to initialize each value. Example: + ## creates a new sequence, calling `init` to initialize each value. + ## + ## Example: ## ## .. code-block:: ## var seq2D = newSeqWith(20, newSeq[bool](10)) @@ -660,7 +700,7 @@ template newSeqWith*(len: int, init: untyped): untyped = ## var seqRand = newSeqWith(20, random(10)) ## echo seqRand var result = newSeq[type(init)](len) - for i in 0 .. <len: + for i in 0 ..< len: result[i] = init result @@ -674,45 +714,178 @@ when isMainModule: total = concat(s1, s2, s3) assert total == @[1, 2, 3, 4, 5, 6, 7] - block: # duplicates test + block: # count test + let + s1 = @[1, 2, 3, 2] + s2 = @['a', 'b', 'x', 'a'] + a1 = [1, 2, 3, 2] + a2 = ['a', 'b', 'x', 'a'] + r0 = count(s1, 0) + r1 = count(s1, 1) + r2 = count(s1, 2) + r3 = count(s2, 'y') + r4 = count(s2, 'x') + r5 = count(s2, 'a') + ar0 = count(a1, 0) + ar1 = count(a1, 1) + ar2 = count(a1, 2) + ar3 = count(a2, 'y') + ar4 = count(a2, 'x') + ar5 = count(a2, 'a') + assert r0 == 0 + assert r1 == 1 + assert r2 == 2 + assert r3 == 0 + assert r4 == 1 + assert r5 == 2 + assert ar0 == 0 + assert ar1 == 1 + assert ar2 == 2 + assert ar3 == 0 + assert ar4 == 1 + assert ar5 == 2 + + block: # cycle tests + let + a = @[1, 2, 3] + b: seq[int] = @[] + c = [1, 2, 3] + + doAssert a.cycle(3) == @[1, 2, 3, 1, 2, 3, 1, 2, 3] + doAssert a.cycle(0) == @[] + #doAssert a.cycle(-1) == @[] # will not compile! + doAssert b.cycle(3) == @[] + doAssert c.cycle(3) == @[1, 2, 3, 1, 2, 3, 1, 2, 3] + doAssert c.cycle(0) == @[] + + block: # repeat tests + assert repeat(10, 5) == @[10, 10, 10, 10, 10] + assert repeat(@[1,2,3], 2) == @[@[1,2,3], @[1,2,3]] + assert repeat([1,2,3], 2) == @[[1,2,3], [1,2,3]] + + block: # deduplicates test let dup1 = @[1, 1, 3, 4, 2, 2, 8, 1, 4] dup2 = @["a", "a", "c", "d", "d"] + dup3 = [1, 1, 3, 4, 2, 2, 8, 1, 4] + dup4 = ["a", "a", "c", "d", "d"] unique1 = deduplicate(dup1) unique2 = deduplicate(dup2) + unique3 = deduplicate(dup3) + unique4 = deduplicate(dup4) assert unique1 == @[1, 3, 4, 2, 8] assert unique2 == @["a", "c", "d"] + assert unique3 == @[1, 3, 4, 2, 8] + assert unique4 == @["a", "c", "d"] block: # zip test let short = @[1, 2, 3] long = @[6, 5, 4, 3, 2, 1] words = @["one", "two", "three"] + ashort = [1, 2, 3] + along = [6, 5, 4, 3, 2, 1] + awords = ["one", "two", "three"] zip1 = zip(short, long) zip2 = zip(short, words) + zip3 = zip(ashort, along) + zip4 = zip(ashort, awords) + zip5 = zip(ashort, words) assert zip1 == @[(1, 6), (2, 5), (3, 4)] assert zip2 == @[(1, "one"), (2, "two"), (3, "three")] + assert zip3 == @[(1, 6), (2, 5), (3, 4)] + assert zip4 == @[(1, "one"), (2, "two"), (3, "three")] + assert zip5 == @[(1, "one"), (2, "two"), (3, "three")] assert zip1[2].b == 4 assert zip2[2].b == "three" + assert zip3[2].b == 4 + assert zip4[2].b == "three" + assert zip5[2].b == "three" + + block: # distribute tests + let numbers = @[1, 2, 3, 4, 5, 6, 7] + doAssert numbers.distribute(3) == @[@[1, 2, 3], @[4, 5], @[6, 7]] + doAssert numbers.distribute(6)[0] == @[1, 2] + doAssert numbers.distribute(6)[5] == @[7] + let a = @[1, 2, 3, 4, 5, 6, 7] + doAssert a.distribute(1, true) == @[@[1, 2, 3, 4, 5, 6, 7]] + doAssert a.distribute(1, false) == @[@[1, 2, 3, 4, 5, 6, 7]] + doAssert a.distribute(2, true) == @[@[1, 2, 3, 4], @[5, 6, 7]] + doAssert a.distribute(2, false) == @[@[1, 2, 3, 4], @[5, 6, 7]] + doAssert a.distribute(3, true) == @[@[1, 2, 3], @[4, 5], @[6, 7]] + doAssert a.distribute(3, false) == @[@[1, 2, 3], @[4, 5, 6], @[7]] + doAssert a.distribute(4, true) == @[@[1, 2], @[3, 4], @[5, 6], @[7]] + doAssert a.distribute(4, false) == @[@[1, 2], @[3, 4], @[5, 6], @[7]] + doAssert a.distribute(5, true) == @[@[1, 2], @[3, 4], @[5], @[6], @[7]] + doAssert a.distribute(5, false) == @[@[1, 2], @[3, 4], @[5, 6], @[7], @[]] + doAssert a.distribute(6, true) == @[@[1, 2], @[3], @[4], @[5], @[6], @[7]] + doAssert a.distribute(6, false) == @[ + @[1, 2], @[3, 4], @[5, 6], @[7], @[], @[]] + doAssert a.distribute(8, false) == a.distribute(8, true) + doAssert a.distribute(90, false) == a.distribute(90, true) + var b = @[0] + for f in 1 .. 25: b.add(f) + doAssert b.distribute(5, true)[4].len == 5 + doAssert b.distribute(5, false)[4].len == 2 + + block: # map test + let + numbers = @[1, 4, 5, 8, 9, 7, 4] + anumbers = [1, 4, 5, 8, 9, 7, 4] + m1 = map(numbers, proc(x: int): int = 2*x) + m2 = map(anumbers, proc(x: int): int = 2*x) + assert m1 == @[2, 8, 10, 16, 18, 14, 8] + assert m2 == @[2, 8, 10, 16, 18, 14, 8] + + block: # apply test + var a = @["1", "2", "3", "4"] + apply(a, proc(x: var string) = x &= "42") + assert a == @["142", "242", "342", "442"] block: # filter proc test let colors = @["red", "yellow", "black"] + acolors = ["red", "yellow", "black"] f1 = filter(colors, proc(x: string): bool = x.len < 6) f2 = filter(colors) do (x: string) -> bool : x.len > 5 + f3 = filter(acolors, proc(x: string): bool = x.len < 6) + f4 = filter(acolors) do (x: string) -> bool : x.len > 5 assert f1 == @["red", "black"] assert f2 == @["yellow"] + assert f3 == @["red", "black"] + assert f4 == @["yellow"] block: # filter iterator test let numbers = @[1, 4, 5, 8, 9, 7, 4] + let anumbers = [1, 4, 5, 8, 9, 7, 4] assert toSeq(filter(numbers, proc (x: int): bool = x mod 2 == 0)) == @[4, 8, 4] + assert toSeq(filter(anumbers, proc (x: int): bool = x mod 2 == 0)) == + @[4, 8, 4] block: # keepIf test var floats = @[13.0, 12.5, 5.8, 2.0, 6.1, 9.9, 10.1] keepIf(floats, proc(x: float): bool = x > 10) assert floats == @[13.0, 12.5, 10.1] + block: # delete tests + let outcome = @[1,1,1,1,1,1,1,1] + var dest = @[1,1,1,2,2,2,2,2,2,1,1,1,1,1] + dest.delete(3, 8) + assert outcome == dest, """\ + Deleting range 3-9 from [1,1,1,2,2,2,2,2,2,1,1,1,1,1] + is [1,1,1,1,1,1,1,1]""" + + block: # insert tests + var dest = @[1,1,1,1,1,1,1,1] + let + src = @[2,2,2,2,2,2] + outcome = @[1,1,1,2,2,2,2,2,2,1,1,1,1,1] + dest.insert(src, 3) + assert dest == outcome, """\ + Inserting [2,2,2,2,2,2] into [1,1,1,1,1,1,1,1] + at 3 is [1,1,1,2,2,2,2,2,2,1,1,1,1,1]""" + block: # filterIt test let temperatures = @[-272.15, -2.0, 24.5, 44.31, 99.9, -113.44] @@ -726,37 +899,49 @@ when isMainModule: keepItIf(candidates, it.len == 3 and it[0] == 'b') assert candidates == @["bar", "baz"] - block: # any - let - numbers = @[1, 4, 5, 8, 9, 7, 4] - len0seq : seq[int] = @[] - assert any(numbers, proc (x: int): bool = return x > 8) == true - assert any(numbers, proc (x: int): bool = return x > 9) == false - assert any(len0seq, proc (x: int): bool = return true) == false - - block: # anyIt - let - numbers = @[1, 4, 5, 8, 9, 7, 4] - len0seq : seq[int] = @[] - assert anyIt(numbers, it > 8) == true - assert anyIt(numbers, it > 9) == false - assert anyIt(len0seq, true) == false - block: # all let numbers = @[1, 4, 5, 8, 9, 7, 4] + anumbers = [1, 4, 5, 8, 9, 7, 4] len0seq : seq[int] = @[] assert all(numbers, proc (x: int): bool = return x < 10) == true assert all(numbers, proc (x: int): bool = return x < 9) == false assert all(len0seq, proc (x: int): bool = return false) == true + assert all(anumbers, proc (x: int): bool = return x < 10) == true + assert all(anumbers, proc (x: int): bool = return x < 9) == false block: # allIt let numbers = @[1, 4, 5, 8, 9, 7, 4] + anumbers = [1, 4, 5, 8, 9, 7, 4] len0seq : seq[int] = @[] assert allIt(numbers, it < 10) == true assert allIt(numbers, it < 9) == false assert allIt(len0seq, false) == true + assert allIt(anumbers, it < 10) == true + assert allIt(anumbers, it < 9) == false + + block: # any + let + numbers = @[1, 4, 5, 8, 9, 7, 4] + anumbers = [1, 4, 5, 8, 9, 7, 4] + len0seq : seq[int] = @[] + assert any(numbers, proc (x: int): bool = return x > 8) == true + assert any(numbers, proc (x: int): bool = return x > 9) == false + assert any(len0seq, proc (x: int): bool = return true) == false + assert any(anumbers, proc (x: int): bool = return x > 8) == true + assert any(anumbers, proc (x: int): bool = return x > 9) == false + + block: # anyIt + let + numbers = @[1, 4, 5, 8, 9, 7, 4] + anumbers = [1, 4, 5, 8, 9, 7, 4] + len0seq : seq[int] = @[] + assert anyIt(numbers, it > 8) == true + assert anyIt(numbers, it > 9) == false + assert anyIt(len0seq, true) == false + assert anyIt(anumbers, it > 8) == true + assert anyIt(anumbers, it > 9) == false block: # toSeq test let @@ -792,56 +977,13 @@ when isMainModule: assert multiplication == 495, "Multiplication is (5*(9*(11)))" assert concatenation == "nimiscool" - block: # delete tests - let outcome = @[1,1,1,1,1,1,1,1] - var dest = @[1,1,1,2,2,2,2,2,2,1,1,1,1,1] - dest.delete(3, 8) - assert outcome == dest, """\ - Deleting range 3-9 from [1,1,1,2,2,2,2,2,2,1,1,1,1,1] - is [1,1,1,1,1,1,1,1]""" - - block: # insert tests - var dest = @[1,1,1,1,1,1,1,1] - let - src = @[2,2,2,2,2,2] - outcome = @[1,1,1,2,2,2,2,2,2,1,1,1,1,1] - dest.insert(src, 3) - assert dest == outcome, """\ - Inserting [2,2,2,2,2,2] into [1,1,1,1,1,1,1,1] - at 3 is [1,1,1,2,2,2,2,2,2,1,1,1,1,1]""" - block: # mapIt tests var nums = @[1, 2, 3, 4] strings = nums.mapIt($(4 * it)) nums.applyIt(it * 3) assert nums[0] + nums[3] == 15 - - block: # distribute tests - let numbers = @[1, 2, 3, 4, 5, 6, 7] - doAssert numbers.distribute(3) == @[@[1, 2, 3], @[4, 5], @[6, 7]] - doAssert numbers.distribute(6)[0] == @[1, 2] - doAssert numbers.distribute(6)[5] == @[7] - let a = @[1, 2, 3, 4, 5, 6, 7] - doAssert a.distribute(1, true) == @[@[1, 2, 3, 4, 5, 6, 7]] - doAssert a.distribute(1, false) == @[@[1, 2, 3, 4, 5, 6, 7]] - doAssert a.distribute(2, true) == @[@[1, 2, 3, 4], @[5, 6, 7]] - doAssert a.distribute(2, false) == @[@[1, 2, 3, 4], @[5, 6, 7]] - doAssert a.distribute(3, true) == @[@[1, 2, 3], @[4, 5], @[6, 7]] - doAssert a.distribute(3, false) == @[@[1, 2, 3], @[4, 5, 6], @[7]] - doAssert a.distribute(4, true) == @[@[1, 2], @[3, 4], @[5, 6], @[7]] - doAssert a.distribute(4, false) == @[@[1, 2], @[3, 4], @[5, 6], @[7]] - doAssert a.distribute(5, true) == @[@[1, 2], @[3, 4], @[5], @[6], @[7]] - doAssert a.distribute(5, false) == @[@[1, 2], @[3, 4], @[5, 6], @[7], @[]] - doAssert a.distribute(6, true) == @[@[1, 2], @[3], @[4], @[5], @[6], @[7]] - doAssert a.distribute(6, false) == @[ - @[1, 2], @[3, 4], @[5, 6], @[7], @[], @[]] - doAssert a.distribute(8, false) == a.distribute(8, true) - doAssert a.distribute(90, false) == a.distribute(90, true) - var b = @[0] - for f in 1 .. 25: b.add(f) - doAssert b.distribute(5, true)[4].len == 5 - doAssert b.distribute(5, false)[4].len == 2 + assert strings[2] == "12" block: # newSeqWith tests var seq2D = newSeqWith(4, newSeq[bool](2)) @@ -850,19 +992,5 @@ when isMainModule: seq2D[0][1] = true doAssert seq2D == @[@[true, true], @[true, false], @[false, false], @[false, false]] - block: # cycle tests - let - a = @[1, 2, 3] - b: seq[int] = @[] - - doAssert a.cycle(3) == @[1, 2, 3, 1, 2, 3, 1, 2, 3] - doAssert a.cycle(0) == @[] - #doAssert a.cycle(-1) == @[] # will not compile! - doAssert b.cycle(3) == @[] - - block: # repeat tests - assert repeat(10, 5) == @[10, 10, 10, 10, 10] - assert repeat(@[1,2,3], 2) == @[@[1,2,3], @[1,2,3]] - when not defined(testing): echo "Finished doc tests" diff --git a/lib/pure/collections/sharedstrings.nim b/lib/pure/collections/sharedstrings.nim index a9e194fb4..83edf8d94 100644 --- a/lib/pure/collections/sharedstrings.nim +++ b/lib/pure/collections/sharedstrings.nim @@ -55,7 +55,7 @@ proc `[]=`*(s: var SharedString; i: Natural; value: char) = if i < s.len: s.buffer.data[i+s.first] = value else: raise newException(IndexError, "index out of bounds") -proc `[]`*(s: SharedString; ab: Slice[int]): SharedString = +proc `[]`*(s: SharedString; ab: HSlice[int, int]): SharedString = #incRef(src.buffer) if ab.a < s.len: result.buffer = s.buffer diff --git a/lib/pure/collections/tableimpl.nim b/lib/pure/collections/tableimpl.nim index eec98fcaf..9a5bffcef 100644 --- a/lib/pure/collections/tableimpl.nim +++ b/lib/pure/collections/tableimpl.nim @@ -149,7 +149,7 @@ template delImpl() {.dirty.} = delImplIdx(t, i) template clearImpl() {.dirty.} = - for i in 0 .. <t.data.len: + for i in 0 ..< t.data.len: when compiles(t.data[i].hcode): # CountTable records don't contain a hcode t.data[i].hcode = 0 t.data[i].key = default(type(t.data[i].key)) diff --git a/lib/pure/concurrency/cpuinfo.nim b/lib/pure/concurrency/cpuinfo.nim index 603fee080..f01488811 100644 --- a/lib/pure/concurrency/cpuinfo.nim +++ b/lib/pure/concurrency/cpuinfo.nim @@ -45,8 +45,25 @@ proc countProcessors*(): int {.rtl, extern: "ncpi$1".} = ## returns the numer of the processors/cores the machine has. ## Returns 0 if it cannot be detected. when defined(windows): - var x = getEnv("NUMBER_OF_PROCESSORS") - if x.len > 0: result = parseInt(x.string) + type + SYSTEM_INFO {.final, pure.} = object + u1: int32 + dwPageSize: int32 + lpMinimumApplicationAddress: pointer + lpMaximumApplicationAddress: pointer + dwActiveProcessorMask: ptr int32 + dwNumberOfProcessors: int32 + dwProcessorType: int32 + dwAllocationGranularity: int32 + wProcessorLevel: int16 + wProcessorRevision: int16 + + proc GetSystemInfo(lpSystemInfo: var SYSTEM_INFO) {.stdcall, dynlib: "kernel32", importc: "GetSystemInfo".} + + var + si: SYSTEM_INFO + GetSystemInfo(si) + result = si.dwNumberOfProcessors elif defined(macosx) or defined(bsd): var mib: array[0..3, cint] diff --git a/lib/pure/concurrency/threadpool.nim b/lib/pure/concurrency/threadpool.nim index 2a0dbd2ca..a5eaec86e 100644 --- a/lib/pure/concurrency/threadpool.nim +++ b/lib/pure/concurrency/threadpool.nim @@ -149,7 +149,7 @@ proc selectWorker(w: ptr Worker; fn: WorkerProc; data: pointer): bool = proc cleanFlowVars(w: ptr Worker) = let q = addr(w.q) acquire(q.lock) - for i in 0 .. <q.len: + for i in 0 ..< q.len: GC_unref(cast[RootRef](q.data[i])) #echo "GC_unref" q.len = 0 @@ -401,7 +401,7 @@ proc setup() = gCpus = p currentPoolSize = min(p, MaxThreadPoolSize) readyWorker = addr(workersData[0]) - for i in 0.. <currentPoolSize: activateWorkerThread(i) + for i in 0..<currentPoolSize: activateWorkerThread(i) proc preferSpawn*(): bool = ## Use this proc to determine quickly if a 'spawn' or a direct call is @@ -446,14 +446,24 @@ proc nimSpawn3(fn: WorkerProc; data: pointer) {.compilerProc.} = # implementation of 'spawn' that is used by the code generator. while true: if selectWorker(readyWorker, fn, data): return - for i in 0.. <currentPoolSize: + for i in 0..<currentPoolSize: if selectWorker(addr(workersData[i]), fn, data): return + # determine what to do, but keep in mind this is expensive too: # state.calls < maxPoolSize: warmup phase # (state.calls and 127) == 0: periodic check if state.calls < maxPoolSize or (state.calls and 127) == 0: # ensure the call to 'advice' is atomic: if tryAcquire(stateLock): + if currentPoolSize < minPoolSize: + if not workersData[currentPoolSize].initialized: + activateWorkerThread(currentPoolSize) + let w = addr(workersData[currentPoolSize]) + atomicInc currentPoolSize + if selectWorker(w, fn, data): + release(stateLock) + return + case advice(state) of doNothing: discard of doCreateThread: @@ -533,7 +543,7 @@ proc sync*() = var toRelease = 0 while true: var allReady = true - for i in 0 .. <currentPoolSize: + for i in 0 ..< currentPoolSize: if not allReady: break allReady = allReady and workersData[i].ready if allReady: break diff --git a/lib/pure/future.nim b/lib/pure/future.nim index 2a6d29933..f6592df71 100644 --- a/lib/pure/future.nim +++ b/lib/pure/future.nim @@ -22,7 +22,7 @@ proc createProcType(p, b: NimNode): NimNode {.compileTime.} = case p.kind of nnkPar: - for i in 0 .. <p.len: + for i in 0 ..< p.len: let ident = p[i] var identDefs = newNimNode(nnkIdentDefs) case ident.kind @@ -77,7 +77,7 @@ macro `=>`*(p, b: untyped): untyped = if c[0].kind == nnkIdent and c[0].ident == !"->": var procTy = createProcType(c[1], c[2]) params[0] = procTy[0][0] - for i in 1 .. <procTy[0].len: + for i in 1 ..< procTy[0].len: params.add(procTy[0][i]) else: error("Expected proc type (->) got (" & $c[0].ident & ").") @@ -96,7 +96,7 @@ macro `=>`*(p, b: untyped): untyped = if p[0].kind == nnkIdent and p[0].ident == !"->": var procTy = createProcType(p[1], p[2]) params[0] = procTy[0][0] - for i in 1 .. <procTy[0].len: + for i in 1 ..< procTy[0].len: params.add(procTy[0][i]) else: error("Expected proc type (->) got (" & $p[0].ident & ").") diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index cb4f4f664..de1d332a3 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -883,7 +883,9 @@ proc recvFull(client: HttpClient | AsyncHttpClient, size: int, timeout: int, let data = client.socket.recv(sizeToRecv, timeout) else: let data = await client.socket.recv(sizeToRecv) - if data == "": break # We've been disconnected. + if data == "": + client.close() + break # We've been disconnected. readLen.inc(data.len) if keep: @@ -950,6 +952,7 @@ proc parseBody(client: HttpClient | AsyncHttpClient, if length > 0: let recvLen = await client.recvFull(length, client.timeout, true) if recvLen == 0: + client.close() httpError("Got disconnected while trying to read body.") if recvLen != length: httpError("Received length doesn't match expected length. Wanted " & @@ -962,13 +965,20 @@ proc parseBody(client: HttpClient | AsyncHttpClient, if headers.getOrDefault"Connection" == "close" or httpVersion == "1.0": while true: let recvLen = await client.recvFull(4000, client.timeout, true) - if recvLen == 0: break + if recvLen == 0: + client.close() + break when client is AsyncHttpClient: client.bodyStream.complete() else: client.bodyStream.setPosition(0) + # If the server will close our connection, then no matter the method of + # reading the body, we need to close our socket. + if headers.getOrDefault"Connection" == "close": + client.close() + proc parseResponse(client: HttpClient | AsyncHttpClient, getBody: bool): Future[Response | AsyncResponse] {.multisync.} = @@ -984,7 +994,10 @@ proc parseResponse(client: HttpClient | AsyncHttpClient, line = await client.socket.recvLine(client.timeout) else: line = await client.socket.recvLine() - if line == "": break # We've been disconnected. + if line == "": + # We've been disconnected. + client.close() + break if line == "\c\L": fullyRead = true break @@ -1033,7 +1046,8 @@ proc newConnection(client: HttpClient | AsyncHttpClient, url: Uri) {.multisync.} = if client.currentURL.hostname != url.hostname or client.currentURL.scheme != url.scheme or - client.currentURL.port != url.port: + client.currentURL.port != url.port or + (not client.connected): let isSsl = url.scheme.toLowerAscii() == "https" if isSsl and not defined(ssl): diff --git a/lib/pure/httpcore.nim b/lib/pure/httpcore.nim index a5ab40ca4..f150fa1c1 100644 --- a/lib/pure/httpcore.nim +++ b/lib/pure/httpcore.nim @@ -113,6 +113,9 @@ proc newHttpHeaders*(keyValuePairs: new result result.table = newTable[string, seq[string]](pairs) +proc `$`*(headers: HttpHeaders): string = + return $headers.table + proc clear*(headers: HttpHeaders) = headers.table.clear() diff --git a/lib/pure/includes/osenv.nim b/lib/pure/includes/osenv.nim index 8d2fc235a..ae62a5c4e 100644 --- a/lib/pure/includes/osenv.nim +++ b/lib/pure/includes/osenv.nim @@ -94,7 +94,7 @@ proc findEnvVar(key: string): int = if startsWith(environment[i], temp): return i return -1 -proc getEnv*(key: string): TaintedString {.tags: [ReadEnvEffect].} = +proc getEnv*(key: string, default = ""): TaintedString {.tags: [ReadEnvEffect].} = ## Returns the value of the `environment variable`:idx: named `key`. ## ## If the variable does not exist, "" is returned. To distinguish @@ -108,7 +108,7 @@ proc getEnv*(key: string): TaintedString {.tags: [ReadEnvEffect].} = return TaintedString(substr(environment[i], find(environment[i], '=')+1)) else: var env = c_getenv(key) - if env == nil: return TaintedString("") + if env == nil: return TaintedString(default) result = TaintedString($env) proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} = diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 656114fb1..3d86cc9d7 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -37,15 +37,15 @@ ## Retrieving the value of a JSON node can then be achieved using one of the ## helper procedures, which include: ## -## * ``getNum`` -## * ``getFNum`` +## * ``getInt`` +## * ``getFloat`` ## * ``getStr`` -## * ``getBVal`` +## * ``getBool`` ## ## To retrieve the value of ``"key"`` you can do the following: ## ## .. code-block:: Nim -## doAssert jsonNode["key"].getFNum() == 3.14 +## doAssert jsonNode["key"].getFloat() == 3.14 ## ## The ``[]`` operator will raise an exception when the specified field does ## not exist. If you wish to avoid this behaviour you can use the ``{}`` @@ -681,14 +681,25 @@ proc getStr*(n: JsonNode, default: string = ""): string = if n.isNil or n.kind != JString: return default else: return n.str -proc getNum*(n: JsonNode, default: BiggestInt = 0): BiggestInt = +proc getInt*(n: JsonNode, default: int = 0): int = ## Retrieves the int value of a `JInt JsonNode`. ## ## Returns ``default`` if ``n`` is not a ``JInt``, or if ``n`` is nil. if n.isNil or n.kind != JInt: return default + else: return int(n.num) + +proc getBiggestInt*(n: JsonNode, default: BiggestInt = 0): BiggestInt = + ## Retrieves the BiggestInt value of a `JInt JsonNode`. + ## + ## Returns ``default`` if ``n`` is not a ``JInt``, or if ``n`` is nil. + if n.isNil or n.kind != JInt: return default else: return n.num -proc getFNum*(n: JsonNode, default: float = 0.0): float = +proc getNum*(n: JsonNode, default: BiggestInt = 0): BiggestInt {.deprecated.} = + ## Deprecated - use getInt or getBiggestInt instead + getBiggestInt(n, default) + +proc getFloat*(n: JsonNode, default: float = 0.0): float = ## Retrieves the float value of a `JFloat JsonNode`. ## ## Returns ``default`` if ``n`` is not a ``JFloat`` or ``JInt``, or if ``n`` is nil. @@ -698,13 +709,21 @@ proc getFNum*(n: JsonNode, default: float = 0.0): float = of JInt: return float(n.num) else: return default -proc getBVal*(n: JsonNode, default: bool = false): bool = +proc getFNum*(n: JsonNode, default: float = 0.0): float {.deprecated.} = + ## Deprecated - use getFloat instead + getFloat(n, default) + +proc getBool*(n: JsonNode, default: bool = false): bool = ## Retrieves the bool value of a `JBool JsonNode`. ## ## Returns ``default`` if ``n`` is not a ``JBool``, or if ``n`` is nil. if n.isNil or n.kind != JBool: return default else: return n.bval +proc getBVal*(n: JsonNode, default: bool = false): bool {.deprecated.} = + ## Deprecated - use getBVal instead + getBool(n, default) + proc getFields*(n: JsonNode, default = initOrderedTable[string, JsonNode](4)): OrderedTable[string, JsonNode] = @@ -804,13 +823,13 @@ proc toJson(x: NimNode): NimNode {.compiletime.} = of nnkBracket: # array if x.len == 0: return newCall(bindSym"newJArray") result = newNimNode(nnkBracket) - for i in 0 .. <x.len: + for i in 0 ..< x.len: result.add(toJson(x[i])) result = newCall(bindSym"%", result) of nnkTableConstr: # object if x.len == 0: return newCall(bindSym"newJObject") result = newNimNode(nnkTableConstr) - for i in 0 .. <x.len: + for i in 0 ..< x.len: x[i].expectKind nnkExprColonExpr result.add newTree(nnkExprColonExpr, x[i][0], toJson(x[i][1])) result = newCall(bindSym"%", result) @@ -1284,7 +1303,7 @@ else: case getVarType(x) of JArray: result = newJArray() - for i in 0 .. <x.len: + for i in 0 ..< x.len: result.add(x[i].convertObject()) of JObject: result = newJObject() @@ -1342,7 +1361,7 @@ proc getEnum(node: JsonNode, ast: string, T: typedesc): T = # TODO: I shouldn't need this proc. proc convert[T](x: BiggestInt): T = T(x) verifyJsonKind(node, {JInt}, ast) - return convert[T](node.getNum()) + return convert[T](node.getBiggestInt()) else: verifyJsonKind(node, {JString}, ast) return parseEnum[T](node.getStr()) @@ -1430,7 +1449,7 @@ proc processElseBranch(recCaseNode, elseBranch, jsonNode, kindType, # We need to build up a list of conditions from each ``of`` branch so that # we can then negate it to get ``else``. var cond = newIdentNode("false") - for i in 1 .. <len(recCaseNode): + for i in 1 ..< len(recCaseNode): if recCaseNode[i].kind == nnkElse: break @@ -1492,7 +1511,7 @@ proc processObjField(field, jsonNode: NimNode): seq[NimNode] = exprColonExpr.add(getEnumCall) # Iterate through each `of` branch. - for i in 1 .. <field.len: + for i in 1 ..< field.len: case field[i].kind of nnkOfBranch: result.add processOfBranch(field[i], jsonNode, kindType, kindJsonNode) @@ -1621,7 +1640,7 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode = ( var list: `typeSym` = @[]; verifyJsonKind(`jsonNode`, {JArray}, astToStr(`jsonNode`)); - for `forLoopI` in 0 .. <`jsonNode`.len: list.add(`constructorNode`); + for `forLoopI` in 0 ..< `jsonNode`.len: list.add(`constructorNode`); list ) of "array": @@ -1635,7 +1654,7 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode = ( var list: `typeSym`; verifyJsonKind(`jsonNode`, {JArray}, astToStr(`jsonNode`)); - for `forLoopI` in 0 .. <`jsonNode`.len: list[`forLoopI`] =`constructorNode`; + for `forLoopI` in 0 ..< `jsonNode`.len: list[`forLoopI`] =`constructorNode`; list ) @@ -1664,7 +1683,7 @@ proc postProcessValue(value: NimNode): NimNode = result = postProcess(value) else: result = value - for i in 0 .. <len(result): + for i in 0 ..< len(result): result[i] = postProcessValue(result[i]) proc postProcessExprColonExpr(exprColonExpr, resIdent: NimNode): NimNode = diff --git a/lib/pure/lexbase.nim b/lib/pure/lexbase.nim index cf2e8bb89..15a390f0b 100644 --- a/lib/pure/lexbase.nim +++ b/lib/pure/lexbase.nim @@ -37,6 +37,7 @@ type lineNumber*: int ## the current line number sentinel: int lineStart: int # index of last line start in buffer + offsetBase*: int # use ``offsetBase + bufpos`` to get the offset refillChars: set[char] {.deprecated: [TBaseLexer: BaseLexer].} @@ -107,7 +108,8 @@ proc fillBaseLexer(L: var BaseLexer, pos: int): int = result = pos + 1 # nothing to do else: fillBuffer(L) - L.bufpos = 0 # XXX: is this really correct? + L.offsetBase += pos + L.bufpos = 0 result = 0 proc handleCR*(L: var BaseLexer, pos: int): int = @@ -147,6 +149,7 @@ proc open*(L: var BaseLexer, input: Stream, bufLen: int = 8192; assert(input != nil) L.input = input L.bufpos = 0 + L.offsetBase = 0 L.bufLen = bufLen L.refillChars = refillChars when defined(js): diff --git a/lib/pure/marshal.nim b/lib/pure/marshal.nim index c4c731acf..6ee830786 100644 --- a/lib/pure/marshal.nim +++ b/lib/pure/marshal.nim @@ -283,7 +283,7 @@ proc to*[T](data: string): T = loadAny(newStringStream(data), toAny(result), tab) when not defined(testing) and isMainModule: - template testit(x: expr) = echo($$to[type(x)]($$x)) + template testit(x: untyped) = echo($$to[type(x)]($$x)) var x: array[0..4, array[0..4, string]] = [ ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"], diff --git a/lib/pure/matchers.nim b/lib/pure/matchers.nim index 7022c21d9..6366fee1a 100644 --- a/lib/pure/matchers.nim +++ b/lib/pure/matchers.nim @@ -48,7 +48,7 @@ proc validEmailAddress*(s: string): bool {.noSideEffect, "aero", "jobs", "museum": return true else: return false -proc parseInt*(s: string, value: var int, validRange: Slice[int]) {. +proc parseInt*(s: string, value: var int, validRange: HSlice[int, int]) {. noSideEffect, rtl, extern: "nmatchParseInt".} = ## parses `s` into an integer in the range `validRange`. If successful, ## `value` is modified to contain the result. Otherwise no exception is diff --git a/lib/pure/math.nim b/lib/pure/math.nim index 8037b31b0..7fd8bbcef 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -184,6 +184,8 @@ when not defined(JS): proc pow*(x, y: float32): float32 {.importc: "powf", header: "<math.h>".} proc pow*(x, y: float64): float64 {.importc: "pow", header: "<math.h>".} ## computes x to power raised of y. + ## + ## To compute power between integers, use `^` e.g. 2 ^ 6 proc erf*(x: float32): float32 {.importc: "erff", header: "<math.h>".} proc erf*(x: float64): float64 {.importc: "erf", header: "<math.h>".} diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim index 98fc62a3b..6c8701843 100644 --- a/lib/pure/nativesockets.nim +++ b/lib/pure/nativesockets.nim @@ -496,11 +496,12 @@ proc getLocalAddr*(socket: SocketHandle, domain: Domain): (string, Port) = addr(namelen)) == -1'i32: raiseOSError(osLastError()) # Cannot use INET6_ADDRSTRLEN here, because it's a C define. - var buf: array[64, char] + result[0] = newString(64) if inet_ntop(name.sin6_family.cint, - addr name.sin6_addr, buf.cstring, sizeof(buf).int32).isNil: + addr name.sin6_addr, addr result[0][0], (result[0].len+1).int32).isNil: raiseOSError(osLastError()) - result = ($buf, Port(nativesockets.ntohs(name.sin6_port))) + setLen(result[0], result[0].cstring.len) + result[1] = Port(nativesockets.ntohs(name.sin6_port)) else: raiseOSError(OSErrorCode(-1), "invalid socket family in getLocalAddr") @@ -532,11 +533,12 @@ proc getPeerAddr*(socket: SocketHandle, domain: Domain): (string, Port) = addr(namelen)) == -1'i32: raiseOSError(osLastError()) # Cannot use INET6_ADDRSTRLEN here, because it's a C define. - var buf: array[64, char] + result[0] = newString(64) if inet_ntop(name.sin6_family.cint, - addr name.sin6_addr, buf.cstring, sizeof(buf).int32).isNil: + addr name.sin6_addr, addr result[0][0], (result[0].len+1).int32).isNil: raiseOSError(osLastError()) - result = ($buf, Port(nativesockets.ntohs(name.sin6_port))) + setLen(result[0], result[0].cstring.len) + result[1] = Port(nativesockets.ntohs(name.sin6_port)) else: raiseOSError(OSErrorCode(-1), "invalid socket family in getLocalAddr") diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 215a301b6..a405ce1bd 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -857,13 +857,16 @@ proc close*(socket: Socket) = # shutdown i.e not wait for the peers "close notify" alert with a second # call to SSLShutdown let res = SSLShutdown(socket.sslHandle) - SSLFree(socket.sslHandle) - socket.sslHandle = nil if res == 0: discard elif res != 1: socketError(socket, res) finally: + when defineSsl: + if socket.isSSL and socket.sslHandle != nil: + SSLFree(socket.sslHandle) + socket.sslHandle = nil + socket.fd.close() proc toCInt*(opt: SOBool): cint = diff --git a/lib/pure/options.nim b/lib/pure/options.nim index ad63bbcb6..6d2869bff 100644 --- a/lib/pure/options.nim +++ b/lib/pure/options.nim @@ -153,7 +153,10 @@ proc `==`*(a, b: Option): bool = (a.has and b.has and a.val == b.val) or (not a.has and not b.has) proc `$`*[T](self: Option[T]): string = - ## Returns the contents of this option or `otherwise` if the option is none. + ## Get the string representation of this option. If the option has a value, + ## the result will be `Some(x)` where `x` is the string representation of the contained value. + ## If the option does not have a value, the result will be `None[T]` where `T` is the name of + ## the type contained in the option. if self.has: "Some(" & $self.val & ")" else: diff --git a/lib/pure/os.nim b/lib/pure/os.nim index b85181edf..a1ae4e250 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -790,7 +790,10 @@ iterator walkDir*(dir: string; relative=false): tuple[kind: PathComponent, path: while true: var x = readdir(d) if x == nil: break - var y = $x.d_name + when defined(nimNoArrayToCstringConversion): + var y = $cstring(addr x.d_name) + else: + var y = $x.d_name.cstring if y != "." and y != "..": var s: Stat if not relative: diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 07429b9a9..71d3d9c72 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -280,7 +280,7 @@ proc execProcesses*(cmds: openArray[string], ## executes the commands `cmds` in parallel. Creates `n` processes ## that execute in parallel. The highest return value of all processes ## is returned. Runs `beforeRunEvent` before running each command. - when defined(posix): + when false: # poParentStreams causes problems on Posix, so we simply disable it: var options = options - {poParentStreams} @@ -617,6 +617,7 @@ when defined(Windows) and not defined(useNimRtl): var res: int32 discard getExitCodeProcess(p.fProcessHandle, res) result = res + p.exitStatus = res discard closeHandle(p.fProcessHandle) proc peekExitCode(p: Process): int = @@ -625,6 +626,7 @@ when defined(Windows) and not defined(useNimRtl): else: var res: int32 discard getExitCodeProcess(p.fProcessHandle, res) + if res == 0: return p.exitStatus return res proc inputStream(p: Process): Stream = diff --git a/lib/pure/parsecsv.nim b/lib/pure/parsecsv.nim index 77b145a73..ca0f3f9e0 100644 --- a/lib/pure/parsecsv.nim +++ b/lib/pure/parsecsv.nim @@ -72,7 +72,10 @@ proc raiseEInvalidCsv(filename: string, line, col: int, msg: string) {.noreturn.} = var e: ref CsvError new(e) - e.msg = filename & "(" & $line & ", " & $col & ") Error: " & msg + if filename.len == 0: + e.msg = "Error: " & msg + else: + e.msg = filename & "(" & $line & ", " & $col & ") Error: " & msg raise e proc error(my: CsvParser, pos: int, msg: string) = diff --git a/lib/pure/parseopt2.nim b/lib/pure/parseopt2.nim index 2e8dbe140..a2ff9bf0c 100644 --- a/lib/pure/parseopt2.nim +++ b/lib/pure/parseopt2.nim @@ -35,7 +35,7 @@ type cmd: seq[string] pos: int remainingShortOptions: string - kind*: CmdLineKind ## the dected command line token + kind*: CmdLineKind ## the detected command line token key*, val*: TaintedString ## key and value pair; ``key`` is the option ## or the argument, ``value`` is not "" if ## the option was given a value diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim index b78e8d000..3c790512f 100644 --- a/lib/pure/parseutils.nim +++ b/lib/pure/parseutils.nim @@ -8,6 +8,8 @@ # ## This module contains helpers for parsing tokens, numbers, identifiers, etc. +## +## To unpack raw bytes look at the `streams <streams.html>`_ module. {.deadCodeElim: on.} diff --git a/lib/pure/random.nim b/lib/pure/random.nim index 27fbfad45..e6a9162c5 100644 --- a/lib/pure/random.nim +++ b/lib/pure/random.nim @@ -93,7 +93,7 @@ proc random*(max: float): float {.benign.} = let u = (0x3FFu64 shl 52u64) or (x shr 12u64) result = (cast[float](u) - 1.0) * max -proc random*[T](x: Slice[T]): T = +proc random*[T](x: HSlice[T, T]): T = ## For a slice `a .. b` returns a value in the range `a .. b-1`. result = T(random(x.b - x.a)) + x.a diff --git a/lib/pure/rationals.nim b/lib/pure/rationals.nim index c2ba2b1f3..7fb24c26f 100644 --- a/lib/pure/rationals.nim +++ b/lib/pure/rationals.nim @@ -39,47 +39,13 @@ proc toRational*[T:SomeInteger](x: T): Rational[T] = result.num = x result.den = 1 -proc toRationalSub(x: float, n: int): Rational[int] = - var - a = 0'i64 - b, c, d = 1'i64 - result = 0 // 1 # rational 0 - while b <= n and d <= n: - let ac = (a+c) - let bd = (b+d) - # scale by 1000 so not overflow for high precision - let mediant = (ac.float/1000) / (bd.float/1000) - if x == mediant: - if bd <= n: - result.num = ac.int - result.den = bd.int - return result - elif d > b: - result.num = c.int - result.den = d.int - return result - else: - result.num = a.int - result.den = b.int - return result - elif x > mediant: - a = ac - b = bd - else: - c = ac - d = bd - if (b > n): - return initRational(c.int, d.int) - return initRational(a.int, b.int) - -proc toRational*(x: float, n: int = high(int)): Rational[int] = - ## Calculate the best rational numerator and denominator +proc toRational*(x: float, n: int = high(int32)): Rational[int] = + ## Calculates the best rational numerator and denominator ## that approximates to `x`, where the denominator is ## smaller than `n` (default is the largest possible - ## int to give maximum resolution) + ## int to give maximum resolution). ## - ## The algorithm is based on the Farey sequence named - ## after John Farey + ## The algorithm is based on the theory of continued fractions. ## ## .. code-block:: Nim ## import math, rationals @@ -88,13 +54,24 @@ proc toRational*(x: float, n: int = high(int)): Rational[int] = ## let x = toRational(PI, t) ## let newPI = x.num / x.den ## echo x, " ", newPI, " error: ", PI - newPI, " ", t - if x > 1: - result = toRationalSub(1.0/x, n) - swap(result.num, result.den) - elif x == 1.0: - result = 1 // 1 - else: - result = toRationalSub(x, n) + + # David Eppstein / UC Irvine / 8 Aug 1993 + # With corrections from Arno Formella, May 2008 + var + m11, m22 = 1 + m12, m21 = 0 + ai = int(x) + x = x + while m21 * ai + m22 <= n: + swap m12, m11 + swap m22, m21 + m11 = m12 * ai + m11 + m21 = m22 * ai + m21 + if x == float(ai): break # division by zero + x = 1/(x - float(ai)) + if x > float(high(int32)): break # representation failure + ai = int(x) + result = m11 // m21 proc toFloat*[T](x: Rational[T]): float = ## Convert a rational number `x` to a float. @@ -346,7 +323,14 @@ when isMainModule: assert abs(toFloat(y) - 0.4814814814814815) < 1.0e-7 assert toInt(z) == 0 - assert toRational(0.98765432) == 12345679 // 12500000 - assert toRational(0.1, 1000000) == 1 // 10 - assert toRational(0.9, 1000000) == 9 // 10 - #assert toRational(PI) == 80143857 // 25510582 + assert toRational(0.98765432) == 2111111029 // 2137499919 + assert toRational(PI) == 817696623 // 260280919 + assert toRational(0.1) == 1 // 10 + assert toRational(0.9) == 9 // 10 + + assert toRational(0.0) == 0 // 1 + assert toRational(-0.25) == 1 // -4 + assert toRational(3.2) == 16 // 5 + assert toRational(0.33) == 33 // 100 + assert toRational(0.22) == 11 // 50 + assert toRational(10.0) == 10 // 1 diff --git a/lib/pure/securehash.nim b/lib/pure/securehash.nim index c19146669..57c1f3631 100644 --- a/lib/pure/securehash.nim +++ b/lib/pure/securehash.nim @@ -181,7 +181,7 @@ proc `$`*(self: SecureHash): string = result.add(toHex(int(v), 2)) proc parseSecureHash*(hash: string): SecureHash = - for i in 0.. <Sha1DigestSize: + for i in 0 ..< Sha1DigestSize: Sha1Digest(result)[i] = uint8(parseHexInt(hash[i*2] & hash[i*2 + 1])) proc `==`*(a, b: SecureHash): bool = diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim index 506b2cec0..d17b6c253 100644 --- a/lib/pure/selectors.nim +++ b/lib/pure/selectors.nim @@ -162,7 +162,7 @@ elif defined(linux): return @[] raiseOSError(err) if evNum == 0: return @[] - for i in 0 .. <evNum: + for i in 0 ..< evNum: let fd = s.events[i].data.fd.SocketHandle var evSet: set[Event] = {} @@ -253,7 +253,7 @@ elif defined(macosx) or defined(freebsd) or defined(openbsd) or defined(netbsd): return @[] raiseOSError(err) if evNum == 0: return @[] - for i in 0 .. <evNum: + for i in 0 ..< evNum: let fd = s.events[i].ident.SocketHandle var evSet: set[Event] = {} diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim index 69f673990..354e07da3 100644 --- a/lib/pure/streams.nim +++ b/lib/pure/streams.nim @@ -224,6 +224,38 @@ proc peekInt64*(s: Stream): int64 = ## peeks an int64 from the stream `s`. Raises `EIO` if an error occurred. peek(s, result) +proc readUint8*(s: Stream): uint8 = + ## reads an uint8 from the stream `s`. Raises `EIO` if an error occurred. + read(s, result) + +proc peekUint8*(s: Stream): uint8 = + ## peeks an uint8 from the stream `s`. Raises `EIO` if an error occurred. + peek(s, result) + +proc readUint16*(s: Stream): uint16 = + ## reads an uint16 from the stream `s`. Raises `EIO` if an error occurred. + read(s, result) + +proc peekUint16*(s: Stream): uint16 = + ## peeks an uint16 from the stream `s`. Raises `EIO` if an error occurred. + peek(s, result) + +proc readUint32*(s: Stream): uint32 = + ## reads an uint32 from the stream `s`. Raises `EIO` if an error occurred. + read(s, result) + +proc peekUint32*(s: Stream): uint32 = + ## peeks an uint32 from the stream `s`. Raises `EIO` if an error occurred. + peek(s, result) + +proc readUint64*(s: Stream): uint64 = + ## reads an uint64 from the stream `s`. Raises `EIO` if an error occurred. + read(s, result) + +proc peekUint64*(s: Stream): uint64 = + ## peeks an uint64 from the stream `s`. Raises `EIO` if an error occurred. + peek(s, result) + proc readFloat32*(s: Stream): float32 = ## reads a float32 from the stream `s`. Raises `EIO` if an error occurred. read(s, result) diff --git a/lib/pure/strscans.nim b/lib/pure/strscans.nim index bf26d2e59..a54556915 100644 --- a/lib/pure/strscans.nim +++ b/lib/pure/strscans.nim @@ -253,7 +253,7 @@ is performed. for r in collectLinks(body): echo r -In this example both macros are combined seamlessly in order to maximise +In this example both macros are combined seamlessly in order to maximise efficiency and perform different checks. .. code-block:: nim @@ -308,7 +308,7 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b ## See top level documentation of his module of how ``scanf`` works. template matchBind(parser) {.dirty.} = var resLen = genSym(nskLet, "resLen") - conds.add newLetStmt(resLen, newCall(bindSym(parser), input, results[i], idx)) + conds.add newLetStmt(resLen, newCall(bindSym(parser), inp, results[i], idx)) conds.add resLen.notZero conds.add resLen @@ -316,7 +316,8 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b var p = 0 var idx = genSym(nskVar, "idx") var res = genSym(nskVar, "res") - result = newTree(nnkStmtListExpr, newVarStmt(idx, newLit 0), newVarStmt(res, newLit false)) + let inp = genSym(nskLet, "inp") + result = newTree(nnkStmtListExpr, newLetStmt(inp, input), newVarStmt(idx, newLit 0), newVarStmt(res, newLit false)) var conds = newTree(nnkStmtList) var fullMatch = false while p < pattern.len: @@ -325,7 +326,7 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b case pattern[p] of '$': var resLen = genSym(nskLet, "resLen") - conds.add newLetStmt(resLen, newCall(bindSym"skip", input, newLit($pattern[p]), idx)) + conds.add newLetStmt(resLen, newCall(bindSym"skip", inp, newLit($pattern[p]), idx)) conds.add resLen.notZero conds.add resLen of 'w': @@ -347,7 +348,7 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b error("no float var given for $f") inc i of 's': - conds.add newCall(bindSym"inc", idx, newCall(bindSym"skipWhitespace", input, idx)) + conds.add newCall(bindSym"inc", idx, newCall(bindSym"skipWhitespace", inp, idx)) conds.add newEmptyNode() conds.add newEmptyNode() of '.': @@ -364,7 +365,7 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b token.add pattern[q] inc q var resLen = genSym(nskLet, "resLen") - conds.add newLetStmt(resLen, newCall(bindSym"parseUntil", input, results[i], newLit(token), idx)) + conds.add newLetStmt(resLen, newCall(bindSym"parseUntil", inp, results[i], newLit(token), idx)) conds.add newCall(bindSym"!=", resLen, newLit min) conds.add resLen else: @@ -386,7 +387,7 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b let expr = pattern.substr(start, p-1) if i < results.len: var resLen = genSym(nskLet, "resLen") - conds.add newLetStmt(resLen, buildUserCall(expr, input, results[i], idx)) + conds.add newLetStmt(resLen, buildUserCall(expr, inp, results[i], idx)) conds.add newCall(bindSym"!=", resLen, newLit 0) conds.add resLen else: @@ -406,7 +407,7 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b else: discard inc p let expr = pattern.substr(start, p-1) - conds.add newCall(bindSym"inc", idx, buildUserCall(expr, input, idx)) + conds.add newCall(bindSym"inc", idx, buildUserCall(expr, inp, idx)) conds.add newEmptyNode() conds.add newEmptyNode() else: error("invalid format string") @@ -417,13 +418,13 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b token.add pattern[p] inc p var resLen = genSym(nskLet, "resLen") - conds.add newLetStmt(resLen, newCall(bindSym"skip", input, newLit(token), idx)) + conds.add newLetStmt(resLen, newCall(bindSym"skip", inp, newLit(token), idx)) conds.add resLen.notZero conds.add resLen result.add conditionsToIfChain(conds, idx, res, 0) if fullMatch: result.add newCall(bindSym"and", res, - newCall(bindSym">=", idx, newCall(bindSym"len", input))) + newCall(bindSym">=", idx, newCall(bindSym"len", inp))) else: result.add res @@ -684,3 +685,14 @@ when isMainModule: "NimMain c:/users/anwender/projects/nim/lib/system.nim:2613", "main c:/users/anwender/projects/nim/lib/system.nim:2620"] doAssert parseGDB(gdbOut) == result + + # bug #6487 + var count = 0 + + proc test(): string = + inc count + result = ",123123" + + var a: int + discard scanf(test(), ",$i", a) + doAssert count == 1 diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 8b4e6bd05..2b87e0d43 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -138,7 +138,8 @@ proc isAlphaNumeric*(s: string): bool {.noSideEffect, procvar, result = true for c in s: - result = c.isAlphaNumeric() and result + if not c.isAlphaNumeric(): + return false proc isDigit*(s: string): bool {.noSideEffect, procvar, rtl, extern: "nsuIsDigitStr".}= @@ -153,7 +154,8 @@ proc isDigit*(s: string): bool {.noSideEffect, procvar, result = true for c in s: - result = c.isDigit() and result + if not c.isDigit(): + return false proc isSpaceAscii*(s: string): bool {.noSideEffect, procvar, rtl, extern: "nsuIsSpaceAsciiStr".}= @@ -1062,8 +1064,8 @@ proc align*(s: string, count: Natural, padding = ' '): string {. ## ## `padding` characters (by default spaces) are added before `s` resulting in ## right alignment. If ``s.len >= count``, no spaces are added and `s` is - ## returned unchanged. If you need to left align a string use the `repeatChar - ## proc <#repeatChar>`_. Example: + ## returned unchanged. If you need to left align a string use the `alignLeft + ## proc <#alignLeft>`_. Example: ## ## .. code-block:: nim ## assert align("abc", 4) == " abc" @@ -1078,6 +1080,28 @@ proc align*(s: string, count: Natural, padding = ' '): string {. else: result = s +proc alignLeft*(s: string, count: Natural, padding = ' '): string {.noSideEffect.} = + ## Left-Aligns a string `s` with `padding`, so that it is of length `count`. + ## + ## `padding` characters (by default spaces) are added after `s` resulting in + ## left alignment. If ``s.len >= count``, no spaces are added and `s` is + ## returned unchanged. If you need to right align a string use the `align + ## proc <#align>`_. Example: + ## + ## .. code-block:: nim + ## assert alignLeft("abc", 4) == "abc " + ## assert alignLeft("a", 0) == "a" + ## assert alignLeft("1232", 6) == "1232 " + ## assert alignLeft("1232", 6, '#') == "1232##" + if s.len < count: + result = newString(count) + if s.len > 0: + result[0 .. (s.len - 1)] = s + for i in s.len ..< count: + result[i] = padding + else: + result = s + iterator tokenize*(s: string, seps: set[char] = Whitespace): tuple[ token: string, isSep: bool] = ## Tokenizes the string `s` into substrings. @@ -1175,7 +1199,7 @@ proc unindent*(s: string, count: Natural, padding: string = " "): string var indentCount = 0 for j in 0..<count.int: indentCount.inc - if line[j .. j + <padding.len] != padding: + if line[j .. j + padding.len-1] != padding: indentCount = j break result.add(line[indentCount*padding.len .. ^1]) @@ -1306,18 +1330,36 @@ proc join*[T: not string](a: openArray[T], sep: string = ""): string {. add(result, $x) type - SkipTable = array[char, int] - -{.push profiler: off.} -proc preprocessSub(sub: string, a: var SkipTable) = - var m = len(sub) - for i in 0..0xff: a[chr(i)] = m+1 - for i in 0..m-1: a[sub[i]] = m-i -{.pop.} + SkipTable* = array[char, int] -proc findAux(s, sub: string, start, last: int, a: SkipTable): int = - # Fast "quick search" algorithm: - var +proc initSkipTable*(a: var SkipTable, sub: string) + {.noSideEffect, rtl, extern: "nsuInitSkipTable".} = + ## Preprocess table `a` for `sub`. + let m = len(sub) + let m1 = m + 1 + var i = 0 + while i <= 0xff-7: + a[chr(i + 0)] = m1 + a[chr(i + 1)] = m1 + a[chr(i + 2)] = m1 + a[chr(i + 3)] = m1 + a[chr(i + 4)] = m1 + a[chr(i + 5)] = m1 + a[chr(i + 6)] = m1 + a[chr(i + 7)] = m1 + i += 8 + + for i in 0..m-1: + a[sub[i]] = m-i + +proc find*(a: SkipTable, s, sub: string, start: Natural = 0, last: Natural = 0): int + {.noSideEffect, rtl, extern: "nsuFindStrA".} = + ## Searches for `sub` in `s` inside range `start`..`last` using preprocessed table `a`. + ## If `last` is unspecified, it defaults to `s.high`. + ## + ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned. + let + last = if last==0: s.high else: last m = len(sub) n = last + 1 # search: @@ -1337,17 +1379,6 @@ when not (defined(js) or defined(nimdoc) or defined(nimscript)): else: const hasCStringBuiltin = false -proc find*(s, sub: string, start: Natural = 0, last: Natural = 0): int {.noSideEffect, - rtl, extern: "nsuFindStr".} = - ## Searches for `sub` in `s` inside range `start`..`last`. - ## If `last` is unspecified, it defaults to `s.high`. - ## - ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned. - var a {.noinit.}: SkipTable - let last = if last==0: s.high else: last - preprocessSub(sub, a) - result = findAux(s, sub, start, last, a) - proc find*(s: string, sub: char, start: Natural = 0, last: Natural = 0): int {.noSideEffect, rtl, extern: "nsuFindChar".} = ## Searches for `sub` in `s` inside range `start`..`last`. @@ -1366,9 +1397,24 @@ proc find*(s: string, sub: char, start: Natural = 0, last: Natural = 0): int {.n else: for i in start..last: if sub == s[i]: return i - return -1 +proc find*(s, sub: string, start: Natural = 0, last: Natural = 0): int {.noSideEffect, + rtl, extern: "nsuFindStr".} = + ## Searches for `sub` in `s` inside range `start`..`last`. + ## If `last` is unspecified, it defaults to `s.high`. + ## + ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned. + if sub.len > s.len: + return -1 + + if sub.len == 1: + return find(s, sub[0], start, last) + + var a {.noinit.}: SkipTable + initSkipTable(a, sub) + result = find(a, s, sub, start, last) + proc find*(s: string, chars: set[char], start: Natural = 0, last: Natural = 0): int {.noSideEffect, rtl, extern: "nsuFindCharSet".} = ## Searches for `chars` in `s` inside range `start`..`last`. @@ -1500,11 +1546,11 @@ proc replace*(s, sub: string, by = ""): string {.noSideEffect, ## Replaces `sub` in `s` by the string `by`. var a {.noinit.}: SkipTable result = "" - preprocessSub(sub, a) + initSkipTable(a, sub) let last = s.high var i = 0 while true: - var j = findAux(s, sub, i, last, a) + var j = find(a, s, sub, i, last) if j < 0: break add result, substr(s, i, j - 1) add result, by @@ -1534,11 +1580,11 @@ proc replaceWord*(s, sub: string, by = ""): string {.noSideEffect, const wordChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\128'..'\255'} var a {.noinit.}: SkipTable result = "" - preprocessSub(sub, a) + initSkipTable(a, sub) var i = 0 let last = s.high while true: - var j = findAux(s, sub, i, last, a) + var j = find(a, s, sub, i, last) if j < 0: break # word boundary? if (j == 0 or s[j-1] notin wordChars) and @@ -1890,17 +1936,32 @@ proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault, frmtstr[3] = '*' frmtstr[4] = floatFormatToChar[format] frmtstr[5] = '\0' - L = c_sprintf(buf, frmtstr, precision, f) + when defined(nimNoArrayToCstringConversion): + L = c_sprintf(addr buf, addr frmtstr, precision, f) + else: + L = c_sprintf(buf, frmtstr, precision, f) else: frmtstr[1] = floatFormatToChar[format] frmtstr[2] = '\0' - L = c_sprintf(buf, frmtstr, f) + when defined(nimNoArrayToCstringConversion): + L = c_sprintf(addr buf, addr frmtstr, f) + else: + L = c_sprintf(buf, frmtstr, f) result = newString(L) for i in 0 ..< L: # Depending on the locale either dot or comma is produced, # but nothing else is possible: if buf[i] in {'.', ','}: result[i] = decimalsep else: result[i] = buf[i] + when defined(vcc): + # VS pre 2015 violates the C standard: "The exponent always contains at + # least two digits, and only as many more digits as necessary to + # represent the exponent." [C11 ยง7.21.6.1] + # The following post-processing fixes this behavior. + if result.len > 4 and result[^4] == '+' and result[^3] == '0': + result[^3] = result[^2] + result[^2] = result[^1] + result.setLen(result.len - 1) proc formatFloat*(f: float, format: FloatFormatMode = ffDefault, precision: range[0..32] = 16; decimalSep = '.'): string {. @@ -2169,11 +2230,26 @@ proc addf*(s: var string, formatstr: string, a: varargs[string, `$`]) {. if idx >% a.high: invalidFormatString() add s, a[idx] of '{': - var j = i+1 - while formatstr[j] notin {'\0', '}'}: inc(j) - var x = findNormalized(substr(formatstr, i+2, j-1), a) - if x >= 0 and x < high(a): add s, a[x+1] - else: invalidFormatString() + var j = i+2 + var k = 0 + var negative = formatstr[j] == '-' + if negative: inc j + var isNumber = 0 + while formatstr[j] notin {'\0', '}'}: + if formatstr[j] in Digits: + k = k * 10 + ord(formatstr[j]) - ord('0') + if isNumber == 0: isNumber = 1 + else: + isNumber = -1 + inc(j) + if isNumber == 1: + let idx = if not negative: k-1 else: a.len-k + if idx >% a.high: invalidFormatString() + add s, a[idx] + else: + var x = findNormalized(substr(formatstr, i+2, j-1), a) + if x >= 0 and x < high(a): add s, a[x+1] + else: invalidFormatString() i = j+1 of 'a'..'z', 'A'..'Z', '\128'..'\255', '_': var j = i+1 @@ -2250,60 +2326,104 @@ proc format*(formatstr: string, a: varargs[string, `$`]): string {.noSideEffect, proc removeSuffix*(s: var string, chars: set[char] = Newlines) {. rtl, extern: "nsuRemoveSuffixCharSet".} = - ## Removes the first matching character from the string (in-place) given a - ## set of characters. If the set of characters is only equal to `Newlines` - ## then it will remove both the newline and return feed. + ## Removes all characters from `chars` from the end of the string `s` + ## (in-place). + ## ## .. code-block:: nim - ## var - ## userInput = "Hello World!\r\n" - ## otherInput = "Hello!?!" + ## var userInput = "Hello World!*~\r\n" ## userInput.removeSuffix - ## userInput == "Hello World!" - ## userInput.removeSuffix({'!', '?'}) - ## userInput == "Hello World" + ## doAssert userInput == "Hello World!*~" + ## userInput.removeSuffix({'~', '*'}) + ## doAssert userInput == "Hello World!" + ## + ## var otherInput = "Hello!?!" ## otherInput.removeSuffix({'!', '?'}) - ## otherInput == "Hello!?" + ## doAssert otherInput == "Hello" if s.len == 0: return - var last = len(s) - 1 - if chars == Newlines: - if s[last] == '\10': - last -= 1 - if s[last] == '\13': - last -= 1 - else: - if s[last] in chars: - last -= 1 + var last = s.high + while last > -1 and s[last] in chars: last -= 1 s.setLen(last + 1) proc removeSuffix*(s: var string, c: char) {. rtl, extern: "nsuRemoveSuffixChar".} = - ## Removes a single character (in-place) from a string. + ## Removes all occurrences of a single character (in-place) from the end + ## of a string. + ## ## .. code-block:: nim - ## var - ## table = "users" + ## var table = "users" ## table.removeSuffix('s') - ## table == "user" + ## doAssert table == "user" + ## + ## var dots = "Trailing dots......." + ## dots.removeSuffix('.') + ## doAssert dots == "Trailing dots" removeSuffix(s, chars = {c}) proc removeSuffix*(s: var string, suffix: string) {. rtl, extern: "nsuRemoveSuffixString".} = ## Remove the first matching suffix (in-place) from a string. + ## ## .. code-block:: nim - ## var - ## answers = "yeses" + ## var answers = "yeses" ## answers.removeSuffix("es") - ## answers == "yes" + ## doAssert answers == "yes" var newLen = s.len if s.endsWith(suffix): newLen -= len(suffix) s.setLen(newLen) +proc removePrefix*(s: var string, chars: set[char] = Newlines) {. + rtl, extern: "nsuRemovePrefixCharSet".} = + ## Removes all characters from `chars` from the start of the string `s` + ## (in-place). + ## + ## .. code-block:: nim + ## var userInput = "\r\n*~Hello World!" + ## userInput.removePrefix + ## doAssert userInput == "*~Hello World!" + ## userInput.removePrefix({'~', '*'}) + ## doAssert userInput == "Hello World!" + ## + ## var otherInput = "?!?Hello!?!" + ## otherInput.removePrefix({'!', '?'}) + ## doAssert otherInput == "Hello!?!" + var start = 0 + while start < s.len and s[start] in chars: start += 1 + if start > 0: s.delete(0, start - 1) + +proc removePrefix*(s: var string, c: char) {. + rtl, extern: "nsuRemovePrefixChar".} = + ## Removes all occurrences of a single character (in-place) from the start + ## of a string. + ## + ## .. code-block:: nim + ## var ident = "pControl" + ## ident.removePrefix('p') + ## doAssert ident == "Control" + removePrefix(s, chars = {c}) + +proc removePrefix*(s: var string, prefix: string) {. + rtl, extern: "nsuRemovePrefixString".} = + ## Remove the first matching prefix (in-place) from a string. + ## + ## .. code-block:: nim + ## var answers = "yesyes" + ## answers.removePrefix("yes") + ## doAssert answers == "yes" + if s.startsWith(prefix): + s.delete(0, prefix.len - 1) + when isMainModule: doAssert align("abc", 4) == " abc" doAssert align("a", 0) == "a" doAssert align("1232", 6) == " 1232" doAssert align("1232", 6, '#') == "##1232" + doAssert alignLeft("abc", 4) == "abc " + doAssert alignLeft("a", 0) == "a" + doAssert alignLeft("1232", 6) == "1232 " + doAssert alignLeft("1232", 6, '#') == "1232##" + let inp = """ this is a long text -- muchlongerthan10chars and here it goes""" @@ -2313,8 +2433,12 @@ when isMainModule: doAssert formatBiggestFloat(0.00000000001, ffDecimal, 11) == "0.00000000001" doAssert formatBiggestFloat(0.00000000001, ffScientific, 1, ',') in ["1,0e-11", "1,0e-011"] + # bug #6589 + doAssert formatFloat(123.456, ffScientific, precision=0) in + ["1.234560e+02", "1.234560e+002"] doAssert "$# $3 $# $#" % ["a", "b", "c"] == "a c b c" + doAssert "${1}12 ${-1}$2" % ["a", "b"] == "a12 bb" block: # formatSize tests doAssert formatSize((1'i64 shl 31) + (300'i64 shl 20)) == "2.293GiB" diff --git a/lib/pure/times.nim b/lib/pure/times.nim index 68a457084..7dd428904 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -77,20 +77,17 @@ when defined(posix) and not defined(JS): when not defined(freebsd) and not defined(netbsd) and not defined(openbsd): var timezone {.importc, header: "<time.h>".}: int + proc tzset(): void {.importc, header: "<time.h>".} + tzset() elif defined(windows): import winlean - when defined(vcc) or defined(bcc) or defined(icl): - # newest version of Visual C++ defines time_t to be of 64 bits - type TimeImpl {.importc: "time_t", header: "<time.h>".} = int64 - # visual c's c runtime exposes these under a different name - var - timezone {.importc: "_timezone", header: "<time.h>".}: int - else: - type TimeImpl {.importc: "time_t", header: "<time.h>".} = int - var - timezone {.importc, header: "<time.h>".}: int + # newest version of Visual C++ defines time_t to be of 64 bits + type TimeImpl {.importc: "time_t", header: "<time.h>".} = int64 + # visual c's c runtime exposes these under a different name + var + timezone {.importc: "_timezone", header: "<time.h>".}: int type Time* = distinct TimeImpl @@ -1028,7 +1025,7 @@ proc countLeapYears*(yearSpan: int): int = ## counts the number of leap years up to January 1st of a given year. ## Keep in mind that if specified year is a leap year, the leap day ## has not happened before January 1st of that year. - (((yearSpan - 1) / 4) - ((yearSpan - 1) / 100) + ((yearSpan - 1) / 400)).int + (yearSpan - 1) div 4 - (yearSpan - 1) div 100 + (yearSpan - 1) div 400 proc countDays*(yearSpan: int): int = ## Returns the number of days spanned by a given number of years. diff --git a/lib/pure/typetraits.nim b/lib/pure/typetraits.nim index 55c4bf038..3b6f7de1a 100644 --- a/lib/pure/typetraits.nim +++ b/lib/pure/typetraits.nim @@ -31,6 +31,10 @@ proc name*(t: typedesc): string {.magic: "TypeTrait".} ## test(@['A','B']) ## # --> type: seq[char], value: @[A, B] +proc `$`*(t: typedesc): string = + ## An alias for `name`. + name(t) + proc arity*(t: typedesc): int {.magic: "TypeTrait".} ## Returns the arity of the given type @@ -49,3 +53,15 @@ proc stripGenericParams*(t: typedesc): typedesc {.magic: "TypeTrait".} ## This trait is similar to `genericHead`, but instead of producing ## error for non-generic types, it will just return them unmodified +proc supportsCopyMem*(t: typedesc): bool {.magic: "TypeTrait".} + ## This trait returns true iff the type ``t`` is safe to use for + ## `copyMem`:idx:. Other languages name a type like these `blob`:idx:. + + +when isMainModule: + # echo type(42) + import streams + var ss = newStringStream() + ss.write($type(42)) # needs `$` + ss.setPosition(0) + doAssert ss.readAll() == "int" diff --git a/lib/pure/unicode.nim b/lib/pure/unicode.nim index 0c4f15c91..7d9c3108b 100644 --- a/lib/pure/unicode.nim +++ b/lib/pure/unicode.nim @@ -285,7 +285,7 @@ proc runeReverseOffset*(s: string, rev:Positive): (int, int) = proc runeSubStr*(s: string, pos:int, len:int = int.high): string = ## Returns the UTF-8 substring starting at codepoint pos - ## with len codepoints. If pos or len is negativ they count from + ## with len codepoints. If pos or len is negative they count from ## the end of the string. If len is not given it means the longest ## possible string. ## diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim index 3772a213a..7a8d1dad0 100644 --- a/lib/pure/unittest.nim +++ b/lib/pure/unittest.nim @@ -312,7 +312,7 @@ proc ensureInitialized() = if not testsToRun.isValid: testsToRun.init() - when declared(os): + when declared(paramCount): # Read tests to run from the command line. for i in 1 .. paramCount(): testsToRun.incl(paramStr(i)) diff --git a/lib/pure/uri.nim b/lib/pure/uri.nim index d8e4ed52f..c702b054c 100644 --- a/lib/pure/uri.nim +++ b/lib/pure/uri.nim @@ -250,7 +250,7 @@ proc combine*(base: Uri, reference: Uri): Uri = proc combine*(uris: varargs[Uri]): Uri = ## Combines multiple URIs together. result = uris[0] - for i in 1 .. <uris.len: + for i in 1 ..< uris.len: result = combine(result, uris[i]) proc isAbsolute*(uri: Uri): bool = @@ -278,7 +278,9 @@ proc `/`*(x: Uri, path: string): Uri = result = x if result.path.len == 0: - result.path = path + if path[0] != '/': + result.path = "/" + result.path.add(path) return if result.path[result.path.len-1] == '/': @@ -476,6 +478,11 @@ when isMainModule: let foo = parseUri("http://example.com") / "/baz" doAssert foo.path == "/baz" + # bug found on stream 13/10/17 + block: + let foo = parseUri("http://localhost:9515") / "status" + doAssert $foo == "http://localhost:9515/status" + # isAbsolute tests block: doAssert "www.google.com".parseUri().isAbsolute() == false @@ -515,4 +522,6 @@ when isMainModule: doAssert "https://example.com/about".parseUri().isAbsolute == true doAssert "https://example.com/about/staff.html".parseUri().isAbsolute == true doAssert "https://example.com/about/staff.html?".parseUri().isAbsolute == true - doAssert "https://example.com/about/staff.html?parameters".parseUri().isAbsolute == true \ No newline at end of file + doAssert "https://example.com/about/staff.html?parameters".parseUri().isAbsolute == true + + echo("All good!") \ No newline at end of file |