aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorGravatar Tom Willemse2021-03-03 22:44:52 -0800
committerGravatar Tom Willemse2021-03-03 22:44:52 -0800
commitb04b6bcf2f31526f6cfa593c342165a65cb9e7ac (patch)
treeaefe4490a9b7ce6adb48c826ef9c5da483335e55 /src
parent715e60b87ea39b0b2acc293e8639e8527c260f93 (diff)
downloadcrafting-interpreters-b04b6bcf2f31526f6cfa593c342165a65cb9e7ac.tar.gz
crafting-interpreters-b04b6bcf2f31526f6cfa593c342165a65cb9e7ac.zip
Chapter 11 - Resolving and Binding
Diffstat (limited to 'src')
-rw-r--r--src/com/craftinginterpreters/lox/CMakeLists.txt1
-rw-r--r--src/com/craftinginterpreters/lox/Environment.java17
-rw-r--r--src/com/craftinginterpreters/lox/Interpreter.java27
-rw-r--r--src/com/craftinginterpreters/lox/Lox.java7
-rw-r--r--src/com/craftinginterpreters/lox/Resolver.java210
5 files changed, 260 insertions, 2 deletions
diff --git a/src/com/craftinginterpreters/lox/CMakeLists.txt b/src/com/craftinginterpreters/lox/CMakeLists.txt
index fa93588..391331a 100644
--- a/src/com/craftinginterpreters/lox/CMakeLists.txt
+++ b/src/com/craftinginterpreters/lox/CMakeLists.txt
@@ -22,4 +22,5 @@ add_jar(Lox
LoxCallable.java
LoxFunction.java
Return.java
+ Resolver.java
ENTRY_POINT com/craftinginterpreters/lox/Lox)
diff --git a/src/com/craftinginterpreters/lox/Environment.java b/src/com/craftinginterpreters/lox/Environment.java
index 0a95282..65c6d88 100644
--- a/src/com/craftinginterpreters/lox/Environment.java
+++ b/src/com/craftinginterpreters/lox/Environment.java
@@ -43,4 +43,21 @@ class Environment {
void define(String name, Object value) {
values.put(name, value);
}
+
+ Environment ancestor(int distance) {
+ Environment environment = this;
+ for (int i = 0; i < distance; i++) {
+ environment = environment.enclosing;
+ }
+
+ return environment;
+ }
+
+ Object getAt(int distance, String name) {
+ return ancestor(distance).values.get(name);
+ }
+
+ void assignAt(int distance, Token name, Object value) {
+ ancestor(distance).values.put(name.lexeme, value);
+ }
}
diff --git a/src/com/craftinginterpreters/lox/Interpreter.java b/src/com/craftinginterpreters/lox/Interpreter.java
index 3e3be86..4092191 100644
--- a/src/com/craftinginterpreters/lox/Interpreter.java
+++ b/src/com/craftinginterpreters/lox/Interpreter.java
@@ -1,11 +1,14 @@
package com.craftinginterpreters.lox;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
final Environment globals = new Environment();
private Environment environment = globals;
+ private final Map<Expr, Integer> locals = new HashMap<>();
Interpreter() {
globals.define("clock", new LoxCallable() {
@@ -64,7 +67,16 @@ class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
@Override
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);
+ }
}
private void checkNumberOperand(Token operator, Object operand) {
@@ -125,6 +137,10 @@ class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
stmt.accept(this);
}
+ public void resolve(Expr expr, int depth) {
+ locals.put(expr, depth);
+ }
+
public void executeBlock(List<Stmt> statements, Environment environment) {
Environment previous = this.environment;
@@ -208,7 +224,14 @@ class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
@Override
public Object visitAssignExpr(Expr.Assign expr) {
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;
}
diff --git a/src/com/craftinginterpreters/lox/Lox.java b/src/com/craftinginterpreters/lox/Lox.java
index bca117e..6115f8f 100644
--- a/src/com/craftinginterpreters/lox/Lox.java
+++ b/src/com/craftinginterpreters/lox/Lox.java
@@ -59,6 +59,13 @@ public class Lox {
if (hadError)
return;
+ Resolver resolver = new Resolver(interpreter);
+ resolver.resolve(statements);
+
+ // Stop if there was a resolution error
+ if (hadError)
+ return;
+
interpreter.interpret(statements);
}
diff --git a/src/com/craftinginterpreters/lox/Resolver.java b/src/com/craftinginterpreters/lox/Resolver.java
new file mode 100644
index 0000000..2a870ab
--- /dev/null
+++ b/src/com/craftinginterpreters/lox/Resolver.java
@@ -0,0 +1,210 @@
+package com.craftinginterpreters.lox;
+
+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
+ }
+
+ public void resolve(List<Stmt> statements) {
+ for (Stmt statement : statements) {
+ resolve(statement);
+ }
+ }
+
+ @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 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;
+ }
+
+ @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;
+ }
+
+ private void resolve(Stmt stmt) {
+ stmt.accept(this);
+ }
+
+ private void resolve(Expr expr) {
+ expr.accept(this);
+ }
+
+ 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;
+ }
+
+ 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 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;
+ }
+ }
+ }
+}