| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316 |
- /* Copyright (C) 2019-2023 Hangzhou HSH Co. Ltd.
- * All right reserved.*/
- package com.craftinginterpreters.lox;
- import java.util.ArrayList;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
- /**
- * @author simon
- * @date 2023-06-07 12:20
- * @desc
- */
- class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
- public final Environment globals = new Environment();
- private final Map<Expr, Integer> locals = new HashMap<>();
- private Environment environment = globals;
- public Interpreter() {
- globals.define("clock", new LoxCallable() {
- @Override
- public int arity() {
- return 0;
- }
- @Override
- public Object call(Interpreter interpreter, List<Object> arguments) {
- return (double) System.currentTimeMillis() / 1000.0;
- }
- @Override
- public String toString() {
- return "<native fn>";
- }
- });
- }
- /**
- * public API
- *
- * @param statements statements
- */
- void interpret(List<Stmt> statements) {
- try {
- for (Stmt statement : statements) {
- execute(statement);
- }
- } catch (RuntimeError error) {
- Lox.runtimeError(error);
- }
- }
- @Override
- public Object visitAssignExpr(Expr.Assign expr) {
- Object value = evaluate(expr.value);
- environment.assign(expr.name, value);
- return value;
- }
- @Override
- public Object visitBinaryExpr(Expr.Binary expr) {
- Object left = evaluate(expr.left);
- Object right = evaluate(expr.right);
- return switch (expr.operator.type) {
- case GRATER -> {
- checkNumberOperand(expr.operator, left, right);
- yield (double) left > (double) right;
- }
- case GRATER_EQUAL -> {
- checkNumberOperand(expr.operator, left, right);
- yield (double) left >= (double) right;
- }
- case LESS -> {
- checkNumberOperand(expr.operator, left, right);
- yield (double) left < (double) right;
- }
- case LESS_EQUAL -> {
- checkNumberOperand(expr.operator, left, right);
- yield (double) left <= (double) right;
- }
- case BANG_EQUAL -> !isEqual(left, right);
- case EQUAL_EQUAL -> isEqual(left, right);
- case MINUS -> {
- checkNumberOperand(expr.operator, left, right);
- yield (double) left - (double) right;
- }
- case PLUS -> {
- if (left instanceof Double && right instanceof Double) {
- yield (double) left + (double) right;
- }
- if (left instanceof String && right instanceof String) {
- yield left + (String) right;
- }
- throw new RuntimeError(expr.operator, "Operands must be two numbers or two Strings.");
- }
- case SLASH -> {
- checkNumberOperand(expr.operator, left, right);
- if (((Double) right) == 0D) throw new RuntimeError(expr.operator, "Division by Zero.");
- yield (double) left / (double) right;
- }
- case STAR -> {
- checkNumberOperand(expr.operator, left, right);
- yield (double) left * (double) right;
- }
- default -> null;
- };
- }
- @Override
- public Object visitCallExpr(Expr.Call expr) {
- Object callee = evaluate(expr.callee);
- List<Object> arguments = new ArrayList<>();
- for (Expr argument : expr.arguments) {
- arguments.add(evaluate(argument));
- }
- if (!(callee instanceof LoxCallable fun)) {
- // "totally not a function"();
- throw new RuntimeError(expr.paren, "Can only call functions and classes.");
- }
- if (arguments.size() != fun.arity()) {
- throw new RuntimeError(expr.paren, "Expected " + fun.arity() + " arguments bug got " +
- arguments.size() + ".");
- }
- return fun.call(this, arguments);
- }
- @Override
- public Object visitGroupingExpr(Expr.Grouping expr) {
- return evaluate(expr.expression);
- }
- @Override
- public Object visitLiteralExpr(Expr.Literal expr) {
- return expr.value;
- }
- @Override
- public Object visitLogicalExpr(Expr.Logical expr) {
- Object left = evaluate(expr.left);
- if (expr.operator.type == TokenType.OR) {
- if (isTruthy(left)) return left;
- } else {
- if (!isTruthy(left)) return left;
- }
- return evaluate(expr.right);
- }
- @Override
- public Object visitUnaryExpr(Expr.Unary expr) {
- Object right = evaluate(expr.right);
- return switch (expr.operator.type) {
- case MINUS -> {
- checkNumberOperand(expr.operator, right);
- yield -(double) right;
- }
- case BANG -> !isTruthy(right);
- default -> null;
- };
- }
- @Override
- public Object visitVariableExpr(Expr.Variable expr) {
- // return environment.get(expr.name);
- return lookUpVariable(expr.name, expr);
- }
- private Object lookUpVariable(Token name, Expr expr) {
- Integer distance = locals.get(expr);
- if (distance != null) {
- return environment.getAt(distance, name.lexeme);
- } else {
- return globals.get(name);
- }
- }
- @Override
- public Void visitBlockStmt(Stmt.Block stmt) {
- executeBlock(stmt.statements, new Environment(environment));
- return null;
- }
- @Override
- public Void visitClassStmt(Stmt.Class stmt) {
- environment.define(stmt.name.lexeme, null);
- LoxClass klass = new LoxClass(stmt.name.lexeme);
- environment.assign(stmt.name, klass);
- return null;
- }
- @Override
- public Void visitExpressionStmt(Stmt.Expression stmt) {
- Object value = evaluate(stmt.expression);
- System.out.println(" " + stringify(value));
- return null;
- }
- @Override
- public Void visitFunctionStmt(Stmt.Function stmt) {
- LoxFunction function = new LoxFunction(stmt, environment);
- environment.define(stmt.name.lexeme, function);
- return null;
- }
- @Override
- public Void visitIfStmt(Stmt.If stmt) {
- if (isTruthy(evaluate(stmt.condition))) {
- execute(stmt.thenBranch);
- } else if (stmt.elseBranch != null) {
- execute(stmt.elseBranch);
- }
- return null;
- }
- @Override
- public Void visitPrintStmt(Stmt.Print stmt) {
- Object value = evaluate(stmt.expression);
- System.out.println(" " + stringify(value));
- return null;
- }
- @Override
- public Void visitReturnStmt(Stmt.Return stmt) {
- Object value = null;
- if (stmt.value != null) value = evaluate(stmt.value);
- throw new Return(value);
- }
- @Override
- public Void visitVarStmt(Stmt.Var stmt) {
- Object value = null;
- if (stmt.initializer != null) {
- value = evaluate(stmt.initializer);
- }
- environment.define(stmt.name.lexeme, value);
- return null;
- }
- @Override
- public Void visitWhileStmt(Stmt.While stmt) {
- while (isTruthy(evaluate(stmt.condition))) {
- execute(stmt.body);
- }
- return null;
- }
- private void execute(Stmt stmt) {
- stmt.accept(this);
- }
- void resolve(Expr expr, int depth) {
- locals.put(expr, depth);
- }
- protected void executeBlock(List<Stmt> statements, Environment environment) {
- Environment previous = this.environment;
- try {
- this.environment = environment;
- for (Stmt statement : statements) {
- execute(statement);
- }
- } finally {
- this.environment = previous;
- }
- }
- private Object evaluate(Expr expr) {
- return expr.accept(this);
- }
- private boolean isTruthy(Object object) {
- if (object == null) return false;
- if (object instanceof Boolean) return (boolean) object;
- return true;
- }
- private boolean isEqual(Object a, Object b) {
- if (a == null && b == null) return true;
- if (a == null) return false;
- return a.equals(b);
- }
- private String stringify(Object object) {
- if (object == null) return "nil";
- if (object instanceof Double) {
- String text = object.toString();
- if (text.endsWith(".0")) {
- text = text.substring(0, text.length() - 2);
- }
- return text;
- }
- return object.toString();
- }
- private void checkNumberOperand(Token operator, Object operand) {
- if (operand instanceof Double) return;
- throw new RuntimeError(operator, "Operand must be a number.");
- }
- private void checkNumberOperand(Token operator, Object left, Object right) {
- if (left instanceof Double && right instanceof Double) return;
- throw new RuntimeError(operator, "Operands must be numbers.");
- }
- }
|