summary refs log tree commit diff stats
path: root/tests/closure
diff options
context:
space:
mode:
Diffstat (limited to 'tests/closure')
-rw-r--r--tests/closure/tclosure.nim47
-rw-r--r--tests/closure/tclosure2.nim101
-rw-r--r--tests/closure/tclosure3.nim20
-rw-r--r--tests/closure/tclosure4.nim13
-rw-r--r--tests/closure/tclosurebug2.nim194
-rw-r--r--tests/closure/tinterf.nim24
-rw-r--r--tests/closure/tinvalidclosure.nim12
-rw-r--r--tests/closure/tinvalidclosure2.nim14
-rw-r--r--tests/closure/tnamedparamanonproc.nim14
-rw-r--r--tests/closure/tnestedproc.nim12
-rw-r--r--tests/closure/uclosures.nim12
11 files changed, 463 insertions, 0 deletions
diff --git a/tests/closure/tclosure.nim b/tests/closure/tclosure.nim
new file mode 100644
index 000000000..d9e7b8ee4
--- /dev/null
+++ b/tests/closure/tclosure.nim
@@ -0,0 +1,47 @@
+discard """
+  file: "tclosure.nim"
+  output: "2 4 6 8 10"
+  disabled: true
+"""
+# Test the closure implementation
+
+proc map(n: var openarray[int], fn: proc (x: int): int {.closure}) =
+  for i in 0..n.len-1: n[i] = fn(n[i])
+
+proc foldr(n: openarray[int], fn: proc (x, y: int): int {.closure}): int =
+  for i in 0..n.len-1:
+    result = fn(result, n[i])
+
+proc each(n: openarray[int], fn: proc(x: int) {.closure.}) =
+  for i in 0..n.len-1:
+    fn(n[i])
+
+var
+  myData: array[0..4, int] = [0, 1, 2, 3, 4]
+
+proc testA() =
+  var p = 0
+  map(myData, proc (x: int): int =
+                result = x + 1 shl (proc (y: int): int =
+                  return y + p
+                )(0)
+                inc(p))
+
+testA()
+
+myData.each do (x: int):
+  write(stout, x)
+
+#OUT 2 4 6 8 10
+
+type
+  ITest = tuple[
+    setter: proc(v: Int),
+    getter: proc(): int]
+
+proc getInterf(): ITest =
+  var shared: int
+  
+  return (setter: proc (x) = shared = x,
+          getter: proc (): int = return shared)
+
diff --git a/tests/closure/tclosure2.nim b/tests/closure/tclosure2.nim
new file mode 100644
index 000000000..d2c16eac9
--- /dev/null
+++ b/tests/closure/tclosure2.nim
@@ -0,0 +1,101 @@
+discard """
+  output: '''0
+11
+1
+11
+2
+11
+3
+11
+4
+11
+5
+11
+6
+11
+7
+11
+8
+11
+9
+11
+11
+py
+py
+py
+py
+px
+6'''
+"""
+
+when true:
+  proc ax =
+    for xxxx in 0..9:
+      var i = 0
+      proc bx =
+        if i > 10: 
+          echo xxxx
+          return
+        i += 1
+        #for j in 0 .. 0: echo i
+        bx()
+      
+      bx()
+      echo i
+
+  ax()
+
+when true:
+  proc accumulator(start: int): (proc(): int {.closure.}) =
+    var x = start-1
+    #let dummy = proc =
+    #  discard start
+    
+    result = proc (): int = 
+      #var x = 9
+      for i in 0 .. 0: x = x + 1
+      
+      return x
+
+  var a = accumulator(3)
+  let b = accumulator(4)
+  echo a() + b() + a()
+
+
+  proc outer =
+
+    proc py() =
+      # no closure here:
+      for i in 0..3: echo "py"
+
+    py()
+    
+  outer()
+
+
+when true:
+  proc outer2 =
+    var errorValue = 3
+    proc fac[T](n: T): T =
+      if n < 0: result = errorValue
+      elif n <= 1: result = 1
+      else: result = n * fac(n-1)
+  
+    proc px() {.closure.} =
+      echo "px"
+
+    proc py() {.closure.} =
+      echo "py"
+
+    const
+      mapping = {
+        "abc": px,
+        "xyz": py
+      }
+    mapping[0][1]()
+    
+    echo fac(3)
+
+
+  outer2()
+
diff --git a/tests/closure/tclosure3.nim b/tests/closure/tclosure3.nim
new file mode 100644
index 000000000..bb217387f
--- /dev/null
+++ b/tests/closure/tclosure3.nim
@@ -0,0 +1,20 @@
+discard """
+  file: "tclosure3.nim"
+  output: "success"
+"""
+
+proc main =
+  const n = 30
+  for iterations in 0..50_000:
+    var s: seq[proc(): string {.closure.}] = @[]
+    for i in 0 .. n-1:
+      let ii = i
+      s.add(proc(): string = return $(ii*ii))
+    for i in 0 .. n-1:
+      let val = s[i]()
+      if val != $(i*i): echo "bug  ", val
+    
+    if getOccupiedMem() > 3000_000: quit("still a leak!")
+  echo "success"
+
+main()
diff --git a/tests/closure/tclosure4.nim b/tests/closure/tclosure4.nim
new file mode 100644
index 000000000..8e08376b6
--- /dev/null
+++ b/tests/closure/tclosure4.nim
@@ -0,0 +1,13 @@
+
+import json, tables
+
+proc run(json_params: TTable) =
+  let json_elems = json_params["files"].elems
+  # These fail compilation.
+  var files = map(json_elems, proc (x: PJsonNode): string = x.str)
+  #var files = json_elems.map do (x: PJsonNode) -> string: x.str
+  echo "Hey!"
+
+when isMainModule:
+  let text = """{"files": ["a", "b", "c"]}"""
+  run(toTable((text.parseJson).fields))
diff --git a/tests/closure/tclosurebug2.nim b/tests/closure/tclosurebug2.nim
new file mode 100644
index 000000000..ec4f0045b
--- /dev/null
+++ b/tests/closure/tclosurebug2.nim
@@ -0,0 +1,194 @@
+import hashes, math
+
+type
+  TSlotEnum = enum seEmpty, seFilled, seDeleted
+  TKeyValuePair[A, B] = tuple[slot: TSlotEnum, key: A, val: B]
+  TKeyValuePairSeq[A, B] = seq[TKeyValuePair[A, B]]
+
+  TOrderedKeyValuePair[A, B] = tuple[
+    slot: TSlotEnum, next: int, key: A, val: B]
+  TOrderedKeyValuePairSeq[A, B] = seq[TOrderedKeyValuePair[A, B]]
+  TOrderedTable*[A, B] = object ## table that remembers insertion order
+    data: TOrderedKeyValuePairSeq[A, B]
+    counter, first, last: int
+
+const
+  growthFactor = 2
+
+proc mustRehash(length, counter: int): bool {.inline.} =
+  assert(length > counter)
+  result = (length * 2 < counter * 3) or (length - counter < 4)
+
+proc nextTry(h, maxHash: THash): THash {.inline.} =
+  result = ((5 * h) + 1) and maxHash
+
+template rawGetImpl() {.dirty.} =
+  var h: THash = hash(key) and high(t.data) # start with real hash value
+  while t.data[h].slot != seEmpty:
+    if t.data[h].key == key and t.data[h].slot == seFilled:
+      return h
+    h = nextTry(h, high(t.data))
+  result = -1
+
+template rawInsertImpl() {.dirty.} =
+  var h: THash = hash(key) and high(data)
+  while data[h].slot == seFilled:
+    h = nextTry(h, high(data))
+  data[h].key = key
+  data[h].val = val
+  data[h].slot = seFilled
+
+template AddImpl() {.dirty.} =
+  if mustRehash(len(t.data), t.counter): Enlarge(t)
+  RawInsert(t, t.data, key, val)
+  inc(t.counter)
+
+template PutImpl() {.dirty.} =
+  var index = RawGet(t, key)
+  if index >= 0:
+    t.data[index].val = val
+  else:
+    AddImpl()
+
+proc len*[A, B](t: TOrderedTable[A, B]): int {.inline.} =
+  ## returns the number of keys in `t`.
+  result = t.counter
+
+template forAllOrderedPairs(yieldStmt: stmt) {.dirty, immediate.} =
+  var h = t.first
+  while h >= 0:
+    var nxt = t.data[h].next
+    if t.data[h].slot == seFilled: yieldStmt
+    h = nxt
+
+iterator pairs*[A, B](t: TOrderedTable[A, B]): tuple[key: A, val: B] =
+  ## iterates over any (key, value) pair in the table `t` in insertion
+  ## order.
+  forAllOrderedPairs:
+    yield (t.data[h].key, t.data[h].val)
+
+iterator mpairs*[A, B](t: var TOrderedTable[A, B]): tuple[key: A, val: var B] =
+  ## iterates over any (key, value) pair in the table `t` in insertion
+  ## order. The values can be modified.
+  forAllOrderedPairs:
+    yield (t.data[h].key, t.data[h].val)
+
+iterator keys*[A, B](t: TOrderedTable[A, B]): A =
+  ## iterates over any key in the table `t` in insertion order.
+  forAllOrderedPairs:
+    yield t.data[h].key
+
+iterator values*[A, B](t: TOrderedTable[A, B]): B =
+  ## iterates over any value in the table `t` in insertion order.
+  forAllOrderedPairs:
+    yield t.data[h].val
+
+iterator mvalues*[A, B](t: var TOrderedTable[A, B]): var B =
+  ## iterates over any value in the table `t` in insertion order. The values
+  ## can be modified.
+  forAllOrderedPairs:
+    yield t.data[h].val
+
+proc RawGet[A, B](t: TOrderedTable[A, B], key: A): int =
+  rawGetImpl()
+
+proc `[]`*[A, B](t: TOrderedTable[A, B], key: A): B =
+  ## retrieves the value at ``t[key]``. If `key` is not in `t`,
+  ## default empty value for the type `B` is returned
+  ## and no exception is raised. One can check with ``hasKey`` whether the key
+  ## exists.
+  var index = RawGet(t, key)
+  if index >= 0: result = t.data[index].val
+
+proc mget*[A, B](t: var TOrderedTable[A, B], key: A): var B =
+  ## retrieves the value at ``t[key]``. The value can be modified.
+  ## If `key` is not in `t`, the ``EInvalidKey`` exception is raised.
+  var index = RawGet(t, key)
+  if index >= 0: result = t.data[index].val
+  else: raise newException(EInvalidKey, "key not found: " & $key)
+
+proc hasKey*[A, B](t: TOrderedTable[A, B], key: A): bool =
+  ## returns true iff `key` is in the table `t`.
+  result = rawGet(t, key) >= 0
+
+proc RawInsert[A, B](t: var TOrderedTable[A, B], 
+                     data: var TOrderedKeyValuePairSeq[A, B],
+                     key: A, val: B) =
+  rawInsertImpl()
+  data[h].next = -1
+  if t.first < 0: t.first = h
+  if t.last >= 0: data[t.last].next = h
+  t.last = h
+
+proc Enlarge[A, B](t: var TOrderedTable[A, B]) =
+  var n: TOrderedKeyValuePairSeq[A, B]
+  newSeq(n, len(t.data) * growthFactor)
+  var h = t.first
+  t.first = -1
+  t.last = -1
+  while h >= 0:
+    var nxt = t.data[h].next
+    if t.data[h].slot == seFilled: 
+      RawInsert(t, n, t.data[h].key, t.data[h].val)
+    h = nxt
+  swap(t.data, n)
+
+proc `[]=`*[A, B](t: var TOrderedTable[A, B], key: A, val: B) =
+  ## puts a (key, value)-pair into `t`.
+  putImpl()
+
+proc add*[A, B](t: var TOrderedTable[A, B], key: A, val: B) =
+  ## puts a new (key, value)-pair into `t` even if ``t[key]`` already exists.
+  AddImpl()
+
+proc initOrderedTable*[A, B](initialSize=64): TOrderedTable[A, B] =
+  ## creates a new ordered hash table that is empty. `initialSize` needs to be
+  ## a power of two.
+  assert isPowerOfTwo(initialSize)
+  result.counter = 0
+  result.first = -1
+  result.last = -1
+  newSeq(result.data, initialSize)
+
+proc toOrderedTable*[A, B](pairs: openarray[tuple[key: A, 
+                           val: B]]): TOrderedTable[A, B] =
+  ## creates a new ordered hash table that contains the given `pairs`.
+  result = initOrderedTable[A, B](nextPowerOfTwo(pairs.len+10))
+  for key, val in items(pairs): result[key] = val
+
+proc sort*[A, B](t: var TOrderedTable[A,B], 
+                 cmp: proc (x, y: tuple[key: A, val: B]): int {.closure.}) =
+  ## sorts the ordered table so that the entry with the highest counter comes
+  ## first. This is destructive (with the advantage of being efficient)! 
+  ## You must not modify `t` afterwards!
+  ## You can use the iterators `pairs`,  `keys`, and `values` to iterate over
+  ## `t` in the sorted order.
+
+  # we use shellsort here; fast enough and simple
+  var h = 1
+  while true:
+    h = 3 * h + 1
+    if h >= high(t.data): break
+  while true:
+    h = h div 3
+    for i in countup(h, high(t.data)):
+      var j = i
+      #echo(t.data.len, " ", j, " - ", h)
+      #echo(repr(t.data[j-h]))
+      proc rawCmp(x, y: TOrderedKeyValuePair[A, B]): int =
+        if x.slot in {seEmpty, seDeleted} and y.slot in {seEmpty, seDeleted}:
+          return 0
+        elif x.slot in {seEmpty, seDeleted}:
+          return -1
+        elif y.slot in {seEmpty, seDeleted}:
+          return 1
+        else:
+          let item1 = (x.key, x.val)
+          let item2 = (y.key, y.val)
+          return cmp(item1, item2)
+      
+      while rawCmp(t.data[j-h], t.data[j]) <= 0:
+        swap(t.data[j], t.data[j-h])
+        j = j-h
+        if j < h: break
+    if h == 1: break
diff --git a/tests/closure/tinterf.nim b/tests/closure/tinterf.nim
new file mode 100644
index 000000000..726fac9f6
--- /dev/null
+++ b/tests/closure/tinterf.nim
@@ -0,0 +1,24 @@
+discard """
+  output: '''56 66'''
+"""
+
+type
+  ITest = tuple[
+    setter: proc(v: int) {.closure.},
+    getter1: proc(): int {.closure.},
+    getter2: proc(): int {.closure.}]
+
+proc getInterf(): ITest =
+  var shared1, shared2: int
+  
+  return (setter: proc (x: int) = 
+            shared1 = x
+            shared2 = x + 10,
+          getter1: proc (): int = result = shared1,
+          getter2: proc (): int = return shared2)
+
+var i = getInterf()
+i.setter(56)
+
+echo i.getter1(), " ", i.getter2()
+
diff --git a/tests/closure/tinvalidclosure.nim b/tests/closure/tinvalidclosure.nim
new file mode 100644
index 000000000..c06270bfa
--- /dev/null
+++ b/tests/closure/tinvalidclosure.nim
@@ -0,0 +1,12 @@
+discard """
+  line: 12
+  errormsg: "type mismatch: got (proc (int){.closure.})"
+"""
+
+proc ugh[T](x: T) {.closure.} =
+  echo "ugha"
+
+
+proc takeCdecl(p: proc (x: int) {.cdecl.}) = nil
+
+takeCDecl(ugh[int])
diff --git a/tests/closure/tinvalidclosure2.nim b/tests/closure/tinvalidclosure2.nim
new file mode 100644
index 000000000..20e465c12
--- /dev/null
+++ b/tests/closure/tinvalidclosure2.nim
@@ -0,0 +1,14 @@
+discard """
+  line: 10
+  errormsg: "illegal capture 'A'"
+"""
+
+proc outer() = 
+  var A: int
+
+  proc ugh[T](x: T) {.cdecl.} =
+    echo "ugha", A, x
+    
+  ugh[int](12)
+
+outer()
diff --git a/tests/closure/tnamedparamanonproc.nim b/tests/closure/tnamedparamanonproc.nim
new file mode 100644
index 000000000..272b84e91
--- /dev/null
+++ b/tests/closure/tnamedparamanonproc.nim
@@ -0,0 +1,14 @@
+
+type
+  PButton = ref object
+  TButtonClicked = proc(button: PButton) {.nimcall.}
+
+proc newButton*(onClick: TButtonClicked) =
+  nil
+  
+proc main() =
+  newButton(onClick = proc(b: PButton) =
+    var requestomat = 12
+    )
+
+main()
diff --git a/tests/closure/tnestedproc.nim b/tests/closure/tnestedproc.nim
new file mode 100644
index 000000000..49ec6f9a7
--- /dev/null
+++ b/tests/closure/tnestedproc.nim
@@ -0,0 +1,12 @@
+discard """
+  output: "11"
+"""
+
+proc p(x, y: int): int = 
+  result = x + y
+
+echo p((proc (): int = 
+          var x = 7
+          return x)(),
+       (proc (): int = return 4)())
+
diff --git a/tests/closure/uclosures.nim b/tests/closure/uclosures.nim
new file mode 100644
index 000000000..6eea29ca1
--- /dev/null
+++ b/tests/closure/uclosures.nim
@@ -0,0 +1,12 @@
+import unittest
+
+test "loop variables are captured by copy":
+  var funcs: seq[proc (): int {.closure.}] = @[]
+  
+  for i in 0..10:
+    let ii = i
+    funcs.add do -> int: return ii * ii
+
+  check funcs[0]() == 0
+  check funcs[3]() == 9
+