Compiler.java 18 KB

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