~jlambda/craftinginterpreters

e045a2dc5898f92dcea1b9c85699039e3f893e53 — jerry 4 months ago 6142a8c dev
Chapter 13 Inheritance

* I apparently forgot to commit the LoxClass to the class chapter,
so I `--amend`ed the previous commit, but then it tracks this chapter.
I don't care too much about that, but now it's out of order :)
M craftinginterpreters/notes.md => craftinginterpreters/notes.md +167 -1
@@ 1,4 1,69 @@
# Notes
**Table of Contents**

- [Notes](#notes)
- [Notes over implementation](#notes-over-implementation)
- [Intepreter](#intepreter)
- [Parser](#parser)
- [Contents](#contents)
- [Chapter 1](#chapter-1)
- [Challenges](#challenges)
- [Chapter 2](#chapter-2)
- [Terms](#terms)
- [Challenges](#challenges-1)
- [Extra](#extra)
- [Chapter 3](#chapter-3)
- [Challenges](#challenges-2)
- [Chapter ||  -- A Tree Walk Interpreter](#chapter------a-tree-walk-interpreter)
- [Terms](#terms-1)
- [Challenges](#challenges-3)
- [Extra](#extra-1)
- [Chapter 4 -- Scanning](#chapter-4----scanning)
- [Terms](#terms-2)
- [Challenges](#challenges-4)
- [Extra](#extra-2)
- [Chapter 5 -- Representing Code](#chapter-5----representing-code)
- [Terms](#terms-3)
- [Challenges](#challenges-5)
- [Extra](#extra-3)
- [Chapter 6 -- Parsing Expressions](#chapter-6----parsing-expressions)
- [Terms](#terms-4)
- [Challenges](#challenges-6)
- [Extra](#extra-4)
- [Chapter 7 -- Evaluating Expressions](#chapter-7----evaluating-expressions)
- [Terms](#terms-5)
- [Challenges](#challenges-7)
- [Extra](#extra-5)
- [Chapter 8 -- Statements and State](#chapter-8----statements-and-state)
- [Terms](#terms-6)
- [Challenges](#challenges-8)
- [Extra](#extra-6)
- [Chapter 9 -- Control Flow](#chapter-9----control-flow)
- [Terms](#terms-7)
- [Challenges](#challenges-9)
- [Extras](#extras)
- [Chapter 10 -- Functions](#chapter-10----functions)
- [Terms](#terms-8)
- [Challenges](#challenges-10)
- [Extras](#extras-1)
- [Chapter 11 -- Resolving and Binding](#chapter-11----resolving-and-binding)
- [Notes](#notes-1)
- [Terms](#terms-9)
- [Challenges](#challenges-11)
- [Extras](#extras-2)
- [Chapter 12 -- Classes](#chapter-12----classes)
- [Notes](#notes-2)
- [Terms](#terms-10)
- [Challenges](#challenges-12)
- [Extras](#extras-3)
- [Chapter 13 -- Inheritance](#chapter-13----inheritance)
- [Notes](#notes-3)
- [Terms](#terms-11)
- [Challenges](#challenges-13)
- [Extra](#extra-7)

<!-- markdown-toc end -->

## Notes

`c-x b` list buffers (which is different than `<leader> bi`)
(The former always works, the latter doesn't work after I accidentally a `<leader> bo`)


@@ 57,6 122,7 @@ https://craftinginterpreters.com/contents.html
https://craftinginterpreters.com/introduction.html

### Challenges

1. There are at least six domain-specific languages used in the little system I cobbled together to write and 
publish this [book](https://github.com/munificent/craftinginterpreters). What are they?



@@ 937,3 1003,103 @@ CLOS is probably more interesting than this implementation.  So look that up at 
LISP (Lisp in Small Pieces) gets to classes fairly early and I plan to read that soon.



## Chapter 13 -- Inheritance

https://craftinginterpreters.com/inheritance.html

### Notes

Woo, the last chapter of Jlox!  I am definitely
excited to finish it because turns out I didn't really 
like doing lox in Java, and all the interesting stuff 
is in the C part.  The whole Class|Inheritance thing 
also prevented me from wanting to continue :(
Perseverance!

The `<` becomes the "(super|sub)" class lexeme
```bnf 
classDecl      → "class" IDENTIFIER ( "<" IDENTIFIER )?
                 "{" function* "}" ;
```

![superhero](https://craftinginterpreters.com/image/inheritance/superhero.png "superhero")
<!-- markdown-toc start - Don't edit this section. Run M-x markdown-toc-refresh-toc -->

### Terms
[https://en.wikipedia.org/wiki/Liskov_substitution_principle](Liskov substitution principle) -- Subtyping

### Challenges


1. Lox supports only single inheritance—a class may have a single superclass and that’s the only way to reuse methods across classes. Other languages have explored a variety of ways to more freely reuse and share capabilities across classes: mixins, traits, multiple inheritance, virtual inheritance, extension methods, etc.

If you were to add some feature along these lines to Lox, which would you pick and why? If you’re feeling courageous (and you should be at this point), go ahead and add it.

Answer: None!  I guess it would be kind of neat to support some type of prototype inheritance, where super methods bind individually but not necessarily totally.  That is `Baz < Bar, Foo` would be incomplete,
it might be something like `Baz < Bar[foo, buzz], Foo[fubar]` where `Foo` would also have a `foo` method.

2. In Lox, as in most other object-oriented languages, when looking up a method, we start at the bottom of the class hierarchy and work our way up—a subclass’s method is preferred over a superclass’s. In order to get to the superclass method from within an overriding method, you use super.

The language BETA takes the opposite approach. When you call a method, it starts at the top of the class hierarchy and works down. A superclass method wins over a subclass method. In order to get to the subclass method, the superclass method can call inner, which is sort of like the inverse of super. It chains to the next method down the hierarchy.

The superclass method controls when and where the subclass is allowed to refine its behavior. If the superclass method doesn’t call inner at all, then the subclass has no way of overriding or modifying the superclass’s behavior.

Take out Lox’s current overriding and super behavior and replace it with BETA’s semantics. In short:

* When calling a method on a class, prefer the method highest on the class’s inheritance chain.
* Inside the body of a method, a call to inner looks for a method with the same name in the nearest subclass along the inheritance chain between the class containing the inner and the class of this. If there is no matching method, the inner call does nothing.

For example:

```lox 
    class Doughnut {
      cook() {
        print "Fry until golden brown.";
        inner();
        print "Place in a nice box.";
      }
    }

    class BostonCream < Doughnut {
      cook() {
        print "Pipe full of custard and coat with chocolate.";
      }
    }

    BostonCream().cook();
```
This should print:

```txt
    Fry until golden brown.
    Pipe full of custard and coat with chocolate.
    Place in a nice box.
```

Answer: I'm absolutely not going to do this. It feels wrong, but not because of OOP, but 
because of lexical scoping.  Imagine having a function like 

```clojure

(def ^dynamic foo nil)
(defn foo 
    [&opts]
    (bind [foo 'foo]
      (fn [] (bind [foo 'bar])
    (prn (inner foo)))) ; 'bar
)
;; See it doesn't even look right
;; because you bind in scope, even if you bind a dynamic variable
```

In the chapter where I introduced Lox, I challenged you to come up with a couple of features you think the language is missing. Now that you know how to build an interpreter, implement one of those features.

Answer: Well the obvious answer is that lox doesn't really do anything because it doesn't have a standard library.
The easiest thing to do would be to just use the host language.  We could either write an api to it 
so it's more "lox-like" or we could just add some ffi.

It would also be interesting to write out class files from lox, but that's more the next section (compiling),
or at add support for threading.

### Extra

M craftinginterpreters/src/main/java/craftinginterpreters/Expr.java => craftinginterpreters/src/main/java/craftinginterpreters/Expr.java +15 -0
@@ 12,6 12,7 @@ abstract class Expr {
    R visitLiteralExpr(Literal expr);
    R visitLogicalExpr(Logical expr);
    R visitSetExpr(Set expr);
    R visitSuperExpr(Super expr);
    R visitThisExpr(This expr);
    R visitUnaryExpr(Unary expr);
    R visitVariableExpr(Variable expr);


@@ 132,6 133,20 @@ abstract class Expr {
    final Token name;
    final Expr value;
  }
 static class Super extends Expr {
    Super(Token keyword, Token method) {
    this.keyword = keyword;
    this.method = method;
    }

    @Override
    <R> R accept(Visitor<R> visitor) {
      return visitor.visitSuperExpr(this);
    }

    final Token keyword;
    final Token method;
  }
 static class This extends Expr {
    This(Token keyword) {
    this.keyword = keyword;

M craftinginterpreters/src/main/java/craftinginterpreters/Interpreter.java => craftinginterpreters/src/main/java/craftinginterpreters/Interpreter.java +35 -1
@@ 91,6 91,22 @@ class Interpreter implements Expr.Visitor<Object>,
    }

    @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);
    }


@@ 122,15 138,33 @@ class Interpreter implements Expr.Visitor<Object>,

    @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);

        if (stmt.superclass != null) {
            environment = new Environment(environment);
            environment.define("super", superclass);
        }

        Map<String, LoxFunction> 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, methods);
        LoxClass klass = new LoxClass(stmt.name.lexeme,
                                      (LoxClass)superclass, methods);

        if (superclass != null) {
            environment = environment.enclosing;
        }
        environment.assign(stmt.name, klass);
        return null;
    }

M craftinginterpreters/src/main/java/craftinginterpreters/Parser.java => craftinginterpreters/src/main/java/craftinginterpreters/Parser.java +15 -1
@@ 122,6 122,12 @@ 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<Stmt.Function> methods = new ArrayList<>();


@@ 131,7 137,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 varDeclaration() {


@@ 352,6 358,14 @@ 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());
        if (match(IDENTIFIER)) {
            return new Expr.Variable(previous());

M craftinginterpreters/src/main/java/craftinginterpreters/Resolver.java => craftinginterpreters/src/main/java/craftinginterpreters/Resolver.java +35 -1
@@ 91,6 91,22 @@ class Resolver implements Expr.Visitor<Void>, Stmt.Visitor<Void> {
        declare(stmt.name);
        define(stmt.name);

        if (stmt.superclass != null &&
            stmt.name.lexeme.equals(stmt.superclass.name.lexeme)) {
            App.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);



@@ 103,6 119,9 @@ class Resolver implements Expr.Visitor<Void>, Stmt.Visitor<Void> {
        }

        endScope();

        if (stmt.superclass != null) endScope();

        currentClass = enclosingClass;
        return null;
    }


@@ 202,6 221,20 @@ class Resolver implements Expr.Visitor<Void>, Stmt.Visitor<Void> {
    }

    @Override
    public Void visitSuperExpr(Expr.Super expr) {
        if (currentClass == ClassType.NONE) {
            App.error(expr.keyword,
                      "Can't use 'super' outside of a class.");
        } else if (currentClass != ClassType.SUBCLASS) {
            App.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) {
            App.error(expr.keyword,


@@ 262,7 295,8 @@ class Resolver implements Expr.Visitor<Void>, Stmt.Visitor<Void> {

    private enum ClassType {
        NONE,
        CLASS
        CLASS,
        SUBCLASS
    }

    private ClassType currentClass = ClassType.NONE;

M craftinginterpreters/src/main/java/craftinginterpreters/Stmt.java => craftinginterpreters/src/main/java/craftinginterpreters/Stmt.java +3 -1
@@ 27,8 27,9 @@ abstract class Stmt {
    final List<Stmt> statements;
  }
 static class Class extends Stmt {
    Class(Token name, List<Stmt.Function> methods) {
    Class(Token name, Expr.Variable superclass, List<Stmt.Function> methods) {
    this.name = name;
    this.superclass = superclass;
    this.methods = methods;
    }



@@ 38,6 39,7 @@ abstract class Stmt {
    }

    final Token name;
    final Expr.Variable superclass;
    final List<Stmt.Function> methods;
  }
 static class Expression extends Stmt {

M craftinginterpreters/src/main/java/craftinginterpreters/tool/GenerateAst.java => craftinginterpreters/src/main/java/craftinginterpreters/tool/GenerateAst.java +3 -1
@@ 19,13 19,15 @@ public class GenerateAst {
                                                   "Grouping : Expr expression", "Literal : Object value",
                                                   "Logical  : Expr left, Token operator, Expr right",
                                                   "Set      : Expr object, Token name, Expr value",
                                                   "Super    : Token keyword, Token method",
                                                   "This     : Token keyword",
                                                   "Unary : Token operator, Expr right",
                                                   "Variable : Token name"));

        defineAst(outputDir, "Stmt", Arrays.asList(
                                                   "Block      : List<Stmt> statements",
                                                   "Class      : Token name, List<Stmt.Function> methods",
                                                   "Class      : Token name, Expr.Variable superclass," +
                                                   " List<Stmt.Function> methods",
                                                   "Expression : Expr expression",
                                                   "Function   : Token name, List<Token> params," +
                                                   " List<Stmt> body",