| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930 | package me.hammerle.snuviscript.code;import java.io.File;import java.io.IOException;import java.nio.charset.StandardCharsets;import java.lang.reflect.Array;import java.nio.file.Files;import java.nio.file.Paths;import java.time.ZonedDateTime;import java.util.List;import java.util.ArrayList;import java.util.Arrays;import java.util.Collections;import java.util.Calendar;import java.util.GregorianCalendar;import java.util.HashMap;import java.util.Map;import java.util.HashSet;import java.util.Iterator;import java.util.Objects;import java.util.Set;import java.util.function.BiFunction;import java.util.stream.Collectors;import me.hammerle.snuviscript.array.DynamicArray;import me.hammerle.snuviscript.config.SnuviConfig;import me.hammerle.snuviscript.exceptions.AssertionException;import me.hammerle.snuviscript.exceptions.FileIOException;import me.hammerle.snuviscript.variable.ArrayVariable;import me.hammerle.snuviscript.variable.Variable;public class FunctionLoader {    private static final HashMap<String, BasicFunction> FUNCTIONS = new HashMap<>();        protected static void registerFunction(String name, String fname, BiFunction<Script, InputProvider[], Object> f)    {        FUNCTIONS.put(name, new BasicFunction(fname, f));    }        protected static void registerFunction(String name, BiFunction<Script, InputProvider[], Object> f)    {        registerFunction(name, name, f);    }        protected static void registerAlias(String original, String alias)    {        FUNCTIONS.put(alias, FUNCTIONS.get(original));    }        public static BasicFunction getFunction(String f)    {        final String function = f.toLowerCase();        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<String, Variable> vars = new HashMap<>();            if(in.length != sub.subScriptInput.length)            {                throw new NullPointerException("invalid number of parameters at 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;        }));    }        static    {        // ---------------------------------------------------------------------            // system stuff        // ---------------------------------------------------------------------         registerFunction("nothing", (sc, in) -> Void.TYPE);        registerFunction("error", (sc, in) ->         {            sc.printStackTrace = !sc.printStackTrace;            return Void.TYPE;        });        registerFunction("", (sc, in) -> in[0].get(sc));                // ---------------------------------------------------------------------          // event        // ---------------------------------------------------------------------         registerFunction("event.load", (sc, in) ->        {            sc.events.add(in[0].getString(sc));            return Void.TYPE;        });        registerFunction("event.unload", (sc, in) ->        {            sc.events.remove(in[0].getString(sc));            return Void.TYPE;        });        registerFunction("event.isloaded", (sc, in) -> sc.isEventLoaded(in[0].getString(sc)));        // ---------------------------------------------------------------------            // bit        // ---------------------------------------------------------------------                 registerFunction(">>", (sc, in) -> (double) (in[0].getInt(sc) >> in[1].getInt(sc)));        registerFunction("<<", (sc, in) -> (double) (in[0].getInt(sc) << in[1].getInt(sc)));        registerFunction("&", (sc, in) -> (double) (in[0].getInt(sc) & in[1].getInt(sc)));        registerFunction("|", (sc, in) -> (double) (in[0].getInt(sc) | in[1].getInt(sc)));        registerFunction("^", (sc, in) -> (double) (in[0].getInt(sc) ^ in[1].getInt(sc)));        registerFunction("~", (sc, in) -> (double) (~in[0].getInt(sc)));        registerFunction("bit.set", (sc, in) -> (double) (in[0].getInt(sc) | (1 << (in[1].getInt(sc)))));        registerFunction("bit.unset", (sc, in) -> (double) (in[0].getInt(sc) & (~(1 << (in[1].getInt(sc))))));        registerFunction("bit.get", (sc, in) -> (in[0].getInt(sc) & (1 << (in[1].getInt(sc)))) != 0);                // ---------------------------------------------------------------------            // math        // ---------------------------------------------------------------------            registerFunction("%", (sc, in) -> (double) (in[0].getInt(sc) % in[1].getInt(sc)));        registerAlias("%", "math.mod");        registerFunction("math.abs", (sc, in) -> Math.abs(in[0].getDouble(sc)));        registerFunction("math.pow", (sc, in) -> Math.pow(in[0].getDouble(sc), in[1].getDouble(sc)));        registerFunction("math.root", (sc, in) -> Math.pow(in[0].getDouble(sc), 1.0 / in[1].getDouble(sc)));        registerFunction("math.sqrt", (sc, in) -> Math.sqrt(in[0].getDouble(sc)));        registerFunction("math.hypot", (sc, in) -> Math.hypot(in[0].getDouble(sc), in[1].getDouble(sc)));        registerFunction("math.sin", (sc, in) -> Math.sin(in[0].getDouble(sc)));        registerFunction("math.cos", (sc, in) -> Math.cos(in[0].getDouble(sc)));        registerFunction("math.tan", (sc, in) -> Math.tan(in[0].getDouble(sc)));        registerFunction("math.sin", (sc, in) -> Math.sin(in[0].getDouble(sc)));        registerFunction("math.acos", (sc, in) -> Math.acos(in[0].getDouble(sc)));        registerFunction("math.atan", (sc, in) -> Math.atan(in[0].getDouble(sc)));        registerFunction("math.asin", (sc, in) -> Math.asin(in[0].getDouble(sc)));        registerFunction("math.e", (sc, in) -> Math.E);        registerFunction("math.pi", (sc, in) -> Math.PI);        registerFunction("math.ln", (sc, in) -> Math.log(in[0].getDouble(sc)));        registerFunction("math.log", (sc, in) -> Math.log10(in[0].getDouble(sc)));        registerFunction("math.random", (sc, in) -> (double) SnuviUtils.randomInt(in[0].getInt(sc), in[1].getInt(sc)));        registerFunction("math.round", (sc, in) -> (double) Math.round(in[0].getDouble(sc)));        registerFunction("math.rounddown", (sc, in) -> Math.floor(in[0].getDouble(sc)));        registerFunction("math.roundup", (sc, in) -> Math.ceil(in[0].getDouble(sc)));        registerFunction("math.roundcomma", (sc, in) ->         {            double d = in[0].getDouble(sc);            int factor = (int) Math.pow(10, in[1].getInt(sc));            return (double) (((int) (d * factor)) / factor);        });                // ---------------------------------------------------------------------          // lists        // ---------------------------------------------------------------------            registerFunction("list.new", (sc, in) ->             {            if(in.length == 0)            {                return new ArrayList<>();            }            in[0].set(sc, new ArrayList<>());            return Void.TYPE;        });        registerFunction("list.exists", (sc, in) -> in[0].get(sc) instanceof List);        registerFunction("list.add", (sc, in) -> ((List) in[0].get(sc)).add(in[1].get(sc)));        registerFunction("list.addall", (sc, in) ->         {            List list = ((List) in[0].get(sc));            for(int i = 1; i < in.length; i++)            {                list.add(in[i].get(sc));            }            return Void.TYPE;        });        registerFunction("list.remove", (sc, in) -> ((List) in[0].get(sc)).remove(in[1].get(sc)));        registerFunction("list.removeindex", (sc, in) -> ((List) in[0].get(sc)).remove(in[1].getInt(sc)));        registerFunction("list.contains", (sc, in) -> ((List) in[0].get(sc)).contains(in[1].get(sc)));        registerFunction("list.getsize", (sc, in) -> (double) ((List) in[0].get(sc)).size());        registerFunction("list.getindex", (sc, in) -> ((List) in[0].get(sc)).get(in[1].getInt(sc)));        registerAlias("list.getindex", "list.get");        registerFunction("list.setindex", (sc, in) -> ((List) in[0].get(sc)).set(in[1].getInt(sc), in[2].get(sc)));        registerFunction("list.clear", (sc, in) ->             {            ((List) in[0].get(sc)).clear();            return Void.TYPE;        });        registerFunction("list.getindexof", (sc, in) -> (double) ((List) in[0].get(sc)).indexOf(in[1].get(sc)));        registerFunction("list.sort", (sc, in) ->             {            Collections.sort(((List<Object>) in[0].get(sc)), (Object o1, Object o2) -> ((Comparable) o1).compareTo(o2));            return Void.TYPE;        });        registerFunction("list.reverse", (sc, in) ->             {            Collections.reverse((List<Object>) in[0].get(sc));             return Void.TYPE;        });        registerFunction("list.shuffle", (sc, in) ->             {            Collections.shuffle((List<Object>) in[0].get(sc));             return Void.TYPE;        });        // ---------------------------------------------------------------------          // arrays        // ---------------------------------------------------------------------           registerFunction("array.new", (sc, in) ->         {            for(InputProvider input : in)            {                ((DynamicArray) input).init(sc);            }            return Void.TYPE;        });        registerFunction("array.getsize", (sc, in) -> (double) Array.getLength(in[0].getArray(sc)));                /*        registerFunction("array.swap", (sc, in) ->                                                           {                    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;                });        registerFunction("array.sort", (sc, in) ->                  {                    if(args.length <= 1)                    {                        Arrays.sort((Object[]) args[0]);                    }                    else                    {                        Arrays.sort((Object[]) args[0], ScriptUtils.getInt(args[1]), ScriptUtils.getInt(args[2]));                    }                });        registerFunction("array.copy", (sc, in) ->                  {                    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);                });        registerFunction("array.rsort", (sc, in) ->                    {                    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));                     }                });        registerFunction("array.fill", (sc, in) ->                     {                    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        // ---------------------------------------------------------------------         registerFunction("map.new", (sc, in) ->             {            if(in.length == 0)            {                return new HashMap<>();            }            in[0].set(sc, new HashMap<>());            return Void.TYPE;        });        registerFunction("map.exists", (sc, in) -> in[0].get(sc) instanceof Map);        registerFunction("map.add", (sc, in) -> ((Map) in[0].get(sc)).put(in[1].get(sc), in[2].get(sc)));        registerFunction("map.remove", (sc, in) -> ((Map) in[0].get(sc)).remove(in[1].get(sc)));        registerFunction("map.contains", (sc, in) -> ((Map) in[0].get(sc)).containsKey(in[1].get(sc)));        registerFunction("map.getsize", (sc, in) -> (double) ((Map) in[0].get(sc)).size());        registerFunction("map.get", (sc, in) -> ((Map) in[0].get(sc)).get(in[1].get(sc)));        registerFunction("map.getordefault", (sc, in) -> ((Map) in[0].get(sc)).getOrDefault(in[1].get(sc), in[2].get(sc)));        registerFunction("map.clear", (sc, in) ->             {            ((Map) in[0].get(sc)).clear();            return Void.TYPE;        });        registerFunction("map.keys", (sc, in) ->             {            in[0].set(sc, ((Map) in[1].get(sc)).keySet().stream().collect(Collectors.toList()));            return Void.TYPE;        });        registerFunction("map.values", (sc, in) ->             {            in[0].set(sc, ((Map) in[1].get(sc)).values().stream().collect(Collectors.toList()));            return Void.TYPE;        });                // ---------------------------------------------------------------------          // sets        // ---------------------------------------------------------------------         registerFunction("set.new", (sc, in) ->             {            if(in.length == 0)            {                return new HashSet<>();            }            in[0].set(sc, new HashSet<>());            return Void.TYPE;        });        registerFunction("set.exists", (sc, in) -> in[0].get(sc) instanceof Set);        registerFunction("set.add", (sc, in) -> ((Set) in[0].get(sc)).add(in[1].get(sc)));        registerFunction("set.addall", (sc, in) ->         {            Set set = ((Set) in[0].get(sc));            for(int i = 1; i < in.length; i++)            {                set.add(in[i].get(sc));            }            return Void.TYPE;        });        registerFunction("set.remove", (sc, in) -> ((Set) in[0].get(sc)).remove(in[1].get(sc)));        registerFunction("set.contains", (sc, in) -> ((Set) in[0].get(sc)).contains(in[1].get(sc)));        registerFunction("set.getsize", (sc, in) -> (double) ((Set) in[0].get(sc)).size());        registerFunction("set.tolist", (sc, in) ->             {            in[0].set(sc, ((Set) in[1].get(sc)).stream().collect(Collectors.toList()));            return Void.TYPE;        });        // ---------------------------------------------------------------------         // time        // ---------------------------------------------------------------------        registerFunction("time.new", (sc, in) ->               {            GregorianCalendar cal = GregorianCalendar.from(ZonedDateTime.now());            cal.setTimeInMillis(in[1].getLong(sc));            in[0].set(sc, cal);            return Void.TYPE;        });        registerFunction("time.getmillis", (sc, in) -> (double) System.currentTimeMillis());        registerFunction("time.getnanos", (sc, in) -> (double) System.nanoTime());        registerFunction("time.from", (sc, in) -> (double) ((GregorianCalendar) in[0].get(sc)).getTimeInMillis());        registerFunction("time.nextday", (sc, in) ->                 {            GregorianCalendar cal = (GregorianCalendar) in[0].get(sc);            cal.add(Calendar.DAY_OF_YEAR, 1);            cal.set(Calendar.HOUR, 0);            cal.set(Calendar.SECOND, 0);            cal.set(Calendar.MINUTE, 0);            cal.set(Calendar.MILLISECOND, 0);            return Void.TYPE;        });           registerFunction("time.getyear", (sc, in) -> (double) ((GregorianCalendar) in[0].get(sc)).get(Calendar.YEAR));        registerFunction("time.getmonth", (sc, in) -> (double) (((GregorianCalendar) in[0].get(sc)).get(Calendar.MONTH) + 1));        registerFunction("time.getday", (sc, in) -> (double) (((GregorianCalendar) in[0].get(sc)).get(Calendar.DAY_OF_MONTH)));        registerFunction("time.gethour", (sc, in) -> (double) ((GregorianCalendar) in[0].get(sc)).get(Calendar.HOUR_OF_DAY));        registerFunction("time.getminute", (sc, in) -> (double) ((GregorianCalendar) in[0].get(sc)).get(Calendar.MINUTE));        registerFunction("time.getsecond", (sc, in) -> (double) ((GregorianCalendar) in[0].get(sc)).get(Calendar.SECOND));                      // ---------------------------------------------------------------------            // text        // ---------------------------------------------------------------------           registerFunction("text.matches", (sc, in) -> in[0].getString(sc).matches(in[1].getString(sc)));              registerFunction("text.number", (sc, in) -> SnuviUtils.toString(in[0].getDouble(sc)));        registerFunction("text.class", (sc, in) -> in[0].get(sc).getClass().getSimpleName());              registerFunction("text.tolowercase", (sc, in) -> SnuviUtils.connect(sc, in, 0).toLowerCase());        registerAlias("text.tolowercase", "tolowercase");        registerFunction("text.touppercase", (sc, in) -> SnuviUtils.connect(sc, in, 0).toUpperCase());        registerAlias("text.touppercase", "touppercase");        registerFunction("text.split", (sc, in) ->              {            String[] parts = SnuviUtils.connect(sc, in, 2).split(in[1].getString(sc));            ArrayList<Object> list = new ArrayList<>();            for(String part : parts)            {                list.add(Compiler.convert(part));            }            in[0].set(sc, list);            return Void.TYPE;        });          registerAlias("text.split", "split");        registerFunction("text.concatlist", (sc, in) ->             {            StringBuilder sb = new StringBuilder();            List<Object> list = (List<Object>) in[0].get(sc);            String splitter = in[1].getString(sc);            Iterator<Object> iter = list.iterator();            int from = in[2].getInt(sc);            int to = Math.min(in[3].getInt(sc), list.size() - 1);            to -= from;            while(iter.hasNext() && from > 0)            {                iter.next();                from--;            }            while(iter.hasNext() && to > 0)            {                sb.append(iter.next());                sb.append(splitter);                to--;            }            if(iter.hasNext() && to == 0)            {                sb.append(iter.next());            }            return sb.toString();        });               registerAlias("text.concatlist", "concatlist");        registerFunction("text.concat", (sc, in) -> SnuviUtils.connect(sc, in, 0));         registerAlias("text.concat", "concat");        registerFunction("text", (sc, in) -> String.valueOf(in[0].get(sc)));               registerFunction("text.substring", (sc, in) -> in[0].getString(sc).substring(in[1].getInt(sc), in[2].getInt(sc)));         registerFunction("text.length", (sc, in) ->  (double) in[0].getString(sc).length());         registerFunction("text.startswith", (sc, in) -> in[0].getString(sc).startsWith(in[1].getString(sc), in[2].getInt(sc)));         registerFunction("text.endswith", (sc, in) -> in[0].getString(sc).endsWith(in[1].getString(sc)));         registerFunction("text.contains", (sc, in) ->  in[0].getString(sc).contains(in[1].getString(sc)));         registerFunction("text.indexof", (sc, in) -> in[0].getString(sc).indexOf(in[1].getString(sc), in[2].getInt(sc)));         registerFunction("text.lastindexof", (sc, in) -> in[0].getString(sc).lastIndexOf(in[1].getString(sc), in[2].getInt(sc)));        registerFunction("text.replace", (sc, in) -> in[0].getString(sc).replace(in[1].getString(sc), in[2].getString(sc)));        registerFunction("text.trim", (sc, in) -> in[0].getString(sc).trim());        registerFunction("text.charat", (sc, in) -> String.valueOf(in[0].getString(sc).charAt(in[1].getInt(sc))));        registerFunction("text.charcode", (sc, in) -> (double) in[0].getString(sc).charAt(in[1].getInt(sc)));        registerFunction("text.fromcode", (sc, in) -> String.valueOf((char) in[0].getInt(sc)));                // -------------------------------------------------------------------------------            // files        // -------------------------------------------------------------------------------                 registerFunction("file.new", (sc, in) ->            {            in[0].set(sc, new File(in[1].getString(sc)));            return Void.TYPE;        });        registerFunction("file.exists", (sc, in) -> ((File) in[0].get(sc)).exists());        registerFunction("file.delete", (sc, in) ->  ((File) in[0].get(sc)).delete());        registerFunction("file.getname", (sc, in) -> ((File) in[0].get(sc)).getName());        registerFunction("file.getlist", (sc, in) ->               {            in[0].set(sc, Arrays.asList(((File) in[0].get(sc)).listFiles()));            return Void.TYPE;        });        registerFunction("file.read", (sc, in) ->                 {            try            {                in[0].set(sc, Files.readAllLines(((File) in[1].get(sc)).toPath()));            }            catch(IOException ex)            {                throw new FileIOException(ex.getMessage());            }            return Void.TYPE;        });        registerFunction("file.write", (sc, in) ->                 {            try            {                File f = (File) in[0].get(sc);                if(f.getParentFile() != null)                {                    f.getParentFile().mkdirs();                }                if(!f.exists())                {                    try                    {                        f.createNewFile();                    }                    catch(IOException ex)                    {                        throw new FileIOException(ex.getMessage());                    }                }                Files.write(Paths.get(f.toURI()), ((List<Object>) in[1].get(sc))                        .stream().map(o -> String.valueOf(o)).collect(Collectors.toList()), StandardCharsets.UTF_8);            }            catch(UnsupportedOperationException | SecurityException | IOException ex)            {                throw new FileIOException(ex.getMessage());            }            return Void.TYPE;        });                // ---------------------------------------------------------------------          // config        // ---------------------------------------------------------------------                registerFunction("config.new", (sc, in) ->                {            in[0].set(sc, new SnuviConfig(sc, in[1].getString(sc), in[2].getString(sc)));            return Void.TYPE;        });              registerFunction("config.exists", (sc, in) -> ((SnuviConfig) in[0].get(sc)).exists());        registerFunction("config.save", (sc, in) -> ((SnuviConfig) in[0].get(sc)).save());        registerFunction("config.load", (sc, in) ->         {            ((SnuviConfig) in[0].get(sc)).load();            return Void.TYPE;        });        registerFunction("config.delete", (sc, in) -> ((SnuviConfig) in[0].get(sc)).delete());        registerFunction("config.set", (sc, in) ->              {            ((SnuviConfig) in[0].get(sc)).set(in[1].getString(sc), in[2].get(sc));            return Void.TYPE;        });        registerFunction("config.getbool", (sc, in) -> ((SnuviConfig) in[0].get(sc)).getBoolean(in[1].getString(sc), in[2].getBoolean(sc)));        registerFunction("config.getdouble", (sc, in) -> ((SnuviConfig) in[0].get(sc)).getDouble(in[1].getString(sc), in[2].getDouble(sc)));        registerFunction("config.getstring", (sc, in) -> ((SnuviConfig) in[0].get(sc)).getString(in[1].getString(sc), in[2].getString(sc)));                // ---------------------------------------------------------------------            // commands without library        // ---------------------------------------------------------------------           // elementary calculating        registerFunction("+", (sc, in) -> in[0].getDouble(sc) + in[1].getDouble(sc));        registerAlias("+", "add");        registerFunction("-", (sc, in) -> in[0].getDouble(sc) - in[1].getDouble(sc));        registerAlias("-", "sub");        registerFunction("*", (sc, in) -> in[0].getDouble(sc) * in[1].getDouble(sc));        registerAlias("*", "mul");        registerFunction("/", (sc, in) -> in[0].getDouble(sc) / in[1].getDouble(sc));        registerAlias("/", "div");        // var setter        registerFunction("=", (sc, in) ->         {            Object o = in[1].get(sc);            in[0].set(sc, o);            return o;        });        registerFunction("+=", (sc, in) ->         {            Object o = in[0].getDouble(sc) + in[1].getDouble(sc);            in[0].set(sc, o);            return o;        });        registerFunction("p++", (sc, in) ->         {            double d = in[0].getDouble(sc);            in[0].set(sc, d + 1.0);            return d;        });        registerAlias("p++", "inc");        registerFunction("++", (sc, in) ->         {            double d = in[0].getDouble(sc) + 1.0;            in[0].set(sc, d);            return d;        });        registerFunction("-=", (sc, in) ->         {            Object o = in[0].getDouble(sc) - in[1].getDouble(sc);            in[0].set(sc, o);            return o;        });        registerFunction("p--", (sc, in) ->         {            double d = in[0].getDouble(sc);            in[0].set(sc, d - 1.0);            return d;        });        registerAlias("p--", "dec");        registerFunction("--", (sc, in) ->         {            double d = in[0].getDouble(sc) - 1.0;            in[0].set(sc, d);            return d;        });        registerFunction("*=", (sc, in) ->         {            Object o = in[0].getDouble(sc) * in[1].getDouble(sc);            in[0].set(sc, o);            return o;        });        registerFunction("/=", (sc, in) ->         {            Object o = in[0].getDouble(sc) / in[1].getDouble(sc);            in[0].set(sc, o);            return o;        });        registerFunction("%=", (sc, in) ->         {            Object o = (double) (in[0].getInt(sc) % in[1].getInt(sc));            in[0].set(sc, o);            return o;        });        registerFunction("<<=", (sc, in) ->         {            Object o = (double) (in[0].getInt(sc) << in[1].getInt(sc));            in[0].set(sc, o);            return o;        });        registerFunction(">>=", (sc, in) ->         {            Object o = (double) (in[0].getInt(sc) >> in[1].getInt(sc));            in[0].set(sc, o);            return o;        });        registerFunction("&=", (sc, in) ->         {            Object o = (double) (in[0].getInt(sc) & in[1].getInt(sc));            in[0].set(sc, o);            return o;        });        registerFunction("^=", (sc, in) ->         {            Object o = (double) (in[0].getInt(sc) ^ in[1].getInt(sc));            in[0].set(sc, o);            return o;        });        registerFunction("|=", (sc, in) ->         {            Object o = (double) (in[0].getInt(sc) | in[1].getInt(sc));            in[0].set(sc, o);            return o;        });                // var stuff        registerFunction("getvar", (sc, in) -> sc.getVar(in[0].getString(sc)).get(sc));           registerFunction("setvar", (sc, in) ->         {            sc.getVar(in[0].getString(sc)).set(sc, in[1].get(sc));            return Void.TYPE;        });        registerFunction("removevar", (sc, in) ->         {            sc.getVar(in[0].getString(sc)).set(sc, null);            return Void.TYPE;        });                          registerFunction("wait", (sc, in) ->         {            sc.isWaiting = true;            return Void.TYPE;        });                // try - catch        registerFunction("try", (sc, in) ->         {            sc.catchLine = sc.currentLine + in[0].getInt(sc);            return Void.TYPE;        });                      registerFunction("catch", (sc, in) ->         {            if(sc.catchLine != -1)            {                sc.currentLine += in[0].getInt(sc);            }            return Void.TYPE;        });                  // branching        registerFunction("goto", (sc, in) ->         {            sc.currentLine = sc.labels.get(in[0].getString(sc));            return Void.TYPE;        });           registerFunction("ignoregoto", (sc, in) ->         {            Integer i = sc.labels.get(in[0].getString(sc));            if(i != null)            {                sc.currentLine = i;            }            return Void.TYPE;        });         registerFunction("sgoto", (sc, in) ->         {            if(sc.subScript)            {                throw new IllegalStateException("sgoto is not allowed in functions");            }            int time = in[0].getInt(sc);            if(time < 0)            {                throw new IllegalArgumentException("time units can't be negative");            }            int label = sc.labels.get(in[1].getString(sc));            sc.scheduler.scheduleTask(() ->             {                if(!sc.isValid || sc.isHolded)                {                    return;                }                sc.currentLine = label + 1;                sc.run();            }, time);            return Void.TYPE;        });        registerFunction("gosub", (sc, in) ->         {            sc.returnStack.push(sc.currentLine);            sc.currentLine = sc.labels.get(in[0].getString(sc));            return Void.TYPE;        });        registerFunction("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("if", (sc, in) ->         {            sc.ifState = in[0].getBoolean(sc);            if(!sc.ifState)            {                sc.currentLine += in[1].getInt(sc);            }            return Void.TYPE;        });        registerFunction("endif", (sc, in) ->         {            sc.ifState = true;            return Void.TYPE;        });        registerFunction("elseif", (sc, in) ->         {            if(sc.ifState)            {                sc.currentLine += in[1].getInt(sc);            }            else            {                sc.ifState = in[0].getBoolean(sc);                if(!sc.ifState)                {                    sc.currentLine += in[1].getInt(sc);                }            }            return Void.TYPE;        });        registerFunction("else", (sc, in) ->         {            if(sc.ifState)            {                sc.currentLine += in[0].getInt(sc);            }            sc.ifState = true;            return Void.TYPE;        });          registerFunction("while", (sc, in) ->         {            if(!in[0].getBoolean(sc))            {                sc.currentLine += in[1].getInt(sc);            }            return Void.TYPE;        });        registerFunction("wend", (sc, in) ->         {            sc.currentLine += in[0].getInt(sc);            return Void.TYPE;        });        registerFunction("for", (sc, in) ->         {            // for(var, start, end, step)            double start = in[1].getDouble(sc);            in[0].set(sc, start);                       if(start > in[2].getDouble(sc))            {                sc.currentLine += in[4].getInt(sc);            }            return Void.TYPE;        });        registerFunction("next", (sc, in) ->         {            int line = sc.currentLine + in[0].getInt(sc);            InputProvider[] f = sc.code[line].getArguments();            // for(var, start, end, step)            double current = f[0].getDouble(sc) + f[3].getDouble(sc);            f[0].set(sc, current);            if(current <= f[2].getDouble(sc))            {                sc.currentLine = line;            }            return Void.TYPE;        });        registerFunction("continue", (sc, in) ->         {            sc.currentLine += in[0].getInt(sc);            return Void.TYPE;        });        registerFunction("break", (sc, in) ->         {            sc.currentLine += in[0].getInt(sc);            return Void.TYPE;        });                // comparing        registerFunction("==", (sc, in) -> Objects.equals(in[0].get(sc), in[1].get(sc)));        registerAlias("==", "equal");        registerAlias("==", "equals");        registerFunction("!=", (sc, in) -> !Objects.equals(in[0].get(sc), in[1].get(sc)));        registerAlias("!=", "notequal");        registerFunction("<", (sc, in) -> in[0].getDouble(sc) < in[1].getDouble(sc));        registerAlias("<", "less");        registerFunction(">", (sc, in) -> in[0].getDouble(sc) > in[1].getDouble(sc));        registerAlias(">", "greater");        registerFunction("<=", (sc, in) -> in[0].getDouble(sc) <= in[1].getDouble(sc));        registerAlias("<=", "lessequal");        registerFunction(">=", (sc, in) -> in[0].getDouble(sc) >= in[1].getDouble(sc));        registerAlias(">=", "greaterequal");        registerFunction("!", (sc, in) -> !in[0].getBoolean(sc));        registerAlias("!", "invert");                // logical stuff        registerFunction("&&", (sc, in) ->         {            for(InputProvider i : in)            {                if(!i.getBoolean(sc))                {                    return false;                }            }            return true;        });        registerAlias("&&", "and");        registerFunction("||", (sc, in) ->         {            for(InputProvider i : in)            {                if(i.getBoolean(sc))                {                    return true;                }            }            return false;        });        registerAlias( "||", "or");                // non grouped stuff        registerFunction("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("print", (sc, in) ->         {            System.out.println(SnuviUtils.connect(sc, in, 0));            return Void.TYPE;        });        registerFunction("waitfor", (sc, in) ->            {            if(sc.subScript)            {                throw new IllegalStateException("waitfor is not allowed in functions");            }            long l = in[0].getInt(sc);            if(l < 0)            {                throw new IllegalArgumentException("time units can't be negative");            }            sc.isHolded = true;            sc.scheduler.scheduleTask(() ->             {                           // activate this again on NullPointerException                // if(sc == null || !sc.isValid)                if(sc.isValid)                {                    sc.isHolded = false;                    sc.run();                }            }, l);             sc.isWaiting = true;            return Void.TYPE;        });        registerFunction("term", (sc, in) ->         {            sc.parser.termSafe(sc);            return Void.TYPE;        });                        registerFunction("islong", (sc, in) ->                                                   {            Object o = in[0].get(sc);            if(o instanceof Double)            {                double d = (Double) o;                return d == (long) d;            }            return false;        });        registerFunction("assert", (sc, in) ->                                                   {            if(!in[0].getBoolean(sc))            {                throw new AssertionException("assertion failed");            }            return Void.TYPE;        });        registerFunction("class", (sc, in) -> in[0].get(sc).getClass());        }}
 |