summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--lib/pure/sugar.nim26
-rw-r--r--tests/stdlib/tsugar.nim333
2 files changed, 229 insertions, 130 deletions
diff --git a/lib/pure/sugar.nim b/lib/pure/sugar.nim
index 5e71ca54b..5cece12b6 100644
--- a/lib/pure/sugar.nim
+++ b/lib/pure/sugar.nim
@@ -53,8 +53,8 @@ proc createProcType(p, b: NimNode): NimNode {.compileTime.} =
   result.add prag
 
 macro `=>`*(p, b: untyped): untyped =
-  ## Syntax sugar for anonymous procedures.
-  ## It also supports pragmas.
+  ## Syntax sugar for anonymous procedures. It also supports pragmas.
+  # TODO: xxx pending #13491: uncomment in runnableExamples
   runnableExamples:
     proc passTwoAndTwo(f: (int, int) -> int): int = f(2, 2)
 
@@ -62,13 +62,16 @@ macro `=>`*(p, b: untyped): untyped =
 
     type
       Bot = object
-        call: proc (name: string): string {.noSideEffect.}
+        call: (string {.noSideEffect.} -> string)
 
     var myBot = Bot()
 
     myBot.call = (name: string) {.noSideEffect.} => "Hello " & name & ", I'm a bot."
     doAssert myBot.call("John") == "Hello John, I'm a bot."
 
+    # let f = () => (discard) # simplest proc that returns void
+    # f()
+
   var
     params = @[ident"auto"]
     name = newEmptyNode()
@@ -140,15 +143,20 @@ macro `=>`*(p, b: untyped): untyped =
                    procType = kind)
 
 macro `->`*(p, b: untyped): untyped =
-  ## Syntax sugar for procedure types.
+  ## Syntax sugar for procedure types. It also supports pragmas.
   runnableExamples:
     proc passTwoAndTwo(f: (int, int) -> int): int = f(2, 2)
-
     # is the same as:
     # proc passTwoAndTwo(f: proc (x, y: int): int): int = f(2, 2)
 
     doAssert passTwoAndTwo((x, y) => x + y) == 4
 
+    proc passOne(f: (int {.noSideEffect.} -> int)): int = f(1)
+    # is the same as:
+    # proc passOne(f: proc (x: int): int {.noSideEffect.}): int = f(1)
+
+    doAssert passOne(x {.noSideEffect.} => x + 1) == 2
+
   result = createProcType(p, b)
 
 macro dump*(x: untyped): untyped =
@@ -216,7 +224,7 @@ macro capture*(locals: varargs[typed], body: untyped): untyped {.since: (1, 1).}
   ## Useful when creating a closure in a loop to capture some local loop variables
   ## by their current iteration values.
   runnableExamples:
-    import std/[strformat, sequtils]
+    import std/strformat
 
     var myClosure: () -> string
     for i in 5..7:
@@ -226,12 +234,6 @@ macro capture*(locals: varargs[typed], body: untyped): untyped {.since: (1, 1).}
             myClosure = () => fmt"{i} * {j} = 42"
     doAssert myClosure() == "6 * 7 = 42"
 
-    let m = @[(s: string) => "to " & s,
-              (s: string) => "not to " & s]
-    let l = m.mapIt(capture(it, (s: string) => it(s)))
-    let r = l.mapIt(it("be"))
-    doAssert fmt"{r[0]}, or {r[1]}" == "to be, or not to be"
-
   var params = @[newIdentNode("auto")]
   let locals = if locals.len == 1 and locals[0].kind == nnkBracket: locals[0]
                else: locals
diff --git a/tests/stdlib/tsugar.nim b/tests/stdlib/tsugar.nim
index 26b4eca1a..968a4ccae 100644
--- a/tests/stdlib/tsugar.nim
+++ b/tests/stdlib/tsugar.nim
@@ -1,120 +1,217 @@
-import sugar
-
-block dup_with_field:
-  type
-    Foo = object
-      col, pos: int
-      name: string
-
-  proc inc_col(foo: var Foo) = inc(foo.col)
-  proc inc_pos(foo: var Foo) = inc(foo.pos)
-  proc name_append(foo: var Foo, s: string) = foo.name &= s
-
-  let a = Foo(col: 1, pos: 2, name: "foo")
-  block:
-    let b = a.dup(inc_col, inc_pos):
-      _.pos = 3
-      name_append("bar")
-      inc_pos
-
-    doAssert(b == Foo(col: 2, pos: 4, name: "foobar"))
-
-  block:
-    let b = a.dup(inc_col, pos = 3, name = "bar"):
-      name_append("bar")
-      inc_pos
-
-    doAssert(b == Foo(col: 2, pos: 4, name: "barbar"))
-
-import algorithm
-
-var a = @[1, 2, 3, 4, 5, 6, 7, 8, 9]
-doAssert dup(a, sort(_)) == sorted(a)
-doAssert a.dup(sort) == sorted(a)
-#Chaining:
-var aCopy = a
-aCopy.insert(10)
-doAssert a.dup(insert(10)).dup(sort()) == sorted(aCopy)
-
-import random
-
-const b = @[0, 1, 2]
-let c = b.dup shuffle()
-doAssert b[0] == 0
-doAssert b[1] == 1
-
-#test collect
-import sets, tables
-
-let data = @["bird", "word"] # if this gets stuck in your head, its not my fault
-doAssert collect(newSeq, for (i, d) in data.pairs: (if i mod 2 == 0: d)) == @["bird"]
-doAssert collect(initTable(2), for (i, d) in data.pairs: {i: d}) == {0: "bird",
-      1: "word"}.toTable
-doAssert initHashSet.collect(for d in data.items: {d}) == data.toHashSet
-
-let x = collect(newSeqOfCap(4)):
-    for (i, d) in data.pairs:
-      if i mod 2 == 0: d
-doAssert x == @["bird"]
-
-# bug #12874
-
-let bug1 = collect(
-    newSeq,
-    for (i, d) in data.pairs:(
+discard """
+  output: '''
+x + y = 30
+'''
+"""
+import std/[sugar, algorithm, random, sets, tables, strutils]
+
+template main() =
+  block: # `=>`
+    block:
+      let f1 = () => 42
+      doAssert f1() == 42
+
+      let f2 = (x: int) => x + 1
+      doAssert f2(42) == 43
+
+      let f3 = (x, y: int) => x + y
+      doAssert f3(1, 2) == 3
+
+      var x = 0
+      let f4 = () => (x = 12)
+      f4()
+      doAssert x == 12
+
+      let f5 = () => (discard) # simplest proc that returns void
+      f5()
+
+    block:
+      proc call1(f: () -> int): int = f()
+      doAssert call1(() => 12) == 12
+
+      proc call2(f: int -> int): int = f(42)
+      doAssert call2(x => x) == 42
+      doAssert call2((x) => x) == 42
+      doAssert call2((x: int) => x) == 42
+
+      proc call3(f: (int, int) -> int): int = f(1, 2)
+      doAssert call3((x, y) => x + y) == 3
+      doAssert call3((x, y: int) => x + y) == 3
+      doAssert call3((x: int, y: int) => x + y) == 3
+
+      var a = 0
+      proc call4(f: int -> void) = f(42)
+      call4((x: int) => (a = x))
+      doAssert a == 42
+
+      proc call5(f: (int {.noSideEffect.} -> int)): int = f(42)
+      doAssert call5(x {.noSideEffect.} => x + 1) == 43
+
+  block: # `->`
+    doAssert $(() -> int) == "proc (): int{.closure.}"
+    doAssert $(float -> int) == "proc (i0: float): int{.closure.}"
+    doAssert $((float) -> int) == "proc (i0: float): int{.closure.}"
+    doAssert $((float, bool) -> int) == "proc (i0: float, i1: bool): int{.closure.}"
+
+    doAssert $(() -> void) == "proc (){.closure.}"
+    doAssert $(float -> void) == "proc (i0: float){.closure.}"
+    doAssert $((float) -> void) == "proc (i0: float){.closure.}"
+    doAssert $((float, bool) -> void) == "proc (i0: float, i1: bool){.closure.}"
+
+    doAssert $(() {.inline.} -> int) == "proc (): int{.inline.}"
+    doAssert $(float {.inline.} -> int) == "proc (i0: float): int{.inline.}"
+    doAssert $((float) {.inline.} -> int) == "proc (i0: float): int{.inline.}"
+    doAssert $((float, bool) {.inline.} -> int) == "proc (i0: float, i1: bool): int{.inline.}"
+
+  block: # capture
+    var closure1: () -> int
+    for i in 0 .. 10:
+      if i == 5:
+        capture i:
+          closure1 = () => i
+    doAssert closure1() == 5
+
+    var closure2: () -> (int, int)
+    for i in 0 .. 10:
+      for j in 0 .. 10:
+        if i == 5 and j == 3:
+          capture i, j:
+            closure2 = () => (i, j)
+    doAssert closure2() == (5, 3)
+
+  block: # dup
+    block dup_with_field:
+      type
+        Foo = object
+          col, pos: int
+          name: string
+
+      proc inc_col(foo: var Foo) = inc(foo.col)
+      proc inc_pos(foo: var Foo) = inc(foo.pos)
+      proc name_append(foo: var Foo, s: string) = foo.name &= s
+
+      let a = Foo(col: 1, pos: 2, name: "foo")
       block:
-        if i mod 2 == 0:
-          d
-        else:
-          d & d
+        let b = a.dup(inc_col, inc_pos):
+          _.pos = 3
+          name_append("bar")
+          inc_pos
+
+        doAssert(b == Foo(col: 2, pos: 4, name: "foobar"))
+
+      block:
+        let b = a.dup(inc_col, pos = 3, name = "bar"):
+          name_append("bar")
+          inc_pos
+
+        doAssert(b == Foo(col: 2, pos: 4, name: "barbar"))
+
+    block:
+      var a = @[1, 2, 3, 4, 5, 6, 7, 8, 9]
+      doAssert dup(a, sort(_)) == sorted(a)
+      doAssert a.dup(sort) == sorted(a)
+      # Chaining:
+      var aCopy = a
+      aCopy.insert(10)
+      doAssert a.dup(insert(10)).dup(sort()) == sorted(aCopy)
+
+    block:
+      when nimvm: discard
+      else:
+        const b = @[0, 1, 2]
+        discard b.dup shuffle()
+        doAssert b[0] == 0
+        doAssert b[1] == 1
+
+  block: # collect
+    let data = @["bird", "word"] # if this gets stuck in your head, its not my fault
+
+    doAssert collect(newSeq, for (i, d) in data.pairs: (if i mod 2 == 0: d)) == @["bird"]
+    doAssert collect(initTable(2), for (i, d) in data.pairs: {i: d}) ==
+      {0: "bird", 1: "word"}.toTable
+    doAssert collect(initHashSet(), for d in data.items: {d}) == data.toHashSet
+
+    block:
+      let x = collect(newSeqOfCap(4)):
+          for (i, d) in data.pairs:
+            if i mod 2 == 0: d
+      doAssert x == @["bird"]
+
+    block: # bug #12874
+      let bug = collect(
+          newSeq,
+          for (i, d) in data.pairs:(
+            block:
+              if i mod 2 == 0:
+                d
+              else:
+                d & d
+            )
       )
-)
-doAssert bug1 == @["bird", "wordword"]
-
-import strutils
-let y = collect(newSeq):
-  for (i, d) in data.pairs:
-    try: parseInt(d) except: 0
-doAssert y == @[0, 0]
-
-let z = collect(newSeq):
-  for (i, d) in data.pairs:
-    case d
-    of "bird": "word"
-    else: d
-doAssert z == @["word", "word"]
-
-proc tforum =
-  let ans = collect(newSeq):
-    for y in 0..10:
-      if y mod 5 == 2:
-        for x in 0..y:
-          x
-tforum()
-
-block:
-  let x = collect:
-    for d in data.items:
-      when d is int: "word"
-      else: d
-  doAssert x == @["bird", "word"]
-doAssert collect(for (i, d) in pairs(data): (i, d)) == @[(0, "bird"), (1, "word")]
-doAssert collect(for d in data.items: (try: parseInt(d) except: 0)) == @[0, 0]
-doAssert collect(for (i, d) in pairs(data): {i: d}) == {1: "word",
-    0: "bird"}.toTable
-doAssert collect(for d in data.items: {d}) == data.toHashSet
-
-# bug #14332
-template foo =
-  discard collect(newSeq, for i in 1..3: i)
-foo()
-
-block: # dumpToString
-  template square(x): untyped = x * x
-  let x = 10
-  doAssert dumpToString(square(x)) == "square(x): x * x = 100"
-  let s = dumpToString(doAssert 1+1 == 2)
-  doAssert "failedAssertImpl" in s
-  let s2 = dumpToString:
-    doAssertRaises(AssertionDefect): doAssert false
-  doAssert "except AssertionDefect" in s2
+      doAssert bug == @["bird", "wordword"]
+
+    block:
+      let y = collect(newSeq):
+        for (i, d) in data.pairs:
+          try: parseInt(d) except: 0
+      doAssert y == @[0, 0]
+
+    block:
+      let z = collect(newSeq):
+        for (i, d) in data.pairs:
+          case d
+          of "bird": "word"
+          else: d
+      doAssert z == @["word", "word"]
+
+    block:
+      proc tforum(): seq[int] =
+        collect(newSeq):
+          for y in 0..10:
+            if y mod 5 == 2:
+              for x in 0..y:
+                x
+      doAssert tforum() == @[0, 1, 2, 0, 1, 2, 3, 4, 5, 6, 7]
+
+    block:
+      let x = collect:
+        for d in data.items:
+          when d is int: "word"
+          else: d
+      doAssert x == @["bird", "word"]
+
+    block:
+      doAssert collect(for (i, d) in pairs(data): (i, d)) == @[(0, "bird"), (1, "word")]
+      doAssert collect(for d in data.items: (try: parseInt(d) except: 0)) == @[0, 0]
+      doAssert collect(for (i, d) in pairs(data): {i: d}) ==
+        {1: "word", 0: "bird"}.toTable
+      doAssert collect(for d in data.items: {d}) == data.toHashSet
+
+    block: # bug #14332
+      template foo =
+        discard collect(newSeq, for i in 1..3: i)
+      foo()
+
+proc mainProc() =
+  block: # dump
+    # symbols in templates are gensym'd
+    let
+      x = 10
+      y = 20
+    dump(x + y) # x + y = 30
+
+  block: # dumpToString
+    template square(x): untyped = x * x
+    let x = 10
+    doAssert dumpToString(square(x)) == "square(x): x * x = 100"
+    let s = dumpToString(doAssert 1+1 == 2)
+    doAssert "failedAssertImpl" in s
+    let s2 = dumpToString:
+      doAssertRaises(AssertionDefect): doAssert false
+    doAssert "except AssertionDefect" in s2
+
+static:
+  main()
+  mainProc()
+main()
+mainProc()