Skip to content

Commit

Permalink
Add a very simple interpreter
Browse files Browse the repository at this point in the history
  • Loading branch information
tjarratt committed Jul 1, 2020
1 parent 148b91a commit cf7219e
Show file tree
Hide file tree
Showing 3 changed files with 147 additions and 1 deletion.
127 changes: 127 additions & 0 deletions src/com/craftinginterpreters/lox/Interpreter.java
@@ -0,0 +1,127 @@
package com.craftinginterpreters.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 String stringify(Object object) {
if (object == null) return "nil";
if (!(object instanceof Double)) return object.toString();

String text = object.toString();
if (text.endsWith(".0")) {
text = text.substring(0, text.length() - 2);
}
return text;
}

@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 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;

case BANG_EQUAL:
return !isEqual(left, right);
case EQUAL_EQUAL:
return isEqual(left, right);
}

return new RuntimeException("Unreachable code in visiting binary expression");
}

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 isEqual(Object left, Object right) {
if (left == null && right == null) return true;
if (left == null) return false;

return left.equals(right);
}

@Override
public Object visitGroupingExpr(Expr.Grouping expr) {
return evaluate(expr.expression);
}

@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;
}

// WE HOPE THIS NEVER HAPPENS
throw new RuntimeException("isn't this code unreachable ???");
}

private void checkNumberOperand(Token operator, Object operand) {
if (operand instanceof Double) return;

throw new RuntimeError(operator, "Operand must be a number");
}

private boolean isTruthy(Object object) {
if (object == null) return false;
if (object instanceof Boolean) return (boolean)object;

return true;
}

private Object evaluate(Expr expression) {
return expression.accept(this);
}
}
10 changes: 9 additions & 1 deletion src/com/craftinginterpreters/lox/Lox.java
Expand Up @@ -11,7 +11,9 @@
import static java.lang.System.exit;

public class Lox {
private static final Interpreter interpreter = new Interpreter();
private static boolean hadError = false;
private static boolean hadRuntimeError = false;

public static void main(String[] args) throws IOException {
if (args.length > 1) {
Expand All @@ -29,6 +31,7 @@ private static void runFile(String filePath) throws IOException {
run(new String(bytes, Charset.defaultCharset()));

if (hadError) exit(65);
if (hadRuntimeError) exit(70);
}

private static void run(String sourceCode) {
Expand All @@ -41,7 +44,7 @@ private static void run(String sourceCode) {
// return early if there was a syntax error
if (hadError) return;

System.out.println(new AstPrinter().print(expression));
interpreter.interpret(expression);
}

private static void runPrompt() throws IOException {
Expand Down Expand Up @@ -79,4 +82,9 @@ private static void report(int line, String where, String message) {

hadError = true;
}

public static void runtimeError(RuntimeError error) {
System.err.println(error.getMessage() + "\n[line " + error.token.line + "]");
hadRuntimeError = true;
}
}
11 changes: 11 additions & 0 deletions src/com/craftinginterpreters/lox/RuntimeError.java
@@ -0,0 +1,11 @@
package com.craftinginterpreters.lox;

public class RuntimeError extends RuntimeException {

final Token token;

public RuntimeError(Token token, String message) {
super(message);
this.token = token;
}
}

0 comments on commit cf7219e

Please sign in to comment.