Evaluating Expressions
This commit is contained in:
parent
bbd8a8ca66
commit
9d05747655
2
src/main/java/me/rose/lox/.clang-format
Normal file
2
src/main/java/me/rose/lox/.clang-format
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
---
|
||||||
|
BasedOnStyle: GNU
|
||||||
164
src/main/java/me/rose/lox/Interpreter.java
Normal file
164
src/main/java/me/rose/lox/Interpreter.java
Normal file
|
|
@ -0,0 +1,164 @@
|
||||||
|
package me.rose.lox;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -10,7 +10,9 @@ 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
|
public static void
|
||||||
main (String[] args) throws IOException
|
main (String[] args) throws IOException
|
||||||
|
|
@ -38,6 +40,8 @@ public class Lox
|
||||||
|
|
||||||
if (hadError)
|
if (hadError)
|
||||||
System.exit (65);
|
System.exit (65);
|
||||||
|
if (hadRuntimeError)
|
||||||
|
System.exit (70);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void
|
private static void
|
||||||
|
|
@ -67,6 +71,8 @@ public class Lox
|
||||||
|
|
||||||
if (hadError)
|
if (hadError)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
interpreter.interpret (expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
@ -75,6 +81,14 @@ public class Lox
|
||||||
report (line, "", message);
|
report (line, "", message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
runtimeError (RuntimeError error)
|
||||||
|
{
|
||||||
|
System.err.println (error.getMessage () + "\n[line " + error.token.line
|
||||||
|
+ "]");
|
||||||
|
hadRuntimeError = true;
|
||||||
|
}
|
||||||
|
|
||||||
private static void
|
private static void
|
||||||
report (int line, String where, String message)
|
report (int line, String where, String message)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
12
src/main/java/me/rose/lox/RuntimeError.java
Normal file
12
src/main/java/me/rose/lox/RuntimeError.java
Normal file
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
2
src/main/java/me/rose/tool/.clang-format
Normal file
2
src/main/java/me/rose/tool/.clang-format
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
---
|
||||||
|
BasedOnStyle: GNU
|
||||||
Loading…
Reference in a new issue