From 9559cab2cecdeff40186f3f33db059ac5265e2f9 Mon Sep 17 00:00:00 2001 From: Adnan Ioricce Date: Fri, 4 Oct 2024 18:28:33 +0000 Subject: [PATCH] [Chapter11&Resolver] - Adding resolving to jlox --- src/com/lox/Environment.java | 21 +++- src/com/lox/Interpreter.java | 24 ++++- src/com/lox/Lox.java | 5 + src/com/lox/Resolver.java | 194 +++++++++++++++++++++++++++++++++++ tests/function_tests.lox | 4 + 5 files changed, 243 insertions(+), 5 deletions(-) create mode 100644 src/com/lox/Resolver.java create mode 100644 tests/function_tests.lox diff --git a/src/com/lox/Environment.java b/src/com/lox/Environment.java index 21f04b7..b4f8715 100644 --- a/src/com/lox/Environment.java +++ b/src/com/lox/Environment.java @@ -39,5 +39,22 @@ class Environment { } void define(String name, Object value) { values.put(name, value); - } -} \ No newline at end of file + } + 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); + } + 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); + } +} diff --git a/src/com/lox/Interpreter.java b/src/com/lox/Interpreter.java index 0fa6eec..3e4ce83 100644 --- a/src/com/lox/Interpreter.java +++ b/src/com/lox/Interpreter.java @@ -1,10 +1,13 @@ package com.lox; import java.util.ArrayList; import java.util.List; +import java.util.HashMap; +import java.util.Map; class Interpreter implements Expr.Visitor,Stmt.Visitor { private Environment environment = new Environment(); final Environment globals = new Environment(); + private final Map locals = new HashMap<>(); Interpreter() { globals.define("clock", new LoxCallable() { @Override @@ -52,9 +55,16 @@ class Interpreter implements Expr.Visitor,Stmt.Visitor { } @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) { if (operand instanceof Double) return; throw new RuntimeError(operator, "Operand must be a number."); @@ -184,7 +194,12 @@ class Interpreter implements Expr.Visitor,Stmt.Visitor { @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; } private Object evaluate(Expr expr) { @@ -214,6 +229,9 @@ class Interpreter implements Expr.Visitor,Stmt.Visitor { private void execute(Stmt stmt) { stmt.accept(this); } + void resolve(Expr expr, int depth) { + locals.put(expr, depth); + } void executeBlock(List statements, Environment environment) { Environment previous = this.environment; diff --git a/src/com/lox/Lox.java b/src/com/lox/Lox.java index 22859fe..f19b1ec 100644 --- a/src/com/lox/Lox.java +++ b/src/com/lox/Lox.java @@ -31,6 +31,11 @@ public class Lox { // Stop if there was a syntax error. if (hadError) return; + + Resolver resolver = new Resolver(interpreter); + resolver.resolve(statements); + + if (hadError) return; interpreter.interpret(statements); } diff --git a/src/com/lox/Resolver.java b/src/com/lox/Resolver.java new file mode 100644 index 0000000..997d2a3 --- /dev/null +++ b/src/com/lox/Resolver.java @@ -0,0 +1,194 @@ +package com.lox; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Stack; + +class Resolver implements Expr.Visitor, Stmt.Visitor { + private final Interpreter interpreter; + private final Stack> scopes = new Stack<>(); + private FunctionType currentFunction = FunctionType.NONE; + Resolver(Interpreter interpreter) { + this.interpreter = interpreter; + } + private enum FunctionType { + NONE, + FUNCTION + } + 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; + } + @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); + 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; + } + void resolve(List 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 beginScope() { + scopes.push(new HashMap()); + } + private void endScope(){ + scopes.pop(); + } + @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 declare(Token name){ + if(scopes.isEmpty()) return; + Map 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); + } + 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; + } + } + } + +} diff --git a/tests/function_tests.lox b/tests/function_tests.lox new file mode 100644 index 0000000..b4b6c14 --- /dev/null +++ b/tests/function_tests.lox @@ -0,0 +1,4 @@ +fun add(a,b,c) { + return a + b + c; +} +print add(1,2,3);