|
@@ -1,831 +1,645 @@
|
|
|
package me.hammerle.snuviscript.code;
|
|
|
|
|
|
+import java.util.ArrayList;
|
|
|
import java.util.HashMap;
|
|
|
-import java.util.LinkedList;
|
|
|
-import java.util.List;
|
|
|
import java.util.Stack;
|
|
|
-import me.hammerle.snuviscript.constants.ConstantDouble;
|
|
|
-import me.hammerle.snuviscript.constants.ConstantNull;
|
|
|
-import me.hammerle.snuviscript.constants.ConstantString;
|
|
|
-import me.hammerle.snuviscript.array.DynamicArray;
|
|
|
-import me.hammerle.snuviscript.constants.ConstantBoolean;
|
|
|
-import me.hammerle.snuviscript.variable.ArrayVariable;
|
|
|
-import me.hammerle.snuviscript.variable.LocalArrayVariable;
|
|
|
-import me.hammerle.snuviscript.variable.LocalVariable;
|
|
|
-import me.hammerle.snuviscript.variable.Variable;
|
|
|
+import me.hammerle.snuviscript.inputprovider.InputProvider;
|
|
|
+import me.hammerle.snuviscript.inputprovider.ConstantBoolean;
|
|
|
+import me.hammerle.snuviscript.inputprovider.ConstantDouble;
|
|
|
+import me.hammerle.snuviscript.inputprovider.ConstantNull;
|
|
|
+import me.hammerle.snuviscript.inputprovider.ConstantString;
|
|
|
import me.hammerle.snuviscript.exceptions.PreScriptException;
|
|
|
+import me.hammerle.snuviscript.tokenizer.Token;
|
|
|
+import me.hammerle.snuviscript.tokenizer.TokenType;
|
|
|
+import static me.hammerle.snuviscript.tokenizer.TokenType.*;
|
|
|
+import me.hammerle.snuviscript.inputprovider.LocalVariable;
|
|
|
+import me.hammerle.snuviscript.inputprovider.Variable;
|
|
|
+import me.hammerle.snuviscript.instructions.Array;
|
|
|
+import me.hammerle.snuviscript.instructions.Break;
|
|
|
+import me.hammerle.snuviscript.instructions.Catch;
|
|
|
+import me.hammerle.snuviscript.instructions.Constant;
|
|
|
+import me.hammerle.snuviscript.instructions.Continue;
|
|
|
+import me.hammerle.snuviscript.instructions.Else;
|
|
|
+import me.hammerle.snuviscript.instructions.ElseIf;
|
|
|
+import me.hammerle.snuviscript.instructions.EndIf;
|
|
|
+import me.hammerle.snuviscript.instructions.For;
|
|
|
+import me.hammerle.snuviscript.instructions.Function;
|
|
|
+import me.hammerle.snuviscript.instructions.Goto;
|
|
|
+import me.hammerle.snuviscript.instructions.If;
|
|
|
+import me.hammerle.snuviscript.instructions.IfGoto;
|
|
|
+import me.hammerle.snuviscript.instructions.Instruction;
|
|
|
+import me.hammerle.snuviscript.instructions.Return;
|
|
|
+import me.hammerle.snuviscript.instructions.Try;
|
|
|
+import me.hammerle.snuviscript.instructions.UserFunction;
|
|
|
+import me.hammerle.snuviscript.instructions.While;
|
|
|
|
|
|
-public class Compiler
|
|
|
+public class Compiler
|
|
|
{
|
|
|
- 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(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 = new HashMap<>();
|
|
|
- private final HashMap<String, Integer> labels;
|
|
|
-
|
|
|
- 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 int index = 0;
|
|
|
+ private Token[] tokens = null;
|
|
|
+ private final ArrayList<Instruction> instr = new ArrayList<>();
|
|
|
|
|
|
- private final LinkedList<Instruction> code = new LinkedList<>();;
|
|
|
- private int line = 0;
|
|
|
- private int layer = 0;
|
|
|
+ private HashMap<String, Integer> labels = null;
|
|
|
+ private HashMap<String, HashMap<String, Integer>> localLabels = null;
|
|
|
+ private HashMap<String, Variable> vars = null;
|
|
|
+ private final HashMap<String, LocalVariable> localVars = new HashMap<>();
|
|
|
+ private HashMap<String, Integer> functions = null;
|
|
|
|
|
|
- private JumpData tryState = null;
|
|
|
+ private final Stack<Break> breakStack = new Stack<>();
|
|
|
+ private final Stack<Continue> continueStack = new Stack<>();
|
|
|
|
|
|
- private class JumpWrapper
|
|
|
+ private String inFunction = null;
|
|
|
+ private boolean lineExpression = false;
|
|
|
+
|
|
|
+ private void addConstant(int line, InputProvider ip)
|
|
|
{
|
|
|
- private final JumpData data;
|
|
|
- private final String function;
|
|
|
-
|
|
|
- public JumpWrapper(JumpData data, String function)
|
|
|
- {
|
|
|
- this.data = data;
|
|
|
- this.function = function;
|
|
|
- }
|
|
|
+ instr.add(new Constant(line, ip));
|
|
|
}
|
|
|
|
|
|
- private final Stack<JumpWrapper> jumps = new Stack<>();
|
|
|
- private final Stack<JumpWrapper> loopJumps = new Stack<>();
|
|
|
- private final LinkedList<JumpData> breakContinueJumps = new LinkedList<>();
|
|
|
-
|
|
|
- private final HashMap<String, String> strings = new HashMap<>();
|
|
|
- private int stringCounter = 0;
|
|
|
-
|
|
|
- private Compiler(List<String> sCode, HashMap<String, Integer> labels,
|
|
|
- HashMap<String, Integer> functions, HashMap<String, HashMap<String, Integer>> localLabels)
|
|
|
+ private void addFunction(int line, int args, String name)
|
|
|
{
|
|
|
- this.sCode = sCode;
|
|
|
- this.labels = labels;
|
|
|
- this.functions = functions;
|
|
|
- this.localLabels = localLabels;
|
|
|
+ instr.add(new Function(line, args, FunctionRegistry.getFunction(name)));
|
|
|
}
|
|
|
|
|
|
- private void addCodeInstruction(String function, InputProvider[] input)
|
|
|
+ private void addGoto(int line, int jump)
|
|
|
{
|
|
|
- code.add(new Instruction(line + 1, (byte) layer, new Function(FunctionLoader.getFunction(function), input)));
|
|
|
+ Goto g = new Goto(line, 0);
|
|
|
+ g.setJump(jump);
|
|
|
+ instr.add(g);
|
|
|
}
|
|
|
|
|
|
- private void addLabel(String name, int line)
|
|
|
+ private boolean match(TokenType... types)
|
|
|
{
|
|
|
- if(currentFunction != null)
|
|
|
+ for(TokenType type : types)
|
|
|
{
|
|
|
- HashMap<String, Integer> map = localLabels.get(currentFunction);
|
|
|
- if(map.put(name, line) != null)
|
|
|
+ if(check(type))
|
|
|
{
|
|
|
- throw new PreScriptException("label duplicate", line);
|
|
|
+ advance();
|
|
|
+ return true;
|
|
|
}
|
|
|
}
|
|
|
- else if(labels.put(name, line) != null)
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ private boolean check(TokenType type)
|
|
|
+ {
|
|
|
+ if(isAtEnd())
|
|
|
+ {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return peek().getType() == type;
|
|
|
+ }
|
|
|
+
|
|
|
+ private Token advance()
|
|
|
+ {
|
|
|
+ if(!isAtEnd())
|
|
|
{
|
|
|
- throw new PreScriptException("label duplicate", line);
|
|
|
+ index++;
|
|
|
}
|
|
|
+ return previous();
|
|
|
+ }
|
|
|
+
|
|
|
+ private boolean isAtEnd()
|
|
|
+ {
|
|
|
+ return peek().getType() == EOF;
|
|
|
+ }
|
|
|
+
|
|
|
+ private Token peek()
|
|
|
+ {
|
|
|
+ return tokens[index];
|
|
|
+ }
|
|
|
+
|
|
|
+ private Token previous()
|
|
|
+ {
|
|
|
+ return tokens[index - 1];
|
|
|
}
|
|
|
|
|
|
- private Instruction[] compile()
|
|
|
+ private Token consume(TokenType type)
|
|
|
{
|
|
|
- int size = sCode.size();
|
|
|
-
|
|
|
-
|
|
|
- StringBuilder sb = new StringBuilder();
|
|
|
- String replacement;
|
|
|
- String check;
|
|
|
- int pos;
|
|
|
- int old = 0;
|
|
|
- boolean text = false;
|
|
|
- boolean comment = false;
|
|
|
- int labelIndex;
|
|
|
-
|
|
|
- for(line = 0; line < size; line++)
|
|
|
+ if(check(type))
|
|
|
{
|
|
|
- pos = sb.length();
|
|
|
- sb.append(sCode.get(line));
|
|
|
-
|
|
|
- while(pos < sb.length())
|
|
|
- {
|
|
|
- if(comment)
|
|
|
- {
|
|
|
- if(pos + 1 < sb.length() && sb.charAt(pos) == '*' && sb.charAt(pos + 1) == '/')
|
|
|
- {
|
|
|
- comment = false;
|
|
|
- sb.delete(old, pos + 2);
|
|
|
- pos = old - 1;
|
|
|
- }
|
|
|
- pos++;
|
|
|
- continue;
|
|
|
- }
|
|
|
- else if(text)
|
|
|
- {
|
|
|
- if(sb.charAt(pos) == '"')
|
|
|
- {
|
|
|
- replacement = "#" + stringCounter++;
|
|
|
- strings.put(replacement, sb.substring(old, pos + 1));
|
|
|
- text = false;
|
|
|
- sb.replace(old, pos + 1, replacement);
|
|
|
- pos = old - 1 + replacement.length();
|
|
|
- }
|
|
|
- pos++;
|
|
|
- continue;
|
|
|
- }
|
|
|
- switch(sb.charAt(pos))
|
|
|
- {
|
|
|
- case '/':
|
|
|
- if(pos + 1 < sb.length())
|
|
|
- {
|
|
|
- switch(sb.charAt(pos + 1))
|
|
|
- {
|
|
|
- case '/':
|
|
|
- sb.delete(pos, sb.length());
|
|
|
- break;
|
|
|
- case '*':
|
|
|
- comment = true;
|
|
|
- old = pos;
|
|
|
- pos++;
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- break;
|
|
|
- case '}':
|
|
|
- sb.delete(0, pos + 1);
|
|
|
- pos = -1;
|
|
|
- layer--;
|
|
|
- if(jumps.isEmpty())
|
|
|
- {
|
|
|
- throw new PreScriptException("} without a corresponding function and / or {", line);
|
|
|
- }
|
|
|
- JumpWrapper data = jumps.pop();
|
|
|
- switch(data.function)
|
|
|
- {
|
|
|
- case "function":
|
|
|
- {
|
|
|
- data.data.setRelativeJump(code.size() + 1);
|
|
|
- currentFunction = null;
|
|
|
- layer++;
|
|
|
- addCodeInstruction("return", new InputProvider[] {});
|
|
|
- layer--;
|
|
|
- break;
|
|
|
- }
|
|
|
- case "try":
|
|
|
- {
|
|
|
- tryState = data.data;
|
|
|
- break;
|
|
|
- }
|
|
|
- case "catch":
|
|
|
- {
|
|
|
- data.data.setRelativeJump(code.size());
|
|
|
- break;
|
|
|
- }
|
|
|
- case "else":
|
|
|
- case "elseif":
|
|
|
- case "if":
|
|
|
- {
|
|
|
- data.data.setRelativeJump(code.size() + 1);
|
|
|
- addCodeInstruction("endif", new InputProvider[] {});
|
|
|
- break;
|
|
|
- }
|
|
|
- case "for":
|
|
|
- {
|
|
|
- loopJumps.pop();
|
|
|
- createBreakContinue(code.size());
|
|
|
- JumpData jump = data.data;
|
|
|
- jump.setRelativeJump(code.size());
|
|
|
- addCodeInstruction("next", new InputProvider[] {new JumpData(-jump.getInt(null) - 1)});
|
|
|
- break;
|
|
|
- }
|
|
|
- case "while":
|
|
|
- {
|
|
|
- loopJumps.pop();
|
|
|
- createBreakContinue(code.size());
|
|
|
+ return advance();
|
|
|
+ }
|
|
|
+ throw new PreScriptException(String.format("expected %s got %s", type, peek().getType()), peek().getLine());
|
|
|
+ }
|
|
|
|
|
|
- JumpData jump = data.data;
|
|
|
- jump.setRelativeJump(code.size() + 1);
|
|
|
- addCodeInstruction("wend", new InputProvider[] {new JumpData(-jump.getInt(null) - 1)});
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- break;
|
|
|
- case '{':
|
|
|
- int currentJumps = jumps.size();
|
|
|
- check = sb.toString();
|
|
|
- if(check.startsWith("function "))
|
|
|
- {
|
|
|
- if(currentFunction != null)
|
|
|
- {
|
|
|
- throw new PreScriptException("function not allowed in another function", line);
|
|
|
- }
|
|
|
- int index = check.indexOf("(");
|
|
|
- if(index == -1)
|
|
|
- {
|
|
|
- throw new PreScriptException("missing function syntax", line);
|
|
|
- }
|
|
|
- currentFunction = check.substring(9, index).toLowerCase();
|
|
|
- functions.put(currentFunction, code.size());
|
|
|
- localLabels.put(currentFunction, new HashMap<>());
|
|
|
- int endIndex = check.indexOf(")", index);
|
|
|
- if(index == -1)
|
|
|
- {
|
|
|
- throw new PreScriptException("missing function syntax", line);
|
|
|
- }
|
|
|
- String[] inputs;
|
|
|
- if(index + 1 == endIndex)
|
|
|
- {
|
|
|
- inputs = new String[0];
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- inputs = check.substring(index + 1, endIndex).split("[ ]*,[ ]*");
|
|
|
- }
|
|
|
- InputProvider[] in = new InputProvider[inputs.length + 1];
|
|
|
- for(int i = 1; i < in.length; i++)
|
|
|
- {
|
|
|
- in[i] = new ConstantString(inputs[i - 1]);
|
|
|
- }
|
|
|
- JumpData jump = new JumpData(code.size());
|
|
|
- in[0] = jump;
|
|
|
- jumps.add(new JumpWrapper(jump, "function"));
|
|
|
- addCodeInstruction("function", in);
|
|
|
-
|
|
|
- pos = endIndex + 1;
|
|
|
- boolean b = true;
|
|
|
- while(b)
|
|
|
- {
|
|
|
- switch(sb.charAt(pos))
|
|
|
- {
|
|
|
- case '{':
|
|
|
- b = false;
|
|
|
- break;
|
|
|
- case '\n':
|
|
|
- case ' ':
|
|
|
- break;
|
|
|
- default:
|
|
|
- throw new PreScriptException("invalid character between function and {", line);
|
|
|
- }
|
|
|
- pos++;
|
|
|
- }
|
|
|
-
|
|
|
- layer++;
|
|
|
- sb.delete(0, pos);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- check = sb.substring(0, pos);
|
|
|
- compileLine(check);
|
|
|
- sb.delete(0, pos + 1);
|
|
|
- layer++;
|
|
|
- if(currentJumps == jumps.size())
|
|
|
- {
|
|
|
- throw new PreScriptException("{ without a corresponding function", line);
|
|
|
- }
|
|
|
- }
|
|
|
- pos = -1;
|
|
|
- break;
|
|
|
- case ';':
|
|
|
- compileLine(sb.substring(0, pos).trim());
|
|
|
- sb.delete(0, pos + 1);
|
|
|
- pos = -1;
|
|
|
- break;
|
|
|
- case '"':
|
|
|
- text = true;
|
|
|
- old = pos;
|
|
|
- break;
|
|
|
- }
|
|
|
- pos++;
|
|
|
- }
|
|
|
- if(!text && !comment)
|
|
|
- {
|
|
|
- labelIndex = sb.indexOf("@");
|
|
|
- if(labelIndex != -1)
|
|
|
- {
|
|
|
- String label = sb.toString().trim();
|
|
|
- if(label.charAt(0) != '@')
|
|
|
- {
|
|
|
- throw new PreScriptException("you seriously fucked up the syntax here", line);
|
|
|
- }
|
|
|
- addLabel(label.substring(1), code.size() - 1);
|
|
|
- sb = new StringBuilder();
|
|
|
- }
|
|
|
- }
|
|
|
+ private void noReturnForLastFunction()
|
|
|
+ {
|
|
|
+ instr.get(instr.size() - 1).setNoReturn();
|
|
|
+ }
|
|
|
+
|
|
|
+ public Instruction[] compile(Token[] tokens, HashMap<String, Integer> labels,
|
|
|
+ HashMap<String, Variable> vars, HashMap<String, Integer> functions,
|
|
|
+ HashMap<String, HashMap<String, Integer>> localLabels)
|
|
|
+ {
|
|
|
+ this.tokens = tokens;
|
|
|
+ index = 0;
|
|
|
+ instr.clear();
|
|
|
+ this.labels = labels;
|
|
|
+ this.localLabels = localLabels;
|
|
|
+ this.vars = vars;
|
|
|
+ this.functions = functions;
|
|
|
+ localVars.clear();
|
|
|
+ inFunction = null;
|
|
|
+
|
|
|
+ while(!isAtEnd())
|
|
|
+ {
|
|
|
+ line();
|
|
|
}
|
|
|
|
|
|
-
|
|
|
-
|
|
|
- Instruction[] input = code.toArray(new Instruction[code.size()]);
|
|
|
+ this.tokens = null;
|
|
|
+ this.labels = null;
|
|
|
+ this.vars = null;
|
|
|
+ this.functions = null;
|
|
|
+ localVars.clear();
|
|
|
|
|
|
-
|
|
|
- {
|
|
|
- System.out.println(in);
|
|
|
+ Instruction[] code = instr.toArray(new Instruction[instr.size()]);
|
|
|
+ instr.clear();
|
|
|
+ return code;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void line()
|
|
|
+ {
|
|
|
+ int oldIndex = index;
|
|
|
+ Token t = advance();
|
|
|
+ switch(t.getType())
|
|
|
+ {
|
|
|
+ case LABEL: handleLabel(); break;
|
|
|
+ case IF: handleIf(); break;
|
|
|
+ case SEMICOLON: break;
|
|
|
+ case FOR: handleFor(); break;
|
|
|
+ case BREAK:
|
|
|
+ Break b = new Break(previous().getLine());
|
|
|
+ breakStack.add(b);
|
|
|
+ instr.add(b);
|
|
|
+ consume(SEMICOLON);
|
|
|
+ break;
|
|
|
+ case CONTINUE:
|
|
|
+ Continue c = new Continue(previous().getLine());
|
|
|
+ continueStack.add(c);
|
|
|
+ instr.add(c);
|
|
|
+ consume(SEMICOLON);
|
|
|
+ break;
|
|
|
+ case FUNCTION: handleUserFunction(); break;
|
|
|
+ case RETURN: handleReturn(); break;
|
|
|
+ case WHILE: handleWhile(); break;
|
|
|
+ case TRY: handleTry(); break;
|
|
|
+ default:
|
|
|
+ index = oldIndex;
|
|
|
+ lineExpression = false;
|
|
|
+ expression();
|
|
|
+ if(!lineExpression)
|
|
|
+ {
|
|
|
+ throw new PreScriptException("missing statement", t.getLine());
|
|
|
+ }
|
|
|
+ consume(SEMICOLON);
|
|
|
}
|
|
|
- System.out.println("__________________________________");*/
|
|
|
-
|
|
|
- {
|
|
|
- System.out.println("LABEL " + e.getKey() + " " + e.getValue());
|
|
|
- });*/
|
|
|
-
|
|
|
- return input;
|
|
|
+ noReturnForLastFunction();
|
|
|
}
|
|
|
|
|
|
- private void compileLine(String currentCode)
|
|
|
+ private void handleLabel()
|
|
|
{
|
|
|
-
|
|
|
- String[] parts = SnuviUtils.split(strings, currentCode, line);
|
|
|
-
|
|
|
- if(tryState != null)
|
|
|
+ String name = previous().getData().toString();
|
|
|
+ name = name.substring(1);
|
|
|
+ if(inFunction != null)
|
|
|
{
|
|
|
- switch(parts.length)
|
|
|
+ HashMap<String, Integer> llabel = localLabels.get(inFunction);
|
|
|
+ if(llabel == null)
|
|
|
{
|
|
|
- case 0: return;
|
|
|
- case 1:
|
|
|
- if(!parts[0].equals("catch"))
|
|
|
- {
|
|
|
- throw new PreScriptException("no catch after try", line);
|
|
|
- }
|
|
|
- if(tryState == null)
|
|
|
- {
|
|
|
- throw new PreScriptException("catch without try", line);
|
|
|
- }
|
|
|
- tryState.setRelativeJump(code.size());
|
|
|
- JumpData jump = new JumpData(code.size());
|
|
|
- addCodeInstruction("catch", new InputProvider[] {jump});
|
|
|
- jumps.push(new JumpWrapper(jump, "catch"));
|
|
|
- tryState = null;
|
|
|
- return;
|
|
|
- default:
|
|
|
- throw new PreScriptException("invalid catch after try", line);
|
|
|
+ llabel = new HashMap<>();
|
|
|
+ localLabels.put(inFunction, llabel);
|
|
|
}
|
|
|
+ llabel.put(name, instr.size() - 1);
|
|
|
}
|
|
|
-
|
|
|
- if(parts.length == 0)
|
|
|
+ else
|
|
|
{
|
|
|
- return;
|
|
|
+ labels.put(name, instr.size() - 1);
|
|
|
}
|
|
|
- else if(parts[0].equals("return"))
|
|
|
- {
|
|
|
- addCodeInstruction("return", compileFunction(parts, true));
|
|
|
- return;
|
|
|
- }
|
|
|
- else if(parts[0].startsWith("@"))
|
|
|
- {
|
|
|
- if(parts.length > 1)
|
|
|
+ }
|
|
|
+
|
|
|
+ private void handleIf()
|
|
|
+ {
|
|
|
+ Token t = previous();
|
|
|
+ consume(OPEN_BRACKET);
|
|
|
+ expression();
|
|
|
+ If i = new If(t.getLine());
|
|
|
+ instr.add(i);
|
|
|
+ consume(CLOSE_BRACKET);
|
|
|
+ consume(OPEN_CURVED_BRACKET);
|
|
|
+ while(!match(CLOSE_CURVED_BRACKET))
|
|
|
+ {
|
|
|
+ line();
|
|
|
+ }
|
|
|
+ i.setJump(instr.size() - 1);
|
|
|
+ handleElseIf();
|
|
|
+ instr.add(new EndIf(instr.get(instr.size() - 1).getLine()));
|
|
|
+ }
|
|
|
+
|
|
|
+ private void handleElseIf()
|
|
|
+ {
|
|
|
+ while(match(ELSEIF))
|
|
|
+ {
|
|
|
+ Token t = previous();
|
|
|
+ consume(OPEN_BRACKET);
|
|
|
+ expression();
|
|
|
+ ElseIf e = new ElseIf(t.getLine());
|
|
|
+ instr.add(e);
|
|
|
+ consume(CLOSE_BRACKET);
|
|
|
+ consume(OPEN_CURVED_BRACKET);
|
|
|
+ while(!match(CLOSE_CURVED_BRACKET))
|
|
|
{
|
|
|
- throw new PreScriptException("arguments after label", line);
|
|
|
+ line();
|
|
|
}
|
|
|
- addLabel(parts[0].substring(1), code.size() - 1);
|
|
|
- return;
|
|
|
+ e.setJump(instr.size() - 1);
|
|
|
}
|
|
|
-
|
|
|
- String input;
|
|
|
- if(parts.length == 1)
|
|
|
+ handleElse();
|
|
|
+ }
|
|
|
+
|
|
|
+ private void handleElse()
|
|
|
+ {
|
|
|
+ if(match(ELSE))
|
|
|
{
|
|
|
- int bPos = parts[0].indexOf('(');
|
|
|
- if(bPos != -1)
|
|
|
+ Else e = new Else(previous().getLine());
|
|
|
+ instr.add(e);
|
|
|
+ consume(OPEN_CURVED_BRACKET);
|
|
|
+ while(!match(CLOSE_CURVED_BRACKET))
|
|
|
{
|
|
|
- input = parts[0].substring(0, bPos);
|
|
|
- parts = SnuviUtils.split(strings, parts[0].substring(bPos + 1, parts[0].length() - 1), line);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- switch(parts[0])
|
|
|
- {
|
|
|
- case "try":
|
|
|
- {
|
|
|
- JumpData jump = new JumpData(code.size());
|
|
|
- addCodeInstruction("try", new InputProvider[] {jump});
|
|
|
- jumps.push(new JumpWrapper(jump, "try"));
|
|
|
- return;
|
|
|
- }
|
|
|
- case "else":
|
|
|
- {
|
|
|
- JumpData jump = new JumpData(code.size());
|
|
|
- addCodeInstruction("else", new InputProvider[] {jump});
|
|
|
- jumps.push(new JumpWrapper(jump, "else"));
|
|
|
- return;
|
|
|
- }
|
|
|
- case "while":
|
|
|
- throw new PreScriptException("missing syntax at while", line);
|
|
|
- case "if":
|
|
|
- throw new PreScriptException("missing syntax at if", line);
|
|
|
- case "elseif":
|
|
|
- throw new PreScriptException("missing syntax at elseif", line);
|
|
|
- case "for":
|
|
|
- throw new PreScriptException("missing syntax at for", line);
|
|
|
- case "break":
|
|
|
- {
|
|
|
- if(loopJumps.isEmpty())
|
|
|
- {
|
|
|
- throw new PreScriptException("break without a loop", line);
|
|
|
- }
|
|
|
- JumpData jump = new JumpData(code.size() - 1);
|
|
|
- breakContinueJumps.add(jump);
|
|
|
- addCodeInstruction("break", new InputProvider[] {jump});
|
|
|
- return;
|
|
|
- }
|
|
|
- case "continue":
|
|
|
- {
|
|
|
- if(loopJumps.isEmpty())
|
|
|
- {
|
|
|
- throw new PreScriptException("continue without a loop", line);
|
|
|
- }
|
|
|
- JumpData jump = new JumpData(code.size());
|
|
|
- breakContinueJumps.add(jump);
|
|
|
- addCodeInstruction("continue", new InputProvider[] {jump});
|
|
|
- return;
|
|
|
- }
|
|
|
- }
|
|
|
- return;
|
|
|
- }
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- switch(parts[0])
|
|
|
- {
|
|
|
- case "++":
|
|
|
- addCodeInstruction("p++", compileFunction(new String[] {parts[1]}, false));
|
|
|
- return;
|
|
|
- case "--":
|
|
|
- addCodeInstruction("p--", compileFunction(new String[] {parts[1]}, false));
|
|
|
- return;
|
|
|
- }
|
|
|
- switch(parts[1])
|
|
|
- {
|
|
|
- case "++":
|
|
|
- case "--":
|
|
|
- input = parts[1];
|
|
|
- parts = new String[] {parts[0]};
|
|
|
- break;
|
|
|
- case "=":
|
|
|
- case "+=":
|
|
|
- case "-=":
|
|
|
- case "*=":
|
|
|
- case "/=":
|
|
|
- case "%=":
|
|
|
- case "<<=":
|
|
|
- case ">>=":
|
|
|
- case "&=":
|
|
|
- case "^=":
|
|
|
- case "|=":
|
|
|
- {
|
|
|
- input = parts[1];
|
|
|
- parts[1] = ",";
|
|
|
- break;
|
|
|
- }
|
|
|
- default:
|
|
|
- throw new PreScriptException("unknown operation " + parts[1], line);
|
|
|
+ line();
|
|
|
}
|
|
|
- }
|
|
|
- switch(input)
|
|
|
- {
|
|
|
- case "break":
|
|
|
- throw new PreScriptException("break does not accept arguments", line);
|
|
|
- case "continue":
|
|
|
- throw new PreScriptException("continue does not accept arguments", line);
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- switch(input)
|
|
|
- {
|
|
|
- case "elseif":
|
|
|
- createIf("elseif", parts);
|
|
|
- break;
|
|
|
- case "if":
|
|
|
- createIf("if", parts);
|
|
|
- break;
|
|
|
- case "for":
|
|
|
- createFor(parts);
|
|
|
- break;
|
|
|
- case "while":
|
|
|
- createWhile(parts);
|
|
|
- break;
|
|
|
- default:
|
|
|
- addCodeInstruction(input, compileFunction(parts, false));
|
|
|
+ e.setJump(instr.size() - 1);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private void addSyntax(LinkedList<InputProvider> list, Syntax sy)
|
|
|
+ private void handleFor()
|
|
|
{
|
|
|
- int pars = sy.getParameters();
|
|
|
- if(pars > list.size())
|
|
|
+ Token t = previous();
|
|
|
+ consume(OPEN_BRACKET);
|
|
|
+ if(!match(SEMICOLON))
|
|
|
{
|
|
|
- throw new PreScriptException("missing syntax argument", line);
|
|
|
+ expression();
|
|
|
+ consume(SEMICOLON);
|
|
|
+ noReturnForLastFunction();
|
|
|
}
|
|
|
- if(sy == Syntax.UNARY_SUB)
|
|
|
+
|
|
|
+ int forConditionStart = instr.size() - 1;
|
|
|
+ if(!match(SEMICOLON))
|
|
|
{
|
|
|
- list.add(new SignInverter(list.pollLast()));
|
|
|
- return;
|
|
|
+ expression();
|
|
|
+ consume(SEMICOLON);
|
|
|
}
|
|
|
- InputProvider[] input = new InputProvider[pars];
|
|
|
- for(int j = input.length - 1; j >= 0; j--)
|
|
|
+ Goto forGoto = new Goto(instr.get(instr.size() - 1).getLine(), 0);
|
|
|
+ instr.add(forGoto);
|
|
|
+
|
|
|
+ int forLoopFunctionStart = instr.size() - 1;
|
|
|
+ if(!match(CLOSE_BRACKET))
|
|
|
{
|
|
|
- input[j] = list.pollLast();
|
|
|
+ expression();
|
|
|
+ consume(CLOSE_BRACKET);
|
|
|
+ noReturnForLastFunction();
|
|
|
}
|
|
|
- list.add(new Function(FunctionLoader.getFunction(sy.getFunction()), input));
|
|
|
+ Goto conditionGoto = new Goto(instr.get(instr.size() - 1).getLine(), 0);
|
|
|
+ conditionGoto.setJump(forConditionStart);
|
|
|
+ instr.add(conditionGoto);
|
|
|
+
|
|
|
+ int forStart = instr.size() - 1;
|
|
|
+ forGoto.setJump(forStart);
|
|
|
+ For f = new For(t.getLine());
|
|
|
+ instr.add(f);
|
|
|
+ consume(OPEN_CURVED_BRACKET);
|
|
|
+ while(!match(CLOSE_CURVED_BRACKET))
|
|
|
+ {
|
|
|
+ line();
|
|
|
+ }
|
|
|
+ Goto loopFunctionGoto = new Goto(instr.get(instr.size() - 1).getLine(), 0);
|
|
|
+ loopFunctionGoto.setJump(forLoopFunctionStart);
|
|
|
+ instr.add(loopFunctionGoto);
|
|
|
+ int forEnd = instr.size() - 1;
|
|
|
+ f.setJump(forEnd);
|
|
|
+
|
|
|
+ setBreakContinueJumps(forLoopFunctionStart, forEnd);
|
|
|
}
|
|
|
|
|
|
- private void validateStackCounter(int stackCounter)
|
|
|
+ private void setBreakContinueJumps(int start, int end)
|
|
|
{
|
|
|
- if(stackCounter < 0)
|
|
|
+ while(!continueStack.empty())
|
|
|
+ {
|
|
|
+ continueStack.pop().setJump(start);
|
|
|
+ }
|
|
|
+ while(!breakStack.empty())
|
|
|
{
|
|
|
- throw new PreScriptException("missing syntax argument", line);
|
|
|
+ breakStack.pop().setJump(end);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private InputProvider[] compileFunction(String[] parts, boolean first)
|
|
|
+ private void handleUserFunction()
|
|
|
{
|
|
|
- LinkedList<InputProvider> list = new LinkedList<>();
|
|
|
- int stackCounter = 0;
|
|
|
-
|
|
|
- Stack<Syntax> syntax = new Stack<>();
|
|
|
- int bottom = first ? 1 : 0;
|
|
|
- Syntax sy;
|
|
|
- for(int i = bottom; i < parts.length; i++)
|
|
|
+ consume(LITERAL);
|
|
|
+ Token t = previous();
|
|
|
+ consume(OPEN_BRACKET);
|
|
|
+ ArrayList<String> list = new ArrayList<>();
|
|
|
+ if(!match(CLOSE_BRACKET))
|
|
|
{
|
|
|
- if(parts[i].equals(","))
|
|
|
- {
|
|
|
-
|
|
|
- while(!syntax.isEmpty())
|
|
|
- {
|
|
|
- addSyntax(list, syntax.pop());
|
|
|
- }
|
|
|
- stackCounter = 0;
|
|
|
- continue;
|
|
|
- }
|
|
|
- sy = Syntax.getSyntax(parts[i]);
|
|
|
- if(sy != Syntax.UNKNOWN)
|
|
|
+ while(true)
|
|
|
{
|
|
|
- if(stackCounter <= 0)
|
|
|
- {
|
|
|
- switch(sy)
|
|
|
- {
|
|
|
- case INVERT:
|
|
|
- break;
|
|
|
- case BIT_INVERT:
|
|
|
- break;
|
|
|
- case SUB:
|
|
|
- sy = Syntax.UNARY_SUB;
|
|
|
- break;
|
|
|
- case POST_INC:
|
|
|
- sy = Syntax.INC;
|
|
|
- break;
|
|
|
- case POST_DEC:
|
|
|
- sy = Syntax.DEC;
|
|
|
- break;
|
|
|
- default:
|
|
|
- throw new PreScriptException("missing syntax argument", line);
|
|
|
- }
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- switch(sy)
|
|
|
- {
|
|
|
- case INVERT:
|
|
|
- case BIT_INVERT:
|
|
|
- throw new PreScriptException("missing syntax argument", line);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- int weight = sy.getWeight();
|
|
|
- while(!syntax.isEmpty() && syntax.peek().getWeight() <= weight)
|
|
|
+ consume(LITERAL);
|
|
|
+ list.add(previous().getData().toString());
|
|
|
+ if(match(CLOSE_BRACKET))
|
|
|
{
|
|
|
- addSyntax(list, syntax.pop());
|
|
|
+ break;
|
|
|
}
|
|
|
- validateStackCounter(stackCounter);
|
|
|
- syntax.add(sy);
|
|
|
- stackCounter -= sy.getParameters() - 1;
|
|
|
- continue;
|
|
|
+ consume(COMMA);
|
|
|
}
|
|
|
- stackCounter++;
|
|
|
- list.add(convertString(parts[i]));
|
|
|
- }
|
|
|
-
|
|
|
- while(!syntax.isEmpty())
|
|
|
- {
|
|
|
- addSyntax(list, syntax.pop());
|
|
|
- }
|
|
|
- validateStackCounter(stackCounter);
|
|
|
- return list.toArray(new InputProvider[list.size()]);
|
|
|
+ }
|
|
|
+ String name = t.getData().toString().toLowerCase();
|
|
|
+ UserFunction uf = new UserFunction(t.getLine(), name, list.toArray(new String[list.size()]));
|
|
|
+ functions.put(name, instr.size());
|
|
|
+ instr.add(uf);
|
|
|
+ consume(OPEN_CURVED_BRACKET);
|
|
|
+ inFunction = name;
|
|
|
+ while(!match(CLOSE_CURVED_BRACKET))
|
|
|
+ {
|
|
|
+ line();
|
|
|
+ }
|
|
|
+ inFunction = null;
|
|
|
+ instr.add(new Return(instr.get(instr.size() - 1).getLine(), 0));
|
|
|
+ uf.setJump(instr.size() - 1);
|
|
|
}
|
|
|
|
|
|
- private InputProvider convertString(String input)
|
|
|
+ private void handleReturn()
|
|
|
{
|
|
|
- if(input.startsWith("@"))
|
|
|
+ Token t = previous();
|
|
|
+ int args = 0;
|
|
|
+ if(!match(SEMICOLON))
|
|
|
{
|
|
|
- return new ConstantString(input.substring(1));
|
|
|
+ args = 1;
|
|
|
+ expression();
|
|
|
+ consume(SEMICOLON);
|
|
|
}
|
|
|
- else if(input.startsWith("\"") && input.endsWith("\""))
|
|
|
- {
|
|
|
- return new ConstantString(input.substring(1, input.length() - 1));
|
|
|
- }
|
|
|
- else if(input.equals("true"))
|
|
|
- {
|
|
|
- return ConstantBoolean.TRUE;
|
|
|
- }
|
|
|
- else if(input.equals("false"))
|
|
|
+ instr.add(new Return(t.getLine(), args));
|
|
|
+ }
|
|
|
+
|
|
|
+ private void handleWhile()
|
|
|
+ {
|
|
|
+ int whileStart = instr.size() - 1;
|
|
|
+ Token t = previous();
|
|
|
+ consume(OPEN_BRACKET);
|
|
|
+ expression();
|
|
|
+ While w = new While(t.getLine());
|
|
|
+ instr.add(w);
|
|
|
+ consume(CLOSE_BRACKET);
|
|
|
+ consume(OPEN_CURVED_BRACKET);
|
|
|
+ while(!match(CLOSE_CURVED_BRACKET))
|
|
|
+ {
|
|
|
+ line();
|
|
|
+ }
|
|
|
+ addGoto(instr.get(instr.size() - 1).getLine(), whileStart);
|
|
|
+ int whileEnd = instr.size() - 1;
|
|
|
+ w.setJump(whileEnd);
|
|
|
+ setBreakContinueJumps(whileStart, whileEnd);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void handleTry()
|
|
|
+ {
|
|
|
+ Try t = new Try(previous().getLine());
|
|
|
+ instr.add(t);
|
|
|
+ consume(OPEN_CURVED_BRACKET);
|
|
|
+ while(!match(CLOSE_CURVED_BRACKET))
|
|
|
{
|
|
|
- return ConstantBoolean.FALSE;
|
|
|
+ line();
|
|
|
}
|
|
|
- else if(input.equals("null"))
|
|
|
+ consume(CATCH);
|
|
|
+ Catch c = new Catch(previous().getLine());
|
|
|
+ instr.add(c);
|
|
|
+ t.setJump(instr.size() - 1);
|
|
|
+ consume(OPEN_CURVED_BRACKET);
|
|
|
+ while(!match(CLOSE_CURVED_BRACKET))
|
|
|
{
|
|
|
- return ConstantNull.NULL;
|
|
|
+ line();
|
|
|
}
|
|
|
- else if(SnuviUtils.isNumber(input))
|
|
|
+ c.setJump(instr.size() - 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void expression()
|
|
|
+ {
|
|
|
+ assignment();
|
|
|
+ }
|
|
|
+
|
|
|
+ private void assignment()
|
|
|
+ {
|
|
|
+ logicalOr();
|
|
|
+ if(match(SET, ADD_SET, SUB_SET, MUL_SET, DIV_SET, MOD_SET, LEFT_SHIFT_SET,
|
|
|
+ RIGHT_SHIFT_SET, BIT_AND_SET, BIT_XOR_SET, BIT_OR_SET))
|
|
|
{
|
|
|
- return new ConstantDouble(Double.parseDouble(input));
|
|
|
+ Token t = previous();
|
|
|
+ assignment();
|
|
|
+ addFunction(t.getLine(), 2, t.getType().getName());
|
|
|
+ lineExpression = true;
|
|
|
}
|
|
|
- else if(SnuviUtils.isFunction(input))
|
|
|
+ }
|
|
|
+
|
|
|
+ private void logicalOr()
|
|
|
+ {
|
|
|
+ logicalAnd();
|
|
|
+ while(match(OR))
|
|
|
{
|
|
|
- int bPos = input.indexOf('(');
|
|
|
- String[] parts = SnuviUtils.split(strings, input.substring(bPos + 1, input.length() - 1), line);
|
|
|
- if(parts.length > 0)
|
|
|
- {
|
|
|
- return new Function(FunctionLoader.getFunction(input.substring(0, bPos)), compileFunction(parts, false));
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- return new Function(FunctionLoader.getFunction(input.substring(0, bPos)), new InputProvider[0]);
|
|
|
- }
|
|
|
+ Token t = previous();
|
|
|
+ IfGoto ifGoto = new IfGoto(t.getLine(), true);
|
|
|
+ instr.add(ifGoto);
|
|
|
+ logicalAnd();
|
|
|
+ ifGoto.setJump(instr.size());
|
|
|
+ addFunction(t.getLine(), 2, t.getType().getName());
|
|
|
}
|
|
|
- else if(SnuviUtils.isArray(input))
|
|
|
+ }
|
|
|
+
|
|
|
+ private void logicalAnd()
|
|
|
+ {
|
|
|
+ equality();
|
|
|
+ while(match(AND))
|
|
|
{
|
|
|
- int bPos = input.indexOf('[');
|
|
|
- String[] parts = SnuviUtils.split(strings, input.substring(bPos + 1, input.length() - 1), line);
|
|
|
- if(parts.length > 0)
|
|
|
- {
|
|
|
- return createArray(input.substring(0, bPos), compileFunction(parts, false));
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- return createArray(input.substring(0, bPos), new InputProvider[0]);
|
|
|
- }
|
|
|
+ Token t = previous();
|
|
|
+ IfGoto ifGoto = new IfGoto(t.getLine(), false);
|
|
|
+ instr.add(ifGoto);
|
|
|
+ equality();
|
|
|
+ ifGoto.setJump(instr.size());
|
|
|
+ addFunction(t.getLine(), 2, t.getType().getName());
|
|
|
}
|
|
|
- else
|
|
|
+ }
|
|
|
+
|
|
|
+ private void equality()
|
|
|
+ {
|
|
|
+ comparison();
|
|
|
+ while(match(EQUAL, NOT_EQUAL))
|
|
|
{
|
|
|
- return getOrCreateVariable(input);
|
|
|
+ Token t = previous();
|
|
|
+ comparison();
|
|
|
+ addFunction(t.getLine(), 2, t.getType().getName());
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- public static Object convert(String input)
|
|
|
+
|
|
|
+ private void comparison()
|
|
|
{
|
|
|
- if(input == null)
|
|
|
+ addition();
|
|
|
+ while(match(GREATER, GREATER_EQUAL, LESS, LESS_EQUAL))
|
|
|
{
|
|
|
- return null;
|
|
|
+ Token t = previous();
|
|
|
+ addition();
|
|
|
+ addFunction(t.getLine(), 2, t.getType().getName());
|
|
|
}
|
|
|
- input = input.trim();
|
|
|
- if(input.equals("true"))
|
|
|
- {
|
|
|
- return true;
|
|
|
- }
|
|
|
- else if(input.equals("false"))
|
|
|
+ }
|
|
|
+
|
|
|
+ private void addition()
|
|
|
+ {
|
|
|
+ multiplication();
|
|
|
+ while(match(SUB, ADD))
|
|
|
{
|
|
|
- return false;
|
|
|
+ Token t = previous();
|
|
|
+ multiplication();
|
|
|
+ addFunction(t.getLine(), 2, t.getType().getName());
|
|
|
}
|
|
|
- else if(input.equals("null"))
|
|
|
+ }
|
|
|
+
|
|
|
+ private void multiplication()
|
|
|
+ {
|
|
|
+ unary();
|
|
|
+ while(match(DIV, MUL, MOD))
|
|
|
{
|
|
|
- return null;
|
|
|
+ Token t = previous();
|
|
|
+ unary();
|
|
|
+ addFunction(t.getLine(), 2, t.getType().getName());
|
|
|
}
|
|
|
- else if(input.startsWith("\"") && input.endsWith("\""))
|
|
|
+ }
|
|
|
+
|
|
|
+ private void unary()
|
|
|
+ {
|
|
|
+ if(match(INVERT, BIT_INVERT, SUB, INC, DEC))
|
|
|
{
|
|
|
- if(input.length() == 1)
|
|
|
+ Token t = previous();
|
|
|
+ unary();
|
|
|
+ addFunction(t.getLine(), 1, t.getType().getName());
|
|
|
+ if(t.getType() == INC || t.getType() == DEC)
|
|
|
{
|
|
|
- return "\"";
|
|
|
+ lineExpression = true;
|
|
|
}
|
|
|
- return input.substring(1, input.length() - 1);
|
|
|
+ return;
|
|
|
}
|
|
|
- try
|
|
|
+ postUnary();
|
|
|
+ }
|
|
|
+
|
|
|
+ private void postUnary()
|
|
|
+ {
|
|
|
+ primary();
|
|
|
+ while(match(INC, DEC))
|
|
|
{
|
|
|
- return Double.parseDouble(input);
|
|
|
+ Token t = previous();
|
|
|
+ addFunction(t.getLine(), 1, "p" + t.getType().getName());
|
|
|
+ lineExpression = true;
|
|
|
}
|
|
|
- catch(NumberFormatException ex)
|
|
|
- {
|
|
|
- return input;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void primary()
|
|
|
+ {
|
|
|
+ Token t = advance();
|
|
|
+ switch(t.getType())
|
|
|
+ {
|
|
|
+ case FALSE: addConstant(t.getLine(), ConstantBoolean.FALSE); return;
|
|
|
+ case TRUE: addConstant(t.getLine(), ConstantBoolean.TRUE); return;
|
|
|
+ case NULL: addConstant(t.getLine(), ConstantNull.NULL); return;
|
|
|
+ case STRING: addConstant(t.getLine(), new ConstantString(t.getData().toString())); return;
|
|
|
+ case LABEL: addConstant(t.getLine(), new ConstantString(t.getData().toString().substring(1))); return;
|
|
|
+ case NUMBER: addConstant(t.getLine(), new ConstantDouble((Double) t.getData())); return;
|
|
|
+ case OPEN_BRACKET:
|
|
|
+ expression();
|
|
|
+ consume(CLOSE_BRACKET);
|
|
|
+ return;
|
|
|
+ case LITERAL:
|
|
|
+ if(match(OPEN_SQUARE_BRACKET))
|
|
|
+ {
|
|
|
+ handleArray(t);
|
|
|
+ }
|
|
|
+ else if(match(OPEN_BRACKET))
|
|
|
+ {
|
|
|
+ handleFunction(t);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ addConstant(t.getLine(), getVariable(t.getData().toString()));
|
|
|
+ }
|
|
|
+ return;
|
|
|
}
|
|
|
+ throw new PreScriptException(String.format("unexpected token %s", t.getType()), t.getLine());
|
|
|
}
|
|
|
|
|
|
- private Variable getOrCreateVariable(String var)
|
|
|
+ public void handleFunction(Token t)
|
|
|
{
|
|
|
- if(currentFunction != null && var.charAt(0) != '$')
|
|
|
+ int args = 0;
|
|
|
+ if(peek().getType() != CLOSE_BRACKET)
|
|
|
{
|
|
|
- Variable oldVar = localVars.get(var);
|
|
|
- if(oldVar == null)
|
|
|
+ while(true)
|
|
|
{
|
|
|
- oldVar = new LocalVariable(var);
|
|
|
- localVars.put(var, oldVar);
|
|
|
+ args++;
|
|
|
+ expression();
|
|
|
+ if(match(CLOSE_BRACKET))
|
|
|
+ {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ consume(COMMA);
|
|
|
}
|
|
|
- return oldVar;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- if(var.charAt(0) == '$')
|
|
|
- {
|
|
|
- var = var.substring(1);
|
|
|
- }
|
|
|
- Variable oldVar = vars.get(var);
|
|
|
- if(oldVar == null)
|
|
|
- {
|
|
|
- oldVar = new Variable(var);
|
|
|
- vars.put(var, oldVar);
|
|
|
- }
|
|
|
- return oldVar;
|
|
|
+ consume(CLOSE_BRACKET);
|
|
|
}
|
|
|
+ addFunction(t.getLine(), args, t.getData().toString());
|
|
|
+ lineExpression = true;
|
|
|
}
|
|
|
|
|
|
- private DynamicArray createArray(String var, InputProvider[] in)
|
|
|
+ public void handleArray(Token t)
|
|
|
{
|
|
|
- if(currentFunction != null)
|
|
|
+ if(peek().getType() == CLOSE_SQUARE_BRACKET)
|
|
|
{
|
|
|
- Variable oldVar = localVars.get(var);
|
|
|
- if(oldVar == null)
|
|
|
- {
|
|
|
- oldVar = new LocalArrayVariable(var);
|
|
|
- localVars.put(var, oldVar);
|
|
|
- }
|
|
|
- return new DynamicArray(oldVar, in);
|
|
|
+ throw new PreScriptException("empty array access", peek().getLine());
|
|
|
}
|
|
|
- else
|
|
|
+ int args = 0;
|
|
|
+ while(true)
|
|
|
{
|
|
|
- Variable oldVar = vars.get(var);
|
|
|
- if(oldVar == null)
|
|
|
+ args++;
|
|
|
+ expression();
|
|
|
+ if(match(CLOSE_SQUARE_BRACKET))
|
|
|
{
|
|
|
- oldVar = new ArrayVariable(var);
|
|
|
- vars.put(var, oldVar);
|
|
|
+ break;
|
|
|
}
|
|
|
- return new DynamicArray(oldVar, in);
|
|
|
+ consume(COMMA);
|
|
|
}
|
|
|
+ instr.add(new Array(t.getLine(), args, getVariable(t.getData().toString())));
|
|
|
}
|
|
|
|
|
|
- private void createIf(String name, String[] parts)
|
|
|
- {
|
|
|
- InputProvider[] input = compileFunction(parts, false);
|
|
|
- InputProvider[] realInput = new InputProvider[input.length + 1];
|
|
|
-
|
|
|
- System.arraycopy(input, 0, realInput, 0, input.length);
|
|
|
- JumpData jump = new JumpData(code.size());
|
|
|
- realInput[input.length] = jump;
|
|
|
- jumps.push(new JumpWrapper(jump, name));
|
|
|
-
|
|
|
- addCodeInstruction(name, realInput);
|
|
|
- }
|
|
|
-
|
|
|
- private void createFor(String[] parts)
|
|
|
+ private Variable getVariable(String name)
|
|
|
{
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- InputProvider[] input = compileFunction(parts, false);
|
|
|
- if(input.length != 3 && input.length != 4)
|
|
|
+ boolean global = name.startsWith("$");
|
|
|
+ if(inFunction != null && !global)
|
|
|
{
|
|
|
- throw new PreScriptException("missing 'for' syntax at", line);
|
|
|
+ LocalVariable v = localVars.get(name);
|
|
|
+ if(v != null)
|
|
|
+ {
|
|
|
+ return v;
|
|
|
+ }
|
|
|
+ v = new LocalVariable(name);
|
|
|
+ localVars.put(name, v);
|
|
|
+ return v;
|
|
|
}
|
|
|
- InputProvider[] realInput = new InputProvider[5];
|
|
|
-
|
|
|
- System.arraycopy(input, 0, realInput, 0, input.length);
|
|
|
|
|
|
- if(input.length == 3)
|
|
|
+ if(global)
|
|
|
{
|
|
|
- realInput[3] = new ConstantDouble(1.0);
|
|
|
+ name = name.substring(1);
|
|
|
}
|
|
|
|
|
|
- JumpData jump = new JumpData(code.size());
|
|
|
- realInput[4] = jump;
|
|
|
- JumpWrapper wrapper = new JumpWrapper(jump, "for");
|
|
|
- jumps.push(wrapper);
|
|
|
- loopJumps.push(wrapper);
|
|
|
-
|
|
|
- addCodeInstruction("for", realInput);
|
|
|
- }
|
|
|
-
|
|
|
- private void createWhile(String[] parts)
|
|
|
- {
|
|
|
-
|
|
|
-
|
|
|
- InputProvider[] input = compileFunction(parts, false);
|
|
|
- if(input.length != 1)
|
|
|
+ Variable v = vars.get(name);
|
|
|
+ if(v != null)
|
|
|
{
|
|
|
- throw new PreScriptException("invalid conditions at 'while'", line);
|
|
|
+ return v;
|
|
|
}
|
|
|
- InputProvider[] realInput = new InputProvider[2];
|
|
|
- realInput[0] = input[0];
|
|
|
-
|
|
|
- JumpData jump = new JumpData(code.size());
|
|
|
- realInput[1] = jump;
|
|
|
-
|
|
|
- JumpWrapper wrapper = new JumpWrapper(jump, "while");
|
|
|
- jumps.push(wrapper);
|
|
|
- loopJumps.push(wrapper);
|
|
|
-
|
|
|
- addCodeInstruction("while", realInput);
|
|
|
- }
|
|
|
-
|
|
|
- private void createBreakContinue(int current)
|
|
|
- {
|
|
|
- breakContinueJumps.forEach(jump -> jump.setRelativeJump(current));
|
|
|
- breakContinueJumps.clear();
|
|
|
+ v = new Variable(name);
|
|
|
+ vars.put(name, v);
|
|
|
+ return v;
|
|
|
}
|
|
|
}
|