[Chapter13&Inheritance] - Adding the inheritance logic

This commit is contained in:
Adnan Ioricce 2024-10-06 12:18:30 +00:00
parent 05a8ac6127
commit 2117e9ed07
7 changed files with 241 additions and 100 deletions

@ -7,9 +7,13 @@ abstract class Expr {
R visitAssignExpr(Assign expr); R visitAssignExpr(Assign expr);
R visitBinaryExpr(Binary expr); R visitBinaryExpr(Binary expr);
R visitCallExpr(Call expr); R visitCallExpr(Call expr);
R visitGetExpr(Get expr);
R visitGroupingExpr(Grouping expr); R visitGroupingExpr(Grouping expr);
R visitLiteralExpr(Literal expr); R visitLiteralExpr(Literal expr);
R visitLogicalExpr(Logical expr); R visitLogicalExpr(Logical expr);
R visitSetExpr(Set expr);
R visitSuperExpr(Super expr);
R visitThisExpr(This expr);
R visitUnaryExpr(Unary expr); R visitUnaryExpr(Unary expr);
R visitVariableExpr(Variable expr); R visitVariableExpr(Variable expr);
} }
@ -59,6 +63,20 @@ abstract class Expr {
final Token paren; final Token paren;
final List<Expr> arguments; final List<Expr> arguments;
} }
static class Get extends Expr {
Get(Expr object, Token name) {
this.object = object;
this.name = name;
}
@Override
<R> R accept(Visitor<R> visitor) {
return visitor.visitGetExpr(this);
}
final Expr object;
final Token name;
}
static class Grouping extends Expr { static class Grouping extends Expr {
Grouping(Expr expression) { Grouping(Expr expression) {
this.expression = expression; this.expression = expression;
@ -99,6 +117,48 @@ abstract class Expr {
final Token operator; final Token operator;
final Expr right; final Expr right;
} }
static class Set extends Expr {
Set(Expr object, Token name, Expr value) {
this.object = object;
this.name = name;
this.value = value;
}
@Override
<R> R accept(Visitor<R> visitor) {
return visitor.visitSetExpr(this);
}
final Expr object;
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;
}
@Override
<R> R accept(Visitor<R> visitor) {
return visitor.visitThisExpr(this);
}
final Token keyword;
}
static class Unary extends Expr { static class Unary extends Expr {
Unary(Token operator, Expr right) { Unary(Token operator, Expr right) {
this.operator = operator; this.operator = operator;

@ -43,14 +43,26 @@ class Interpreter implements Expr.Visitor<Object>,Stmt.Visitor<Void> {
Object object = evaluate(expr.object); Object object = evaluate(expr.object);
if (!(object instanceof LoxInstance)) { if (!(object instanceof LoxInstance)) {
throw new RuntimeError(expr.name, throw new RuntimeError(expr.name,"Only instances have fields.");
"Only instances have fields.");
} }
Object value = evaluate(expr.value); Object value = evaluate(expr.value);
((LoxInstance)object).set(expr.name, value); ((LoxInstance)object).set(expr.name, value);
return value; return value;
} }
@Override
public Object visitSuperExpr(Expr.Super expr) {
int distance = locals.get(expr);
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);
@ -58,7 +70,6 @@ class Interpreter implements Expr.Visitor<Object>,Stmt.Visitor<Void> {
@Override @Override
public Object visitUnaryExpr(Expr.Unary expr) { public Object visitUnaryExpr(Expr.Unary expr) {
Object right = evaluate(expr.right); Object right = evaluate(expr.right);
switch (expr.operator.type) { switch (expr.operator.type) {
case BANG: case BANG:
return !isTruthy(right); return !isTruthy(right);
@ -86,8 +97,7 @@ class Interpreter implements Expr.Visitor<Object>,Stmt.Visitor<Void> {
if (operand instanceof Double) return; if (operand instanceof Double) return;
throw new RuntimeError(operator, "Operand must be a number."); throw new RuntimeError(operator, "Operand must be a number.");
} }
private void checkNumberOperands(Token operator, private void checkNumberOperands(Token operator,Object left, Object right) {
Object left, Object right) {
if (left instanceof Double && right instanceof Double) return; if (left instanceof Double && right instanceof Double) return;
throw new RuntimeError(operator, "Operands must be numbers."); throw new RuntimeError(operator, "Operands must be numbers.");
@ -281,7 +291,19 @@ class Interpreter implements Expr.Visitor<Object>,Stmt.Visitor<Void> {
} }
@Override @Override
public void visitClassStmt(Stmt.Class stmt){ 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); 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 (Stmt.Function method : stmt.methods) { for (Stmt.Function method : stmt.methods) {
LoxFunction function = new LoxFunction(method, environment, LoxFunction function = new LoxFunction(method, environment,
@ -289,7 +311,10 @@ class Interpreter implements Expr.Visitor<Object>,Stmt.Visitor<Void> {
methods.put(method.name.lexeme, function); methods.put(method.name.lexeme, function);
} }
LoxClass klass = new LoxClass(stmt.name.lexeme, methods); LoxClass klass = new LoxClass(stmt.name.lexeme, methods,(LoxClass)superclass,methods);
if (superclass != null) {
environment = environment.enclosing;
}
environment.assign(stmt.name, klass); environment.assign(stmt.name, klass);
return null; return null;
} }

@ -5,9 +5,11 @@ import java.util.Map;
class LoxClass implements LoxCallable { class LoxClass implements LoxCallable {
final String name; final String name;
final LoxClass superclass;
private final Map<String, LoxFunction> methods; private final Map<String, LoxFunction> methods;
LoxClass(String name, Map<String, LoxFunction> methods) { LoxClass(String name, LoxClass superclass, Map<String, LoxFunction> methods) {
this.superclass = superclass;
this.name = name; this.name = name;
this.methods = methods; this.methods = methods;
} }
@ -15,7 +17,9 @@ class LoxClass implements LoxCallable {
if (methods.containsKey(name)) { if (methods.containsKey(name)) {
return methods.get(name); return methods.get(name);
} }
if (superclass != null) {
return superclass.findMethod(name);
}
return null; return null;
} }
@Override @Override

@ -33,6 +33,11 @@ class Parser {
} }
private Stmt classDeclaration(){ private Stmt classDeclaration(){
Token name = consume(IDENTIFIER, "Expect class name."); 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"); consume(LEFT_BRACE, "Expect '{' before class body");
List<Stmt.Function> methods = new ArrayList<>(); List<Stmt.Function> methods = new ArrayList<>();
@ -41,8 +46,7 @@ class Parser {
} }
consume(RIGHT_BRACE, "Expect '}' after class body."); consume(RIGHT_BRACE, "Expect '}' after class body.");
return new Stmt.Class(name, superclass, methods);
return new Stmt.Class(name,)
} }
private Expr expression(){ private Expr expression(){
return assignment(); return assignment();
@ -322,7 +326,13 @@ class Parser {
if (match(IDENTIFIER)) { if (match(IDENTIFIER)) {
return new Expr.Variable(previous()); return new Expr.Variable(previous());
} }
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());
throw error(peek(), "Expect expression."); throw error(peek(), "Expect expression.");

@ -21,7 +21,8 @@ class Resolver implements Expr.Visitor<Void>, Stmt.Visitor<Void> {
} }
private enum ClassType { private enum ClassType {
NONE, NONE,
CLASS CLASS,
SUBCLASS
} }
private ClassType currentClass = ClassType.NONE; private ClassType currentClass = ClassType.NONE;
@ -57,6 +58,19 @@ class Resolver implements Expr.Visitor<Void>, Stmt.Visitor<Void> {
declare(stmt.name); declare(stmt.name);
define(stmt.name); define(stmt.name);
if (stmt.superclass != null &&
stmt.name.lexeme.equals(stmt.superclass.name.lexeme)) {
Lox.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(); beginScope();
scopes.peek().put("this", true); scopes.peek().put("this", true);
for (Stmt.Function method : stmt.methods) { for (Stmt.Function method : stmt.methods) {
@ -67,7 +81,7 @@ class Resolver implements Expr.Visitor<Void>, Stmt.Visitor<Void> {
resolveFunction(method, declaration); resolveFunction(method, declaration);
} }
endScope(); endScope();
if (stmt.superclass != null) endScope();
currentClass = enclosingClass; currentClass = enclosingClass;
return null; return null;
} }
@ -196,6 +210,18 @@ class Resolver implements Expr.Visitor<Void>, Stmt.Visitor<Void> {
return null; return null;
} }
@Override @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) { public Void visitThisExpr(Expr.This expr) {
if (currentClass == ClassType.NONE) { if (currentClass == ClassType.NONE) {
Lox.error(expr.keyword, Lox.error(expr.keyword,

@ -5,6 +5,7 @@ import java.util.List;
abstract class Stmt { abstract class Stmt {
interface Visitor<R> { interface Visitor<R> {
R visitBlockStmt(Block stmt); R visitBlockStmt(Block stmt);
R visitClassStmt(Class stmt);
R visitIfStmt(If stmt); R visitIfStmt(If stmt);
R visitExpressionStmt(Expression stmt); R visitExpressionStmt(Expression stmt);
R visitFunctionStmt(Function stmt); R visitFunctionStmt(Function stmt);
@ -25,6 +26,20 @@ abstract class Stmt {
final List<Stmt> statements; final List<Stmt> statements;
} }
static class Class extends Stmt {
Class(Token name, List<Stmt.Function> methods) {
this.name = name;
this.methods = methods;
}
@Override
<R> R accept(Visitor<R> visitor) {
return visitor.visitClassStmt(this);
}
final Token name;
final List<Stmt.Function> methods;
}
static class If extends Stmt { static class If extends Stmt {
If(Expr condition, Stmt thenBranch, Stmt elseBranch) { If(Expr condition, Stmt thenBranch, Stmt elseBranch) {
this.condition = condition; this.condition = condition;

@ -21,6 +21,7 @@ public class GenerateAst {
"Literal : Object value", "Literal : Object value",
"Logical : Expr left, Token operator, Expr right", "Logical : Expr left, Token operator, Expr right",
"Set : Expr object, Token name, Expr value", "Set : Expr object, Token name, Expr value",
"Super : Token keyword, Token method",
"This : Token keyword", "This : Token keyword",
"Unary : Token operator, Expr right", "Unary : Token operator, Expr right",
"Variable : Token name" "Variable : Token name"