From 62bd0f83dc909547a69abb8b0aed40cf098b4c95 Mon Sep 17 00:00:00 2001 From: Tom Willemse Date: Tue, 15 Jun 2021 23:18:29 -0700 Subject: [PATCH] 13.3 Calling Superclass Methods --- .../craftinginterpreters/lox/Environment.java | 2 +- .../craftinginterpreters/lox/Interpreter.java | 28 ++++++++++++++++++- src/com/craftinginterpreters/lox/Parser.java | 7 +++++ .../craftinginterpreters/lox/Resolver.java | 25 ++++++++++++++++- .../tool/GenerateAst.java | 3 +- 5 files changed, 61 insertions(+), 4 deletions(-) diff --git a/src/com/craftinginterpreters/lox/Environment.java b/src/com/craftinginterpreters/lox/Environment.java index 65c6d88..407b9b3 100644 --- a/src/com/craftinginterpreters/lox/Environment.java +++ b/src/com/craftinginterpreters/lox/Environment.java @@ -4,7 +4,7 @@ import java.util.HashMap; import java.util.Map; class Environment { - private final Environment enclosing; + final Environment enclosing; private final Map values = new HashMap<>(); Environment() { diff --git a/src/com/craftinginterpreters/lox/Interpreter.java b/src/com/craftinginterpreters/lox/Interpreter.java index 101c33c..3f4d385 100644 --- a/src/com/craftinginterpreters/lox/Interpreter.java +++ b/src/com/craftinginterpreters/lox/Interpreter.java @@ -62,6 +62,22 @@ class Interpreter implements Expr.Visitor, Stmt.Visitor { return value; } + @Override + public Object visitSuperExpr(Expr.Super expr) { + int distance = locals.get(expr); + LoxClass superclass = (LoxClass) environment.getAt(distance, "super"); + + LoxInstance object = (LoxInstance) environment.getAt(distance - 1, "this"); + + LoxFunction method = superclass.findMethod(expr.method.lexeme); + + if (method == null) { + throw new RuntimeError(expr.method, "Undefined property '" + expr.method.lexeme + "'."); + } + + return method.bind(object); + } + @Override public Object visitThisExpr(Expr.This expr) { return lookUpVariable(expr.keyword, expr); @@ -190,13 +206,23 @@ class Interpreter implements Expr.Visitor, Stmt.Visitor { } environment.define(stmt.name.lexeme, null); + if (stmt.superclass != null) { + environment = new Environment(environment); + environment.define("super", superclass); + } + Map methods = new HashMap<>(); for (Stmt.Function method : stmt.methods) { LoxFunction function = new LoxFunction(method, environment, method.name.lexeme.equals("init")); methods.put(method.name.lexeme, function); } - LoxClass klass = new LoxClass(stmt.name.lexeme, (LoxClass)superclass, methods); + LoxClass klass = new LoxClass(stmt.name.lexeme, (LoxClass) superclass, methods); + + if (superclass != null) { + environment = environment.enclosing; + } + environment.assign(stmt.name, klass); return null; } diff --git a/src/com/craftinginterpreters/lox/Parser.java b/src/com/craftinginterpreters/lox/Parser.java index 3150035..ab4baaa 100644 --- a/src/com/craftinginterpreters/lox/Parser.java +++ b/src/com/craftinginterpreters/lox/Parser.java @@ -362,6 +362,13 @@ class Parser { return new Expr.Literal(previous().literal); } + if (match(SUPER)) { + Token keyword = previous(); + consume(DOT, "Expect '.' after 'super'."); + Token method = consume(IDENTIFIER, "Expect superclass method name."); + return new Expr.Super(keyword, method); + } + if (match(THIS)) return new Expr.This(previous()); diff --git a/src/com/craftinginterpreters/lox/Resolver.java b/src/com/craftinginterpreters/lox/Resolver.java index dc7332b..fe3a641 100644 --- a/src/com/craftinginterpreters/lox/Resolver.java +++ b/src/com/craftinginterpreters/lox/Resolver.java @@ -19,7 +19,7 @@ class Resolver implements Expr.Visitor, Stmt.Visitor { } private enum ClassType { - NONE, CLASS + NONE, CLASS, SUBCLASS } private ClassType currentClass = ClassType.NONE; @@ -51,9 +51,16 @@ class Resolver implements Expr.Visitor, Stmt.Visitor { } 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); @@ -67,6 +74,10 @@ class Resolver implements Expr.Visitor, Stmt.Visitor { endScope(); + if (stmt.superclass != null) { + endScope(); + } + currentClass = enclosingClass; return null; } @@ -183,6 +194,18 @@ class Resolver implements Expr.Visitor, Stmt.Visitor { 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) { diff --git a/src/com/craftinginterpreters/tool/GenerateAst.java b/src/com/craftinginterpreters/tool/GenerateAst.java index a6789b9..04b4500 100644 --- a/src/com/craftinginterpreters/tool/GenerateAst.java +++ b/src/com/craftinginterpreters/tool/GenerateAst.java @@ -19,7 +19,8 @@ public class GenerateAst { "Call : Expr callee, Token paren, List arguments", "Get : Expr object, Token name", "Grouping : Expr expression", "Literal : Object value", "Logical : Expr left, Token operator, Expr right", "Set : Expr object, Token name, Expr value", - "This : Token keyword", "Unary : Token operator, Expr right", "Variable : Token name")); + "Super : Token keyword, Token method", "This : Token keyword", + "Unary : Token operator, Expr right", "Variable : Token name")); defineAst(outputDir, "Stmt", Arrays.asList("Block : List statements", "Class : Token name, Expr.Variable superclass, List methods",