From 580a5acb580a858c8ffa705f5f64f095cb0b4b37 Mon Sep 17 00:00:00 2001 From: Tom Willemse Date: Wed, 17 Mar 2021 22:52:35 -0700 Subject: [PATCH] Chapter 12, Sections 1, 2, and 3 --- .../craftinginterpreters/lox/CMakeLists.txt | 2 ++ .../craftinginterpreters/lox/Interpreter.java | 31 +++++++++++++++++++ .../craftinginterpreters/lox/LoxClass.java | 27 ++++++++++++++++ .../craftinginterpreters/lox/LoxInstance.java | 30 ++++++++++++++++++ src/com/craftinginterpreters/lox/Parser.java | 22 +++++++++++++ .../craftinginterpreters/lox/Resolver.java | 20 ++++++++++++ .../tool/GenerateAst.java | 9 +++--- 7 files changed, 137 insertions(+), 4 deletions(-) create mode 100644 src/com/craftinginterpreters/lox/LoxClass.java create mode 100644 src/com/craftinginterpreters/lox/LoxInstance.java diff --git a/src/com/craftinginterpreters/lox/CMakeLists.txt b/src/com/craftinginterpreters/lox/CMakeLists.txt index 391331a..aeab240 100644 --- a/src/com/craftinginterpreters/lox/CMakeLists.txt +++ b/src/com/craftinginterpreters/lox/CMakeLists.txt @@ -23,4 +23,6 @@ add_jar(Lox LoxFunction.java Return.java Resolver.java + LoxClass.java + LoxInstance.java ENTRY_POINT com/craftinginterpreters/lox/Lox) diff --git a/src/com/craftinginterpreters/lox/Interpreter.java b/src/com/craftinginterpreters/lox/Interpreter.java index 4092191..5c1ea50 100644 --- a/src/com/craftinginterpreters/lox/Interpreter.java +++ b/src/com/craftinginterpreters/lox/Interpreter.java @@ -49,6 +49,19 @@ class Interpreter implements Expr.Visitor, Stmt.Visitor { 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, Stmt.Visitor { 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, Stmt.Visitor { 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 statements) { try { for (Stmt statement : statements) { diff --git a/src/com/craftinginterpreters/lox/LoxClass.java b/src/com/craftinginterpreters/lox/LoxClass.java new file mode 100644 index 0000000..4f7ef4f --- /dev/null +++ b/src/com/craftinginterpreters/lox/LoxClass.java @@ -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 arguments) { + LoxInstance instance = new LoxInstance(this); + return instance; + } + + @Override + public int arity() { + return 0; + } +} diff --git a/src/com/craftinginterpreters/lox/LoxInstance.java b/src/com/craftinginterpreters/lox/LoxInstance.java new file mode 100644 index 0000000..4e5eb9e --- /dev/null +++ b/src/com/craftinginterpreters/lox/LoxInstance.java @@ -0,0 +1,30 @@ +package com.craftinginterpreters.lox; + +import java.util.HashMap; +import java.util.Map; + +class LoxInstance { + private LoxClass klass; + private final Map 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"; + } +} diff --git a/src/com/craftinginterpreters/lox/Parser.java b/src/com/craftinginterpreters/lox/Parser.java index 528326c..fe51e45 100644 --- a/src/com/craftinginterpreters/lox/Parser.java +++ b/src/com/craftinginterpreters/lox/Parser.java @@ -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 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; } diff --git a/src/com/craftinginterpreters/lox/Resolver.java b/src/com/craftinginterpreters/lox/Resolver.java index 2a870ab..a7c9911 100644 --- a/src/com/craftinginterpreters/lox/Resolver.java +++ b/src/com/craftinginterpreters/lox/Resolver.java @@ -32,6 +32,13 @@ class Resolver implements Expr.Visitor, Stmt.Visitor { 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, Stmt.Visitor { 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, Stmt.Visitor { 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); diff --git a/src/com/craftinginterpreters/tool/GenerateAst.java b/src/com/craftinginterpreters/tool/GenerateAst.java index 2bf442b..0ca2239 100644 --- a/src/com/craftinginterpreters/tool/GenerateAst.java +++ b/src/com/craftinginterpreters/tool/GenerateAst.java @@ -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 arguments", "Grouping : Expr expression", - "Literal : Object value", "Logical : Expr left, Token operator, Expr right", + "Call : Expr callee, Token paren, List 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 statements", "Expression : Expr expression", - "Function : Token name, List params, List body", + Arrays.asList("Block : List statements", "Class : Token name, List methods", + "Expression : Expr expression", "Function : Token name, List params, List 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"));