LineCompiler.java 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  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 LineCompiler
  8. {
  9. private int realLine;
  10. // comma counter, for functions
  11. private final Stack<Integer> commaCounter;
  12. private String line;
  13. private byte layer;
  14. private String scriptName;
  15. // helper to support things like print(-3);
  16. private boolean minus;
  17. public LineCompiler(SnuviParser parser, String scriptName)
  18. {
  19. realLine = 0;
  20. commaCounter = new Stack<>();
  21. line = "";
  22. layer = 0;
  23. minus = false;
  24. this.scriptName = scriptName;
  25. }
  26. public void reset(int realLine, byte layer, String line)
  27. {
  28. this.realLine = realLine;
  29. commaCounter.clear();
  30. minus = false;
  31. this.line = line;
  32. this.layer = layer;
  33. }
  34. private boolean isAllowedChar(char c)
  35. {
  36. return Character.isLetterOrDigit(c) || c == '.' || c == '_' || c == '#' || c == '$';
  37. }
  38. public void compile(ArrayList<Code> co, HashMap<String, String> strings)
  39. {
  40. Stack<Stack<Syntax>> syntaxStack = new Stack<>();
  41. syntaxStack.push(new Stack<>());
  42. char[] chars = line.toCharArray();
  43. char c;
  44. int old = chars.length;
  45. Syntax syntax;
  46. String s;
  47. for(int i = old - 1; i >= 0; i--)
  48. {
  49. c = chars[i];
  50. if(!isAllowedChar(c))
  51. {
  52. syntax = getSyntax(line, i);
  53. if(syntax == Syntax.UNKNOWN)
  54. {
  55. throw new PreScriptException(scriptName, realLine, "unexpected character '" + c + "'");
  56. }
  57. else if(syntax.isIncOrDec())
  58. {
  59. Code change = co.get(co.size() - 1);
  60. if(change.function.equals("array.get"))
  61. {
  62. change.function = "array." + syntax.getFunction();
  63. }
  64. }
  65. s = line.substring(i + 1, old).toLowerCase();
  66. if(!s.isEmpty())
  67. {
  68. Stack<Syntax> stack = syntaxStack.peek();
  69. if(!stack.isEmpty())
  70. {
  71. Syntax sy = stack.peek();
  72. if(sy == Syntax.INC)
  73. {
  74. stack.pop();
  75. stack.push(Syntax.POST_INC);
  76. }
  77. else if(sy == Syntax.DEC)
  78. {
  79. stack.pop();
  80. stack.push(Syntax.POST_DEC);
  81. }
  82. }
  83. co.add(new Code(Code.convertInput(strings, s, true), realLine, layer));
  84. minus = false;
  85. }
  86. i += 1 - syntax.getFunction().length();
  87. old = i;
  88. if(syntax.shouldStartLayer())
  89. {
  90. syntaxStack.push(new Stack<>());
  91. commaCounter.push(0);
  92. continue;
  93. }
  94. else if(syntax.shouldEndLayer())
  95. {
  96. Stack<Syntax> currentStack = syntaxStack.pop();
  97. while(!currentStack.isEmpty())
  98. {
  99. doStackEmptying(co, currentStack.pop());
  100. }
  101. old--;
  102. int pos = old;
  103. while(old >= 0 && isAllowedChar(chars[old]))
  104. {
  105. old--;
  106. }
  107. old++;
  108. s = line.substring(old, pos + 1).toLowerCase();
  109. if(syntax.isArray())
  110. {
  111. minus = false;
  112. co.add(new Code(s, realLine, layer));
  113. if(!syntaxStack.isEmpty())
  114. {
  115. Stack<Syntax> arrayFunction = syntaxStack.pop();
  116. if(!arrayFunction.isEmpty())
  117. {
  118. Syntax sy = arrayFunction.pop();
  119. if(sy.isIncOrDec())
  120. {
  121. if(sy == Syntax.INC)
  122. {
  123. sy = Syntax.POST_INC;
  124. }
  125. else if(sy == Syntax.DEC)
  126. {
  127. sy = Syntax.POST_DEC;
  128. }
  129. }
  130. co.add(new Code("array." + sy.getFunction(), sy.isIncOrDec() ? 2 : 3, realLine, layer));
  131. continue;
  132. }
  133. }
  134. co.add(new Code("array.get", 2, realLine, layer));
  135. }
  136. else
  137. {
  138. minus = false;
  139. if(line.startsWith("()", pos + 1))
  140. {
  141. co.add(new Code(s, 0, realLine, layer));
  142. }
  143. else
  144. {
  145. co.add(new Code(s, commaCounter.pop() + 1, realLine, layer));
  146. }
  147. }
  148. continue;
  149. }
  150. else if(!syntaxStack.isEmpty())
  151. {
  152. Stack<Syntax> currentStack = syntaxStack.peek();
  153. while(!currentStack.isEmpty() && currentStack.peek().getWeight() <= syntax.getWeight())
  154. {
  155. doStackEmptying(co, currentStack.pop());
  156. }
  157. currentStack.push(syntax);
  158. }
  159. if(syntax == Syntax.COMMA)
  160. {
  161. commaCounter.push(commaCounter.pop() + 1);
  162. }
  163. else if(syntax == Syntax.SUB)
  164. {
  165. minus = true;
  166. }
  167. }
  168. }
  169. s = line.substring(0, old).toLowerCase();
  170. if(!s.isEmpty())
  171. {
  172. co.add(new Code(s, realLine, layer));
  173. }
  174. if(!syntaxStack.isEmpty())
  175. {
  176. Stack<Syntax> currentStack = syntaxStack.peek();
  177. while(!currentStack.isEmpty())
  178. {
  179. doStackEmptying(co, currentStack.pop());
  180. }
  181. }
  182. }
  183. private void doStackEmptying(ArrayList<Code> co, Syntax sy)
  184. {
  185. if(sy.isFunction())
  186. {
  187. if(sy.isIncOrDec())
  188. {
  189. Code change = co.get(co.size() - 1);
  190. if(change.value instanceof Variable)
  191. {
  192. change.value = ((Variable) change.value).getName();
  193. }
  194. co.add(new Code(sy.getFunction(), sy.getParameters(), realLine, layer));
  195. }
  196. else
  197. {
  198. if(minus)
  199. {
  200. minus = false;
  201. co.add(new Code(new Fraction(0), realLine, layer));
  202. }
  203. co.add(new Code(sy.getFunction(), sy.getParameters(), realLine, layer));
  204. }
  205. }
  206. }
  207. private static Syntax getSyntax(String code, int pos)
  208. {
  209. // priorities from C specification
  210. // http://en.cppreference.com/w/c/language/operator_precedence
  211. switch(code.charAt(pos))
  212. {
  213. case '(': return Syntax.OPEN_BRACKET;
  214. case ')': return Syntax.CLOSE_BRACKET;
  215. case '[': return Syntax.OPEN_SBRACKET;
  216. case ']': return Syntax.CLOSE_SBRACKET;
  217. case '!': return Syntax.NOT;
  218. case '~': return Syntax.BIT_NOT;
  219. case '*': return Syntax.MUL;
  220. case '/': return Syntax.DIV;
  221. case '%': return Syntax.MOD;
  222. case '^': return Syntax.BIT_XOR;
  223. case ',': return Syntax.COMMA;
  224. case '+':
  225. if(pos >= 1 && code.charAt(pos - 1) == '+')
  226. {
  227. return Syntax.INC;
  228. }
  229. return Syntax.ADD;
  230. case '-':
  231. if(pos >= 1 && code.charAt(pos - 1) == '-')
  232. {
  233. return Syntax.DEC;
  234. }
  235. return Syntax.SUB;
  236. case '<':
  237. if(pos >= 1 && code.charAt(pos - 1) == '<')
  238. {
  239. return Syntax.LEFT_SHIFT;
  240. }
  241. return Syntax.SMALLER;
  242. case '>':
  243. if(pos >= 1 && code.charAt(pos - 1) == '>')
  244. {
  245. return Syntax.RIGHT_SHIFT;
  246. }
  247. return Syntax.GREATER;
  248. case '&':
  249. if(pos >= 1 && code.charAt(pos - 1) == '&')
  250. {
  251. return Syntax.AND;
  252. }
  253. return Syntax.BIT_AND;
  254. case '|':
  255. if(pos >= 1 && code.charAt(pos - 1) == '|')
  256. {
  257. return Syntax.OR;
  258. }
  259. return Syntax.BIT_OR;
  260. case '=':
  261. if(pos >= 1)
  262. {
  263. switch(code.charAt(pos - 1))
  264. {
  265. case '<':
  266. if(pos >= 2 && code.charAt(pos - 2) == '<')
  267. {
  268. return Syntax.SET_SHIFT_LEFT;
  269. }
  270. return Syntax.SMALLER_EQUAL;
  271. case '>':
  272. if(pos >= 2 && code.charAt(pos - 2) == '>')
  273. {
  274. return Syntax.SET_SHIFT_RIGHT;
  275. }
  276. return Syntax.GREATER_EQUAL;
  277. case '=': return Syntax.EQUAL;
  278. case '!': return Syntax.NOT_EQUAL;
  279. case '+': return Syntax.SET_ADD;
  280. case '-': return Syntax.SET_SUB;
  281. case '*': return Syntax.SET_MUL;
  282. case '/': return Syntax.SET_DIV;
  283. case '%': return Syntax.SET_MOD;
  284. case '&': return Syntax.SET_BIT_AND;
  285. case '^': return Syntax.SET_BIT_XOR;
  286. case '|': return Syntax.SET_BIT_OR;
  287. }
  288. }
  289. return Syntax.SET;
  290. }
  291. return Syntax.UNKNOWN;
  292. }
  293. }