statements
This commit is contained in:
parent
f5e4606073
commit
d6cbc63c18
153
src/lox/Interpreter.java
Normal file
153
src/lox/Interpreter.java
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
package lox;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
|
||||||
|
void interpret(List<Stmt> statements) {
|
||||||
|
try {
|
||||||
|
for (Stmt statement : statements) {
|
||||||
|
execute(statement);
|
||||||
|
}
|
||||||
|
} catch (RuntimeError error) {
|
||||||
|
Lox.runtimeError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitLiteralExpr(Expr.Literal expr) {
|
||||||
|
return expr.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitGroupingExpr(Expr.Grouping expr) {
|
||||||
|
return evaluate(expr.expression);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
// actually unreachable
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitBinaryExpr(Expr.Binary expr) {
|
||||||
|
Object left = evaluate(expr.left);
|
||||||
|
Object right = evaluate(expr.right);
|
||||||
|
|
||||||
|
switch (expr.operator.type) {
|
||||||
|
case MINUS:
|
||||||
|
checkNumberOperands(expr.operator, left, right);
|
||||||
|
return (double) left - (double) right;
|
||||||
|
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 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 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);
|
||||||
|
}
|
||||||
|
// actually unreachable
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Void visitExpressionStmt(Stmt.Expression stmt) {
|
||||||
|
evaluate(stmt.expression);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Void visitPrintStmt(Stmt.Print stmt) {
|
||||||
|
Object value = evaluate(stmt.expression);
|
||||||
|
System.out.println(stringify(value));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkNumberOperand(Token operator, Object operand) {
|
||||||
|
if (operand instanceof Double) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new RuntimeError(operator, "Operator 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 a numbers.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isTruthy(Object object) {
|
||||||
|
if (object == null) {
|
||||||
|
return false; // null is false
|
||||||
|
}
|
||||||
|
if (object instanceof Boolean) {
|
||||||
|
return (boolean) object; // booleans are their own truthiness
|
||||||
|
}
|
||||||
|
return true; // everything else is true
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isEqual(Object left, Object right) {
|
||||||
|
if (left == null && right == null) {
|
||||||
|
return true; // null is equal to another null
|
||||||
|
}
|
||||||
|
if (left == null) {
|
||||||
|
return false; // null is never equal to anything else
|
||||||
|
}
|
||||||
|
return left.equals(right);
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object evaluate(Expr expr) {
|
||||||
|
return expr.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void execute(Stmt stmt) {
|
||||||
|
stmt.accept(this);
|
||||||
|
}
|
||||||
|
}
|
@ -11,7 +11,9 @@ import java.util.List;
|
|||||||
import static lox.TokenType.EOF;
|
import static lox.TokenType.EOF;
|
||||||
|
|
||||||
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) {
|
||||||
@ -32,6 +34,9 @@ public class Lox {
|
|||||||
if (hadError) {
|
if (hadError) {
|
||||||
System.exit(65);
|
System.exit(65);
|
||||||
}
|
}
|
||||||
|
if (hadRuntimeError) {
|
||||||
|
System.exit(70);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void runPrompt() throws IOException {
|
private static void runPrompt() throws IOException {
|
||||||
@ -53,19 +58,24 @@ public class Lox {
|
|||||||
Scanner scanner = new Scanner(source);
|
Scanner scanner = new Scanner(source);
|
||||||
List<Token> tokens = scanner.scanTokens();
|
List<Token> tokens = scanner.scanTokens();
|
||||||
Parser parser = new Parser(tokens);
|
Parser parser = new Parser(tokens);
|
||||||
Expr expression = parser.parse();
|
List<Stmt> statements = parser.parse();
|
||||||
|
|
||||||
if (hadError) {
|
if (hadError) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
System.out.println(new ASTPrinter().print(expression));
|
interpreter.interpret(statements);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void error(int line, String message) {
|
static void error(int line, String message) {
|
||||||
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 report(int line, String where, String message) {
|
private static void report(int line, String where, String message) {
|
||||||
System.err.println("[line " + line + "] Error" + where + ": " + message);
|
System.err.println("[line " + line + "] Error" + where + ": " + message);
|
||||||
hadError = true;
|
hadError = true;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package lox;
|
package lox;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static lox.TokenType.*;
|
import static lox.TokenType.*;
|
||||||
@ -14,12 +15,31 @@ class Parser {
|
|||||||
this.tokens = tokens;
|
this.tokens = tokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr parse() {
|
List<Stmt> parse() {
|
||||||
try {
|
List<Stmt> statements = new ArrayList<>();
|
||||||
return expression();
|
while (!isAtEnd()) {
|
||||||
} catch (ParseError error) {
|
statements.add(statement());
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
return statements;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Stmt statement() {
|
||||||
|
if (match(PRINT)) {
|
||||||
|
return printStatement();
|
||||||
|
}
|
||||||
|
return expressionStatement();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Stmt printStatement() {
|
||||||
|
Expr value = expression();
|
||||||
|
consume(SEMICOLON, "Expect ';' after value.");
|
||||||
|
return new Stmt.Print(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Stmt expressionStatement() {
|
||||||
|
Expr value = expression();
|
||||||
|
consume(SEMICOLON, "Expect ';' after value.");
|
||||||
|
return new Stmt.Expression(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Expr expression() {
|
private Expr expression() {
|
||||||
|
10
src/lox/RuntimeError.java
Normal file
10
src/lox/RuntimeError.java
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package lox;
|
||||||
|
|
||||||
|
class RuntimeError extends RuntimeException {
|
||||||
|
final Token token;
|
||||||
|
|
||||||
|
RuntimeError(Token token, String message) {
|
||||||
|
super(message);
|
||||||
|
this.token = token;
|
||||||
|
}
|
||||||
|
}
|
37
src/lox/Stmt.java
Normal file
37
src/lox/Stmt.java
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package lox;
|
||||||
|
|
||||||
|
abstract class Stmt {
|
||||||
|
interface Visitor<R> {
|
||||||
|
R visitExpressionStmt(Expression stmt);
|
||||||
|
|
||||||
|
R visitPrintStmt(Print stmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
static class Expression extends Stmt {
|
||||||
|
Expression(Expr expression) {
|
||||||
|
this.expression = expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<R> R accept(Visitor<R> visitor) {
|
||||||
|
return visitor.visitExpressionStmt(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Expr expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
static class Print extends Stmt {
|
||||||
|
Print(Expr expression) {
|
||||||
|
this.expression = expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<R> R accept(Visitor<R> visitor) {
|
||||||
|
return visitor.visitPrintStmt(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Expr expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract <R> R accept(Visitor<R> visitor);
|
||||||
|
}
|
@ -21,6 +21,11 @@ public class GenerateAST {
|
|||||||
"Grouping : Expr expression",
|
"Grouping : Expr expression",
|
||||||
"Literal : Object value",
|
"Literal : Object value",
|
||||||
"Unary : Token operator, Expr right"));
|
"Unary : Token operator, Expr right"));
|
||||||
|
|
||||||
|
defineAST(
|
||||||
|
outputDir,
|
||||||
|
"Stmt",
|
||||||
|
Arrays.asList("Expression : Expr expression", "Print : Expr expression"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void defineAST(String outputDir, String baseName, List<String> types)
|
private static void defineAST(String outputDir, String baseName, List<String> types)
|
||||||
|
Loading…
Reference in New Issue
Block a user