Implement super

This commit is contained in:
ctsk
2022-10-11 13:18:40 +02:00
parent f0b55491d8
commit da00410738
6 changed files with 67 additions and 6 deletions

View File

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

View File

@@ -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() {

View File

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

View File

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

View File

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

View File

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