Browse Source

new faster, intelligent parser, interface and exception clean up

Kajetan Johannes Hammerle 7 years ago
parent
commit
e8705a4269

+ 320 - 384
src/me/hammerle/code/Code.java

@@ -2,64 +2,54 @@ package me.hammerle.code;
 
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.LinkedList;
 import java.util.Stack;
-import java.util.TreeSet;
 import me.hammerle.exceptions.PreScriptException;
 import me.hammerle.exceptions.NoSuchMethodException;
+import me.hammerle.math.Fraction;
 
-public class Code implements Comparable<Code>
+public class Code
 {
     protected final int realLine;
-    protected final int line;
-    private int subline;
+    protected final String function;
+    protected final int pars;
+    protected Object value;
     
-    private final String function;
-    private final int level;
-    private final int pars;
-    
-    private final Object value;
-    private int jump;
-    
-    private Code(String function, int level, int pars, int line, int subline, Object value, int jump, int realLine)
+    private Code(String function, int pars, Object value, int realLine)
     {
         this.function = function;
-        this.level = level;
         this.pars = pars;
-        this.line = line;
-        this.subline = subline;
         this.value = value;
-        this.jump = jump;
         this.realLine = realLine;
     }
     
-    public Code(String function, int level, int pars, int line, int subline, int realLine)
+    public Code(String function, int pars, int realLine)
     {
-        this(function.trim(), level, pars, line, subline, null, 0, realLine);
+        this(function, pars, null, realLine);
     }
     
-    public Code(int level, int pars, int line, int subline, Object value, int realLine)
+    public Code(Object value, int realLine)
     {
-        this(null, level, pars, line, subline, value, 0, realLine);
+        this(null, 0, value, realLine);
     }
     
     public void executeFunction(SnuviParser parser, Script sc, Stack<Object> stack)
     {
-        //System.out.println("Executing: " + this.toString());
-        if(value != null)
+        if(function == null)
         {
-            if(value.getClass() == Variable.class)
+            if(value != null)
             {
-                stack.push(sc.getVar(((Variable) value).getName()));
+                if(value.getClass() == Variable.class)
+                {
+                    stack.push(sc.getVar(((Variable) value).getName()));
+                    return;
+                }
+                stack.push(value);
                 return;
             }
             stack.push(value);
             return;
         }
-        else if(value == null && function == null)
-        {
-            stack.push(value);
-            return;
-        }
         Object[] input;
         if(pars > 0)
         {
@@ -79,106 +69,66 @@ public class Code implements Comparable<Code>
             stack.push(output);
         }
     }
-    
-    public String getFunction()
-    {
-        return function;
-    }
 
     @Override
     public String toString() 
     {
-        StringBuilder sb = new StringBuilder(">");
-        for(int i = 1; i < level; i++)
-        {
-            sb.append(">");
-        }
-        sb.append(" (");
-        sb.append(line);
-        sb.append(",");
-        sb.append(subline);
+        StringBuilder sb = new StringBuilder("(");
+        sb.append(realLine);
         sb.append(") ");
         
-        if(value != null)
+        if(function != null)
         {
-            sb.append("push ");
-            sb.append(value.getClass().getSimpleName());
-            sb.append(" '");
-            sb.append(value);
-            sb.append("'");
-            return sb.toString();
+            sb.append("function '");
+            sb.append(function);
+            sb.append("' ");
+            for(int i = 0; i < pars; i++)
+            {
+                sb.append("X,");
+            }
+            if(pars > 0)
+            {
+                sb.deleteCharAt(sb.length() - 1);
+            }
+            if(value != null)
+            {
+                sb.append("(");
+                sb.append(value);
+                sb.append(")");
+            }
         }
-        else if(value == null && function == null)
+        else
         {
-            sb.append("push Null '");
+            sb.append("push '");
             sb.append(value);
-            sb.append("'");
+            sb.append("' ");
+            if(value != null)
+            {
+                sb.append(value.getClass().getSimpleName());
+            }
             return sb.toString();
         }
-        
-        sb.append("function ");
-        sb.append(function);
-        sb.append(" (");
-        for(int i = 0; i < pars; i++)
-        {
-            sb.append("X,");
-        }
-        if(pars > 0)
-        {
-            sb.deleteCharAt(sb.length() - 1);
-        }
-        sb.append(")");
-        if(jump != 0)
-        {
-            sb.append(" (");
-            sb.append(jump);
-            sb.append(")");
-        }
         return sb.toString();
     }
     
-    @Override
-    public int compareTo(Code o) 
-    {
-        int i = Integer.compare(line, o.line);
-        if(i == 0)
-        {
-            return Integer.compare(o.subline, subline);
-        }
-        return i;
-    }
-    
-    public int getJumpLine()
-    {
-        return jump;
-    }
-    
     // -----------------------------------------------------------------------------------
     // code builder
     // -----------------------------------------------------------------------------------
     
-    private static SnuviParser parser = null;
     private static String scriptName = null;
-    private static int sublines = 0;
-    private static int realLines = 0;
-    private static HashMap<String, String> strings;
     
-    private static int findEndOfLine(String code, int pos)
+    private static int findFirstNonLetterOrDigit(StringBuilder code, int pos)
     {
-        int start = pos;
         int length = code.length();
         while(pos < length)
         {
-            switch(code.charAt(pos))
+            if(!Character.isLetterOrDigit(code.charAt(pos)))
             {
-                case ';':
-                case '{':
-                case '}':
-                    return pos;
+                return pos;
             }
             pos++;
         }
-        return start;
+        return -1;
     }
     
     private static int findStartOfSyntax(StringBuilder code, int pos)
@@ -197,7 +147,7 @@ public class Code implements Comparable<Code>
                 case '}':
                     if(bracketCounter != 0)
                     {
-                        throw new PreScriptException(scriptName, code.substring(pos - 1, Math.min(code.length(), pos + 20)), "unbalanced ()"); 
+                        throw new PreScriptException(scriptName, countChar('\n', pos, code), "unbalanced ()"); 
                     }
                     return pos + 1;
                 case ' ':
@@ -268,7 +218,7 @@ public class Code implements Comparable<Code>
                 case '}':
                     if(bracketCounter != 0)
                     {
-                        throw new PreScriptException(scriptName, code.substring(pos - 1, Math.min(code.length(), pos + 20)), "unbalanced ()"); 
+                        throw new PreScriptException(scriptName, countChar('\n', pos, code), "unbalanced ()"); 
                     }
                     return pos;
                 case ' ':
@@ -324,13 +274,11 @@ public class Code implements Comparable<Code>
     private static int findSyntax(String find, StringBuilder code, int pos)
     {
         int length = code.length();
-        int add = find.length() + 1;
-        String s;
+        String s = code.toString();
         while(pos < length)
         {
-            s = code.substring(pos, Math.min(pos + add, length));
             // additionel check for e.g. difference between + and +=
-            if(s.startsWith(find) && !s.startsWith(find + "=") && (pos == 0 || code.charAt(pos - 1) != '=')) 
+            if(s.startsWith(find, pos) && !s.startsWith(find + "=", pos) && (pos == 0 || code.charAt(pos - 1) != '=')) 
             {
                 return pos;
             }
@@ -345,7 +293,6 @@ public class Code implements Comparable<Code>
         int add = find.length();
         char c;
         boolean text = false;
-        String s;
         while(pos < length)
         {
             c = code.charAt(pos);
@@ -354,15 +301,13 @@ public class Code implements Comparable<Code>
                 pos++;
                 continue;
             }
-            if(c == '"')
+            else if(c == '"')
             {
                 text = !text;
                 pos++;
                 continue;
             }
-            s = code.substring(pos, Math.min(pos + add, length));
-            // additionel check for e.g. difference between + and +=
-            if(s.equals(find)) 
+            else if(code.substring(pos, Math.min(pos + add, length)).equals(find)) 
             {
                 return pos;
             }
@@ -441,17 +386,13 @@ public class Code implements Comparable<Code>
             sb.replace(start, end, newSyntax.toString());
         }
     }
-    
-    private static String replaceChar(char find, char replacement, String code)
-    {
-        return code.replace(find, replacement);
-    }
-    
-    public static int countChar(char find, String code)
+
+    public static int countChar(char find, int end, StringBuilder code)
     {
         int counter = 0;
         int pos = 0;
-        while(pos < code.length())
+        end = Math.min(end, code.length());
+        while(pos < end)
         {
             if(code.charAt(pos) == find)
             {
@@ -469,22 +410,12 @@ public class Code implements Comparable<Code>
         int pos = 0;
         while(true)
         {
-            pos = findSyntax(keyWord, sb, pos);
+            pos = sb.indexOf(keyWord, pos);
             if(pos == -1)
             {
                 break;
             }
-            if(pos >= 1 && Character.isLetter(sb.charAt(pos - 1)))
-            {
-                pos += length;
-                continue;
-            }
-            if(pos + length < sb.length() && Character.isLetter(sb.charAt(pos + length)))
-            {
-                pos += length;
-                continue;
-            }
-            if(!sb.substring(pos).startsWith(with))
+            else if(sb.charAt(pos + length) != '(')
             {
                 sb.replace(pos, pos + length, with);
             }
@@ -492,17 +423,40 @@ public class Code implements Comparable<Code>
         }
     }
     
+    private static String removeNonSyntax(String s)
+    {
+        StringBuilder sb = new StringBuilder(s);
+        int pos = 0;
+        while(pos < sb.length())
+        {
+            switch(sb.charAt(pos))
+            {
+                case ' ':
+                case '\t':
+                case '\n':
+                    sb.deleteCharAt(pos);
+                    continue;
+            }
+            pos++;
+        }
+        return sb.toString();
+    }
+    
     public static Code[] generate(SnuviParser parser, String scriptName, String code, HashMap<String, Integer> gotos)
     {
         System.out.println("START GENERATE");
         long startTime = System.currentTimeMillis();
         
         Code.scriptName = scriptName;
-        Code.parser = parser;
-        // comments
+        
         int old = 0;
         int pos;
         StringBuilder sb = new StringBuilder(code);
+        
+        // ---------------------------------------------------------------------
+        // comments
+        // ---------------------------------------------------------------------
+        
         while(true)
         {
             old = findSyntaxIgnoreString("/*", sb, old);
@@ -513,7 +467,7 @@ public class Code implements Comparable<Code>
             pos = findSyntaxIgnoreString("*/", sb, old);
             if(pos == -1)
             {
-                throw new PreScriptException(scriptName, sb.substring(old, Math.min(old + 20, sb.length())), "/* without */");
+                throw new PreScriptException(scriptName, countChar('\n', old, sb), "/* without */");
             }
             sb.delete(old, pos + 2);
         }
@@ -533,14 +487,16 @@ public class Code implements Comparable<Code>
             }
             sb.delete(old, pos + 1);
         }
-        // end comments
-
-        // replacing Strings with #... to get rid of "
+        
+        // ---------------------------------------------------------------------
+        // replacing Strings with #... to get rid of " 
+        // ---------------------------------------------------------------------
+        
         pos = 0;
-        String text;
         String replacement;
+        String text;
         int stringIndex = 0;
-        strings = new HashMap<>();
+        HashMap<String, String> strings = new HashMap<>();
         while(pos < sb.length())
         {
             if(sb.charAt(pos) == '"')
@@ -549,15 +505,14 @@ public class Code implements Comparable<Code>
                 pos++;
                 while(sb.charAt(pos) != '"')
                 {
+                    pos++;
                     if(pos >= sb.length())
                     {
-                        throw new PreScriptException(scriptName, sb.substring(old, Math.min(old + 20, sb.length())), "\" without another");
+                        throw new PreScriptException(scriptName, countChar('\n', old, sb), "\" without another");
                     }
-                    pos++;
                 }
                 pos++;
                 text = sb.substring(old, pos);
-                //System.out.println("Found String: " + text);
                 replacement = "#" + stringIndex;
                 sb.replace(old, pos, replacement);
                 strings.put(replacement, text);
@@ -566,9 +521,11 @@ public class Code implements Comparable<Code>
             }
             pos++;
         }
-        // end of string replacing
         
+        // ---------------------------------------------------------------------
         // allowing labels without ;
+        // ---------------------------------------------------------------------
+        
         old = 0;
         while(true)
         {
@@ -579,7 +536,7 @@ public class Code implements Comparable<Code>
             }
             pos++;
             old = pos;
-            pos = findEndOfSyntax(sb, pos, true);
+            pos = findFirstNonLetterOrDigit(sb, pos);
             if(pos == -1)
             {
                 break;
@@ -589,15 +546,19 @@ public class Code implements Comparable<Code>
                 sb.insert(pos, ';');
             }
         }
+        
+        // ---------------------------------------------------------------------
+        // allowing key words without ()
+        // ---------------------------------------------------------------------
 
-        // key words without ()
         addKeyWordBrackets("else", sb);        
         addKeyWordBrackets("try", sb);   
         addKeyWordBrackets("catch", sb);   
-        //System.out.println(sb);
-        // end key words
-
+        
+        // ---------------------------------------------------------------------
         // replacing of function syntax
+        // ---------------------------------------------------------------------
+ 
         changeBiSyntax("^", "math.pow", sb, false);
         changeBiSyntax("/", "div", sb, false);
         changeBiSyntax("%", "math.mod", sb, false);
@@ -622,7 +583,10 @@ public class Code implements Comparable<Code>
 
         changeBiSyntax("=", "setvar", sb, true); 
         
+        // ---------------------------------------------------------------------
         // numbers like -5 turn to sub(,5) --> fixing
+        // ---------------------------------------------------------------------
+        
         pos = 0;
         while(true)
         {
@@ -636,266 +600,229 @@ public class Code implements Comparable<Code>
         }
         
         code = sb.toString();
-
-        //System.out.println(code);
-        // end of substitution
+        
+        // ---------------------------------------------------------------------
         // split code into lines
-        // tree for sorting and inserting of code
-        TreeSet<Code> tree = new TreeSet();
-        sublines = 0;
-        realLines = 1;
-        String actual;
-        int length = code.length();
-        int level = 1;
+        // ---------------------------------------------------------------------
+
+        ArrayList<String> lines = new ArrayList<>();
+        int lineCounter = 1;
+        ArrayList<Integer> realCodeLine = new ArrayList<>();
+        old = 0;
         pos = 0;
-        int line = 0;
+        int length = code.length();
+        int counter = 0;
+        int bracketCounter = 0;
+        boolean closed = true;
+        Stack<Integer> brackets = new Stack<>();
         while(pos < length)
         {
-            old = pos;
-            pos = findEndOfLine(code, pos);
-            actual = code.substring(old, pos);
-            //System.out.println(actual);
-            realLines += countChar('\n', actual);
-            actual = actual.trim();
-            if(actual.startsWith("@"))
-            {
-                // sets the right layer of the goto, the exact position is set later
-                if(gotos.put(actual.substring(1), tree.size()) != null)
-                {
-                    throw new PreScriptException(scriptName, realLines, code.substring(old, Math.min(code.length(), pos + 20)), "double goto");
-                }
-                pos++;
-                continue;
-            }
             switch(code.charAt(pos))
             {
-                case '{':
-                    line++;
-                    splitFunctions(tree, actual, level, line);
-                    level++;
-                    break;
-                case '}': 
-                    level--;
-                    line++;
-                    sublines++;
-                    tree.add(new Code("gotoline", level, 0, line, sublines, realLines));
+                case '(':
+                    closed = false;
+                    bracketCounter++;
                     break;
-                case ';': 
-                    line++;
-                    splitFunctions(tree, actual, level, line);
+                case ')':
+                    bracketCounter--;
                     break;
-            }
-            pos++;
-        }
-        // end of code splitting
-
-        Code[] c = tree.toArray(new Code[tree.size()]);
-
-        // generating gotos of key words
-        String function;
-        int baseLevel;
-        boolean gotoLine = false;
-        int lastLineChange = 0;
-        line = 0;
-        for(int i = 0; i < c.length; i++)
-        {
-            function = c[i].function;
-            if(c[i].line != line)
-            {
-                line = c[i].line;
-                lastLineChange = i;
-            }
-            if(function == null)
-            {
-                continue;
-            }
-            switch(function)
-            {
-                case "if":
-                case "else":
-                case "try":
-                case "catch":
+                case ';':
+                    if(bracketCounter != 0)
+                    {
+                        throw new PreScriptException(scriptName, lineCounter, "unbalanced ()");
+                    }
+                    lines.add(removeNonSyntax(code.substring(old, pos)));
+                    realCodeLine.add(lineCounter);
+                    old = pos + 1;
+                    closed = true;
                     break;
-                case "while":
-                    gotoLine = true;
+                case '{':
+                    if(bracketCounter != 0)
+                    {
+                        throw new PreScriptException(scriptName, lineCounter, "unbalanced ()");
+                    }
+                    brackets.push(lineCounter);
+                    counter++;
+                    lines.add(removeNonSyntax(code.substring(old, pos)));
+                    realCodeLine.add(lineCounter);
+                    old = pos + 1;
+                    lines.add("{");
+                    realCodeLine.add(lineCounter);
                     break;
-                default:
-                    continue;
-            }
-            baseLevel = c[i].level;
-            for(int j = i + 1; j < c.length; j++)
-            {
-                if(c[j].level <= baseLevel)
-                {
-                    c[i].jump = j - i; // key word pos - end pos = jump
-                    if(gotoLine)
+                case '}':
+                    if(!closed)
                     {
-                        // setting right jump for gotoline of loops
-                        gotoLine = false;
-                        c[j].jump = lastLineChange - j - 1;
+                        throw new PreScriptException(scriptName, lineCounter, "missing ;");
                     }
+                    else if(bracketCounter != 0)
+                    {
+                        throw new PreScriptException(scriptName, lineCounter, "unbalanced ()");
+                    }
+                    counter--;
+                    if(counter < 0)
+                    {
+                        throw new PreScriptException(scriptName, lineCounter, "unexpected }");
+                    }
+                    old = pos + 1;
+                    lines.add("}");
+                    realCodeLine.add(lineCounter);
+                    brackets.pop();
                     break;
-                }
-            }
-        }
-        // end of key word jumps
-
-        // end
-        //java.util.Arrays.stream(c).forEach(co -> System.out.println(co.toString()));
-        //gotos.forEach((k, v) -> System.out.println("Lable " + k + "   " + v));
-        System.out.println("END GENERATE - " + (System.currentTimeMillis() - startTime));     
-        //System.exit(0);
-        return c;
-    } 
-    
-    private static int findOpenBracket(String code, int pos)
-    {
-        int length = code.length();
-        char c;
-        boolean text = false;
-        while(pos < length)
-        {
-            c = code.charAt(pos);
-            if(text && c != '"')
-            {
-                pos++;
-                continue;
-            }
-            switch(c)
-            {
-                case '(':
-                    return pos;
-                case '"':
-                    text = !text;
+                case '\n':
+                    lineCounter++;
                     break;
             }
             pos++;
         }
-        return -1;
-    }
-    
-    private static int findClosingBracket(String code, int pos)
-    {
-        if(pos == -1)
+        // well the second check should be useless
+        if(counter != 0 && !brackets.isEmpty())
         {
-           pos++;
+            throw new PreScriptException(scriptName, brackets.pop(), "unbalanced {}");
         }
-        int length = code.length();
-        char c;
-        boolean text = false;
-        int brackets = 0;
-        while(pos < length)
+        
+        // ---------------------------------------------------------------------
+        // generating byte code
+        // ---------------------------------------------------------------------
+        
+        ArrayList<Code> co = new ArrayList<>();    
+        Stack<Integer> parCounter = new Stack<>();
+        char current;
+        char previous;
+        int layer = 0;
+        int line;
+        boolean noFunction;
+        LinkedList<Integer> whileStart = new LinkedList<>();
+        String s;
+        for(int i = 0; i < lines.size(); i++)
         {
-            c = code.charAt(pos);
-            if(text && c != '"')
+            s = lines.get(i);
+            if(s.isEmpty())
             {
-                pos++;
                 continue;
             }
-            switch(c)
+            else if(s.startsWith("while"))
             {
-                case '(':
-                    brackets++;
-                    break;
-                case ')':
-                    brackets--;
-                    if(brackets == 0)
-                    {
-                        return pos;
-                    }
-                    break;
-                case '"':
-                    text = !text;
-                    break;
+                whileStart.push(co.size());
             }
-            pos++;
-        }
-        return -1;
-    }
-    
-    private static ArrayList<String> splitComma(String code)
-    {
-        ArrayList<String> list = new ArrayList<>();
-        int length = code.length();
-        int pos = 0;
-        int old = 0;
-        int brackets = 0;
-        char c;
-        boolean text = false;
-        while(pos < length)
-        {
-            c = code.charAt(pos);
-            if(text && c != '"')
+            else if(s.charAt(0) == '@')
             {
-                pos++;
+                gotos.put(s.substring(1), co.size());
                 continue;
             }
-            
-            switch(c)
+            line = realCodeLine.get(i);
+            previous = ',';
+            noFunction = true;
+            pos = s.length() - 1;
+            while(pos >= 0)
             {
-                case '"':
-                    text = !text;
-                    break;
-                case '(':
-                    brackets++;
-                    break;
-                case ')':
-                    brackets--;
-                    break;
-                case ',':
-                    if(brackets == 0)
-                    {
-                        list.add(code.substring(old, pos));
-                        old = pos + 1;
-                    }
-                    break;
+                current = s.charAt(pos);
+                switch(current)
+                {
+                    case ')':
+                        parCounter.add(0);
+                        if(previous != ',' && previous != ')')
+                        {
+                            throw new PreScriptException(scriptName, line, "unexpected )");
+                        }
+                        old = pos;
+                        break;
+                    case '(':
+                        if(previous != ')')
+                        {
+                            parCounter.set(parCounter.size() - 1, parCounter.lastElement() + 1);
+                            if(noFunction)
+                            {
+                                co.add(new Code(Code.convertInput(strings, s.substring(pos + 1, old), true), line));
+                            }
+                        }
+                        old = pos;
+                        pos--;
+                        while(pos >= 0 && (Character.isLetterOrDigit(s.charAt(pos)) 
+                                || s.charAt(pos) == '_' || s.charAt(pos) == '.'))
+                        {
+                            pos--;
+                        }
+                        pos++;
+                        String functionName = s.substring(pos, old).toLowerCase();
+                        if(!parser.isRegisteredFunction(functionName))
+                        {
+                            throw new NoSuchMethodException(scriptName, line, functionName);
+                        }
+                        co.add(new Code(functionName, parCounter.pop(), line));
+                        noFunction = false;
+                        break;
+                    case ',':
+                        parCounter.set(parCounter.size() - 1, parCounter.lastElement() + 1);
+                        if(noFunction)
+                        {
+                            co.add(new Code(Code.convertInput(strings, s.substring(pos + 1, old), true), line));
+                        }
+                        old = pos;
+                        noFunction = true;
+                        break;
+                    case '{':
+                        layer++;
+                        co.add(new Code("goto", 0, layer, line));
+                        break;
+                    case '}':
+                        co.add(new Code("goto", 0, layer, line));
+                        layer--;
+                        break;
+                }
+                pos--;
+                previous = current;
             }
-            pos++;
-        }
-        if(old < pos)
-        {
-            list.add(code.substring(old, pos));
-        }
-        return list;
-    }
-    
-    private static void splitFunctions(TreeSet<Code> tree, String f, int level, int line)
-    {
-        f = f.trim();
-        sublines++;
-        int start = findOpenBracket(f, 0);
-        int end = findClosingBracket(f, start);
-        if((start != -1 || end != -1) && 
-                (((start == -1 || end == -1) && start != end) || end != f.length() - 1))
-        {
-            throw new PreScriptException(scriptName, realLines, f, "unbalanced ()");
         }
-        if(start == -1)
-        {
-            tree.add(new Code(level, 0, line, sublines, convertInput(f, true), realLines));
-            return;
-        }
-        else if(start >= end - 1)
+        
+        // ---------------------------------------------------------------------
+        // generating gotos of key words
+        // ---------------------------------------------------------------------
+        
+        Code[] c = co.toArray(new Code[co.size()]);
+        for(int i = 0; i < c.length; i++)
         {
-            String functionName = f.substring(0, start).trim().toLowerCase();
-            if(!parser.isRegisteredFunction(functionName))
+            if("goto".equals(c[i].function) && c[i].value != null)
             {
-                throw new NoSuchMethodException(scriptName, realLines, f, functionName);
-            }
-            tree.add(new Code(functionName, level, 0, line, sublines, realLines));
-            return;
-        }
-        ArrayList<String> splitted = splitComma(f.substring(start + 1, end));
-        String functionName = f.substring(0, start).trim().toLowerCase();
-        if(!parser.isRegisteredFunction(functionName))
-        {
-            throw new NoSuchMethodException(scriptName, realLines, f, functionName);
+                Object value = c[i].value;
+                if((int) value <= 0)
+                {
+                    continue;
+                }
+                for(int j = i + 1; j < c.length; j++)
+                {
+                    if(value.equals(c[j].value))
+                    {
+                        c[i].value = j - i;
+                        switch(c[i-1].function)
+                        {   
+                            case "while":
+                                c[j].value = whileStart.pollLast() - j - 1;
+                                break;
+                            /*case "if":
+                            case "else":
+                            case "try":
+                            case "catch":
+                                c[j].value = 0;
+                                break;*/
+                            default:
+                                c[j].value = 0;
+                                break;
+                        }
+                        break;
+                    }
+                }
+            }        
         }
-        tree.add(new Code(functionName, level, splitted.size(), line, sublines, realLines));
-        splitted.forEach(s -> splitFunctions(tree, s, level, line));
-    }
+
+        // end
+        System.out.println("END GENERATE - " + (System.currentTimeMillis() - startTime));  
+        //java.util.Arrays.stream(c).forEach(cod -> System.out.println(cod.toString()));
+        //gotos.forEach((k, v) -> System.out.println("Lable " + k + "   " + v));   
+        //System.exit(0);
+        return c;
+    }     
     
-    public static Object convertInput(String s, boolean variable)
+    public static Object convertInput(HashMap<String, String> map, String s, boolean variable)
     {
         if(s == null)
         {
@@ -910,17 +837,14 @@ public class Code implements Comparable<Code>
         {
             if(s.length() == 1)
             {
-                return "\"";
-            }
-            else if(s.charAt(1) == '$')
-            {
-                return s.substring(2, s.length() - 1);
+                System.out.println("Convert-Input - this should never happen");
+                return "";
             }
             return s.substring(1, s.length() - 1);
         }
         else if(variable && s.startsWith("#"))
         {
-            return convertInput(strings.get(s), variable);
+            return convertInput(null, map.get(s), false);
         }
         else if(s.equals("true"))
         {
@@ -936,20 +860,32 @@ public class Code implements Comparable<Code>
         }
         try
         {
-            return Double.parseDouble(s);
+            if(s.contains("/"))
+            {
+                String[] parts = s.split("/");
+                return new Fraction(Long.parseLong(parts[0].trim()), Long.parseLong(parts[1].trim()));
+            }
+            return new Fraction(Long.parseLong(s));
         }
         catch(NumberFormatException ex)
         {
-            if(variable)
+            try
             {
-                return new Variable(s);
+                return Fraction.fromDouble(Double.parseDouble(s));
+            }
+            catch(NumberFormatException ex2)
+            {
+                if(variable)
+                {
+                    return new Variable(s);
+                }
+                return s;
             }
-            return s;
         }
     }
     
     public static Object convertInput(String s)
     {
-        return convertInput(s, false);
+        return convertInput(null, s, false);
     }
 }

+ 2 - 4
src/me/hammerle/code/ISnuviLogger.java

@@ -1,11 +1,9 @@
 package me.hammerle.code;
 
-import me.hammerle.exceptions.SnuviException;
-
 public interface ISnuviLogger 
 {
-    public void printException(SnuviException ex);
-    public void printException(SnuviException ex, Script s);
+    public void printException(Exception ex);
+    public void printException(Exception ex, Script s, int line);
     
     public void printWarning(String s);
     public void printInfo(String s);

+ 59 - 24
src/me/hammerle/code/Script.java

@@ -8,7 +8,7 @@ import java.util.Stack;
 import me.hammerle.exceptions.CodeTooLongException;
 import me.hammerle.exceptions.GotoLabelNotFoundException;
 import me.hammerle.exceptions.HoldCodeException;
-import me.hammerle.exceptions.SnuviException;
+import me.hammerle.exceptions.PreScriptException;
 
 public class Script 
 {
@@ -30,7 +30,9 @@ public class Script
 
     private int loopCounter;
     
+    // halt is only used by waitfor, the script does not react to events if halted
     private boolean halt;
+    private boolean isRunning;
     private boolean valid;
     
     private int tryJumpLine;
@@ -50,12 +52,17 @@ public class Script
         returnStack = new Stack<>();
         
         this.code = Code.generate(parser, name, code, gotos);
+        if(this.code.length == 0)
+        {
+            throw new PreScriptException(name, 0, "empty file");
+        }
         
         position = 0;
 
         loopCounter = 0;
         
         halt = false;
+        isRunning = false;
         valid = true;
         
         tryJumpLine = -1;
@@ -85,6 +92,7 @@ public class Script
     
     public Code[] getCode(int line)
     {
+        line = code[line].realLine;
         int start = 0;
         int end = code.length;
         int helper;
@@ -92,11 +100,11 @@ public class Script
         while(end - start > 1)
         {
             helper = (start + end) >> 1;
-            if(code[helper].line > line)
+            if(code[helper].realLine > line)
             {
                 end = helper;
             }
-            else if(code[helper].line <= line)
+            else if(code[helper].realLine <= line)
             {
                 start = helper;
             }
@@ -107,11 +115,11 @@ public class Script
         while(end - start > 1)
         {
             helper = (start + end) >> 1;
-            if(code[helper].line >= line)
+            if(code[helper].realLine >= line)
             {
                 end = helper;
             }
-            else if(code[helper].line < line)
+            else if(code[helper].realLine < line)
             {
                 start = helper;
             }
@@ -123,6 +131,11 @@ public class Script
     // Script-Daten
     // -----------------------------------------------------------------------------------
     
+    public ISnuviLogger getLogger()
+    {
+        return parser.logger;
+    }
+    
     public int getId() 
     {
         return id;
@@ -143,6 +156,11 @@ public class Script
         halt = b;
     }
     
+    public boolean isRunning() 
+    {
+        return isRunning;
+    }
+    
     public boolean isValid() 
     {
         return valid;
@@ -153,13 +171,12 @@ public class Script
         valid = false;
     }
     
-    public int getActiveCodeLine()
-    {
-        return code[position].line;
-    }
-    
     public int getActiveRealCodeLine()
     {
+        if(position < 0 || position >= code.length)
+        {
+            return 0;
+        }
         return code[position].realLine;
     }
     
@@ -196,22 +213,21 @@ public class Script
         {
             try
             {
+                isRunning = true;
                 while(position < code.length)
                 {
                     code[position].executeFunction(parser, this, valueStack);
                     position++;
-                    if(isHalt())
-                    {
-                        return;
-                    }
                 }
+                isRunning = false;
                 parser.termSafe(this);
             }
             catch(Exception ex)
             {
+                isRunning = false;
                 if(ex.getClass() != HoldCodeException.class)
                 {
-                    parser.logger.printException(new SnuviException(ex, this), this);
+                    parser.logger.printException(ex, this, getActiveRealCodeLine());
                 }
                 position++;
             }
@@ -270,12 +286,23 @@ public class Script
         if(loopCounter > 50)
         {
             resetLoopCounter();
-            throw new CodeTooLongException(this);
+            parser.logger.printException(new CodeTooLongException(), this, getActiveRealCodeLine());
+            throw new HoldCodeException();
         }
     }
     
     public void gotoLabel(String label, boolean scheduled)
     {
+        if(label == null)
+        {
+            Object o = code[position].value;
+            if(o != null)
+            {
+                position += (int) o;
+                incLoopCounter();
+                return;
+            }
+        }
         try
         {
             int i = gotos.get(label);
@@ -284,7 +311,8 @@ public class Script
         }
         catch(NullPointerException ex)
         {
-            throw new GotoLabelNotFoundException(this, label);
+            parser.logger.printException(new GotoLabelNotFoundException(label), this, getActiveRealCodeLine());
+            throw new HoldCodeException();
         }
     }  
     
@@ -304,21 +332,21 @@ public class Script
         position = returnStack.pop(); 
     }
     
-    public void gotoSpecialJumpLine()
+    public void jump(int i)
     {
-        position += code[position].getJumpLine();
+        position += i;
     }
     
-    public void jumpNextIfElse()
+    public void jumpNextDoElse()
     {
         if(code.length <= position + 1)
         {
             return;
         }
-        String s = code[position + 1].getFunction();
-        if(s != null && s.equals("else"))
+        position += (int) code[position + 1].value + 1;
+        if("else".equals(code[position + 1].function))
         {
-            position++;
+            position += 2;
         }
     }
     
@@ -333,7 +361,14 @@ public class Script
     
     public void saveTryJumpLine()
     {
-        tryJumpLine = position + code[position].getJumpLine() + 1;
+        System.out.println("WUSI");
+        tryJumpLine = position + ((int) code[position + 1].value) + 2;
+        if(!"catch".equals(code[tryJumpLine].function))
+        {
+            throw new IllegalStateException("try without catch");
+        }
+        tryJumpLine++;
+        System.out.println(tryJumpLine);
     }
     
     public void resetTryJumpLine()

+ 8 - 3
src/me/hammerle/code/ScriptUtils.java

@@ -33,6 +33,11 @@ public class ScriptUtils
         return ((Number) o).byteValue();
     }
     
+    public static long getLong(Object o)
+    {
+        return ((Number) o).longValue();
+    }
+    
     public static double getDouble(Object o)
     {
         return ((Number) o).doubleValue();
@@ -83,14 +88,14 @@ public class ScriptUtils
             } 
             catch (MalformedInputException ex) 
             {
-                throw new PreScriptException(filename, "", "file contains an illegal character, change file encoding");
+                throw new PreScriptException(filename, 0, "file contains an illegal character, change file encoding");
             }
             catch (IOException ex) 
             {
-                throw new PreScriptException(filename, "", "file '" + filename + "' cannot be read");
+                throw new PreScriptException(filename, 0, "file '" + filename + "' cannot be read");
             }
         }
-        throw new PreScriptException(filename, "", "file '" + filename + "' does not exist");
+        throw new PreScriptException(filename, 0, "file '" + filename + "' does not exist");
     }
     
     public static String readCode(String filename)

+ 242 - 126
src/me/hammerle/code/SnuviParser.java

@@ -1,9 +1,12 @@
 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.math.BigDecimal;
-import java.math.RoundingMode;
 import java.time.ZonedDateTime;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -19,9 +22,11 @@ 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.PreScriptException;
-import me.hammerle.exceptions.SnuviException;
+import me.hammerle.math.Fraction;
 
 public class SnuviParser 
 {
@@ -105,64 +110,76 @@ public class SnuviParser
         // bit
         // ------------------------------------------------------------------------------- 
         
-        registerFunction("bit.rightshift", (args, qd) -> 
-                (double) (getInt(args[0]) >> getInt(args[1])));
-        registerFunction("bit.leftshift", (args, qd) -> 
-                (double) (getInt(args[0]) << getInt(args[1])));
+        registerFunction("bit.rightshiftn", (args, qd) -> 
+                ((Fraction) args[0]).rightShiftNumerator(getInt(args[1])));
+        registerFunction("bit.rightshiftd", (args, qd) -> 
+                ((Fraction) args[0]).rightShiftDenominator(getInt(args[1])));
+        registerFunction("bit.leftshiftn", (args, qd) -> 
+                ((Fraction) args[0]).leftShiftNumerator(getInt(args[1])));
+        registerFunction("bit.leftshiftd", (args, qd) -> 
+                ((Fraction) args[0]).leftShiftDenominator(getInt(args[1])));
         registerFunction("bit.and", (args, qd) -> 
-                (double) (getInt(args[0]) & getInt(args[1])));
+                ((Fraction) args[0]).and((Fraction) args[1]));
         registerFunction("bit.or", (args, qd) -> 
-                (double) (getInt(args[0]) | getInt(args[1])));
+                ((Fraction) args[0]).or((Fraction) args[1]));
         registerFunction("bit.xor", (args, qd) -> 
-                (double) (getInt(args[0]) ^ getInt(args[1])));
+                ((Fraction) args[0]).xor((Fraction) args[1]));
         registerFunction("bit.invert", (args, qd) -> 
-                (double) (~getInt(args[0])));
-        registerFunction("bit.set", (args, qd) -> 
-                (double) (getInt(args[0]) | (getInt(args[2]) << getInt(args[1]))));
-        registerFunction("bit.get", (args, qd) -> 
-                (double) ((getInt(args[0]) & (1 << getInt(args[1])))) != 0);
+                ((Fraction) args[0]).invert());
+        registerFunction("bit.setn", (args, qd) -> 
+                ((Fraction) args[0]).setNumeratorBit(getInt(args[1])));
+        registerFunction("bit.setd", (args, qd) -> 
+                ((Fraction) args[0]).setDenominatorBit(getInt(args[1])));
+        registerFunction("bit.unsetn", (args, qd) -> 
+                ((Fraction) args[0]).unsetNumeratorBit(getInt(args[1])));
+        registerFunction("bit.unsetd", (args, qd) -> 
+                ((Fraction) args[0]).unsetDenominatorBit(getInt(args[1])));
+        registerFunction("bit.getn", (args, qd) -> 
+                ((Fraction) args[0]).getNumeratorBit(getInt(args[1])));
+        registerFunction("bit.getd", (args, qd) -> 
+                ((Fraction) args[0]).getDenominatorBit(getInt(args[1])));
         
         // -------------------------------------------------------------------------------    
         // math
         // -------------------------------------------------------------------------------    
         registerFunction("math.mod", (args, sc) -> 
-                ((double) args[0]) % ((double) args[1]));
+                new Fraction(getInt(args[0]) % getInt(args[1])));
         registerFunction("math.abs", (args, sc) -> 
-                Math.abs((double) args[0]));
+                ((Fraction) args[0]).abs());
         registerFunction("math.pow", (args, sc) -> 
-                Math.pow((double) args[0], (double) args[1]));
+                ((Fraction) args[0]).power((Fraction) args[1]));
         registerFunction("math.root", (args, sc) -> 
-                Math.pow((double) args[0], 1d / ((double) args[1])));
+                ((Fraction) args[0]).power(((Fraction) args[1]).invert()));
         registerFunction("math.sin", (args, sc) -> 
-                Math.sin((double) args[0]));
+                ((Fraction) args[0]).sin());
         registerFunction("math.cos", (args, sc) -> 
-                Math.cos((double) args[0]));
+                ((Fraction) args[0]).cos());
         registerFunction("math.tan", (args, sc) -> 
-                Math.tan((double) args[0]));
+                ((Fraction) args[0]).tan());
         registerFunction("math.asin", (args, sc) -> 
-                Math.asin((double) args[0]));
+                ((Fraction) args[0]).asin());
         registerFunction("math.acos", (args, sc) -> 
-                Math.acos((double) args[0]));
+                ((Fraction) args[0]).acos());
         registerFunction("math.atan", (args, sc) -> 
-                Math.atan((double) args[0]));
+                ((Fraction) args[0]).atan());
         registerFunction("math.e", (args, sc) -> 
-                Math.E);
+                Fraction.E);
         registerFunction("math.pi", (args, sc) -> 
-                Math.PI);
+                Fraction.PI);
         registerFunction("math.ln", (args, sc) -> 
-                Math.log((double) args[0]));
+                ((Fraction) args[0]).log());
         registerFunction("math.log", (args, sc) -> 
-                Math.log10((double) args[0]));
+                ((Fraction) args[0]).log());
         registerFunction("math.random", (args, sc) -> 
-                (double) ScriptUtils.randomInt(getInt(args[0]), getInt(args[1])));
+                new Fraction(ScriptUtils.randomInt(getInt(args[0]), getInt(args[1]))));
         registerFunction("math.round", (args, sc) -> 
-                Math.round((double) args[0]));
+                ((Fraction) args[0]).round());
         registerFunction("math.rounddown", (args, sc) -> 
-                Math.floor((double) args[0]));
+                ((Fraction) args[0]).floor());
         registerFunction("math.roundup", (args, sc) -> 
-                Math.ceil((double) args[0]));
+                ((Fraction) args[0]).ceil());
         registerFunction("math.roundcomma", (args, sc) -> 
-                new BigDecimal(((double) args[0])).setScale(getInt(args[1]), RoundingMode.HALF_UP).doubleValue());
+                ((Fraction) args[0]).round(getInt(args[1])));
         
         // -------------------------------------------------------------------------------  
         // lists
@@ -180,7 +197,7 @@ public class SnuviParser
         registerFunction("list.contains", (args, sc) ->                            
                 ((List) args[0]).contains(args[1]));
         registerFunction("list.getsize", (args, sc) ->                            
-                (double) ((List) args[0]).size());
+                new Fraction(((List) args[0]).size()));
         registerFunction("list.getindex", (args, sc) ->                            
                 ((List) args[0]).get(getInt(args[1])));
         registerConsumer("list.setindex", (args, sc) ->                            
@@ -188,7 +205,7 @@ public class SnuviParser
         registerConsumer("list.clear", (args, sc) ->                            
                 ((List) args[0]).clear());
         registerFunction("list.getindexof", (args, sc) ->                            
-                (double) ((List) args[0]).indexOf(args[1]));  
+                new Fraction(((List) args[0]).indexOf(args[1])));  
         registerConsumer("list.sort", (args, sc) ->  
                 sortList((List<Object>) args[0], sc));
         registerConsumer("list.reverse", (args, sc) ->                            
@@ -210,7 +227,7 @@ public class SnuviParser
         registerFunction("map.contains", (args, sc) ->                            
                 ((HashMap) args[0]).containsKey(args[1]));
         registerFunction("map.getsize", (args, sc) ->                            
-                (double) ((HashMap) args[0]).size());
+                new Fraction(((HashMap) args[0]).size()));
         registerFunction("map.get", (args, sc) ->                            
                 ((HashMap) args[0]).get(args[1]));
         registerConsumer("map.clear", (args, sc) ->                            
@@ -230,51 +247,111 @@ public class SnuviParser
         registerFunction("set.contains", (args, sc) ->                            
                 ((HashSet) args[0]).contains(args[1]));
         registerFunction("set.getsize", (args, sc) ->                            
-                (double) ((HashSet) args[0]).size());
+                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) ->                            
-                getDouble(System.currentTimeMillis()));
+                new Fraction(System.currentTimeMillis()));
         registerFunction("time.nextday", (args, sc) ->         
-                getDouble(getNextDay(args)));   
+                getNextDay(args));   
         registerFunction("time.getyear", (args, sc) ->         
-                (double) getYear(args));   
+                getYear(args));   
         registerFunction("time.getmonth", (args, sc) ->         
-                (double) getMonth(args));   
+                getMonth(args));   
         registerFunction("time.getday", (args, sc) ->         
-                (double) getDay(args));   
+                getDay(args));   
         registerFunction("time.gethour", (args, sc) ->         
-                (double) getHour(args));   
+                getHour(args));   
         registerFunction("time.getminute", (args, sc) ->         
-                (double) getMinute(args));   
+                getMinute(args));   
         registerFunction("time.getsecond", (args, sc) ->         
-                (double) getSecond(args));                   
+                getSecond(args));                   
               
         // -------------------------------------------------------------------------------    
         // text
         // -------------------------------------------------------------------------------   
         registerFunction("text.number", (args, sc) ->         
-                doubleToString(getDouble(args[0]))); 
+                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<Object>) 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]))));
+        
+        // -------------------------------------------------------------------------------    
+        // 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.", (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("add", (args, sc) -> 
-                Arrays.stream(args).mapToDouble(s -> (double) s).sum());
+                ((Fraction) args[0]).add((Fraction) args[1]));
         registerFunction("sub", (args, sc) -> 
-                ((double) args[0]) - ((double) args[1]));
+                ((Fraction) args[0]).sub((Fraction) args[1]));
         registerFunction("inc", (args, sc) -> 
                 increaseVar(args[0], sc, 1)); 
         registerFunction("dec", (args, sc) -> 
                 increaseVar(args[0], sc, -1));
         registerFunction("mul", (args, sc) -> 
-                ((double) args[0]) * ((double) args[1]));
+                ((Fraction) args[0]).mul((Fraction) args[1]));
         registerFunction("div", (args, sc) -> 
-                ((double) args[0]) / ((double) args[1]));
+                ((Fraction) args[0]).div((Fraction) args[1]));
         registerFunction("getvar", (args, sc) -> 
                 sc.getVar(args[0].toString()));
         registerConsumer("setvar", (args, sc) -> 
@@ -286,9 +363,7 @@ public class SnuviParser
         registerConsumer("wait", (args, sc) -> 
                 { sc.resetLoopCounter(); throw new HoldCodeException(); });
         registerConsumer("goto", (args, sc) -> 
-                sc.gotoLabel(args[0].toString(), false));
-        registerConsumer("gotoline", (args, sc) -> 
-                { sc.gotoSpecialJumpLine(); sc.incLoopCounter(); });
+                goTo(args, sc));
         registerConsumer("sgoto", (args, sc) -> 
                 scheduleGoto(args, sc));
         registerConsumer("gosub", (args, sc) -> 
@@ -296,44 +371,34 @@ public class SnuviParser
         registerConsumer("return", (args, sc) -> 
                 sc.doReturn());
         registerConsumer("try", (args, sc) -> 
-                sc.saveTryJumpLine());                   
+                {sc.saveTryJumpLine(); sc.jump(1);});                   
         registerConsumer("catch", (args, sc) -> 
-                {sc.gotoSpecialJumpLine(); sc.resetTryJumpLine();});
+                sc.resetTryJumpLine());
         registerConsumer("if", (args, sc) -> 
                 ifFunction(args, sc));   
         registerConsumer("else", (args, sc) -> 
-                sc.gotoSpecialJumpLine());   
+                {});   
         registerConsumer("while", (args, sc) -> 
                 whileFunction(args, sc)); 
         registerFunction("equal", (args, sc) -> 
                 isEqual(args));
         registerAlias("equals", "equal");
         registerFunction("less", (args, sc) -> 
-                (double) args[0] < (double) args[1]);
+                ((Fraction) args[0]).compareTo((Fraction) args[1]) < 0);
         registerFunction("greater", (args, sc) -> 
-                (double) args[0] > (double) args[1]);
+                ((Fraction) args[0]).compareTo((Fraction) args[1]) > 0);
         registerFunction("notequal", (args, sc) -> 
                 !isEqual(args));
         registerFunction("lessequal", (args, sc) -> 
-                (double) args[0] <= (double) args[1]);
+                ((Fraction) args[0]).compareTo((Fraction) args[1]) <= 0);
         registerFunction("greaterequal", (args, sc) -> 
-                (double) args[0] >= (double) args[1]);
+                ((Fraction) args[0]).compareTo((Fraction) args[1]) >= 0);
         registerFunction("invert", (args, sc) -> 
                 !((boolean) args[0]));
         registerFunction("and", (args, sc) -> 
                 Arrays.stream(args).allMatch(s -> s.equals(true)));
         registerFunction("or", (args, sc) -> 
                 Arrays.stream(args).anyMatch(s -> s.equals(true)));
-        registerFunction("concatlist", (args, sc) ->      
-                ((List<Object>) args[0]).stream().limit(getInt(args[3]) + 1).skip(getInt(args[2])).map(o -> o.toString()).collect(Collectors.joining(args[1].toString())));
-        registerConsumer("split", (args, sc) ->      
-                split(args, sc));             
-        registerFunction("concat", (args, sc) ->                            
-                ScriptUtils.connect(args, 0));
-        registerFunction("tolowercase", (args, sc) ->                            
-                ScriptUtils.connect(args, 0).toLowerCase());
-        registerFunction("touppercase", (args, sc) ->                            
-                ScriptUtils.connect(args, 0).toUpperCase());   
         registerConsumer("waitfor", (args, sc) ->     
                 waitFor(args, sc));
         registerConsumer("term", (args, sc) -> 
@@ -364,14 +429,7 @@ public class SnuviParser
                 sc.resetTryJumpLine();
                 return Void.TYPE;
             }
-            if(ex instanceof SnuviException)
-            {
-                logger.printException((SnuviException) ex, sc);
-            }
-            else
-            {
-                logger.printException(new SnuviException(ex, sc), sc);
-            }
+            logger.printException(ex, sc, sc.getActiveRealCodeLine());
             sc.resetLoopCounter();
             throw new HoldCodeException();
         }
@@ -451,7 +509,7 @@ public class SnuviParser
         }
         catch(PreScriptException ex)
         {
-            logger.printException(ex, script);
+            logger.printException(ex, script, ex.getLine());
             return null;
         }
     }
@@ -475,9 +533,9 @@ public class SnuviParser
             if(ex instanceof InvocationTargetException)
             {
                 Throwable t = ((InvocationTargetException) ex).getCause();
-                if(t != null && t instanceof SnuviException)
+                if(t != null && t instanceof Exception)
                 {
-                    logger.printException((SnuviException) t);
+                    logger.printException((Exception) t);
                     return null;
                 }
             }
@@ -490,11 +548,12 @@ public class SnuviParser
     // event
     // -----------------------------------------------------------------------------------
     
-    public void callEvent(String name, Consumer<Script> before, Consumer<Script> after)
+    public void callEvent(String name, Consumer<Script> before, Consumer<Script> after, Predicate<Script> check)
     {
         scripts.values().stream()
-                .filter(script -> script.receiveEventBroadcast)
-                .filter(script -> script.isLoadedEvent(name))
+                .filter(sc -> sc.receiveEventBroadcast && !sc.isHalt() && !sc.isRunning())
+                .filter(sc -> sc.isLoadedEvent(name))
+                .filter(check)
                 .forEach(sc -> 
                 {
                     sc.setVar("event", name);
@@ -513,9 +572,14 @@ public class SnuviParser
         term();
     }
     
-    public boolean callEvent(String name, Script sc, Consumer<Script> before, Consumer<Script> after)
+    public void callEvent(String name, Consumer<Script> before, Consumer<Script> after)
+    {
+        callEvent(name, before, after, sc -> true);
+    }
+    
+    public boolean callEvent(String name, Script sc, Consumer<Script> before, Consumer<Script> after, boolean check)
     {
-        if(sc.isLoadedEvent(name))
+        if(sc.isLoadedEvent(name) && !sc.isHalt() && !sc.isRunning() && check)
         {
             sc.setVar("event", name);
             if(before != null)
@@ -535,33 +599,41 @@ public class SnuviParser
         return false;
     }
     
+    public boolean callEvent(String name, Script sc, Consumer<Script> before, Consumer<Script> after)
+    {
+        return callEvent(name, sc, before, after, true);
+    }
+    
     // -----------------------------------------------------------------------------------
     // functions
     // -----------------------------------------------------------------------------------
     
-    private String doubleToString(Double d)
+    private String fractionToDoubleString(Fraction f)
     {
-        if(d.doubleValue() == d.longValue())
+        if(f.doubleValue() == f.longValue())
         {
-            return String.valueOf(d.longValue());
+            return String.valueOf(f.longValue()); 
         }
-        return d.toString();
+        return String.valueOf(f.doubleValue()); 
     }
     
     private void ifFunction(Object[] args, Script sc)
     {
         if(Arrays.stream(args).anyMatch(s -> !((boolean) s)))
         {
-            sc.gotoSpecialJumpLine();
-            sc.jumpNextIfElse();
+            sc.jumpNextDoElse();
+        }
+        else
+        {
+            sc.jump(1);
         }
     }  
     
     private void whileFunction(Object[] args, Script sc)
     {
-        if(Arrays.stream(args).anyMatch(s -> !((boolean) s)))
+        if(Arrays.stream(args).allMatch(s -> ((boolean) s)))
         {
-            sc.gotoSpecialJumpLine();
+            sc.jump(1);
         }
     } 
                   
@@ -575,19 +647,18 @@ public class SnuviParser
     {
         scheduler.scheduleTask(() -> 
         {
-            if(!sc.isValid())
+            if(!sc.isValid() && !sc.isHalt())
             {
                 return;
             }
             try
             {
                 sc.gotoLabel(args[1].toString(), true);
-                sc.setHalt(false);
                 sc.runCode();
             }
             catch(Exception ex)
             {
-                logger.printException(new SnuviException(ex, sc.getName(), "scheduled goto"), sc);
+                logger.printException(ex, sc, sc.getActiveRealCodeLine());
             }
         }, getInt(args[0]));
     }
@@ -611,11 +682,12 @@ public class SnuviParser
             sc.setHalt(false);
             sc.runCode();
         }, l); 
+        throw new HoldCodeException();
     }   
     
     private Number increaseVar(Object var, Script sc, int value)
     {
-        double n = ((double) sc.getVar(var.toString())) + value;
+        Fraction n = ((Fraction) sc.getVar(var.toString())).add(new Fraction(value));
         sc.setVar(var.toString(), n);
         return n;
     }   
@@ -630,12 +702,6 @@ public class SnuviParser
         {
             return false;
         }
-        // this is needed for comparing different number types (e.g. Integer and Double)
-        // allthough there should be no numbers except doubles...
-        else if(args[1] instanceof Number && args[0] instanceof Number)
-        {
-            return ((Number) args[0]).doubleValue() == ((Number) args[1]).doubleValue();
-        }
         return args[0].equals(args[1]);
     }
     
@@ -643,33 +709,83 @@ public class SnuviParser
     {
         sc.setVar(args[0].toString(), 
                 Arrays.stream(ScriptUtils.connect(args, 2).split(args[1].toString()))
-                        .map(s -> Code.convertInput(s, false)).collect(Collectors.toList()));
+                        .map(s -> Code.convertInput(null, s, false)).collect(Collectors.toList()));
+    }
+    
+    private void goTo(Object[] args, Script sc)
+    {
+        if(args.length == 0)
+        {
+            sc.gotoLabel(null, false);
+            return;
+        }
+        sc.gotoLabel(args[0].toString(), false);
+    }
+    
+    private void readFile(Object[] args, Script sc)
+    {
+        try
+        {
+            sc.setVar(args[0].toString(), Files.readAllLines(((File) args[1]).toPath()));
+        }
+        catch(IOException ex)
+        {
+            throw new FileIOException(ex.getMessage());
+        }
+    }
+    
+    private void writeFile(Object[] args, Script sc)
+    {
+        try
+        {
+            File f = (File) args[0];
+            if(!f.getParentFile().exists())
+            {
+                f.getParentFile().mkdirs();
+            }
+            if(!f.exists())
+            {
+                try
+                {
+                    f.createNewFile();
+                }
+                catch(IOException ex)
+                {
+                    throw new FileIOException(ex.getMessage());
+                }
+            }
+            Files.write(Paths.get(f.toURI()), (List<String>) args[1], StandardCharsets.UTF_8);
+        }
+        catch(UnsupportedOperationException | SecurityException | IOException ex)
+        {
+            throw new FileIOException(ex.getMessage());
+        }
     }
     
     // -----------------------------------------------------------------------------------   
     // number handlers, cause primitive types transform into their classes all the time
     // -----------------------------------------------------------------------------------
     
-    private int getInt(Object o)
+    public static int getInt(Object o)
     {
-        return ((Number) o).intValue();
+        return ScriptUtils.getInt(o);
     }
     
-    private long getLong(Object o)
+    public static long getLong(Object o)
     {
-        return ((Number) o).longValue();
+        return ScriptUtils.getLong(o);
     }
     
-    private double getDouble(Object o)
+    public static double getDouble(Object o)
     {
-        return ((Number) o).doubleValue();
+        return ScriptUtils.getDouble(o);
     }
     
     // -----------------------------------------------------------------------------------    
     // time handler
     // -----------------------------------------------------------------------------------
     
-    private long getNextDay(Object[] args)       
+    private Fraction getNextDay(Object[] args)       
     {
         GregorianCalendar cal = GregorianCalendar.from(ZonedDateTime.now());
         cal.setTimeInMillis(getLong(args[0]));
@@ -678,48 +794,48 @@ public class SnuviParser
         cal.set(Calendar.SECOND, 0);
         cal.set(Calendar.MINUTE, 0);
         cal.set(Calendar.MILLISECOND, 0);
-        return cal.getTimeInMillis();   
+        return new Fraction(cal.getTimeInMillis());   
     } 
     
-    private int getYear(Object[] args)       
+    private Fraction getYear(Object[] args)       
     {
         GregorianCalendar cal = GregorianCalendar.from(ZonedDateTime.now());
         cal.setTimeInMillis(getLong(args[0]));
-        return cal.get(Calendar.YEAR);   
+        return new Fraction(cal.get(Calendar.YEAR));   
     }
     
-    private int getMonth(Object[] args)       
+    private Fraction getMonth(Object[] args)       
     {
         GregorianCalendar cal = GregorianCalendar.from(ZonedDateTime.now());
         cal.setTimeInMillis(getLong(args[0]));
-        return cal.get(Calendar.MONTH) + 1;   
+        return new Fraction(cal.get(Calendar.MONTH) + 1);   
     }
     
-    private int getDay(Object[] args)       
+    private Fraction getDay(Object[] args)       
     {
         GregorianCalendar cal = GregorianCalendar.from(ZonedDateTime.now());
         cal.setTimeInMillis(getLong(args[0]));
-        return cal.get(Calendar.DAY_OF_MONTH);   
+        return new Fraction(cal.get(Calendar.DAY_OF_MONTH));   
     }
     
-    private int getHour(Object[] args)       
+    private Fraction getHour(Object[] args)       
     {
         GregorianCalendar cal = GregorianCalendar.from(ZonedDateTime.now());
         cal.setTimeInMillis(getLong(args[0]));
-        return cal.get(Calendar.HOUR_OF_DAY);   
+        return new Fraction(cal.get(Calendar.HOUR_OF_DAY));   
     }
     
-    private int getMinute(Object[] args)       
+    private Fraction getMinute(Object[] args)       
     {
         GregorianCalendar cal = GregorianCalendar.from(ZonedDateTime.now());
         cal.setTimeInMillis(getLong(args[0]));
-        return cal.get(Calendar.MINUTE);   
+        return new Fraction(cal.get(Calendar.MINUTE));   
     }
     
-    private int getSecond(Object[] args)       
+    private Fraction getSecond(Object[] args)       
     {
         GregorianCalendar cal = GregorianCalendar.from(ZonedDateTime.now());
         cal.setTimeInMillis(getLong(args[0]));
-        return cal.get(Calendar.SECOND);   
+        return new Fraction(cal.get(Calendar.SECOND));   
     }
 }

+ 9 - 6
src/me/hammerle/config/SnuviConfig.java

@@ -4,12 +4,14 @@ import java.io.File;
 import java.io.IOException;
 import java.nio.charset.Charset;
 import java.nio.charset.MalformedInputException;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Paths;
 import java.util.TreeMap;
 import java.util.stream.Collectors;
 import me.hammerle.code.Code;
 import me.hammerle.code.ISnuviLogger;
+import me.hammerle.math.Fraction;
 
 public class SnuviConfig
 {            
@@ -39,7 +41,8 @@ public class SnuviConfig
     {
         if(!exists())
         {
-            throw new IllegalStateException("cannot load non existent file '" + file.getPath() + "'");
+            logger.printWarning("cannot load non existent file '" + file.getPath() + "'");
+            return;
         }
         try
         {
@@ -118,7 +121,7 @@ public class SnuviConfig
                                 }
                                 return e.getKey() + "=" + String.valueOf(e.getValue());
                             })
-                            .collect(Collectors.toList()), Charset.forName("UTF-8"));
+                            .collect(Collectors.toList()), StandardCharsets.UTF_8);
             return true;
         }
         catch(UnsupportedOperationException ex)
@@ -145,7 +148,7 @@ public class SnuviConfig
     // getter
     // -----------------------------------------------------------------------------------
     
-    protected final <T> T get(String key, Class<T> c, T error)
+    public final <T> T get(String key, Class<T> c, T error)
     {
         try
         {
@@ -174,17 +177,17 @@ public class SnuviConfig
     
     public final float getFloat(String key, float error)
     {
-        return get(key, Double.class, Double.valueOf(error)).floatValue();
+        return get(key, Fraction.class, Fraction.fromDouble(error)).floatValue();
     }
     
     public final double getDouble(String key, double error)
     {
-        return get(key, Double.class, error);
+        return get(key, Fraction.class, Fraction.fromDouble(error)).doubleValue();
     }
     
     public final int getInt(String key, int error)
     {
-        return get(key, Double.class, Double.valueOf(error)).intValue();
+        return get(key, Fraction.class, Fraction.fromDouble(error)).intValue();
     }
     
     public final boolean getBoolean(String key, boolean error)

+ 1 - 7
src/me/hammerle/exceptions/CodeTooLongException.java

@@ -1,11 +1,5 @@
 package me.hammerle.exceptions;
 
-import me.hammerle.code.Script;
-
-public class CodeTooLongException extends SnuviException
+public class CodeTooLongException extends RuntimeException
 {
-    public CodeTooLongException(Script sc)
-    {
-        super(null, sc);
-    }
 }

+ 9 - 0
src/me/hammerle/exceptions/FileIOException.java

@@ -0,0 +1,9 @@
+package me.hammerle.exceptions;
+
+public class FileIOException extends RuntimeException
+{
+    public FileIOException(String message)
+    {
+        super(message);
+    }
+}

+ 4 - 6
src/me/hammerle/exceptions/GotoLabelNotFoundException.java

@@ -1,11 +1,9 @@
 package me.hammerle.exceptions;
 
-import me.hammerle.code.Script;
-
-public class GotoLabelNotFoundException extends IllegalStringException
+public class GotoLabelNotFoundException extends RuntimeException
 {
-    public GotoLabelNotFoundException(Script sc, String s) 
+    public GotoLabelNotFoundException(String s) 
     {
-        super(sc, s);
-    }  
+        super("invalid goto: " + s);
+    }
 }

+ 2 - 18
src/me/hammerle/exceptions/IllegalStringException.java

@@ -1,25 +1,9 @@
 package me.hammerle.exceptions;
 
-import me.hammerle.code.Script;
-
-public class IllegalStringException extends SnuviException
+public class IllegalStringException extends RuntimeException
 {
-    private final String s;
-    
-    public IllegalStringException(Script sc, String s)
-    {
-        super(null, sc);
-        this.s = s;
-    }
-    
     public IllegalStringException(String s)
     {
-        super(null);
-        this.s = s;
-    }
-    
-    public String getBadString()
-    {
-        return s;
+        super("invalid string: " + s);
     }
 }

+ 2 - 2
src/me/hammerle/exceptions/NoSuchMethodException.java

@@ -2,8 +2,8 @@ package me.hammerle.exceptions;
 
 public class NoSuchMethodException extends PreScriptException
 {
-    public NoSuchMethodException(String scriptName, int line, String code, String error) 
+    public NoSuchMethodException(String scriptName, int line, String error) 
     {
-        super(scriptName, line, code, error);
+        super(scriptName, line, error);
     }
 }

+ 7 - 12
src/me/hammerle/exceptions/PreScriptException.java

@@ -1,22 +1,17 @@
 package me.hammerle.exceptions;
 
-public class PreScriptException extends SnuviException
+public class PreScriptException extends RuntimeException
 {
-    private final String error;
+    private final int line;
     
-    public PreScriptException(String scriptName, int line, String code, String error) 
+    public PreScriptException(String scriptName, int line, String error) 
     {
-        super(null, scriptName, line, code);
-        this.error = error;
+        super(error);
+        this.line = line;
     }
     
-    public PreScriptException(String scriptName, String code, String error) 
+    public int getLine()
     {
-        this(scriptName, -1, code, error);
-    }
-    
-    public String getException()
-    {
-        return error;
+        return line;
     }
 }

+ 0 - 87
src/me/hammerle/exceptions/SnuviException.java

@@ -1,87 +0,0 @@
-package me.hammerle.exceptions;
-
-import java.util.ArrayList;
-import me.hammerle.code.Code;
-import me.hammerle.code.Script;
-
-public class SnuviException extends RuntimeException
-{
-    private final Exception ex;
-    private final String scriptName;
-    private final ArrayList<String> code;
-    private final int line;
-    
-    public SnuviException(Exception ex, String scriptName, int line, String code)
-    {
-        this.ex = ex;
-        this.scriptName = scriptName;
-        this.code = new ArrayList<>();
-        addCode(code);
-        this.line = line;
-    }
-    
-    private void addCode(String code)
-    {
-        if(code != null)
-        {
-            this.code.add(code);
-        }
-    }
-    
-    public SnuviException(Exception ex, String scriptName, String code)
-    {
-        this(ex, scriptName, -1, code);
-    }
-    
-    public SnuviException(Exception ex, String scriptName, int line)
-    {
-        this(ex, scriptName, line, null);
-    }
-    
-    public SnuviException(Exception ex, Script sc)
-    {
-        this(ex, sc.getName(), sc.getActiveRealCodeLine());
-        int i = sc.getActiveCodeLine();
-        if(i != -1)
-        {
-            for(Code c : sc.getCode(i))
-            {
-                this.code.add(c.toString());
-            }
-        }
-    }
-    
-    public SnuviException(Exception ex)
-    {
-        this(ex, null, -1);
-    }
-    
-    public Exception getOriginalException()
-    {
-        return ex;
-    }
-    
-    public int getLine()
-    {
-        return line;
-    }
-    
-    public String getCode()
-    {
-        if(code.isEmpty())
-        {
-            return null;
-        }
-        return code.get(0);
-    }
-    
-    public ArrayList<String> getWholeCode()
-    {
-        return code;
-    }
-    
-    public String getScriptName()
-    {
-        return scriptName;
-    }
-}

+ 327 - 88
src/me/hammerle/math/Fraction.java

@@ -2,7 +2,7 @@ package me.hammerle.math;
 
 import java.util.TreeSet;
 
-public final class Fraction implements Comparable<Fraction>
+public final class Fraction extends Number implements Comparable<Fraction>
 {
     private final long numerator;
     private final long denominator;
@@ -42,16 +42,27 @@ public final class Fraction implements Comparable<Fraction>
         this(numerator, 1);
     }
     
-    // -----------------------------------------------------------------------------------
+    private static final long DOUBLE_FACTOR = 10000000000l;
+    
+    public static Fraction fromDouble(double d)
+    {
+        if(d == (long) d)
+        {
+            return new Fraction((long) d);
+        }
+        return new Fraction(Math.round(d * DOUBLE_FACTOR), DOUBLE_FACTOR);
+    }
+    
+    // -------------------------------------------------------------------------
     // constants, chain fractions
-    // ----------------------------------------------------------------------------------- 
+    // ------------------------------------------------------------------------- 
     
-    /*public static final Fraction PI = Fraction.getChainFraction(
+    public static final Fraction PI = Fraction.getChainFraction(
               //3,7,15,1,292,1,1,1,2,1,3,1,14,2,1,1,2,2,2,2,1,84,2,1,1,15,3,13,1,4,2,6,6
                 3,7,15,1,292,1,1,1,2,1,3,1,14,2,1);
     public static final Fraction E = Fraction.getChainFraction(
               //2,1,2,1,1,4,1,1,6,1,1,8,1,1,10,1,1,12,1,1,14,1,1,16,...
-                2,1,2,1,1,4,1,1,6,1,1,8,1,1,10,1,1,12,1,1,14);*/
+                2,1,2,1,1,4,1,1,6,1,1,8,1,1,10,1,1,12,1,1,14);
     
     public static Fraction getChainFraction(int... ints)
     {
@@ -71,9 +82,9 @@ public final class Fraction implements Comparable<Fraction>
         return new Fraction(ints[index]).add(getChainFractionPart(index + 1, ints)).invert();
     }
     
-    // -----------------------------------------------------------------------------------
+    // -------------------------------------------------------------------------
     // basic calculating
-    // ----------------------------------------------------------------------------------- 
+    // ------------------------------------------------------------------------- 
     
     public Fraction add(Fraction f)
     {
@@ -105,12 +116,20 @@ public final class Fraction implements Comparable<Fraction>
         return mul(f.invert());
     }
     
-    // -----------------------------------------------------------------------------------
+    // -------------------------------------------------------------------------
     // roots, power
-    // ----------------------------------------------------------------------------------- 
+    // ------------------------------------------------------------------------- 
     
     private static long power(long l, long power)
     {
+        if(power == 1)
+        {
+            return l;
+        }
+        else if(power == 2)
+        {
+            return l * l;
+        }
         long factor = 1;
         if(l < 0)
         {
@@ -189,11 +208,26 @@ public final class Fraction implements Comparable<Fraction>
     
     public Fraction power(Fraction f)
     {
-        if(f.numerator == 0)
+        if(numerator < 0 && f.denominator != 1)
         {
-            return new Fraction(1);
+            throw new ArithmeticException("root of negative fraction");
+        }
+        try
+        {
+            if(f.numerator == 0)
+            {
+                return new Fraction(1);
+            }
+            else if(f.numerator < 0)
+            {
+                return this.invert().power(-f.numerator).root(f.denominator);
+            }
+            return this.power(f.numerator).root(f.denominator);
+        }
+        catch(ArithmeticException ex)
+        {
+            return fromDouble(Math.pow(this.doubleValue(), f.doubleValue()));
         }
-        return this.power(f.numerator).root(f.denominator);
     }
     
     private Fraction power(long p)
@@ -225,9 +259,9 @@ public final class Fraction implements Comparable<Fraction>
         return new Fraction(prodn, prodd);
     }
     
-    // -----------------------------------------------------------------------------------
+    // -------------------------------------------------------------------------
     // inverting
-    // ----------------------------------------------------------------------------------- 
+    // -------------------------------------------------------------------------
     
     public Fraction invertSign()
     {
@@ -247,92 +281,205 @@ public final class Fraction implements Comparable<Fraction>
         return new Fraction(denominator, numerator);
     }
     
-    // -----------------------------------------------------------------------------------
-    // trigonometric
-    // ----------------------------------------------------------------------------------- 
+    // -------------------------------------------------------------------------
+    // functions from math library
+    // -------------------------------------------------------------------------
     
-    public Fraction sin()
+    public Fraction abs()
     {
-        /*int factor = 1;
-        if(numerator < 0)
-        {
-            factor = -1;
-            numerator = -numerator;
-        }
-        
-        Fraction pi = Fraction.PI.mul(2);
-        while(this.compareTo(pi) >= 0)
+        return new Fraction(Math.abs(numerator), denominator);
+    }
+    
+    public Fraction acos()
+    {
+        return Fraction.fromDouble(Math.acos(doubleValue()));
+    }
+    
+    public Fraction asin()
+    {
+        return Fraction.fromDouble(Math.asin(doubleValue()));
+    }
+    
+    public Fraction atan()
+    {
+        return Fraction.fromDouble(Math.atan(doubleValue()));
+    }
+    
+    public Fraction atan2(Fraction f)
+    {
+        return Fraction.fromDouble(Math.atan2(doubleValue(), f.doubleValue()));
+    }
+    
+    public Fraction cbrt()
+    {
+        return this.power(new Fraction(1, 3));
+    }
+    
+    public Fraction ceil()
+    {
+        return Fraction.fromDouble(Math.ceil(doubleValue()));
+    }
+    
+    public Fraction cos()
+    {
+        return Fraction.fromDouble(Math.cos(doubleValue()));
+    }
+    
+    public Fraction cosh()
+    {
+        return Fraction.fromDouble(Math.cosh(doubleValue()));
+    }
+    
+    public Fraction floor()
+    {
+        return Fraction.fromDouble(Math.floor(doubleValue()));
+    }
+    
+    public Fraction log()
+    {
+        return Fraction.fromDouble(Math.log(doubleValue()));
+    }
+    
+    public Fraction log10()
+    {
+        return Fraction.fromDouble(Math.log10(doubleValue()));
+    }
+    
+    public Fraction log1p()
+    {
+        return Fraction.fromDouble(Math.log1p(doubleValue()));
+    }
+    
+    public Fraction max(Fraction f)
+    {
+        if(this.compareTo(f) < 0)
         {
-            this.sub(pi);
+            return f;
         }
-        
-        if(isBetween(new Fraction(0), Fraction.PI.div(2)))
+        return this;
+    }
+    
+    public Fraction min(Fraction f)
+    {
+        if(this.compareTo(f) > 0)
         {
-            // Nichts
+            return f;
         }
-        else if(isBetween(Fraction.PI.div(2), Fraction.PI))
+        return this;
+    }
+    
+    public Fraction rint()
+    {
+        return Fraction.fromDouble(Math.rint(doubleValue()));
+    }
+    
+    public Fraction round()
+    {
+        return Fraction.fromDouble(Math.round(doubleValue()));
+    }
+    
+    public Fraction round(int times)
+    {
+        if(times < 0)
         {
-            this.invertSign().add(Fraction.PI);
+            throw new IllegalArgumentException("a positive number of decimal points is needed");
         }
-        else if(this.compareTo(Fraction.PI) >= 0)
+        int factor = 1;
+        while(times > 0)
         {
-            this.sub(Fraction.PI);
-            factor = -factor;
+            factor *= 10;
+            times--;
         }
-        
-        
-        Fraction quad = this.clone().power(2);
-        Fraction value = this.clone();
-        Fraction result = this.clone();
-        
-        int k = 1;
-        for(int i = 0; i < 20; i++)
+        double d = doubleValue() * factor;
+        return new Fraction(Math.round(d), factor);
+    }
+    
+    public Fraction signum()
+    {
+        if(numerator < 0)
         {
-            value.div(i + 2);
-            if((i & 1) == 1)
-            {
-                value.mul(quad);
-                k = -k;
-                if(k == -1)
-                {
-                    result.sub(value);
-                }
-                else
-                {
-                    result.add(value);
-                }
-            }
+            return new Fraction(-1);
         }
-        numerator = factor * result.numerator;
-        denominator = result.denominator;*/
-        return this;
+        return new Fraction(1);
     }
     
-    public Fraction cos()
+    public Fraction sin()
     {
-        return this;
-        //return add(Fraction.PI.div(new Fraction(2))).sin();
+        return Fraction.fromDouble(Math.sin(doubleValue()));
+    }
+    
+    public Fraction sinh()
+    {
+        return Fraction.fromDouble(Math.sinh(doubleValue()));
     }
     
     public Fraction tan()
     {
-        return sin().div(cos());
+        return Fraction.fromDouble(Math.tan(doubleValue()));
     }
     
-    // -----------------------------------------------------------------------------------
+    public Fraction tanh()
+    {
+        return Fraction.fromDouble(Math.tanh(doubleValue()));
+    }
+    
+    public Fraction toDegrees()
+    {
+        return Fraction.fromDouble(Math.toDegrees(doubleValue()));
+    }
+    
+    public Fraction toRadians()
+    {
+        return Fraction.fromDouble(Math.toRadians(doubleValue()));
+    }
+    
+    // -------------------------------------------------------------------------
     // simplifying
-    // ----------------------------------------------------------------------------------- 
+    // ------------------------------------------------------------------------- 
     
-    /*private void simplify()
+    public Fraction simplify(int times)
     {
         if(denominator == 1)
         {
-            throw new ArithmeticException();
+            return this.copy();
         }
-        denominator = (denominator & 1) == 1 ? denominator - 1 : denominator;
-        numerator = (numerator & 1) == 1 ? numerator - 1 : numerator;
-        shortFraction();
-    }*/
+        long d = denominator;
+        long n = numerator;
+        int switcher = -1;
+        for(int i = 0; i < times; i++)
+        {
+            if(switcher == -1 && d == 1)
+            {
+                d = (d & 1) == 1 ? d + 1: d;
+                n = (n & 1) == 1 ? n + 1 : n;
+                switcher = -1;
+            }
+            else if(switcher == 1 && d == -1)
+            {
+                d = (d & 1) == 1 ? d - 1: d;
+                n = (n & 1) == 1 ? n - 1 : n;
+                switcher = 1;
+            }
+            else
+            {
+                d = (d & 1) == 1 ? d + switcher: d;
+                n = (n & 1) == 1 ? n + switcher : n;
+                switcher = -switcher;
+            }
+            
+            if(d != 1)
+            {
+                long divisor = getGreatestCommonDivisor(n, d);
+                //System.out.println("DIV: " + divisor);
+                if(divisor != 1)
+                {
+                    d /= divisor;
+                    n /= divisor;
+                }
+            }
+        }
+        return new Fraction(n, d);
+    }
     
     private long getGreatestCommonDivisor(long i, long n)
     {
@@ -370,19 +517,9 @@ public final class Fraction implements Comparable<Fraction>
         return Math.abs(Math.multiplyExact(i, n)) / getGreatestCommonDivisor(i, n);
     }
     
-    // -----------------------------------------------------------------------------------
+    // -------------------------------------------------------------------------
     // basic stuff
-    // ----------------------------------------------------------------------------------- 
-    
-    public double getDouble()
-    {
-        return numerator / (double) denominator;
-    }
-    
-    public long getLong()
-    {
-        return Math.round(numerator / (double) denominator);
-    }
+    // ------------------------------------------------------------------------- 
     
     @Override
     public String toString() 
@@ -394,7 +531,7 @@ public final class Fraction implements Comparable<Fraction>
         return numerator + " / " + denominator;
     }
 
-    public Fraction copy()
+    private Fraction copy()
     {
         return new Fraction(numerator, denominator);
     }
@@ -452,8 +589,110 @@ public final class Fraction implements Comparable<Fraction>
         }
     }
     
-    public boolean isBetween(Fraction f1, Fraction f2)
+    // -------------------------------------------------------------------------
+    // bit stuff
+    // -------------------------------------------------------------------------
+    
+    public Fraction rightShiftNumerator(int times)
+    {
+        return new Fraction(numerator >> times, denominator);
+    }
+    
+    public Fraction rightShiftDenominator(int times)
+    {
+        return new Fraction(numerator, denominator >> times);
+    }
+    
+    public Fraction leftShiftNumerator(int times)
+    {
+        return new Fraction(numerator << times, denominator);
+    }
+    
+    public Fraction leftShiftDenominator(int times)
+    {
+        return new Fraction(numerator, denominator << times);
+    }
+    
+    public Fraction and(Fraction f)
+    {
+        return new Fraction(numerator & f.numerator, denominator & f.denominator);
+    }
+    
+    public Fraction or(Fraction f)
+    {
+        return new Fraction(numerator | f.numerator, denominator | f.denominator);
+    }
+    
+    public Fraction xor(Fraction f)
+    {
+        return new Fraction(numerator ^ f.numerator, denominator ^ f.denominator);
+    }
+    
+    public Fraction invertNumerator()
+    {
+        return new Fraction(~numerator, denominator);
+    }
+    
+    public Fraction invertDenominator()
+    {
+        return new Fraction(numerator, ~denominator);
+    }
+    
+    public Fraction setNumeratorBit(int n)
+    {
+        return new Fraction(numerator | 1 << n, denominator);
+    }
+    
+    public Fraction setDenominatorBit(int n)
+    {
+        return new Fraction(numerator, denominator | 1 << n);
+    }
+    
+    public Fraction unsetNumeratorBit(int n)
+    {
+        return new Fraction(numerator & ~(1 << n), denominator);
+    }
+    
+    public Fraction unsetDenominatorBit(int n)
     {
-        return this.compareTo(f1) >= 0 && this.compareTo(f2) <= 0;
+        return new Fraction(numerator, denominator & ~(1 << n));
+    }
+    
+    public boolean getNumeratorBit(int n)
+    {
+        return (numerator & (1 << n)) != 0;
+    }
+    
+    public boolean getDenominatorBit(int n)
+    {
+        return (denominator & (1 << n)) != 0;
+    }
+    
+    // -------------------------------------------------------------------------
+    // number stuff
+    // ------------------------------------------------------------------------- 
+
+    @Override
+    public int intValue() 
+    {
+        return (int) longValue();
+    }
+
+    @Override
+    public long longValue()
+    {
+        return numerator / denominator;
+    }
+
+    @Override
+    public float floatValue() 
+    {
+        return (float) doubleValue();
+    }
+
+    @Override
+    public double doubleValue()
+    {
+        return numerator / (double) denominator;
     }
 }

+ 29 - 34
src/me/hammerle/snuviscript/SnuviScript.java

@@ -5,8 +5,6 @@ import me.hammerle.code.ISnuviScheduler;
 import me.hammerle.code.Script;
 import me.hammerle.code.SnuviParser;
 import me.hammerle.exceptions.PreScriptException;
-import me.hammerle.exceptions.SnuviException;
-import me.hammerle.math.Fraction;
 
 public class SnuviScript 
 {
@@ -15,31 +13,16 @@ public class SnuviScript
         ISnuviLogger logger = new ISnuviLogger() 
         {
             @Override
-            public void printException(SnuviException ex, Script s) 
+            public void printException(Exception ex, Script s, int line) 
             {
                 System.out.println("Exception " + ex);
-                System.out.println(ex.getOriginalException());
-                System.out.println(ex.getCode());
-                if(ex instanceof PreScriptException)
-                {
-                    System.out.println(((PreScriptException) ex).getException());
-                }
-                System.out.println(ex.getLine());
-                System.out.println(ex.getScriptName());
+                System.out.println("Line: " + line);
             }
 
             @Override
-            public void printException(SnuviException ex) 
+            public void printException(Exception ex) 
             {
                 System.out.println("Exception " + ex);
-                System.out.println(ex.getOriginalException());
-                System.out.println(ex.getCode());
-                if(ex instanceof PreScriptException)
-                {
-                    System.out.println(((PreScriptException) ex).getException());
-                }
-                System.out.println(ex.getLine());
-                System.out.println(ex.getScriptName());
             }
 
             @Override
@@ -72,6 +55,7 @@ public class SnuviScript
             }
         });
         parser.registerConsumer("debug", (o, sc) -> System.out.println(o[0]));
+        parser.registerConsumer("deb.ug", (o, sc) -> System.out.println(o[0]));
         //parser.registerFunction("ggv", (o, sc) -> o[0]);
         //parser.registerFunction("read.item", (o, sc) -> o[0]);
         
@@ -95,25 +79,36 @@ public class SnuviScript
         }
         sb.append("debug(wusi);");*/
         
-        String s = "debug(\"dev\", 3);\n" +
-"if(false) {\n" +
-"    debug($player, \"§cFehler\", \"§rDer Block unter dem Gildenblock muss ein solider Block sein\");\n" +
-"  setVar(\"cancel\", true);\n" +
-"  goto(\"wait\");\n" +
+        String s = "debug(\"Das ist ein test\");\n" +
+"if(5 < 6)\n" +
+"{\n" +
+"    debug(\"hehe\");\n" +
 "}\n" +
-"debug(\"dev\", 4);";
+"else\n" +
+"{\n" +
+"    deb.ug(\"muhahah\");\n" +
+"    debug(\"ich bin auch da\");\n" +
+"}\n" +
+"{\n" +
+"debug(\"wir sind fertig\");\n" +
+"}";
         //System.out.println(s);
         //System.out.println("___________");
         //parser.startScript(Script.class, "test", sb.toString(), true);
-        parser.startScript(Script.class, "test", s, true);
+        try
+        {
+            parser.startScript("test", s);
+        }
+        catch(PreScriptException ex)
+        {
+            //ex.printStackTrace();
+            logger.printException(ex);
+        }
         //parser.getScript(0).runCode();
 
-        /*Fraction f = new Fraction(1, 3);
-        Fraction f2 = new Fraction(2, 3);
-        System.out.println(
-                f.power(new Fraction(2))
-                        .add(f2.power(new Fraction(2)))
-                        .add(f2.power(new Fraction(2)))
-                        .power(new Fraction(1, 2)));*/
+        //Fraction f = Fraction.fromDouble(Fraction.PI.doubleValue());
+        //Object o = Code.convertInput("32.323");
+        //System.out.println(o + " " + o.getClass().getSimpleName());
+        //System.out.println(Fraction.fromDouble(0.324234235));
     }   
 }

+ 1 - 0
test.snuvic

@@ -0,0 +1 @@
+f=1.23