|
|
@@ -13,220 +13,255 @@ import java.util.Stack;
|
|
|
* @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;
|
|
|
+ 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);
|
|
|
}
|
|
|
|
|
|
- @Override
|
|
|
- public Void visitBinaryExpr(Expr.Binary expr) {
|
|
|
- resolve(expr.left);
|
|
|
- resolve(expr.right);
|
|
|
- return null;
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Void visitGetExpr(Expr.Get expr) {
|
|
|
+ resolve(expr.object);
|
|
|
+ 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 visitSetExpr(Expr.Set expr) {
|
|
|
+ resolve(expr.value);
|
|
|
+ resolve(expr.object);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Void visitThisExpr(Expr.This expr) {
|
|
|
+ if (currentClass == ClassType.NONE) {
|
|
|
+ Lox.error(expr.keyword, "Can't use 'this' outside of a class");
|
|
|
+ return null;
|
|
|
}
|
|
|
|
|
|
- @Override
|
|
|
- public Void visitCallExpr(Expr.Call expr) {
|
|
|
- resolve(expr.callee);
|
|
|
+ resolveLocal(expr, expr.keyword);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
|
|
|
- for (Expr argument : expr.arguments) {
|
|
|
- resolve(argument);
|
|
|
- }
|
|
|
+ @Override
|
|
|
+ public Void visitUnaryExpr(Expr.Unary expr) {
|
|
|
+ resolve(expr.right);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
|
|
|
- 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.");
|
|
|
}
|
|
|
|
|
|
- @Override
|
|
|
- public Void visitGetExpr(Expr.Get expr) {
|
|
|
- resolve(expr.object);
|
|
|
- return null;
|
|
|
- }
|
|
|
+ resolveLocal(expr, expr.name);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
|
|
|
- @Override
|
|
|
- public Void visitGroupingExpr(Expr.Grouping expr) {
|
|
|
- resolve(expr.expression);
|
|
|
- return null;
|
|
|
- }
|
|
|
+ @Override
|
|
|
+ public Void visitBlockStmt(Stmt.Block stmt) {
|
|
|
+ beginScope();
|
|
|
+ resolve(stmt.statements);
|
|
|
+ endScope();
|
|
|
+ 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 visitClassStmt(Stmt.Class stmt) {
|
|
|
+ ClassType enclosingClass = currentClass;
|
|
|
+ currentClass = ClassType.CLASS;
|
|
|
|
|
|
- @Override
|
|
|
- public Void visitLogicalExpr(Expr.Logical expr) {
|
|
|
- resolve(expr.left);
|
|
|
- resolve(expr.right);
|
|
|
- return null;
|
|
|
- }
|
|
|
+ declare(stmt.name);
|
|
|
+ define(stmt.name);
|
|
|
|
|
|
- @Override
|
|
|
- public Void visitSetExpr(Expr.Set expr) {
|
|
|
- resolve(expr.value);
|
|
|
- resolve(expr.object);
|
|
|
- return null;
|
|
|
- }
|
|
|
+ beginScope();
|
|
|
+ scopes.peek().put("this", true);
|
|
|
|
|
|
- @Override
|
|
|
- public Void visitUnaryExpr(Expr.Unary expr) {
|
|
|
- resolve(expr.right);
|
|
|
- return null;
|
|
|
+ for (Stmt.Function method : stmt.methods) {
|
|
|
+ FunctionType declaration = FunctionType.METHOD;
|
|
|
+ resolveFunction(method, declaration);
|
|
|
}
|
|
|
-
|
|
|
- @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;
|
|
|
+ endScope();
|
|
|
+
|
|
|
+ currentClass = enclosingClass;
|
|
|
+ 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");
|
|
|
}
|
|
|
-
|
|
|
- @Override
|
|
|
- public Void visitBlockStmt(Stmt.Block stmt) {
|
|
|
- beginScope();
|
|
|
- resolve(stmt.statements);
|
|
|
- endScope();
|
|
|
- return null;
|
|
|
+ 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);
|
|
|
}
|
|
|
-
|
|
|
- @Override
|
|
|
- public Void visitClassStmt(Stmt.Class stmt) {
|
|
|
- declare(stmt.name);
|
|
|
- define(stmt.name);
|
|
|
- return null;
|
|
|
+ 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);
|
|
|
}
|
|
|
-
|
|
|
- @Override
|
|
|
- public Void visitExpressionStmt(Stmt.Expression stmt) {
|
|
|
- resolve(stmt.expression);
|
|
|
- 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);
|
|
|
}
|
|
|
+ endScope();
|
|
|
+ currentFunction = enclosingFunction;
|
|
|
+ }
|
|
|
|
|
|
- @Override
|
|
|
- public Void visitFunctionStmt(Stmt.Function stmt) {
|
|
|
- declare(stmt.name);
|
|
|
- define(stmt.name);
|
|
|
+ private void beginScope() {
|
|
|
+ scopes.push(new HashMap<>());
|
|
|
+ }
|
|
|
|
|
|
- resolveFunction(stmt, FunctionType.FUNCTION);
|
|
|
- return null;
|
|
|
- }
|
|
|
+ private void endScope() {
|
|
|
+ scopes.pop();
|
|
|
+ }
|
|
|
|
|
|
- @Override
|
|
|
- public Void visitIfStmt(Stmt.If stmt) {
|
|
|
- resolve(stmt.condition);
|
|
|
- resolve(stmt.thenBranch);
|
|
|
- if (stmt.elseBranch != null) resolve(stmt.elseBranch);
|
|
|
- return null;
|
|
|
- }
|
|
|
+ private void declare(Token name) {
|
|
|
+ if (scopes.isEmpty()) return;
|
|
|
|
|
|
- @Override
|
|
|
- public Void visitPrintStmt(Stmt.Print stmt) {
|
|
|
- resolve(stmt.expression);
|
|
|
- return null;
|
|
|
- }
|
|
|
+ Map<String, Boolean> scope = scopes.peek();
|
|
|
+ scope.put(name.lexeme, false);
|
|
|
+ }
|
|
|
|
|
|
- @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;
|
|
|
- }
|
|
|
+ private void define(Token name) {
|
|
|
+ if (scopes.isEmpty()) return;
|
|
|
|
|
|
- @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;
|
|
|
- }
|
|
|
+ scopes.peek().put(name.lexeme, true);
|
|
|
+ }
|
|
|
|
|
|
- @Override
|
|
|
- public Void visitWhileStmt(Stmt.While stmt) {
|
|
|
- resolve(stmt.condition);
|
|
|
- resolve(stmt.body);
|
|
|
- return null;
|
|
|
+ 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;
|
|
|
+ }
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- void resolve(List<Stmt> statements) {
|
|
|
- for (Stmt statement : statements) {
|
|
|
- resolve(statement);
|
|
|
- }
|
|
|
- }
|
|
|
+ /** fun 类型 */
|
|
|
+ private enum FunctionType {
|
|
|
+ NONE,
|
|
|
+ FUNCTION,
|
|
|
+ METHOD
|
|
|
+ }
|
|
|
|
|
|
- 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 enum ClassType {
|
|
|
+ NONE,
|
|
|
+ CLASS
|
|
|
+ }
|
|
|
|
|
|
- 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
|
|
|
- }
|
|
|
+ private ClassType currentClass = ClassType.NONE;
|
|
|
}
|