Code.java 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600
  1. package me.hammerle.code;
  2. import java.util.ArrayList;
  3. import java.util.HashMap;
  4. import java.util.Stack;
  5. import me.hammerle.exceptions.PreScriptException;
  6. import me.hammerle.math.Fraction;
  7. public class Code
  8. {
  9. protected final int realLine;
  10. protected final String function;
  11. protected final int pars;
  12. protected final byte layer;
  13. protected Object value;
  14. private Code(String function, int pars, Object value, int realLine, byte layer)
  15. {
  16. this.function = function;
  17. this.pars = pars;
  18. this.value = value;
  19. this.realLine = realLine;
  20. this.layer = layer;
  21. }
  22. public Code(String function, int pars, int realLine, byte layer)
  23. {
  24. this(function, pars, null, realLine, layer);
  25. }
  26. public Code(Object value, int realLine, byte layer)
  27. {
  28. this(null, 0, value, realLine, layer);
  29. }
  30. public void executeFunction(SnuviParser parser, Script sc, Stack<Object> stack)
  31. {
  32. if(function == null)
  33. {
  34. if(value != null)
  35. {
  36. if(value.getClass() == Variable.class)
  37. {
  38. stack.push(sc.getVar(((Variable) value).getName()));
  39. return;
  40. }
  41. stack.push(value);
  42. return;
  43. }
  44. stack.push(value);
  45. return;
  46. }
  47. Object[] input;
  48. if(pars > 0)
  49. {
  50. input = new Object[pars];
  51. for(int i = 0; i < pars; i++)
  52. {
  53. input[i] = stack.pop();
  54. }
  55. }
  56. else
  57. {
  58. input = new Object[0];
  59. }
  60. Object output = parser.parseFunction(sc, function, input);
  61. if(output != Void.TYPE)
  62. {
  63. stack.push(output);
  64. }
  65. }
  66. @Override
  67. public String toString()
  68. {
  69. StringBuilder sb = new StringBuilder();
  70. sb.append(realLine);
  71. sb.append("| ");
  72. for(int i = -1; i < layer; i++)
  73. {
  74. sb.append('>');
  75. }
  76. sb.append(' ');
  77. if(function != null)
  78. {
  79. sb.append("call ");
  80. sb.append(function);
  81. sb.append(' ');
  82. sb.append(pars);
  83. if(value != null)
  84. {
  85. sb.append(" (");
  86. sb.append(value);
  87. sb.append(")");
  88. }
  89. }
  90. else
  91. {
  92. sb.append("push ");
  93. if(value != null)
  94. {
  95. sb.append(value.getClass().getSimpleName());
  96. }
  97. sb.append(" '");
  98. sb.append(value);
  99. sb.append("'");
  100. return sb.toString();
  101. }
  102. return sb.toString();
  103. }
  104. // -----------------------------------------------------------------------------------
  105. // code builder
  106. // -----------------------------------------------------------------------------------
  107. private static String scriptName = null;
  108. private static int findFirstNonLetterOrDigit(StringBuilder code, int pos)
  109. {
  110. int length = code.length();
  111. while(pos < length)
  112. {
  113. if(!Character.isLetterOrDigit(code.charAt(pos)) && code.charAt(pos) != '_')
  114. {
  115. return pos;
  116. }
  117. pos++;
  118. }
  119. return -1;
  120. }
  121. private static int findChar(char c, StringBuilder code, int pos)
  122. {
  123. int length = code.length();
  124. while(pos < length)
  125. {
  126. if(code.charAt(pos) == c)
  127. {
  128. return pos;
  129. }
  130. pos++;
  131. }
  132. return -1;
  133. }
  134. private static int findSyntaxIgnoreString(String find, StringBuilder code, int pos)
  135. {
  136. int length = code.length();
  137. int add = find.length();
  138. char c;
  139. boolean text = false;
  140. while(pos < length)
  141. {
  142. c = code.charAt(pos);
  143. if(text && c != '"')
  144. {
  145. pos++;
  146. continue;
  147. }
  148. else if(c == '"')
  149. {
  150. text = !text;
  151. pos++;
  152. continue;
  153. }
  154. else if(code.substring(pos, Math.min(pos + add, length)).equals(find))
  155. {
  156. return pos;
  157. }
  158. pos++;
  159. }
  160. return -1;
  161. }
  162. public static int countChar(char find, int end, StringBuilder code)
  163. {
  164. int counter = 0;
  165. int pos = 0;
  166. end = Math.min(end, code.length());
  167. while(pos < end)
  168. {
  169. if(code.charAt(pos) == find)
  170. {
  171. counter++;
  172. }
  173. pos++;
  174. }
  175. return counter;
  176. }
  177. @SuppressWarnings("")
  178. private static String removeNonSyntax(String s)
  179. {
  180. StringBuilder sb = new StringBuilder(s);
  181. int pos = 0;
  182. while(pos < sb.length())
  183. {
  184. switch(sb.charAt(pos))
  185. {
  186. case ' ':
  187. case '\t':
  188. case '\n':
  189. sb.deleteCharAt(pos);
  190. continue;
  191. }
  192. pos++;
  193. }
  194. return sb.toString();
  195. }
  196. private static void removeComments(StringBuilder sb)
  197. {
  198. int old = 0;
  199. int pos;
  200. while(true)
  201. {
  202. old = findSyntaxIgnoreString("/*", sb, old);
  203. if(old == -1)
  204. {
  205. break;
  206. }
  207. pos = findSyntaxIgnoreString("*/", sb, old);
  208. if(pos == -1)
  209. {
  210. throw new PreScriptException(scriptName, countChar('\n', old, sb), "/* without */");
  211. }
  212. sb.delete(old, pos + 2);
  213. }
  214. old = 0;
  215. while(true)
  216. {
  217. old = findSyntaxIgnoreString("//", sb, old);
  218. if(old == -1)
  219. {
  220. break;
  221. }
  222. pos = findSyntaxIgnoreString("\n", sb, old);
  223. if(pos == -1)
  224. {
  225. sb.delete(old, sb.length());
  226. break;
  227. }
  228. sb.delete(old, pos + 1);
  229. }
  230. }
  231. private static HashMap<String, String> replaceStrings(StringBuilder sb)
  232. {
  233. int old;
  234. int pos = 0;
  235. String replacement;
  236. String text;
  237. int stringIndex = 0;
  238. HashMap<String, String> strings = new HashMap<>();
  239. while(pos < sb.length())
  240. {
  241. if(sb.charAt(pos) == '"')
  242. {
  243. old = pos;
  244. pos++;
  245. while(sb.charAt(pos) != '"')
  246. {
  247. pos++;
  248. if(pos >= sb.length())
  249. {
  250. throw new PreScriptException(scriptName, countChar('\n', old, sb), "\" without another");
  251. }
  252. }
  253. pos++;
  254. text = sb.substring(old, pos);
  255. replacement = "#" + stringIndex;
  256. sb.replace(old, pos, replacement);
  257. strings.put(replacement, text);
  258. stringIndex++;
  259. pos -= (pos - old) - replacement.length();
  260. }
  261. pos++;
  262. }
  263. return strings;
  264. }
  265. private static void formatLabels(StringBuilder sb)
  266. {
  267. int pos;
  268. int old = 0;
  269. while(true)
  270. {
  271. pos = findChar('@', sb, old);
  272. if(pos == -1)
  273. {
  274. break;
  275. }
  276. pos++;
  277. old = pos;
  278. pos = findFirstNonLetterOrDigit(sb, pos);
  279. if(pos == -1)
  280. {
  281. break;
  282. }
  283. if(sb.charAt(pos) != ';')
  284. {
  285. sb.insert(pos, ';');
  286. }
  287. }
  288. }
  289. public static Code[] generate(SnuviParser parser, String scriptName, String code, HashMap<String, Integer> gotos)
  290. {
  291. System.out.print("Starting '" + scriptName + "' ... ");
  292. long startTime = System.currentTimeMillis();
  293. Code.scriptName = scriptName;
  294. StringBuilder sb = new StringBuilder(code);
  295. // ---------------------------------------------------------------------
  296. // comments
  297. // ---------------------------------------------------------------------
  298. removeComments(sb);
  299. // ---------------------------------------------------------------------
  300. // replacing Strings with #... to get rid of "
  301. // ---------------------------------------------------------------------
  302. HashMap<String, String> strings = replaceStrings(sb);
  303. // ---------------------------------------------------------------------
  304. // allowing labels without ;
  305. // ---------------------------------------------------------------------
  306. formatLabels(sb);
  307. code = sb.toString();
  308. // ---------------------------------------------------------------------
  309. // split code into lines
  310. // ---------------------------------------------------------------------
  311. ArrayList<String> lines = new ArrayList<>();
  312. int lineCounter = 1;
  313. ArrayList<Integer> realCodeLine = new ArrayList<>();
  314. int old = 0;
  315. int pos = 0;
  316. int length = code.length();
  317. int counter = 0;
  318. int bracketCounter = 0;
  319. boolean closed = true;
  320. Stack<Integer> brackets = new Stack<>();
  321. while(pos < length)
  322. {
  323. switch(code.charAt(pos))
  324. {
  325. case '(':
  326. closed = false;
  327. bracketCounter++;
  328. break;
  329. case ')':
  330. bracketCounter--;
  331. break;
  332. case ';':
  333. if(bracketCounter != 0)
  334. {
  335. throw new PreScriptException(scriptName, lineCounter, "unbalanced ()");
  336. }
  337. lines.add(removeNonSyntax(code.substring(old, pos)));
  338. realCodeLine.add(lineCounter);
  339. old = pos + 1;
  340. closed = true;
  341. break;
  342. case '{':
  343. if(bracketCounter != 0)
  344. {
  345. throw new PreScriptException(scriptName, lineCounter, "unbalanced ()");
  346. }
  347. brackets.push(lineCounter);
  348. counter++;
  349. lines.add(removeNonSyntax(code.substring(old, pos)));
  350. realCodeLine.add(lineCounter);
  351. old = pos + 1;
  352. lines.add("{");
  353. realCodeLine.add(lineCounter);
  354. break;
  355. case '}':
  356. if(!closed)
  357. {
  358. throw new PreScriptException(scriptName, lineCounter, "missing ;");
  359. }
  360. else if(bracketCounter != 0)
  361. {
  362. throw new PreScriptException(scriptName, lineCounter, "unbalanced ()");
  363. }
  364. counter--;
  365. if(counter < 0)
  366. {
  367. throw new PreScriptException(scriptName, lineCounter, "unexpected }");
  368. }
  369. old = pos + 1;
  370. lines.add("}");
  371. realCodeLine.add(lineCounter);
  372. brackets.pop();
  373. break;
  374. case '\n':
  375. lineCounter++;
  376. break;
  377. }
  378. pos++;
  379. }
  380. // well the second check should be useless
  381. if(counter != 0 && !brackets.isEmpty())
  382. {
  383. throw new PreScriptException(scriptName, brackets.pop(), "unbalanced {}");
  384. }
  385. // ---------------------------------------------------------------------
  386. // generating byte code
  387. // ---------------------------------------------------------------------
  388. LineCompiler compiler = new LineCompiler(parser, scriptName);
  389. ArrayList<Code> co = new ArrayList<>();
  390. int line;
  391. byte layer = 0;
  392. String s;
  393. for(int i = 0; i < lines.size(); i++)
  394. {
  395. s = lines.get(i);
  396. //System.out.println(s);
  397. if(s.isEmpty() || s.equals("{"))
  398. {
  399. continue;
  400. }
  401. else if(lines.get(Math.min(i + 1, lines.size() - 1)).equals("{"))
  402. {
  403. layer++;
  404. if(s.equals("try") || s.equals("catch") || s.equals("else"))
  405. {
  406. co.add(new Code(s, 0, realCodeLine.get(i), layer));
  407. continue;
  408. }
  409. }
  410. else if(s.equals("}"))
  411. {
  412. layer--;
  413. co.add(new Code("gotoline", 0, realCodeLine.get(i), layer));
  414. continue;
  415. }
  416. else if(s.charAt(0) == '@')
  417. {
  418. gotos.put(s.substring(1), co.size());
  419. continue;
  420. }
  421. line = realCodeLine.get(i);
  422. compiler.reset(line, layer, s);
  423. compiler.compile(co, strings);
  424. }
  425. // ---------------------------------------------------------------------
  426. // generating gotos of key words
  427. // ---------------------------------------------------------------------
  428. Code[] c = co.toArray(new Code[co.size()]);
  429. //java.util.Arrays.stream(c).forEach(cod -> System.out.println(cod.toString()));
  430. //System.exit(0);
  431. int oldLayer = 0;
  432. int newLayer;
  433. for(int i = 0; i < c.length; i++)
  434. {
  435. newLayer = c[i].layer;
  436. if(oldLayer < newLayer)
  437. {
  438. int j = findKeyWord(i, c);
  439. if(j == 0)
  440. {
  441. //java.util.Arrays.stream(c).forEach(cod -> System.out.println(cod.toString()));
  442. throw new PreScriptException(scriptName, c[i].realLine, "well, fuck this, this should never happen");
  443. }
  444. boolean jumpBack = j < 0;
  445. j = Math.abs(j);
  446. int k;
  447. for(k = j; k < c.length; k++)
  448. {
  449. if(c[k].layer < newLayer)
  450. {
  451. break;
  452. }
  453. }
  454. c[j].value = k - j;
  455. if(jumpBack)
  456. {
  457. c[k].value = i - k - 1;
  458. }
  459. else
  460. {
  461. c[k].value = 0;
  462. }
  463. }
  464. oldLayer = newLayer;
  465. }
  466. // end
  467. System.out.println((System.currentTimeMillis() - startTime) + " ms");
  468. //java.util.Arrays.stream(c).forEach(cod -> System.out.println(cod.toString()));
  469. //gotos.forEach((k, v) -> System.out.println("Lable " + k + " " + v));
  470. //System.exit(0);
  471. return c;
  472. }
  473. private static int findKeyWord(int start, Code[] c)
  474. {
  475. for(int j = start; j < c.length; j++)
  476. {
  477. if(c[j].function != null)
  478. {
  479. switch(c[j].function)
  480. {
  481. case "while":
  482. return -j;
  483. case "if":
  484. case "else":
  485. case "try":
  486. case "catch":
  487. return j;
  488. }
  489. }
  490. }
  491. return 0;
  492. }
  493. public static Object convertInput(HashMap<String, String> map, String s, boolean variable)
  494. {
  495. if(s == null)
  496. {
  497. return null;
  498. }
  499. s = s.trim();
  500. if(s.length() == 0)
  501. {
  502. return "";
  503. }
  504. if(s.charAt(0) == '"' && s.charAt(s.length() - 1) == '"')
  505. {
  506. if(s.length() == 1)
  507. {
  508. System.out.println("Convert-Input - this should never happen");
  509. return "";
  510. }
  511. return s.substring(1, s.length() - 1);
  512. }
  513. else if(variable && s.startsWith("#"))
  514. {
  515. return convertInput(null, map.get(s), false);
  516. }
  517. else if(s.equals("true"))
  518. {
  519. return true;
  520. }
  521. else if(s.equals("false"))
  522. {
  523. return false;
  524. }
  525. else if(s.equals("null"))
  526. {
  527. return null;
  528. }
  529. try
  530. {
  531. if(s.contains("/"))
  532. {
  533. String[] parts = s.split("/");
  534. return new Fraction(Long.parseLong(parts[0].trim()), Long.parseLong(parts[1].trim()));
  535. }
  536. return new Fraction(Long.parseLong(s));
  537. }
  538. catch(NumberFormatException ex)
  539. {
  540. try
  541. {
  542. return Fraction.fromDouble(Double.parseDouble(s));
  543. }
  544. catch(NumberFormatException ex2)
  545. {
  546. if(variable)
  547. {
  548. return new Variable(s);
  549. }
  550. return s;
  551. }
  552. }
  553. }
  554. public static Object convertInput(String s)
  555. {
  556. return convertInput(null, s, false);
  557. }
  558. }
  559. // 916