package me.hammerle.code; import java.io.File; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; import me.hammerle.exceptions.HoldCodeException; import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collection; import java.util.Collections; import java.util.GregorianCalendar; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.BiFunction; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Predicate; import java.util.stream.Collectors; import me.hammerle.exceptions.FileIOException; import me.hammerle.exceptions.GotoLabelNotFoundException; import me.hammerle.exceptions.PreScriptException; import me.hammerle.math.Fraction; public class SnuviParser { protected final ISnuviLogger logger; private final ISnuviScheduler scheduler; private final HashMap> functions; public boolean printStack; private int idCounter; private final HashMap scripts; private final ArrayList termQueue; public SnuviParser(ISnuviLogger logger, ISnuviScheduler scheduler) { this.logger = logger; this.scheduler = scheduler; functions = new HashMap<>(); registerStandardLibraries(); printStack = false; scripts = new HashMap<>(); termQueue = new ArrayList<>(); idCounter = 0; } // ----------------------------------------------------------------------------------- // function registry // ----------------------------------------------------------------------------------- public boolean isRegisteredFunction(String s) { return functions.containsKey(s); } public void registerFunction(String s, BiFunction f) { functions.put(s, f); } public void registerConsumer(String s, BiConsumer f) { functions.put(s, (BiFunction) (args, sc) -> { f.accept(args, sc); return Void.TYPE; }); } public void registerAlias(String alias, String original) { BiFunction f = functions.get(original); if(f == null) { throw new IllegalArgumentException("registering alias for non existent function"); } registerFunction(alias, functions.get(original)); } @SuppressWarnings("unchecked") private void registerStandardLibraries() { registerFunction("nothing", (args, sc) -> 0); registerFunction("", (args, sc) -> args[0]); registerConsumer("gotoline", (args, sc) -> { sc.jump(); sc.incLoopCounter(); }); registerConsumer("error", (args, sc) -> printStack = !printStack); // ------------------------------------------------------------------------------- // event // ------------------------------------------------------------------------------- registerConsumer("event.load", (args, sc) -> sc.loadEvent(args[0].toString())); registerConsumer("event.unload", (args, sc) -> sc.unloadEvent(args[0].toString())); registerFunction("event.isloaded", (args, sc) -> sc.isLoadedEvent(args[0].toString())); // ------------------------------------------------------------------------------- // bit // ------------------------------------------------------------------------------- registerFunction(">>", (args, qd) -> ((Fraction) args[0]).rightShift(getInt(args[1]))); registerFunction("<<", (args, qd) -> ((Fraction) args[0]).leftShift(getInt(args[1]))); registerFunction("&", (args, qd) -> ((Fraction) args[0]).and((Fraction) args[1])); registerFunction("|", (args, qd) -> ((Fraction) args[0]).or((Fraction) args[1])); registerFunction("^", (args, qd) -> ((Fraction) args[0]).xor((Fraction) args[1])); registerFunction("~", (args, qd) -> ((Fraction) args[0]).invertBits()); registerFunction("bit.set", (args, qd) -> ((Fraction) args[0]).setBit(getInt(args[1]))); registerFunction("bit.unset", (args, qd) -> ((Fraction) args[0]).unsetBit(getInt(args[1]))); registerFunction("bit.get", (args, qd) -> ((Fraction) args[0]).getBit(getInt(args[1]))); // ------------------------------------------------------------------------------- // math // ------------------------------------------------------------------------------- registerFunction("%", (args, sc) -> new Fraction(getInt(args[0]) % getInt(args[1]))); registerAlias("math.mod", "%"); registerFunction("math.abs", (args, sc) -> ((Fraction) args[0]).abs()); registerFunction("math.pow", (args, sc) -> ((Fraction) args[0]).power((Fraction) args[1])); registerFunction("math.root", (args, sc) -> ((Fraction) args[0]).power(((Fraction) args[1]).invert())); registerFunction("math.sin", (args, sc) -> ((Fraction) args[0]).sin()); registerFunction("math.cos", (args, sc) -> ((Fraction) args[0]).cos()); registerFunction("math.tan", (args, sc) -> ((Fraction) args[0]).tan()); registerFunction("math.asin", (args, sc) -> ((Fraction) args[0]).asin()); registerFunction("math.acos", (args, sc) -> ((Fraction) args[0]).acos()); registerFunction("math.atan", (args, sc) -> ((Fraction) args[0]).atan()); registerFunction("math.e", (args, sc) -> Fraction.E); registerFunction("math.pi", (args, sc) -> Fraction.PI); registerFunction("math.ln", (args, sc) -> ((Fraction) args[0]).log()); registerFunction("math.log", (args, sc) -> ((Fraction) args[0]).log()); registerFunction("math.random", (args, sc) -> new Fraction(ScriptUtils.randomInt(getInt(args[0]), getInt(args[1])))); registerFunction("math.round", (args, sc) -> ((Fraction) args[0]).round()); registerFunction("math.rounddown", (args, sc) -> ((Fraction) args[0]).floor()); registerFunction("math.roundup", (args, sc) -> ((Fraction) args[0]).ceil()); registerFunction("math.roundcomma", (args, sc) -> ((Fraction) args[0]).round(getInt(args[1]))); // ------------------------------------------------------------------------------- // lists // ------------------------------------------------------------------------------- registerConsumer("list.new", (args, sc) -> sc.setVar(args[0].toString(), new ArrayList<>())); registerFunction("list.exists", (args, sc) -> args[0] instanceof List); registerConsumer("list.add", (args, sc) -> ((List) args[0]).add(args[1])); registerConsumer("list.remove", (args, sc) -> ((List) args[0]).remove(args[1])); registerConsumer("list.removeindex", (args, sc) -> ((List) args[0]).remove(getInt(args[1]))); registerFunction("list.contains", (args, sc) -> ((List) args[0]).contains(args[1])); registerFunction("list.getsize", (args, sc) -> new Fraction(((List) args[0]).size())); registerFunction("list.getindex", (args, sc) -> ((List) args[0]).get(getInt(args[1]))); registerConsumer("list.setindex", (args, sc) -> ((List) args[0]).set(getInt(args[1]), args[2])); registerConsumer("list.clear", (args, sc) -> ((List) args[0]).clear()); registerFunction("list.getindexof", (args, sc) -> new Fraction(((List) args[0]).indexOf(args[1]))); registerConsumer("list.sort", (args, sc) -> sortList((List) args[0], sc)); registerConsumer("list.reverse", (args, sc) -> Collections.reverse((List) args[0])); registerConsumer("list.shuffle", (args, sc) -> Collections.shuffle((List) args[0])); // ------------------------------------------------------------------------------- // arrays // ------------------------------------------------------------------------------- registerConsumer("array.new", (args, sc) -> sc.setVar(args[0].toString(), new Object[ScriptUtils.getInt(args[1])])); registerConsumer("array.=", (args, sc) -> ((Object[]) sc.getVar(args[0].toString()))[ScriptUtils.getInt(args[1])] = args[2]); registerAlias("array.set", "array.="); registerFunction("array.get", (args, sc) -> ((Object[]) sc.getVar(args[0].toString()))[ScriptUtils.getInt(args[1])]); registerFunction("array.++", (args, sc) -> { Object[] o = (Object[]) sc.getVar(args[0].toString()); int i = ScriptUtils.getInt(args[1]); o[i] = ((Fraction) o[i]).add(new Fraction(1)); return o[i]; }); registerFunction("array.--", (args, sc) -> { Object[] o = (Object[]) sc.getVar(args[0].toString()); int i = ScriptUtils.getInt(args[1]); o[i] = ((Fraction) o[i]).sub(new Fraction(1)); return o[i]; }); registerFunction("array.p+", (args, sc) -> { Object[] o = (Object[]) sc.getVar(args[0].toString()); int i = ScriptUtils.getInt(args[1]); Fraction f = (Fraction) o[i]; o[i] = f.add(new Fraction(1)); return f; }); registerFunction("array.p-", (args, sc) -> { Object[] o = (Object[]) sc.getVar(args[0].toString()); int i = ScriptUtils.getInt(args[1]); Fraction f = (Fraction) o[i]; o[i] = f.sub(new Fraction(1)); return f; }); registerConsumer("array.+=", (args, sc) -> { Object[] o = (Object[]) sc.getVar(args[0].toString()); int i = ScriptUtils.getInt(args[1]); o[i] = ((Fraction) o[i]).add((Fraction) args[2]); }); registerConsumer("array.-=", (args, sc) -> { Object[] o = (Object[]) sc.getVar(args[0].toString()); int i = ScriptUtils.getInt(args[1]); o[i] = ((Fraction) o[i]).sub((Fraction) args[2]); }); registerConsumer("array.*=", (args, sc) -> { Object[] o = (Object[]) sc.getVar(args[0].toString()); int i = ScriptUtils.getInt(args[1]); o[i] = ((Fraction) o[i]).mul((Fraction) args[2]); }); registerConsumer("array./=", (args, sc) -> { Object[] o = (Object[]) sc.getVar(args[0].toString()); int i = ScriptUtils.getInt(args[1]); o[i] = ((Fraction) o[i]).div((Fraction) args[2]); }); registerConsumer("array.%=", (args, sc) -> { Object[] o = (Object[]) sc.getVar(args[0].toString()); int i = ScriptUtils.getInt(args[1]); o[i] = ScriptUtils.getInt(o[i]) % ScriptUtils.getInt(args[2]); }); registerConsumer("array.<<=", (args, sc) -> { Object[] o = (Object[]) sc.getVar(args[0].toString()); int i = ScriptUtils.getInt(args[1]); o[i] = ((Fraction) o[i]).leftShift(ScriptUtils.getInt(args[2])); }); registerConsumer("array.>>=", (args, sc) -> { Object[] o = (Object[]) sc.getVar(args[0].toString()); int i = ScriptUtils.getInt(args[1]); o[i] = ((Fraction) o[i]).rightShift(ScriptUtils.getInt(args[2])); }); registerConsumer("array.&=", (args, sc) -> { Object[] o = (Object[]) sc.getVar(args[0].toString()); int i = ScriptUtils.getInt(args[1]); o[i] = ((Fraction) o[i]).and((Fraction) args[2]); }); registerConsumer("array.^=", (args, sc) -> { Object[] o = (Object[]) sc.getVar(args[0].toString()); int i = ScriptUtils.getInt(args[1]); o[i] = ((Fraction) o[i]).xor((Fraction) args[2]); }); registerConsumer("array.|=", (args, sc) -> { Object[] o = (Object[]) sc.getVar(args[0].toString()); int i = ScriptUtils.getInt(args[1]); o[i] = ((Fraction) o[i]).or((Fraction) args[2]); }); registerFunction("array.getsize", (args, sc) -> ((Object[]) args[0]).length); registerConsumer("array.swap", (args, sc) -> { Object[] o = (Object[]) args[0]; int first = ScriptUtils.getInt(args[1]); int sec = ScriptUtils.getInt(args[2]); Object helper = o[first]; o[first] = o[sec]; o[sec] = helper; }); registerConsumer("array.sort", (args, sc) -> { if(args.length <= 1) { Arrays.sort((Object[]) args[0]); } else { Arrays.sort((Object[]) args[0], ScriptUtils.getInt(args[1]), ScriptUtils.getInt(args[2])); } }); registerConsumer("array.copy", (args, sc) -> { int first = ScriptUtils.getInt(args[2]); System.arraycopy((Object[]) args[0], first, (Object[]) args[1], ScriptUtils.getInt(args[4]), ScriptUtils.getInt(args[3]) - first + 1); }); registerConsumer("array.rsort", (args, sc) -> { if(args.length <= 1) { Arrays.sort((Object[]) args[0], (Object o, Object o1) -> -((Comparable) o).compareTo(o)); } else { Arrays.sort((Object[]) args[0], ScriptUtils.getInt(args[1]), ScriptUtils.getInt(args[2]), (Object o, Object o1) -> -((Comparable) o).compareTo(o)); } }); registerConsumer("array.fill", (args, sc) -> { if(args.length <= 2) { Arrays.fill((Object[]) args[0], args[1]); } else { Arrays.fill((Object[]) args[0], ScriptUtils.getInt(args[2]), ScriptUtils.getInt(args[3]), args[1]); } }); // ------------------------------------------------------------------------------- // maps // ------------------------------------------------------------------------------- registerConsumer("map.new", (args, sc) -> sc.setVar(args[0].toString(), new HashMap<>())); registerFunction("map.exists", (args, sc) -> args[0] instanceof Map); registerConsumer("map.add", (args, sc) -> ((HashMap) args[0]).put(args[1], args[2])); registerConsumer("map.remove", (args, sc) -> ((HashMap) args[0]).remove(args[1])); registerFunction("map.contains", (args, sc) -> ((HashMap) args[0]).containsKey(args[1])); registerFunction("map.getsize", (args, sc) -> new Fraction(((HashMap) args[0]).size())); registerFunction("map.get", (args, sc) -> ((HashMap) args[0]).get(args[1])); registerConsumer("map.clear", (args, sc) -> ((HashMap) args[0]).clear()); registerConsumer("map.keys", (args, sc) -> sc.setVar(args[0].toString(), ((HashMap) args[1]).keySet().stream().collect(Collectors.toList()))); registerConsumer("map.values", (args, sc) -> sc.setVar(args[0].toString(), ((HashMap) args[1]).values().stream().collect(Collectors.toList()))); // ------------------------------------------------------------------------------- // sets // ------------------------------------------------------------------------------- registerConsumer("set.new", (args, sc) -> sc.setVar(args[0].toString(), new HashSet<>())); registerFunction("set.exists", (args, sc) -> args[0] instanceof Set); registerConsumer("set.add", (args, sc) -> ((HashSet) args[0]).add(args[1])); registerConsumer("set.remove", (args, sc) -> ((HashSet) args[0]).remove(args[1])); registerFunction("set.contains", (args, sc) -> ((HashSet) args[0]).contains(args[1])); registerFunction("set.getsize", (args, sc) -> new Fraction(((HashSet) args[0]).size())); registerConsumer("set.tolist", (args, sc) -> sc.setVar(args[0].toString(), ((HashSet) args[1]).stream().collect(Collectors.toList()))); // ------------------------------------------------------------------------------- // time // ------------------------------------------------------------------------------- registerFunction("time.get", (args, sc) -> new Fraction(System.currentTimeMillis())); registerFunction("time.nextday", (args, sc) -> getNextDay(args)); registerFunction("time.getyear", (args, sc) -> getYear(args)); registerFunction("time.getmonth", (args, sc) -> getMonth(args)); registerFunction("time.getday", (args, sc) -> getDay(args)); registerFunction("time.gethour", (args, sc) -> getHour(args)); registerFunction("time.getminute", (args, sc) -> getMinute(args)); registerFunction("time.getsecond", (args, sc) -> getSecond(args)); // ------------------------------------------------------------------------------- // text // ------------------------------------------------------------------------------- registerFunction("text.number", (args, sc) -> fractionToDoubleString((Fraction) args[0])); registerFunction("text.class", (args, sc) -> args[0].getClass().getSimpleName()); registerFunction("text.tolowercase", (args, sc) -> ScriptUtils.connect(args, 0).toLowerCase()); registerAlias("tolowercase", "text.tolowercase"); registerFunction("text.touppercase", (args, sc) -> ScriptUtils.connect(args, 0).toUpperCase()); registerAlias("touppercase", "text.touppercase"); registerConsumer("text.split", (args, sc) -> split(args, sc)); registerAlias("split", "text.split"); registerFunction("text.concatlist", (args, sc) -> ((List) args[0]).stream().limit(getInt(args[3]) + 1).skip(getInt(args[2])).map(o -> o.toString()).collect(Collectors.joining(args[1].toString()))); registerAlias("concatlist", "text.concatlist"); registerFunction("text.concat", (args, sc) -> ScriptUtils.connect(args, 0)); registerAlias("concat", "text.concat"); registerFunction("text", (args, sc) -> String.valueOf(args[0])); registerFunction("text.substring", (args, sc) -> args[0].toString().substring(getInt(args[1]), getInt(args[2]))); registerFunction("text.length", (args, sc) -> args[0].toString().length()); registerFunction("text.startswith", (args, sc) -> args[0].toString().startsWith(args[1].toString(), getInt(args[2]))); registerFunction("text.endswith", (args, sc) -> args[0].toString().endsWith(args[1].toString())); registerFunction("text.contains", (args, sc) -> args[0].toString().contains(args[1].toString())); registerFunction("text.indexof", (args, sc) -> args[0].toString().indexOf(args[1].toString(), getInt(args[2]))); registerFunction("text.lastindexof", (args, sc) -> args[0].toString().lastIndexOf(args[1].toString(), getInt(args[2]))); registerFunction("text.replace", (args, sc) -> args[0].toString().replace(args[1].toString(), args[2].toString())); registerFunction("text.trim", (args, sc) -> args[0].toString().trim()); registerFunction("text.charat", (args, sc) -> String.valueOf(args[0].toString().charAt(getInt(args[1])))); registerFunction("text.charcode", (args, sc) -> new Fraction(args[0].toString().charAt(getInt(args[1])))); registerFunction("text.fromcode", (args, sc) -> new String(new char[] {(char) ScriptUtils.getInt(args[0])})); // ------------------------------------------------------------------------------- // files // ------------------------------------------------------------------------------- registerFunction("file.new", (args, sc) -> new File(args[0].toString())); registerFunction("file.exists", (args, sc) -> ((File) args[0]).exists()); registerFunction("file.delete", (args, sc) -> ((File) args[0]).delete()); registerFunction("file.getname", (args, sc) -> ((File) args[0]).getName()); registerFunction("file.rename", (args, sc) -> ((File) args[0]).renameTo(new File(args[1].toString()))); registerConsumer("file.getlist", (args, sc) -> sc.setVar(args[0].toString(), Arrays.asList(((File) args[0]).listFiles()))); registerConsumer("file.read", (args, sc) -> readFile(args, sc)); registerConsumer("file.write", (args, sc) -> writeFile(args, sc)); // ------------------------------------------------------------------------------- // commands without library // ------------------------------------------------------------------------------- registerFunction("+", (args, sc) -> ((Fraction) args[0]).add((Fraction) args[1])); registerAlias("add", "+"); registerFunction("-", (args, sc) -> ((Fraction) args[0]).sub((Fraction) args[1])); registerAlias("sub", "-"); registerConsumer("+=", (args, sc) -> { String s = args[0].toString(); sc.setVar(s, ((Fraction) sc.getVar(s)).add((Fraction) args[1])); }); registerConsumer("-=", (args, sc) -> { String s = args[0].toString(); sc.setVar(s, ((Fraction) sc.getVar(s)).sub((Fraction) args[1])); }); registerConsumer("*=", (args, sc) -> { String s = args[0].toString(); sc.setVar(s, ((Fraction) sc.getVar(s)).mul((Fraction) args[1])); }); registerConsumer("/=", (args, sc) -> { String s = args[0].toString(); sc.setVar(s, ((Fraction) sc.getVar(s)).div((Fraction) args[1])); }); registerConsumer("%=", (args, sc) -> { String s = args[0].toString(); sc.setVar(s, ScriptUtils.getInt(sc.getVar(s)) % ScriptUtils.getInt(args[1])); }); registerConsumer("<<=", (args, sc) -> { String s = args[0].toString(); sc.setVar(s, ((Fraction) sc.getVar(s)).leftShift(ScriptUtils.getInt(args[1]))); }); registerConsumer(">>=", (args, sc) -> { String s = args[0].toString(); sc.setVar(s, ((Fraction) sc.getVar(s)).rightShift(ScriptUtils.getInt(args[1]))); }); registerConsumer("&=", (args, sc) -> { String s = args[0].toString(); sc.setVar(s, ((Fraction) sc.getVar(s)).and((Fraction) args[1])); }); registerConsumer("^=", (args, sc) -> { String s = args[0].toString(); sc.setVar(s, ((Fraction) sc.getVar(s)).xor((Fraction) args[1])); }); registerConsumer("|=", (args, sc) -> { String s = args[0].toString(); sc.setVar(s, ((Fraction) sc.getVar(s)).or((Fraction) args[1])); }); registerFunction("inc", (args, sc) -> { String s = args[0].toString(); Fraction n = ((Fraction) sc.getVar(s)).add(new Fraction(1)); sc.setVar(s, n); return n; }); registerAlias("++", "inc"); registerFunction("dec", (args, sc) -> { String s = args[0].toString(); Fraction n = ((Fraction) sc.getVar(s)).add(new Fraction(-1)); sc.setVar(s, n); return n; }); registerAlias("--", "dec"); registerFunction("p+", (args, sc) -> { String s = args[0].toString(); Fraction n = (Fraction) sc.getVar(s); sc.setVar(s, n.add(new Fraction(1))); return n; }); registerFunction("p-", (args, sc) -> { String s = args[0].toString(); Fraction n = (Fraction) sc.getVar(s); sc.setVar(s, n.add(new Fraction(-1))); return n; }); registerFunction("*", (args, sc) -> ((Fraction) args[0]).mul((Fraction) args[1])); registerAlias("mul", "*"); registerFunction("/", (args, sc) -> ((Fraction) args[0]).div((Fraction) args[1])); registerAlias("div", "/"); registerFunction("getvar", (args, sc) -> sc.getVar(args[0].toString())); registerConsumer("setvar", (args, sc) -> sc.setVar(args[0].toString(), args[1])); registerAlias("=", "setvar"); registerConsumer("removevar", (args, sc) -> sc.removeVar(args[0].toString())); registerConsumer("reset", (args, sc) -> sc.resetLoopCounter()); registerConsumer("wait", (args, sc) -> { sc.resetLoopCounter(); throw new HoldCodeException(); }); // branching registerConsumer("goto", (args, sc) -> sc.gotoLabel(args[0].toString(), false, true)); registerConsumer("sgoto", (args, sc) -> scheduleGoto(args, sc)); registerConsumer("gosub", (args, sc) -> sc.gotoLabelWithReturn(args[0].toString())); // push / pop values for functions registerConsumer("pusharg", (args, sc) -> sc.pushFunctionInput(args)); registerFunction("poparg", (args, sc) -> sc.popFunctionInput()); // push / pop values for saving registerConsumer("push", (args, sc) -> sc.push(0, args)); registerConsumer("pop", (args, sc) -> sc.pop(args)); registerConsumer("return", (args, sc) -> sc.doReturn()); registerConsumer("try", (args, sc) -> sc.saveTryJumpLine()); registerConsumer("catch", (args, sc) -> { sc.resetTryJumpLine(); sc.jump(); }); registerConsumer("if", (args, sc) -> ifFunction(args, sc)); registerConsumer("else", (args, sc) -> sc.jump()); registerConsumer("while", (args, sc) -> whileFunction(args, sc)); registerConsumer("for", (args, sc) -> { Fraction f = sc.nextRepeatData((Fraction) args[1], (Fraction) args[3]); sc.setVar(args[0].toString(), f); if(sc.repeatFinished(f, (Fraction) args[3], (Fraction) args[2])) { sc.clearRepeatData(); sc.jump(); } }); registerConsumer("break", (args, sc) -> sc.jump()); registerConsumer("breakreset", (args, sc) -> { sc.jump(); sc.clearLastRepeatData(); }); registerConsumer("continue", (args, sc) -> sc.jump()); registerFunction("==", (args, sc) -> isEqual(args)); registerAlias("equal", "=="); registerAlias("equals", "=="); registerFunction("<", (args, sc) -> ((Fraction) args[0]).compareTo((Fraction) args[1]) < 0); registerAlias("less", "<"); registerFunction(">", (args, sc) -> ((Fraction) args[0]).compareTo((Fraction) args[1]) > 0); registerAlias("greater", ">"); registerFunction("!=", (args, sc) -> !isEqual(args)); registerAlias("notequal", "!="); registerFunction("<=", (args, sc) -> ((Fraction) args[0]).compareTo((Fraction) args[1]) <= 0); registerAlias("lessequal", "<="); registerFunction(">=", (args, sc) -> ((Fraction) args[0]).compareTo((Fraction) args[1]) >= 0); registerAlias("greaterequal", ">="); registerFunction("!", (args, sc) -> !((boolean) args[0])); registerAlias("invert", "!"); registerFunction("&&", (args, sc) -> Arrays.stream(args).allMatch(s -> s.equals(true))); registerAlias("and", "&&"); registerFunction("||", (args, sc) -> Arrays.stream(args).anyMatch(s -> s.equals(true))); registerAlias("or", "||"); registerConsumer("waitfor", (args, sc) -> waitFor(args, sc)); registerConsumer("term", (args, sc) -> { termSafe(sc); throw new HoldCodeException(); }); registerConsumer("swap", (args, sc) -> { String first = args[0].toString(); String sec = args[1].toString(); Object helper = sc.getVar(first); sc.setVar(first, sc.getVar(sec)); sc.setVar(sec, helper); }); } @SuppressWarnings("unchecked") protected Object parseFunction(Script sc, String function, Object[] args) throws HoldCodeException { try { BiFunction bif = functions.get(function); if(bif == null) { try { sc.gotoLabelWithReturn(function); sc.pushFunctionInput(args, new Fraction(args.length)); return Void.TYPE; } catch(GotoLabelNotFoundException ex) { throw new NoSuchMethodException(function); } } return bif.apply(args, sc); } catch(HoldCodeException ex) { throw ex; } catch(Exception ex) { if(printStack) { ex.printStackTrace(); } if(sc.getTryJumpLine() != -1) { sc.setVar("error", ex.getClass().getSimpleName()); sc.gotoTryJumpLine(); sc.resetTryJumpLine(); return Void.TYPE; } logger.printException(ex, function, sc, sc.getActiveRealCodeLine()); sc.resetLoopCounter(); throw new HoldCodeException(); } } // ----------------------------------------------------------------------------------- // script controller // ----------------------------------------------------------------------------------- public Script getScript(int id) { return scripts.get(id); } public boolean termUnsafe(Script sc) { if(sc == null) { return false; } sc.setInvalid(); sc.onTerm(); return scripts.remove(sc.getId()) != null; } public void termSafe(Script sc) { if(sc == null) { return; } sc.setInvalid(); termQueue.add(sc.getId()); } private void term() { if(!termQueue.isEmpty()) { termQueue.forEach(i -> { Script sc = scripts.remove(i); if(sc != null) { sc.onTerm(); } }); termQueue.clear(); } } public void termAllUnsafe() { scripts.values().forEach(sc -> { sc.onTerm(); sc.setInvalid(); }); scripts.clear(); } public Collection