Script.java 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. package me.hammerle.snuviscript.code;
  2. import me.hammerle.snuviscript.inputprovider.InputProvider;
  3. import java.io.FileInputStream;
  4. import java.io.FileNotFoundException;
  5. import java.io.InputStream;
  6. import java.util.ArrayList;
  7. import java.util.HashMap;
  8. import java.util.HashSet;
  9. import java.util.Stack;
  10. import java.util.function.Consumer;
  11. import me.hammerle.snuviscript.exceptions.PreScriptException;
  12. import me.hammerle.snuviscript.exceptions.StackTrace;
  13. import me.hammerle.snuviscript.inputprovider.ReturnWrapper;
  14. import me.hammerle.snuviscript.tokenizer.Tokenizer;
  15. import me.hammerle.snuviscript.inputprovider.Variable;
  16. import me.hammerle.snuviscript.instructions.Instruction;
  17. import me.hammerle.snuviscript.instructions.UserFunction;
  18. public final class Script {
  19. private static int idCounter = 0;
  20. private final int id;
  21. private final String name;
  22. private final ScriptManager sm;
  23. private int lineIndex = 0;
  24. private final Instruction[] code;
  25. private final Stack<InputProvider> dataStack = new Stack<>();
  26. private final Stack<Integer> returnStack = new Stack<>();
  27. private final HashMap<String, Integer> labels = new HashMap<>();
  28. private final HashMap<String, HashMap<String, Integer>> localLabels = new HashMap<>();
  29. private final HashMap<String, Variable> vars = new HashMap<>();
  30. private final Stack<HashMap<String, Variable>> localVars = new Stack<>();
  31. private final HashMap<String, Integer> functions = new HashMap<>();
  32. private Stack<Boolean> ifState = new Stack<>();
  33. private Stack<Integer> stackElements = new Stack<>();
  34. private int errorLine = -1;
  35. private Stack<String> inFunction = new Stack<>();
  36. private Stack<Boolean> returnVarPop = new Stack<>();
  37. // states if event broadcasts should be received, otherwise only direct event calls work
  38. private boolean eventBroadcast;
  39. // waiting scripts stop executing and run again on an event
  40. private boolean isWaiting;
  41. // holded scripts do not receive events
  42. private boolean isHolded;
  43. private boolean stackTrace;
  44. private HashSet<String> loadedEvents = new HashSet<>();
  45. private final Consumer<Script> onStart;
  46. private final Consumer<Script> onTerm;
  47. private final ArrayList<AutoCloseable> closeables = new ArrayList<>();
  48. public Script(ScriptManager sm, Consumer<Script> onStart, Consumer<Script> onTerm, String name, String... path) {
  49. ifState.push(true);
  50. this.id = idCounter++;
  51. this.name = name;
  52. this.sm = sm;
  53. this.onStart = onStart;
  54. this.onTerm = onTerm;
  55. Tokenizer t = new Tokenizer();
  56. InputStream[] streams = new InputStream[path.length];
  57. for(int i = 0; i < streams.length; i++) {
  58. try {
  59. streams[i] = new FileInputStream(path[i]);
  60. } catch(FileNotFoundException ex) {
  61. throw new PreScriptException(ex.getMessage(), -1);
  62. }
  63. }
  64. Compiler c = new Compiler();
  65. this.code = c.compile(t.tokenize(streams), labels, vars, functions, localLabels);
  66. /*int i = 0;
  67. for(Instruction in : code)
  68. {
  69. System.out.printf("%3d: %5b | %s\n", i, in.shouldNotReturnValue(), in);
  70. i++;
  71. }*/
  72. /*this.parser = parser;
  73. this.logger = parser.getLogger();
  74. this.scheduler = parser.getScheduler();
  75. this.labels = new HashMap<>();
  76. this.returnStack = new Stack<>();
  77. this.events = new HashSet<>();
  78. this.currentLine = 0;
  79. this.isWaiting = false;
  80. this.isHolded = false;
  81. this.isValid = true;
  82. this.receiveEventBroadcast = receiveEventBroadcast;
  83. this.cpuTime = 0;
  84. this.catchLine = -1;
  85. this.currentCommand = null;
  86. this.ifState = true;
  87. this.printStackTrace = false;
  88. this.simpleName = simpleName;
  89. this.name = name;
  90. this.id = id;
  91. this.onStart = onStart;
  92. this.onTerm = onTerm;
  93. this.localVars = new Stack<>();
  94. this.functions = new HashMap<>();
  95. this.localLabels = new HashMap<>();
  96. this.code = OldCompiler.compile(this, code, labels, functions, localLabels);*/
  97. }
  98. private void pushIfNotNull(InputProvider in) {
  99. if(in != null) {
  100. dataStack.push(in);
  101. }
  102. }
  103. public void run() {
  104. isWaiting = false;
  105. //System.out.println("_________________________");
  106. long endTime = System.nanoTime() + 15_000_000;
  107. while(lineIndex < code.length && !isWaiting && !isHolded) {
  108. try {
  109. Instruction instr = code[lineIndex];
  110. //System.out.println("EXECUTE: " + instr + " " + dataStack);
  111. if(instr.getArguments() > 0) {
  112. InputProvider[] args = InputProviderArrayPool.get(instr.getArguments());
  113. for(int i = args.length - 1; i >= 0; i--) {
  114. args[i] = dataStack.pop();
  115. }
  116. pushIfNotNull(instr.execute(this, args));
  117. } else {
  118. pushIfNotNull(instr.execute(this, new InputProvider[0]));
  119. }
  120. //System.out.println("AFTER EXECUTE: " + dataStack);
  121. lineIndex++;
  122. } catch(Exception ex) {
  123. if(stackTrace) {
  124. ex.printStackTrace();
  125. }
  126. if(errorLine != -1) {
  127. int elements = stackElements.pop();
  128. while(dataStack.size() > elements) {
  129. dataStack.pop();
  130. }
  131. lineIndex = errorLine + 1;
  132. errorLine = -1;
  133. continue;
  134. }
  135. sm.getLogger().print(null, ex, code[lineIndex].getName(), name, this, new StackTrace(code[lineIndex].getLine(), returnStack));
  136. break;
  137. }
  138. if(System.nanoTime() > endTime) {
  139. isHolded = true;
  140. sm.getScheduler().scheduleTask(()
  141. -> {
  142. if(!shouldTerm()) {
  143. isHolded = false;
  144. run();
  145. }
  146. }, 1);
  147. break;
  148. }
  149. }
  150. //System.out.println(count + " " + (15_000_000 / count));
  151. if(shouldTerm() && !dataStack.isEmpty()) {
  152. sm.getLogger().print(String.format("data stack is not empty %s", dataStack));
  153. }
  154. }
  155. public String getName() {
  156. return name;
  157. }
  158. public int getId() {
  159. return id;
  160. }
  161. public StackTrace getStackTrace() {
  162. if(lineIndex >= 0 && lineIndex < code.length) {
  163. return new StackTrace(code[lineIndex].getLine(), returnStack);
  164. }
  165. return null;
  166. }
  167. public ScriptManager getScriptManager() {
  168. return sm;
  169. }
  170. private HashMap<String, Integer> getLabels() {
  171. return inFunction.isEmpty() ? labels : localLabels.get(inFunction.peek());
  172. }
  173. public void gotoLabel(String label, boolean error, int add) {
  174. lineIndex = getLabels().getOrDefault(label, error ? null : lineIndex) + add;
  175. }
  176. public void gotoLabel(String label, boolean error) {
  177. gotoLabel(label, error, 0);
  178. }
  179. public void goSub(String label) {
  180. int line = getLabels().get(label);
  181. returnStack.push(lineIndex);
  182. lineIndex = line;
  183. returnVarPop.push(false);
  184. }
  185. public void jumpTo(int jump) {
  186. lineIndex = jump;
  187. }
  188. public void setIfState(boolean state) {
  189. ifState.pop();
  190. ifState.push(state);
  191. }
  192. public boolean getIfState() {
  193. return ifState.peek();
  194. }
  195. public void setErrorLine(int line) {
  196. errorLine = line;
  197. if(line != -1) {
  198. stackElements.push(dataStack.size());
  199. }
  200. }
  201. public void handleFunction(String function, InputProvider[] in) throws Exception {
  202. Integer sub = functions.get(function);
  203. if(sub == null) {
  204. throw new IllegalArgumentException(String.format("function '%s' does not exist", function));
  205. }
  206. UserFunction uf = (UserFunction) code[sub];
  207. String[] args = uf.getArgumentNames();
  208. HashMap<String, Variable> lvars = new HashMap<>();
  209. if(in.length != args.length) {
  210. throw new IllegalArgumentException(String.format("invalid number of arguments at function '%s'", function));
  211. }
  212. for(int i = 0; i < in.length; i++) {
  213. Variable v = new Variable(args[i]);
  214. v.set(this, in[i].get(this));
  215. lvars.put(args[i], v);
  216. }
  217. ifState.push(true);
  218. localVars.push(lvars);
  219. returnStack.push(lineIndex);
  220. lineIndex = sub;
  221. inFunction.push(function);
  222. returnVarPop.push(true);
  223. }
  224. public void handleReturn(ReturnWrapper wrapper) {
  225. lineIndex = returnStack.pop();
  226. if(returnVarPop.pop()) {
  227. ifState.pop();
  228. inFunction.pop();
  229. localVars.pop();
  230. if(wrapper != null && !code[lineIndex].shouldNotReturnValue()) {
  231. dataStack.add(wrapper);
  232. }
  233. }
  234. }
  235. public Variable getOrAddLocalVariable(String name) {
  236. HashMap<String, Variable> map = localVars.peek();
  237. Variable v = map.get(name);
  238. if(v != null) {
  239. return v;
  240. }
  241. v = new Variable(name);
  242. map.put(name, v);
  243. return v;
  244. }
  245. public InputProvider peekDataStack() {
  246. return dataStack.peek();
  247. }
  248. public void setEventBroadcast(boolean eventBroadcast) {
  249. this.eventBroadcast = eventBroadcast;
  250. }
  251. public boolean shouldReceiveEventBroadcast() {
  252. return eventBroadcast;
  253. }
  254. public void term() {
  255. lineIndex = code.length;
  256. }
  257. public boolean shouldTerm() {
  258. return lineIndex < 0 || lineIndex >= code.length;
  259. }
  260. public void onTerm() {
  261. if(onTerm != null) {
  262. onTerm.accept(this);
  263. }
  264. closeables.forEach(c
  265. -> {
  266. sm.getLogger().print("prepared statement not closed", null, null, name, this, null);
  267. try {
  268. c.close();
  269. } catch(Exception ex) {
  270. sm.getLogger().print("cannot close closeable in script", ex, null, name, this, null);
  271. }
  272. });
  273. }
  274. public void onStart() {
  275. if(onStart != null) {
  276. onStart.accept(this);
  277. }
  278. }
  279. public void setHolded(boolean b) {
  280. isHolded = b;
  281. }
  282. public boolean isHolded() {
  283. return isHolded;
  284. }
  285. public void setWaiting() {
  286. isWaiting = true;
  287. }
  288. public boolean isWaiting() {
  289. return isWaiting;
  290. }
  291. public void setVar(String name, Object value) {
  292. Variable v = vars.get(name);
  293. if(v != null) {
  294. v.set(this, value);
  295. }
  296. }
  297. public Variable getVar(String name) {
  298. return vars.get(name);
  299. }
  300. public boolean isEventLoaded(String event) {
  301. return loadedEvents.contains(event);
  302. }
  303. public boolean loadEvent(String event) {
  304. return loadedEvents.add(event);
  305. }
  306. public boolean unloadEvent(String event) {
  307. return loadedEvents.remove(event);
  308. }
  309. public void setStackTrace(boolean b) {
  310. stackTrace = b;
  311. }
  312. public synchronized void addCloseable(AutoCloseable closeable) {
  313. closeables.add(closeable);
  314. }
  315. public synchronized void removeCloseable(AutoCloseable closeable) {
  316. closeables.remove(closeable);
  317. }
  318. @Override
  319. public int hashCode() {
  320. return id;
  321. }
  322. @Override
  323. public boolean equals(Object o) {
  324. return o != null && o instanceof Script && ((Script) o).id == id;
  325. }
  326. }