/
StandaloneGenerator.scala
78 lines (68 loc) · 2.17 KB
/
StandaloneGenerator.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
/* Copyright 2010 Jim McBeath under GPLv2 */
package net.jimmc.scoroutine
import scala.collection.Iterator
import scala.util.continuations._
/** Standalone generic generator class.
* @author Jim McBeath
* @since 1.0.1
*/
class StandaloneGenerator[T] extends Iterator[T] {
private var nextValue:Option[T] = None
private var nextStep:Option[Unit=>Unit] = None
/** Subclass calls this method to generate values.
* @param body The code for your generator.
*/
protected def generate(body: => Unit @suspendable) {
reset {
suspend
body
}
}
/** Yield the next generated value.
* Call this code from your generator to deliver the next value.
*/
protected def yld(x:T):Unit @suspendable = {
nextValue = Some(x)
suspend
}
/** Retrieve the next generated value.
* Call this from your main code.
*/
def next:T = {
step
nextValue match {
case None => throw new NoSuchElementException("next on empty generator")
//make it similar to the equivalent Iterator exception
case Some(x) => nextValue = None; x
}
}
/** True if there is another value to retrieve.
* Call this from your main code.
*/
def hasNext:Boolean = {
step
nextValue.isDefined
}
/** Save our continuation to resume later. */
private def suspend:Unit @suspendable = {
shift { k:(Unit=>Unit) =>
nextStep = Some(k)
}
}
/** If we have a next step but we don't have a value queued up,
* run the generator until it calls yld or completes. */
private def step = {
if (nextValue.isEmpty) nextStep foreach { nextStep = None; _() }
}
/** This method is useful for nested executions of the stored continuation,
* such as from NonDetEval. */
def stepUntilDone:Unit @suspendable = {
//Use of var for saveStep is workaround for Scala bug #3501
var saveStep:(Unit=>Unit) = null
while (nextStep.isDefined) {
saveStep = nextStep.get
suspend //sets nextStep to point here
saveStep()
}
}
}