Script.java 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  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.inputprovider.ReturnWrapper;
  13. import me.hammerle.snuviscript.tokenizer.Tokenizer;
  14. import me.hammerle.snuviscript.inputprovider.Variable;
  15. import me.hammerle.snuviscript.instructions.Instruction;
  16. import me.hammerle.snuviscript.instructions.UserFunction;
  17. public final class Script
  18. {
  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 boolean ifState = true;
  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. {
  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. {
  59. try
  60. {
  61. streams[i] = new FileInputStream(path[i]);
  62. }
  63. catch(FileNotFoundException ex)
  64. {
  65. throw new PreScriptException(ex.getMessage(), -1);
  66. }
  67. }
  68. Compiler c = new Compiler();
  69. this.code = c.compile(t.tokenize(streams), labels, vars, functions, localLabels);
  70. /*int i = 0;
  71. for(Instruction in : code)
  72. {
  73. System.out.printf("%3d: %5b | %s\n", i, in.shouldNotReturnValue(), in);
  74. i++;
  75. }*/
  76. /*this.parser = parser;
  77. this.logger = parser.getLogger();
  78. this.scheduler = parser.getScheduler();
  79. this.labels = new HashMap<>();
  80. this.returnStack = new Stack<>();
  81. this.events = new HashSet<>();
  82. this.currentLine = 0;
  83. this.isWaiting = false;
  84. this.isHolded = false;
  85. this.isValid = true;
  86. this.receiveEventBroadcast = receiveEventBroadcast;
  87. this.cpuTime = 0;
  88. this.catchLine = -1;
  89. this.currentCommand = null;
  90. this.ifState = true;
  91. this.printStackTrace = false;
  92. this.simpleName = simpleName;
  93. this.name = name;
  94. this.id = id;
  95. this.onStart = onStart;
  96. this.onTerm = onTerm;
  97. this.localVars = new Stack<>();
  98. this.functions = new HashMap<>();
  99. this.localLabels = new HashMap<>();
  100. this.code = OldCompiler.compile(this, code, labels, functions, localLabels);*/
  101. }
  102. private void pushIfNotNull(InputProvider in)
  103. {
  104. if(in != null)
  105. {
  106. dataStack.push(in);
  107. }
  108. }
  109. public void run()
  110. {
  111. isWaiting = false;
  112. //System.out.println("_________________________");
  113. long endTime = System.nanoTime() + 15_000_000;
  114. int count = 0;
  115. while(lineIndex < code.length && !isWaiting && !isHolded)
  116. {
  117. try
  118. {
  119. Instruction instr = code[lineIndex];
  120. //System.out.println("EXECUTE: " + instr + " " + dataStack);
  121. if(instr.getArguments() > 0)
  122. {
  123. InputProvider[] args = InputProviderArrayPool.get(instr.getArguments());
  124. for(int i = args.length - 1; i >= 0; i--)
  125. {
  126. args[i] = dataStack.pop();
  127. }
  128. pushIfNotNull(instr.execute(this, args));
  129. }
  130. else
  131. {
  132. pushIfNotNull(instr.execute(this, new InputProvider[0]));
  133. }
  134. //System.out.println("AFTER EXECUTE: " + dataStack);
  135. lineIndex++;
  136. }
  137. catch(Exception ex)
  138. {
  139. if(stackTrace)
  140. {
  141. ex.printStackTrace();
  142. }
  143. if(errorLine != -1)
  144. {
  145. int elements = stackElements.pop();
  146. while(dataStack.size() > elements)
  147. {
  148. dataStack.pop();
  149. }
  150. lineIndex = errorLine + 1;
  151. errorLine = -1;
  152. continue;
  153. }
  154. sm.getLogger().print(ex.getLocalizedMessage(), ex,
  155. code[lineIndex].getName(), name, this, code[lineIndex].getLine());
  156. break;
  157. }
  158. count++;
  159. if(System.nanoTime() > endTime)
  160. {
  161. isHolded = true;
  162. sm.getScheduler().scheduleTask(() ->
  163. {
  164. if(!shouldTerm())
  165. {
  166. isHolded = false;
  167. run();
  168. }
  169. }, 1);
  170. break;
  171. }
  172. }
  173. //System.out.println(count + " " + (15_000_000 / count));
  174. if(shouldTerm() && !dataStack.isEmpty())
  175. {
  176. sm.getLogger().print(String.format("data stack is not empty %s", dataStack));
  177. }
  178. }
  179. public String getName()
  180. {
  181. return name;
  182. }
  183. public int getId()
  184. {
  185. return id;
  186. }
  187. public int getActiveSourceLine()
  188. {
  189. if(lineIndex >= 0 && lineIndex < code.length)
  190. {
  191. return code[lineIndex].getLine();
  192. }
  193. return -1;
  194. }
  195. public ScriptManager getScriptManager()
  196. {
  197. return sm;
  198. }
  199. private HashMap<String, Integer> getLabels()
  200. {
  201. return inFunction.isEmpty() ? labels : localLabels.get(inFunction.peek());
  202. }
  203. public void gotoLabel(String label, boolean error, int add)
  204. {
  205. lineIndex = getLabels().getOrDefault(label, error ? null : lineIndex) + add;
  206. }
  207. public void gotoLabel(String label, boolean error)
  208. {
  209. gotoLabel(label, error, 0);
  210. }
  211. public void goSub(String label)
  212. {
  213. int line = getLabels().get(label);
  214. returnStack.push(lineIndex);
  215. lineIndex = line;
  216. returnVarPop.push(false);
  217. }
  218. public void jumpTo(int jump)
  219. {
  220. lineIndex = jump;
  221. }
  222. public void setIfState(boolean state)
  223. {
  224. ifState = state;
  225. }
  226. public boolean getIfState()
  227. {
  228. return ifState;
  229. }
  230. public void setErrorLine(int line)
  231. {
  232. errorLine = line;
  233. if(line != -1)
  234. {
  235. stackElements.push(dataStack.size());
  236. }
  237. }
  238. public void handleFunction(String function, InputProvider[] in) throws Exception
  239. {
  240. Integer sub = functions.get(function);
  241. if(sub == null)
  242. {
  243. throw new IllegalArgumentException(String.format("function '%s' does not exist", function));
  244. }
  245. UserFunction uf = (UserFunction) code[sub];
  246. String[] args = uf.getArgumentNames();
  247. HashMap<String, Variable> lvars = new HashMap<>();
  248. if(in.length != args.length)
  249. {
  250. throw new IllegalArgumentException(String.format("invalid number of arguments at function '%s'", function));
  251. }
  252. for(int i = 0; i < in.length; i++)
  253. {
  254. Variable v = new Variable(args[i]);
  255. v.set(this, in[i].get(this));
  256. lvars.put(args[i], v);
  257. }
  258. localVars.push(lvars);
  259. returnStack.push(lineIndex);
  260. lineIndex = sub;
  261. inFunction.push(function);
  262. returnVarPop.push(true);
  263. }
  264. public void handleReturn(ReturnWrapper wrapper)
  265. {
  266. if(returnVarPop.pop())
  267. {
  268. inFunction.pop();
  269. localVars.pop();
  270. if(wrapper != null)
  271. {
  272. dataStack.add(wrapper);
  273. }
  274. }
  275. lineIndex = returnStack.pop();
  276. }
  277. public Variable getOrAddLocalVariable(String name)
  278. {
  279. HashMap<String, Variable> map = localVars.peek();
  280. Variable v = map.get(name);
  281. if(v != null)
  282. {
  283. return v;
  284. }
  285. v = new Variable(name);
  286. map.put(name, v);
  287. return v;
  288. }
  289. public InputProvider peekDataStack()
  290. {
  291. return dataStack.peek();
  292. }
  293. public void setEventBroadcast(boolean eventBroadcast)
  294. {
  295. this.eventBroadcast = eventBroadcast;
  296. }
  297. public boolean shouldReceiveEventBroadcast()
  298. {
  299. return eventBroadcast;
  300. }
  301. public void term()
  302. {
  303. lineIndex = code.length;
  304. }
  305. public boolean shouldTerm()
  306. {
  307. return lineIndex < 0 || lineIndex >= code.length;
  308. }
  309. public void onTerm()
  310. {
  311. if(onTerm != null)
  312. {
  313. onTerm.accept(this);
  314. }
  315. closeables.forEach(c ->
  316. {
  317. sm.getLogger().print("prepared statement not closed", null, null, name, this, -1);
  318. try
  319. {
  320. c.close();
  321. }
  322. catch(Exception ex)
  323. {
  324. sm.getLogger().print("cannot close closeable in script", ex, null, name, this, -1);
  325. }
  326. });
  327. }
  328. public void onStart()
  329. {
  330. if(onStart != null)
  331. {
  332. onStart.accept(this);
  333. }
  334. }
  335. public void setHolded(boolean b)
  336. {
  337. isHolded = b;
  338. }
  339. public boolean isHolded()
  340. {
  341. return isHolded;
  342. }
  343. public void setWaiting()
  344. {
  345. isWaiting = true;
  346. }
  347. public boolean isWaiting()
  348. {
  349. return isWaiting;
  350. }
  351. public void setVar(String name, Object value)
  352. {
  353. Variable v = vars.get(name);
  354. if(v != null)
  355. {
  356. v.set(this, value);
  357. }
  358. }
  359. public Variable getVar(String name)
  360. {
  361. return vars.get(name);
  362. }
  363. public boolean isEventLoaded(String event)
  364. {
  365. return loadedEvents.contains(event);
  366. }
  367. public boolean loadEvent(String event)
  368. {
  369. return loadedEvents.add(event);
  370. }
  371. public boolean unloadEvent(String event)
  372. {
  373. return loadedEvents.remove(event);
  374. }
  375. public void setStackTrace(boolean b)
  376. {
  377. stackTrace = b;
  378. }
  379. public synchronized void addCloseable(AutoCloseable closeable)
  380. {
  381. closeables.add(closeable);
  382. }
  383. public synchronized void removeCloseable(AutoCloseable closeable)
  384. {
  385. closeables.remove(closeable);
  386. }
  387. @Override
  388. public int hashCode()
  389. {
  390. return id;
  391. }
  392. @Override
  393. public boolean equals(Object o)
  394. {
  395. return o != null && o instanceof Script && ((Script) o).id == id;
  396. }
  397. }