SnuviParser.java 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146
  1. package me.hammerle.code;
  2. import java.io.File;
  3. import java.io.IOException;
  4. import java.lang.reflect.InvocationTargetException;
  5. import java.nio.charset.StandardCharsets;
  6. import java.nio.file.Files;
  7. import java.nio.file.Paths;
  8. import me.hammerle.exceptions.HoldCodeException;
  9. import java.time.ZonedDateTime;
  10. import java.util.ArrayList;
  11. import java.util.Arrays;
  12. import java.util.Calendar;
  13. import java.util.Collection;
  14. import java.util.Collections;
  15. import java.util.GregorianCalendar;
  16. import java.util.HashMap;
  17. import java.util.HashSet;
  18. import java.util.List;
  19. import java.util.Map;
  20. import java.util.Set;
  21. import java.util.function.BiFunction;
  22. import java.util.function.BiConsumer;
  23. import java.util.function.Consumer;
  24. import java.util.function.Predicate;
  25. import java.util.stream.Collectors;
  26. import me.hammerle.exceptions.FileIOException;
  27. import me.hammerle.exceptions.GotoLabelNotFoundException;
  28. import me.hammerle.exceptions.PreScriptException;
  29. import me.hammerle.math.Fraction;
  30. public class SnuviParser
  31. {
  32. protected final ISnuviLogger logger;
  33. private final ISnuviScheduler scheduler;
  34. private final HashMap<String, BiFunction<Object[], Script, Object>> functions;
  35. public boolean printStack;
  36. private int idCounter;
  37. private final HashMap<Integer, Script> scripts;
  38. private final ArrayList<Integer> termQueue;
  39. public SnuviParser(ISnuviLogger logger, ISnuviScheduler scheduler)
  40. {
  41. this.logger = logger;
  42. this.scheduler = scheduler;
  43. functions = new HashMap<>();
  44. registerStandardLibraries();
  45. printStack = false;
  46. scripts = new HashMap<>();
  47. termQueue = new ArrayList<>();
  48. idCounter = 0;
  49. }
  50. // -----------------------------------------------------------------------------------
  51. // function registry
  52. // -----------------------------------------------------------------------------------
  53. public boolean isRegisteredFunction(String s)
  54. {
  55. return functions.containsKey(s);
  56. }
  57. public void registerFunction(String s, BiFunction<Object[], Script, Object> f)
  58. {
  59. functions.put(s, f);
  60. }
  61. public void registerConsumer(String s, BiConsumer<Object[], Script> f)
  62. {
  63. functions.put(s, (BiFunction<Object[], Script, Object>) (args, sc) ->
  64. {
  65. f.accept(args, sc);
  66. return Void.TYPE;
  67. });
  68. }
  69. public void registerAlias(String alias, String original)
  70. {
  71. BiFunction<Object[], Script, Object> f = functions.get(original);
  72. if(f == null)
  73. {
  74. throw new IllegalArgumentException("registering alias for non existent function");
  75. }
  76. registerFunction(alias, functions.get(original));
  77. }
  78. @SuppressWarnings("unchecked")
  79. private void registerStandardLibraries()
  80. {
  81. registerFunction("nothing", (args, sc) ->
  82. 0);
  83. registerFunction("", (args, sc) ->
  84. args[0]);
  85. registerConsumer("gotoline", (args, sc) ->
  86. {
  87. sc.jump();
  88. sc.incLoopCounter();
  89. });
  90. registerConsumer("error", (args, sc) ->
  91. printStack = !printStack);
  92. // -------------------------------------------------------------------------------
  93. // event
  94. // -------------------------------------------------------------------------------
  95. registerConsumer("event.load", (args, sc) ->
  96. sc.loadEvent(args[0].toString()));
  97. registerConsumer("event.unload", (args, sc) ->
  98. sc.unloadEvent(args[0].toString()));
  99. registerFunction("event.isloaded", (args, sc) ->
  100. sc.isLoadedEvent(args[0].toString()));
  101. // -------------------------------------------------------------------------------
  102. // bit
  103. // -------------------------------------------------------------------------------
  104. registerFunction(">>", (args, qd) ->
  105. ((Fraction) args[0]).rightShift(getInt(args[1])));
  106. registerFunction("<<", (args, qd) ->
  107. ((Fraction) args[0]).leftShift(getInt(args[1])));
  108. registerFunction("&", (args, qd) ->
  109. ((Fraction) args[0]).and((Fraction) args[1]));
  110. registerFunction("|", (args, qd) ->
  111. ((Fraction) args[0]).or((Fraction) args[1]));
  112. registerFunction("^", (args, qd) ->
  113. ((Fraction) args[0]).xor((Fraction) args[1]));
  114. registerFunction("~", (args, qd) ->
  115. ((Fraction) args[0]).invertBits());
  116. registerFunction("bit.set", (args, qd) ->
  117. ((Fraction) args[0]).setBit(getInt(args[1])));
  118. registerFunction("bit.unset", (args, qd) ->
  119. ((Fraction) args[0]).unsetBit(getInt(args[1])));
  120. registerFunction("bit.get", (args, qd) ->
  121. ((Fraction) args[0]).getBit(getInt(args[1])));
  122. // -------------------------------------------------------------------------------
  123. // math
  124. // -------------------------------------------------------------------------------
  125. registerFunction("%", (args, sc) ->
  126. new Fraction(getInt(args[0]) % getInt(args[1])));
  127. registerAlias("math.mod", "%");
  128. registerFunction("math.abs", (args, sc) ->
  129. ((Fraction) args[0]).abs());
  130. registerFunction("math.pow", (args, sc) ->
  131. ((Fraction) args[0]).power((Fraction) args[1]));
  132. registerFunction("math.root", (args, sc) ->
  133. ((Fraction) args[0]).power(((Fraction) args[1]).invert()));
  134. registerFunction("math.sin", (args, sc) ->
  135. ((Fraction) args[0]).sin());
  136. registerFunction("math.cos", (args, sc) ->
  137. ((Fraction) args[0]).cos());
  138. registerFunction("math.tan", (args, sc) ->
  139. ((Fraction) args[0]).tan());
  140. registerFunction("math.asin", (args, sc) ->
  141. ((Fraction) args[0]).asin());
  142. registerFunction("math.acos", (args, sc) ->
  143. ((Fraction) args[0]).acos());
  144. registerFunction("math.atan", (args, sc) ->
  145. ((Fraction) args[0]).atan());
  146. registerFunction("math.e", (args, sc) ->
  147. Fraction.E);
  148. registerFunction("math.pi", (args, sc) ->
  149. Fraction.PI);
  150. registerFunction("math.ln", (args, sc) ->
  151. ((Fraction) args[0]).log());
  152. registerFunction("math.log", (args, sc) ->
  153. ((Fraction) args[0]).log());
  154. registerFunction("math.random", (args, sc) ->
  155. new Fraction(ScriptUtils.randomInt(getInt(args[0]), getInt(args[1]))));
  156. registerFunction("math.round", (args, sc) ->
  157. ((Fraction) args[0]).round());
  158. registerFunction("math.rounddown", (args, sc) ->
  159. ((Fraction) args[0]).floor());
  160. registerFunction("math.roundup", (args, sc) ->
  161. ((Fraction) args[0]).ceil());
  162. registerFunction("math.roundcomma", (args, sc) ->
  163. ((Fraction) args[0]).round(getInt(args[1])));
  164. // -------------------------------------------------------------------------------
  165. // lists
  166. // -------------------------------------------------------------------------------
  167. registerConsumer("list.new", (args, sc) ->
  168. sc.setVar(args[0].toString(), new ArrayList<>()));
  169. registerFunction("list.exists", (args, sc) ->
  170. args[0] instanceof List);
  171. registerConsumer("list.add", (args, sc) ->
  172. ((List) args[0]).add(args[1]));
  173. registerConsumer("list.remove", (args, sc) ->
  174. ((List) args[0]).remove(args[1]));
  175. registerConsumer("list.removeindex", (args, sc) ->
  176. ((List) args[0]).remove(getInt(args[1])));
  177. registerFunction("list.contains", (args, sc) ->
  178. ((List) args[0]).contains(args[1]));
  179. registerFunction("list.getsize", (args, sc) ->
  180. new Fraction(((List) args[0]).size()));
  181. registerFunction("list.getindex", (args, sc) ->
  182. ((List) args[0]).get(getInt(args[1])));
  183. registerConsumer("list.setindex", (args, sc) ->
  184. ((List) args[0]).set(getInt(args[1]), args[2]));
  185. registerConsumer("list.clear", (args, sc) ->
  186. ((List) args[0]).clear());
  187. registerFunction("list.getindexof", (args, sc) ->
  188. new Fraction(((List) args[0]).indexOf(args[1])));
  189. registerConsumer("list.sort", (args, sc) ->
  190. sortList((List<Object>) args[0], sc));
  191. registerConsumer("list.reverse", (args, sc) ->
  192. Collections.reverse((List<Object>) args[0]));
  193. registerConsumer("list.shuffle", (args, sc) ->
  194. Collections.shuffle((List<Object>) args[0]));
  195. // -------------------------------------------------------------------------------
  196. // arrays
  197. // -------------------------------------------------------------------------------
  198. registerConsumer("array.new", (args, sc) ->
  199. sc.setVar(args[0].toString(), new Object[ScriptUtils.getInt(args[1])]));
  200. registerConsumer("array.=", (args, sc) ->
  201. ((Object[]) sc.getVar(args[0].toString()))[ScriptUtils.getInt(args[1])] = args[2]);
  202. registerAlias("array.set", "array.=");
  203. registerFunction("array.get", (args, sc) ->
  204. ((Object[]) sc.getVar(args[0].toString()))[ScriptUtils.getInt(args[1])]);
  205. registerFunction("array.++", (args, sc) ->
  206. {
  207. Object[] o = (Object[]) sc.getVar(args[0].toString());
  208. int i = ScriptUtils.getInt(args[1]);
  209. o[i] = ((Fraction) o[i]).add(new Fraction(1));
  210. return o[i];
  211. });
  212. registerFunction("array.--", (args, sc) ->
  213. {
  214. Object[] o = (Object[]) sc.getVar(args[0].toString());
  215. int i = ScriptUtils.getInt(args[1]);
  216. o[i] = ((Fraction) o[i]).sub(new Fraction(1));
  217. return o[i];
  218. });
  219. registerFunction("array.p+", (args, sc) ->
  220. {
  221. Object[] o = (Object[]) sc.getVar(args[0].toString());
  222. int i = ScriptUtils.getInt(args[1]);
  223. Fraction f = (Fraction) o[i];
  224. o[i] = f.add(new Fraction(1));
  225. return f;
  226. });
  227. registerFunction("array.p-", (args, sc) ->
  228. {
  229. Object[] o = (Object[]) sc.getVar(args[0].toString());
  230. int i = ScriptUtils.getInt(args[1]);
  231. Fraction f = (Fraction) o[i];
  232. o[i] = f.sub(new Fraction(1));
  233. return f;
  234. });
  235. registerConsumer("array.+=", (args, sc) ->
  236. {
  237. Object[] o = (Object[]) sc.getVar(args[0].toString());
  238. int i = ScriptUtils.getInt(args[1]);
  239. o[i] = ((Fraction) o[i]).add((Fraction) args[2]);
  240. });
  241. registerConsumer("array.-=", (args, sc) ->
  242. {
  243. Object[] o = (Object[]) sc.getVar(args[0].toString());
  244. int i = ScriptUtils.getInt(args[1]);
  245. o[i] = ((Fraction) o[i]).sub((Fraction) args[2]);
  246. });
  247. registerConsumer("array.*=", (args, sc) ->
  248. {
  249. Object[] o = (Object[]) sc.getVar(args[0].toString());
  250. int i = ScriptUtils.getInt(args[1]);
  251. o[i] = ((Fraction) o[i]).mul((Fraction) args[2]);
  252. });
  253. registerConsumer("array./=", (args, sc) ->
  254. {
  255. Object[] o = (Object[]) sc.getVar(args[0].toString());
  256. int i = ScriptUtils.getInt(args[1]);
  257. o[i] = ((Fraction) o[i]).div((Fraction) args[2]);
  258. });
  259. registerConsumer("array.%=", (args, sc) ->
  260. {
  261. Object[] o = (Object[]) sc.getVar(args[0].toString());
  262. int i = ScriptUtils.getInt(args[1]);
  263. o[i] = ScriptUtils.getInt(o[i]) % ScriptUtils.getInt(args[2]);
  264. });
  265. registerConsumer("array.<<=", (args, sc) ->
  266. {
  267. Object[] o = (Object[]) sc.getVar(args[0].toString());
  268. int i = ScriptUtils.getInt(args[1]);
  269. o[i] = ((Fraction) o[i]).leftShift(ScriptUtils.getInt(args[2]));
  270. });
  271. registerConsumer("array.>>=", (args, sc) ->
  272. {
  273. Object[] o = (Object[]) sc.getVar(args[0].toString());
  274. int i = ScriptUtils.getInt(args[1]);
  275. o[i] = ((Fraction) o[i]).rightShift(ScriptUtils.getInt(args[2]));
  276. });
  277. registerConsumer("array.&=", (args, sc) ->
  278. {
  279. Object[] o = (Object[]) sc.getVar(args[0].toString());
  280. int i = ScriptUtils.getInt(args[1]);
  281. o[i] = ((Fraction) o[i]).and((Fraction) args[2]);
  282. });
  283. registerConsumer("array.^=", (args, sc) ->
  284. {
  285. Object[] o = (Object[]) sc.getVar(args[0].toString());
  286. int i = ScriptUtils.getInt(args[1]);
  287. o[i] = ((Fraction) o[i]).xor((Fraction) args[2]);
  288. });
  289. registerConsumer("array.|=", (args, sc) ->
  290. {
  291. Object[] o = (Object[]) sc.getVar(args[0].toString());
  292. int i = ScriptUtils.getInt(args[1]);
  293. o[i] = ((Fraction) o[i]).or((Fraction) args[2]);
  294. });
  295. registerFunction("array.getsize", (args, sc) ->
  296. ((Object[]) args[0]).length);
  297. registerConsumer("array.swap", (args, sc) ->
  298. {
  299. Object[] o = (Object[]) args[0];
  300. int first = ScriptUtils.getInt(args[1]);
  301. int sec = ScriptUtils.getInt(args[2]);
  302. Object helper = o[first];
  303. o[first] = o[sec];
  304. o[sec] = helper;
  305. });
  306. registerConsumer("array.sort", (args, sc) ->
  307. {
  308. if(args.length <= 1)
  309. {
  310. Arrays.sort((Object[]) args[0]);
  311. }
  312. else
  313. {
  314. Arrays.sort((Object[]) args[0], ScriptUtils.getInt(args[1]), ScriptUtils.getInt(args[2]));
  315. }
  316. });
  317. registerConsumer("array.copy", (args, sc) ->
  318. {
  319. int first = ScriptUtils.getInt(args[2]);
  320. System.arraycopy((Object[]) args[0], first, (Object[]) args[1],
  321. ScriptUtils.getInt(args[4]), ScriptUtils.getInt(args[3]) - first + 1);
  322. });
  323. registerConsumer("array.rsort", (args, sc) ->
  324. {
  325. if(args.length <= 1)
  326. {
  327. Arrays.sort((Object[]) args[0], (Object o, Object o1) -> -((Comparable) o).compareTo(o));
  328. }
  329. else
  330. {
  331. Arrays.sort((Object[]) args[0], ScriptUtils.getInt(args[1]),
  332. ScriptUtils.getInt(args[2]), (Object o, Object o1) -> -((Comparable) o).compareTo(o));
  333. }
  334. });
  335. registerConsumer("array.fill", (args, sc) ->
  336. {
  337. if(args.length <= 2)
  338. {
  339. Arrays.fill((Object[]) args[0], args[1]);
  340. }
  341. else
  342. {
  343. Arrays.fill((Object[]) args[0], ScriptUtils.getInt(args[2]), ScriptUtils.getInt(args[3]), args[1]);
  344. }
  345. });
  346. // -------------------------------------------------------------------------------
  347. // maps
  348. // -------------------------------------------------------------------------------
  349. registerConsumer("map.new", (args, sc) ->
  350. sc.setVar(args[0].toString(), new HashMap<>()));
  351. registerFunction("map.exists", (args, sc) ->
  352. args[0] instanceof Map);
  353. registerConsumer("map.add", (args, sc) ->
  354. ((HashMap) args[0]).put(args[1], args[2]));
  355. registerConsumer("map.remove", (args, sc) ->
  356. ((HashMap) args[0]).remove(args[1]));
  357. registerFunction("map.contains", (args, sc) ->
  358. ((HashMap) args[0]).containsKey(args[1]));
  359. registerFunction("map.getsize", (args, sc) ->
  360. new Fraction(((HashMap) args[0]).size()));
  361. registerFunction("map.get", (args, sc) ->
  362. ((HashMap) args[0]).get(args[1]));
  363. registerConsumer("map.clear", (args, sc) ->
  364. ((HashMap) args[0]).clear());
  365. registerConsumer("map.keys", (args, sc) ->
  366. sc.setVar(args[0].toString(), ((HashMap) args[1]).keySet().stream().collect(Collectors.toList())));
  367. registerConsumer("map.values", (args, sc) ->
  368. sc.setVar(args[0].toString(), ((HashMap) args[1]).values().stream().collect(Collectors.toList())));
  369. // -------------------------------------------------------------------------------
  370. // sets
  371. // -------------------------------------------------------------------------------
  372. registerConsumer("set.new", (args, sc) ->
  373. sc.setVar(args[0].toString(), new HashSet<>()));
  374. registerFunction("set.exists", (args, sc) ->
  375. args[0] instanceof Set);
  376. registerConsumer("set.add", (args, sc) ->
  377. ((HashSet) args[0]).add(args[1]));
  378. registerConsumer("set.remove", (args, sc) ->
  379. ((HashSet) args[0]).remove(args[1]));
  380. registerFunction("set.contains", (args, sc) ->
  381. ((HashSet) args[0]).contains(args[1]));
  382. registerFunction("set.getsize", (args, sc) ->
  383. new Fraction(((HashSet) args[0]).size()));
  384. registerConsumer("set.tolist", (args, sc) ->
  385. sc.setVar(args[0].toString(), ((HashSet) args[1]).stream().collect(Collectors.toList())));
  386. // -------------------------------------------------------------------------------
  387. // time
  388. // -------------------------------------------------------------------------------
  389. registerFunction("time.get", (args, sc) ->
  390. new Fraction(System.currentTimeMillis()));
  391. registerFunction("time.nextday", (args, sc) ->
  392. getNextDay(args));
  393. registerFunction("time.getyear", (args, sc) ->
  394. getYear(args));
  395. registerFunction("time.getmonth", (args, sc) ->
  396. getMonth(args));
  397. registerFunction("time.getday", (args, sc) ->
  398. getDay(args));
  399. registerFunction("time.gethour", (args, sc) ->
  400. getHour(args));
  401. registerFunction("time.getminute", (args, sc) ->
  402. getMinute(args));
  403. registerFunction("time.getsecond", (args, sc) ->
  404. getSecond(args));
  405. // -------------------------------------------------------------------------------
  406. // text
  407. // -------------------------------------------------------------------------------
  408. registerFunction("text.number", (args, sc) ->
  409. fractionToDoubleString((Fraction) args[0]));
  410. registerFunction("text.class", (args, sc) ->
  411. args[0].getClass().getSimpleName());
  412. registerFunction("text.tolowercase", (args, sc) ->
  413. ScriptUtils.connect(args, 0).toLowerCase());
  414. registerAlias("tolowercase", "text.tolowercase");
  415. registerFunction("text.touppercase", (args, sc) ->
  416. ScriptUtils.connect(args, 0).toUpperCase());
  417. registerAlias("touppercase", "text.touppercase");
  418. registerConsumer("text.split", (args, sc) ->
  419. split(args, sc));
  420. registerAlias("split", "text.split");
  421. registerFunction("text.concatlist", (args, sc) ->
  422. ((List<Object>) args[0]).stream().limit(getInt(args[3]) + 1).skip(getInt(args[2])).map(o -> o.toString()).collect(Collectors.joining(args[1].toString())));
  423. registerAlias("concatlist", "text.concatlist");
  424. registerFunction("text.concat", (args, sc) ->
  425. ScriptUtils.connect(args, 0));
  426. registerAlias("concat", "text.concat");
  427. registerFunction("text", (args, sc) ->
  428. String.valueOf(args[0]));
  429. registerFunction("text.substring", (args, sc) ->
  430. args[0].toString().substring(getInt(args[1]), getInt(args[2])));
  431. registerFunction("text.length", (args, sc) ->
  432. args[0].toString().length());
  433. registerFunction("text.startswith", (args, sc) ->
  434. args[0].toString().startsWith(args[1].toString(), getInt(args[2])));
  435. registerFunction("text.endswith", (args, sc) ->
  436. args[0].toString().endsWith(args[1].toString()));
  437. registerFunction("text.contains", (args, sc) ->
  438. args[0].toString().contains(args[1].toString()));
  439. registerFunction("text.indexof", (args, sc) ->
  440. args[0].toString().indexOf(args[1].toString(), getInt(args[2])));
  441. registerFunction("text.lastindexof", (args, sc) ->
  442. args[0].toString().lastIndexOf(args[1].toString(), getInt(args[2])));
  443. registerFunction("text.replace", (args, sc) ->
  444. args[0].toString().replace(args[1].toString(), args[2].toString()));
  445. registerFunction("text.trim", (args, sc) ->
  446. args[0].toString().trim());
  447. registerFunction("text.charat", (args, sc) ->
  448. String.valueOf(args[0].toString().charAt(getInt(args[1]))));
  449. registerFunction("text.charcode", (args, sc) ->
  450. new Fraction(args[0].toString().charAt(getInt(args[1]))));
  451. registerFunction("text.fromcode", (args, sc) ->
  452. new String(new char[] {(char) ScriptUtils.getInt(args[0])}));
  453. // -------------------------------------------------------------------------------
  454. // files
  455. // -------------------------------------------------------------------------------
  456. registerFunction("file.new", (args, sc) ->
  457. new File(args[0].toString()));
  458. registerFunction("file.exists", (args, sc) ->
  459. ((File) args[0]).exists());
  460. registerFunction("file.delete", (args, sc) ->
  461. ((File) args[0]).delete());
  462. registerFunction("file.getname", (args, sc) ->
  463. ((File) args[0]).getName());
  464. registerFunction("file.rename", (args, sc) ->
  465. ((File) args[0]).renameTo(new File(args[1].toString())));
  466. registerConsumer("file.getlist", (args, sc) ->
  467. sc.setVar(args[0].toString(), Arrays.asList(((File) args[0]).listFiles())));
  468. registerConsumer("file.read", (args, sc) ->
  469. readFile(args, sc));
  470. registerConsumer("file.write", (args, sc) ->
  471. writeFile(args, sc));
  472. // -------------------------------------------------------------------------------
  473. // commands without library
  474. // -------------------------------------------------------------------------------
  475. registerFunction("+", (args, sc) ->
  476. ((Fraction) args[0]).add((Fraction) args[1]));
  477. registerAlias("add", "+");
  478. registerFunction("-", (args, sc) ->
  479. ((Fraction) args[0]).sub((Fraction) args[1]));
  480. registerAlias("sub", "-");
  481. registerConsumer("+=", (args, sc) ->
  482. {
  483. String s = args[0].toString();
  484. sc.setVar(s, ((Fraction) sc.getVar(s)).add((Fraction) args[1]));
  485. });
  486. registerConsumer("-=", (args, sc) ->
  487. {
  488. String s = args[0].toString();
  489. sc.setVar(s, ((Fraction) sc.getVar(s)).sub((Fraction) args[1]));
  490. });
  491. registerConsumer("*=", (args, sc) ->
  492. {
  493. String s = args[0].toString();
  494. sc.setVar(s, ((Fraction) sc.getVar(s)).mul((Fraction) args[1]));
  495. });
  496. registerConsumer("/=", (args, sc) ->
  497. {
  498. String s = args[0].toString();
  499. sc.setVar(s, ((Fraction) sc.getVar(s)).div((Fraction) args[1]));
  500. });
  501. registerConsumer("%=", (args, sc) ->
  502. {
  503. String s = args[0].toString();
  504. sc.setVar(s, ScriptUtils.getInt(sc.getVar(s)) % ScriptUtils.getInt(args[1]));
  505. });
  506. registerConsumer("<<=", (args, sc) ->
  507. {
  508. String s = args[0].toString();
  509. sc.setVar(s, ((Fraction) sc.getVar(s)).leftShift(ScriptUtils.getInt(args[1])));
  510. });
  511. registerConsumer(">>=", (args, sc) ->
  512. {
  513. String s = args[0].toString();
  514. sc.setVar(s, ((Fraction) sc.getVar(s)).rightShift(ScriptUtils.getInt(args[1])));
  515. });
  516. registerConsumer("&=", (args, sc) ->
  517. {
  518. String s = args[0].toString();
  519. sc.setVar(s, ((Fraction) sc.getVar(s)).and((Fraction) args[1]));
  520. });
  521. registerConsumer("^=", (args, sc) ->
  522. {
  523. String s = args[0].toString();
  524. sc.setVar(s, ((Fraction) sc.getVar(s)).xor((Fraction) args[1]));
  525. });
  526. registerConsumer("|=", (args, sc) ->
  527. {
  528. String s = args[0].toString();
  529. sc.setVar(s, ((Fraction) sc.getVar(s)).or((Fraction) args[1]));
  530. });
  531. registerFunction("inc", (args, sc) ->
  532. {
  533. String s = args[0].toString();
  534. Fraction n = ((Fraction) sc.getVar(s)).add(new Fraction(1));
  535. sc.setVar(s, n);
  536. return n;
  537. });
  538. registerAlias("++", "inc");
  539. registerFunction("dec", (args, sc) ->
  540. {
  541. String s = args[0].toString();
  542. Fraction n = ((Fraction) sc.getVar(s)).add(new Fraction(-1));
  543. sc.setVar(s, n);
  544. return n;
  545. });
  546. registerAlias("--", "dec");
  547. registerFunction("p+", (args, sc) ->
  548. {
  549. String s = args[0].toString();
  550. Fraction n = (Fraction) sc.getVar(s);
  551. sc.setVar(s, n.add(new Fraction(1)));
  552. return n;
  553. });
  554. registerFunction("p-", (args, sc) ->
  555. {
  556. String s = args[0].toString();
  557. Fraction n = (Fraction) sc.getVar(s);
  558. sc.setVar(s, n.add(new Fraction(-1)));
  559. return n;
  560. });
  561. registerFunction("*", (args, sc) ->
  562. ((Fraction) args[0]).mul((Fraction) args[1]));
  563. registerAlias("mul", "*");
  564. registerFunction("/", (args, sc) ->
  565. ((Fraction) args[0]).div((Fraction) args[1]));
  566. registerAlias("div", "/");
  567. registerFunction("getvar", (args, sc) ->
  568. sc.getVar(args[0].toString()));
  569. registerConsumer("setvar", (args, sc) ->
  570. sc.setVar(args[0].toString(), args[1]));
  571. registerAlias("=", "setvar");
  572. registerConsumer("removevar", (args, sc) ->
  573. sc.removeVar(args[0].toString()));
  574. registerConsumer("reset", (args, sc) ->
  575. sc.resetLoopCounter());
  576. registerConsumer("wait", (args, sc) ->
  577. { sc.resetLoopCounter(); throw new HoldCodeException(); });
  578. // branching
  579. registerConsumer("goto", (args, sc) ->
  580. sc.gotoLabel(args[0].toString(), false, true));
  581. registerConsumer("sgoto", (args, sc) ->
  582. scheduleGoto(args, sc));
  583. registerConsumer("gosub", (args, sc) ->
  584. sc.gotoLabelWithReturn(args[0].toString()));
  585. // push / pop values for functions
  586. registerConsumer("pusharg", (args, sc) ->
  587. sc.pushFunctionInput(args));
  588. registerFunction("poparg", (args, sc) ->
  589. sc.popFunctionInput());
  590. // push / pop values for saving
  591. registerConsumer("push", (args, sc) ->
  592. sc.push(0, args));
  593. registerConsumer("pop", (args, sc) ->
  594. sc.pop(args));
  595. registerConsumer("return", (args, sc) ->
  596. sc.doReturn());
  597. registerConsumer("try", (args, sc) ->
  598. sc.saveTryJumpLine());
  599. registerConsumer("catch", (args, sc) ->
  600. {
  601. sc.resetTryJumpLine();
  602. sc.jump();
  603. });
  604. registerConsumer("if", (args, sc) ->
  605. ifFunction(args, sc));
  606. registerConsumer("else", (args, sc) ->
  607. sc.jump());
  608. registerConsumer("while", (args, sc) ->
  609. whileFunction(args, sc));
  610. registerConsumer("for", (args, sc) ->
  611. {
  612. Fraction f = sc.nextRepeatData((Fraction) args[1], (Fraction) args[3]);
  613. sc.setVar(args[0].toString(), f);
  614. if(sc.repeatFinished(f, (Fraction) args[3], (Fraction) args[2]))
  615. {
  616. sc.clearRepeatData();
  617. sc.jump();
  618. }
  619. });
  620. registerConsumer("break", (args, sc) ->
  621. sc.jump());
  622. registerConsumer("breakreset", (args, sc) ->
  623. {
  624. sc.jump();
  625. sc.clearLastRepeatData();
  626. });
  627. registerConsumer("continue", (args, sc) ->
  628. sc.jump());
  629. registerFunction("==", (args, sc) ->
  630. isEqual(args));
  631. registerAlias("equal", "==");
  632. registerAlias("equals", "==");
  633. registerFunction("<", (args, sc) ->
  634. ((Fraction) args[0]).compareTo((Fraction) args[1]) < 0);
  635. registerAlias("less", "<");
  636. registerFunction(">", (args, sc) ->
  637. ((Fraction) args[0]).compareTo((Fraction) args[1]) > 0);
  638. registerAlias("greater", ">");
  639. registerFunction("!=", (args, sc) ->
  640. !isEqual(args));
  641. registerAlias("notequal", "!=");
  642. registerFunction("<=", (args, sc) ->
  643. ((Fraction) args[0]).compareTo((Fraction) args[1]) <= 0);
  644. registerAlias("lessequal", "<=");
  645. registerFunction(">=", (args, sc) ->
  646. ((Fraction) args[0]).compareTo((Fraction) args[1]) >= 0);
  647. registerAlias("greaterequal", ">=");
  648. registerFunction("!", (args, sc) ->
  649. !((boolean) args[0]));
  650. registerAlias("invert", "!");
  651. registerFunction("&&", (args, sc) ->
  652. Arrays.stream(args).allMatch(s -> s.equals(true)));
  653. registerAlias("and", "&&");
  654. registerFunction("||", (args, sc) ->
  655. Arrays.stream(args).anyMatch(s -> s.equals(true)));
  656. registerAlias("or", "||");
  657. registerConsumer("waitfor", (args, sc) ->
  658. waitFor(args, sc));
  659. registerConsumer("term", (args, sc) ->
  660. { termSafe(sc); throw new HoldCodeException(); });
  661. registerConsumer("swap", (args, sc) ->
  662. {
  663. String first = args[0].toString();
  664. String sec = args[1].toString();
  665. Object helper = sc.getVar(first);
  666. sc.setVar(first, sc.getVar(sec));
  667. sc.setVar(sec, helper);
  668. });
  669. }
  670. @SuppressWarnings("unchecked")
  671. protected Object parseFunction(Script sc, String function, Object[] args) throws HoldCodeException
  672. {
  673. try
  674. {
  675. BiFunction<Object[], Script, Object> bif = functions.get(function);
  676. if(bif == null)
  677. {
  678. try
  679. {
  680. sc.gotoLabelWithReturn(function);
  681. sc.pushFunctionInput(args, new Fraction(args.length));
  682. return Void.TYPE;
  683. }
  684. catch(GotoLabelNotFoundException ex)
  685. {
  686. throw new NoSuchMethodException(function);
  687. }
  688. }
  689. return bif.apply(args, sc);
  690. }
  691. catch(HoldCodeException ex)
  692. {
  693. throw ex;
  694. }
  695. catch(Exception ex)
  696. {
  697. if(printStack)
  698. {
  699. ex.printStackTrace();
  700. }
  701. if(sc.getTryJumpLine() != -1)
  702. {
  703. sc.setVar("error", ex.getClass().getSimpleName());
  704. sc.gotoTryJumpLine();
  705. sc.resetTryJumpLine();
  706. return Void.TYPE;
  707. }
  708. logger.printException(ex, function, sc, sc.getActiveRealCodeLine());
  709. sc.resetLoopCounter();
  710. throw new HoldCodeException();
  711. }
  712. }
  713. // -----------------------------------------------------------------------------------
  714. // script controller
  715. // -----------------------------------------------------------------------------------
  716. public Script getScript(int id)
  717. {
  718. return scripts.get(id);
  719. }
  720. public boolean termUnsafe(Script sc)
  721. {
  722. if(sc == null)
  723. {
  724. return false;
  725. }
  726. sc.setInvalid();
  727. sc.onTerm();
  728. return scripts.remove(sc.getId()) != null;
  729. }
  730. public void termSafe(Script sc)
  731. {
  732. if(sc == null)
  733. {
  734. return;
  735. }
  736. sc.setInvalid();
  737. termQueue.add(sc.getId());
  738. }
  739. private void term()
  740. {
  741. if(!termQueue.isEmpty())
  742. {
  743. termQueue.forEach(i ->
  744. {
  745. Script sc = scripts.remove(i);
  746. if(sc != null)
  747. {
  748. sc.onTerm();
  749. }
  750. });
  751. termQueue.clear();
  752. }
  753. }
  754. public void termAllUnsafe()
  755. {
  756. scripts.values().forEach(sc ->
  757. {
  758. sc.onTerm();
  759. sc.setInvalid();
  760. });
  761. scripts.clear();
  762. }
  763. public Collection<Script> getScripts()
  764. {
  765. return scripts.values();
  766. }
  767. private Script startScript(Script script, Object... o)
  768. {
  769. try
  770. {
  771. script.initExpansion(o);
  772. scripts.put(idCounter, script);
  773. idCounter++;
  774. script.runCode();
  775. term();
  776. return script;
  777. }
  778. catch(PreScriptException ex)
  779. {
  780. logger.printException(ex, null, script, ex.getLine());
  781. return null;
  782. }
  783. }
  784. public Script startScript(String scriptName, String code)
  785. {
  786. try
  787. {
  788. return startScript(new Script(this, idCounter, scriptName, code));
  789. }
  790. catch(PreScriptException ex)
  791. {
  792. logger.printException(ex, null, scriptName, ex.getLine());
  793. return null;
  794. }
  795. }
  796. @SuppressWarnings(value = "unchecked")
  797. public <T extends Script> T startScript(Class<T> c, String scriptName, String code, boolean b, Object... o)
  798. {
  799. try
  800. {
  801. return (T) startScript(c.getDeclaredConstructor(
  802. SnuviParser.class, int.class, String.class, String.class, boolean.class)
  803. .newInstance(this, idCounter, scriptName, code, b), o);
  804. }
  805. catch(IllegalAccessException | IllegalArgumentException | InstantiationException |
  806. InvocationTargetException | NoSuchMethodException | SecurityException ex)
  807. {
  808. if(ex instanceof InvocationTargetException)
  809. {
  810. Throwable t = ((InvocationTargetException) ex).getCause();
  811. if(t != null && t instanceof Exception)
  812. {
  813. if(t instanceof PreScriptException)
  814. {
  815. logger.printException((Exception) t, null, scriptName, ((PreScriptException) t).getLine());
  816. return null;
  817. }
  818. logger.printException((Exception) t, null, scriptName, -1);
  819. return null;
  820. }
  821. }
  822. ex.printStackTrace();
  823. return null;
  824. }
  825. }
  826. // -----------------------------------------------------------------------------------
  827. // event
  828. // -----------------------------------------------------------------------------------
  829. public void callEvent(String name, Consumer<Script> before, Consumer<Script> after, Predicate<Script> check)
  830. {
  831. scripts.values().stream()
  832. .filter(sc -> sc.receiveEventBroadcast && !sc.isHalt() && !sc.isRunning())
  833. .filter(sc -> sc.isLoadedEvent(name))
  834. .filter(check)
  835. .forEach(sc ->
  836. {
  837. sc.setVar("event", name);
  838. if(before != null)
  839. {
  840. before.accept(sc);
  841. }
  842. sc.runCode();
  843. if(after != null)
  844. {
  845. after.accept(sc);
  846. }
  847. sc.eventVars.forEach(s -> sc.removeVar(s));
  848. sc.eventVars.clear();
  849. });
  850. term();
  851. }
  852. public void callEvent(String name, Consumer<Script> before, Consumer<Script> after)
  853. {
  854. callEvent(name, before, after, sc -> true);
  855. }
  856. public boolean callEvent(String name, Script sc, Consumer<Script> before, Consumer<Script> after, boolean check)
  857. {
  858. if(sc.isLoadedEvent(name) && !sc.isHalt() && !sc.isRunning() && check)
  859. {
  860. sc.setVar("event", name);
  861. if(before != null)
  862. {
  863. before.accept(sc);
  864. }
  865. sc.runCode();
  866. if(after != null)
  867. {
  868. after.accept(sc);
  869. }
  870. sc.eventVars.forEach(s -> sc.removeVar(s));
  871. sc.eventVars.clear();
  872. term();
  873. return true;
  874. }
  875. return false;
  876. }
  877. public boolean callEvent(String name, Script sc, Consumer<Script> before, Consumer<Script> after)
  878. {
  879. return callEvent(name, sc, before, after, true);
  880. }
  881. // -----------------------------------------------------------------------------------
  882. // functions
  883. // -----------------------------------------------------------------------------------
  884. private String fractionToDoubleString(Fraction f)
  885. {
  886. if(f.doubleValue() == f.longValue())
  887. {
  888. return String.valueOf(f.longValue());
  889. }
  890. return String.valueOf(f.doubleValue());
  891. }
  892. private void ifFunction(Object[] args, Script sc)
  893. {
  894. if(Arrays.stream(args).anyMatch(s -> !((boolean) s)))
  895. {
  896. sc.jumpDoElse();
  897. }
  898. }
  899. private void whileFunction(Object[] args, Script sc)
  900. {
  901. if(Arrays.stream(args).anyMatch(s -> !((boolean) s)))
  902. {
  903. sc.jump();
  904. }
  905. }
  906. @SuppressWarnings("unchecked")
  907. private void sortList(List<Object> args, Script sc)
  908. {
  909. Collections.sort(args, (Object o1, Object o2) -> ((Comparable) o1).compareTo(o2));
  910. }
  911. private void scheduleGoto(Object[] args, Script sc)
  912. {
  913. int time = getInt(args[0]);
  914. if(time < 0)
  915. {
  916. throw new IllegalArgumentException("time units can't be negative");
  917. }
  918. scheduler.scheduleTask(() ->
  919. {
  920. if(!sc.isValid() && !sc.isHalt())
  921. {
  922. return;
  923. }
  924. try
  925. {
  926. sc.gotoLabel(args[1].toString(), true, true);
  927. sc.runCode();
  928. }
  929. catch(Exception ex)
  930. {
  931. logger.printException(ex, "sgoto", sc, sc.getActiveRealCodeLine());
  932. }
  933. }, time);
  934. }
  935. @SuppressWarnings("")
  936. private void waitFor(Object[] args, Script sc) throws UnsupportedOperationException
  937. {
  938. sc.resetLoopCounter();
  939. long l = getLong(args[0]);
  940. if(l < 0)
  941. {
  942. throw new IllegalArgumentException("time units can't be negative");
  943. }
  944. sc.setHalt(true);
  945. scheduler.scheduleTask(() ->
  946. {
  947. if(sc == null || !sc.isValid())
  948. {
  949. return;
  950. }
  951. sc.setHalt(false);
  952. sc.runCode();
  953. }, l);
  954. throw new HoldCodeException();
  955. }
  956. private static boolean isEqual(Object[] args)
  957. {
  958. if(args[0] == null)
  959. {
  960. return args[1] == null;
  961. }
  962. else if(args[1] == null)
  963. {
  964. return false;
  965. }
  966. return args[0].equals(args[1]);
  967. }
  968. private void split(Object[] args, Script sc)
  969. {
  970. sc.setVar(args[0].toString(),
  971. Arrays.stream(ScriptUtils.connect(args, 2).split(args[1].toString()))
  972. .map(s -> Code.convertInput(null, s, false)).collect(Collectors.toList()));
  973. }
  974. private void readFile(Object[] args, Script sc)
  975. {
  976. try
  977. {
  978. sc.setVar(args[0].toString(), Files.readAllLines(((File) args[1]).toPath()));
  979. }
  980. catch(IOException ex)
  981. {
  982. throw new FileIOException(ex.getMessage());
  983. }
  984. }
  985. @SuppressWarnings(value = "unchecked")
  986. private void writeFile(Object[] args, Script sc)
  987. {
  988. try
  989. {
  990. File f = (File) args[0];
  991. if(!f.getParentFile().exists())
  992. {
  993. f.getParentFile().mkdirs();
  994. }
  995. if(!f.exists())
  996. {
  997. try
  998. {
  999. f.createNewFile();
  1000. }
  1001. catch(IOException ex)
  1002. {
  1003. throw new FileIOException(ex.getMessage());
  1004. }
  1005. }
  1006. Files.write(Paths.get(f.toURI()), ((List<Object>) args[1])
  1007. .stream().map(o -> String.valueOf(o)).collect(Collectors.toList()), StandardCharsets.UTF_8);
  1008. }
  1009. catch(UnsupportedOperationException | SecurityException | IOException ex)
  1010. {
  1011. throw new FileIOException(ex.getMessage());
  1012. }
  1013. }
  1014. // -----------------------------------------------------------------------------------
  1015. // number handlers, cause primitive types transform into their classes all the time
  1016. // -----------------------------------------------------------------------------------
  1017. public static int getInt(Object o)
  1018. {
  1019. return ScriptUtils.getInt(o);
  1020. }
  1021. public static long getLong(Object o)
  1022. {
  1023. return ScriptUtils.getLong(o);
  1024. }
  1025. public static double getDouble(Object o)
  1026. {
  1027. return ScriptUtils.getDouble(o);
  1028. }
  1029. // -----------------------------------------------------------------------------------
  1030. // time handler
  1031. // -----------------------------------------------------------------------------------
  1032. private Fraction getNextDay(Object[] args)
  1033. {
  1034. GregorianCalendar cal = GregorianCalendar.from(ZonedDateTime.now());
  1035. cal.setTimeInMillis(getLong(args[0]));
  1036. cal.add(Calendar.DAY_OF_YEAR, 1);
  1037. cal.set(Calendar.HOUR, 0);
  1038. cal.set(Calendar.SECOND, 0);
  1039. cal.set(Calendar.MINUTE, 0);
  1040. cal.set(Calendar.MILLISECOND, 0);
  1041. return new Fraction(cal.getTimeInMillis());
  1042. }
  1043. private Fraction getYear(Object[] args)
  1044. {
  1045. GregorianCalendar cal = GregorianCalendar.from(ZonedDateTime.now());
  1046. cal.setTimeInMillis(getLong(args[0]));
  1047. return new Fraction(cal.get(Calendar.YEAR));
  1048. }
  1049. private Fraction getMonth(Object[] args)
  1050. {
  1051. GregorianCalendar cal = GregorianCalendar.from(ZonedDateTime.now());
  1052. cal.setTimeInMillis(getLong(args[0]));
  1053. return new Fraction(cal.get(Calendar.MONTH) + 1);
  1054. }
  1055. private Fraction getDay(Object[] args)
  1056. {
  1057. GregorianCalendar cal = GregorianCalendar.from(ZonedDateTime.now());
  1058. cal.setTimeInMillis(getLong(args[0]));
  1059. return new Fraction(cal.get(Calendar.DAY_OF_MONTH));
  1060. }
  1061. private Fraction getHour(Object[] args)
  1062. {
  1063. GregorianCalendar cal = GregorianCalendar.from(ZonedDateTime.now());
  1064. cal.setTimeInMillis(getLong(args[0]));
  1065. return new Fraction(cal.get(Calendar.HOUR_OF_DAY));
  1066. }
  1067. private Fraction getMinute(Object[] args)
  1068. {
  1069. GregorianCalendar cal = GregorianCalendar.from(ZonedDateTime.now());
  1070. cal.setTimeInMillis(getLong(args[0]));
  1071. return new Fraction(cal.get(Calendar.MINUTE));
  1072. }
  1073. private Fraction getSecond(Object[] args)
  1074. {
  1075. GregorianCalendar cal = GregorianCalendar.from(ZonedDateTime.now());
  1076. cal.setTimeInMillis(getLong(args[0]));
  1077. return new Fraction(cal.get(Calendar.SECOND));
  1078. }
  1079. }