runningwater пре 2 година
родитељ
комит
5ef7aa1907

+ 43 - 29
src/main/java/com/craftinginterpreters/lox/Environment.java

@@ -1,5 +1,6 @@
 /* Copyright (C) 2019-2023 Hangzhou HSH Co. Ltd.
- * All right reserved.*/ package com.craftinginterpreters.lox;
+ * All right reserved.*/
+package com.craftinginterpreters.lox;
 
 import java.util.HashMap;
 import java.util.Map;
@@ -8,45 +9,58 @@ import java.util.Map;
  * @author simon
  * @date 2023-06-09 16:34
  * @desc The bindings that associate variables to values need to be stored somewhere. Ever since the
- *     Lisp folks invented parentheses, this data structure has been called an environment.
+ * Lisp folks invented parentheses, this data structure has been called an environment.
  */
 public class Environment {
-  private final Map<String, Object> values = new HashMap<>(16);
-  final Environment enclosing;
+    final Environment enclosing;
+    private final Map<String, Object> values = new HashMap<>(16);
 
-  public Environment() {
-    enclosing = null;
-  }
-
-  public Environment(Environment enclosing) {
-    this.enclosing = enclosing;
-  }
+    public Environment() {
+        enclosing = null;
+    }
 
-  void define(String name, Object value) {
-    values.put(name, value);
-  }
+    public Environment(Environment enclosing) {
+        this.enclosing = enclosing;
+    }
 
-  Object get(Token name) {
-    if (values.containsKey(name.lexeme)) {
-      return values.get(name.lexeme);
+    void define(String name, Object value) {
+        values.put(name, value);
     }
 
-    if (enclosing != null) return enclosing.get(name);
+    Object getAt(int distance, String name) {
+        return ancestor(distance).values.get(name);
+    }
 
-    throw new RuntimeError(name, "Undefined variable '" + name.lexeme + "'.");
-  }
+    Environment ancestor(int distance) {
+        Environment environment = this;
+        for (int i = 0; i < distance; i++) {
+            environment = environment.enclosing;
+        }
 
-  void assign(Token name, Object value) {
-    if (values.containsKey(name.lexeme)) {
-      values.put(name.lexeme, value);
-      return;
+        return environment;
     }
 
-    if (enclosing != null) {
-      enclosing.assign(name, value);
-      return;
+    Object get(Token name) {
+        if (values.containsKey(name.lexeme)) {
+            return values.get(name.lexeme);
+        }
+
+        if (enclosing != null) return enclosing.get(name);
+
+        throw new RuntimeError(name, "Undefined variable '" + name.lexeme + "'.");
     }
 
-    throw new RuntimeError(name, "Undefined variable '" + name.lexeme + "'.");
-  }
+    void assign(Token name, Object value) {
+        if (values.containsKey(name.lexeme)) {
+            values.put(name.lexeme, value);
+            return;
+        }
+
+        if (enclosing != null) {
+            enclosing.assign(name, value);
+            return;
+        }
+
+        throw new RuntimeError(name, "Undefined variable '" + name.lexeme + "'.");
+    }
 }

+ 20 - 2
src/main/java/com/craftinginterpreters/lox/Interpreter.java

@@ -3,15 +3,18 @@
 package com.craftinginterpreters.lox;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * @author simon
  * @date 2023-06-07 12:20
  * @desc
  */
-public class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
+class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
     public final Environment globals = new Environment();
+    private final Map<Expr, Integer> locals = new HashMap<>();
     private Environment environment = globals;
 
     public Interpreter() {
@@ -21,6 +24,7 @@ public class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
                 return 0;
             }
 
+
             @Override
             public Object call(Interpreter interpreter, List<Object> arguments) {
                 return (double) System.currentTimeMillis() / 1000.0;
@@ -163,7 +167,17 @@ public class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
 
     @Override
     public Object visitVariableExpr(Expr.Variable expr) {
-        return environment.get(expr.name);
+        // 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
@@ -234,6 +248,10 @@ public class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
         stmt.accept(this);
     }
 
+    void resolve(Expr expr, int depth) {
+        locals.put(expr, depth);
+    }
+
     protected void executeBlock(List<Stmt> statements, Environment environment) {
         Environment previous = this.environment;
         try {

+ 3 - 0
src/main/java/com/craftinginterpreters/lox/Lox.java

@@ -73,6 +73,9 @@ public class Lox {
     Parser parser = new Parser(tokens);
     List<Stmt> statements = parser.parse();
 
+    Resolver resolver = new Resolver(interpreter);
+    resolver.resolve(statements);
+
     // Stop if there was a syntax error.
     if (hadError) return;
 

+ 1 - 1
src/main/java/com/craftinginterpreters/lox/LoxCallable.java

@@ -9,7 +9,7 @@ import java.util.List;
  * @date 2023-06-18 17:33
  * @desc
  */
-public interface LoxCallable {
+interface LoxCallable{
   int arity();
   Object call(Interpreter interpreter, List<Object> arguments);
 }

+ 212 - 0
src/main/java/com/craftinginterpreters/lox/Resolver.java

@@ -0,0 +1,212 @@
+/* Copyright (C) 2019-2023 Hangzhou HSH Co. Ltd.
+ * All right reserved.*/
+package com.craftinginterpreters.lox;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Stack;
+
+/**
+ * @author simon
+ * @date 2023-08-02 10:06
+ * @desc variable resolution
+ */
+public 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;
+
+    public Resolver(Interpreter interpreter) {
+        this.interpreter = interpreter;
+    }
+
+    @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) {
+        // A literal expression doesn't mention any variables
+        // and doesn't contain any subexpressions so there is
+        // no work to do.
+        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;
+    }
+
+    @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) {
+        // after declaring the variable, we
+        // resolve its initializer expression in
+        // that same scope where the new variable now
+        // exists but is unavailable.
+        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;
+    }
+
+    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 resolveFunction(Stmt.Function function, FunctionType type) {
+        FunctionType enclosingFunction = currentFunction;
+        currentFunction = type;
+        beginScope();
+        for (Token param : function.params) {
+            declare(param);
+            define(param);
+        }
+        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();
+        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 enum FunctionType {
+        NONE, FUNCTION
+    }
+}