Compiler.java 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572
  1. package me.hammerle.snuviscript.code;
  2. import java.util.ArrayList;
  3. import java.util.HashMap;
  4. import java.util.Stack;
  5. import me.hammerle.snuviscript.inputprovider.InputProvider;
  6. import me.hammerle.snuviscript.inputprovider.ConstantBoolean;
  7. import me.hammerle.snuviscript.inputprovider.ConstantDouble;
  8. import me.hammerle.snuviscript.inputprovider.ConstantNull;
  9. import me.hammerle.snuviscript.inputprovider.ConstantString;
  10. import me.hammerle.snuviscript.exceptions.PreScriptException;
  11. import me.hammerle.snuviscript.tokenizer.Token;
  12. import me.hammerle.snuviscript.tokenizer.TokenType;
  13. import static me.hammerle.snuviscript.tokenizer.TokenType.*;
  14. import me.hammerle.snuviscript.inputprovider.LocalVariable;
  15. import me.hammerle.snuviscript.inputprovider.Variable;
  16. import me.hammerle.snuviscript.instructions.Array;
  17. import me.hammerle.snuviscript.instructions.Break;
  18. import me.hammerle.snuviscript.instructions.Constant;
  19. import me.hammerle.snuviscript.instructions.Continue;
  20. import me.hammerle.snuviscript.instructions.Else;
  21. import me.hammerle.snuviscript.instructions.ElseIf;
  22. import me.hammerle.snuviscript.instructions.EndIf;
  23. import me.hammerle.snuviscript.instructions.For;
  24. import me.hammerle.snuviscript.instructions.Function;
  25. import me.hammerle.snuviscript.instructions.Goto;
  26. import me.hammerle.snuviscript.instructions.If;
  27. import me.hammerle.snuviscript.instructions.IfGoto;
  28. import me.hammerle.snuviscript.instructions.Instruction;
  29. import me.hammerle.snuviscript.instructions.Return;
  30. import me.hammerle.snuviscript.instructions.UserFunction;
  31. import me.hammerle.snuviscript.instructions.While;
  32. public class Compiler {
  33. private int index = 0;
  34. private Token[] tokens = null;
  35. private final ArrayList<Instruction> instr = new ArrayList<>();
  36. private HashMap<String, Integer> labels = null;
  37. private HashMap<String, HashMap<String, Integer>> localLabels = null;
  38. private HashMap<String, Variable> vars = null;
  39. private final HashMap<String, LocalVariable> localVars = new HashMap<>();
  40. private HashMap<String, Integer> functions = null;
  41. private final Stack<Break> breakStack = new Stack<>();
  42. private final Stack<Continue> continueStack = new Stack<>();
  43. private String inFunction = null;
  44. private boolean lineExpression = false;
  45. private void addConstant(int line, InputProvider ip) {
  46. instr.add(new Constant(line, ip));
  47. }
  48. private void addFunction(int line, int args, String name) {
  49. instr.add(new Function(line, args, FunctionRegistry.getFunction(name)));
  50. }
  51. private void addGoto(int line, int jump) {
  52. Goto g = new Goto(line, 0);
  53. g.setJump(jump);
  54. instr.add(g);
  55. }
  56. private boolean match(boolean notEOF, TokenType... types) {
  57. for(TokenType type : types) {
  58. if(check(type, notEOF)) {
  59. advance();
  60. return true;
  61. }
  62. }
  63. return false;
  64. }
  65. private boolean check(TokenType type, boolean notEOF) {
  66. if(isAtEnd()) {
  67. if(notEOF) {
  68. throw new PreScriptException(
  69. String.format("expected %s got %s", type, peek().getType()),
  70. peek().getLine());
  71. }
  72. return false;
  73. }
  74. return peek().getType() == type;
  75. }
  76. private Token advance() {
  77. if(!isAtEnd()) {
  78. index++;
  79. }
  80. return previous();
  81. }
  82. private boolean isAtEnd() {
  83. return peek().getType() == EOF;
  84. }
  85. private Token peek() {
  86. return tokens[index];
  87. }
  88. private Token previous() {
  89. return tokens[index - 1];
  90. }
  91. private Token consume(TokenType type) {
  92. if(check(type, false)) {
  93. return advance();
  94. }
  95. throw new PreScriptException(String.format("expected %s got %s", type, peek().getType()),
  96. peek().getLine());
  97. }
  98. private void noReturnForLastFunction() {
  99. if(!instr.isEmpty()) {
  100. instr.get(instr.size() - 1).setNoReturn();
  101. }
  102. }
  103. public Instruction[] compile(Token[] tokens, HashMap<String, Integer> labels,
  104. HashMap<String, Variable> vars, HashMap<String, Integer> functions,
  105. HashMap<String, HashMap<String, Integer>> localLabels) {
  106. this.tokens = tokens;
  107. index = 0;
  108. instr.clear();
  109. this.labels = labels;
  110. this.localLabels = localLabels;
  111. this.vars = vars;
  112. this.functions = functions;
  113. localVars.clear();
  114. inFunction = null;
  115. while(!isAtEnd()) {
  116. line();
  117. }
  118. this.tokens = null;
  119. this.labels = null;
  120. this.vars = null;
  121. this.functions = null;
  122. localVars.clear();
  123. Instruction[] code = instr.toArray(new Instruction[instr.size()]);
  124. instr.clear();
  125. return code;
  126. }
  127. private void line() {
  128. int oldIndex = index;
  129. Token t = advance();
  130. switch(t.getType()) {
  131. case LABEL:
  132. handleLabel();
  133. break;
  134. case IF:
  135. handleIf();
  136. break;
  137. case SEMICOLON:
  138. break;
  139. case FOR:
  140. handleFor();
  141. break;
  142. case BREAK:
  143. Break b = new Break(previous().getLine());
  144. breakStack.add(b);
  145. instr.add(b);
  146. consume(SEMICOLON);
  147. break;
  148. case CONTINUE:
  149. Continue c = new Continue(previous().getLine());
  150. continueStack.add(c);
  151. instr.add(c);
  152. consume(SEMICOLON);
  153. break;
  154. case FUNCTION:
  155. handleUserFunction();
  156. break;
  157. case RETURN:
  158. handleReturn();
  159. break;
  160. case WHILE:
  161. handleWhile();
  162. break;
  163. default:
  164. index = oldIndex;
  165. lineExpression = false;
  166. expression();
  167. if(!lineExpression) {
  168. throw new PreScriptException("missing statement", t.getLine());
  169. }
  170. consume(SEMICOLON);
  171. }
  172. noReturnForLastFunction();
  173. }
  174. private void handleLabel() {
  175. String name = previous().getData().toString();
  176. name = name.substring(1); // cut off @ at start
  177. if(inFunction != null) {
  178. HashMap<String, Integer> llabel = localLabels.get(inFunction);
  179. if(llabel == null) {
  180. llabel = new HashMap<>();
  181. localLabels.put(inFunction, llabel);
  182. }
  183. llabel.put(name, instr.size() - 1);
  184. } else {
  185. labels.put(name, instr.size() - 1);
  186. }
  187. }
  188. private void handleIf() {
  189. Token t = previous();
  190. consume(OPEN_BRACKET);
  191. expression();
  192. If i = new If(t.getLine());
  193. instr.add(i);
  194. consume(CLOSE_BRACKET);
  195. consume(OPEN_CURVED_BRACKET);
  196. while(!match(true, CLOSE_CURVED_BRACKET)) {
  197. line();
  198. }
  199. i.setJump(instr.size() - 1);
  200. handleElseIf();
  201. instr.add(new EndIf(instr.get(instr.size() - 1).getLine()));
  202. }
  203. private void handleElseIf() {
  204. while(match(false, ELSEIF)) {
  205. Token t = previous();
  206. consume(OPEN_BRACKET);
  207. expression();
  208. ElseIf e = new ElseIf(t.getLine());
  209. instr.add(e);
  210. consume(CLOSE_BRACKET);
  211. consume(OPEN_CURVED_BRACKET);
  212. while(!match(true, CLOSE_CURVED_BRACKET)) {
  213. line();
  214. }
  215. e.setJump(instr.size() - 1);
  216. }
  217. handleElse();
  218. }
  219. private void handleElse() {
  220. if(match(false, ELSE)) {
  221. Else e = new Else(previous().getLine());
  222. instr.add(e);
  223. consume(OPEN_CURVED_BRACKET);
  224. while(!match(true, CLOSE_CURVED_BRACKET)) {
  225. line();
  226. }
  227. e.setJump(instr.size() - 1);
  228. }
  229. }
  230. private void handleFor() {
  231. Token t = previous();
  232. consume(OPEN_BRACKET);
  233. if(!match(false, SEMICOLON)) {
  234. expression();
  235. consume(SEMICOLON);
  236. noReturnForLastFunction();
  237. }
  238. int forConditionStart = instr.size() - 1;
  239. if(!match(false, SEMICOLON)) {
  240. expression();
  241. consume(SEMICOLON);
  242. } else {
  243. int line = instr.isEmpty() ? 1 : instr.get(instr.size() - 1).getLine();
  244. addConstant(line, ConstantBoolean.TRUE);
  245. }
  246. int realGotoLine = instr.get(instr.size() - 1).getLine();
  247. Goto forGoto = new Goto(realGotoLine, 0);
  248. instr.add(forGoto);
  249. int forLoopFunctionStart = instr.size() - 1;
  250. if(!match(false, CLOSE_BRACKET)) {
  251. expression();
  252. consume(CLOSE_BRACKET);
  253. noReturnForLastFunction();
  254. }
  255. Goto conditionGoto = new Goto(instr.get(instr.size() - 1).getLine(), 0);
  256. conditionGoto.setJump(forConditionStart);
  257. instr.add(conditionGoto);
  258. int forStart = instr.size() - 1;
  259. forGoto.setJump(forStart);
  260. For f = new For(t.getLine());
  261. instr.add(f);
  262. consume(OPEN_CURVED_BRACKET);
  263. while(!match(true, CLOSE_CURVED_BRACKET)) {
  264. line();
  265. }
  266. Goto loopFunctionGoto = new Goto(instr.get(instr.size() - 1).getLine(), 0);
  267. loopFunctionGoto.setJump(forLoopFunctionStart);
  268. instr.add(loopFunctionGoto);
  269. int forEnd = instr.size() - 1;
  270. f.setJump(forEnd);
  271. setBreakContinueJumps(forLoopFunctionStart, forEnd);
  272. }
  273. private void setBreakContinueJumps(int start, int end) {
  274. while(!continueStack.empty()) {
  275. continueStack.pop().setJump(start);
  276. }
  277. while(!breakStack.empty()) {
  278. breakStack.pop().setJump(end);
  279. }
  280. }
  281. private void handleUserFunction() {
  282. consume(LITERAL);
  283. Token t = previous();
  284. consume(OPEN_BRACKET);
  285. ArrayList<String> list = new ArrayList<>();
  286. if(!match(false, CLOSE_BRACKET)) {
  287. while(true) {
  288. consume(LITERAL);
  289. list.add(previous().getData().toString());
  290. if(match(false, CLOSE_BRACKET)) {
  291. break;
  292. }
  293. consume(COMMA);
  294. }
  295. }
  296. String name = t.getData().toString().toLowerCase();
  297. UserFunction uf =
  298. new UserFunction(t.getLine(), name, list.toArray(new String[list.size()]));
  299. functions.put(name, instr.size());
  300. instr.add(uf);
  301. consume(OPEN_CURVED_BRACKET);
  302. inFunction = name;
  303. while(!match(true, CLOSE_CURVED_BRACKET)) {
  304. line();
  305. }
  306. inFunction = null;
  307. instr.add(new Return(instr.get(instr.size() - 1).getLine(), 0));
  308. uf.setJump(instr.size() - 1);
  309. }
  310. private void handleReturn() {
  311. Token t = previous();
  312. int args = 0;
  313. if(!match(false, SEMICOLON)) {
  314. args = 1;
  315. expression();
  316. consume(SEMICOLON);
  317. }
  318. instr.add(new Return(t.getLine(), args));
  319. }
  320. private void handleWhile() {
  321. int whileStart = instr.size() - 1;
  322. Token t = previous();
  323. consume(OPEN_BRACKET);
  324. expression();
  325. While w = new While(t.getLine());
  326. instr.add(w);
  327. consume(CLOSE_BRACKET);
  328. consume(OPEN_CURVED_BRACKET);
  329. while(!match(true, CLOSE_CURVED_BRACKET)) {
  330. line();
  331. }
  332. addGoto(instr.get(instr.size() - 1).getLine(), whileStart);
  333. int whileEnd = instr.size() - 1;
  334. w.setJump(whileEnd);
  335. setBreakContinueJumps(whileStart, whileEnd);
  336. }
  337. private void expression() {
  338. if(isAtEnd()) {
  339. throw new PreScriptException(
  340. String.format("expected expression got %s", peek().getType()),
  341. peek().getLine());
  342. }
  343. assignment();
  344. }
  345. private void assignment() {
  346. logicalOr();
  347. if(match(false, SET, ADD_SET, SUB_SET, MUL_SET, DIV_SET, MOD_SET, LEFT_SHIFT_SET,
  348. RIGHT_SHIFT_SET, BIT_AND_SET, BIT_XOR_SET, BIT_OR_SET)) {
  349. Token t = previous();
  350. assignment();
  351. addFunction(t.getLine(), 2, t.getType().getName());
  352. lineExpression = true;
  353. }
  354. }
  355. private void logicalOr() {
  356. logicalAnd();
  357. while(match(false, OR)) {
  358. Token t = previous();
  359. IfGoto ifGoto = new IfGoto(t.getLine(), true);
  360. instr.add(ifGoto);
  361. logicalAnd();
  362. ifGoto.setJump(instr.size());
  363. addFunction(t.getLine(), 2, t.getType().getName());
  364. }
  365. }
  366. private void logicalAnd() {
  367. equality();
  368. while(match(false, AND)) {
  369. Token t = previous();
  370. IfGoto ifGoto = new IfGoto(t.getLine(), false);
  371. instr.add(ifGoto);
  372. equality();
  373. ifGoto.setJump(instr.size());
  374. addFunction(t.getLine(), 2, t.getType().getName());
  375. }
  376. }
  377. private void equality() {
  378. comparison();
  379. while(match(false, EQUAL, NOT_EQUAL)) {
  380. Token t = previous();
  381. comparison();
  382. addFunction(t.getLine(), 2, t.getType().getName());
  383. }
  384. }
  385. private void comparison() {
  386. addition();
  387. while(match(false, GREATER, GREATER_EQUAL, LESS, LESS_EQUAL)) {
  388. Token t = previous();
  389. addition();
  390. addFunction(t.getLine(), 2, t.getType().getName());
  391. }
  392. }
  393. private void addition() {
  394. multiplication();
  395. while(match(false, SUB, ADD)) {
  396. Token t = previous();
  397. multiplication();
  398. addFunction(t.getLine(), 2, t.getType().getName());
  399. }
  400. }
  401. private void multiplication() {
  402. unary();
  403. while(match(false, DIV, MUL, MOD)) {
  404. Token t = previous();
  405. unary();
  406. addFunction(t.getLine(), 2, t.getType().getName());
  407. }
  408. }
  409. private void unary() {
  410. if(match(false, INVERT, BIT_INVERT, SUB, INC, DEC)) {
  411. Token t = previous();
  412. unary();
  413. addFunction(t.getLine(), 1, t.getType().getName());
  414. if(t.getType() == INC || t.getType() == DEC) {
  415. lineExpression = true;
  416. }
  417. return;
  418. }
  419. postUnary();
  420. }
  421. private void postUnary() {
  422. primary();
  423. while(match(false, INC, DEC)) {
  424. Token t = previous();
  425. addFunction(t.getLine(), 1, "p" + t.getType().getName());
  426. lineExpression = true;
  427. }
  428. }
  429. private void primary() {
  430. Token t = advance();
  431. switch(t.getType()) {
  432. case FALSE:
  433. addConstant(t.getLine(), ConstantBoolean.FALSE);
  434. return;
  435. case TRUE:
  436. addConstant(t.getLine(), ConstantBoolean.TRUE);
  437. return;
  438. case NULL:
  439. addConstant(t.getLine(), ConstantNull.NULL);
  440. return;
  441. case STRING:
  442. addConstant(t.getLine(), new ConstantString(t.getData().toString()));
  443. return;
  444. case LABEL:
  445. addConstant(t.getLine(), new ConstantString(t.getData().toString().substring(1)));
  446. return;
  447. case NUMBER:
  448. addConstant(t.getLine(), new ConstantDouble((Double) t.getData()));
  449. return;
  450. case OPEN_BRACKET:
  451. expression();
  452. consume(CLOSE_BRACKET);
  453. return;
  454. case LITERAL:
  455. if(match(false, OPEN_SQUARE_BRACKET)) {
  456. handleArray(t);
  457. } else if(match(false, OPEN_BRACKET)) {
  458. handleFunction(t);
  459. } else {
  460. addConstant(t.getLine(), getVariable(t.getData().toString()));
  461. }
  462. return;
  463. default:
  464. }
  465. throw new PreScriptException(String.format("unexpected token %s", t.getType()),
  466. t.getLine());
  467. }
  468. public void handleFunction(Token t) {
  469. int args = 0;
  470. if(peek().getType() != CLOSE_BRACKET) {
  471. while(true) {
  472. args++;
  473. expression();
  474. if(match(false, CLOSE_BRACKET)) {
  475. break;
  476. }
  477. consume(COMMA);
  478. }
  479. } else {
  480. consume(CLOSE_BRACKET);
  481. }
  482. addFunction(t.getLine(), args, t.getData().toString());
  483. lineExpression = true;
  484. }
  485. public void handleArray(Token t) {
  486. if(peek().getType() == CLOSE_SQUARE_BRACKET) {
  487. throw new PreScriptException("empty array access", peek().getLine());
  488. }
  489. int args = 0;
  490. while(true) {
  491. args++;
  492. expression();
  493. if(match(false, CLOSE_SQUARE_BRACKET)) {
  494. break;
  495. }
  496. consume(COMMA);
  497. }
  498. instr.add(new Array(t.getLine(), args, getVariable(t.getData().toString())));
  499. }
  500. private Variable getVariable(String name) {
  501. boolean global = name.startsWith("$");
  502. if(inFunction != null && !global) {
  503. LocalVariable v = localVars.get(name);
  504. if(v != null) {
  505. return v;
  506. }
  507. v = new LocalVariable(name);
  508. localVars.put(name, v);
  509. return v;
  510. }
  511. if(global) {
  512. name = name.substring(1);
  513. }
  514. Variable v = vars.get(name);
  515. if(v != null) {
  516. return v;
  517. }
  518. v = new Variable(name);
  519. vars.put(name, v);
  520. return v;
  521. }
  522. }