statements

This commit is contained in:
David 2021-09-18 16:09:43 -04:00
parent f5e4606073
commit d6cbc63c18
6 changed files with 242 additions and 7 deletions

153
src/lox/Interpreter.java Normal file
View 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);
}
}

View File

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

View File

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

View File

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