From 9d057476552a554f3636aa8b685e4e5cbea506b7 Mon Sep 17 00:00:00 2001 From: Ashley Rose Date: Sat, 1 Nov 2025 22:09:40 -0500 Subject: [PATCH] Evaluating Expressions --- src/main/java/me/rose/lox/.clang-format | 2 + src/main/java/me/rose/lox/Interpreter.java | 164 ++++++++++++++++++++ src/main/java/me/rose/lox/Lox.java | 14 ++ src/main/java/me/rose/lox/RuntimeError.java | 12 ++ src/main/java/me/rose/tool/.clang-format | 2 + 5 files changed, 194 insertions(+) create mode 100644 src/main/java/me/rose/lox/.clang-format create mode 100644 src/main/java/me/rose/lox/Interpreter.java create mode 100644 src/main/java/me/rose/lox/RuntimeError.java create mode 100644 src/main/java/me/rose/tool/.clang-format diff --git a/src/main/java/me/rose/lox/.clang-format b/src/main/java/me/rose/lox/.clang-format new file mode 100644 index 0000000..1aeac0d --- /dev/null +++ b/src/main/java/me/rose/lox/.clang-format @@ -0,0 +1,2 @@ +--- +BasedOnStyle: GNU diff --git a/src/main/java/me/rose/lox/Interpreter.java b/src/main/java/me/rose/lox/Interpreter.java new file mode 100644 index 0000000..1c6e561 --- /dev/null +++ b/src/main/java/me/rose/lox/Interpreter.java @@ -0,0 +1,164 @@ +package me.rose.lox; + +class Interpreter implements Expr.Visitor +{ + void + interpret (Expr expression) + { + try + { + Object value = evaluate (expression); + System.out.println (stringify (value)); + } + catch (RuntimeError error) + { + Lox.runtimeError (error); + } + } + + private Object + evaluate (Expr expr) + { + return expr.accept (this); + } + + @Override + public Object + visitGroupingExpr (Expr.Grouping expr) + { + return evaluate (expr.expression); + } + + @Override + public Object + visitBinaryExpr (Expr.Binary expr) + { + Object left = evaluate (expr.left); + Object right = evaluate (expr.right); + + switch (expr.operator.type) + { + case GREATER: + checkNumberOperand (expr.operator, left, right); + return (double)left > (double)right; + case GREATER_EQUAL: + checkNumberOperand (expr.operator, left, right); + return (double)left >= (double)right; + case LESS: + checkNumberOperand (expr.operator, left, right); + return (double)left < (double)right; + case LESS_EQUAL: + checkNumberOperand (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: + checkNumberOperand (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: + checkNumberOperand (expr.operator, left, right); + return (double)left / (double)right; + case STAR: + checkNumberOperand (expr.operator, left, right); + return (double)left * (double)right; + } + + return null; + } + + @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; + } + + 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 + checkNumberOperand (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 (); + } +} diff --git a/src/main/java/me/rose/lox/Lox.java b/src/main/java/me/rose/lox/Lox.java index d9edbb8..97cf44e 100644 --- a/src/main/java/me/rose/lox/Lox.java +++ b/src/main/java/me/rose/lox/Lox.java @@ -10,7 +10,9 @@ import java.util.List; public class Lox { + private static final Interpreter interpreter = new Interpreter (); static boolean hadError = false; + static boolean hadRuntimeError = false; public static void main (String[] args) throws IOException @@ -38,6 +40,8 @@ public class Lox if (hadError) System.exit (65); + if (hadRuntimeError) + System.exit (70); } private static void @@ -67,6 +71,8 @@ public class Lox if (hadError) return; + + interpreter.interpret (expression); } static void @@ -75,6 +81,14 @@ public class Lox report (line, "", message); } + static void + runtimeError (RuntimeError error) + { + System.err.println (error.getMessage () + "\n[line " + error.token.line + + "]"); + hadRuntimeError = true; + } + private static void report (int line, String where, String message) { diff --git a/src/main/java/me/rose/lox/RuntimeError.java b/src/main/java/me/rose/lox/RuntimeError.java new file mode 100644 index 0000000..789ab55 --- /dev/null +++ b/src/main/java/me/rose/lox/RuntimeError.java @@ -0,0 +1,12 @@ +package me.rose.lox; + +class RuntimeError extends RuntimeException +{ + final Token token; + + RuntimeError (Token token, String message) + { + super (message); + this.token = token; + } +} diff --git a/src/main/java/me/rose/tool/.clang-format b/src/main/java/me/rose/tool/.clang-format new file mode 100644 index 0000000..1aeac0d --- /dev/null +++ b/src/main/java/me/rose/tool/.clang-format @@ -0,0 +1,2 @@ +--- +BasedOnStyle: GNU