diff options
-rw-r--r-- | lib/pure/sugar.nim | 26 | ||||
-rw-r--r-- | tests/stdlib/tsugar.nim | 333 |
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() |