chapter 11 in java

This commit is contained in:
Sebastian Hugentobler 2025-02-27 14:25:40 +01:00
parent a98d249399
commit 946d2dd1cb
4 changed files with 274 additions and 2 deletions

View File

@ -76,4 +76,30 @@ class Environment {
void define(String name, Object value) { void define(String name, Object value) {
values.put(name, value); values.put(name, value);
} }
/**
* Get a variable value at the n-th parent environment of the current one.
*/
Object getAt(int distance, String name) {
return ancestor(distance).values.get(name);
}
/**
* Assign a variable value at the n-th parent environment of the current one.
*/
void assignAt(int distance, Token name, Object value) {
ancestor(distance).values.put(name.lexeme(), value);
}
/**
* Get the n-th parent environment of the current one.
*/
Environment ancestor(int distance) {
Environment environment = this;
for (int i = 0; i < distance; i++) {
environment = environment.enclosing;
}
return environment;
}
} }

View File

@ -1,7 +1,9 @@
package ch.vanwa.lox_interpreter; package ch.vanwa.lox_interpreter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* Evaluate Lox expressions. * Evaluate Lox expressions.
@ -9,6 +11,7 @@ import java.util.List;
class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> { class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
final Environment globals = new Environment(); final Environment globals = new Environment();
private Environment environment = globals; private Environment environment = globals;
private final Map<Expr, Integer> locals = new HashMap<>();
Interpreter() { Interpreter() {
globals.define("clock", new LoxCallable() { globals.define("clock", new LoxCallable() {
@ -73,6 +76,10 @@ class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
stmt.accept(this); stmt.accept(this);
} }
void resolve(Expr expr, int depth) {
locals.put(expr, depth);
}
/** /**
* Execute all statements within a block while using lexical scoping for * Execute all statements within a block while using lexical scoping for
* variables. * variables.
@ -158,7 +165,14 @@ class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
@Override @Override
public Object visitAssignExpr(Expr.Assign expr) { public Object visitAssignExpr(Expr.Assign expr) {
Object value = evaluate(expr.value); Object value = evaluate(expr.value);
environment.assign(expr.name, value);
Integer distance = locals.get(expr);
if (distance != null) {
environment.assignAt(distance, expr.name, value);
} else {
globals.assign(expr.name, value);
}
return value; return value;
} }
@ -251,7 +265,16 @@ class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
@Override @Override
public Object visitVariableExpr(Expr.Variable expr) { public Object visitVariableExpr(Expr.Variable expr) {
return environment.get(expr.name); return lookUpVariable(expr.name, expr);
}
private Object lookUpVariable(Token name, Expr expr) {
Integer distance = locals.get(expr);
if (distance != null) {
return environment.getAt(distance, name.lexeme());
} else {
return globals.get(name);
}
} }
/** /**

View File

@ -75,6 +75,14 @@ public class Lox {
return; return;
} }
Resolver resolver = new Resolver(interpreter);
resolver.resolve(statements);
// Stop if there was a resolution error.
if (hadError) {
return;
}
interpreter.interpret(statements); interpreter.interpret(statements);
} }

View File

@ -0,0 +1,215 @@
package ch.vanwa.lox_interpreter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
class Resolver implements Expr.Visitor<Void>, Stmt.Visitor<Void> {
private final Interpreter interpreter;
private final Stack<Map<String, Boolean>> scopes = new Stack<>();
private FunctionType currentFunction = FunctionType.NONE;
Resolver(Interpreter interpreter) {
this.interpreter = interpreter;
}
private enum FunctionType {
NONE,
FUNCTION
}
@Override
public Void visitBlockStmt(Stmt.Block stmt) {
beginScope();
resolve(stmt.statements);
endScope();
return null;
}
@Override
public Void visitExpressionStmt(Stmt.Expression stmt) {
resolve(stmt.expression);
return null;
}
@Override
public Void visitFunctionStmt(Stmt.Function stmt) {
declare(stmt.name);
define(stmt.name);
resolveFunction(stmt, FunctionType.FUNCTION);
return null;
}
@Override
public Void visitIfStmt(Stmt.If stmt) {
resolve(stmt.condition);
resolve(stmt.thenBranch);
if (stmt.elseBranch != null)
resolve(stmt.elseBranch);
return null;
}
@Override
public Void visitPrintStmt(Stmt.Print stmt) {
resolve(stmt.expression);
return null;
}
@Override
public Void visitReturnStmt(Stmt.Return stmt) {
if (currentFunction == FunctionType.NONE) {
Lox.error(stmt.keyword, "Can't return from top-level code.");
}
if (stmt.value != null) {
resolve(stmt.value);
}
return null;
}
@Override
public Void visitVariableExpr(Expr.Variable expr) {
if (!scopes.isEmpty() &&
scopes.peek().get(expr.name.lexeme()) == Boolean.FALSE) {
Lox.error(expr.name,
"Can't read local variable in its own initializer.");
}
resolveLocal(expr, expr.name);
return null;
}
@Override
public Void visitVarStmt(Stmt.Var stmt) {
declare(stmt.name);
if (stmt.initializer != null) {
resolve(stmt.initializer);
}
define(stmt.name);
return null;
}
@Override
public Void visitWhileStmt(Stmt.While stmt) {
resolve(stmt.condition);
resolve(stmt.body);
return null;
}
@Override
public Void visitAssignExpr(Expr.Assign expr) {
resolve(expr.value);
resolveLocal(expr, expr.name);
return null;
}
@Override
public Void visitBinaryExpr(Expr.Binary expr) {
resolve(expr.left);
resolve(expr.right);
return null;
}
@Override
public Void visitCallExpr(Expr.Call expr) {
resolve(expr.callee);
for (Expr argument : expr.arguments) {
resolve(argument);
}
return null;
}
@Override
public Void visitGroupingExpr(Expr.Grouping expr) {
resolve(expr.expression);
return null;
}
@Override
public Void visitLiteralExpr(Expr.Literal expr) {
return null;
}
@Override
public Void visitLogicalExpr(Expr.Logical expr) {
resolve(expr.left);
resolve(expr.right);
return null;
}
@Override
public Void visitUnaryExpr(Expr.Unary expr) {
resolve(expr.right);
return null;
}
void resolve(List<Stmt> statements) {
for (Stmt statement : statements) {
resolve(statement);
}
}
private void resolve(Stmt stmt) {
stmt.accept(this);
}
private void resolve(Expr expr) {
expr.accept(this);
}
private void beginScope() {
scopes.push(new HashMap<String, Boolean>());
}
private void endScope() {
scopes.pop();
}
private void declare(Token name) {
if (scopes.isEmpty()) {
return;
}
Map<String, Boolean> scope = scopes.peek();
if (scope.containsKey(name.lexeme())) {
Lox.error(name,
"Already a variable with this name in this scope.");
}
scope.put(name.lexeme(), false);
}
private void define(Token name) {
if (scopes.isEmpty())
return;
scopes.peek().put(name.lexeme(), true);
}
private void resolveLocal(Expr expr, Token name) {
for (int i = scopes.size() - 1; i >= 0; i--) {
if (scopes.get(i).containsKey(name.lexeme())) {
interpreter.resolve(expr, scopes.size() - 1 - i);
return;
}
}
}
private void resolveFunction(Stmt.Function function, FunctionType type) {
FunctionType enclosingFunction = currentFunction;
currentFunction = type;
beginScope();
for (Token param : function.params) {
declare(param);
define(param);
}
resolve(function.body);
endScope();
currentFunction = enclosingFunction;
}
}