summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Tom Willemse2020-11-11 21:33:02 -0800
committerGravatar Tom Willemse2020-11-11 21:33:02 -0800
commitee63307aee839c7ce5e7bf032402a3b05809c51e (patch)
treefeccbfc402460125b3182f504eabb153dc24e762
parenta78fe97f60c2ad39d3d6335c1d058bd8ee4921bb (diff)
downloadcrafting-interpreters-ee63307aee839c7ce5e7bf032402a3b05809c51e.tar.gz
crafting-interpreters-ee63307aee839c7ce5e7bf032402a3b05809c51e.zip
Add the interpreter
-rw-r--r--src/com/craftinginterpreters/lox/CMakeLists.txt2
-rw-r--r--src/com/craftinginterpreters/lox/Interpreter.java134
-rw-r--r--src/com/craftinginterpreters/lox/Lox.java16
-rw-r--r--src/com/craftinginterpreters/lox/RuntimeError.java10
4 files changed, 159 insertions, 3 deletions
diff --git a/src/com/craftinginterpreters/lox/CMakeLists.txt b/src/com/craftinginterpreters/lox/CMakeLists.txt
index c326c88..ca2ee27 100644
--- a/src/com/craftinginterpreters/lox/CMakeLists.txt
+++ b/src/com/craftinginterpreters/lox/CMakeLists.txt
@@ -14,4 +14,6 @@ add_jar(Lox
${EXPR_JAVA_FILENAME}
AstPrinter.java
Parser.java
+ Interpreter.java
+ RuntimeError.java
ENTRY_POINT com/craftinginterpreters/lox/Lox)
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<Object> {
+ @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);
+ }
+ }
+}
diff --git a/src/com/craftinginterpreters/lox/Lox.java b/src/com/craftinginterpreters/lox/Lox.java
index 586442d..a3334e3 100644
--- a/src/com/craftinginterpreters/lox/Lox.java
+++ b/src/com/craftinginterpreters/lox/Lox.java
@@ -9,7 +9,9 @@ import java.nio.file.Paths;
import java.util.List;
public class Lox {
- private static boolean hadError = false;
+ private static final Interpreter interpreter = new Interpreter();
+ public static boolean hadError = false;
+ public static boolean hadRuntimeError = false;
public static void main(String[] args) throws IOException {
if (args.length > 1) {
@@ -29,6 +31,8 @@ public class Lox {
// Indicate an error in the exit code.
if (hadError)
System.exit(65);
+ if (hadRuntimeError)
+ System.exit(70);
}
private static void runPrompt() throws IOException {
@@ -52,9 +56,10 @@ public class Lox {
Expr expression = parser.parse();
// Stop if there was a syntax error
- if (hadError) return;
+ if (hadError)
+ return;
- System.out.println(new AstPrinter().print(expression));
+ interpreter.interpret(expression);
}
public static void error(int line, String message) {
@@ -73,4 +78,9 @@ public class Lox {
report(token.line, " at '" + token.lexeme + "'", message);
}
}
+
+ public static void runtimeError(RuntimeError error) {
+ System.err.println(error.getMessage() + "\n[line " + error.token.line + "]");
+ hadRuntimeError = true;
+ }
}
diff --git a/src/com/craftinginterpreters/lox/RuntimeError.java b/src/com/craftinginterpreters/lox/RuntimeError.java
new file mode 100644
index 0000000..017865b
--- /dev/null
+++ b/src/com/craftinginterpreters/lox/RuntimeError.java
@@ -0,0 +1,10 @@
+package com.craftinginterpreters.lox;
+
+class RuntimeError extends RuntimeException {
+ final Token token;
+
+ RuntimeError(Token token, String message) {
+ super(message);
+ this.token = token;
+ }
+}