package com.hypixel.hytale.server.npc.util.expression;
import com.hypixel.hytale.common.util.ArrayUtil;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.BooleanSupplier;
import java.util.function.DoubleSupplier;
import java.util.function.Supplier;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class ExecutionContext {
public static final int STACK_GROW_INCREMENT = 8;
protected Scope scope;
protected Operand[] operandStack;
protected int stackTop;
protected ValueType lastPushedType;
protected String combatConfig;
protected Map<String, String> interactionVars;
public static final Instruction UNARY_PLUS = (context) -> {
};
public static final Instruction UNARY_MINUS = (context) -> context.push(-context.popNumber());
public static final Instruction LOGICAL_NOT = (context) -> context.push(!context.popBoolean());
public static final Instruction BITWISE_NOT = (context) -> context.push(~context.popInt());
public static final Instruction EXPONENTIATION = (context) -> context.popPush(Math.pow(context.getNumber(1), context.getNumber(0)), 2);
public static final Instruction REMAINDER = (context) -> context.popPush(context.getNumber(1) % context.getNumber(0), 2);
public static final Instruction DIVIDE = (context) -> context.popPush(context.getNumber(1) / context.getNumber(0), 2);
public static final Instruction MULTIPLY = (context) -> context.popPush(context.getNumber(1) * context.getNumber(0), 2);
public static final Instruction MINUS = (context) -> context.popPush(context.getNumber(1) - context.getNumber(0), 2);
public static final Instruction PLUS = (context) -> context.popPush(context.getNumber(1) + context.getNumber(0), 2);
public static final Instruction GREATER_EQUAL = (context) -> context.popPush(context.getNumber(1) >= context.getNumber(0), 2);
public static final Instruction GREATER = (context) -> context.popPush(context.getNumber(1) > context.getNumber(0), 2);
public static final Instruction LESS_EQUAL = (context) -> context.popPush(context.getNumber(1) <= context.getNumber(0), 2);
public static final Instruction LESS = (context) -> context.popPush(context.getNumber(1) < context.getNumber(0), 2);
public static final Instruction NOT_EQUAL = (context) -> context.popPush(context.getNumber(1) != context.getNumber(0), 2);
public static final Instruction EQUAL = (context) -> context.popPush(context.getNumber(1) == context.getNumber(0), 2);
public static final Instruction NOT_EQUAL_BOOL = (context) -> context.popPush(context.getBoolean(1) != context.getBoolean(0), 2);
public static final Instruction EQUAL_BOOL = (context) -> context.popPush(context.getBoolean(1) == context.getBoolean(0), 2);
public static final Instruction BITWISE_AND = (context) -> context.popPush(context.getInt(1) & context.getInt(0), 2);
public static final Instruction BITWISE_XOR = (context) -> context.popPush(context.getInt(1) ^ context.getInt(0), 2);
public static final Instruction BITWISE_OR = (context) -> context.popPush(context.getInt(1) | context.getInt(0), 2);
public static final Instruction LOGICAL_AND = (context) -> context.popPush(context.getBoolean(1) && context.getBoolean(0), 2);
public static final Instruction LOGICAL_OR = (context) -> context.popPush(context.getBoolean(1) || context.getBoolean(0), 2);
public ExecutionContext(Scope scope) {
this.scope = scope;
this.operandStack = new Operand[8];
for(int i = 0; i < this.operandStack.length; ++i) {
this.operandStack[i] = new Operand();
}
}
public ExecutionContext() {
this((Scope)null);
}
public ValueType execute(@Nonnull List<Instruction> instructions, Scope scope) {
this.setScope(scope);
return this.execute(instructions);
}
public ValueType execute(@Nonnull List<Instruction> instructions) {
Objects.requireNonNull(this.scope, "Scope not initialised executing instructions");
Objects.requireNonNull(instructions, "Instruction sequence is null executing instructions");
this.stackTop = -1;
this.lastPushedType = ValueType.VOID;
instructions.forEach((instruction) -> instruction.execute(this));
return this.getType();
}
public ValueType execute(@Nonnull Instruction[] instructions, Scope scope) {
this.setScope(scope);
return this.execute(instructions);
}
public ValueType execute(@Nonnull Instruction[] instructions) {
Objects.requireNonNull(this.scope, "Scope not initialised executing instructions");
Objects.requireNonNull(instructions, "Instruction sequence is null executing instructions");
try {
this.stackTop = -1;
this.lastPushedType = ValueType.VOID;
for(Instruction instruction : instructions) {
instruction.execute(this);
}
return this.getType();
} catch (Throwable t) {
throw new IllegalStateException("Failed to execute instruction sequence: ", t);
}
}
public ValueType getType() {
return this.lastPushedType;
}
public Operand top() {
return this.get(0);
}
public Scope setScope(Scope scope) {
Scope oldScope = this.getScope();
this.scope = scope;
return oldScope;
}
public Scope getScope() {
return this.scope;
}
public String getCombatConfig() {
return this.combatConfig;
}
public void setCombatConfig(String combatConfig) {
this.combatConfig = combatConfig;
}
public Map<String, String> getInteractionVars() {
return this.interactionVars;
}
public void setInteractionVars(Map<String, String> interactionVars) {
this.interactionVars = interactionVars;
}
protected Operand push() {
++this.stackTop;
if (this.operandStack.length <= this.stackTop) {
int i = this.operandStack.length;
for(this.operandStack = (Operand[])Arrays.copyOf(this.operandStack, i + 8); i < this.operandStack.length; this.operandStack[i++] = new Operand()) {
}
}
return this.operandStack[this.stackTop];
}
public void push(String value) {
this.lastPushedType = this.push().set(value);
}
public void push(double value) {
this.lastPushedType = this.push().set(value);
}
public void push(int value) {
this.lastPushedType = this.push().set((double)value);
}
public void push(boolean value) {
this.lastPushedType = this.push().set(value);
}
public void push(String[] value) {
this.lastPushedType = this.push().set(value);
}
public void push(double[] value) {
this.lastPushedType = this.push().set(value);
}
public void push(boolean[] value) {
this.lastPushedType = this.push().set(value);
}
public void pushEmptyArray() {
this.lastPushedType = this.push().setEmptyArray();
}
protected Operand popPush(int popCount) {
this.stackTop -= popCount - 1;
return this.operandStack[this.stackTop];
}
public void popPush(String value, int popCount) {
this.lastPushedType = this.popPush(popCount).set(value);
}
public void popPush(double value, int popCount) {
this.lastPushedType = this.popPush(popCount).set(value);
}
public void popPush(int value, int popCount) {
this.lastPushedType = this.popPush(popCount).set((double)value);
}
public void popPush(boolean value, int popCount) {
this.lastPushedType = this.popPush(popCount).set(value);
}
public void popPush(String[] value, int popCount) {
this.lastPushedType = this.popPush(popCount).set(value);
}
public void popPush(double[] value, int popCount) {
this.lastPushedType = this.popPush(popCount).set(value);
}
public void popPush(boolean[] value, int popCount) {
this.lastPushedType = this.popPush(popCount).set(value);
}
public void popPushEmptyArray(int popCount) {
this.lastPushedType = this.popPush(popCount).setEmptyArray();
}
protected Operand pop() {
this.lastPushedType = ValueType.VOID;
return this.operandStack[this.stackTop--];
}
public double popNumber() {
return this.pop().number;
}
public int popInt() {
return (int)this.pop().number;
}
public String popString() {
return this.pop().string;
}
public boolean popBoolean() {
return this.pop().bool;
}
public double[] popNumberArray() {
return this.top().type != ValueType.EMPTY_ARRAY ? this.pop().numberArray : ArrayUtil.EMPTY_DOUBLE_ARRAY;
}
@Nullable
public String[] popStringArray() {
return this.top().type != ValueType.EMPTY_ARRAY ? this.pop().stringArray : ArrayUtil.EMPTY_STRING_ARRAY;
}
public boolean[] popBooleanArray() {
return this.top().type != ValueType.EMPTY_ARRAY ? this.pop().boolArray : ArrayUtil.EMPTY_BOOLEAN_ARRAY;
}
public String popAsString() {
Operand op = this.pop();
String var10000;
switch (op.type) {
case VOID -> var10000 = "null";
case STRING -> var10000 = op.string;
case NUMBER -> var10000 = Double.toString(op.number);
case BOOLEAN -> var10000 = Boolean.toString(op.bool);
case NUMBER_ARRAY -> var10000 = Arrays.toString(op.numberArray);
case STRING_ARRAY -> var10000 = Arrays.toString(op.stringArray);
case BOOLEAN_ARRAY -> var10000 = Arrays.toString(op.boolArray);
case EMPTY_ARRAY -> var10000 = "[]";
default -> throw new MatchException((String)null, (Throwable)null);
}
return var10000;
}
protected Operand get(int index) {
return this.operandStack[this.stackTop - index];
}
public double getNumber(int index) {
return this.get(index).number;
}
public int getInt(int index) {
return (int)this.get(index).number;
}
public String getString(int index) {
return this.get(index).string;
}
public boolean getBoolean(int index) {
return this.get(index).bool;
}
public double[] getNumberArray(int index) {
return this.get(index).numberArray;
}
@Nullable
public String[] getStringArray(int index) {
return this.get(index).stringArray;
}
public boolean[] getBooleanArray(int index) {
return this.get(index).boolArray;
}
@Nonnull
public static Instruction genPUSH(String value) {
return (context) -> context.push(value);
}
@Nonnull
public static Instruction genPUSH(double value) {
return (context) -> context.push(value);
}
@Nonnull
public static Instruction genPUSH(boolean value) {
return (context) -> context.push(value);
}
@Nonnull
public static Instruction genPUSH(String[] value) {
return (context) -> context.push(value);
}
@Nonnull
public static Instruction genPUSH(double[] value) {
return (context) -> context.push(value);
}
@Nonnull
public static Instruction genPUSH(boolean[] value) {
return (context) -> context.push(value);
}
@Nonnull
public static Instruction genPUSHEmptyArray() {
return ExecutionContext::pushEmptyArray;
}
@Nonnull
public static Instruction genREAD(String ident, @Nonnull ValueType type, @Nullable Scope scope) {
if (scope == null) {
Instruction var9;
switch (type) {
case STRING -> var9 = (context) -> context.push(context.scope.getString(ident));
case NUMBER -> var9 = (context) -> context.push(context.scope.getNumber(ident));
case BOOLEAN -> var9 = (context) -> context.push(context.scope.getBoolean(ident));
case NUMBER_ARRAY -> var9 = (context) -> context.push(context.scope.getNumberArray(ident));
case STRING_ARRAY -> var9 = (context) -> context.push(context.scope.getStringArray(ident));
case BOOLEAN_ARRAY -> var9 = (context) -> context.push(context.scope.getBooleanArray(ident));
default -> throw new RuntimeException("ExecutionContext: Invalid read type");
}
return var9;
} else {
Instruction var10000;
switch (type) {
case STRING:
Supplier<String> supplier = scope.getStringSupplier(ident);
var10000 = (context) -> context.push((String)supplier.get());
break;
case NUMBER:
DoubleSupplier supplier = scope.getNumberSupplier(ident);
var10000 = (context) -> context.push(supplier.getAsDouble());
break;
case BOOLEAN:
BooleanSupplier supplier = scope.getBooleanSupplier(ident);
var10000 = (context) -> context.push(supplier.getAsBoolean());
break;
case NUMBER_ARRAY:
Supplier<double[]> supplier = scope.getNumberArraySupplier(ident);
var10000 = (context) -> context.push((double[])supplier.get());
break;
case STRING_ARRAY:
Supplier<String[]> supplier = scope.getStringArraySupplier(ident);
var10000 = (context) -> context.push((String[])supplier.get());
break;
case BOOLEAN_ARRAY:
Supplier<boolean[]> supplier = scope.getBooleanArraySupplier(ident);
var10000 = (context) -> context.push((boolean[])supplier.get());
break;
default:
throw new RuntimeException("ExecutionContext: Invalid read type");
}
return var10000;
}
}
@Nonnull
public static Instruction genCALL(String ident, int numArgs, @Nullable Scope scope) {
if (scope == null) {
return (context) -> context.scope.getFunction(ident).call(context, numArgs);
} else {
Scope.Function function = scope.getFunction(ident);
return (context) -> function.call(context, numArgs);
}
}
@Nonnull
public static Instruction genNumberPACK(int size) {
return (context) -> {
double[] array = new double[size];
for(int i = 0; i < size; ++i) {
array[i] = context.getNumber(size - i);
}
context.popPush(array, size);
};
}
@Nonnull
public static Instruction genStringPACK(int size) {
return (context) -> {
String[] array = new String[size];
for(int i = 0; i < size; ++i) {
array[i] = context.getString(size - i);
}
context.popPush(array, size);
};
}
@Nonnull
public static Instruction genBooleanPACK(int size) {
return (context) -> {
boolean[] array = new boolean[size];
for(int i = 0; i < size; ++i) {
array[i] = context.getBoolean(size - i);
}
context.popPush(array, size);
};
}
@Nonnull
public static Instruction genPACK(@Nonnull ValueType arrayType, int size) {
Instruction var10000;
switch (arrayType) {
case NUMBER_ARRAY -> var10000 = genNumberPACK(size);
case STRING_ARRAY -> var10000 = genStringPACK(size);
case BOOLEAN_ARRAY -> var10000 = genBooleanPACK(size);
default -> throw new IllegalStateException("Cannot create PACK instruction for type " + String.valueOf(arrayType));
}
return var10000;
}
@Nonnull
public String toString() {
String var10000 = String.valueOf(this.scope);
return "ExecutionContext{scope=" + var10000 + ", operandStack=" + Arrays.toString(this.operandStack) + ", stackTop=" + this.stackTop + ", lastPushedType=" + String.valueOf(this.lastPushedType) + "}";
}
public static class Operand {
public ValueType type;
public String string;
public double number;
public boolean bool;
@Nullable
public double[] numberArray;
@Nullable
public String[] stringArray;
@Nullable
public boolean[] boolArray;
public Operand() {
}
public ValueType set(String value) {
this.reInit(ValueType.STRING);
this.string = value;
return this.type;
}
public ValueType set(double value) {
this.reInit(ValueType.NUMBER);
this.number = value;
return this.type;
}
public ValueType set(boolean value) {
this.reInit(ValueType.BOOLEAN);
this.bool = value;
return this.type;
}
public ValueType set(String[] value) {
this.reInit(ValueType.STRING_ARRAY);
this.stringArray = value;
return this.type;
}
public ValueType set(double[] value) {
this.reInit(ValueType.NUMBER_ARRAY);
this.numberArray = value;
return this.type;
}
public ValueType set(boolean[] value) {
this.reInit(ValueType.BOOLEAN_ARRAY);
this.boolArray = value;
return this.type;
}
public ValueType setEmptyArray() {
this.reInit(ValueType.EMPTY_ARRAY);
return this.type;
}
private void reInit(ValueType type) {
this.type = type;
this.numberArray = null;
this.stringArray = null;
this.boolArray = null;
}
@Nonnull
public String toString() {
String var10000 = String.valueOf(this.type);
return "Operand{type=" + var10000 + ", string='" + this.string + "', number=" + this.number + ", bool=" + this.bool + ", numberArray=" + Arrays.toString(this.numberArray) + ", stringArray=" + Arrays.toString(this.stringArray) + ", boolArray=" + Arrays.toString(this.boolArray) + "}";
}
}
@FunctionalInterface
public interface Instruction {
void execute(ExecutionContext var1);
}
}