Compiler.java 17 KB

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