aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/com/craftinginterpreters/lox/Environment.java2
-rw-r--r--src/com/craftinginterpreters/lox/Interpreter.java28
-rw-r--r--src/com/craftinginterpreters/lox/Parser.java7
-rw-r--r--src/com/craftinginterpreters/lox/Resolver.java25
-rw-r--r--src/com/craftinginterpreters/tool/GenerateAst.java3
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<String, Object> 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
@@ -63,6 +63,22 @@ class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
}
@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<Object>, Stmt.Visitor<Void> {
}
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, (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<Void>, Stmt.Visitor<Void> {
}
private enum ClassType {
- NONE, CLASS
+ NONE, CLASS, SUBCLASS
}
private ClassType currentClass = ClassType.NONE;
@@ -51,9 +51,16 @@ class Resolver implements Expr.Visitor<Void>, Stmt.Visitor<Void> {
}
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<Void>, Stmt.Visitor<Void> {
endScope();
+ if (stmt.superclass != null) {
+ endScope();
+ }
+
currentClass = enclosingClass;
return null;
}
@@ -184,6 +195,18 @@ class Resolver implements Expr.Visitor<Void>, Stmt.Visitor<Void> {
}
@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.");
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<Expr> 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<Stmt> statements",
"Class : Token name, Expr.Variable superclass, List<Stmt.Function> methods",