commonMain.reset.kt Maven / Gradle / Ivy
Show all versions of kontinuity-jvm Show documentation
import arrow.core.identity
import kotlinx.coroutines.CancellationException
import kotlin.coroutines.*
import kotlin.coroutines.intrinsics.*
@Target(AnnotationTarget.CLASS, AnnotationTarget.TYPE, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY)
@DslMarker
public annotation class ResetDsl
public class SubCont internal constructor(
private var init: Segment?,
private val prompt: Prompt
) {
public fun clear() {
init = null
}
private fun composedWith(
k: Continuation, isDelimiting: Boolean, shouldClear: Boolean
) = (init!! prependTo collectStack(k).let { if (isDelimiting) it.pushPrompt(prompt) else it }).also {
if (shouldClear) clear()
}
@ResetDsl
public suspend fun pushSubContWith(
value: Result,
isDelimiting: Boolean = false,
shouldClear: Boolean = false,
): R = suspendCoroutineUnintercepted { k ->
composedWith(k, isDelimiting, shouldClear).resumeWith(value, isIntercepted = true)
}
@ResetDsl
public suspend fun pushSubCont(
isDelimiting: Boolean = false,
shouldClear: Boolean = false,
value: suspend () -> T
): R = suspendCoroutineUnintercepted { k ->
value.startCoroutine(WrapperCont(composedWith(k, isDelimiting, shouldClear)))
}
public fun copy(): SubCont = SubCont(init, prompt)
}
@ResetDsl
public suspend fun Prompt.pushPrompt(
body: suspend () -> R
): R = suspendCoroutineUnintercepted { k ->
val stack = collectStack(k)
body.startCoroutine(WrapperCont(stack.pushPrompt(this)))
}
/*
suspendCoroutineUnintercepted { k ->
val stack = collectStack(k).pushPrompt(this)
stack.resumeWith(runCatching {
body.startCoroutineUninterceptedOrReturn(WrapperCont(stack)).also {
if (it == COROUTINE_SUSPENDED) return@suspendCoroutineUnintercepted
} as R
}, isIntercepted = true)
}
*/
public suspend fun Reader.pushReader(value: T, fork: T.() -> T = ::identity, body: suspend () -> R): R =
suspendCoroutineUnintercepted { k ->
val stack = collectStack(k)
body.startCoroutine(WrapperCont(stack.pushReader(this, value, fork)))
}
@ResetDsl
public suspend fun nonReentrant(
body: suspend () -> R
): R = runCC(body)
public suspend fun Reader.deleteBinding(): Unit = suspendCoroutineUnintercepted { k ->
val stack = collectStack(k)
val toResume: SplitSeq = when (stack) {
is EmptyCont -> error("Reader not found $this")
is FramesCont<*, *, *, *> -> {
stack.rest!!.deleteReader(this, stack)
stack
}
is PromptCont -> {
stack.rest!!.deleteReader(this, stack)
stack
}
is ReaderCont<*, Unit, *, *> -> {
if (this === stack.p) {
stack.rest!!
} else {
stack.rest!!.deleteReader(this, stack)
stack
}
}
}
toResume.resumeWith(Result.success(Unit), isIntercepted = true)
}
private fun SplitSeq<*, *, *>.holeFor(prompt: Prompt, deleteDelimiter: Boolean): SplitSeq {
val splitSeq = find(prompt)
return if (deleteDelimiter) splitSeq else splitSeq.pushPrompt(prompt)
}
@ResetDsl
public suspend fun Prompt.takeSubCont(
deleteDelimiter: Boolean = true, body: suspend (SubCont) -> R
): T = suspendCoroutineUnintercepted { k ->
val stack = collectStack(k)
val (init, rest) = stack.splitAt(this)
body.startCoroutine(SubCont(init, this), WrapperCont(if (deleteDelimiter) rest else rest.pushPrompt(this)))
}
@ResetDsl
public suspend fun Prompt.takeSubContOnce(
deleteDelimiter: Boolean = true, body: suspend (SubCont) -> R
): T = suspendCoroutineUnintercepted { k ->
val stack = collectStack(k)
val (init, rest) = stack.splitAtOnce(this)
body.startCoroutine(SubCont(init, this), WrapperCont(if (deleteDelimiter) rest else rest.pushPrompt(this)))
}
@ResetDsl
public suspend fun Prompt.takeSubContWithFinal(
deleteDelimiter: Boolean = true, body: suspend (Pair, SubCont>) -> R
): T = suspendCoroutineUnintercepted { k ->
val stack = collectStack(k)
val (reusableInit, _) = stack.splitAt(this)
val (init, rest) = stack.splitAtOnce(this)
body.startCoroutine(
SubCont(reusableInit, this) to SubCont(init, this),
WrapperCont(if (deleteDelimiter) rest else rest.pushPrompt(this))
)
}
// Acts like shift0/control { it(body()) }
// TODO make it faster
@ResetDsl
public suspend fun Prompt.inHandlingContext(
includeBodyContext: Boolean = false, body: suspend () -> T
): T = takeSubCont(!includeBodyContext) { k ->
k.pushSubContWith(runCatching { body() }, isDelimiting = !includeBodyContext)
}
@Suppress("UNCHECKED_CAST")
internal fun Prompt.abortWith(deleteDelimiter: Boolean, value: Result): Nothing =
throw AbortWithValueException(this as Prompt, value, deleteDelimiter)
@Suppress("UNCHECKED_CAST")
internal suspend fun Prompt.abortWithFast(deleteDelimiter: Boolean, value: Result): Nothing =
suspendCoroutineUnintercepted { k ->
collectStack(k).holeFor(this, deleteDelimiter).resumeWith(value, isIntercepted = true)
}
private class AbortWithValueException(
private val prompt: Prompt, private val value: Result, private val deleteDelimiter: Boolean
) : SeekingStackException() {
override fun use(stack: SplitSeq<*, *, *>) =
stack.holeFor(prompt, deleteDelimiter).resumeWith(value, isIntercepted = true)
}
@Suppress("UNCHECKED_CAST")
internal fun Prompt.abortS(deleteDelimiter: Boolean = false, value: suspend () -> R): Nothing =
throw AbortWithProducerException(this as Prompt, value, deleteDelimiter)
private class AbortWithProducerException(
private val prompt: Prompt, private val value: suspend () -> Any?, private val deleteDelimiter: Boolean
) : SeekingStackException() {
override fun use(stack: SplitSeq<*, *, *>) = value.startCoroutine(WrapperCont(stack.holeFor(prompt, deleteDelimiter)))
}
public class Prompt
public class Reader
@Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING")
internal expect abstract class SeekingStackException() : CancellationException {
abstract fun use(stack: SplitSeq<*, *, *>)
}
public suspend fun runCC(body: suspend () -> R): R = suspendCoroutine {
body.startCoroutine(WrapperCont(EmptyCont(it)))
}
private suspend inline fun suspendCoroutineUnintercepted(
crossinline block: (Continuation) -> Unit
): T = suspendCoroutineUninterceptedOrReturn {
block(it)
COROUTINE_SUSPENDED
}