Make continuations immutable and functional

This commit is contained in:
Alwinfy 2022-05-23 18:54:33 -04:00
parent cf89dfe38b
commit cd5657d7b8
No known key found for this signature in database
GPG key ID: 2CCB99445F0C949E
18 changed files with 127 additions and 90 deletions

View file

@ -1,7 +1,7 @@
package at.petrak.hexcasting.api.spell package at.petrak.hexcasting.api.spell
import at.petrak.hexcasting.api.spell.casting.CastingContext import at.petrak.hexcasting.api.spell.casting.CastingContext
import at.petrak.hexcasting.api.spell.casting.ContinuationFrame import at.petrak.hexcasting.api.spell.casting.SpellContinuation
import at.petrak.hexcasting.api.spell.casting.OperatorSideEffect import at.petrak.hexcasting.api.spell.casting.OperatorSideEffect
import at.petrak.hexcasting.api.spell.mishaps.MishapNotEnoughArgs import at.petrak.hexcasting.api.spell.mishaps.MishapNotEnoughArgs
@ -15,7 +15,7 @@ interface ConstManaOperator : Operator {
fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>>
override fun operate(continuation: MutableList<ContinuationFrame>, stack: MutableList<SpellDatum<*>>, local: SpellDatum<*>, ctx: CastingContext): OperationResult { override fun operate(continuation: SpellContinuation, stack: MutableList<SpellDatum<*>>, local: SpellDatum<*>, ctx: CastingContext): OperationResult {
if (this.argc > stack.size) if (this.argc > stack.size)
throw MishapNotEnoughArgs(this.argc, stack.size) throw MishapNotEnoughArgs(this.argc, stack.size)
val args = stack.takeLast(this.argc) val args = stack.takeLast(this.argc)
@ -25,6 +25,6 @@ interface ConstManaOperator : Operator {
val sideEffects = mutableListOf<OperatorSideEffect>(OperatorSideEffect.ConsumeMana(this.manaCost)) val sideEffects = mutableListOf<OperatorSideEffect>(OperatorSideEffect.ConsumeMana(this.manaCost))
return OperationResult(stack, local, sideEffects) return OperationResult(continuation, stack, local, sideEffects)
} }
} }

View file

@ -1,8 +1,9 @@
package at.petrak.hexcasting.api.spell package at.petrak.hexcasting.api.spell
import at.petrak.hexcasting.api.spell.casting.OperatorSideEffect import at.petrak.hexcasting.api.spell.casting.OperatorSideEffect
import at.petrak.hexcasting.api.spell.casting.SpellContinuation
/** /**
* What happens when an operator is through? * What happens when an operator is through?
*/ */
data class OperationResult(val newStack: List<SpellDatum<*>>, val newLocalIota: SpellDatum<*>, val sideEffects: List<OperatorSideEffect>) data class OperationResult(val newContinuation: SpellContinuation, val newStack: List<SpellDatum<*>>, val newLocalIota: SpellDatum<*>, val sideEffects: List<OperatorSideEffect>)

View file

@ -1,7 +1,7 @@
package at.petrak.hexcasting.api.spell package at.petrak.hexcasting.api.spell
import at.petrak.hexcasting.api.spell.casting.CastingContext import at.petrak.hexcasting.api.spell.casting.CastingContext
import at.petrak.hexcasting.api.spell.casting.ContinuationFrame import at.petrak.hexcasting.api.spell.casting.SpellContinuation
import net.minecraft.world.phys.Vec3 import net.minecraft.world.phys.Vec3
/** /**
@ -22,7 +22,7 @@ interface Operator {
* *
* A particle effect at the cast site and various messages and advancements are done automagically. * A particle effect at the cast site and various messages and advancements are done automagically.
*/ */
fun operate(continuation: MutableList<ContinuationFrame>, stack: MutableList<SpellDatum<*>>, local: SpellDatum<*>, ctx: CastingContext): OperationResult fun operate(continuation: SpellContinuation, stack: MutableList<SpellDatum<*>>, local: SpellDatum<*>, ctx: CastingContext): OperationResult
/** /**
* Do you need to be enlightened to use this operator? * Do you need to be enlightened to use this operator?

View file

@ -1,7 +1,7 @@
package at.petrak.hexcasting.api.spell package at.petrak.hexcasting.api.spell
import at.petrak.hexcasting.api.spell.casting.CastingContext import at.petrak.hexcasting.api.spell.casting.CastingContext
import at.petrak.hexcasting.api.spell.casting.ContinuationFrame import at.petrak.hexcasting.api.spell.casting.SpellContinuation
import at.petrak.hexcasting.api.spell.casting.OperatorSideEffect import at.petrak.hexcasting.api.spell.casting.OperatorSideEffect
import at.petrak.hexcasting.api.spell.mishaps.MishapNotEnoughArgs import at.petrak.hexcasting.api.spell.mishaps.MishapNotEnoughArgs
@ -17,12 +17,12 @@ interface SpellOperator : Operator {
ctx: CastingContext ctx: CastingContext
): Triple<RenderedSpell, Int, List<ParticleSpray>>? ): Triple<RenderedSpell, Int, List<ParticleSpray>>?
override fun operate(continuation: MutableList<ContinuationFrame>, stack: MutableList<SpellDatum<*>>, local: SpellDatum<*>, ctx: CastingContext): OperationResult { override fun operate(continuation: SpellContinuation, stack: MutableList<SpellDatum<*>>, local: SpellDatum<*>, ctx: CastingContext): OperationResult {
if (this.argc > stack.size) if (this.argc > stack.size)
throw MishapNotEnoughArgs(this.argc, stack.size) throw MishapNotEnoughArgs(this.argc, stack.size)
val args = stack.takeLast(this.argc) val args = stack.takeLast(this.argc)
for (_i in 0 until this.argc) stack.removeLast() for (_i in 0 until this.argc) stack.removeLast()
val executeResult = this.execute(args, ctx) ?: return OperationResult(stack, local, listOf()) val executeResult = this.execute(args, ctx) ?: return OperationResult(continuation, stack, local, listOf())
val (spell, mana, particles) = executeResult val (spell, mana, particles) = executeResult
val sideEffects = mutableListOf( val sideEffects = mutableListOf(
@ -33,7 +33,7 @@ interface SpellOperator : Operator {
sideEffects.add(OperatorSideEffect.Particles(spray)) sideEffects.add(OperatorSideEffect.Particles(spray))
} }
return OperationResult(stack, local, sideEffects) return OperationResult(continuation, stack, local, sideEffects)
} }
} }

View file

@ -63,19 +63,20 @@ class CastingHarness private constructor(
*/ */
fun executeIotas(iotas: List<SpellDatum<*>>, world: ServerLevel): ControllerInfo { fun executeIotas(iotas: List<SpellDatum<*>>, world: ServerLevel): ControllerInfo {
// Initialize the continuation stack to a single top-level eval for all iotas. // Initialize the continuation stack to a single top-level eval for all iotas.
val continuation: MutableList<ContinuationFrame> = mutableListOf(ContinuationFrame.Evaluate(SpellList.LList(0, iotas))) var continuation = SpellContinuation.Done.pushFrame(ContinuationFrame.Evaluate(SpellList.LList(0, iotas)))
// Begin aggregating info // Begin aggregating info
val info = TempControllerInfo(false, false) val info = TempControllerInfo(false, false)
var lastResolutionType = ResolvedPatternType.UNKNOWN var lastResolutionType = ResolvedPatternType.UNKNOWN
while (continuation.isNotEmpty() && !info.earlyExit) { while (continuation is SpellContinuation.NotDone && !info.earlyExit) {
// Take the top of the continuation stack... // Take the top of the continuation stack...
val next = continuation.removeLast() val next = continuation.frame
// ...and execute it. // ...and execute it.
val result = next.evaluate(continuation, world, this) val result = next.evaluate(continuation.next, world, this)
// Then write all pertinent data back to the harness for the next iteration. // Then write all pertinent data back to the harness for the next iteration.
if (result.newData != null) { if (result.newData != null) {
this.applyFunctionalData(result.newData) this.applyFunctionalData(result.newData)
} }
continuation = result.continuation
lastResolutionType = result.resolutionType lastResolutionType = result.resolutionType
performSideEffects(info, result.sideEffects) performSideEffects(info, result.sideEffects)
info.earlyExit = info.earlyExit || !lastResolutionType.success info.earlyExit = info.earlyExit || !lastResolutionType.success
@ -89,16 +90,17 @@ class CastingHarness private constructor(
) )
} }
fun getUpdate(iota: SpellDatum<*>, world: ServerLevel, continuation: MutableList<ContinuationFrame>): CastResult { fun getUpdate(iota: SpellDatum<*>, world: ServerLevel, continuation: SpellContinuation): CastResult {
try { try {
this.handleParentheses(iota)?.let { (data, resolutionType) -> this.handleParentheses(iota)?.let { (data, resolutionType) ->
return@getUpdate CastResult(data, resolutionType, listOf()) return@getUpdate CastResult(continuation, data, resolutionType, listOf())
} }
return if (iota.getType() == DatumType.PATTERN) { return if (iota.getType() == DatumType.PATTERN) {
updateWithPattern(iota.payload as HexPattern, world, continuation) updateWithPattern(iota.payload as HexPattern, world, continuation)
} else { } else {
CastResult( CastResult(
continuation,
null, null,
ResolvedPatternType.INVALID, // Should never matter ResolvedPatternType.INVALID, // Should never matter
listOf( listOf(
@ -111,6 +113,7 @@ class CastingHarness private constructor(
} }
} catch (mishap: Mishap) { } catch (mishap: Mishap) {
return CastResult( return CastResult(
continuation,
null, null,
mishap.resolutionType(ctx), mishap.resolutionType(ctx),
listOf(OperatorSideEffect.DoMishap(mishap, Mishap.Context(iota.payload as? HexPattern ?: HexPattern(HexDir.WEST), null))), listOf(OperatorSideEffect.DoMishap(mishap, Mishap.Context(iota.payload as? HexPattern ?: HexPattern(HexDir.WEST), null))),
@ -118,6 +121,7 @@ class CastingHarness private constructor(
} catch (exception: Exception) { } catch (exception: Exception) {
exception.printStackTrace() exception.printStackTrace()
return CastResult( return CastResult(
continuation,
null, null,
ResolvedPatternType.ERROR, ResolvedPatternType.ERROR,
listOf(OperatorSideEffect.DoMishap(MishapError(exception), Mishap.Context(iota.payload as? HexPattern ?: HexPattern(HexDir.WEST), null))) listOf(OperatorSideEffect.DoMishap(MishapError(exception), Mishap.Context(iota.payload as? HexPattern ?: HexPattern(HexDir.WEST), null)))
@ -129,7 +133,7 @@ class CastingHarness private constructor(
* When the server gets a packet from the client with a new pattern, * When the server gets a packet from the client with a new pattern,
* handle it functionally. * handle it functionally.
*/ */
fun updateWithPattern(newPat: HexPattern, world: ServerLevel, continuation: MutableList<ContinuationFrame>): CastResult { fun updateWithPattern(newPat: HexPattern, world: ServerLevel, continuation: SpellContinuation): CastResult {
if (this.ctx.spellCircle == null) if (this.ctx.spellCircle == null)
this.ctx.caster.awardStat(HexStatistics.PATTERNS_DRAWN) this.ctx.caster.awardStat(HexStatistics.PATTERNS_DRAWN)
@ -141,7 +145,7 @@ class CastingHarness private constructor(
if (!HexConfig.server().isActionAllowed(operatorIdPair.second)) { if (!HexConfig.server().isActionAllowed(operatorIdPair.second)) {
throw MishapDisallowedSpell() throw MishapDisallowedSpell()
} }
val (stack2, local2, sideEffectsUnmut) = operatorIdPair.first.operate(continuation, this.stack.toMutableList(), this.localIota, this.ctx) val (cont2, stack2, local2, sideEffectsUnmut) = operatorIdPair.first.operate(continuation, this.stack.toMutableList(), this.localIota, this.ctx)
this.localIota = local2 this.localIota = local2
// Stick a poofy particle effect at the caster position // Stick a poofy particle effect at the caster position
val sideEffects = sideEffectsUnmut.toMutableList() val sideEffects = sideEffectsUnmut.toMutableList()
@ -161,12 +165,14 @@ class CastingHarness private constructor(
) )
return CastResult( return CastResult(
cont2,
fd, fd,
ResolvedPatternType.OK, ResolvedPatternType.OK,
sideEffects, sideEffects,
) )
} catch (mishap: Mishap) { } catch (mishap: Mishap) {
return CastResult( return CastResult(
continuation,
null, null,
mishap.resolutionType(ctx), mishap.resolutionType(ctx),
listOf(OperatorSideEffect.DoMishap(mishap, Mishap.Context(newPat, operatorIdPair?.second))), listOf(OperatorSideEffect.DoMishap(mishap, Mishap.Context(newPat, operatorIdPair?.second))),
@ -174,6 +180,7 @@ class CastingHarness private constructor(
} catch (exception: Exception) { } catch (exception: Exception) {
exception.printStackTrace() exception.printStackTrace()
return CastResult( return CastResult(
continuation,
null, null,
ResolvedPatternType.ERROR, ResolvedPatternType.ERROR,
listOf(OperatorSideEffect.DoMishap(MishapError(exception), Mishap.Context(newPat, operatorIdPair?.second))) listOf(OperatorSideEffect.DoMishap(MishapError(exception), Mishap.Context(newPat, operatorIdPair?.second)))
@ -469,6 +476,7 @@ class CastingHarness private constructor(
) )
data class CastResult( data class CastResult(
val continuation: SpellContinuation,
val newData: FunctionalData?, val newData: FunctionalData?,
val resolutionType: ResolvedPatternType, val resolutionType: ResolvedPatternType,
val sideEffects: List<OperatorSideEffect>, val sideEffects: List<OperatorSideEffect>,

View file

@ -23,7 +23,7 @@ sealed interface ContinuationFrame {
* For Evaluate, this consumes one pattern; for ForEach this queues the next iteration of the outer loop. * For Evaluate, this consumes one pattern; for ForEach this queues the next iteration of the outer loop.
* @return the result of this pattern step * @return the result of this pattern step
*/ */
fun evaluate(continuation: MutableList<ContinuationFrame>, level: ServerLevel, harness: CastingHarness): CastResult fun evaluate(continuation: SpellContinuation, level: ServerLevel, harness: CastingHarness): CastResult
/** /**
* The OpHalt instruction wants us to "jump to" the END of the nearest meta-eval. * The OpHalt instruction wants us to "jump to" the END of the nearest meta-eval.
* In other words, we should consume Evaluate frames until we hit a FinishEval or Thoth frame. * In other words, we should consume Evaluate frames until we hit a FinishEval or Thoth frame.
@ -35,25 +35,23 @@ sealed interface ContinuationFrame {
* A list of patterns to be evaluated in sequence. * A list of patterns to be evaluated in sequence.
* @property list the *remaining* list of patterns to be evaluated * @property list the *remaining* list of patterns to be evaluated
*/ */
data class Evaluate(var list: SpellList): ContinuationFrame { data class Evaluate(val list: SpellList): ContinuationFrame {
// Discard this frame and keep discarding frames. // Discard this frame and keep discarding frames.
override fun breakDownwards(stack: List<SpellDatum<*>>) = Pair(false, stack) override fun breakDownwards(stack: List<SpellDatum<*>>) = Pair(false, stack)
// Step the list of patterns, evaluating a single one. // Step the list of patterns, evaluating a single one.
override fun evaluate(continuation: MutableList<ContinuationFrame>, level: ServerLevel, harness: CastingHarness): CastResult { override fun evaluate(continuation: SpellContinuation, level: ServerLevel, harness: CastingHarness): CastResult {
// If there are patterns left... // If there are patterns left...
if (list.nonEmpty) { if (list.nonEmpty) {
val toEval = list.car val newCont = if (list.cdr.nonEmpty) { // yay TCO
list = list.cdr
if (list.nonEmpty) { // yay TCO
// ...enqueue the evaluation of the rest of the patterns... // ...enqueue the evaluation of the rest of the patterns...
continuation.add(this) continuation.pushFrame(Evaluate(list.cdr))
} } else continuation
// ...before evaluating the first one in the list. // ...before evaluating the first one in the list.
return harness.getUpdate(toEval, level, continuation) return harness.getUpdate(list.car, level, newCont)
} else { } else {
// If there are no patterns (e.g. empty Hermes), just return OK. // If there are no patterns (e.g. empty Hermes), just return OK.
return CastResult(null, ResolvedPatternType.OK, listOf()) return CastResult(continuation, null, ResolvedPatternType.OK, listOf())
} }
} }
@ -68,8 +66,8 @@ sealed interface ContinuationFrame {
override fun breakDownwards(stack: List<SpellDatum<*>>) = Pair(true, stack) override fun breakDownwards(stack: List<SpellDatum<*>>) = Pair(true, stack)
// Evaluating it does nothing; it's only a boundary condition. // Evaluating it does nothing; it's only a boundary condition.
override fun evaluate(continuation: MutableList<ContinuationFrame>, level: ServerLevel, harness: CastingHarness): CastResult { override fun evaluate(continuation: SpellContinuation, level: ServerLevel, harness: CastingHarness): CastResult {
return CastResult(FunctionalData(harness.stack.toList(), 0, listOf(), false), ResolvedPatternType.OK, listOf()) return CastResult(continuation, FunctionalData(harness.stack.toList(), 0, listOf(), false), ResolvedPatternType.OK, listOf())
} }
} }
@ -82,44 +80,45 @@ sealed interface ContinuationFrame {
* @property baseStack the stack state at Thoth entry * @property baseStack the stack state at Thoth entry
* @property acc concatenated list of final stack states after Thoth exit * @property acc concatenated list of final stack states after Thoth exit
*/ */
data class ForEach(var first: Boolean, var data: SpellList, val code: SpellList, val baseStack: List<SpellDatum<*>>, var acc: MutableList<SpellDatum<*>>): ContinuationFrame { data class ForEach(val data: SpellList, val code: SpellList, val baseStack: List<SpellDatum<*>>?, val acc: MutableList<SpellDatum<*>>): ContinuationFrame {
/** Helper to append something to the original stack. */
fun appendBase(iota: SpellDatum<*>): List<SpellDatum<*>> {
val mutStack = baseStack.toMutableList()
mutStack.add(iota)
return mutStack
}
/** When halting, we add the stack state at halt to the stack accumulator, then return the original pre-Thoth stack, plus the accumulator. */ /** 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<SpellDatum<*>>): Pair<Boolean, List<SpellDatum<*>>> { override fun breakDownwards(stack: List<SpellDatum<*>>): Pair<Boolean, List<SpellDatum<*>>> {
val newStack = baseStack!!.toMutableList()
acc.addAll(stack) acc.addAll(stack)
return Pair(true, appendBase(SpellDatum.make(acc))) newStack.add(SpellDatum.make(acc))
return Pair(true, newStack)
} }
/** Step the Thoth computation, enqueueing one code evaluation. */ /** Step the Thoth computation, enqueueing one code evaluation. */
override fun evaluate(continuation: MutableList<ContinuationFrame>, level: ServerLevel, harness: CastingHarness): CastResult { override fun evaluate(continuation: SpellContinuation, level: ServerLevel, harness: CastingHarness): CastResult {
// If this isn't the very first Thoth step (i.e. no Thoth computations run yet)... // If this isn't the very first Thoth step (i.e. no Thoth computations run yet)...
if (!first) { val stack = if (baseStack == null) {
// add everything we've added // init stack to the harness stack...
harness.stack.toList()
} else {
// else save the stack to the accumulator and reuse the saved base stack.
acc.addAll(harness.stack) acc.addAll(harness.stack)
baseStack
} }
first = false
// If we still have data to process... // If we still have data to process...
val stackTop = if (data.nonEmpty) { val (stackTop, newCont) = if (data.nonEmpty) {
// queue next datum for Thoth eval. Pair(
val toEval = data.car data.car, // Push the next datum to the top of the stack,
data = data.cdr continuation
// Put this Thoth object: back on the stack for the next Thoth cycle, // put the next Thoth object back on the stack for the next Thoth cycle,
continuation.add(this) .pushFrame(ForEach(data.cdr, code, stack, acc))
// and prep the Thoth'd code block for evaluation. // and prep the Thoth'd code block for evaluation.
continuation.add(Evaluate(code)) .pushFrame(Evaluate(code))
// Push the next datum to the top of the stack. )
toEval
} else { } else {
// Else, dump our final list onto the stack. // Else, dump our final list onto the stack.
SpellDatum.make(acc) Pair(SpellDatum.make(acc), continuation)
} }
return CastResult(FunctionalData(appendBase(stackTop), 0, listOf(), false), ResolvedPatternType.OK, listOf()) val tStack = stack.toMutableList()
tStack.add(stackTop)
return CastResult(newCont, FunctionalData(tStack, 0, listOf(), false), ResolvedPatternType.OK, listOf())
} }
} }
} }

View file

@ -0,0 +1,17 @@
package at.petrak.hexcasting.api.spell.casting
import at.petrak.hexcasting.api.spell.SpellDatum
import at.petrak.hexcasting.api.spell.SpellList
import at.petrak.hexcasting.api.spell.casting.CastingHarness.CastResult
import net.minecraft.server.level.ServerLevel
/**
* A continuation during the execution of a spell.
*/
sealed interface SpellContinuation {
object Done: SpellContinuation {}
data class NotDone(val frame: ContinuationFrame, val next: SpellContinuation): SpellContinuation {}
fun pushFrame(frame: ContinuationFrame): SpellContinuation = NotDone(frame, this)
}

View file

@ -9,20 +9,23 @@ import at.petrak.hexcasting.api.spell.casting.CastingContext
import at.petrak.hexcasting.api.spell.casting.CastingHarness import at.petrak.hexcasting.api.spell.casting.CastingHarness
import at.petrak.hexcasting.api.spell.casting.ContinuationFrame import at.petrak.hexcasting.api.spell.casting.ContinuationFrame
import at.petrak.hexcasting.api.spell.casting.OperatorSideEffect import at.petrak.hexcasting.api.spell.casting.OperatorSideEffect
import at.petrak.hexcasting.api.spell.casting.SpellContinuation
object OpEval : Operator { object OpEval : Operator {
override fun operate(continuation: MutableList<ContinuationFrame>, stack: MutableList<SpellDatum<*>>, local: SpellDatum<*>, ctx: CastingContext): OperationResult { override fun operate(continuation: SpellContinuation, stack: MutableList<SpellDatum<*>>, local: SpellDatum<*>, ctx: CastingContext): OperationResult {
val instrs: SpellList = stack.getChecked(stack.lastIndex) val instrs: SpellList = stack.getChecked(stack.lastIndex)
stack.removeLastOrNull() stack.removeLastOrNull()
ctx.incDepth() ctx.incDepth()
// if not installed already... // if not installed already...
if (!(continuation.isNotEmpty() && continuation.last() is ContinuationFrame.FinishEval)) { val newCont = if (continuation is SpellContinuation.NotDone && continuation.frame is ContinuationFrame.FinishEval) {
continuation.add(ContinuationFrame.FinishEval()) // install a break-boundary after eval continuation
} else {
continuation.pushFrame(ContinuationFrame.FinishEval()) // install a break-boundary after eval
} }
continuation.add(ContinuationFrame.Evaluate(instrs))
return OperationResult(stack, local, listOf()) val frame = ContinuationFrame.Evaluate(instrs)
return OperationResult(newCont.pushFrame(frame), stack, local, listOf())
} }
} }

View file

@ -4,10 +4,10 @@ import at.petrak.hexcasting.api.spell.OperationResult
import at.petrak.hexcasting.api.spell.Operator import at.petrak.hexcasting.api.spell.Operator
import at.petrak.hexcasting.api.spell.SpellDatum import at.petrak.hexcasting.api.spell.SpellDatum
import at.petrak.hexcasting.api.spell.casting.CastingContext import at.petrak.hexcasting.api.spell.casting.CastingContext
import at.petrak.hexcasting.api.spell.casting.ContinuationFrame import at.petrak.hexcasting.api.spell.casting.SpellContinuation
object OpEvalDelay : Operator { object OpEvalDelay : Operator {
override fun operate(continuation: MutableList<ContinuationFrame>, stack: MutableList<SpellDatum<*>>, local: SpellDatum<*>, ctx: CastingContext): OperationResult { override fun operate(continuation: SpellContinuation, stack: MutableList<SpellDatum<*>>, local: SpellDatum<*>, ctx: CastingContext): OperationResult {
return OperationResult(stack, local, listOf()) return OperationResult(continuation, stack, local, listOf())
} }
} }

View file

@ -10,9 +10,10 @@ import at.petrak.hexcasting.api.spell.casting.CastingHarness
import at.petrak.hexcasting.api.spell.casting.ContinuationFrame import at.petrak.hexcasting.api.spell.casting.ContinuationFrame
import at.petrak.hexcasting.api.spell.casting.OperatorSideEffect import at.petrak.hexcasting.api.spell.casting.OperatorSideEffect
import at.petrak.hexcasting.api.spell.mishaps.MishapNotEnoughArgs import at.petrak.hexcasting.api.spell.mishaps.MishapNotEnoughArgs
import at.petrak.hexcasting.api.spell.casting.SpellContinuation
object OpForEach : Operator { object OpForEach : Operator {
override fun operate(continuation: MutableList<ContinuationFrame>, stack: MutableList<SpellDatum<*>>, local: SpellDatum<*>, ctx: CastingContext): OperationResult { override fun operate(continuation: SpellContinuation, stack: MutableList<SpellDatum<*>>, local: SpellDatum<*>, ctx: CastingContext): OperationResult {
if (stack.size < 2) if (stack.size < 2)
throw MishapNotEnoughArgs(2, stack.size) throw MishapNotEnoughArgs(2, stack.size)
@ -21,8 +22,13 @@ object OpForEach : Operator {
stack.removeLastOrNull() stack.removeLastOrNull()
stack.removeLastOrNull() stack.removeLastOrNull()
continuation.add(ContinuationFrame.ForEach(true, datums, instrs, stack, mutableListOf())) val frame = ContinuationFrame.ForEach(datums, instrs, null, mutableListOf())
return OperationResult(stack, local, listOf()) return OperationResult(
continuation.pushFrame(frame),
stack,
local,
listOf()
)
} }
} }

View file

@ -7,18 +7,20 @@ import at.petrak.hexcasting.api.spell.SpellDatum
import at.petrak.hexcasting.api.spell.SpellList import at.petrak.hexcasting.api.spell.SpellList
import at.petrak.hexcasting.api.spell.casting.CastingContext import at.petrak.hexcasting.api.spell.casting.CastingContext
import at.petrak.hexcasting.api.spell.casting.CastingHarness import at.petrak.hexcasting.api.spell.casting.CastingHarness
import at.petrak.hexcasting.api.spell.casting.ContinuationFrame import at.petrak.hexcasting.api.spell.casting.SpellContinuation
import at.petrak.hexcasting.api.spell.casting.OperatorSideEffect import at.petrak.hexcasting.api.spell.casting.OperatorSideEffect
object OpHalt : Operator { object OpHalt : Operator {
override fun operate(continuation: MutableList<ContinuationFrame>, stack: MutableList<SpellDatum<*>>, local: SpellDatum<*>, ctx: CastingContext): OperationResult { override fun operate(continuation: SpellContinuation, stack: MutableList<SpellDatum<*>>, local: SpellDatum<*>, ctx: CastingContext): OperationResult {
var newStack = stack.toList() var newStack = stack.toList()
var done = false var done = false
while (!done && continuation.isNotEmpty()) { var newCont = continuation
while (!done && newCont is SpellContinuation.NotDone) {
// Kotlin Y U NO destructuring assignment // Kotlin Y U NO destructuring assignment
val newInfo = continuation.removeLast().breakDownwards(newStack) val newInfo = newCont.frame.breakDownwards(newStack)
done = newInfo.first done = newInfo.first
newStack = newInfo.second newStack = newInfo.second
newCont = newCont.next
} }
// if we hit no continuation boundaries (i.e. thoth/hermes exits), we've TOTALLY cleared the itinerary... // if we hit no continuation boundaries (i.e. thoth/hermes exits), we've TOTALLY cleared the itinerary...
if (!done) { if (!done) {
@ -26,6 +28,6 @@ object OpHalt : Operator {
newStack = listOf() newStack = listOf()
} }
return OperationResult(newStack, local, listOf()) return OperationResult(newCont, newStack, local, listOf())
} }
} }

View file

@ -2,7 +2,7 @@ package at.petrak.hexcasting.common.casting.operators.lists
import at.petrak.hexcasting.api.spell.* import at.petrak.hexcasting.api.spell.*
import at.petrak.hexcasting.api.spell.casting.CastingContext import at.petrak.hexcasting.api.spell.casting.CastingContext
import at.petrak.hexcasting.api.spell.casting.ContinuationFrame import at.petrak.hexcasting.api.spell.casting.SpellContinuation
import at.petrak.hexcasting.api.spell.mishaps.MishapInvalidIota import at.petrak.hexcasting.api.spell.mishaps.MishapInvalidIota
import at.petrak.hexcasting.api.spell.mishaps.MishapNotEnoughArgs import at.petrak.hexcasting.api.spell.mishaps.MishapNotEnoughArgs
import net.minecraft.network.chat.TranslatableComponent import net.minecraft.network.chat.TranslatableComponent
@ -10,7 +10,7 @@ import kotlin.math.abs
import kotlin.math.roundToInt import kotlin.math.roundToInt
object OpLastNToList : Operator { object OpLastNToList : Operator {
override fun operate(continuation: MutableList<ContinuationFrame>, stack: MutableList<SpellDatum<*>>, local: SpellDatum<*>, ctx: CastingContext): OperationResult { override fun operate(continuation: SpellContinuation, stack: MutableList<SpellDatum<*>>, local: SpellDatum<*>, ctx: CastingContext): OperationResult {
if (stack.isEmpty()) if (stack.isEmpty())
throw MishapNotEnoughArgs(1, 0) throw MishapNotEnoughArgs(1, 0)
val arg = stack.takeLast(1).getChecked<Double>(0) val arg = stack.takeLast(1).getChecked<Double>(0)
@ -31,6 +31,6 @@ object OpLastNToList : Operator {
} }
stack.addAll(output.asSpellResult) stack.addAll(output.asSpellResult)
return OperationResult(stack, local, listOf()) return OperationResult(continuation, stack, local, listOf())
} }
} }

View file

@ -4,16 +4,16 @@ import at.petrak.hexcasting.api.spell.OperationResult
import at.petrak.hexcasting.api.spell.Operator import at.petrak.hexcasting.api.spell.Operator
import at.petrak.hexcasting.api.spell.SpellDatum import at.petrak.hexcasting.api.spell.SpellDatum
import at.petrak.hexcasting.api.spell.casting.CastingContext import at.petrak.hexcasting.api.spell.casting.CastingContext
import at.petrak.hexcasting.api.spell.casting.ContinuationFrame import at.petrak.hexcasting.api.spell.casting.SpellContinuation
object OpPeekLocal : Operator { object OpPeekLocal : Operator {
override fun operate( override fun operate(
continuation: MutableList<ContinuationFrame>, continuation: SpellContinuation,
stack: MutableList<SpellDatum<*>>, stack: MutableList<SpellDatum<*>>,
local: SpellDatum<*>, local: SpellDatum<*>,
ctx: CastingContext ctx: CastingContext
): OperationResult { ): OperationResult {
stack.add(local) stack.add(local)
return OperationResult(stack, local, listOf()) return OperationResult(continuation, stack, local, listOf())
} }
} }

View file

@ -5,11 +5,11 @@ import at.petrak.hexcasting.api.spell.Operator
import at.petrak.hexcasting.api.spell.SpellDatum import at.petrak.hexcasting.api.spell.SpellDatum
import at.petrak.hexcasting.api.spell.casting.CastingContext import at.petrak.hexcasting.api.spell.casting.CastingContext
import at.petrak.hexcasting.api.spell.mishaps.MishapNotEnoughArgs import at.petrak.hexcasting.api.spell.mishaps.MishapNotEnoughArgs
import at.petrak.hexcasting.api.spell.casting.ContinuationFrame import at.petrak.hexcasting.api.spell.casting.SpellContinuation
object OpPushLocal : Operator { object OpPushLocal : Operator {
override fun operate( override fun operate(
continuation: MutableList<ContinuationFrame>, continuation: SpellContinuation,
stack: MutableList<SpellDatum<*>>, stack: MutableList<SpellDatum<*>>,
local: SpellDatum<*>, local: SpellDatum<*>,
ctx: CastingContext ctx: CastingContext
@ -17,6 +17,6 @@ object OpPushLocal : Operator {
if (stack.isEmpty()) if (stack.isEmpty())
throw MishapNotEnoughArgs(1, 0) throw MishapNotEnoughArgs(1, 0)
val newLocal = stack.removeLast() val newLocal = stack.removeLast()
return OperationResult(stack, newLocal, listOf()) return OperationResult(continuation, stack, newLocal, listOf())
} }
} }

View file

@ -5,19 +5,19 @@ import at.petrak.hexcasting.api.spell.Operator
import at.petrak.hexcasting.api.spell.RenderedSpell import at.petrak.hexcasting.api.spell.RenderedSpell
import at.petrak.hexcasting.api.spell.SpellDatum import at.petrak.hexcasting.api.spell.SpellDatum
import at.petrak.hexcasting.api.spell.casting.CastingContext import at.petrak.hexcasting.api.spell.casting.CastingContext
import at.petrak.hexcasting.api.spell.casting.ContinuationFrame import at.petrak.hexcasting.api.spell.casting.SpellContinuation
import at.petrak.hexcasting.api.spell.casting.OperatorSideEffect import at.petrak.hexcasting.api.spell.casting.OperatorSideEffect
import at.petrak.hexcasting.api.spell.mishaps.MishapNotEnoughArgs import at.petrak.hexcasting.api.spell.mishaps.MishapNotEnoughArgs
import net.minecraft.Util import net.minecraft.Util
object OpPrint : Operator { object OpPrint : Operator {
override fun operate(continuation: MutableList<ContinuationFrame>, stack: MutableList<SpellDatum<*>>, local: SpellDatum<*>, ctx: CastingContext): OperationResult { override fun operate(continuation: SpellContinuation, stack: MutableList<SpellDatum<*>>, local: SpellDatum<*>, ctx: CastingContext): OperationResult {
if (stack.isEmpty()) { if (stack.isEmpty()) {
throw MishapNotEnoughArgs(1, 0) throw MishapNotEnoughArgs(1, 0)
} }
val datum = stack[stack.lastIndex] val datum = stack[stack.lastIndex]
return OperationResult( return OperationResult(
stack, local, listOf( continuation, stack, local, listOf(
OperatorSideEffect.AttemptSpell(Spell(datum), false) OperatorSideEffect.AttemptSpell(Spell(datum), false)
) )
) )

View file

@ -6,10 +6,10 @@ import at.petrak.hexcasting.api.spell.Operator
import at.petrak.hexcasting.api.spell.getChecked import at.petrak.hexcasting.api.spell.getChecked
import at.petrak.hexcasting.api.spell.SpellDatum import at.petrak.hexcasting.api.spell.SpellDatum
import at.petrak.hexcasting.api.spell.casting.CastingContext import at.petrak.hexcasting.api.spell.casting.CastingContext
import at.petrak.hexcasting.api.spell.casting.ContinuationFrame
import at.petrak.hexcasting.api.spell.casting.OperatorSideEffect import at.petrak.hexcasting.api.spell.casting.OperatorSideEffect
import at.petrak.hexcasting.api.spell.mishaps.MishapInvalidIota import at.petrak.hexcasting.api.spell.mishaps.MishapInvalidIota
import at.petrak.hexcasting.api.spell.mishaps.MishapNotEnoughArgs import at.petrak.hexcasting.api.spell.mishaps.MishapNotEnoughArgs
import at.petrak.hexcasting.api.spell.casting.SpellContinuation
import it.unimi.dsi.fastutil.ints.IntArrayList import it.unimi.dsi.fastutil.ints.IntArrayList
import net.minecraft.network.chat.TranslatableComponent import net.minecraft.network.chat.TranslatableComponent
import kotlin.math.abs import kotlin.math.abs
@ -18,7 +18,7 @@ import kotlin.math.roundToInt
// "lehmer code" // "lehmer code"
object OpAlwinfyHasAscendedToABeingOfPureMath : Operator { object OpAlwinfyHasAscendedToABeingOfPureMath : Operator {
override fun operate(continuation: MutableList<ContinuationFrame>, stack: MutableList<SpellDatum<*>>, local: SpellDatum<*>, ctx: CastingContext): OperationResult { override fun operate(continuation: SpellContinuation, stack: MutableList<SpellDatum<*>>, local: SpellDatum<*>, ctx: CastingContext): OperationResult {
if (stack.isEmpty()) if (stack.isEmpty())
throw MishapNotEnoughArgs(1, 0) // todo: better message? throw MishapNotEnoughArgs(1, 0) // todo: better message?
@ -56,6 +56,7 @@ object OpAlwinfyHasAscendedToABeingOfPureMath : Operator {
val cost = (ln((strides.lastOrNull() ?: 0).toFloat()) * ManaConstants.DUST_UNIT).toInt() val cost = (ln((strides.lastOrNull() ?: 0).toFloat()) * ManaConstants.DUST_UNIT).toInt()
return OperationResult( return OperationResult(
continuation,
stack, stack,
local, local,
listOf(OperatorSideEffect.ConsumeMana(cost)) listOf(OperatorSideEffect.ConsumeMana(cost))

View file

@ -5,7 +5,7 @@ import at.petrak.hexcasting.api.spell.Operator
import at.petrak.hexcasting.api.spell.getChecked import at.petrak.hexcasting.api.spell.getChecked
import at.petrak.hexcasting.api.spell.SpellDatum import at.petrak.hexcasting.api.spell.SpellDatum
import at.petrak.hexcasting.api.spell.casting.CastingContext import at.petrak.hexcasting.api.spell.casting.CastingContext
import at.petrak.hexcasting.api.spell.casting.ContinuationFrame import at.petrak.hexcasting.api.spell.casting.SpellContinuation
import at.petrak.hexcasting.api.spell.mishaps.MishapInvalidIota import at.petrak.hexcasting.api.spell.mishaps.MishapInvalidIota
import at.petrak.hexcasting.api.spell.mishaps.MishapNotEnoughArgs import at.petrak.hexcasting.api.spell.mishaps.MishapNotEnoughArgs
import net.minecraft.network.chat.TranslatableComponent import net.minecraft.network.chat.TranslatableComponent
@ -13,7 +13,7 @@ import kotlin.math.abs
import kotlin.math.roundToInt import kotlin.math.roundToInt
object OpFisherman : Operator { object OpFisherman : Operator {
override fun operate(continuation: MutableList<ContinuationFrame>, stack: MutableList<SpellDatum<*>>, local: SpellDatum<*>, ctx: CastingContext): OperationResult { override fun operate(continuation: SpellContinuation, stack: MutableList<SpellDatum<*>>, local: SpellDatum<*>, ctx: CastingContext): OperationResult {
if (stack.isEmpty()) if (stack.isEmpty())
throw MishapNotEnoughArgs(1, 0) throw MishapNotEnoughArgs(1, 0)
val arg = stack.getChecked<Double>(stack.lastIndex) val arg = stack.getChecked<Double>(stack.lastIndex)
@ -32,6 +32,6 @@ object OpFisherman : Operator {
) )
} }
return OperationResult(stack, local, listOf()) return OperationResult(continuation, stack, local, listOf())
} }
} }

View file

@ -4,11 +4,11 @@ import at.petrak.hexcasting.api.spell.OperationResult
import at.petrak.hexcasting.api.spell.Operator import at.petrak.hexcasting.api.spell.Operator
import at.petrak.hexcasting.api.spell.SpellDatum import at.petrak.hexcasting.api.spell.SpellDatum
import at.petrak.hexcasting.api.spell.casting.CastingContext import at.petrak.hexcasting.api.spell.casting.CastingContext
import at.petrak.hexcasting.api.spell.casting.ContinuationFrame import at.petrak.hexcasting.api.spell.casting.SpellContinuation
object OpStackSize : Operator { object OpStackSize : Operator {
override fun operate(continuation: MutableList<ContinuationFrame>, stack: MutableList<SpellDatum<*>>, local: SpellDatum<*>, ctx: CastingContext): OperationResult { override fun operate(continuation: SpellContinuation, stack: MutableList<SpellDatum<*>>, local: SpellDatum<*>, ctx: CastingContext): OperationResult {
stack.add(SpellDatum.make(stack.size.toDouble())) stack.add(SpellDatum.make(stack.size.toDouble()))
return OperationResult(stack, local, listOf()) return OperationResult(continuation, stack, local, listOf())
} }
} }