Skip to content

Commit

Permalink
std/pointers; std/chunks; fixes sequtils.applyIt
Browse files Browse the repository at this point in the history
  • Loading branch information
timotheecour committed Jul 1, 2020
1 parent 1440e70 commit f810db8
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 22 deletions.
57 changes: 39 additions & 18 deletions lib/pure/collections/sequtils.nim
Original file line number Diff line number Diff line change
Expand Up @@ -964,26 +964,47 @@ template mapIt*(s: typed, op: untyped): untyped =
op
map(s, f)

template applyIt*(varSeq, op: untyped) =
## Convenience template around the mutable ``apply`` proc to reduce typing.
##
## 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.
##
## See also:
## * `apply proc<#apply,openArray[T],proc(T)_2>`_
## * `mapIt template<#mapIt.t,typed,untyped>`_
##
runnableExamples:
var nums = @[1, 2, 3, 4]
nums.applyIt(it * 3)
assert nums[0] + nums[3] == 15
import std/chunks

for i in low(varSeq) .. high(varSeq):
let it {.inject.} = varSeq[i]
varSeq[i] = op
template evalonceVar(lhs, typ, expr) =
## makes sure `expr` is evaluated once, and no copy is done when using
## lvalues. The only current limitation is when expr is an expression returning
## an openArray and is not a symbol.
runnableExamples:
let s = @[1,2]
let s2 {.evalonce.} = s
doAssert s2[0].unsafeAddr == s[0].unsafeAddr
# this should eventually be moved to std/decls

# when type(expr) is openArray: # hits https://github.com/nim-lang/Nim/issues/12030
type Expr = type(expr)
when Expr is openArray:
static: doAssert typ is type(nil) # we could support but a bit pointless
# caveat: that's the only case that's not sideeffect safe;
# it could be made safe either with 1st class openArray, or with a
# macro that transforms `expr` aka `(body; last)` into:
# `body; let tmp = unsafeAddr(last)`
# template lhs: untyped = expr
let lhs = toMChunk(expr)
else:
when typ is type(nil):
let tmp = addr(expr)
else:
let tmp: ptr typ = addr(expr)
template lhs: untyped = tmp[]

template applyIt*(varSeq, op: untyped) =
#[
A better syntax would be: `var s {.evalonce.} = varSeq` but this
is blocked by 2 issues:
https://github.com/nim-lang/RFCs/issues/220 to allow disinguishing
var vs let (vs const)
and https://github.com/timotheecour/Nim/issues/89, which allows
using pragma macro inside a template
]#
evalonceVar(s, nil, varSeq)
for it {.inject.} in mitems(s):
it = op

template newSeqWith*(len: int, init: untyped): untyped =
## Creates a new sequence of length `len`, calling `init` to initialize
Expand Down
32 changes: 32 additions & 0 deletions lib/std/chunks.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#[
`Slice` would be a better name but unfortunately, `system.Slice` exists.
]#

import std/pointers

type Chunk*[T] = object
data*: ptr T
len*: int # TODO: or lenImpl + template accessor?

type MChunk*[T] = object
data*: ptr T
len*: int

proc toChunk*[T](a: openArray[T]): Chunk[T] =
result = Chunk[T](data: a[0].addr, len: a.len)

# proc toChunk*[T](a: var openArray[T]): var Chunk[T] =
# let x = a[0].unsafeAddr
# # result = Chunk[T](data: a[0].unsafeAddr, len: a.len)
# result = Chunk[T](data: x, len: a.len)

proc toMChunk*[T](a: var openArray[T]): MChunk[T] =
result = MChunk[T](data: a[0].addr, len: a.len)

iterator mitems*[T](a: MChunk[T]): var T =
for i in 0..<a.len:
yield a.data[i]

iterator items*[T](a: MChunk[T] | Chunk[T]): lent T =
for i in 0..<a.len:
yield a.data[i]
20 changes: 20 additions & 0 deletions lib/std/pointers.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
template `+`*[T](p: ptr T, off: int): ptr T =
type T = typeof(p[]) # pending https://github.com/nim-lang/Nim/issues/13527
cast[ptr T](cast[ByteAddress](p) +% off * sizeof(T))

template `-`*[T](p: ptr T, off: int): ptr T =
type T = typeof(p[])
cast[ptr T](cast[ByteAddress](p) -% off * sizeof(T))

template `[]`*[T](p: ptr T, off: int): T =
(p + off)[]

template `[]=`*[T](p: ptr T, off: int, val: T) =
(p + off)[] = val

proc `+=`*[T](p: ptr T, off: int) {.inline.} =
# not a template to avoid double evaluation issues
p = p + off

proc `-=`*[T](p: ptr T, off: int) {.inline.} =
p = p - off
53 changes: 49 additions & 4 deletions tests/stdlib/tsequtils.nim
Original file line number Diff line number Diff line change
Expand Up @@ -369,15 +369,60 @@ block: # foldr tests
assert concatenation == "nimiscool"
doAssert toSeq(1..3).foldr(a + b) == 6 # issue #14404

block: # mapIt + applyIt test
block: # mapIt tests
counter = 0
var
nums = @[1, 2, 3, 4]
strings = nums.identity.mapIt($(4 * it))
doAssert counter == 1
nums.applyIt(it * 3)
assert nums[0] + nums[3] == 15
assert strings[2] == "12"
doAssert strings == @["4", "8", "12", "16"]

block: # applyIt tests
const expected = @[3, 6, 9]
block:
var a = @[1, 2, 3]
a.applyIt(it * 3)
doAssert a == expected

block:
var a = @[1,2,3]
applyIt a, it*3
doAssert a == expected

block:
var count = 0
applyIt (var a = @[1,2,3]; count.inc; a), it*3
doAssert count == 1
doAssert a == expected

block:
proc fn(a: var openArray[int]) =
applyIt(a, it*3)
var a = @[1,2,3]
fn(a)
doAssert a == expected

block:
var count = 0
proc fn(a: var openArray[int]) =
applyIt((count.inc; a), it*3)
var a = @[1,2,3]
fn(a)
doAssert a == expected
doAssert count == 1

block:
template bar() =
let a = @[1,2,3]
applyIt a, it*3
doAssert a == expected
doAssert not compiles(bar()) # because of `let`

block:
template bar() =
proc fn(a: openArray[int]) =
applyIt(a, it*3)
doAssert not compiles(bar()) # because of `let` param

block: # newSeqWith tests
var seq2D = newSeqWith(4, newSeq[bool](2))
Expand Down

0 comments on commit f810db8

Please sign in to comment.