[Chapter7&Evaluation] - Adding expression evaluation from chapter 7
This commit is contained in:
parent
80ad509e6c
commit
920afcc25d
118
src/com/lox/Interpreter.java
Normal file
118
src/com/lox/Interpreter.java
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
package com.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.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@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:
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unreachable.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
private Object evaluate(Expr expr) {
|
||||||
|
return expr.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
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) {
|
||||||
|
String text = object.toString();
|
||||||
|
if (text.endsWith(".0")) {
|
||||||
|
text = text.substring(0, text.length() - 2);
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
return object.toString();
|
||||||
|
}
|
||||||
|
}
|
@ -9,7 +9,9 @@ import java.nio.file.Paths;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class Lox {
|
public class Lox {
|
||||||
|
private static final Interpreter interpreter = new Interpreter();
|
||||||
static boolean hadError = false;
|
static boolean hadError = false;
|
||||||
|
static boolean hadRuntimeError = false;
|
||||||
public static void main(String[] args) throws IOException {
|
public static void main(String[] args) throws IOException {
|
||||||
if (args.length > 1) {
|
if (args.length > 1) {
|
||||||
System.out.println("Usage: jlox [script]");
|
System.out.println("Usage: jlox [script]");
|
||||||
@ -29,8 +31,7 @@ public class Lox {
|
|||||||
|
|
||||||
// Stop if there was a syntax error.
|
// Stop if there was a syntax error.
|
||||||
if (hadError) return;
|
if (hadError) return;
|
||||||
|
interpreter.interpret(expression);
|
||||||
System.out.println(new AstPrinter().print(expression));
|
|
||||||
}
|
}
|
||||||
static void error(int line, String message) {
|
static void error(int line, String message) {
|
||||||
report(line, "", message);
|
report(line, "", message);
|
||||||
@ -51,11 +52,17 @@ public class Lox {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void runtimeError(RuntimeError error) {
|
||||||
|
System.err.println(error.getMessage() +
|
||||||
|
"\n[line " + error.token.line + "]");
|
||||||
|
hadRuntimeError = true;
|
||||||
|
}
|
||||||
|
|
||||||
private static void runFile(String path) throws IOException {
|
private static void runFile(String path) throws IOException {
|
||||||
byte[] bytes = Files.readAllBytes(Paths.get(path));
|
byte[] bytes = Files.readAllBytes(Paths.get(path));
|
||||||
run(new String(bytes, Charset.defaultCharset()));
|
run(new String(bytes, Charset.defaultCharset()));
|
||||||
if(hadError) System.exit(65);
|
if(hadError) System.exit(65);
|
||||||
|
if(hadRuntimeError) System.exit(70);
|
||||||
}
|
}
|
||||||
private static void runPrompt() throws IOException {
|
private static void runPrompt() throws IOException {
|
||||||
InputStreamReader input = new InputStreamReader(System.in);
|
InputStreamReader input = new InputStreamReader(System.in);
|
||||||
|
10
src/com/lox/RuntimeError.java
Normal file
10
src/com/lox/RuntimeError.java
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package com.lox;
|
||||||
|
|
||||||
|
class RuntimeError extends RuntimeException {
|
||||||
|
final Token token;
|
||||||
|
|
||||||
|
RuntimeError(Token token, String message) {
|
||||||
|
super(message);
|
||||||
|
this.token = token;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user