package openmods.calc.command; import com.google.common.base.Joiner; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import java.math.BigInteger; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import net.minecraft.command.ICommandSender; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.util.ChatComponentText; import openmods.calc.Calculator; import openmods.calc.ExprType; import openmods.calc.Frame; import openmods.calc.ICallable; import openmods.calc.IGettable; import openmods.calc.IValuePrinter; import openmods.calc.NullaryFunction; import openmods.calc.UnaryFunction; import openmods.calc.types.bigint.BigIntCalculatorFactory; import openmods.calc.types.bool.BoolCalculatorFactory; import openmods.calc.types.fp.DoubleCalculatorFactory; import openmods.calc.types.fraction.FractionCalculatorFactory; import openmods.calc.types.multi.EntityPlayerWrapper; import openmods.calc.types.multi.StructWrapper; import openmods.calc.types.multi.TypeDomain; import openmods.calc.types.multi.TypedValue; import openmods.calc.types.multi.TypedValueCalculatorFactory; import openmods.utils.OptionalInt; import openmods.utils.Stack; import openmods.utils.StackValidationException; import org.apache.commons.lang3.math.Fraction; public class CalcState { public static class NoSuchNameException extends RuntimeException { private static final long serialVersionUID = 1L; public NoSuchNameException(String message) { super(message); } } private static interface IFunction<E> { public E call(); } private static class SenderHolder { private ICommandSender sender; public int getX() { Preconditions.checkNotNull(sender, "DERP"); return sender.getPlayerCoordinates().posX; } public int getY() { Preconditions.checkNotNull(sender, "DERP"); return sender.getPlayerCoordinates().posY; } public int getZ() { Preconditions.checkNotNull(sender, "DERP"); return sender.getPlayerCoordinates().posZ; } public <E> Calculator<E, ExprType> addPrinter(Calculator<E, ExprType> calculator) { final IValuePrinter<E> printer = calculator.printer; calculator.environment.setGlobalSymbol("p", new ICallable<E>() { @Override public void call(Frame<E> frame, OptionalInt argumentsCount, OptionalInt returnsCount) { Preconditions.checkNotNull(sender, "DERP"); if (!returnsCount.compareIfPresent(0)) throw new StackValidationException("This function does not return any values"); final Stack<E> stack = frame.stack(); final int in = argumentsCount.or(1); final List<String> results = Lists.newArrayListWithExpectedSize(in); for (int i = 0; i < in; i++) { final E value = stack.pop(); results.add(printer.repr(value)); } final String result = ": " + Joiner.on(" ").join(results); sender.addChatMessage(new ChatComponentText(result)); } }); calculator.environment.setGlobalSymbol("print", new UnaryFunction.Direct<E>() { @Override protected E call(E value) { sender.addChatMessage(new ChatComponentText(printer.str(value))); return value; } }); return calculator; } public synchronized <E> E call(ICommandSender sender, IFunction<E> function) { this.sender = sender; final E result = function.call(); this.sender = null; return result; } } public enum CalculatorType { DOUBLE { @Override public Calculator<?, ExprType> newCalculator(final SenderHolder holder) { final Calculator<Double, ExprType> calculator = DoubleCalculatorFactory.createDefault(); calculator.environment.setGlobalSymbol("_x", new IGettable<Double>() { @Override public Double get() { return Double.valueOf(holder.getX()); } }); calculator.environment.setGlobalSymbol("_y", new IGettable<Double>() { @Override public Double get() { return Double.valueOf(holder.getY()); } }); calculator.environment.setGlobalSymbol("_z", new IGettable<Double>() { @Override public Double get() { return Double.valueOf(holder.getZ()); } }); return calculator; } }, FRACTION { @Override public Calculator<?, ExprType> newCalculator(final SenderHolder holder) { final Calculator<Fraction, ExprType> calculator = FractionCalculatorFactory.createDefault(); calculator.environment.setGlobalSymbol("_x", new IGettable<Fraction>() { @Override public Fraction get() { return Fraction.getFraction(holder.getX(), 1); } }); calculator.environment.setGlobalSymbol("_y", new IGettable<Fraction>() { @Override public Fraction get() { return Fraction.getFraction(holder.getY(), 1); } }); calculator.environment.setGlobalSymbol("_z", new IGettable<Fraction>() { @Override public Fraction get() { return Fraction.getFraction(holder.getZ(), 1); } }); return calculator; } }, BIGINT { @Override public Calculator<?, ExprType> newCalculator(final SenderHolder holder) { final Calculator<BigInteger, ExprType> calculator = BigIntCalculatorFactory.createDefault(); calculator.environment.setGlobalSymbol("_x", new IGettable<BigInteger>() { @Override public BigInteger get() { return BigInteger.valueOf(holder.getX()); } }); calculator.environment.setGlobalSymbol("_y", new IGettable<BigInteger>() { @Override public BigInteger get() { return BigInteger.valueOf(holder.getY()); } }); calculator.environment.setGlobalSymbol("_z", new IGettable<BigInteger>() { @Override public BigInteger get() { return BigInteger.valueOf(holder.getZ()); } }); return calculator; } }, MULTI { @Override protected Calculator<?, ExprType> newCalculator(final SenderHolder holder) { final Calculator<TypedValue, ExprType> calculator = TypedValueCalculatorFactory.create(); final TypedValue nullValue = calculator.environment.nullValue(); final TypeDomain domain = nullValue.domain; calculator.environment.setGlobalSymbol("player", new NullaryFunction.Direct<TypedValue>() { @Override protected TypedValue call() { if (holder.sender instanceof EntityPlayer) { final EntityPlayerWrapper wrapper = new EntityPlayerWrapper((EntityPlayer)holder.sender, nullValue); return StructWrapper.create(domain, wrapper); } return nullValue; } }); return calculator; } }, BOOL { @Override protected Calculator<?, ExprType> newCalculator(SenderHolder holder) { return BoolCalculatorFactory.createDefault(); } }; public Calculator<?, ExprType> createCalculator(SenderHolder holder) { return holder.addPrinter(newCalculator(holder)); } protected abstract Calculator<?, ExprType> newCalculator(SenderHolder holder); } private final SenderHolder senderHolder = new SenderHolder(); private Calculator<?, ExprType> active = CalculatorType.DOUBLE.createCalculator(senderHolder); private Calculator<?, ExprType> prev; public ExprType exprType = ExprType.INFIX; private Stack<Calculator<?, ExprType>> calculatorStack = Stack.create(); private Map<String, Calculator<?, ExprType>> calculatorMap = Maps.newHashMap(); public Calculator<?, ExprType> getActiveCalculator() { return active; } private void setActiveCalculator(final Calculator<?, ExprType> newCalculator) { prev = active; active = newCalculator; } public void restorePreviousCalculator() { final Calculator<?, ExprType> tmp = active; active = prev; prev = tmp; } public void createCalculator(CalculatorType type) { setActiveCalculator(type.createCalculator(senderHolder)); } public int pushCalculator() { calculatorStack.push(active); return calculatorStack.size(); } public int popCalculator() { setActiveCalculator(calculatorStack.pop()); return calculatorStack.size(); } public void nameCalculator(String name) { calculatorMap.put(name, active); } public Set<String> getCalculatorsNames() { return Collections.unmodifiableSet(calculatorMap.keySet()); } public void loadCalculator(String name) { final Calculator<?, ExprType> newCalculator = calculatorMap.get(name); if (newCalculator == null) throw new NoSuchNameException(name); setActiveCalculator(newCalculator); } public void compileAndExecute(ICommandSender sender, final String expr) { senderHolder.call(sender, new IFunction<Void>() { @Override public Void call() { active.compileAndExecute(exprType, expr); return null; } }); } public String compileExecuteAndPrint(ICommandSender sender, final String expr) { return senderHolder.call(sender, new IFunction<String>() { @Override public String call() { return active.compileExecuteAndPrint(exprType, expr); } }); } public Object compileAndSetGlobalSymbol(ICommandSender sender, final String id, final String expr) { return senderHolder.call(sender, new IFunction<Object>() { @Override public Object call() { return active.compileAndSetGlobalSymbol(exprType, id, expr); } }); } public void compileAndDefineGlobalFunction(ICommandSender sender, final String id, final int argCount, final String expr) { senderHolder.call(sender, new IFunction<Void>() { @Override public Void call() { active.compileAndDefineGlobalFunction(exprType, id, argCount, expr); return null; } }); } }