Reimplemented divide by 0 mishaps in DoubleArithmetic, fixed mishaps not being able to alter the stack (accepting mutable copy of immutable list, changes to copy not changing original)

This commit is contained in:
Talia-12 2023-06-01 20:50:40 +10:00
parent 7c6b6e530d
commit d51fe053e3
7 changed files with 126 additions and 119 deletions

View file

@ -3,6 +3,7 @@ package at.petrak.hexcasting.api.casting.arithmetic.operator;
import at.petrak.hexcasting.api.casting.arithmetic.predicates.IotaMultiPredicate;
import at.petrak.hexcasting.api.casting.iota.Iota;
import at.petrak.hexcasting.api.casting.iota.IotaType;
import at.petrak.hexcasting.api.casting.mishaps.Mishap;
import org.jetbrains.annotations.NotNull;
/**
@ -32,11 +33,13 @@ public abstract class Operator {
}
/**
/**
* The method called when this Operator is actually acting on the stack, for real.
* @param iotas An iterable of iotas with {@link Operator#arity} elements that satisfied {@link Operator#accepts}.
* @return the iotas that this operator will return to the stack (with the first element of the returned iterable being placed deepest into the stack, and the last element on top of the stack).
* @throws Mishap if the Operator mishaps for any reason it will be passed up the chain.
*/
public abstract @NotNull Iterable<Iota> apply(@NotNull Iterable<Iota> iotas);
public abstract @NotNull Iterable<Iota> apply(@NotNull Iterable<Iota> iotas) throws Mishap;
/**
* A helper method to take an iota that you know is of iotaType and returning it as an iota of that type.

View file

@ -76,7 +76,7 @@ sealed class OperatorSideEffect {
)
)
mishap.execute(harness.env, errorCtx, harness.image.stack.toMutableList())
harness.image = harness.image.copy(stack = mishap.executeReturnStack(harness.env, errorCtx, harness.image.stack.toMutableList()))
return true
}

View file

@ -39,12 +39,17 @@ abstract class Mishap : Throwable() {
*/
abstract fun execute(ctx: CastingEnvironment, errorCtx: Context, stack: MutableList<Iota>)
fun executeReturnStack(ctx: CastingEnvironment, errorCtx: Context, stack: MutableList<Iota>): List<Iota> {
execute(ctx, errorCtx, stack)
return stack
}
protected abstract fun errorMessage(ctx: CastingEnvironment, errorCtx: Context): Component
/**
* Every error message should be prefixed with the name of the action...
*/
public fun errorMessageWithName(ctx: CastingEnvironment, errorCtx: Context): Component {
fun errorMessageWithName(ctx: CastingEnvironment, errorCtx: Context): Component {
return if (errorCtx.name != null) {
"hexcasting.mishap".asTranslatedComponent(errorCtx.name, this.errorMessage(ctx, errorCtx))
} else {

View file

@ -27,6 +27,13 @@ class MishapDivideByZero(val operand1: Component, val operand2: Component, val s
companion object {
private const val PREFIX = "hexcasting.mishap.divide_by_zero"
@JvmStatic
fun of(operand1: Double, operand2: Double, suffix: String = "divide"): MishapDivideByZero {
if (suffix == "exponent")
return MishapDivideByZero(translate(DoubleIota(operand1)), powerOf(DoubleIota(operand2)), suffix)
return MishapDivideByZero(translate(DoubleIota(operand1)), translate(DoubleIota(operand2)), suffix)
}
@JvmStatic
fun of(operand1: Iota, operand2: Iota, suffix: String = "divide"): MishapDivideByZero {
if (suffix == "exponent")
@ -34,6 +41,15 @@ class MishapDivideByZero(val operand1: Component, val operand2: Component, val s
return MishapDivideByZero(translate(operand1), translate(operand2), suffix)
}
@JvmStatic
fun tan(angle: Double): MishapDivideByZero {
val translatedAngle = translate(DoubleIota(angle))
return MishapDivideByZero(
"$PREFIX.sin".asTranslatedComponent(translatedAngle),
"$PREFIX.cos".asTranslatedComponent(translatedAngle)
)
}
@JvmStatic
fun tan(angle: DoubleIota): MishapDivideByZero {
val translatedAngle = translate(angle)

View file

@ -1,104 +1,80 @@
package at.petrak.hexcasting.common.casting.arithmetic;
package at.petrak.hexcasting.common.casting.arithmetic
import at.petrak.hexcasting.api.casting.arithmetic.Arithmetic;
import at.petrak.hexcasting.api.casting.arithmetic.predicates.IotaMultiPredicate;
import at.petrak.hexcasting.api.casting.arithmetic.predicates.IotaPredicate;
import at.petrak.hexcasting.api.casting.arithmetic.operator.Operator;
import at.petrak.hexcasting.api.casting.arithmetic.operator.OperatorBinary;
import at.petrak.hexcasting.api.casting.arithmetic.operator.OperatorUnary;
import at.petrak.hexcasting.api.casting.iota.DoubleIota;
import at.petrak.hexcasting.api.casting.math.HexPattern;
import at.petrak.hexcasting.common.casting.arithmetic.operator.OperatorLog;
import at.petrak.hexcasting.api.casting.arithmetic.Arithmetic
import at.petrak.hexcasting.api.casting.arithmetic.Arithmetic.*
import at.petrak.hexcasting.api.casting.arithmetic.engine.InvalidOperatorException
import at.petrak.hexcasting.api.casting.arithmetic.operator.Operator
import at.petrak.hexcasting.api.casting.arithmetic.operator.OperatorBinary
import at.petrak.hexcasting.api.casting.arithmetic.operator.OperatorUnary
import at.petrak.hexcasting.api.casting.arithmetic.predicates.IotaMultiPredicate
import at.petrak.hexcasting.api.casting.arithmetic.predicates.IotaPredicate
import at.petrak.hexcasting.api.casting.iota.DoubleIota
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.casting.math.HexPattern
import at.petrak.hexcasting.api.casting.mishaps.MishapDivideByZero
import at.petrak.hexcasting.common.casting.arithmetic.operator.OperatorLog
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes
import java.util.function.DoubleBinaryOperator
import java.util.function.DoubleUnaryOperator
import kotlin.math.*
import java.util.List;
import java.util.function.DoubleBinaryOperator;
import java.util.function.DoubleUnaryOperator;
object DoubleArithmetic : Arithmetic {
@JvmField
val OPS = listOf(
ADD,
SUB,
MUL,
DIV,
ABS,
POW,
FLOOR,
CEIL,
SIN,
COS,
TAN,
ARCSIN,
ARCCOS,
ARCTAN,
ARCTAN2,
LOG,
MOD
)
import static at.petrak.hexcasting.api.casting.arithmetic.operator.Operator.downcast;
import static at.petrak.hexcasting.common.lib.hex.HexIotaTypes.DOUBLE;
/**
* An example of an IotaMultiPredicate, which returns true only if all arguments to the Operator are DoubleIotas.
*/
val ACCEPTS: IotaMultiPredicate = IotaMultiPredicate.all(IotaPredicate.ofType(HexIotaTypes.DOUBLE))
public enum DoubleArithmetic implements Arithmetic {
INSTANCE;
override fun arithName() = "double_math"
public static final List<HexPattern> OPS = List.of(
ADD,
SUB,
MUL,
DIV,
ABS,
POW,
FLOOR,
CEIL,
SIN,
COS,
TAN,
ARCSIN,
ARCCOS,
ARCTAN,
ARCTAN2,
LOG,
MOD
);
override fun opTypes() = OPS
/**
* An example of an IotaMultiPredicate, which returns true only if all arguments to the Operator are DoubleIotas.
*/
public static final IotaMultiPredicate ACCEPTS = IotaMultiPredicate.all(IotaPredicate.ofType(DOUBLE));
override fun getOperator(pattern: HexPattern): Operator {
return when (pattern) {
ADD -> make2 { a, b -> java.lang.Double.sum(a, b) }
SUB -> make2 { a, b -> a - b }
MUL -> make2 { a, b -> a * b }
DIV -> make2 { a, b -> if (b == 0.0) throw MishapDivideByZero.of(a, b) else a / b }
ABS -> make1 { a -> abs(a) }
POW -> make2 { a, b -> a.pow(b) }
FLOOR -> make1 { a -> floor(a) }
CEIL -> make1 { a -> ceil(a) }
SIN -> make1 { a -> sin(a) }
COS -> make1 { a -> cos(a) }
TAN -> make1 { a -> if (cos(a) == 0.0) throw MishapDivideByZero.tan(a) else tan(a) }
ARCSIN -> make1 { a -> asin(a) }
ARCCOS -> make1 { a -> acos(a) }
ARCTAN -> make1 { a -> atan(a) }
ARCTAN2 -> make2 { a, b -> atan2(a, b) }
LOG -> OperatorLog
MOD -> make2 { a, b -> if (b == 0.0) throw MishapDivideByZero.of(a, b) else a % b }
else -> throw InvalidOperatorException("$pattern is not a valid operator in Arithmetic $this.")
}
}
@Override
public String arithName() {
return "double_math";
}
fun make1(op: DoubleUnaryOperator) = OperatorUnary(ACCEPTS)
{ i: Iota -> DoubleIota(op.applyAsDouble(Operator.downcast(i, HexIotaTypes.DOUBLE).double)) }
@Override
public Iterable<HexPattern> opTypes() {
return OPS;
}
@Override
public Operator getOperator(HexPattern pattern) {
if (pattern.equals(ADD)) {
return make2(Double::sum);
} else if (pattern.equals(SUB)) {
return make2((p, q) -> p - q);
} else if (pattern.equals(MUL)) {
return make2((p, q) -> p * q);
} else if (pattern.equals(DIV)) {
return make2((p, q) -> p / q);
} else if (pattern.equals(ABS)) {
return make1(Math::abs);
} else if (pattern.equals(POW)) {
return make2(Math::pow);
} else if (pattern.equals(FLOOR)) {
return make1(Math::floor);
} else if (pattern.equals(CEIL)) {
return make1(Math::ceil);
} else if (pattern.equals(SIN)) {
return make1(Math::sin);
} else if (pattern.equals(COS)) {
return make1(Math::cos);
} else if (pattern.equals(TAN)) {
return make1(Math::tan);
} else if (pattern.equals(ARCSIN)) {
return make1(Math::asin);
} else if (pattern.equals(ARCCOS)) {
return make1(Math::acos);
} else if (pattern.equals(ARCTAN)) {
return make1(Math::atan);
} else if (pattern.equals(ARCTAN2)) {
return make2(Math::atan2);
} else if (pattern.equals(LOG)) {
return OperatorLog.INSTANCE;
} else if (pattern.equals(MOD)) {
return make2((p, q) -> p % q);
}
return null;
}
public static OperatorUnary make1(DoubleUnaryOperator op) {
return new OperatorUnary(ACCEPTS, i -> new DoubleIota(op.applyAsDouble(downcast(i, DOUBLE).getDouble())));
}
public static OperatorBinary make2(DoubleBinaryOperator op) {
return new OperatorBinary(ACCEPTS, (i, j) -> new DoubleIota(op.applyAsDouble(downcast(i, DOUBLE).getDouble(), downcast(j, DOUBLE).getDouble())));
}
fun make2(op: DoubleBinaryOperator) = OperatorBinary(ACCEPTS)
{ i: Iota, j: Iota -> DoubleIota(op.applyAsDouble(Operator.downcast(i, HexIotaTypes.DOUBLE).double, Operator.downcast(j, HexIotaTypes.DOUBLE).double)) }
}

View file

@ -13,7 +13,6 @@ import at.petrak.hexcasting.common.casting.arithmetic.operator.vec.OperatorUnpac
import at.petrak.hexcasting.common.casting.arithmetic.operator.vec.OperatorVec3Delegating;
import net.minecraft.world.phys.Vec3;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function;
@ -24,15 +23,17 @@ import static at.petrak.hexcasting.common.lib.hex.HexIotaTypes.*;
public enum Vec3Arithmetic implements Arithmetic {
INSTANCE;
public static final List<HexPattern> OPS;
static {
var ops = new ArrayList<>(DoubleArithmetic.OPS);
ops.add(PACK);
ops.add(UNPACK);
ops.remove(FLOOR);
ops.remove(CEIL);
OPS = ops;
}
public static final List<HexPattern> OPS = List.of(
PACK,
UNPACK,
ADD,
SUB,
MUL,
DIV,
ABS,
POW,
MOD
);
public static final IotaMultiPredicate ACCEPTS = IotaMultiPredicate.any(IotaPredicate.ofType(VEC3), IotaPredicate.ofType(DOUBLE));

View file

@ -5,6 +5,8 @@ import at.petrak.hexcasting.api.casting.arithmetic.predicates.IotaPredicate;
import at.petrak.hexcasting.api.casting.arithmetic.IterPair;
import at.petrak.hexcasting.api.casting.arithmetic.TripleIterable;
import at.petrak.hexcasting.api.casting.arithmetic.operator.Operator;
import at.petrak.hexcasting.api.casting.mishaps.Mishap;
import at.petrak.hexcasting.api.casting.mishaps.MishapDivideByZero;
import at.petrak.hexcasting.common.casting.arithmetic.DoubleArithmetic;
import at.petrak.hexcasting.api.casting.iota.DoubleIota;
import at.petrak.hexcasting.api.casting.iota.Iota;
@ -30,21 +32,25 @@ public class OperatorVec3Delegating extends Operator {
}
@Override
public @NotNull Iterable<Iota> apply(@NotNull Iterable<Iota> iotas) {
public @NotNull Iterable<Iota> apply(@NotNull Iterable<Iota> iotas) throws Mishap {
var it = iotas.iterator();
var left = it.next();
var right = it.next();
if (op != null && left instanceof Vec3Iota lh && right instanceof Vec3Iota rh) {
return List.of(op.apply(lh.getVec3(), rh.getVec3()));
try {
if (op != null && left instanceof Vec3Iota lh && right instanceof Vec3Iota rh) {
return List.of(op.apply(lh.getVec3(), rh.getVec3()));
}
var lh = left instanceof Vec3Iota l ? l.getVec3() : triplicate(downcast(left, DOUBLE).getDouble());
var rh = right instanceof Vec3Iota r ? r.getVec3() : triplicate(downcast(right, DOUBLE).getDouble());
return new TripleIterable<>(
fb.apply(new IterPair<>(new DoubleIota(lh.x()), new DoubleIota(rh.x()))),
fb.apply(new IterPair<>(new DoubleIota(lh.y()), new DoubleIota(rh.y()))),
fb.apply(new IterPair<>(new DoubleIota(lh.z()), new DoubleIota(rh.z()))),
(x, y, z) -> new Vec3Iota(new Vec3(downcast(x, DOUBLE).getDouble(), downcast(y, DOUBLE).getDouble(), downcast(z, DOUBLE).getDouble()))
);
} catch (MishapDivideByZero e) {
throw MishapDivideByZero.of(left, right, e.getSuffix());
}
var lh = left instanceof Vec3Iota l ? l.getVec3() : triplicate(downcast(left, DOUBLE).getDouble());
var rh = right instanceof Vec3Iota r ? r.getVec3() : triplicate(downcast(right, DOUBLE).getDouble());
return new TripleIterable<>(
fb.apply(new IterPair<>(new DoubleIota(lh.x()), new DoubleIota(rh.x()))),
fb.apply(new IterPair<>(new DoubleIota(lh.y()), new DoubleIota(rh.y()))),
fb.apply(new IterPair<>(new DoubleIota(lh.z()), new DoubleIota(rh.z()))),
(x, y, z) -> new Vec3Iota(new Vec3(downcast(x, DOUBLE).getDouble(), downcast(y, DOUBLE).getDouble(), downcast(z, DOUBLE).getDouble()))
);
}
public static Vec3 triplicate(double in) {