Interpreter.java 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. /* Copyright (C) 2019-2023 Hangzhou HSH Co. Ltd.
  2. * All right reserved.*/
  3. package com.craftinginterpreters.lox;
  4. import java.util.ArrayList;
  5. import java.util.HashMap;
  6. import java.util.List;
  7. import java.util.Map;
  8. /**
  9. * @author simon
  10. * @date 2023-06-07 12:20
  11. * @desc
  12. */
  13. class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
  14. public final Environment globals = new Environment();
  15. private final Map<Expr, Integer> locals = new HashMap<>();
  16. private Environment environment = globals;
  17. public Interpreter() {
  18. globals.define("clock", new LoxCallable() {
  19. @Override
  20. public int arity() {
  21. return 0;
  22. }
  23. @Override
  24. public Object call(Interpreter interpreter, List<Object> arguments) {
  25. return (double) System.currentTimeMillis() / 1000.0;
  26. }
  27. @Override
  28. public String toString() {
  29. return "<native fn>";
  30. }
  31. });
  32. }
  33. /**
  34. * public API
  35. *
  36. * @param statements statements
  37. */
  38. void interpret(List<Stmt> statements) {
  39. try {
  40. for (Stmt statement : statements) {
  41. execute(statement);
  42. }
  43. } catch (RuntimeError error) {
  44. Lox.runtimeError(error);
  45. }
  46. }
  47. @Override
  48. public Object visitAssignExpr(Expr.Assign expr) {
  49. Object value = evaluate(expr.value);
  50. environment.assign(expr.name, value);
  51. return value;
  52. }
  53. @Override
  54. public Object visitBinaryExpr(Expr.Binary expr) {
  55. Object left = evaluate(expr.left);
  56. Object right = evaluate(expr.right);
  57. return switch (expr.operator.type) {
  58. case GRATER -> {
  59. checkNumberOperand(expr.operator, left, right);
  60. yield (double) left > (double) right;
  61. }
  62. case GRATER_EQUAL -> {
  63. checkNumberOperand(expr.operator, left, right);
  64. yield (double) left >= (double) right;
  65. }
  66. case LESS -> {
  67. checkNumberOperand(expr.operator, left, right);
  68. yield (double) left < (double) right;
  69. }
  70. case LESS_EQUAL -> {
  71. checkNumberOperand(expr.operator, left, right);
  72. yield (double) left <= (double) right;
  73. }
  74. case BANG_EQUAL -> !isEqual(left, right);
  75. case EQUAL_EQUAL -> isEqual(left, right);
  76. case MINUS -> {
  77. checkNumberOperand(expr.operator, left, right);
  78. yield (double) left - (double) right;
  79. }
  80. case PLUS -> {
  81. if (left instanceof Double && right instanceof Double) {
  82. yield (double) left + (double) right;
  83. }
  84. if (left instanceof String && right instanceof String) {
  85. yield left + (String) right;
  86. }
  87. throw new RuntimeError(expr.operator, "Operands must be two numbers or two Strings.");
  88. }
  89. case SLASH -> {
  90. checkNumberOperand(expr.operator, left, right);
  91. if (((Double) right) == 0D) throw new RuntimeError(expr.operator, "Division by Zero.");
  92. yield (double) left / (double) right;
  93. }
  94. case STAR -> {
  95. checkNumberOperand(expr.operator, left, right);
  96. yield (double) left * (double) right;
  97. }
  98. default -> null;
  99. };
  100. }
  101. @Override
  102. public Object visitCallExpr(Expr.Call expr) {
  103. Object callee = evaluate(expr.callee);
  104. List<Object> arguments = new ArrayList<>();
  105. for (Expr argument : expr.arguments) {
  106. arguments.add(evaluate(argument));
  107. }
  108. if (!(callee instanceof LoxCallable fun)) {
  109. // "totally not a function"();
  110. throw new RuntimeError(expr.paren, "Can only call functions and classes.");
  111. }
  112. if (arguments.size() != fun.arity()) {
  113. throw new RuntimeError(expr.paren, "Expected " + fun.arity() + " arguments bug got " +
  114. arguments.size() + ".");
  115. }
  116. return fun.call(this, arguments);
  117. }
  118. @Override
  119. public Object visitGroupingExpr(Expr.Grouping expr) {
  120. return evaluate(expr.expression);
  121. }
  122. @Override
  123. public Object visitLiteralExpr(Expr.Literal expr) {
  124. return expr.value;
  125. }
  126. @Override
  127. public Object visitLogicalExpr(Expr.Logical expr) {
  128. Object left = evaluate(expr.left);
  129. if (expr.operator.type == TokenType.OR) {
  130. if (isTruthy(left)) return left;
  131. } else {
  132. if (!isTruthy(left)) return left;
  133. }
  134. return evaluate(expr.right);
  135. }
  136. @Override
  137. public Object visitUnaryExpr(Expr.Unary expr) {
  138. Object right = evaluate(expr.right);
  139. return switch (expr.operator.type) {
  140. case MINUS -> {
  141. checkNumberOperand(expr.operator, right);
  142. yield -(double) right;
  143. }
  144. case BANG -> !isTruthy(right);
  145. default -> null;
  146. };
  147. }
  148. @Override
  149. public Object visitVariableExpr(Expr.Variable expr) {
  150. // return environment.get(expr.name);
  151. return lookUpVariable(expr.name, expr);
  152. }
  153. private Object lookUpVariable(Token name, Expr expr) {
  154. Integer distance = locals.get(expr);
  155. if (distance != null) {
  156. return environment.getAt(distance, name.lexeme);
  157. } else {
  158. return globals.get(name);
  159. }
  160. }
  161. @Override
  162. public Void visitBlockStmt(Stmt.Block stmt) {
  163. executeBlock(stmt.statements, new Environment(environment));
  164. return null;
  165. }
  166. @Override
  167. public Void visitClassStmt(Stmt.Class stmt) {
  168. environment.define(stmt.name.lexeme, null);
  169. LoxClass klass = new LoxClass(stmt.name.lexeme);
  170. environment.assign(stmt.name, klass);
  171. return null;
  172. }
  173. @Override
  174. public Void visitExpressionStmt(Stmt.Expression stmt) {
  175. Object value = evaluate(stmt.expression);
  176. System.out.println(" " + stringify(value));
  177. return null;
  178. }
  179. @Override
  180. public Void visitFunctionStmt(Stmt.Function stmt) {
  181. LoxFunction function = new LoxFunction(stmt, environment);
  182. environment.define(stmt.name.lexeme, function);
  183. return null;
  184. }
  185. @Override
  186. public Void visitIfStmt(Stmt.If stmt) {
  187. if (isTruthy(evaluate(stmt.condition))) {
  188. execute(stmt.thenBranch);
  189. } else if (stmt.elseBranch != null) {
  190. execute(stmt.elseBranch);
  191. }
  192. return null;
  193. }
  194. @Override
  195. public Void visitPrintStmt(Stmt.Print stmt) {
  196. Object value = evaluate(stmt.expression);
  197. System.out.println(" " + stringify(value));
  198. return null;
  199. }
  200. @Override
  201. public Void visitReturnStmt(Stmt.Return stmt) {
  202. Object value = null;
  203. if (stmt.value != null) value = evaluate(stmt.value);
  204. throw new Return(value);
  205. }
  206. @Override
  207. public Void visitVarStmt(Stmt.Var stmt) {
  208. Object value = null;
  209. if (stmt.initializer != null) {
  210. value = evaluate(stmt.initializer);
  211. }
  212. environment.define(stmt.name.lexeme, value);
  213. return null;
  214. }
  215. @Override
  216. public Void visitWhileStmt(Stmt.While stmt) {
  217. while (isTruthy(evaluate(stmt.condition))) {
  218. execute(stmt.body);
  219. }
  220. return null;
  221. }
  222. private void execute(Stmt stmt) {
  223. stmt.accept(this);
  224. }
  225. void resolve(Expr expr, int depth) {
  226. locals.put(expr, depth);
  227. }
  228. protected void executeBlock(List<Stmt> statements, Environment environment) {
  229. Environment previous = this.environment;
  230. try {
  231. this.environment = environment;
  232. for (Stmt statement : statements) {
  233. execute(statement);
  234. }
  235. } finally {
  236. this.environment = previous;
  237. }
  238. }
  239. private Object evaluate(Expr expr) {
  240. return expr.accept(this);
  241. }
  242. private boolean isTruthy(Object object) {
  243. if (object == null) return false;
  244. if (object instanceof Boolean) return (boolean) object;
  245. return true;
  246. }
  247. private boolean isEqual(Object a, Object b) {
  248. if (a == null && b == null) return true;
  249. if (a == null) return false;
  250. return a.equals(b);
  251. }
  252. private String stringify(Object object) {
  253. if (object == null) return "nil";
  254. if (object instanceof Double) {
  255. String text = object.toString();
  256. if (text.endsWith(".0")) {
  257. text = text.substring(0, text.length() - 2);
  258. }
  259. return text;
  260. }
  261. return object.toString();
  262. }
  263. private void checkNumberOperand(Token operator, Object operand) {
  264. if (operand instanceof Double) return;
  265. throw new RuntimeError(operator, "Operand must be a number.");
  266. }
  267. private void checkNumberOperand(Token operator, Object left, Object right) {
  268. if (left instanceof Double && right instanceof Double) return;
  269. throw new RuntimeError(operator, "Operands must be numbers.");
  270. }
  271. }