scopes
This commit is contained in:
		@@ -19,6 +19,14 @@ class Environment {
 | 
			
		||||
    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) {
 | 
			
		||||
    if (values.containsKey(name.lexeme)) {
 | 
			
		||||
      values.put(name.lexeme, value);
 | 
			
		||||
@@ -33,6 +41,10 @@ class Environment {
 | 
			
		||||
    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) {
 | 
			
		||||
    if (values.containsKey(name.lexeme)) {
 | 
			
		||||
      return values.get(name.lexeme);
 | 
			
		||||
@@ -44,4 +56,8 @@ class Environment {
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
 | 
			
		||||
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(
 | 
			
		||||
@@ -147,13 +150,29 @@ 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);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @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;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -276,6 +295,10 @@ class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
 | 
			
		||||
    stmt.accept(this);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void resolve(Expr expr, int depth) {
 | 
			
		||||
    locals.put(expr, depth);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void executeBlock(List<Stmt> statements, Environment environment) {
 | 
			
		||||
    Environment previous = this.environment;
 | 
			
		||||
    try {
 | 
			
		||||
 
 | 
			
		||||
@@ -64,6 +64,13 @@ public class Lox {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Resolver resolver = new Resolver(interpreter);
 | 
			
		||||
    resolver.resolve(statements);
 | 
			
		||||
 | 
			
		||||
    if (hadError) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user