From ee63307aee839c7ce5e7bf032402a3b05809c51e Mon Sep 17 00:00:00 2001 From: Tom Willemse Date: Wed, 11 Nov 2020 21:33:02 -0800 Subject: Add the interpreter --- src/com/craftinginterpreters/lox/Interpreter.java | 134 ++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 src/com/craftinginterpreters/lox/Interpreter.java (limited to 'src/com/craftinginterpreters/lox/Interpreter.java') diff --git a/src/com/craftinginterpreters/lox/Interpreter.java b/src/com/craftinginterpreters/lox/Interpreter.java new file mode 100644 index 0000000..24cf1c0 --- /dev/null +++ b/src/com/craftinginterpreters/lox/Interpreter.java @@ -0,0 +1,134 @@ +package com.craftinginterpreters.lox; + +class Interpreter implements Expr.Visitor { + @Override + public Object visitLiteralExpr(Expr.Literal expr) { + return expr.value; + } + + @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; + } + + // Unreachable. + return null; + } + + private void checkNumberOperand(Token operator, Object operand) { + if (operand instanceof Double) + return; + throw new RuntimeError(operator, "Operand 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 numbers."); + } + + 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(); + } + + @Override + public Object visitGroupingExpr(Expr.Grouping expr) { + return evaluate(expr.expression); + } + + private Object evaluate(Expr expr) { + return expr.accept(this); + } + + @Override + public Object visitBinaryExpr(Expr.Binary expr) { + Object left = evaluate(expr.left); + Object right = evaluate(expr.right); + + switch (expr.operator.type) { + 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); + case MINUS: + 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 SLASH: + checkNumberOperands(expr.operator, left, right); + return (double) left / (double) right; + case STAR: + checkNumberOperands(expr.operator, left, right); + return (double) left * (double) right; + } + + // Unreachable. + return null; + } + + public void interpret(Expr expression) { + try { + Object value = evaluate(expression); + System.out.println(stringify(value)); + } catch (RuntimeError error) { + Lox.runtimeError(error); + } + } +} -- cgit v1.2.3-54-g00ecf