package openmods.calc; import com.google.common.collect.Lists; import java.util.List; import openmods.utils.OptionalInt; import openmods.utils.Stack; import openmods.utils.StackValidationException; public class GenericFunctions { public interface Accumulator<E> { public E accumulate(E prev, E value); } // WARNING: this assumes 'accumulate' operation is associative! public abstract static class AccumulatorFunction<E> extends SingleReturnCallable<E> { private final E nullValue; public AccumulatorFunction(E nullValue) { this.nullValue = nullValue; } @Override public E call(Frame<E> frame, OptionalInt argumentsCount) { final Stack<E> stack = frame.stack(); final int args = argumentsCount.or(2); if (args == 0) return nullValue; E result = stack.pop(); for (int i = 1; i < args; i++) { final E value = stack.pop(); result = accumulate(value, result); } return process(result, args); } protected E process(E result, int argCount) { return result; } protected abstract E accumulate(E result, E value); } public static <E> void createStackManipulationFunctions(Environment<E> calculator) { calculator.setGlobalSymbol("swap", new FixedCallable<E>(2, 2) { @Override public void call(Frame<E> frame) { final Stack<E> stack = frame.stack(); final E first = stack.pop(); final E second = stack.pop(); stack.push(first); stack.push(second); } }); calculator.setGlobalSymbol("pop", new ICallable<E>() { @Override public void call(Frame<E> frame, OptionalInt argumentsCount, OptionalInt returnsCount) { if (returnsCount.isPresent() && returnsCount.get() != 0) throw new StackValidationException("Invalid expected return values on 'pop'"); final Stack<E> stack = frame.stack(); final int count = argumentsCount.or(1); for (int i = 0; i < count; i++) stack.pop(); } }); calculator.setGlobalSymbol("dup", new ICallable<E>() { @Override public void call(Frame<E> frame, OptionalInt argumentsCount, OptionalInt returnsCount) { final Stack<E> stack = frame.stack(); List<E> values = Lists.newArrayList(); final int in = argumentsCount.or(1); for (int i = 0; i < in; i++) { final E value = stack.pop(); values.add(value); } values = Lists.reverse(values); final int out = returnsCount.or(2 * in); for (int i = 0; i < out; i++) { final E value = values.get(i % in); stack.push(value); } } }); } }