package me.hammerle.snuviscript.code; import java.util.Arrays; import java.util.HashMap; import java.util.Random; import java.util.stream.Collectors; import me.hammerle.snuviscript.array.DynamicArray; import me.hammerle.snuviscript.variable.ArrayVariable; import me.hammerle.snuviscript.variable.Variable; import me.hammerle.snuviscript.math.Fraction; public class FunctionLoader { private static final HashMap FUNCTIONS = new HashMap<>(); public static void registerFunction(String name, BasicFunction function) { FUNCTIONS.put(name, function); } public static void registerFunction(BasicFunction function) { registerFunction(function.getName(), function); } public static void registerAlias(String original, String alias) { FUNCTIONS.put(alias, FUNCTIONS.get(original)); } public static BasicFunction getFunction(String function) { return FUNCTIONS.getOrDefault(function, new BasicFunction(function, (sc, in) -> { Script sub = sc.subScripts.get(function); if(sub == null) { throw new NullPointerException("function " + function + " does not exist"); } // push storage for local vars HashMap vars = new HashMap<>(); if(in.length != sub.subScriptInput.length) { throw new NullPointerException("invalid number of input for function " + function); } // generate local vars String s; Variable v; for(int i = 0; i < in.length; i++) { s = sub.subScriptInput[i]; if(in[i].isArray(sc)) { v = new ArrayVariable(s); v.set(sc, in[i].getArray(sc)); } else { v = new Variable(s); v.set(sc, in[i].get(sc)); } vars.put(s, v); } sub.localVars.push(vars); // saving line for return int line = sub.currentLine; // set starting line for current run sub.currentLine = 0; // run subscript and save return value Object r = sub.run(); // return back to previous line sub.currentLine = line; // pop storage for local vars sub.localVars.pop(); return r; })); } private static final Random[] RND; static { RND = new Random[8]; for(int i = 0; i < 8; i++) { RND[i] = new Random(); } // --------------------------------------------------------------------- // brackets // --------------------------------------------------------------------- registerFunction("", new BasicFunction("", (sc, in) -> { return in[0].get(sc); })); // --------------------------------------------------------------------- // elementary arithmetic // --------------------------------------------------------------------- registerFunction("+", new BasicFunction("ADD", (sc, in) -> { return in[0].getFraction(sc).add(in[1].getFraction(sc)); })); registerFunction("-", new BasicFunction("SUB", (sc, in) -> { return in[0].getFraction(sc).sub(in[1].getFraction(sc)); })); registerFunction("*", new BasicFunction("MUL", (sc, in) -> { return in[0].getFraction(sc).mul(in[1].getFraction(sc)); })); registerFunction("/", new BasicFunction("DIV", (sc, in) -> { return in[0].getFraction(sc).div(in[1].getFraction(sc)); })); // --------------------------------------------------------------------- // comparing // --------------------------------------------------------------------- registerFunction("==", new BasicFunction("EQUAL", (sc, in) -> { Object a = in[0].get(sc); Object b = in[1].get(sc); if(a == null || b == null) { return a == b ? 1 : 0; } else if(a instanceof String || b instanceof String) { return a.equals(b) ? 1 : 0; } return ((Number) a).doubleValue() == ((Number) b).doubleValue() ? 1 : 0; })); registerFunction("!=", new BasicFunction("NOTEQUAL", (sc, in) -> { Object a = in[0].get(sc); Object b = in[1].get(sc); if(a == null || b == null) { return a != b ? 1 : 0; } else if(a instanceof String || b instanceof String) { return a.equals(b) ? 1 : 0; } return ((Number) a).doubleValue() != ((Number) b).doubleValue() ? 1 : 0; })); registerFunction(">", new BasicFunction("GREATER", (sc, in) -> { return in[0].getDouble(sc) > in[1].getDouble(sc) ? 1 : 0; })); registerFunction(">=", new BasicFunction("GREATEREQUAL", (sc, in) -> { return in[0].getDouble(sc) >= in[1].getDouble(sc) ? 1 : 0; })); registerFunction("<", new BasicFunction("SMALLER", (sc, in) -> { return in[0].getDouble(sc) < in[1].getDouble(sc) ? 1 : 0; })); registerFunction("<=", new BasicFunction("SMALLEREQUAL", (sc, in) -> { return in[0].getDouble(sc) <= in[1].getDouble(sc) ? 1 : 0; })); // --------------------------------------------------------------------- // logical operators // --------------------------------------------------------------------- registerFunction("&&", new BasicFunction("AND", (sc, in) -> { return (in[0].getDouble(sc) != 0 && in[1].getDouble(sc) != 0) ? 1 : 0; })); registerFunction("||", new BasicFunction("OR", (sc, in) -> { return (in[0].getDouble(sc) != 0 || in[1].getDouble(sc) != 0) ? 1 : 0; })); // --------------------------------------------------------------------- // bit stuff // --------------------------------------------------------------------- registerFunction(new BasicFunction("MOD", (sc, in) -> { return in[0].getInt(sc) % in[1].getInt(sc); })); registerFunction("&", new BasicFunction("AND", (sc, in) -> { return in[0].getInt(sc) & in[1].getInt(sc); })); registerFunction("|", new BasicFunction("OR", (sc, in) -> { return in[0].getInt(sc) | in[1].getInt(sc); })); registerFunction("^", new BasicFunction("XOR", (sc, in) -> { return in[0].getInt(sc) ^ in[1].getInt(sc); })); registerFunction("<<", new BasicFunction("SHIFTL", (sc, in) -> { return in[0].getInt(sc) << in[1].getInt(sc); })); registerFunction(">>", new BasicFunction("SHIFTR", (sc, in) -> { return in[0].getInt(sc) >> in[1].getInt(sc); })); // --------------------------------------------------------------------- // basic instructions (variables and arrays) // --------------------------------------------------------------------- registerFunction("=", new BasicFunction("SET", (sc, in) -> { in[0].set(sc, in[1].get(sc)); return Void.TYPE; })); registerFunction("+=", new BasicFunction("ADD_SET", (sc, in) -> { in[0].set(sc, in[0].getFraction(sc).add(in[1].getFraction(sc))); return Void.TYPE; })); registerFunction("-=", new BasicFunction("SUB_SET", (sc, in) -> { in[0].set(sc, in[0].getFraction(sc).sub(in[1].getFraction(sc))); return Void.TYPE; })); registerFunction("*=", new BasicFunction("MUL_SET", (sc, in) -> { in[0].set(sc, in[0].getFraction(sc).mul(in[1].getFraction(sc))); return Void.TYPE; })); registerFunction("/=", new BasicFunction("DIV_SET", (sc, in) -> { in[0].set(sc, in[0].getFraction(sc).div(in[1].getFraction(sc))); return Void.TYPE; })); registerFunction("%=", new BasicFunction("MOD_SET", (sc, in) -> { in[0].set(sc, new Fraction(in[0].getInt(sc) % in[1].getInt(sc))); return Void.TYPE; })); registerFunction("<<=", new BasicFunction("LEFT_SHIFT_SET", (sc, in) -> { in[0].set(sc, in[0].getFraction(sc).leftShift(in[1].getInt(sc))); return Void.TYPE; })); registerFunction(">>=", new BasicFunction("RIGHT_SHIFT_SET", (sc, in) -> { in[0].set(sc, in[0].getFraction(sc).rightShift(in[1].getInt(sc))); return Void.TYPE; })); registerFunction("&=", new BasicFunction("BIT_AND_SET", (sc, in) -> { in[0].set(sc, in[0].getFraction(sc).and(in[1].getFraction(sc))); return Void.TYPE; })); registerFunction("^=", new BasicFunction("BIT_XOR_SET", (sc, in) -> { in[0].set(sc, in[0].getFraction(sc).xor(in[1].getFraction(sc))); return Void.TYPE; })); registerFunction("|=", new BasicFunction("BIT_OR_SET", (sc, in) -> { in[0].set(sc, in[0].getFraction(sc).or(in[1].getFraction(sc))); return Void.TYPE; })); registerFunction(new BasicFunction("DIM", (sc, in) -> { for(InputProvider input : in) { ((DynamicArray) input).init(sc); } return Void.TYPE; })); registerAlias("DIM", "VAR"); registerFunction(new BasicFunction("SWAP", (sc, in) -> { Object o = in[0].get(sc); in[0].set(sc, in[1].get(sc)); in[1].set(sc, o); return Void.TYPE; })); registerFunction(new BasicFunction("INC", (sc, in) -> { in[0].set(sc, in[0].getInt(sc) + (in.length > 1 ? in[1].getInt(sc) : 1)); return Void.TYPE; })); registerFunction(new BasicFunction("DEC", (sc, in) -> { in[0].set(sc, in[0].getInt(sc) - (in.length > 1 ? in[1].getInt(sc) : 1)); return Void.TYPE; })); // --------------------------------------------------------------------- // basic instructions (control and branching) // --------------------------------------------------------------------- registerFunction(new BasicFunction("goto", (sc, in) -> { sc.currentLine = sc.labels.get(in[0].getString(sc)); return Void.TYPE; })); registerFunction(new BasicFunction("GOSUB", (sc, in) -> { sc.returnStack.push(sc.currentLine); sc.currentLine = sc.labels.get(in[0].getString(sc)); return Void.TYPE; })); registerFunction(new BasicFunction("return", (sc, in) -> { if(sc.returnStack.isEmpty()) { sc.end(); sc.returnValue = in.length > 0 ? in[0].get(sc) : null; } else { sc.currentLine = sc.returnStack.pop(); } return Void.TYPE; })); registerFunction(new BasicFunction("if", (sc, in) -> { int p = in[0].getInt(sc); if(p == 0) { sc.currentLine += in[1].getInt(sc); } return Void.TYPE; })); registerFunction(new BasicFunction("endif", (sc, in) -> { return Void.TYPE; })); registerFunction(new BasicFunction("for", (sc, in) -> { // for(var, start, end, step) Fraction start = in[1].getFraction(sc); in[0].set(sc, start); if(start.compareTo(in[2].getFraction(sc)) > 0) { sc.currentLine += in[4].getInt(sc); } return Void.TYPE; })); registerFunction(new BasicFunction("next", (sc, in) -> { int line = sc.currentLine + in[0].getInt(sc); InputProvider[] f = sc.code[line].getParameters(); // for(var, start, end, step) Fraction current = f[0].getFraction(sc).add(f[3].getFraction(sc)); f[0].set(sc, current); if(current.compareTo(f[2].getFraction(sc)) <= 0) { sc.currentLine = line; } return Void.TYPE; })); registerFunction(new BasicFunction("while", (sc, in) -> { if(in[0].getInt(sc) == 0) { sc.currentLine += in[1].getInt(sc); } return Void.TYPE; })); registerFunction(new BasicFunction("wend", (sc, in) -> { sc.currentLine += in[0].getInt(sc); return Void.TYPE; })); registerFunction(new BasicFunction("continue", (sc, in) -> { sc.currentLine += in[0].getInt(sc); return Void.TYPE; })); registerFunction(new BasicFunction("break", (sc, in) -> { sc.currentLine += in[0].getInt(sc); return Void.TYPE; })); // --------------------------------------------------------------------- // mathematics // --------------------------------------------------------------------- registerFunction(new BasicFunction("FLOOR", (sc, in) -> { return (int) Math.floor(in[0].getDouble(sc)); })); registerFunction(new BasicFunction("ROUND", (sc, in) -> { return (int) Math.round(in[0].getDouble(sc)); })); registerFunction(new BasicFunction("CEIL", (sc, in) -> { return (int) Math.ceil(in[0].getDouble(sc)); })); registerFunction(new BasicFunction("ABS", (sc, in) -> { return in[0].getFraction(sc).abs(); })); registerFunction(new BasicFunction("SGN", (sc, in) -> { double d = in[0].getDouble(sc); return d < 0 ? -1 : (d > 0 ? 1 : 0); })); /*registerFunction(new BasicFunction("MIN", (sc, in) -> { if(in.length == 1) { return ((IMathOperation) in[0]).min(sc); } double min = Arrays.stream(in).mapToDouble(i -> i.getDouble(sc)).min().getAsDouble(); if(min == (int) min) { return (int) min; } return min; })); registerFunction(new BasicFunction("MAX", (sc, in) -> { if(in.length == 1) { return ((IMathOperation) in[0]).max(sc); } double max = Arrays.stream(in).mapToDouble(i -> i.getDouble(sc)).max().getAsDouble(); if(max == (int) max) { return (int) max; } return max; }));*/ registerFunction(new BasicFunction("RND", (sc, in) -> { int seedId; int max; switch (in.length) { case 1: seedId = 0; max = in[0].getInt(sc); break; case 2: seedId = in[0].getInt(sc); max = in[1].getInt(sc); break; default: throw new IllegalArgumentException("invalid number of arguments"); } if(seedId < 0 || seedId > 7) { throw new IllegalArgumentException("seed id must be from 0 to 7"); } return RND[seedId].nextInt(max); })); registerFunction(new BasicFunction("RNDF", (sc, in) -> { int seedId = 0; if(in.length > 0) { seedId = in[0].getInt(sc); } if(seedId < 0 || seedId > 7) { throw new IllegalArgumentException("seed id must be from 0 to 7"); } return RND[seedId].nextDouble(); })); registerFunction(new BasicFunction("RANDOMIZE", (sc, in) -> { int seedId = in[0].getInt(sc); if(seedId < 0 || seedId > 7) { throw new IllegalArgumentException("seed id must be from 0 to 7"); } switch (in.length) { case 1: RND[seedId] = new Random(); break; case 2: RND[seedId] = new Random(in[1].getInt(sc)); break; default: throw new IllegalArgumentException("invalid number of arguments"); } return Void.TYPE; })); registerFunction(new BasicFunction("SQR", (sc, in) -> { return Math.sqrt(in[0].getDouble(sc)); })); registerFunction(new BasicFunction("EXP", (sc, in) -> { if(in.length == 0) { return Math.E; } return Math.exp(in[0].getDouble(sc)); })); registerFunction(new BasicFunction("LOG", (sc, in) -> { if(in.length >= 2) { return Math.log(in[0].getDouble(sc)) / Math.log(in[1].getDouble(sc)); } return Math.log(in[0].getDouble(sc)); })); registerFunction(new BasicFunction("POW", (sc, in) -> { return Math.pow(in[0].getDouble(sc), in[1].getDouble(sc)); })); registerFunction(new BasicFunction("PI", (sc, in) -> { return Math.PI; })); registerFunction(new BasicFunction("RAD", (sc, in) -> { return Math.toRadians(in[0].getDouble(sc)); })); registerFunction(new BasicFunction("DEG", (sc, in) -> { return Math.toDegrees(in[0].getDouble(sc)); })); registerFunction(new BasicFunction("SIN", (sc, in) -> { return Math.sin(in[0].getDouble(sc)); })); registerFunction(new BasicFunction("COS", (sc, in) -> { return Math.cos(in[0].getDouble(sc)); })); registerFunction(new BasicFunction("TAN", (sc, in) -> { return Math.tan(in[0].getDouble(sc)); })); registerFunction(new BasicFunction("ASIN", (sc, in) -> { return Math.asin(in[0].getDouble(sc)); })); registerFunction(new BasicFunction("ACOS", (sc, in) -> { return Math.acos(in[0].getDouble(sc)); })); registerFunction(new BasicFunction("ATAN", (sc, in) -> { if(in.length >= 2) { return Math.atan2(in[0].getDouble(sc), in[1].getDouble(sc)); } return Math.atan(in[0].getDouble(sc)); })); registerFunction(new BasicFunction("SINH", (sc, in) -> { return Math.sinh(in[0].getDouble(sc)); })); registerFunction(new BasicFunction("COSH", (sc, in) -> { return Math.cosh(in[0].getDouble(sc)); })); registerFunction(new BasicFunction("TANH", (sc, in) -> { return Math.tanh(in[0].getDouble(sc)); })); registerFunction(new BasicFunction("CLASSIFY", (sc, in) -> { double d = in[0].getDouble(sc); if(Double.isNaN(d)) { return 2; } else if(Double.isInfinite(d)) { return 1; } return 0; })); registerFunction(new BasicFunction("print", (sc, in) -> { printMessage(Arrays.stream(in).map(s -> s.getString(sc)).collect(Collectors.joining())); return Void.TYPE; })); registerFunction(new BasicFunction("TEST", (sc, in) -> { return 1; })); } private static void printMessage(String message) { System.out.println(message); } }