package lox; import java.util.List; class Interpreter implements Expr.Visitor, Stmt.Visitor { private Environment environment = new Environment(); void interpret(List statements) { try { for (Stmt statement : statements) { execute(statement); } } catch (RuntimeError error) { Lox.runtimeError(error); } } @Override public Object visitLiteralExpr(Expr.Literal expr) { return expr.value; } @Override public Object visitGroupingExpr(Expr.Grouping expr) { return evaluate(expr.expression); } @Override public Object visitUnaryExpr(Expr.Unary expr) { Object right = evaluate(expr.right); switch (expr.operator.type) { case BANG: return !isTruthy(right); case MINUS: checkNumberOperand(expr.operator, right); return -(double) right; } // actually unreachable return null; } @Override public Object visitBinaryExpr(Expr.Binary expr) { Object left = evaluate(expr.left); Object right = evaluate(expr.right); switch (expr.operator.type) { case MINUS: checkNumberOperands(expr.operator, left, right); return (double) left - (double) right; case SLASH: checkNumberOperands(expr.operator, left, right); return (double) left / (double) right; case STAR: checkNumberOperands(expr.operator, left, right); return (double) left * (double) right; case PLUS: if (left instanceof Double && right instanceof Double) { return (double) left + (double) right; } if (left instanceof String && right instanceof String) { return (String) left + (String) right; } throw new RuntimeError(expr.operator, "Operands must be two numbers or two strings"); case GREATER: checkNumberOperands(expr.operator, left, right); return (double) left > (double) right; case GREATER_EQUAL: checkNumberOperands(expr.operator, left, right); return (double) left >= (double) right; case LESS: checkNumberOperands(expr.operator, left, right); return (double) left < (double) right; case LESS_EQUAL: checkNumberOperands(expr.operator, left, right); return (double) left <= (double) right; case BANG_EQUAL: return !isEqual(left, right); case EQUAL_EQUAL: return isEqual(left, right); } // actually unreachable return null; } @Override public Object visitVariableExpr(Expr.Variable expr) { return environment.get(expr.name); } @Override public Void visitExpressionStmt(Stmt.Expression stmt) { evaluate(stmt.expression); return null; } @Override public Void visitPrintStmt(Stmt.Print stmt) { Object value = evaluate(stmt.expression); System.out.println(stringify(value)); return null; } @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; } private void checkNumberOperand(Token operator, Object operand) { if (operand instanceof Double) { return; } throw new RuntimeError(operator, "Operator must be a number."); } private void checkNumberOperands(Token operator, Object left, Object right) { if (left instanceof Double && right instanceof Double) { return; } throw new RuntimeError(operator, "Operands must be a numbers."); } private boolean isTruthy(Object object) { if (object == null) { return false; // null is false } if (object instanceof Boolean) { return (boolean) object; // booleans are their own truthiness } return true; // everything else is true } private boolean isEqual(Object left, Object right) { if (left == null && right == null) { return true; // null is equal to another null } if (left == null) { return false; // null is never equal to anything else } return left.equals(right); } 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 Object evaluate(Expr expr) { return expr.accept(this); } private void execute(Stmt stmt) { stmt.accept(this); } }