From db403da4b2ed543100dc42fd3f0036fd64ed76a9 Mon Sep 17 00:00:00 2001 From: Tom Willemse Date: Mon, 14 Jun 2021 23:36:10 -0700 Subject: [PATCH] 13.1 Superclasses and Subclasses --- .../craftinginterpreters/lox/Interpreter.java | 9 ++++- .../craftinginterpreters/lox/LoxClass.java | 4 ++- src/com/craftinginterpreters/lox/Parser.java | 34 ++++++++++++------- .../craftinginterpreters/lox/Resolver.java | 8 +++++ .../tool/GenerateAst.java | 3 +- 5 files changed, 42 insertions(+), 16 deletions(-) diff --git a/src/com/craftinginterpreters/lox/Interpreter.java b/src/com/craftinginterpreters/lox/Interpreter.java index 7dfd0fb..101c33c 100644 --- a/src/com/craftinginterpreters/lox/Interpreter.java +++ b/src/com/craftinginterpreters/lox/Interpreter.java @@ -181,6 +181,13 @@ class Interpreter implements Expr.Visitor, Stmt.Visitor { @Override public Void visitClassStmt(Stmt.Class stmt) { + Object superclass = null; + if (stmt.superclass != null) { + superclass = evaluate(stmt.superclass); + if (!(superclass instanceof LoxClass)) { + throw new RuntimeError(stmt.superclass.name, "Superclass must be a class."); + } + } environment.define(stmt.name.lexeme, null); Map methods = new HashMap<>(); @@ -189,7 +196,7 @@ class Interpreter implements Expr.Visitor, Stmt.Visitor { methods.put(method.name.lexeme, function); } - LoxClass klass = new LoxClass(stmt.name.lexeme, methods); + LoxClass klass = new LoxClass(stmt.name.lexeme, (LoxClass)superclass, methods); environment.assign(stmt.name, klass); return null; } diff --git a/src/com/craftinginterpreters/lox/LoxClass.java b/src/com/craftinginterpreters/lox/LoxClass.java index 5e0e755..c9261b9 100644 --- a/src/com/craftinginterpreters/lox/LoxClass.java +++ b/src/com/craftinginterpreters/lox/LoxClass.java @@ -5,9 +5,11 @@ import java.util.Map; class LoxClass implements LoxCallable { final String name; + final LoxClass superclass; private final Map methods; - LoxClass(String name, Map methods) { + LoxClass(String name, LoxClass superclass, Map methods) { + this.superclass = superclass; this.name = name; this.methods = methods; } diff --git a/src/com/craftinginterpreters/lox/Parser.java b/src/com/craftinginterpreters/lox/Parser.java index a3f4897..3150035 100644 --- a/src/com/craftinginterpreters/lox/Parser.java +++ b/src/com/craftinginterpreters/lox/Parser.java @@ -3,8 +3,8 @@ package com.craftinginterpreters.lox; import static com.craftinginterpreters.lox.TokenType.*; import java.util.ArrayList; -import java.util.List; import java.util.Arrays; +import java.util.List; class Parser { private static class ParseError extends RuntimeException { @@ -49,6 +49,13 @@ class Parser { private Stmt classDeclaration() { Token name = consume(IDENTIFIER, "Expect class name."); + + Expr.Variable superclass = null; + if (match(LESS)) { + consume(IDENTIFIER, "Expect superclass name."); + superclass = new Expr.Variable(previous()); + } + consume(LEFT_BRACE, "Expect '{' before class body."); List methods = new ArrayList<>(); @@ -58,7 +65,7 @@ class Parser { consume(RIGHT_BRACE, "Expect '}' after class body."); - return new Stmt.Class(name, methods); + return new Stmt.Class(name, superclass, methods); } private Stmt statement() { @@ -218,7 +225,7 @@ class Parser { Token name = ((Expr.Variable) expr).name; return new Expr.Assign(name, value); } else if (expr instanceof Expr.Get) { - Expr.Get get = (Expr.Get)expr; + Expr.Get get = (Expr.Get) expr; return new Expr.Set(get.object, get.name, value); } @@ -355,7 +362,8 @@ class Parser { return new Expr.Literal(previous().literal); } - if (match(THIS)) return new Expr.This(previous()); + if (match(THIS)) + return new Expr.This(previous()); if (match(IDENTIFIER)) { return new Expr.Variable(previous()); @@ -425,15 +433,15 @@ class Parser { return; switch (peek().type) { - case CLASS: - case FUN: - case VAR: - case FOR: - case IF: - case WHILE: - case PRINT: - case RETURN: - return; + case CLASS: + case FUN: + case VAR: + case FOR: + case IF: + case WHILE: + case PRINT: + case RETURN: + return; } advance(); diff --git a/src/com/craftinginterpreters/lox/Resolver.java b/src/com/craftinginterpreters/lox/Resolver.java index cda0f60..dc7332b 100644 --- a/src/com/craftinginterpreters/lox/Resolver.java +++ b/src/com/craftinginterpreters/lox/Resolver.java @@ -46,6 +46,14 @@ class Resolver implements Expr.Visitor, Stmt.Visitor { 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) { + resolve(stmt.superclass); + } + beginScope(); scopes.peek().put("this", true); diff --git a/src/com/craftinginterpreters/tool/GenerateAst.java b/src/com/craftinginterpreters/tool/GenerateAst.java index 2097e31..a6789b9 100644 --- a/src/com/craftinginterpreters/tool/GenerateAst.java +++ b/src/com/craftinginterpreters/tool/GenerateAst.java @@ -21,7 +21,8 @@ public class GenerateAst { "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")); defineAst(outputDir, "Stmt", - Arrays.asList("Block : List statements", "Class : Token name, List methods", + Arrays.asList("Block : List statements", + "Class : Token name, Expr.Variable superclass, List methods", "Expression : Expr expression", "Function : Token name, List params, List body", "If : Expr condition, Stmt thenBranch, Stmt elseBranch", "Print : Expr expression", "Return : Token keyword, Expr value", "Var : Token name, Expr initializer",