Implement Expression interpreter

This commit is contained in:
ctsk
2022-09-08 11:30:26 +02:00
parent e1247c3360
commit 57681d2eeb
3 changed files with 129 additions and 5 deletions

View File

@@ -0,0 +1,108 @@
package xyz.ctsk.lox;
public class Interpreter implements Expr.Visitor<Object> {
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);
}
private String stringify(Object object) {
if (object == null) return "nil";
var text = object.toString();
if (object instanceof Double && text.endsWith(".0")) {
text = text.substring(0, text.length() - 2);
}
return text;
}
@Override
public Object visitBinaryExpr(Expr.Binary binary) {
var left = evaluate(binary.left());
var right = evaluate(binary.right());
switch (binary.operator().type()) {
case MINUS, SLASH, STAR, GREATER, GREATER_EQUAL, LESS, LESS_EQUAL ->
checkNumberOperands(binary.operator(), left, right);
}
return switch (binary.operator().type()) {
case MINUS -> (double) left - (double) right;
case PLUS -> {
if (left instanceof Double leftD && right instanceof Double rightD) {
yield leftD + rightD;
}
if (left instanceof String leftStr && right instanceof String rightStr) {
yield leftStr + rightStr;
}
throw new RuntimeError(binary.operator(), "Operands must be two numbers or two strings.");
}
case SLASH -> (double) left / (double) right;
case STAR -> (double) left * (double) right;
case GREATER -> (double)left > (double)right;
case GREATER_EQUAL -> (double)left >= (double)right;
case LESS -> (double)left < (double)right;
case LESS_EQUAL -> (double)left <= (double)right;
case BANG_EQUAL -> !isEqual(left, right);
case EQUAL_EQUAL -> isEqual(left, right);
default -> null;
};
}
@Override
public Object visitGroupingExpr(Expr.Grouping grouping) {
return evaluate(grouping.expression());
}
@Override
public Object visitLiteralExpr(Expr.Literal literal) {
return literal.value();
}
@Override
public Object visitUnaryExpr(Expr.Unary unary) {
var right = evaluate(unary.right());
return switch(unary.operator().type()) {
case MINUS -> -asNumber(unary.operator(), right);
case BANG -> !isTruthy(right);
default -> null;
};
}
private boolean isTruthy(Object object) {
if (object == null) return false;
if (object instanceof Boolean bool) return bool;
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 double asNumber(Token operator, Object operand) {
if (operand instanceof Double d) return d;
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");
}
}

View File

@@ -8,7 +8,10 @@ import java.nio.file.Files;
import java.nio.file.Paths;
public class Lox {
private static final Interpreter interpreter = new Interpreter();
private static boolean hadError = false;
private static boolean hadRuntimeError = false;
private static void report(int line, String where, String message) {
System.err.printf("[line %d] Error %s: %s%n", line, where, message);
@@ -27,6 +30,11 @@ public class Lox {
}
}
static void runtimeError(RuntimeError error) {
System.err.printf("%s%n[line %d]%n", error.getMessage(), error.token.line());
hadRuntimeError = true;
}
private static void run(String source) {
var scanner = new Scanner(source);
var tokens = scanner.scanTokens();
@@ -36,8 +44,7 @@ public class Lox {
if (hadError) return;
System.out.println(AstPrinter.polish(expression, true));
System.out.println(AstPrinter.reverse_polish(expression, false));
interpreter.interpret(expression);
}
private static void runPrompt() throws IOException {
@@ -57,9 +64,8 @@ public class Lox {
byte[] bytes = Files.readAllBytes(Paths.get(path));
run(new String(bytes, Charset.defaultCharset()));
if (hadError) {
System.exit(65);
}
if (hadError) System.exit(65);
if (hadRuntimeError) System.exit(70);
}
private static void printUsage() {

View File

@@ -0,0 +1,10 @@
package xyz.ctsk.lox;
public class RuntimeError extends RuntimeException {
final Token token;
RuntimeError(Token token, String message) {
super(message);
this.token = token;
}
}