Chapter 12, Sections 1, 2, and 3

This commit is contained in:
Tom Willemse 2021-03-17 22:52:35 -07:00
parent b04b6bcf2f
commit 580a5acb58
7 changed files with 137 additions and 4 deletions

View file

@ -23,4 +23,6 @@ add_jar(Lox
LoxFunction.java LoxFunction.java
Return.java Return.java
Resolver.java Resolver.java
LoxClass.java
LoxInstance.java
ENTRY_POINT com/craftinginterpreters/lox/Lox) ENTRY_POINT com/craftinginterpreters/lox/Lox)

View file

@ -49,6 +49,19 @@ class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
return evaluate(expr.right); return evaluate(expr.right);
} }
@Override
public Object visitSetExpr(Expr.Set expr) {
Object object = evaluate(expr.object);
if (!(object instanceof LoxInstance)) {
throw new RuntimeError(expr.name, "Only instances have fields.");
}
Object value = evaluate(expr.value);
((LoxInstance) object).set(expr.name, value);
return value;
}
@Override @Override
public Object visitUnaryExpr(Expr.Unary expr) { public Object visitUnaryExpr(Expr.Unary expr) {
Object right = evaluate(expr.right); Object right = evaluate(expr.right);
@ -161,6 +174,14 @@ class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
return null; return null;
} }
@Override
public Void visitClassStmt(Stmt.Class stmt) {
environment.define(stmt.name.lexeme, null);
LoxClass klass = new LoxClass(stmt.name.lexeme);
environment.assign(stmt.name, klass);
return null;
}
@Override @Override
public Void visitExpressionStmt(Stmt.Expression stmt) { public Void visitExpressionStmt(Stmt.Expression stmt) {
evaluate(stmt.expression); evaluate(stmt.expression);
@ -304,6 +325,16 @@ class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
return function.call(this, arguments); return function.call(this, arguments);
} }
@Override
public Object visitGetExpr(Expr.Get expr) {
Object object = evaluate(expr.object);
if (object instanceof LoxInstance) {
return ((LoxInstance) object).get(expr.name);
}
throw new RuntimeError(expr.name, "Only instances have properties.");
}
public void interpret(List<Stmt> statements) { public void interpret(List<Stmt> statements) {
try { try {
for (Stmt statement : statements) { for (Stmt statement : statements) {

View file

@ -0,0 +1,27 @@
package com.craftinginterpreters.lox;
import java.util.List;
class LoxClass implements LoxCallable {
final String name;
LoxClass(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
@Override
public Object call(Interpreter interpreter, List<Object> arguments) {
LoxInstance instance = new LoxInstance(this);
return instance;
}
@Override
public int arity() {
return 0;
}
}

View file

@ -0,0 +1,30 @@
package com.craftinginterpreters.lox;
import java.util.HashMap;
import java.util.Map;
class LoxInstance {
private LoxClass klass;
private final Map<String, Object> fields = new HashMap<>();
LoxInstance(LoxClass klass) {
this.klass = klass;
}
Object get(Token name) {
if (fields.containsKey(name.lexeme)) {
return fields.get(name.lexeme);
}
throw new RuntimeError(name, "Undefined proprety '" + name.lexeme + "'.");
}
void set(Token name, Object value) {
fields.put(name.lexeme, value);
}
@Override
public String toString() {
return klass.name + " instance";
}
}

View file

@ -33,6 +33,8 @@ class Parser {
private Stmt declaration() { private Stmt declaration() {
try { try {
if (match(CLASS))
return classDeclaration();
if (match(FUN)) if (match(FUN))
return function("function"); return function("function");
if (match(VAR)) if (match(VAR))
@ -45,6 +47,20 @@ class Parser {
} }
} }
private Stmt classDeclaration() {
Token name = consume(IDENTIFIER, "Expect class name.");
consume(LEFT_BRACE, "Expect '{' before class body.");
List<Stmt.Function> methods = new ArrayList<>();
while (!check(RIGHT_BRACE) && !isAtEnd()) {
methods.add(function("method"));
}
consume(RIGHT_BRACE, "Expect '}' after class body.");
return new Stmt.Class(name, methods);
}
private Stmt statement() { private Stmt statement() {
if (match(FOR)) if (match(FOR))
return forStatement(); return forStatement();
@ -201,6 +217,9 @@ class Parser {
if (expr instanceof Expr.Variable) { if (expr instanceof Expr.Variable) {
Token name = ((Expr.Variable) expr).name; Token name = ((Expr.Variable) expr).name;
return new Expr.Assign(name, value); return new Expr.Assign(name, value);
} else if (expr instanceof Expr.Get) {
Expr.Get get = (Expr.Get)expr;
return new Expr.Set(get.object, get.name, value);
} }
error(equals, "Invalid assignment target."); error(equals, "Invalid assignment target.");
@ -313,6 +332,9 @@ class Parser {
while (true) { while (true) {
if (match(LEFT_PAREN)) { if (match(LEFT_PAREN)) {
expr = finishCall(expr); expr = finishCall(expr);
} else if (match(DOT)) {
Token name = consume(IDENTIFIER, "Expect property name after '.'");
expr = new Expr.Get(expr, name);
} else { } else {
break; break;
} }

View file

@ -32,6 +32,13 @@ class Resolver implements Expr.Visitor<Void>, Stmt.Visitor<Void> {
return null; return null;
} }
@Override
public Void visitClassStmt(Stmt.Class stmt) {
declare(stmt.name);
define(stmt.name);
return null;
}
@Override @Override
public Void visitExpressionStmt(Stmt.Expression stmt) { public Void visitExpressionStmt(Stmt.Expression stmt) {
resolve(stmt.expression); resolve(stmt.expression);
@ -117,6 +124,12 @@ class Resolver implements Expr.Visitor<Void>, Stmt.Visitor<Void> {
return null; return null;
} }
@Override
public Void visitGetExpr(Expr.Get expr) {
resolve(expr.object);
return null;
}
@Override @Override
public Void visitGroupingExpr(Expr.Grouping expr) { public Void visitGroupingExpr(Expr.Grouping expr) {
resolve(expr.expression); resolve(expr.expression);
@ -128,6 +141,13 @@ class Resolver implements Expr.Visitor<Void>, Stmt.Visitor<Void> {
return null; return null;
} }
@Override
public Void visitSetExpr(Expr.Set expr) {
resolve(expr.value);
resolve(expr.object);
return null;
}
@Override @Override
public Void visitLogicalExpr(Expr.Logical expr) { public Void visitLogicalExpr(Expr.Logical expr) {
resolve(expr.left); resolve(expr.left);

View file

@ -16,12 +16,13 @@ public class GenerateAst {
defineAst(outputDir, "Expr", defineAst(outputDir, "Expr",
Arrays.asList("Assign : Token name, Expr value", "Binary : Expr left, Token operator, Expr right", Arrays.asList("Assign : Token name, Expr value", "Binary : Expr left, Token operator, Expr right",
"Call : Expr callee, Token paren, List<Expr> arguments", "Grouping : Expr expression", "Call : Expr callee, Token paren, List<Expr> arguments", "Get : Expr object, Token name",
"Literal : Object value", "Logical : Expr left, Token operator, Expr right", "Grouping : Expr expression", "Literal : Object value",
"Logical : Expr left, Token operator, Expr right", "Set : Expr object, Token name, Expr value",
"Unary : Token operator, Expr right", "Variable : Token name")); "Unary : Token operator, Expr right", "Variable : Token name"));
defineAst(outputDir, "Stmt", defineAst(outputDir, "Stmt",
Arrays.asList("Block : List<Stmt> statements", "Expression : Expr expression", Arrays.asList("Block : List<Stmt> statements", "Class : Token name, List<Stmt.Function> methods",
"Function : Token name, List<Token> params, List<Stmt> body", "Expression : Expr expression", "Function : Token name, List<Token> params, List<Stmt> body",
"If : Expr condition, Stmt thenBranch, Stmt elseBranch", "Print : Expr expression", "If : Expr condition, Stmt thenBranch, Stmt elseBranch", "Print : Expr expression",
"Return : Token keyword, Expr value", "Var : Token name, Expr initializer", "Return : Token keyword, Expr value", "Var : Token name, Expr initializer",
"While : Expr condition, Stmt body")); "While : Expr condition, Stmt body"));