HexCasting/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/FrameForEach.kt

90 lines
3.5 KiB
Kotlin

package at.petrak.hexcasting.api.casting.eval.vm
import at.petrak.hexcasting.api.casting.SpellList
import at.petrak.hexcasting.api.casting.eval.CastResult
import at.petrak.hexcasting.api.casting.eval.ResolvedPatternType
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.casting.iota.ListIota
import at.petrak.hexcasting.api.utils.NBTBuilder
import at.petrak.hexcasting.api.utils.serializeToNBT
import at.petrak.hexcasting.common.lib.hex.HexEvalSounds
import net.minecraft.server.level.ServerLevel
/**
* A frame representing all the state for a Thoth evaluation.
* Pushed by an OpForEach.
* @property first whether the input stack state is the first one (since we don't want to save the base-stack before any changes are made)
* @property data list of *remaining* datums to ForEach over
* @property code code to run per datum
* @property baseStack the stack state at Thoth entry
* @property acc concatenated list of final stack states after Thoth exit
*/
data class FrameForEach(
val data: SpellList,
val code: SpellList,
val baseStack: List<Iota>?,
val acc: MutableList<Iota>
) : ContinuationFrame {
/** When halting, we add the stack state at halt to the stack accumulator, then return the original pre-Thoth stack, plus the accumulator. */
override fun breakDownwards(stack: List<Iota>): Pair<Boolean, List<Iota>> {
val newStack = baseStack?.toMutableList() ?: mutableListOf()
acc.addAll(stack)
newStack.add(ListIota(acc))
return true to newStack
}
/** Step the Thoth computation, enqueueing one code evaluation. */
override fun evaluate(
continuation: SpellContinuation,
level: ServerLevel,
harness: CastingVM
): CastResult {
// If this isn't the very first Thoth step (i.e. no Thoth computations run yet)...
val stack = if (baseStack == null) {
// init stack to the harness stack...
harness.image.stack.toList()
} else {
// else save the stack to the accumulator and reuse the saved base stack.
acc.addAll(harness.image.stack)
baseStack
}
// If we still have data to process...
val (stackTop, newImage, newCont) = if (data.nonEmpty) {
// Increment the evaluation depth,
// push the next datum to the top of the stack,
val cont2 = continuation
// put the next Thoth object back on the stack for the next Thoth cycle,
.pushFrame(FrameForEach(data.cdr, code, stack, acc))
// and prep the Thoth'd code block for evaluation.
.pushFrame(FrameEvaluate(code, true))
Triple(data.car, harness.image.withUsedOp(), cont2)
} else {
// Else, dump our final list onto the stack.
Triple(ListIota(acc), harness.image, continuation)
}
val tStack = stack.toMutableList()
tStack.add(stackTop)
return CastResult(
newCont,
newImage.copy(stack = tStack),
listOf(),
ResolvedPatternType.EVALUATED,
HexEvalSounds.THOTH,
)
}
override fun serializeToNBT() = NBTBuilder {
"type" %= "foreach"
"data" %= data.serializeToNBT()
"code" %= code.serializeToNBT()
if (baseStack != null)
"base" %= baseStack.serializeToNBT()
"accumulator" %= acc.serializeToNBT()
}
override fun size() = data.size() + code.size() + acc.size + (baseStack?.size ?: 0)
}