diff options
Diffstat (limited to 'tests/iter')
49 files changed, 2938 insertions, 0 deletions
diff --git a/tests/iter/t1550.nim b/tests/iter/t1550.nim new file mode 100644 index 000000000..c971943ee --- /dev/null +++ b/tests/iter/t1550.nim @@ -0,0 +1,24 @@ +discard """ + targets: "c js" +""" + +type + A[T] = iterator(x: T): T {.gcsafe, closure.} + +iterator aimp[T](x: T): T {.gcsafe, closure.} = + var total = 0 + while (total < 100): + yield total + total += x + +iterator bimp(y: A[int], z:int): int {.gcsafe, closure.} = + for i in y(z): + yield i + +for x in aimp[int](3): + discard x + +var y = aimp[int] +var z = bimp +for x in z(y, 1): + discard x \ No newline at end of file diff --git a/tests/iter/t16076.nim b/tests/iter/t16076.nim new file mode 100644 index 000000000..2eb409068 --- /dev/null +++ b/tests/iter/t16076.nim @@ -0,0 +1,45 @@ +discard """ + targets: "c js" +""" + +proc main() = + block: # bug #17485 + type + O = ref object + i: int + + iterator t(o: O): int = + if o != nil: + yield o.i + yield 0 + + proc m = + var data = "" + for i in t(nil): + data.addInt i + + doAssert data == "0" + + m() + + + block: # bug #16076 + type + R = ref object + z: int + + var data = "" + + iterator foo(x: int; y: R = nil): int {.inline.} = + if y == nil: + yield x + else: + yield y.z + + for b in foo(10): + data.addInt b + + doAssert data == "10" + +static: main() +main() diff --git a/tests/iter/t20891.nim b/tests/iter/t20891.nim new file mode 100644 index 000000000..34deec41b --- /dev/null +++ b/tests/iter/t20891.nim @@ -0,0 +1,28 @@ +import macros, tables + +var mapping {.compileTime.}: Table[string, NimNode] + +macro register(a: static[string], b: typed): untyped = + mapping[a] = b + +macro getPtr(a: static[string]): untyped = + result = mapping[a] + +proc foo() = + iterator it() {.closure.} = + discard + proc getIterPtr(): pointer {.nimcall.} = + rawProc(it) + register("foo", getIterPtr()) + discard getIterPtr() # Comment either this to make it work +foo() # or this + +proc bar() = + iterator it() {.closure.} = + discard getPtr("foo") # Or this + discard + proc getIterPtr(): pointer {.nimcall.} = + rawProc(it) + register("bar", getIterPtr()) + discard getIterPtr() +bar() diff --git a/tests/iter/t21306.nim b/tests/iter/t21306.nim new file mode 100644 index 000000000..4d0396294 --- /dev/null +++ b/tests/iter/t21306.nim @@ -0,0 +1,118 @@ +discard """ + targets: "c js" +""" + +# bug #21306 +type + FutureState {.pure.} = enum + Pending, Finished, Cancelled, Failed + + FutureBase = ref object of RootObj + state: FutureState + error: ref CatchableError + id: uint + + Future[T] = ref object of FutureBase + closure: iterator(f: Future[T]): FutureBase {.raises: [Defect, CatchableError, Exception], gcsafe.} + value: T + +template setupFutureBase() = + new(result) + result.state = FutureState.Pending + +proc newFutureImpl[T](): Future[T] = + setupFutureBase() + +template newFuture[T](fromProc: static[string] = ""): Future[T] = + newFutureImpl[T]() + +proc internalRead[T](fut: Future[T]): T = + when T isnot void: + return fut.value + +template await[T](f: Future[T]): untyped = + when declared(chronosInternalRetFuture): + when not declaredInScope(chronosInternalTmpFuture): + var chronosInternalTmpFuture {.inject.}: FutureBase = f + else: + chronosInternalTmpFuture = f + + yield chronosInternalTmpFuture + + when T isnot void: + cast[type(f)](chronosInternalTmpFuture).internalRead() + +type + VerifierError {.pure.} = enum + Invalid + MissingParent + UnviableFork + Duplicate + ProcessingCallback = proc() {.gcsafe, raises: [Defect].} + BlockVerifier = + proc(signedBlock: int): + Future[VerifierError] {.gcsafe, raises: [Defect].} + + SyncQueueKind {.pure.} = enum + Forward, Backward + + SyncRequest[T] = object + kind: SyncQueueKind + index: uint64 + slot: uint64 + count: uint64 + item: T + + SyncResult[T] = object + request: SyncRequest[T] + data: seq[ref int] + + SyncQueue[T] = ref object + kind: SyncQueueKind + readyQueue: seq[SyncResult[T]] + blockVerifier: BlockVerifier + +iterator blocks[T](sq: SyncQueue[T], + sr: SyncResult[T]): ref int = + case sq.kind + of SyncQueueKind.Forward: + for i in countup(0, len(sr.data) - 1): + yield sr.data[i] + of SyncQueueKind.Backward: + for i in countdown(len(sr.data) - 1, 0): + yield sr.data[i] + +proc push[T](sq: SyncQueue[T]; sr: SyncRequest[T]; data: seq[ref int]; + processingCb: ProcessingCallback = nil): Future[void] {. + stackTrace: off, gcsafe.} = + iterator push_436208182(chronosInternalRetFuture: Future[void]): FutureBase {. + closure, gcsafe, raises: [Defect, CatchableError, Exception].} = + block: + template result(): auto {.used.} = + {.fatal: "You should not reference the `result` variable inside" & + " a void async proc".} + + let item = default(SyncResult[T]) + for blk in sq.blocks(item): + let res = await sq.blockVerifier(blk[]) + + var resultFuture = newFuture[void]("push") + resultFuture.closure = push_436208182 + return resultFuture + +type + SomeTPeer = ref object + score: int + +proc getSlice(): seq[ref int] = + discard + +template smokeTest(kkind: SyncQueueKind, start, finish: uint64, + chunkSize: uint64) = + var queue: SyncQueue[SomeTPeer] + var request: SyncRequest[SomeTPeer] + discard queue.push(request, getSlice()) + +for k in {SyncQueueKind.Forward}: + for item in [(uint64(1181), uint64(1399), 41'u64)]: + smokeTest(k, item[0], item[1], item[2]) \ No newline at end of file diff --git a/tests/iter/t21737.nim b/tests/iter/t21737.nim new file mode 100644 index 000000000..da06faea7 --- /dev/null +++ b/tests/iter/t21737.nim @@ -0,0 +1,22 @@ +discard """ + action: compile +""" + +template mytoSeq*(iter: untyped): untyped = + var result: seq[typeof(iter)]# = @[] + for x in iter: + result.add(x) + result + +iterator test(dir:int): int = + yield 1234 + +iterator walkGlobKinds (): int = + let dir2 = 123 + let it = mytoSeq(test(dir2)) + +proc main()= + let it = iterator(): int= + for path in walkGlobKinds(): + yield path +main() diff --git a/tests/iter/t22148.nim b/tests/iter/t22148.nim new file mode 100644 index 000000000..9954eed87 --- /dev/null +++ b/tests/iter/t22148.nim @@ -0,0 +1,15 @@ +discard """ + action: compile +""" + +import std/memfiles + +# bug #22148 +proc make*(input: string) = + var inp = memfiles.open(input) + for line in memSlices(inp): + let lineF = MemFile(mem: line.data, size: line.size) + for word in memSlices(lineF, ','): + discard + +make("") # Must call to trigger diff --git a/tests/iter/t22548.nim b/tests/iter/t22548.nim new file mode 100644 index 000000000..b9abb75d0 --- /dev/null +++ b/tests/iter/t22548.nim @@ -0,0 +1,21 @@ +discard """ + action: compile +""" + +type Xxx[T] = object + +iterator x(v: string): char = + var v2: Xxx[int] + + var y: v2.T + + echo y + +proc bbb(vv: string): proc () = + proc xxx() = + for c in x(vv): + echo c + + return xxx + +bbb("test")() diff --git a/tests/iter/t22619.nim b/tests/iter/t22619.nim new file mode 100644 index 000000000..6a98391f3 --- /dev/null +++ b/tests/iter/t22619.nim @@ -0,0 +1,83 @@ +# bug #22619 + +when false: # todo fixme + block: + type + Resource = object + value: int + + Object = object + r {.cursor.}: Resource + s {.cursor.}: seq[Resource] + + var numDestroy = 0 + + proc `=copy`(x: var Resource, y: Resource) {.error.} # disallow full copies + proc `=destroy`(x: Resource) = + inc numDestroy + + proc test() = + # perform the test in procedure so that globals aren't used (their different + # semantics with regards to destruction would interfere) + var + r = Resource(value: 1) # initialize a resource + s = @[Resource(value: 2)] + + # make sure no copy is required in the initializer expression: + var o = Object(r: r, s: s) + + # copying the object doesn't perform a full copy of the cursor fields: + var o2 = o + discard addr(o2) # prevent `o2` from being turned into a cursor + + # check that the fields were shallow-copied: + doAssert o2.r.value == 1 + doAssert o2.s[0].value == 2 + + # make sure no copy is required with normal field assignments: + o.r = r + o.s = s + + + # when `o` and `o2` are destroyed, their destructor must not be called on + # their fields + + test() + + # one call for the `r` local and one for the object in `s` + doAssert numDestroy == 2 + +block: + type Value = distinct int + + var numDestroy = 0 + + when defined(gcRefc): + proc `=destroy`(x: var Value) = + inc numDestroy + else: + proc `=destroy`(x: Value) = + inc numDestroy + + iterator iter(s: seq[Value]): int {.closure.} = + # because it is used across yields, `s2` is lifted into the iterator's + # environment. Since non-ref cursors in object didn't have their hooks + # disabled inside the environments lifted hooks, this led to double + # frees + var s2 {.cursor.} = s + var i = 0 + let L = s2.len + while i < L: + yield s2[i].int + inc i + + proc test() = + var s = @[Value(1), Value(2)] + let cl = iter + # make sure resuming the iterator works: + doAssert cl(s) == 1 + doAssert cl(s) == 2 + doAssert cl(s) == 0 + + test() + doAssert numDestroy == 2 diff --git a/tests/iter/t2771.nim b/tests/iter/t2771.nim new file mode 100644 index 000000000..71a8a9dcd --- /dev/null +++ b/tests/iter/t2771.nim @@ -0,0 +1,25 @@ +discard """ + targets: "c js" +""" + +template t1(i: int): int= + i+1 +template t2(i: int): int= + i+1 + +doAssert t1(10).t2() == 12 + + +template it1(i: int): iterator(): int = + iterator result(): int {.closure, gensym.} = + yield i+1 + result + +template it2(iter: iterator(): int): iterator(): int = + iterator result(): int {.closure, gensym.} = + yield iter()+1 + result + +let x2 = it1(10).it2() + +doAssert x2() == 12 diff --git a/tests/iter/tanoniter1.nim b/tests/iter/tanoniter1.nim new file mode 100644 index 000000000..fee16497f --- /dev/null +++ b/tests/iter/tanoniter1.nim @@ -0,0 +1,33 @@ +discard """ + targets: "c js" + output: '''1 +2 +3 +4 +1 +2''' +""" + +proc factory(a, b: int): iterator (): int = + iterator foo(): int {.closure.} = + var x = a + while x <= b: + yield x + inc x + return foo + +proc factory2(a, b: int): iterator (): int = + return iterator (): int = + var x = a + while x <= b: + yield x + inc x + +let foo = factory(1, 4) + +for f in foo(): + echo f + +let foo2 = factory2(1,2) + +for f in foo2(): echo f diff --git a/tests/iter/tarrayiter.nim b/tests/iter/tarrayiter.nim new file mode 100644 index 000000000..eb7ba591a --- /dev/null +++ b/tests/iter/tarrayiter.nim @@ -0,0 +1,14 @@ +block: + iterator `[]`(a: int, r: int): int = + for q in 0 .. r: + yield a + + for val in 10[2]: discard + + type Custom = distinct string + + iterator `[]`(a: Custom, r: int): char = + for q in 0 .. r: + yield a.string[q] + + for val in Custom("test")[2]: discard \ No newline at end of file diff --git a/tests/iter/tchainediterators.nim b/tests/iter/tchainediterators.nim new file mode 100644 index 000000000..796672783 --- /dev/null +++ b/tests/iter/tchainediterators.nim @@ -0,0 +1,41 @@ +discard """ + output: '''16 +32 +48 +64 +128 +192 +''' + disabled: "true" +""" + +# This all relies on non-documented and questionable features. + +iterator gaz(it: iterator{.inline.}): type(it) = + for x in it: + yield x*2 + +iterator baz(it: iterator{.inline.}): auto = + for x in gaz(it): + yield x*2 + +type T1 = auto + +iterator bar(it: iterator: T1{.inline.}): T1 = + for x in baz(it): + yield x*2 + +iterator foo[T](x: iterator: T{.inline.}): T = + for e in bar(x): + yield e*2 + +var s = @[1, 2, 3] + +# pass an iterator several levels deep: +for x in s.items.foo: + echo x + +# use some complex iterator as an input for another one: +for x in s.items.baz.foo: + echo x + diff --git a/tests/iter/tclosureiters.nim b/tests/iter/tclosureiters.nim new file mode 100644 index 000000000..4a2639852 --- /dev/null +++ b/tests/iter/tclosureiters.nim @@ -0,0 +1,176 @@ +discard """ + targets: "c js" + output: '''0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +5 5 +7 7 +9 9 +0 +0 +0 +0 +1 +2 +70 +0 +(1, 1) +(1, 2) +(1, 3) +(2, 1) +(2, 2) +(2, 3) +(3, 1) +(3, 2) +(3, 3) +''' +""" + +when true: + proc main() = + let + lo=0 + hi=10 + + iterator itA(): int = + for x in lo..hi: + yield x + + for x in itA(): + echo x + + var y: int + + iterator itB(): int = + while y <= hi: + yield y + inc y + + y = 5 + for x in itB(): + echo x, " ", y + inc y + + main() + + +iterator infinite(): int {.closure.} = + var i = 0 + while true: + yield i + inc i + +iterator take[T](it: iterator (): T, numToTake: int): T {.closure.} = + var i = 0 + for x in it(): + if i >= numToTake: + break + yield x + inc i + +# gives wrong reasult (3 times 0) +for x in infinite.take(3): + echo x + +# does what we want +let inf = infinite +for x in inf.take(3): + echo x + +# bug #3583 +proc foo(f: (iterator(): int)) = + for i in f(): echo i + +let fIt = iterator(): int = yield 70 +foo fIt + +# bug #5321 + +proc lineIter*(filename: string): iterator(): string = + result = iterator(): string {.closure.} = + for line in lines(filename): + yield line + +proc unused = + var count = 0 + let iter = lineIter("temp10.nim") + for line in iter(): + count += 1 + +iterator lineIter2*(filename: string): string {.closure.} = + var f = open(filename, bufSize=8000) + defer: close(f) # <-- commenting defer "solves" the problem + var res = newStringOfCap(80) + while f.readLine(res): yield res + +proc unusedB = + var count = 0 + for line in lineIter2("temp10.nim"): + count += 1 + +# bug #5519 +import os, algorithm + +iterator filesIt(path: string): auto {.closure.} = + var files = newSeq[string]() + var dirs = newSeq[string]() + for k, p in os.walkDir(path): + let (_, n, e) = p.splitFile + if e != "": + continue + case k + of pcFile, pcLinkToFile: + files.add(n) + else: + dirs.add(n) + files.sort(system.cmp) + dirs.sort(system.cmp) + for f in files: + yield f + + for d in dirs: + files = newSeq[string]() + for k, p in os.walkDir(path / d): + let (_, n, e) = p.splitFile + if e != "": + continue + case k + of pcFile, pcLinkToFile: + files.add(n) + else: + discard + files.sort(system.cmp) + let prefix = path.splitPath[1] + for f in files: + yield prefix / f + +# bug #13815 +when not defined(js): + var love = iterator: int {.closure.} = + yield cast[type( + block: + var a = 0 + yield a + a)](0) + + for i in love(): + echo i +else: + echo 0 + +# bug #18474 +iterator pairs(): (int, int) {.closure.} = + for i in 1..3: + for j in 1..3: + yield (i, j) + +for pair in pairs(): + echo pair diff --git a/tests/iter/tcountup.nim b/tests/iter/tcountup.nim new file mode 100644 index 000000000..5f75e653c --- /dev/null +++ b/tests/iter/tcountup.nim @@ -0,0 +1,25 @@ +discard """ + output: ''' +0123456789 +0.0 +''' +""" + +# Test new countup + +for i in 0 ..< 10'i64: + stdout.write(i) + +echo() + +# 11099 + +var + x: uint32 + y: float + +for i in 0 ..< x: + if i == 1: echo i + y += 1 + +echo y diff --git a/tests/iter/tgeniteratorinblock.nim b/tests/iter/tgeniteratorinblock.nim new file mode 100644 index 000000000..2ab903996 --- /dev/null +++ b/tests/iter/tgeniteratorinblock.nim @@ -0,0 +1,54 @@ +discard """ + output: '''30 +60 +90 +150 +180 +210 +240 +60 +180 +240 +[60, 180, 240] +[60, 180]''' +""" +import std/enumerate + +template map[T; Y](i: iterable[T], fn: proc(x: T): Y): untyped = + iterator internal(): Y {.gensym.} = + for it in i: + yield fn(it) + internal() + +template filter[T](i: iterable[T], fn: proc(x: T): bool): untyped = + iterator internal(): T {.gensym.} = + for it in i: + if fn(it): + yield it + internal() + +template group[T](i: iterable[T], amount: static int): untyped = + iterator internal(): array[amount, T] {.gensym.} = + var val: array[amount, T] + for ind, it in enumerate i: + val[ind mod amount] = it + if ind mod amount == amount - 1: + yield val + internal() + +var a = [10, 20, 30, 50, 60, 70, 80] + +proc mapFn(x: int): int = x * 3 +proc filterFn(x: int): bool = x mod 20 == 0 + +for x in a.items.map(mapFn): + echo x + +for y in a.items.map(mapFn).filter(filterFn): + echo y + +for y in a.items.map(mapFn).filter(filterFn).group(3): + echo y + +for y in a.items.map(mapFn).filter(filterFn).group(2): + echo y diff --git a/tests/iter/timplicit_auto.nim b/tests/iter/timplicit_auto.nim new file mode 100644 index 000000000..1b9f06843 --- /dev/null +++ b/tests/iter/timplicit_auto.nim @@ -0,0 +1,18 @@ +# bug #1838 + +type State = enum Empty, Tree, Fire + +const + disp: array[State, string] = [" ", "\e[32m/\\\e[m", "\e[07;31m/\\\e[m"] + +proc univ(x, y: int): State = Tree + +var w, h = 30 + +iterator fields(a = (0,0), b = (h-1,w-1)): auto = + for y in max(a[0], 0) .. min(b[0], h-1): + for x in max(a[1], 0) .. min(b[1], w-1): + yield (y,x) + +for y,x in fields(): + doAssert disp[univ(x, y)] == disp[Tree] diff --git a/tests/iter/titer.nim b/tests/iter/titer.nim new file mode 100644 index 000000000..b03d43f36 --- /dev/null +++ b/tests/iter/titer.nim @@ -0,0 +1,147 @@ +discard """ +output: ''' +testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttest2!test3?hi +what's +your +name +hi +what's +your +name +''' +""" + +# Test the new iterators + +iterator xrange(fromm, to: int, step = 1): int = + var a = fromm + while a <= to: + yield a + inc(a, step) + +iterator interval[T](a, b: T): T = + var x = a + while x <= b: + yield x + inc(x) + +# +#iterator lines(filename: string): (line: string) = +# var +# f: tTextfile +# shouldClose = open(f, filename) +# if shouldClose: +# setSpace(line, 256) +# while readTextLine(f, line): +# yield line +# finally: +# if shouldClose: close(f) +# + +for i in xrange(0, 5): + for k in xrange(1, 7): + write(stdout, "test") + +for j in interval(45, 45): + write(stdout, "test2!") + write(stdout, "test3?") + +for x in items(["hi", "what's", "your", "name"]): + echo(x) + +const + stringArray = ["hi", "what's", "your", "name"] + +for i in 0..len(stringArray)-1: + echo(stringArray[i]) + +# bug #15360 + +type Rule[T] = (int, T) + +var t: seq[Rule[int]] +for (c, t) in t: + discard + + + +import std/sugar + +# bug #14165 +iterator log_nodups_hamming(): int {.inline.} = + let lb3 = 1 + let lb4 = 123 + proc mul3(): int = lb3 + lb4 + yield mul3() + +for h in log_nodups_hamming(): + break +for h in log_nodups_hamming(): + break +for h in log_nodups_hamming(): + break + +# bug #18536 +iterator envPairs*(): int = + var foo: seq[int] + proc fun() = + foo = @[] + fun() + yield 3 + +proc main() = + for a in envPairs(): + discard + for a in envPairs(): + discard +static: main() +main() + +# bug #6269 +iterator makeFn(outer_val: int): proc(a: int): int = + for i in 0..1: + yield proc(a:int): int = + return a + i.int + +let v1 = 42 + +let res = collect: + for fn1 in makeFn(v1): + let v2 = fn1(v1) + for fn2 in makeFn(v2): + fn2(v2) + +doAssert res == @[42, 43, 43, 44] + +block: # bug #21110 + iterator p(): int = + when nimvm: + yield 0 + else: + yield 0 + + template foo = + for k in p(): + let m = "" + proc e() = discard m & "" + e() + static: foo() + foo() + + +# bug #15924 +iterator walk(): (int, int) {.closure.} = + yield (10,11) + +for (i,j) in walk(): + doAssert i == 10 + +proc main123() = + let x = false + iterator it(): (bool, bool) {.closure.} = # normally {.closure.} here makes #21476 work + discard x + + for (_, _) in it(): + discard + +main123() diff --git a/tests/iter/titer10.nim b/tests/iter/titer10.nim new file mode 100644 index 000000000..ae36aa519 --- /dev/null +++ b/tests/iter/titer10.nim @@ -0,0 +1,51 @@ +discard """ + output: '''3 +2 +5 +1 +@[@[0, 0], @[0, 1]] +@[@[0, 0], @[0, 1]] +@[@[2, 2], @[2, 3]] +@[@[2, 2], @[2, 3]]''' +""" + +when true: + # bug #2604 + + import algorithm + + iterator byDistance*[int]( ints: openArray[int], base: int ): int = + var sortable = @ints + + sortable.sort do (a, b: int) -> int: + result = cmp( abs(base - a), abs(base - b) ) + + for val in sortable: + yield val + + when true: + proc main = + for val in byDistance([2, 3, 5, 1], 3): + echo val + main() + +when true: + # bug #1527 + + import sequtils + + let thread = @[@[0, 0], + @[0, 1], + @[2, 2], + @[2, 3]] + + iterator threadUniqs(seq1: seq[seq[int]]): seq[seq[int]] = + for i in 0 ..< seq1.len: + block: + let i = i + yield seq1.filter do (x: seq[int]) -> bool: x[0] == seq1[i][0] + proc main2 = + for uniqs in thread.threadUniqs: + echo uniqs + + main2() diff --git a/tests/iter/titer11.nim b/tests/iter/titer11.nim new file mode 100644 index 000000000..153b3c29a --- /dev/null +++ b/tests/iter/titer11.nim @@ -0,0 +1,40 @@ +discard """ +targets: "c js" +output: ''' +[ +1 +2 +3 +] +''' +""" + +proc represent(i: int): iterator(): string = + result = iterator(): string = + yield $i + +proc represent(s: seq[int]): iterator(): string = + result = iterator(): string = + yield "[" + for i in s: + var events = represent(i) + for event in events(): + yield event + yield "]" + +let s = @[1, 2, 3] +var output = represent(s) + +for item in output(): + echo item + + +#------------------------------------------------------------------------------ +# Issue #12747 + +type + ABC = ref object + arr: array[0x40000, pointer] +let a = ABC() +for a in a.arr: + assert a == nil \ No newline at end of file diff --git a/tests/iter/titer12.nim b/tests/iter/titer12.nim new file mode 100644 index 000000000..f264a0e82 --- /dev/null +++ b/tests/iter/titer12.nim @@ -0,0 +1,83 @@ +discard """ +targets: "c js" +output: ''' +Selecting 2 +1.0 +Selecting 4 +2.0 +''' +""" + + +# bug #5522 +import macros, sugar, sequtils + +proc tryS(f: () -> void): void = + (try: f() except: discard) + +template trySTImpl(body: untyped): untyped = + tryS do() -> auto: + `body` + +macro tryST*(body: untyped): untyped = + var b = if body.kind == nnkDo: body[^1] else: body + result = quote do: + trySTImpl((block: + `b` + )) + +iterator testIt(): int {.closure.} = + for x in 0..10: + yield x + +var xs = newSeq[int]() +proc test = tryST do: + for x in testIt(): + xs.add(x) + +test() + +doAssert xs == toSeq(0..10) + + + +# bug #5690 +proc filter[T](it: (iterator : T), f: proc(x: T): bool): (iterator : T) = + return iterator (): T {.closure.} = + for x in it(): + if f(x): + yield x + +proc len[T](it : iterator : T) : Natural = + for i in it(): + result += 1 + +proc simpleSeqIterator(s :seq[int]) : iterator : int = + iterator it: int {.closure.} = + for x in s: + yield x + result = it + +let a = newSeq[int](99) + +doAssert len(simpleSeqIterator(a).filter(proc(x : int) : bool = true)) == 99 + + + +# bug #5340 +proc where[A](input: seq[A], filter: (A) -> bool): iterator (): A = + result = iterator (): A {.closure.} = + for item in input: + if filter(item): + yield item + +proc select[A,B](input: iterator(): A {.closure.}, selector: (A) -> B): iterator (): B {.closure.} = + result = iterator (): B = + for item in input(): + echo "Selecting " & $item + yield selector(item) + +let query = @[1,2,3,4].where(x=>x mod 2==0).select((x)=>x/2) + +for i in query(): + echo $i diff --git a/tests/iter/titer13.nim b/tests/iter/titer13.nim new file mode 100644 index 000000000..086c40ca4 --- /dev/null +++ b/tests/iter/titer13.nim @@ -0,0 +1,83 @@ +discard """ + output: '''b yields +c yields +a returns +b yields +b returns +c yields + + +1 +2 +3 +4 +''' +""" + +block: + template tloop(iter: untyped) = + for i in iter(): + echo i + + template twhile(iter: untyped) = + let it = iter + while not finished(it): + echo it() + + iterator a(): auto {.closure.} = + if true: return "a returns" + yield "a yields" + + iterator b(): auto {.closure.} = + yield "b yields" + if true: return "b returns" + + iterator c(): auto {.closure.} = + yield "c yields" + if true: return + + iterator d(): auto {.closure.} = + if true: return + yield "d yields" + + tloop(a) + tloop(b) + tloop(c) + tloop(d) + twhile(a) + twhile(b) + twhile(c) + twhile(d) + +block: + iterator a: auto = + yield 1 + for x in a(): + echo x + + let b = iterator: int = + yield 2 + for x in b(): + echo x + + let c = iterator: auto = + yield 3 + for x in c(): + echo x + +block: + iterator myIter2(): auto {.closure.} = + yield 4 + for a in myIter2(): + echo a + +block t5859: + proc flatIterator[T](s: openArray[T]): auto {.noSideEffect.}= + result = iterator(): auto = + when (T is not seq|array): + for item in s: + yield item + else: + yield 123456 + # issue #5859 + let it = flatIterator(@[@[1,2], @[3,4]]) diff --git a/tests/iter/titer14.nim b/tests/iter/titer14.nim new file mode 100644 index 000000000..7e483bbae --- /dev/null +++ b/tests/iter/titer14.nim @@ -0,0 +1,7 @@ +proc f() = + var s: seq[int] + iterator a(): int = + for x in s: yield x + + iterator b(): int = + for x in a(): yield x diff --git a/tests/iter/titer2.nim b/tests/iter/titer2.nim new file mode 100644 index 000000000..975cc786c --- /dev/null +++ b/tests/iter/titer2.nim @@ -0,0 +1,66 @@ +discard """ + output: '''true +3 +4 +5 +0 +1 +2 +3 +4''' + cmd: "nim $target --gc:none --hints:on --warnings:off $options $file" +""" + +import hashes + +type + TSlotEnum = enum seEmpty, seFilled, seDeleted + TKeyValuePair[A, B] = tuple[slot: TSlotEnum, key: A, val: B] + TKeyValuePairSeq[A, B] = seq[TKeyValuePair[A, B]] + TTable*[A, B] {.final.} = object + data: TKeyValuePairSeq[A, B] + counter: int + +iterator mycountup(a, b: int): int = + var res = a + while res <= b: + yield res + inc(res) + +when true: + iterator pairs*[A, B](t: TTable[A, B]): tuple[key: A, val: B] = + ## iterates over any (key, value) pair in the table `t`. + for h in mycountup(0, high(t.data)): + var k = t.data[h].key + if t.data[h].slot == seFilled: yield (k, t.data[h].val) +else: + iterator pairs*(t: TTable[int, string]): tuple[key: int, val: string] = + ## iterates over any (key, value) pair in the table `t`. + for h in mycountup(0, high(t.data)): + var k = t.data[h].key + if t.data[h].slot == seFilled: yield (k, t.data[h].val) + +proc initTable*[A, B](initialSize=64): TTable[A, B] = + ## creates a new hash table that is empty. `initialSize` needs to be + ## a power of two. + result.counter = 0 + newSeq(result.data, initialSize) + +block Test1: + # generic cache does not instantiate the same iterator[types] twice. This + # means we have only one instantiation of 'h'. However, this is the same for + # a non-generic iterator! + + var t = initTable[int, string]() + for k, v in t.pairs: discard + for k, v in t.pairs: discard + +echo "true" + +# bug #1560 +for i in @[3, 4, 5]: + echo($i) + +# bug #6992 +for i in 0 ..< 5u32: + echo i diff --git a/tests/iter/titer3.nim b/tests/iter/titer3.nim new file mode 100644 index 000000000..defd56c98 --- /dev/null +++ b/tests/iter/titer3.nim @@ -0,0 +1,54 @@ +discard """ + output: '''1231 +4 +6 +8 +-------- +4 +6 +8 +''' +""" + +iterator count1_3: int = + yield 1 + yield 2 + yield 3 + +for x in count1_3(): + write(stdout, $x) + +# yield inside an iterator, but not in a loop: +iterator iter1(a: openArray[int]): int = + yield a[0] + +var x = [[1, 2, 3], [4, 5, 6]] +for y in iter1(x[0]): write(stdout, $y) +writeLine(stdout, "") + +# ensure closure and inline iterators have the same behaviour regarding +# parameter passing + +iterator clo(a: int): int {.closure.} = + yield 0+a + yield 1+a + yield 2+a + +iterator inl(a: int): int {.inline.} = + yield 0+a + yield 1+a + yield 2+a + +proc main = + var y = 4 + for i in clo(y): + echo i + inc y + + echo "--------" + y = 4 + for i in inl(y): + echo i + inc y + +main() diff --git a/tests/iter/titer4.nim b/tests/iter/titer4.nim new file mode 100644 index 000000000..912883a63 --- /dev/null +++ b/tests/iter/titer4.nim @@ -0,0 +1,8 @@ +discard """ + errormsg: "iterator within for loop context expected" + file: "titer4.nim" + line: 7 +""" +# implicit items/pairs, but not if we have 3 for loop vars: +for x, y, z in {'a'..'z'}: #ERROR_MSG iterator within for loop context expected + nil diff --git a/tests/iter/titer5.nim b/tests/iter/titer5.nim new file mode 100644 index 000000000..cb691ffdb --- /dev/null +++ b/tests/iter/titer5.nim @@ -0,0 +1,13 @@ +discard """ + output: "abcxyz" +""" +# Test method call syntax for iterators: +import strutils + +const lines = """abc xyz""" + +for x in lines.split(): + stdout.write(x) + +#OUT abcxyz +stdout.write "\n" diff --git a/tests/iter/titer6.nim b/tests/iter/titer6.nim new file mode 100644 index 000000000..69a10d868 --- /dev/null +++ b/tests/iter/titer6.nim @@ -0,0 +1,35 @@ +discard """ + output: "000" +""" +# Test iterator with more than 1 yield statement + +import strutils + +iterator tokenize2(s: string, seps: set[char] = Whitespace): tuple[ + token: string, isSep: bool] = + var i = 0 + while i < s.len: + var j = i + if s[j] in seps: + while j < s.len and s[j] in seps: inc(j) + if j > i: + yield (substr(s, i, j-1), true) + else: + while j < s.len and s[j] notin seps: inc(j) + if j > i: + yield (substr(s, i, j-1), false) + i = j + +for word, isSep in tokenize2("ta da", WhiteSpace): + var titer2TestVar = 0 + stdout.write(titer2TestVar) + +proc wordWrap2(s: string, maxLineWidth = 80, + splitLongWords = true, + seps: set[char] = Whitespace, + newLine = "\n"): string = + result = "" + for word, isSep in tokenize2(s, seps): + var w = 0 + +stdout.write "\n" diff --git a/tests/iter/titer7.nim b/tests/iter/titer7.nim new file mode 100644 index 000000000..9cba3038d --- /dev/null +++ b/tests/iter/titer7.nim @@ -0,0 +1,53 @@ +discard """ + output: '''--- evens +2 +4 +6 +8 +--- squares +1 +4 +9 +16 +25 +36 +49 +64 +81 +''' +""" + +iterator `/`[T](sequence: seq[T], + filter: proc(e:T):bool {.closure.}) : T = + for element in sequence: + if (filter(element)): + yield element + +iterator `>>`[I,O](sequence: seq[I], + map: proc(e:I):O {.closure.}) : O = + for element in sequence: + yield map(element) + +iterator `/>>`[I,O](sequence: seq[I], + filtermap:tuple[ + f:proc(e:I):bool {.closure.}, + m:proc(e:I):O {.closure.}]) : O = + for element in sequence: + if (filtermap.f(element)): + yield filtermap.m(element) + +proc isEven(x:int): bool = + (x and 1) == 0 + +proc square(x:int): int = + x * x + +let list = @[1,2,3,4,5,6,7,8,9] + +echo ("--- evens") +for item in list / isEven: echo(item) +echo ("--- squares") +for item in list >> square: echo(item) +#echo ("--- squares of evens, only") +# next line doesn't compile. Generic types are not inferred +#for item in list />> (isEven, square) : echo(item) diff --git a/tests/iter/titer8.nim b/tests/iter/titer8.nim new file mode 100644 index 000000000..7b6d7b6de --- /dev/null +++ b/tests/iter/titer8.nim @@ -0,0 +1,117 @@ +discard """ + output: '''tada +1 +2 +3 +ta da1 1 +1 2 +1 3 +2 1 +2 2 +2 3 +3 1 +3 2 +3 3 +0 +1 +2 +a1: A +a2: A +a1: B +a2: B +a1: C +a2: C +a1: D''' +""" +# Test first class iterator: + +import strutils + +iterator tokenize2(s: string, seps: set[char] = Whitespace): tuple[ + token: string, isSep: bool] {.closure.} = + var i = 0 + while i < s.len: + var j = i + if s[j] in seps: + while j < s.len and s[j] in seps: inc(j) + if j > i: + yield (substr(s, i, j-1), true) + else: + while j < s.len and s[j] notin seps: inc(j) + if j > i: + yield (substr(s, i, j-1), false) + i = j + +iterator count3(): int {.closure.} = + yield 1 + yield 2 + yield 3 + +for word, isSep in tokenize2("ta da", WhiteSpace): + if not isSep: + stdout.write(word) +echo "" + +proc inProc() = + for c in count3(): + echo c + + for word, isSep in tokenize2("ta da", WhiteSpace): + stdout.write(word) + + for c in count3(): + for d in count3(): + echo c, " ", d + + +inProc() + +iterator count0(): int {.closure.} = + # note: doesn't require anything in its closure (except 'state') + yield 0 + +iterator count2(): int {.closure.} = + # note: requires 'x' in its closure + var x = 1 + yield x + inc x + yield x + +# a first class iterator has the type 'proc {.closure.}', but maybe +# it shouldn't: +proc invoke(iter: iterator(): int {.closure.}) = + for x in iter(): echo x + +invoke(count0) +invoke(count2) + + +# simple tasking: +type + TTask = iterator (ticker: int) + +iterator a1(ticker: int) {.closure.} = + echo "a1: A" + yield + echo "a1: B" + yield + echo "a1: C" + yield + echo "a1: D" + +iterator a2(ticker: int) {.closure.} = + echo "a2: A" + yield + echo "a2: B" + yield + echo "a2: C" + +proc runTasks(t: varargs[TTask]) = + var ticker = 0 + while true: + let x = t[ticker mod t.len] + if finished(x): break + x(ticker) + inc ticker + +runTasks(a1, a2) diff --git a/tests/iter/titer9.nim b/tests/iter/titer9.nim new file mode 100644 index 000000000..99874e70a --- /dev/null +++ b/tests/iter/titer9.nim @@ -0,0 +1,20 @@ +discard """ + output: '''5 +14 +0''' +""" + +iterator count[T](x: T, skip: bool): int {.closure.} = + if skip: return x+10 + else: yield x+1 + + if skip: return x+10 + else: yield x+2 + +proc takeProc[T](x: iterator (x: T, skip: bool): int) = + echo x(4, false) + echo x(4, true) + echo x(4, false) + +takeProc(count[int]) + diff --git a/tests/iter/titer_issues.nim b/tests/iter/titer_issues.nim new file mode 100644 index 000000000..c82b3902d --- /dev/null +++ b/tests/iter/titer_issues.nim @@ -0,0 +1,411 @@ +discard """ + target: "c js" + output: ''' +0 +1 +2 +3 +4 +1 +start +false +0 +1 +2 +end +@[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 18, 20, 21, 24, 27, 30, 36, 40, 42] +1002 +0 +1 +2 +7 +9002 +9004 +9006 +9008 +9010 +9012 +9014 +9016 +9018 +@[1, 2] +@[1, 2, 3] +1 +nested finally +outer finally +nested finally +outer finally +nested finally +outer finally +nested finally +outer finally +In defer +trying +exception caught +finally block +''' +""" + + +import sequtils, strutils + + +block t338: + proc moo(): iterator (): int = + iterator fooGen: int {.closure.} = + while true: + yield result + result.inc + return fooGen + + var foo = moo() + + for i in 0 .. 4: + echo foo() + + + +block t8041: + iterator xy[T](a: T, b: set[T]): T = + if a in b: + yield a + + for a in xy(1'i8, {}): + for b in xy(a, {}): + echo a + + + +block t3837_chained: + iterator t1(): int {.closure.} = + yield 1 + + iterator t2(): int {.closure.} = + for i in t1(): + yield i + + for i in t2(): + echo $i + + + proc iter1(): (iterator: int) = + let coll = [0,1,2] + result = iterator: int {.closure.} = + for i in coll: + yield i + + proc iter2(it: (iterator: int)): (iterator: int) = + result = iterator: int {.closure.} = + echo finished(it) + for i in it(): + yield i + + echo "start" + let myiter1 = iter1() + let myiter2 = iter2(myiter1) + for i in myiter2(): + echo i + echo "end" + + + type Iterable[T] = (iterator: T) | Slice[T] + ## Everything that can be iterated over, iterators and slices so far. + + proc toIter[T](s: Slice[T]): iterator: T = + ## Iterate over a slice. + iterator it: T {.closure.} = + for x in s.a..s.b: + yield x + return it + + proc toIter[T](i: iterator: T): iterator: T = + ## Nop + i + + iterator map[T,S](i: Iterable[T], f: proc(x: T): S): S = + let i = toIter(i) + for x in i(): + yield f(x) + + proc filter[T](i: Iterable[T], f: proc(x: T): bool): iterator: T = + let i = toIter(i) + iterator it: T {.closure.} = + for x in i(): + if f(x): + yield x + result = it + + iterator filter[T](i: Iterable[T], f: proc(x: T): bool): T = + let i = toIter(i) + for x in i(): + if f(x): + yield x + + var it = toSeq(filter(2..10, proc(x: int): bool = x mod 2 == 0)) + doAssert it == @[2, 4, 6, 8, 10] + it = toSeq(map(filter(2..10, proc(x: int): bool = x mod 2 == 0), proc(x: int): int = x * 2)) + doAssert it == @[4, 8, 12, 16, 20] + + + +block t3221_complex: + iterator permutations[T](ys: openArray[T]): seq[T] = + var + d = 1 + c = newSeq[int](ys.len) + xs = newSeq[T](ys.len) + for i, y in ys: xs[i] = y + yield xs + block outer: + while true: + while d > 1: + dec d + c[d] = 0 + while c[d] >= d: + inc d + if d >= ys.len: break outer + let i = if (d and 1) == 1: c[d] else: 0 + swap xs[i], xs[d] + yield xs + inc c[d] + + proc dig_vectors(): void = + var v_nums: seq[int] + v_nums = newSeq[int](1) + for perm in permutations(toSeq(0 .. 1)): + v_nums[0] = 1 + + dig_vectors() + + + +block t3499_keepstate: + proc slice[T](iter: iterator(): T {.closure.}, sl: auto): seq[T] = + var res: seq[int64] = @[] + var i = 0 + for n in iter(): + if i > sl.b: + break + if i >= sl.a: + res.add(n) + inc i + res + + iterator harshad(): int64 {.closure.} = + for n in 1 ..< int64.high: + var sum = 0 + for ch in string($n): + sum += parseInt("" & ch) + if n mod sum == 0: + yield n + + echo harshad.slice 0 ..< 20 + + for n in harshad(): + if n > 1000: + echo n + break + + # bug #3499 last snippet fixed + # bug #705 last snippet fixed + + + +block t1725_nested: + iterator factory(): int {.closure.} = + iterator bar(): int {.closure.} = + yield 0 + yield 1 + yield 2 + + for x in bar(): yield x + + for x in factory(): + echo x + + + +block t2023_objiter: + type + Obj = object + iter: iterator (): int8 {.closure.} + + iterator test(): int8 {.closure.} = + yield 7 + + proc init():Obj= + result.iter = test + + var o = init() + echo(o.iter()) + + +block: + # bug #13739 + iterator myIter(arg: openArray[int]): int = + var tmp = 0 + let len = arg.len + while tmp < len: + yield arg[tmp] * 2 + inc tmp + + proc someProc() = + var data = [4501,4502,4503,4504,4505,4506,4507,4508,4509] + # StmtListExpr should not get special treatment. + for x in myIter((discard;data)): + echo x + + someProc() + +block: + # bug #12576 + iterator ff(sq: varargs[seq[int]]): int = + for x in sq: + echo x + + for x in ff(@[1, 2], @[1, 2, 3]): + echo x + + +# bug #19575 + +iterator bb() {.closure.} = + while true: + try: discard + except: break + finally: break + +var a = bb + +iterator cc() {.closure.} = + while true: + try: discard + except: + if true: + break + finally: + if true: + break + +var a2 = cc + +# bug #16876 +block: + iterator a(num: int): int {.closure.} = + if num == 1: + yield num + else: + for i in a(num - 1): + yield i + + for i in a(5): + echo i + +block: + # bug #19911 (return in nested try) + + # try yield -> try + iterator p1: int {.closure.} = + try: + yield 0 + try: + return + finally: + echo "nested finally" + echo "shouldn't run" + finally: + echo "outer finally" + echo "shouldn't run" + + for _ in p1(): + discard + + # try -> try yield + iterator p2: int {.closure.} = + try: + try: + yield 0 + return + finally: + echo "nested finally" + echo "shouldn't run" + finally: + echo "outer finally" + echo "shouldn't run" + + for _ in p2(): + discard + + # try yield -> try yield + iterator p3: int {.closure.} = + try: + yield 0 + try: + yield 0 + return + finally: + echo "nested finally" + echo "shouldn't run" + finally: + echo "outer finally" + echo "shouldn't run" + + for _ in p3(): + discard + + # try -> try + iterator p4: int {.closure.} = + try: + try: + return + finally: + echo "nested finally" + echo "shouldn't run" + finally: + echo "outer finally" + echo "shouldn't run" + + for _ in p4(): + discard + +# bug #18824 +iterator poc_iterator: int {.closure.} = + block bug18824: + try: + break bug18824 + finally: + echo "In defer" + +for _ in poc_iterator(): + discard + +# bug #20624 +iterator tryFinally() {.closure.} = + block route: + try: + echo "trying" + raise + except: + echo "exception caught" + break route + finally: + echo "finally block" + +var x = tryFinally +x() + +block: # bug #24033 + type Query = ref object + + iterator pairs(query: Query): (int, (string, float32)) = + var output: (int, (string, float32)) = (0, ("foo", 3.14)) + for id in @[0, 1, 2]: + output[0] = id + yield output + + var collections: seq[(int, string, string)] + + for id, (str, num) in Query(): + collections.add (id, str, $num) + + doAssert collections[1] == (1, "foo", "3.14") diff --git a/tests/iter/titer_no_tuple_unpack.nim b/tests/iter/titer_no_tuple_unpack.nim new file mode 100644 index 000000000..d8df10189 --- /dev/null +++ b/tests/iter/titer_no_tuple_unpack.nim @@ -0,0 +1,27 @@ +discard """ +output: ''' +3 4 +4 5 +5 6 +6 7 +7 8 +(x: 3, y: 4) +(x: 4, y: 5) +(x: 5, y: 6) +(x: 6, y: 7) +(x: 7, y: 8) +''' +""" + + +iterator xrange(fromm, to: int, step = 1): tuple[x, y: int] = + var a = fromm + while a <= to: + yield (a, a+1) + inc(a, step) + +for a, b in xrange(3, 7): + echo a, " ", b + +for tup in xrange(3, 7): + echo tup diff --git a/tests/iter/titerable.nim b/tests/iter/titerable.nim new file mode 100644 index 000000000..f503836d3 --- /dev/null +++ b/tests/iter/titerable.nim @@ -0,0 +1,29 @@ +discard """ + output: '''2 +4 +6 +4 +8 +12 +''' + disabled: "true" +""" + +# Will eventually fix it... + +iterator map[T, U](s: iterator:T{.inline.}, f: proc(x: T): U): U = + for e in s: yield f(e) + +template toSeq(s: untyped): untyped = + var res = newSeq[type(s)](0) + for e in s: res.add(e) + res + +var s1 = @[1, 2, 3] +for x in map(s1.items, proc (a:int): int = a*2): + echo x + +var s2 = toSeq(map(s1.items, proc (a:int): int = a*4)) +for x in s2: + echo x + diff --git a/tests/iter/titerautoerr1.nim b/tests/iter/titerautoerr1.nim new file mode 100644 index 000000000..c7e5642c8 --- /dev/null +++ b/tests/iter/titerautoerr1.nim @@ -0,0 +1,8 @@ +discard """ + errormsg: "type mismatch: got <int literal(1)> but expected 'string'" + line: 8 +""" + +iterator a(): auto {.closure.} = + if true: return "str" + yield 1 diff --git a/tests/iter/titerautoerr2.nim b/tests/iter/titerautoerr2.nim new file mode 100644 index 000000000..eb48a36f9 --- /dev/null +++ b/tests/iter/titerautoerr2.nim @@ -0,0 +1,8 @@ +discard """ + errormsg: "type mismatch: got <string> but expected 'int literal(1)'" + line: 8 +""" + +iterator b(): auto {.closure.} = + yield 1 + if true: return "str" diff --git a/tests/iter/titerautoerr3.nim b/tests/iter/titerautoerr3.nim new file mode 100644 index 000000000..e47b65540 --- /dev/null +++ b/tests/iter/titerautoerr3.nim @@ -0,0 +1,9 @@ +discard """ + errormsg: "cannot infer the return type of 'bar'" + line: 6 +""" + +iterator bar(): auto = + discard +for t in bar(): + discard diff --git a/tests/iter/titerconcat.nim b/tests/iter/titerconcat.nim new file mode 100644 index 000000000..477ac5e26 --- /dev/null +++ b/tests/iter/titerconcat.nim @@ -0,0 +1,24 @@ +discard """ + output: '''1 +2 +3 +4 +20 +21 +22 +23''' +""" + +proc toIter*[T](s: Slice[T]): iterator: T = + iterator it: T {.closure.} = + for x in s.a..s.b: + yield x + return it + +iterator concat*[T](its: varargs[T, toIter]): auto = + for i in its: + for x in i(): + yield x + +for i in concat(1..4, 20..23): + echo i diff --git a/tests/iter/titerovl.nim b/tests/iter/titerovl.nim new file mode 100644 index 000000000..be665b2b7 --- /dev/null +++ b/tests/iter/titerovl.nim @@ -0,0 +1,21 @@ +discard """ + output: '''9 +1 +2 +3 +''' +""" + +# Test the new overloading rules for iterators: + +# test that iterator 'p' is preferred: +proc p(): seq[int] = @[1, 2, 3] +iterator p(): int = yield 9 + +for x in p(): echo x + +# test that 'q' works in this position: +proc q(): seq[int] = @[1, 2, 3] + +for x in q(): echo x + diff --git a/tests/iter/titerslice.nim b/tests/iter/titerslice.nim new file mode 100644 index 000000000..e5d2e14a3 --- /dev/null +++ b/tests/iter/titerslice.nim @@ -0,0 +1,9 @@ +discard """ + output: '''2 +3 +4''' +""" + +var t1 = @["1", "2", "3", "4"] +for t in t1[1..3]: + echo t diff --git a/tests/iter/titertypedesc.nim b/tests/iter/titertypedesc.nim new file mode 100644 index 000000000..a69f703c6 --- /dev/null +++ b/tests/iter/titertypedesc.nim @@ -0,0 +1,17 @@ +discard """ + output: '''0 +(id: 0) +@[] +[0, 0, 0]''' +""" + +iterator foo*(T: typedesc): T = + var x: T + yield x + +for a in foo(int): echo a +for b in foo(tuple[id: int]): echo b +for c in foo(seq[int]): echo c + +type Generic[T] = T +for d in foo(Generic[array[0..2, int]]): echo d diff --git a/tests/iter/titervaropenarray.nim b/tests/iter/titervaropenarray.nim new file mode 100644 index 000000000..b2fe71ceb --- /dev/null +++ b/tests/iter/titervaropenarray.nim @@ -0,0 +1,15 @@ +discard """ + output: "123" + targets: "c cpp" +""" +# Try to break the transformation pass: +iterator iterAndZero(a: var openArray[int]): int = + for i in 0..len(a)-1: + yield a[i] + a[i] = 0 + +var x = [[1, 2, 3], [4, 5, 6]] +for y in iterAndZero(x[0]): write(stdout, $y) +#OUT 123 + +write stdout, "\n" diff --git a/tests/iter/tmoditer.nim b/tests/iter/tmoditer.nim new file mode 100644 index 000000000..99e5b642d --- /dev/null +++ b/tests/iter/tmoditer.nim @@ -0,0 +1,88 @@ +discard """ + output: "XXXXX01234" +""" + +iterator modPairs(a: var array[0..4,string]): tuple[key: int, val: var string] = + for i in 0..a.high: + yield (key: i, val: a[i]) + +iterator modItems*[T](a: var array[0..4,T]): var T = + for i in 0..a.high: + yield a[i] + +var + arr = ["a", "b", "c", "d", "e"] + +for a in modItems(arr): + a = "X" + +for a in items(arr): + stdout.write(a) + +for i, a in modPairs(arr): + a = $i + +for a in items(arr): + stdout.write(a) + +echo "" + +#-------------------------------------------------------------------- +# Lent iterators +#-------------------------------------------------------------------- +type + NonCopyable = object + x: int + + +proc `=destroy`(o: var NonCopyable) = + discard + +proc `=copy`(dst: var NonCopyable, src: NonCopyable) {.error.} + +proc `=sink`(dst: var NonCopyable, src: NonCopyable) = + dst.x = src.x + +iterator lentItems[T](a: openArray[T]): lent T = + for i in 0..a.high: + yield a[i] + +iterator lentPairs[T](a: array[0..1, T]): tuple[key: int, val: lent T] = + for i in 0..a.high: + yield (key: i, val: a[i]) + + +let arr1 = [1, 2, 3] +let arr2 = @["a", "b", "c"] +let arr3 = [NonCopyable(x: 1), NonCopyable(x: 2)] +let arr4 = @[(1, "a"), (2, "b"), (3, "c")] + +var accum: string +for x in lentItems(arr1): + accum &= $x +doAssert(accum == "123") + +accum = "" +for x in lentItems(arr2): + accum &= $x +doAssert(accum == "abc") + +accum = "" +for val in lentItems(arr3): + accum &= $val.x +doAssert(accum == "12") + +accum = "" +for i, val in lentPairs(arr3): + accum &= $i & "-" & $val.x & " " +doAssert(accum == "0-1 1-2 ") + +accum = "" +for i, val in lentItems(arr4): + accum &= $i & "-" & $val & " " +doAssert(accum == "1-a 2-b 3-c ") + +accum = "" +for (i, val) in lentItems(arr4): + accum &= $i & "-" & $val & " " +doAssert(accum == "1-a 2-b 3-c ") diff --git a/tests/iter/tpermutations.nim b/tests/iter/tpermutations.nim new file mode 100644 index 000000000..30a66460f --- /dev/null +++ b/tests/iter/tpermutations.nim @@ -0,0 +1,69 @@ +discard """ +output: ''' +@[@[1.0, 2.0], @[3.0, 4.0]] +perm: 10.0 det: -2.0 +@[@[1.0, 2.0, 3.0, 4.0], @[4.0, 5.0, 6.0, 7.0], @[7.0, 8.0, 9.0, 10.0], @[10.0, 11.0, 12.0, 13.0]] +perm: 29556.0 det: 0.0 +@[@[0.0, 1.0, 2.0, 3.0, 4.0], @[5.0, 6.0, 7.0, 8.0, 9.0], @[10.0, 11.0, 12.0, 13.0, 14.0], @[15.0, 16.0, 17.0, 18.0, 19.0], @[20.0, 21.0, 22.0, 23.0, 24.0]] +perm: 6778800.0 det: 0.0 +''' +""" + + +import sequtils, sugar + +iterator permutations*[T](ys: openArray[T]): tuple[perm: seq[T], sign: int] = + var + d = 1 + c = newSeq[int](ys.len) + xs = newSeq[T](ys.len) + sign = 1 + + for i, y in ys: xs[i] = y + yield (xs, sign) + + block outter: + while true: + while d > 1: + dec d + c[d] = 0 + while c[d] >= d: + inc d + if d >= ys.len: break outter + + let i = if (d and 1) == 1: c[d] else: 0 + swap xs[i], xs[d] + sign *= -1 + yield (xs, sign) + inc c[d] + +proc det(a: seq[seq[float]]): float = + let n = toSeq 0..a.high + for sigma, sign in n.permutations: + result += sign.float * n.map((i: int) => a[i][sigma[i]]).foldl(a * b) + +proc perm(a: seq[seq[float]]): float = + let n = toSeq 0..a.high + for sigma, sign in n.permutations: + result += n.map((i: int) => a[i][sigma[i]]).foldl(a * b) + +for a in [ + @[ @[1.0, 2.0] + , @[3.0, 4.0] + ], + @[ @[ 1.0, 2, 3, 4] + , @[ 4.0, 5, 6, 7] + , @[ 7.0, 8, 9, 10] + , @[10.0, 11, 12, 13] + ], + @[ @[ 0.0, 1, 2, 3, 4] + , @[ 5.0, 6, 7, 8, 9] + , @[10.0, 11, 12, 13, 14] + , @[15.0, 16, 17, 18, 19] + , @[20.0, 21, 22, 23, 24] + ] ]: + echo a + echo "perm: ", a.perm, " det: ", a.det + +# bug #3499 last snippet fixed +# bug 705 last snippet fixed diff --git a/tests/iter/treciter.nim b/tests/iter/treciter.nim new file mode 100644 index 000000000..11fb58224 --- /dev/null +++ b/tests/iter/treciter.nim @@ -0,0 +1,12 @@ +discard """ + errormsg: "recursion is not supported in iterators: 'myrec'" + file: "treciter.nim" + line: 9 +""" +# Test that an error message occurs for a recursive iterator + +iterator myrec(n: int): int = + for x in myrec(n-1): #ERROR_MSG recursive dependency: 'myrec' + yield x + +for x in myrec(10): echo x diff --git a/tests/iter/tscheduler.nim b/tests/iter/tscheduler.nim new file mode 100644 index 000000000..f4b04f311 --- /dev/null +++ b/tests/iter/tscheduler.nim @@ -0,0 +1,77 @@ +discard """ + output: '''a1 5 +a2 10 +a1 3 +a1 1 +a2 8 +a2 6 +a2 4 +a2 2''' + disabled: "true" +""" + +import os, strutils, times, algorithm + + +type TaskFn = iterator (): float + +type Task = object + coro: TaskFn + next_run: float + + +type Scheduler = object + tasks: seq[Task] + + +proc newScheduler(): Scheduler = + var s = Scheduler() + s.tasks = @[] + return s + + +proc start(this: var Scheduler, task: TaskFn) = + var t = Task() + t.coro = task + t.next_run = 0.0 + this.tasks.add(t) + + +proc run(this: var Scheduler) = + while this.tasks.len > 0: + var dead: seq[int] = @[] + for i in this.tasks.low..this.tasks.high: + var task = this.tasks[i] + if finished(task.coro): + dead.add(i) + continue + if task.next_run <= epochTime(): + task.next_run = task.coro() + epochTime() + this.tasks[i] = task + for i in dead: + this.tasks.delete(i) + if this.tasks.len > 0: + sort(this.tasks, proc (t1: Task, t2: Task): int = cmp(t1.next_run, t2.next_run)) + sleep(int((this.tasks[0].next_run - epochTime()) * 1000)) + + +iterator a1(): float {.closure.} = + var k = 5 + while k > 0: + echo "a1 $1" % [$k] + dec k, 2 + yield 0.5 + + +iterator a2(): float {.closure.} = + var k = 10 + while k > 0: + echo "a2 $1" % [$k] + dec k, 2 + yield 1.5 + + +var sched = newScheduler() +sched.start(a1) +sched.start(a2) +sched.run() diff --git a/tests/iter/tshallowcopy_closures.nim b/tests/iter/tshallowcopy_closures.nim new file mode 100644 index 000000000..06b04a788 --- /dev/null +++ b/tests/iter/tshallowcopy_closures.nim @@ -0,0 +1,36 @@ +discard """ + matrix: "--mm:refc" + ccodecheck: "!@('{' \\s* 'NI HEX3Astate;' \\s* '}')" + output: ''' +a1 10 +a1 9 +''' +""" + +# bug #1803 +type TaskFn = iterator (): float + +iterator a1(): float {.closure.} = + var k = 10 + while k > 0: + echo "a1 ", k + dec k + yield 1.0 + + +iterator a2(): float {.closure.} = + var k = 15 + while k > 0: + echo "a2 ", k + dec k + yield 2.0 + +var + x = a1 + y = a2 + z: TaskFn + +discard x() +shallowCopy(z, x) +shallowCopy(z, y) +discard x() diff --git a/tests/iter/twrap_walkdir.nim b/tests/iter/twrap_walkdir.nim new file mode 100644 index 000000000..1d52e9791 --- /dev/null +++ b/tests/iter/twrap_walkdir.nim @@ -0,0 +1,17 @@ +discard """ +action: compile +""" + +import os + +# bug #3636 + +proc fooIt(foo: string): iterator(): (string) = + iterator temp(): (string) = + for f in walkDirRec(foo): # No problem with walkFiles + yield f + return temp + +let it = fooIt(".") +for x in it(): + echo x diff --git a/tests/iter/twrongiter.nim b/tests/iter/twrongiter.nim new file mode 100644 index 000000000..577b8c4f1 --- /dev/null +++ b/tests/iter/twrongiter.nim @@ -0,0 +1,13 @@ +discard """ +errormsg: "type mismatch" +line: 12 +""" + +proc first(it: iterator(): int): seq[int] = + return @[] + +iterator primes(): int = + yield 1 + +for i in first(primes): + break diff --git a/tests/iter/tyieldintry.nim b/tests/iter/tyieldintry.nim new file mode 100644 index 000000000..e51ab7f0d --- /dev/null +++ b/tests/iter/tyieldintry.nim @@ -0,0 +1,529 @@ +discard """ + matrix: "; --experimental:strictdefs; -d:nimOptIters" + targets: "c cpp" +""" + +var closureIterResult = newSeq[int]() + +proc checkpoint(arg: int) = + closureIterResult.add(arg) + +type + TestError = object of CatchableError + AnotherError = object of CatchableError + +proc testClosureIterAux(it: iterator(): int, exceptionExpected: bool, expectedResults: varargs[int]) = + closureIterResult.setLen(0) + + var exceptionCaught = false + + try: + for i in it(): + closureIterResult.add(i) + except TestError: + exceptionCaught = true + + if closureIterResult != @expectedResults or exceptionCaught != exceptionExpected: + if closureIterResult != @expectedResults: + echo "Expected: ", @expectedResults + echo "Actual: ", closureIterResult + if exceptionCaught != exceptionExpected: + echo "Expected exception: ", exceptionExpected + echo "Got exception: ", exceptionCaught + doAssert(false) + +proc test(it: iterator(): int, expectedResults: varargs[int]) = + testClosureIterAux(it, false, expectedResults) + +proc testExc(it: iterator(): int, expectedResults: varargs[int]) = + testClosureIterAux(it, true, expectedResults) + +proc raiseTestError() = + raise newException(TestError, "Test exception!") + +block: + iterator it(): int {.closure.} = + var i = 5 + while i != 0: + yield i + if i == 3: + yield 123 + dec i + + test(it, 5, 4, 3, 123, 2, 1) + +block: + iterator it(): int {.closure.} = + yield 0 + try: + checkpoint(1) + raiseTestError() + except TestError: + checkpoint(2) + yield 3 + checkpoint(4) + finally: + checkpoint(5) + + checkpoint(6) + + test(it, 0, 1, 2, 3, 4, 5, 6) + +block: + iterator it(): int {.closure.} = + yield 0 + try: + yield 1 + checkpoint(2) + finally: + checkpoint(3) + yield 4 + checkpoint(5) + yield 6 + + test(it, 0, 1, 2, 3, 4, 5, 6) + +block: + iterator it(): int {.closure.} = + yield 0 + try: + yield 1 + raiseTestError() + yield 2 + finally: + checkpoint(3) + yield 4 + checkpoint(5) + yield 6 + + testExc(it, 0, 1, 3, 4, 5, 6) + +block: + iterator it(): int {.closure.} = + try: + try: + raiseTestError() + except AnotherError: + yield 123 + finally: + checkpoint(3) + finally: + checkpoint(4) + + testExc(it, 3, 4) + +block: + iterator it(): int {.closure.} = + try: + yield 1 + raiseTestError() + except AnotherError: + checkpoint(123) + finally: + checkpoint(2) + checkpoint(3) + + testExc(it, 1, 2) + +block: + iterator it(): int {.closure.} = + try: + yield 0 + try: + yield 1 + try: + yield 2 + raiseTestError() + except AnotherError: + yield 123 + finally: + yield 3 + except AnotherError: + yield 124 + finally: + yield 4 + checkpoint(1234) + except: + yield 5 + checkpoint(6) + finally: + checkpoint(7) + yield 8 + checkpoint(9) + + test(it, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9) + +block: + iterator it(): int {.closure.} = + try: + yield 0 + return 2 + finally: + checkpoint(1) + checkpoint(123) + + test(it, 0, 1) + +block: + iterator it(): int {.closure.} = + try: + try: + yield 0 + raiseTestError() + finally: + checkpoint(1) + except TestError: + yield 2 + return + finally: + yield 3 + + checkpoint(123) + + test(it, 0, 1, 2, 3) + +block: + iterator it(): int {.closure.} = + try: + try: + yield 0 + raiseTestError() + finally: + return # Return in finally should stop exception propagation + except AnotherError: + yield 2 + return + finally: + yield 3 + checkpoint(123) + + test(it, 0, 3) + +block: # Yield in yield + iterator it(): int {.closure.} = + template foo(): int = + yield 1 + 2 + + for i in 0 .. 2: + checkpoint(0) + yield foo() + + test(it, 0, 1, 2, 0, 1, 2, 0, 1, 2) + +block: + iterator it(): int {.closure.} = + let i = if true: + yield 0 + 1 + else: + 2 + yield i + + test(it, 0, 1) + +block: + iterator it(): int {.closure.} = + var foo = 123 + let i = try: + yield 0 + raiseTestError() + 1 + except TestError as e: + assert(e.msg == "Test exception!") + case foo + of 1: + yield 123 + 2 + of 123: + yield 5 + 6 + else: + 7 + yield i + + test(it, 0, 5, 6) + +block: + iterator it(): int {.closure.} = + proc voidFoo(i1, i2, i3: int) = + checkpoint(i1) + checkpoint(i2) + checkpoint(i3) + + proc foo(i1, i2, i3: int): int = + voidFoo(i1, i2, i3) + i3 + + proc bar(i1: int): int = + checkpoint(i1) + + template tryexcept: int = + try: + yield 1 + raiseTestError() + 123 + except TestError: + yield 2 + checkpoint(3) + 4 + + let e1 = true + + template ifelse1: int = + if e1: + yield 10 + 11 + else: + 12 + + template ifelse2: int = + if ifelse1() == 12: + yield 20 + 21 + else: + yield 22 + 23 + + let i = foo(bar(0), tryexcept, ifelse2) + discard foo(bar(0), tryexcept, ifelse2) + voidFoo(bar(0), tryexcept, ifelse2) + yield i + + test(it, + + # let i = foo(bar(0), tryexcept, ifelse2) + 0, # bar(0) + 1, 2, 3, # tryexcept + 10, # ifelse1 + 22, # ifelse22 + 0, 4, 23, # foo + + # discard foo(bar(0), tryexcept, ifelse2) + 0, # bar(0) + 1, 2, 3, # tryexcept + 10, # ifelse1 + 22, # ifelse22 + 0, 4, 23, # foo + + # voidFoo(bar(0), tryexcept, ifelse2) + 0, # bar(0) + 1, 2, 3, # tryexcept + 10, # ifelse1 + 22, # ifelse22 + 0, 4, 23, # foo + + 23 # i + ) + +block: + iterator it(): int {.closure.} = + checkpoint(0) + for i in 0 .. 1: + try: + yield 1 + raiseTestError() + except TestError as e: + doAssert(e.msg == "Test exception!") + yield 2 + except AnotherError: + yield 123 + except: + yield 1234 + finally: + yield 3 + checkpoint(4) + yield 5 + + test(it, 0, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5) + +block: + iterator it(): int {.closure.} = + var i = 5 + template foo(): bool = + yield i + true + + while foo(): + dec i + if i == 0: + break + + test(it, 5, 4, 3, 2, 1) + +block: # Short cirquits + iterator it(): int {.closure.} = + template trueYield: bool = + yield 1 + true + + template falseYield: bool = + yield 0 + false + + if trueYield or falseYield: + discard falseYield and trueYield + + if falseYield and trueYield: + checkpoint(123) + + test(it, 1, 0, 0) + +block: #7969 + type + SomeObj = object + id: int + + iterator it(): int {.closure.} = + template yieldAndSomeObj: SomeObj = + var s: SomeObj + s.id = 2 + yield 1 + s + + checkpoint(yieldAndSomeObj().id) + + var i = 5 + case i + of 0: + checkpoint(123) + of 1, 2, 5: + checkpoint(3) + else: + checkpoint(123) + + test(it, 1, 2, 3) + +block: # yield in blockexpr + iterator it(): int {.closure.} = + yield(block: + checkpoint(1) + yield 2 + 3 + ) + + test(it, 1, 2, 3) + +block: #8851 + type + Foo = ref object of RootObj + template someFoo(): Foo = + var f: Foo + yield 1 + f + iterator it(): int {.closure.} = + var o: RootRef + o = someFoo() + + test(it, 1) + +block: # 8243 + iterator it(): int {.closure.} = + template yieldAndSeq: seq[int] = + yield 1 + @[123, 5, 123] + + checkpoint(yieldAndSeq[1]) + + test(it, 1, 5) + +block: + iterator it(): int {.closure.} = + template yieldAndSeq: seq[int] = + yield 1 + @[123, 5, 123] + + template yieldAndNum: int = + yield 2 + 1 + + checkpoint(yieldAndSeq[yieldAndNum]) + + test(it, 1, 2, 5) + +block: #9694 - yield in ObjConstr + type Foo = object + a, b: int + + template yieldAndNum: int = + yield 1 + 2 + + iterator it(): int {.closure.} = + let a = Foo(a: 5, b: yieldAndNum()) + checkpoint(a.b) + + test(it, 1, 2) + +block: #9716 + iterator it(): int {.closure.} = + var a = 0 + for i in 1 .. 3: + var a: int # Make sure the "local" var is reset + var b: string # ditto + yield 1 + a += 5 + b &= "hello" + doAssert(a == 5) + doAssert(b == "hello") + test(it, 1, 1, 1) + +block: # nnkChckRange + type Foo = distinct uint64 + template yieldDistinct: Foo = + yield 2 + Foo(0) + + iterator it(): int {.closure.} = + yield 1 + var a: int + a = int(yieldDistinct()) + yield 3 + + test(it, 1, 2, 3) + +block: #17849 - yield in case subject + template yieldInCase: int = + yield 2 + 3 + + iterator it(): int {.closure.} = + yield 1 + case yieldInCase() + of 1: checkpoint(11) + of 3: checkpoint(13) + else: checkpoint(14) + yield 5 + + test(it, 1, 2, 13, 5) + +block: # void iterator + iterator it() {.closure.} = + try: + yield + except: + discard + var a = it + +if defined(nimOptIters): # Locals present in only 1 state should be on the stack + proc checkOnStack(a: pointer, shouldBeOnStack: bool) = + # Quick and dirty way to check if a points to stack + var dummy = 0 + let dummyAddr = addr dummy + let distance = abs(cast[int](dummyAddr) - cast[int](a)) + const requiredDistance = 300 + if shouldBeOnStack: + doAssert(distance <= requiredDistance, "a is not on stack, but should") + else: + doAssert(distance > requiredDistance, "a is on stack, but should not") + + iterator it(): int {.closure.} = + var a = 1 + var b = 2 + var c {.liftLocals.} = 3 + checkOnStack(addr a, true) + checkOnStack(addr b, false) + checkOnStack(addr c, false) + yield a + yield b + test(it, 1, 2) |