Script.java 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. package me.hammerle.code;
  2. import java.util.ArrayList;
  3. import java.util.Arrays;
  4. import java.util.HashMap;
  5. import java.util.HashSet;
  6. import java.util.Stack;
  7. import java.util.TreeMap;
  8. import me.hammerle.exceptions.CodeTooLongException;
  9. import me.hammerle.exceptions.GotoLabelNotFoundException;
  10. import me.hammerle.exceptions.HoldCodeException;
  11. import me.hammerle.exceptions.PreScriptException;
  12. import me.hammerle.math.Fraction;
  13. public class Script
  14. {
  15. private final SnuviParser parser;
  16. protected final boolean receiveEventBroadcast;
  17. protected final ArrayList<String> eventVars;
  18. private final int id;
  19. private final String name;
  20. private final HashMap<String, Object> variables;
  21. private final HashMap<String, Integer> gotos;
  22. private final HashSet<String> events;
  23. private final Stack<Object> valueStack;
  24. private final Stack<Object> functionStack;
  25. private final Stack<Integer> returnStack;
  26. private final TreeMap<Integer, Fraction> forMap;
  27. private Code[] code;
  28. private int position;
  29. private int loopCounter;
  30. // halt is only used by waitfor, the script does not react to events if halted
  31. private boolean halt;
  32. private boolean isRunning;
  33. private boolean valid;
  34. private int tryJumpLine;
  35. public Script(SnuviParser parser, int id, String name, String code, boolean receiveEventBroadcast)
  36. {
  37. this.eventVars = new ArrayList<>();
  38. this.receiveEventBroadcast = receiveEventBroadcast;
  39. this.parser = parser;
  40. this.id = id;
  41. this.name = name;
  42. variables = new HashMap<>();
  43. gotos = new HashMap<>();
  44. events = new HashSet<>();
  45. valueStack = new Stack<>();
  46. functionStack = new Stack<>();
  47. returnStack = new Stack<>();
  48. forMap = new TreeMap<>();
  49. this.code = Code.generate(parser, name, code, gotos);
  50. if(this.code.length == 0)
  51. {
  52. throw new PreScriptException(name, 0, "empty file");
  53. }
  54. position = 0;
  55. loopCounter = 0;
  56. halt = false;
  57. isRunning = false;
  58. valid = true;
  59. tryJumpLine = -1;
  60. }
  61. public Script(SnuviParser parser, int id, String name, String code)
  62. {
  63. this(parser, id, name, code, true);
  64. }
  65. protected void initExpansion(Object... o)
  66. {
  67. }
  68. public void overload(String code)
  69. {
  70. gotos.clear();
  71. valueStack.clear();
  72. functionStack.clear();
  73. returnStack.clear();
  74. forMap.clear();
  75. this.code = Code.generate(parser, name, code, gotos);
  76. position = -1;
  77. halt = false;
  78. valid = true;
  79. tryJumpLine = -1;
  80. }
  81. public Code[] getCode(int line)
  82. {
  83. line = code[line].realLine;
  84. int start = 0;
  85. int end = code.length;
  86. int helper;
  87. while(end - start > 1)
  88. {
  89. helper = (start + end) >> 1;
  90. if(code[helper].realLine > line)
  91. {
  92. end = helper;
  93. }
  94. else if(code[helper].realLine <= line)
  95. {
  96. start = helper;
  97. }
  98. }
  99. int realEnd = start;
  100. start = 0;
  101. end = code.length;
  102. while(end - start > 1)
  103. {
  104. helper = (start + end) >> 1;
  105. if(code[helper].realLine >= line)
  106. {
  107. end = helper;
  108. }
  109. else if(code[helper].realLine < line)
  110. {
  111. start = helper;
  112. }
  113. }
  114. return Arrays.copyOfRange(code, end, realEnd + 1);
  115. }
  116. // -----------------------------------------------------------------------------------
  117. // Script-Daten
  118. // -----------------------------------------------------------------------------------
  119. public ISnuviLogger getLogger()
  120. {
  121. return parser.logger;
  122. }
  123. public int getId()
  124. {
  125. return id;
  126. }
  127. public String getName()
  128. {
  129. return name;
  130. }
  131. public boolean isHalt()
  132. {
  133. return halt;
  134. }
  135. public void setHalt(boolean b)
  136. {
  137. halt = b;
  138. }
  139. public boolean isRunning()
  140. {
  141. return isRunning;
  142. }
  143. public boolean isValid()
  144. {
  145. return valid;
  146. }
  147. public void setInvalid()
  148. {
  149. valid = false;
  150. }
  151. public int getActiveRealCodeLine()
  152. {
  153. if(position < 0 || position >= code.length)
  154. {
  155. return 0;
  156. }
  157. return code[position].realLine;
  158. }
  159. // -----------------------------------------------------------------------------------
  160. // Event-Handling
  161. // -----------------------------------------------------------------------------------
  162. public void loadEvent(String s)
  163. {
  164. events.add(s);
  165. }
  166. public boolean isLoadedEvent(String s)
  167. {
  168. return events.contains(s);
  169. }
  170. public void unloadEvent(String s)
  171. {
  172. events.remove(s);
  173. }
  174. // -----------------------------------------------------------------------------------
  175. // Script-Flow
  176. // -----------------------------------------------------------------------------------
  177. public void onTerm()
  178. {
  179. }
  180. public void runCode()
  181. {
  182. if(this.isValid())
  183. {
  184. try
  185. {
  186. isRunning = true;
  187. while(position < code.length)
  188. {
  189. code[position].executeFunction(parser, this, valueStack);
  190. position++;
  191. }
  192. isRunning = false;
  193. parser.termSafe(this);
  194. }
  195. catch(Exception ex)
  196. {
  197. isRunning = false;
  198. if(ex.getClass() != HoldCodeException.class)
  199. {
  200. parser.logger.printException(ex, code[position].function, this, getActiveRealCodeLine());
  201. }
  202. position++;
  203. }
  204. }
  205. }
  206. // -----------------------------------------------------------------------------------
  207. // Variablen
  208. // -----------------------------------------------------------------------------------
  209. public void setEventVar(String var, Object value)
  210. {
  211. variables.put(var, value);
  212. eventVars.add(var);
  213. }
  214. public void setVar(String var, Object value)
  215. {
  216. variables.put(var, value);
  217. }
  218. public Object getVar(String var)
  219. {
  220. return variables.get(var);
  221. }
  222. public HashMap<String, Object> getVars()
  223. {
  224. return variables;
  225. }
  226. public boolean getBooleanVar(String var)
  227. {
  228. try
  229. {
  230. return (boolean) getVar(var);
  231. }
  232. catch(ClassCastException | NullPointerException ex)
  233. {
  234. return false;
  235. }
  236. }
  237. public void removeVar(String var)
  238. {
  239. variables.remove(var);
  240. }
  241. // -----------------------------------------------------------------------------------
  242. // Goto, Return
  243. // -----------------------------------------------------------------------------------
  244. public void incLoopCounter()
  245. {
  246. loopCounter++;
  247. if(loopCounter > 50)
  248. {
  249. resetLoopCounter();
  250. throw new CodeTooLongException();
  251. }
  252. }
  253. public void gotoLabel(String label, boolean scheduled, boolean resetFor)
  254. {
  255. Integer i = gotos.get(label);
  256. if(i == null)
  257. {
  258. throw new GotoLabelNotFoundException(label);
  259. }
  260. if(resetFor)
  261. {
  262. forMap.clear();
  263. }
  264. incLoopCounter();
  265. position = i - (scheduled ? 0 : 1);
  266. }
  267. public void resetLoopCounter()
  268. {
  269. loopCounter = 0;
  270. }
  271. public void gotoLabelWithReturn(String label)
  272. {
  273. returnStack.push(position);
  274. gotoLabel(label, false, false);
  275. }
  276. public void doReturn()
  277. {
  278. position = returnStack.pop();
  279. }
  280. public void jump()
  281. {
  282. position += (int) code[position].value;
  283. }
  284. public void jumpDoElse()
  285. {
  286. position += (int) code[position].value;
  287. if(code.length <= position + 1)
  288. {
  289. return;
  290. }
  291. if("else".equals(code[position + 1].function))
  292. {
  293. position++;
  294. }
  295. }
  296. // -----------------------------------------------------------------------------------
  297. // try
  298. // -----------------------------------------------------------------------------------
  299. public void gotoTryJumpLine()
  300. {
  301. position = tryJumpLine;
  302. }
  303. public void saveTryJumpLine()
  304. {
  305. tryJumpLine = position + ((int) code[position].value) + 1;
  306. if(!"catch".equals(code[tryJumpLine].function))
  307. {
  308. throw new IllegalStateException("try without catch");
  309. }
  310. }
  311. public void resetTryJumpLine()
  312. {
  313. tryJumpLine = -1;
  314. }
  315. public int getTryJumpLine()
  316. {
  317. return tryJumpLine;
  318. }
  319. // -------------------------------------------------------------------------
  320. // for
  321. // -------------------------------------------------------------------------
  322. public boolean repeatFinished(Fraction value, Fraction step, Fraction border)
  323. {
  324. return (step.isNegative() && border.compareTo(value) > 0) || border.compareTo(value) < 0;
  325. }
  326. public Fraction nextRepeatData(Fraction start, Fraction step)
  327. {
  328. Fraction f = forMap.get(position);
  329. if(f == null)
  330. {
  331. forMap.put(position, start);
  332. return start;
  333. }
  334. f = f.add(step);
  335. forMap.put(position, f);
  336. return f;
  337. }
  338. public void clearRepeatData()
  339. {
  340. forMap.remove(position);
  341. }
  342. public void clearLastRepeatData()
  343. {
  344. forMap.pollLastEntry();
  345. }
  346. // -------------------------------------------------------------------------
  347. // push, pop stuff for functions
  348. // -------------------------------------------------------------------------
  349. public void pushFunctionInput(Object[] o)
  350. {
  351. for(Object ob : o)
  352. {
  353. valueStack.push(ob);
  354. }
  355. }
  356. public void pushFunctionInput(Object[] o, Fraction f)
  357. {
  358. pushFunctionInput(o);
  359. valueStack.push(f);
  360. }
  361. public Object popFunctionInput()
  362. {
  363. return valueStack.pop();
  364. }
  365. public void push(int start, Object... o)
  366. {
  367. for(int i = start; i < o.length; i++)
  368. {
  369. functionStack.push(o[i]);
  370. }
  371. }
  372. public void pop(Object... o)
  373. {
  374. for(int i = o.length - 1; i >= 0; i--)
  375. {
  376. setVar(o[i].toString(), functionStack.pop());
  377. }
  378. }
  379. }