Quellcode durchsuchen

bugfixes, $ for access to global vars in functions

Kajetan Johannes Hammerle vor 6 Jahren
Ursprung
Commit
c08380c482

+ 2 - 3
src/me/hammerle/snuviscript/SnuviScript.java

@@ -68,7 +68,7 @@ public class SnuviScript
         SnuviParser parser = new SnuviParser(logger, scheduler);
         parser.startScript(true, ".sbasic", "./test");  
         
-        try
+        /*try
         {
             List<String> list = SnuviUtils.readCode(".sbasic", "./test");           
             Tokenizer t = new Tokenizer(String.join("\n", list));
@@ -79,9 +79,8 @@ public class SnuviScript
             System.out.println(ex);
             System.out.println(ex.getStartLine() + "   " + ex.getEndLine());
             ex.printStackTrace();
-        }
+        }*/
         
-        //System.out.println("spawn - " + conf.getLocation("spawn"));
         //parser.callEvent("testevent", null, null);      
     }  
     

+ 2 - 2
src/me/hammerle/snuviscript/code/BasicFunction.java

@@ -20,9 +20,9 @@ public final class BasicFunction
     
     public Object execute(Script sc, InputProvider[] input)
     {
-        sc.currentFunction = name;
+        sc.currentCommand = name;
         Object o = f.apply(sc, input);
-        sc.currentFunction = name;
+        sc.currentCommand = name;
         return o;
     }
 }

+ 69 - 96
src/me/hammerle/snuviscript/code/Compiler.java

@@ -1,6 +1,5 @@
 package me.hammerle.snuviscript.code;
 
-import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
@@ -18,26 +17,30 @@ import me.hammerle.snuviscript.exceptions.PreScriptException;
 
 public class Compiler 
 {
-    public static Instruction[] compile(Script sc, List<String> sCode, HashMap<String, Integer> labels, boolean locale, int lineOffset)
+    public static Instruction[] compile(
+            Script sc, List<String> sCode, HashMap<String, Integer> labels, 
+            HashMap<String, Integer> functions, HashMap<String, HashMap<String, Integer>> localLabels)
     {
-        Compiler compiler = new Compiler(sc, sCode, labels, locale);
-        compiler.lineOffset = lineOffset;
+        Compiler compiler = new Compiler(sCode, labels, functions, localLabels);
         Instruction[] instructions = compiler.compile();
         sc.vars = compiler.vars;
         return instructions;
     }
     
     private final List<String> sCode;
-    private final HashMap<String, Variable> vars;
-    private final HashMap<String, Variable> localVars;
+    private final HashMap<String, Variable> vars = new HashMap<>();
     private final HashMap<String, Integer> labels;
     
-    private final LinkedList<Instruction> code;
-    private int line;
-    private int lineOffset;
-    private int layer;
+    private final HashMap<String, Variable> localVars = new HashMap<>();
+    private final HashMap<String, Integer> functions;
+    private final HashMap<String, HashMap<String, Integer>> localLabels;
+    private String currentFunction = null;
     
-    private JumpData tryState;
+    private final LinkedList<Instruction> code = new LinkedList<>();;
+    private int line = 0;
+    private int layer = 0;
+    
+    private JumpData tryState = null;
     
     private class JumpWrapper
     {
@@ -51,44 +54,38 @@ public class Compiler
         }
     }
     
-    private final Stack<JumpWrapper> jumps;
-    private final Stack<JumpWrapper> loopJumps;
-    private final LinkedList<JumpData> breakContinueJumps;
-    private final Script parentScript;
-    
-    private final HashMap<String, String> strings;
-    private int stringCounter;
+    private final Stack<JumpWrapper> jumps = new Stack<>();
+    private final Stack<JumpWrapper> loopJumps = new Stack<>();
+    private final LinkedList<JumpData> breakContinueJumps = new LinkedList<>();
     
-    private final boolean locale;
+    private final HashMap<String, String> strings = new HashMap<>();
+    private int stringCounter = 0;
     
-    private Compiler(Script sc, List<String> sCode, HashMap<String, Integer> labels, boolean locale)
+    private Compiler(List<String> sCode, HashMap<String, Integer> labels, 
+            HashMap<String, Integer> functions, HashMap<String, HashMap<String, Integer>> localLabels)
     {
-        this.parentScript = sc;
         this.sCode = sCode;
-        this.vars = new HashMap<>();
-        this.localVars = new HashMap<>();
-        this.labels = labels;
-        
-        this.code = new LinkedList<>();
-        this.line = 0;
-        this.layer = 0;
-        this.tryState = null;
-        this.jumps = new Stack<>();
-        this.loopJumps = new Stack<>();
-        this.breakContinueJumps = new LinkedList<>();
-        this.strings = new HashMap<>();
-        this.stringCounter = 0;
-        this.locale = locale;
+        this.labels = labels;       
+        this.functions = functions;
+        this.localLabels = localLabels;
     }
     
     private void addCodeInstruction(String function, InputProvider[] input)
     {
-        code.add(new Instruction(line + lineOffset + 1, (byte) layer, new Function(FunctionLoader.getFunction(function), input)));
+        code.add(new Instruction(line + 1, (byte) layer, new Function(FunctionLoader.getFunction(function), input)));
     }
     
     private void addLabel(String name, int line)
     {
-        if(labels.put(name, line) != null)
+        if(currentFunction != null)
+        {
+            HashMap<String, Integer> map = localLabels.get(currentFunction);
+            if(map.put(name, line) != null)
+            {
+                throw new PreScriptException("label duplicate", line);
+            }
+        }
+        else if(labels.put(name, line) != null)
         {
             throw new PreScriptException("label duplicate", line);
         }
@@ -167,6 +164,12 @@ public class Compiler
                         JumpWrapper data = jumps.pop();
                         switch(data.function)
                         {
+                            case "function":
+                            {
+                                data.data.setRelativeJump(code.size());
+                                currentFunction = null;
+                                break;
+                            }
                             case "try":
                             {
                                 tryState = data.data;
@@ -211,7 +214,7 @@ public class Compiler
                         check = sb.toString();
                         if(check.startsWith("function "))
                         {
-                            if(parentScript.subScript)
+                            if(currentFunction != null)
                             {
                                 throw new PreScriptException("function not allowed in another function", line);
                             }
@@ -220,7 +223,9 @@ public class Compiler
                             {
                                 throw new PreScriptException("missing function syntax", line);
                             }
-                            String function = check.substring(9, index);
+                            currentFunction = check.substring(9, index);
+                            functions.put(currentFunction, code.size());
+                            localLabels.put(currentFunction, new HashMap<>());
                             int endIndex = check.indexOf(")", index);
                             if(index == -1)
                             {
@@ -235,57 +240,18 @@ public class Compiler
                             {
                                 inputs = check.substring(index + 1, endIndex).split("[ ]*,[ ]*");
                             }
-                            ArrayList<String> subList = new ArrayList<>();
-                            int bCounter = 1;
-                            String subLine = check;
-                            pos++;
-                            
-                            int oldLine = line;
-                            out: while(true)
+                            InputProvider[] in = new InputProvider[inputs.length + 1];
+                            for(int i = 1; i < in.length; i++)
                             {
-                                old = pos;
-                                while(pos < subLine.length())
-                                {
-                                    switch(subLine.charAt(pos))
-                                    {
-                                        case '"':
-                                            text = !text;
-                                            break;
-                                        case '{':
-                                            if(!text)
-                                            {
-                                                bCounter++;
-                                            }
-                                            break;
-                                        case '}':
-                                            if(!text)
-                                            {
-                                                bCounter--;
-                                                if(bCounter == 0)
-                                                {
-                                                    subList.add(subLine.substring(old, pos).trim());
-                                                    sb = new StringBuilder();
-                                                    sCode.set(line, subLine.substring(pos + 1));
-                                                    line--;
-                                                    break out;
-                                                }
-                                            }
-                                            break;
-                                    }
-                                    pos++;
-                                }
-                                subList.add(subLine.substring(old, pos).trim());
-                                line++;
-                                if(line >= sCode.size())
-                                {
-                                    throw new PreScriptException("{ without }", line);
-                                }
-                                pos = 0;
-                                subLine = sCode.get(line);
+                                in[i] = new ConstantString(inputs[i - 1]);
                             }
-                            
-                            Script sub = new Script(subList, inputs, parentScript, oldLine);
-                            parentScript.subScripts.put(function, sub);
+                            JumpData jump = new JumpData(code.size());
+                            in[0] = jump;
+                            jumps.add(new JumpWrapper(jump, "function"));
+                            addCodeInstruction("function", in);
+                            pos = endIndex + 1;
+                            layer++;
+                            sb.delete(0, pos + 1);
                         }
                         else
                         {
@@ -332,11 +298,11 @@ public class Compiler
         
         Instruction[] input = code.toArray(new Instruction[code.size()]);
         
-        for(Instruction in : input)
+        /*for(Instruction in : input)
         {
             System.out.println(in);
         }
-        System.out.println("__________________________________");
+        System.out.println("__________________________________");*/
         /*labels.entrySet().stream().forEach((e) -> 
         {
             System.out.println("LABEL " + e.getKey() + " " + e.getValue());
@@ -702,20 +668,23 @@ public class Compiler
         {
             return null;
         }
-        else if(SnuviUtils.isNumber(input))
+        else if(input.startsWith("\"") && input.endsWith("\""))
+        {
+            return input.substring(1, input.length() - 1);
+        }
+        try
         {
             return Double.parseDouble(input);
         }
-        else if(input.startsWith("\"") && input.endsWith("\""))
+        catch(NumberFormatException ex)
         {
-            return input.substring(1, input.length() - 1);
+            return input;
         }
-        return input;
     }
     
     private Variable getOrCreateVariable(String var)
     {
-        if(locale)
+        if(currentFunction != null && var.charAt(0) != '$')
         {
             Variable oldVar = localVars.get(var);
             if(oldVar == null)
@@ -727,6 +696,10 @@ public class Compiler
         }
         else
         {
+            if(var.charAt(0) == '$')
+            {
+                var = var.substring(1);
+            }                     
             Variable oldVar = vars.get(var);
             if(oldVar == null)
             {
@@ -739,7 +712,7 @@ public class Compiler
     
     private DynamicArray createArray(String var, InputProvider[] in)
     {
-        if(locale)
+        if(currentFunction != null)
         {
             Variable oldVar = localVars.get(var);
             if(oldVar == null)

+ 44 - 28
src/me/hammerle/snuviscript/code/FunctionLoader.java

@@ -52,23 +52,24 @@ public class FunctionLoader
         final String function = f.toLowerCase();
         return FUNCTIONS.getOrDefault(function, new BasicFunction(function, (sc, in) -> 
         {
-            Script sub = sc.subScripts.get(function);
+            Integer sub = sc.functions.get(function);
             if(sub == null)
             {
                 throw new NullPointerException("function " + function + " does not exist");
             }
+            InputProvider[] arguments = sc.code[sub].getArguments();
             // push storage for local vars
             HashMap<String, Variable> vars = new HashMap<>();
-            if(in.length != sub.subScriptInput.length)
+            if(in.length != arguments.length - 1)
             {
-                throw new NullPointerException("invalid number of parameters at function '" + function + "'");
+                throw new NullPointerException("invalid number of arguments at function '" + function + "'");
             }
             // generate local vars
             String s;
             Variable v;
             for(int i = 0; i < in.length; i++)
             {
-                s = sub.subScriptInput[i];
+                s = arguments[i + 1].getString(sc);
                 if(in[i].isArray(sc))
                 {
                     v = new ArrayVariable(s);
@@ -82,17 +83,14 @@ public class FunctionLoader
                 vars.put(s, v);
             }
             
-            sub.localVars.push(vars);
-            // saving line for return
-            int line = sub.currentLine;
-            // set starting line for current run
-            sub.currentLine = 0;
-            // run subscript and save return value
-            Object r = sub.run();
-            // return back to previous line
-            sub.currentLine = line;
-            // pop storage for local vars
-            sub.localVars.pop();
+            sc.currentFunction = function;
+            sc.localVars.push(vars);
+            sc.returnStack.push(sc.currentLine);
+            sc.currentLine = sub + 1;
+            Object r = sc.run();
+            sc.currentLine = sc.returnStack.pop();
+            sc.localVars.pop();
+            sc.currentFunction = null;
             return r;
         }));
     }
@@ -353,6 +351,11 @@ public class FunctionLoader
             in[0].set(sc, ((Set) in[1].get(sc)).stream().collect(Collectors.toList()));
             return Void.TYPE;
         });
+        registerFunction("set.clear", (sc, in) ->     
+        {
+            ((Set) in[0].get(sc)).clear();
+            return Void.TYPE;
+        });
 
         // --------------------------------------------------------------------- 
         // time
@@ -534,6 +537,14 @@ public class FunctionLoader
         registerFunction("config.getdouble", (sc, in) -> ((SnuviConfig) in[0].get(sc)).getDouble(in[1].getString(sc), in[2].getDouble(sc)));
         registerFunction("config.getstring", (sc, in) -> ((SnuviConfig) in[0].get(sc)).getString(in[1].getString(sc), in[2].getString(sc)));
         
+        // ---------------------------------------------------------------------  
+        // read library   
+        // ---------------------------------------------------------------------
+        registerFunction("read.number", (sc, in) -> 
+        {
+            return Double.parseDouble(in[0].getString(sc));
+        });
+        
         // ---------------------------------------------------------------------    
         // commands without library
         // ---------------------------------------------------------------------   
@@ -677,14 +688,19 @@ public class FunctionLoader
         });  
         
         // branching
+        registerFunction("function", (sc, in) -> 
+        {
+            sc.currentLine += in[0].getInt(sc);
+            return Void.TYPE;
+        });  
         registerFunction("goto", (sc, in) -> 
         {
-            sc.currentLine = sc.labels.get(in[0].getString(sc));
+            sc.currentLine = sc.getLabel(in[0].getString(sc));
             return Void.TYPE;
         });   
         registerFunction("ignoregoto", (sc, in) -> 
         {
-            Integer i = sc.labels.get(in[0].getString(sc));
+            Integer i = sc.getLabel(in[0].getString(sc));
             if(i != null)
             {
                 sc.currentLine = i;
@@ -693,16 +709,12 @@ public class FunctionLoader
         }); 
         registerFunction("sgoto", (sc, in) -> 
         {
-            if(sc.subScript)
-            {
-                throw new IllegalStateException("sgoto is not allowed in functions");
-            }
             int time = in[0].getInt(sc);
             if(time < 0)
             {
                 throw new IllegalArgumentException("time units can't be negative");
             }
-            int label = sc.labels.get(in[1].getString(sc));
+            int label = sc.getLabel(in[1].getString(sc));
             sc.scheduler.scheduleTask(() -> 
             {
                 if(!sc.isValid || sc.isHolded)
@@ -717,7 +729,7 @@ public class FunctionLoader
         registerFunction("gosub", (sc, in) -> 
         {
             sc.returnStack.push(sc.currentLine);
-            sc.currentLine = sc.labels.get(in[0].getString(sc));
+            sc.currentLine = sc.getLabel(in[0].getString(sc));
             return Void.TYPE;
         });
         registerFunction("return", (sc, in) -> 
@@ -729,7 +741,15 @@ public class FunctionLoader
             }
             else
             {
-                sc.currentLine = sc.returnStack.pop();
+                if(sc.localVars.empty())
+                {
+                    sc.currentLine = sc.returnStack.pop();
+                }
+                else
+                {
+                    sc.end();
+                    sc.returnValue = in.length > 0 ? in[0].get(sc) : null;
+                }
             }
             return Void.TYPE;
         });
@@ -878,10 +898,6 @@ public class FunctionLoader
         });
         registerFunction("waitfor", (sc, in) ->    
         {
-            if(sc.subScript)
-            {
-                throw new IllegalStateException("waitfor is not allowed in functions");
-            }
             long l = in[0].getInt(sc);
             if(l < 0)
             {

+ 31 - 50
src/me/hammerle/snuviscript/code/Script.java

@@ -5,7 +5,6 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Stack;
 import java.util.function.Consumer;
-import me.hammerle.snuviscript.exceptions.CodeTooLongException;
 import me.hammerle.snuviscript.variable.LocalVariable;
 import me.hammerle.snuviscript.variable.Variable;
 
@@ -33,20 +32,21 @@ public final class Script
     protected long cpuTime;
     
     protected int catchLine;
-    protected String currentFunction;
+    protected String currentCommand;
     protected boolean ifState;
     
-    protected final HashMap<String, Integer> labels;
+    private final HashMap<String, Integer> labels;
     protected final Stack<Integer> returnStack;
     protected HashMap<String, Variable> vars;
-    protected final Stack<HashMap<String, Variable>> localVars;
     protected final HashSet<String> events;
     
-    protected Object returnValue;
-    protected final boolean subScript;
-    protected final String[] subScriptInput;
-    protected final HashMap<String, Script> subScripts;
+    // local function stuff
+    protected final Stack<HashMap<String, Variable>> localVars;
+    protected final HashMap<String, Integer> functions;
+    protected final HashMap<String, HashMap<String, Integer>> localLabels;
+    protected String currentFunction = null;
     
+    protected Object returnValue;
     protected boolean printStackTrace;
     
     private final Consumer<Script> onStart;
@@ -58,13 +58,9 @@ public final class Script
         this.parser = parser;
         this.logger = parser.getLogger();
         this.scheduler = parser.getScheduler();
-        this.subScriptInput = null;
-        this.subScripts = new HashMap<>();
         this.labels = new HashMap<>();
-        this.returnStack = new Stack<>();
-        this.localVars = new Stack<>();
+        this.returnStack = new Stack<>();       
         this.events = new HashSet<>();
-        this.subScript = false;
         this.currentLine = 0;
         this.isWaiting = false;
         this.isHolded = false;
@@ -72,7 +68,7 @@ public final class Script
         this.receiveEventBroadcast = receiveEventBroadcast;
         this.cpuTime = 0;
         this.catchLine = -1;
-        this.currentFunction = null;
+        this.currentCommand = null;
         this.ifState = true;
         this.printStackTrace = false;
         this.simpleName = simpleName;
@@ -81,35 +77,11 @@ public final class Script
         this.onStart = onStart;
         this.onTerm = onTerm;
         
-        this.code = Compiler.compile(this, code, labels, subScript, 0);
-    }
-    
-    public Script(List<String> code, String[] subScriptInput, Script sc, int lineOffset)
-    {
-        this.parser = sc.parser;
-        this.logger = sc.logger;
-        this.scheduler = sc.scheduler;
-        this.subScriptInput = subScriptInput;
-        this.subScripts = sc.subScripts;
-        this.labels = new HashMap<>();
-        this.returnStack = new Stack<>();
-        this.localVars = sc.localVars;
-        this.events = sc.events;
-        this.subScript = true;
-        this.currentLine = 0;
-        this.isWaiting = sc.isWaiting;
-        this.isHolded = sc.isHolded;
-        this.isValid = sc.isValid;
-        this.receiveEventBroadcast = sc.receiveEventBroadcast;
-        this.catchLine = -1;
-        this.printStackTrace = false;
-        this.name = sc.name;
-        this.simpleName = sc.simpleName;
-        this.id = sc.id;
-        this.onStart = sc.onStart;
-        this.onTerm = sc.onTerm;
+        this.localVars = new Stack<>();
+        this.functions = new HashMap<>();
+        this.localLabels = new HashMap<>();
         
-        this.code = Compiler.compile(this, code, labels, subScript, lineOffset); 
+        this.code = Compiler.compile(this, code, labels, functions, localLabels);
     }
     
     public HashMap<String, Variable> getLocalVars()
@@ -139,6 +111,7 @@ public final class Script
             {
                 //System.out.println("EXECUTE: " + code[currentLine]);
                 code[currentLine].execute(this);
+                //System.out.println("EXECUTE: " + currentLine);
                 currentLine++;
             }
             catch(Exception ex)
@@ -154,7 +127,7 @@ public final class Script
                     setVar("error", ex.getClass().getSimpleName());
                     continue;
                 }
-                logger.print(ex.getLocalizedMessage(), ex, currentFunction, name, this, code[currentLine].getRealLine() + 1);
+                logger.print(ex.getLocalizedMessage(), ex, currentCommand, name, this, code[currentLine].getRealLine() + 1);
                 //ex.printStackTrace();
                 return returnValue;
             }
@@ -162,10 +135,6 @@ public final class Script
             cpuTime += time;
             if(cpuTime > 15_000_000)
             {
-                if(subScript)
-                {
-                    throw new CodeTooLongException();
-                }
                 isWaiting = true;
                 isHolded = true;
                 scheduler.scheduleTask(() -> 
@@ -179,7 +148,7 @@ public final class Script
                 return Void.TYPE;
             }
         }
-        if(!subScript && currentLine >= length && !isWaiting)
+        if(currentLine >= length && !isWaiting && localVars.empty())
         {
             parser.termSafe(this);
         }
@@ -228,7 +197,7 @@ public final class Script
     public Variable getVar(String name)
     {
         HashMap<String, Variable> map;
-        if(subScript)
+        if(!localVars.isEmpty())
         {
             map = localVars.peek();
             Variable var = map.get(name);
@@ -255,7 +224,7 @@ public final class Script
     public void setVar(String name, Object value)
     {
         HashMap<String, Variable> map;
-        if(subScript)
+        if(!localVars.isEmpty())
         {
             map = localVars.peek();
             Variable var = map.get(name);
@@ -278,6 +247,18 @@ public final class Script
             var.set(this, value);
         }
     }
+    
+    protected Integer getLabel(String name)
+    {
+        if(localVars.isEmpty())
+        {
+            return labels.get(name);
+        }
+        else
+        {
+            return localLabels.get(currentFunction).get(name);
+        }
+    }
 
     // -------------------------------------------------------------------------
     // event handling

+ 1 - 0
src/me/hammerle/snuviscript/code/SnuviUtils.java

@@ -151,6 +151,7 @@ public class SnuviUtils
                     case '_':
                     case '.':
                     case '#':
+                    case '$':
                     case '@':
                         break;
                     case ')':

+ 9 - 1
src/me/hammerle/snuviscript/token/Parser.java

@@ -126,7 +126,7 @@ public class Parser
     {
         while(current < tokens.length && tokens[current].getToken() != END_OF_FILE)
         {
-            if(match(LABEL))
+            while(match(LABEL))
             {
                 if(labels.put(previous().getData().toString(), inst.size()) != null)
                 {
@@ -134,6 +134,10 @@ public class Parser
                 }
                 match(SEMICOLON);
             }
+            if(match(END_OF_FILE))
+            {
+                break;
+            }
             
             int line = tokens[current].getLine();
             inst.add(new Instruction(line, (byte) layer, parseExpression()));
@@ -463,6 +467,10 @@ public class Parser
         {
             return new ConstantString((String) previous().getData());
         }
+        else if(match(LABEL)) 
+        {
+            return new ConstantString(((String) previous().getData()).substring(1));
+        }
         else if(match(VAR)) 
         {
             String name = (String) previous().getData();

+ 0 - 1
src/me/hammerle/snuviscript/variable/LocalVariable.java

@@ -1,7 +1,6 @@
 package me.hammerle.snuviscript.variable;
 
 import java.util.HashMap;
-import me.hammerle.snuviscript.code.InputProvider;
 import me.hammerle.snuviscript.code.Script;
 
 public class LocalVariable extends Variable

+ 12 - 7
test.sbasic

@@ -1,9 +1,14 @@
-i = 5;
-i = 2;
-i = i+i++;
+n = 5;
+print(n);
+print(wusi(n));
 
-function wusi(a, b)
+function wusi(n)
 {
-    print("HALLO");
-    return;
-}
+    if(n < 1)
+    {
+        return 1;
+    }
+    return n * wusi(n - 1);
+}
+
+print(n);