Implement super
This commit is contained in:
@@ -79,6 +79,11 @@ public class AstPrinter {
|
||||
return wrap("=", left + "." + expr.name().lexeme(), val);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitSuperExpr(Expr.Super expr) {
|
||||
return "super";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitThisExpr(Expr.This expr) {
|
||||
return "this";
|
||||
@@ -162,6 +167,11 @@ public class AstPrinter {
|
||||
return left + "." + expr.name().lexeme() + " = " + val;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitSuperExpr(Expr.Super expr) {
|
||||
return "super";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitThisExpr(Expr.This expr) {
|
||||
return "this";
|
||||
|
||||
@@ -5,7 +5,7 @@ import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
public class Environment {
|
||||
private final Environment enclosing;
|
||||
final Environment enclosing;
|
||||
private final Map<String, Object> values = new HashMap<>();
|
||||
|
||||
Environment() {
|
||||
|
||||
@@ -90,6 +90,11 @@ public 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 (var method : stmt.methods()) {
|
||||
var function = new LoxFunction(method, environment, method.name().lexeme().equals("init"));
|
||||
@@ -97,6 +102,11 @@ public class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
|
||||
}
|
||||
|
||||
LoxClass clazz = new LoxClass(stmt.name().lexeme(), (LoxClass) superclass, methods);
|
||||
|
||||
if (superclass != null) {
|
||||
environment = environment.enclosing;
|
||||
}
|
||||
|
||||
environment.assign(stmt.name(), clazz);
|
||||
return null;
|
||||
}
|
||||
@@ -263,6 +273,21 @@ public 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);
|
||||
|
||||
@@ -50,10 +50,9 @@ import static xyz.ctsk.lox.TokenType.*;
|
||||
* | call ;
|
||||
* call → primary ( "(" arguments? ")" | "." IDENTIFIER )* ;
|
||||
* arguments → expression ( "," expression )* ;
|
||||
* primary → NUMBER | STRING | "true" | "false" | "nil"
|
||||
* | "(" expression ")"
|
||||
* | IDENTIFIER
|
||||
* | THIS ;
|
||||
* primary → "true" | "false" | "nil" | "this"
|
||||
* | NUMBER | STRING | IDENTIFIER | "(" expression ")"
|
||||
* | "super" "." IDENTIFIER ;
|
||||
*/
|
||||
public class Parser {
|
||||
private final List<Token> tokens;
|
||||
@@ -396,6 +395,13 @@ public 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)) {
|
||||
|
||||
@@ -10,7 +10,7 @@ public class Resolver implements Expr.Visitor<Void>, Stmt.Visitor<Void> {
|
||||
|
||||
private ClassType currentClass = ClassType.NONE;
|
||||
|
||||
private enum ClassType { NONE, CLASS }
|
||||
private enum ClassType { NONE, CLASS, SUBCLASS }
|
||||
|
||||
Resolver(Interpreter interpreter) {
|
||||
this.interpreter = interpreter;
|
||||
@@ -90,12 +90,18 @@ public class Resolver implements Expr.Visitor<Void>, Stmt.Visitor<Void> {
|
||||
define(stmt.name());
|
||||
|
||||
if (stmt.superclass() != null) {
|
||||
currentClass = ClassType.SUBCLASS;
|
||||
if (stmt.name().lexeme().equals(stmt.superclass().name().lexeme())) {
|
||||
Lox.error(stmt.superclass().name(), "A class can't inherit from itself.");
|
||||
}
|
||||
resolve(stmt.superclass());
|
||||
}
|
||||
|
||||
if (stmt.superclass() != null) {
|
||||
beginScope();
|
||||
scopes.peek().put("super", true);
|
||||
}
|
||||
|
||||
beginScope();
|
||||
scopes.peek().put("this", true);
|
||||
|
||||
@@ -111,6 +117,8 @@ public class Resolver implements Expr.Visitor<Void>, Stmt.Visitor<Void> {
|
||||
|
||||
endScope();
|
||||
|
||||
if (stmt.superclass() != null) endScope();
|
||||
|
||||
currentClass = enclosing;
|
||||
return null;
|
||||
}
|
||||
@@ -229,6 +237,17 @@ public class Resolver implements Expr.Visitor<Void>, Stmt.Visitor<Void> {
|
||||
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 subclass.");
|
||||
}
|
||||
resolveLocal(expr, expr.keyword());
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitThisExpr(Expr.This expr) {
|
||||
if (currentClass == ClassType.NONE) {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
@Rule(head = "Literal", body = {"Object value"}),
|
||||
@Rule(head = "Logical", body = {"Expr left", "Token operator", "Expr right"}),
|
||||
@Rule(head = "Set", body = {"Expr object", "Token name", "Expr value"}),
|
||||
@Rule(head = "Super", body = {"Token keyword", "Token method"}),
|
||||
@Rule(head = "This", body = {"Token keyword"}),
|
||||
@Rule(head = "Unary", body = {"Token operator", "Expr right"}),
|
||||
@Rule(head = "Variable", body = {"Token name"})
|
||||
|
||||
Reference in New Issue
Block a user