Chapter 12, Sections 1, 2, and 3
This commit is contained in:
parent
b04b6bcf2f
commit
580a5acb58
7 changed files with 137 additions and 4 deletions
|
@ -23,4 +23,6 @@ add_jar(Lox
|
|||
LoxFunction.java
|
||||
Return.java
|
||||
Resolver.java
|
||||
LoxClass.java
|
||||
LoxInstance.java
|
||||
ENTRY_POINT com/craftinginterpreters/lox/Lox)
|
||||
|
|
|
@ -49,6 +49,19 @@ class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
|
|||
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
|
||||
public Object visitUnaryExpr(Expr.Unary expr) {
|
||||
Object right = evaluate(expr.right);
|
||||
|
@ -161,6 +174,14 @@ class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
|
|||
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
|
||||
public Void visitExpressionStmt(Stmt.Expression stmt) {
|
||||
evaluate(stmt.expression);
|
||||
|
@ -304,6 +325,16 @@ class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
|
|||
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) {
|
||||
try {
|
||||
for (Stmt statement : statements) {
|
||||
|
|
27
src/com/craftinginterpreters/lox/LoxClass.java
Normal file
27
src/com/craftinginterpreters/lox/LoxClass.java
Normal 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;
|
||||
}
|
||||
}
|
30
src/com/craftinginterpreters/lox/LoxInstance.java
Normal file
30
src/com/craftinginterpreters/lox/LoxInstance.java
Normal 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";
|
||||
}
|
||||
}
|
|
@ -33,6 +33,8 @@ class Parser {
|
|||
|
||||
private Stmt declaration() {
|
||||
try {
|
||||
if (match(CLASS))
|
||||
return classDeclaration();
|
||||
if (match(FUN))
|
||||
return function("function");
|
||||
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() {
|
||||
if (match(FOR))
|
||||
return forStatement();
|
||||
|
@ -201,6 +217,9 @@ class Parser {
|
|||
if (expr instanceof Expr.Variable) {
|
||||
Token name = ((Expr.Variable) expr).name;
|
||||
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.");
|
||||
|
@ -313,6 +332,9 @@ class Parser {
|
|||
while (true) {
|
||||
if (match(LEFT_PAREN)) {
|
||||
expr = finishCall(expr);
|
||||
} else if (match(DOT)) {
|
||||
Token name = consume(IDENTIFIER, "Expect property name after '.'");
|
||||
expr = new Expr.Get(expr, name);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -32,6 +32,13 @@ class Resolver implements Expr.Visitor<Void>, Stmt.Visitor<Void> {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitClassStmt(Stmt.Class stmt) {
|
||||
declare(stmt.name);
|
||||
define(stmt.name);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitExpressionStmt(Stmt.Expression stmt) {
|
||||
resolve(stmt.expression);
|
||||
|
@ -117,6 +124,12 @@ class Resolver implements Expr.Visitor<Void>, Stmt.Visitor<Void> {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitGetExpr(Expr.Get expr) {
|
||||
resolve(expr.object);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitGroupingExpr(Expr.Grouping expr) {
|
||||
resolve(expr.expression);
|
||||
|
@ -128,6 +141,13 @@ class Resolver implements Expr.Visitor<Void>, Stmt.Visitor<Void> {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitSetExpr(Expr.Set expr) {
|
||||
resolve(expr.value);
|
||||
resolve(expr.object);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitLogicalExpr(Expr.Logical expr) {
|
||||
resolve(expr.left);
|
||||
|
|
|
@ -16,12 +16,13 @@ public class GenerateAst {
|
|||
|
||||
defineAst(outputDir, "Expr",
|
||||
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",
|
||||
"Literal : Object value", "Logical : Expr left, Token operator, Expr right",
|
||||
"Call : Expr callee, Token paren, List<Expr> arguments", "Get : Expr object, Token name",
|
||||
"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"));
|
||||
defineAst(outputDir, "Stmt",
|
||||
Arrays.asList("Block : List<Stmt> statements", "Expression : Expr expression",
|
||||
"Function : Token name, List<Token> params, List<Stmt> body",
|
||||
Arrays.asList("Block : List<Stmt> statements", "Class : Token name, List<Stmt.Function> methods",
|
||||
"Expression : Expr expression", "Function : Token name, List<Token> params, List<Stmt> body",
|
||||
"If : Expr condition, Stmt thenBranch, Stmt elseBranch", "Print : Expr expression",
|
||||
"Return : Token keyword, Expr value", "Var : Token name, Expr initializer",
|
||||
"While : Expr condition, Stmt body"));
|
||||
|
|
Loading…
Reference in a new issue