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