141 lines
3.5 KiB
Nim
141 lines
3.5 KiB
Nim
|
import std/sugar
|
||
|
|
||
|
import pkg/questionable
|
||
|
import pkg/questionable/results
|
||
|
|
||
|
type
|
||
|
Function*[T, U] = proc(fut: T): U {.raises: [CatchableError], gcsafe, closure.}
|
||
|
IsFinished* = proc(): bool {.raises: [], gcsafe, closure.}
|
||
|
GenNext*[T] = proc(): T {.raises: [CatchableError], gcsafe.}
|
||
|
Iter*[T] = ref object
|
||
|
finished: bool
|
||
|
next*: GenNext[T]
|
||
|
|
||
|
proc finish*[T](self: Iter[T]): void =
|
||
|
self.finished = true
|
||
|
|
||
|
proc finished*[T](self: Iter[T]): bool =
|
||
|
self.finished
|
||
|
|
||
|
iterator items*[T](self: Iter[T]): T =
|
||
|
while not self.finished:
|
||
|
yield self.next()
|
||
|
|
||
|
iterator pairs*[T](self: Iter[T]): tuple[key: int, val: T] {.inline.} =
|
||
|
var i = 0
|
||
|
while not self.finished:
|
||
|
yield (i, self.next())
|
||
|
inc(i)
|
||
|
|
||
|
proc new*[T](_: type Iter[T], genNext: GenNext[T], isFinished: IsFinished, finishOnErr: bool = true): Iter[T] =
|
||
|
## Creates a new Iter using elements returned by supplier function `genNext`.
|
||
|
## Iter is finished whenever `isFinished` returns true.
|
||
|
##
|
||
|
|
||
|
var iter = Iter[T]()
|
||
|
|
||
|
proc next(): T {.raises: [CatchableError].} =
|
||
|
if not iter.finished:
|
||
|
var item: T
|
||
|
try:
|
||
|
item = genNext()
|
||
|
except CatchableError as err:
|
||
|
if finishOnErr or isFinished():
|
||
|
iter.finish
|
||
|
raise err
|
||
|
|
||
|
if isFinished():
|
||
|
iter.finish
|
||
|
return item
|
||
|
else:
|
||
|
raise newException(CatchableError, "Iter is finished but next item was requested")
|
||
|
|
||
|
if isFinished():
|
||
|
iter.finish
|
||
|
|
||
|
iter.next = next
|
||
|
return iter
|
||
|
|
||
|
proc new*[U, V, S: Ordinal](_: type Iter[U], a: U, b: V, step: S = 1): Iter[U] =
|
||
|
## Creates a new Iter in range a..b with specified step (default 1)
|
||
|
##
|
||
|
|
||
|
var i = a
|
||
|
|
||
|
proc genNext(): U =
|
||
|
let u = i
|
||
|
inc(i, step)
|
||
|
u
|
||
|
|
||
|
proc isFinished(): bool =
|
||
|
(step > 0 and i > b) or
|
||
|
(step < 0 and i < b)
|
||
|
|
||
|
Iter[U].new(genNext, isFinished)
|
||
|
|
||
|
proc new*[U, V: Ordinal](_: type Iter[U], slice: HSlice[U, V]): Iter[U] =
|
||
|
## Creates a new Iter from a slice
|
||
|
##
|
||
|
|
||
|
Iter[U].new(slice.a.int, slice.b.int, 1)
|
||
|
|
||
|
proc new*[T](_: type Iter[T], items: seq[T]): Iter[T] =
|
||
|
## Creates a new Iter from a sequence
|
||
|
##
|
||
|
|
||
|
Iter[int].new(0..<items.len)
|
||
|
.map((i: int) => items[i])
|
||
|
|
||
|
proc empty*[T](_: type Iter[T]): Iter[T] =
|
||
|
## Creates an empty Iter
|
||
|
##
|
||
|
|
||
|
proc genNext(): T {.raises: [CatchableError].} =
|
||
|
raise newException(CatchableError, "Next item requested from an empty Iter")
|
||
|
proc isFinished(): bool = true
|
||
|
|
||
|
Iter[T].new(genNext, isFinished)
|
||
|
|
||
|
proc map*[T, U](iter: Iter[T], fn: Function[T, U]): Iter[U] =
|
||
|
Iter[U].new(
|
||
|
genNext = () => fn(iter.next()),
|
||
|
isFinished = () => iter.finished
|
||
|
)
|
||
|
|
||
|
proc mapFilter*[T, U](iter: Iter[T], mapPredicate: Function[T, Option[U]]): Iter[U] =
|
||
|
var nextUOrErr: Option[Result[U, ref CatchableError]]
|
||
|
|
||
|
proc tryFetch(): void =
|
||
|
nextUOrErr = Result[U, ref CatchableError].none
|
||
|
while not iter.finished:
|
||
|
try:
|
||
|
let t = iter.next()
|
||
|
if u =? mapPredicate(t):
|
||
|
nextUOrErr = some(success(u))
|
||
|
break
|
||
|
except CatchableError as err:
|
||
|
nextUOrErr = some(U.failure(err))
|
||
|
|
||
|
proc genNext(): U {.raises: [CatchableError].} =
|
||
|
# at this point nextUOrErr should always be some(..)
|
||
|
without u =? nextUOrErr.unsafeGet, err:
|
||
|
raise err
|
||
|
|
||
|
tryFetch()
|
||
|
return u
|
||
|
|
||
|
proc isFinished(): bool =
|
||
|
nextUOrErr.isNone
|
||
|
|
||
|
tryFetch()
|
||
|
Iter[U].new(genNext, isFinished)
|
||
|
|
||
|
proc filter*[T](iter: Iter[T], predicate: Function[T, bool]): Iter[T] =
|
||
|
proc wrappedPredicate(t: T): Option[T] =
|
||
|
if predicate(t):
|
||
|
some(t)
|
||
|
else:
|
||
|
T.none
|
||
|
|
||
|
mapFilter[T, T](iter, wrappedPredicate)
|