scopes
This commit is contained in:
parent
eca4492492
commit
36e56567db
@ -19,6 +19,14 @@ class Environment {
|
|||||||
values.put(name, value);
|
values.put(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Environment ancestor(int distance) {
|
||||||
|
Environment environment = this;
|
||||||
|
for (int i = 0; i < distance; i++) {
|
||||||
|
environment = environment.enclosing;
|
||||||
|
}
|
||||||
|
return environment;
|
||||||
|
}
|
||||||
|
|
||||||
void assign(Token name, Object value) {
|
void assign(Token name, Object value) {
|
||||||
if (values.containsKey(name.lexeme)) {
|
if (values.containsKey(name.lexeme)) {
|
||||||
values.put(name.lexeme, value);
|
values.put(name.lexeme, value);
|
||||||
@ -33,6 +41,10 @@ class Environment {
|
|||||||
throw new RuntimeError(name, "Undefined variable '" + name.lexeme + "'.");
|
throw new RuntimeError(name, "Undefined variable '" + name.lexeme + "'.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void assignAt(int distance, Token name, Object value) {
|
||||||
|
ancestor(distance).values.put(name.lexeme, value);
|
||||||
|
}
|
||||||
|
|
||||||
Object get(Token name) {
|
Object get(Token name) {
|
||||||
if (values.containsKey(name.lexeme)) {
|
if (values.containsKey(name.lexeme)) {
|
||||||
return values.get(name.lexeme);
|
return values.get(name.lexeme);
|
||||||
@ -44,4 +56,8 @@ class Environment {
|
|||||||
|
|
||||||
throw new RuntimeError(name, "Undefined variable '" + name.lexeme + "'.");
|
throw new RuntimeError(name, "Undefined variable '" + name.lexeme + "'.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Object getAt(int distance, String name) {
|
||||||
|
return ancestor(distance).values.get(name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
package lox;
|
package lox;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
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(
|
globals.define(
|
||||||
@ -147,13 +150,29 @@ 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -276,6 +295,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);
|
||||||
|
}
|
||||||
|
|
||||||
void executeBlock(List<Stmt> statements, Environment environment) {
|
void executeBlock(List<Stmt> statements, Environment environment) {
|
||||||
Environment previous = this.environment;
|
Environment previous = this.environment;
|
||||||
try {
|
try {
|
||||||
|
@ -64,6 +64,13 @@ public class Lox {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Resolver resolver = new Resolver(interpreter);
|
||||||
|
resolver.resolve(statements);
|
||||||
|
|
||||||
|
if (hadError) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
interpreter.interpret(statements);
|
interpreter.interpret(statements);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
209
src/lox/Resolver.java
Normal file
209
src/lox/Resolver.java
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
package 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
|
||||||
|
}
|
||||||
|
|
||||||
|
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 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() - i - 1);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void beginScope() {
|
||||||
|
scopes.push(new HashMap<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Void visitBlockStmt(Stmt.Block stmt) {
|
||||||
|
beginScope();
|
||||||
|
resolve(stmt);
|
||||||
|
endScope();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Void visitExpressionStmt(Stmt.Expression stmt) {
|
||||||
|
resolve(stmt.expression);
|
||||||
|
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 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 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 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;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user