From 68a2ebd34fc94488e89ffb82b359ec6e7e152ae9 Mon Sep 17 00:00:00 2001 From: Tom Willemse Date: Thu, 8 Jul 2021 00:14:31 -0700 Subject: Restructure project to make room for clox --- .../src/com/craftinginterpreters/lox/Resolver.java | 299 +++++++++++++++++++++ 1 file changed, 299 insertions(+) create mode 100644 jlox/src/com/craftinginterpreters/lox/Resolver.java (limited to 'jlox/src/com/craftinginterpreters/lox/Resolver.java') diff --git a/jlox/src/com/craftinginterpreters/lox/Resolver.java b/jlox/src/com/craftinginterpreters/lox/Resolver.java new file mode 100644 index 0000000..fe3a641 --- /dev/null +++ b/jlox/src/com/craftinginterpreters/lox/Resolver.java @@ -0,0 +1,299 @@ +package com.craftinginterpreters.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, INITIALIZER, METHOD + } + + private enum ClassType { + NONE, CLASS, SUBCLASS + } + + private ClassType currentClass = ClassType.NONE; + + public void resolve(List statements) { + for (Stmt statement : statements) { + resolve(statement); + } + } + + @Override + public Void visitBlockStmt(Stmt.Block stmt) { + beginScope(); + resolve(stmt.statements); + endScope(); + return null; + } + + @Override + public Void visitClassStmt(Stmt.Class stmt) { + ClassType enclosingClass = currentClass; + currentClass = ClassType.CLASS; + + declare(stmt.name); + define(stmt.name); + + if (stmt.superclass != null && stmt.name.lexeme.equals(stmt.superclass.name.lexeme)) { + Lox.error(stmt.superclass.name, "A class can't inherit from itself."); + } + + if (stmt.superclass != null) { + currentClass = ClassType.SUBCLASS; + + resolve(stmt.superclass); + } + + if (stmt.superclass != null) { + beginScope(); + scopes.peek().put("super", true); + } + + beginScope(); + scopes.peek().put("this", true); + + for (Stmt.Function method : stmt.methods) { + FunctionType declaration = FunctionType.METHOD; + if (method.name.lexeme.equals("init")) { + declaration = FunctionType.INITIALIZER; + } + resolveFunction(method, declaration); + } + + endScope(); + + if (stmt.superclass != 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."); + } + + if (stmt.value != null) { + if (currentFunction == FunctionType.INITIALIZER) { + Lox.error(stmt.keyword, "Can't return a value from an initializer."); + } + resolve(stmt.value); + } + + 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 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 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) { + return null; + } + + @Override + public Void visitSetExpr(Expr.Set expr) { + resolve(expr.value); + resolve(expr.object); + return null; + } + + @Override + public Void visitSuperExpr(Expr.Super expr) { + if (currentClass == ClassType.NONE) { + Lox.error(expr.keyword, "Can't use 'super' outside of a class."); + } else if (currentClass != ClassType.SUBCLASS) { + Lox.error(expr.keyword, "Can't use 'super' in a class with no superclass."); + } + + resolveLocal(expr, expr.keyword); + 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; + } + + resolveLocal(expr, expr.keyword); + 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 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); + } + 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 scope = scopes.peek(); + if (scope.containsKey(name.lexeme)) { + Lox.error(name, "Already 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; + } + } + } +} -- cgit v1.2.3-54-g00ecf