From bec5a8285d6a89fc88e4d66989297e7f9441d1dc Mon Sep 17 00:00:00 2001 From: quephird Date: Tue, 19 Mar 2024 14:53:27 -0700 Subject: [PATCH 1/2] One possible approach. --- slox/Interpreter.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/slox/Interpreter.swift b/slox/Interpreter.swift index 3d927fd..0435a8b 100644 --- a/slox/Interpreter.swift +++ b/slox/Interpreter.swift @@ -435,6 +435,10 @@ class Interpreter { args: [ResolvedExpression]) throws -> LoxValue { let callee = try evaluate(expr: calleeExpr) + if case .instance(let klass as LoxClass) = callee, klass.name == "List" { + return try handleListExpression(elements: args) + } + let actualCallable: LoxCallable = switch callee { case .userDefinedFunction(let userDefinedFunction): userDefinedFunction From 12341a051bcab0e3dd42a338871ab1fe62684f15 Mon Sep 17 00:00:00 2001 From: quephird Date: Wed, 20 Mar 2024 11:06:48 -0700 Subject: [PATCH 2/2] First draft. --- slox/Interpreter.swift | 26 +++-------------------- slox/LoxInstance.swift | 6 +----- slox/LoxList.swift | 43 ++++++++++++++++++++++++++++++++++----- slox/LoxValue.swift | 19 +++++++++++++++++ slox/NativeFunction.swift | 25 ----------------------- 5 files changed, 61 insertions(+), 58 deletions(-) diff --git a/slox/Interpreter.swift b/slox/Interpreter.swift index 0435a8b..6aa545f 100644 --- a/slox/Interpreter.swift +++ b/slox/Interpreter.swift @@ -8,17 +8,6 @@ import Foundation class Interpreter { - static let standardLibrary = """ - class List { - append(element) { - appendNative(this, element); - } - - deleteAt(index) { - return deleteAtNative(this, index); - } - } -""" var environment: Environment = Environment() init() { @@ -40,8 +29,6 @@ class Interpreter { environment.define(name: String(describing: nativeFunction), value: .nativeFunction(nativeFunction)) } - - try! interpret(source: Self.standardLibrary) } func interpret(source: String) throws { @@ -435,10 +422,6 @@ class Interpreter { args: [ResolvedExpression]) throws -> LoxValue { let callee = try evaluate(expr: calleeExpr) - if case .instance(let klass as LoxClass) = callee, klass.name == "List" { - return try handleListExpression(elements: args) - } - let actualCallable: LoxCallable = switch callee { case .userDefinedFunction(let userDefinedFunction): userDefinedFunction @@ -446,6 +429,8 @@ class Interpreter { nativeFunction case .instance(let klass as LoxClass): klass + case .callable(let callable): + callable default: throw RuntimeError.notACallableObject } @@ -522,12 +507,7 @@ class Interpreter { return try evaluate(expr: element) } - guard case .instance(let listClass as LoxClass) = try environment.getValue(name: "List") else { - // TODO: Do we need throw an exception here? - fatalError() - } - - let list = LoxList(elements: elementValues, klass: listClass) + let list = LoxList(elements: elementValues) return .instance(list) } diff --git a/slox/LoxInstance.swift b/slox/LoxInstance.swift index c27c6b6..7939a78 100644 --- a/slox/LoxInstance.swift +++ b/slox/LoxInstance.swift @@ -5,7 +5,7 @@ // Created by Danielle Kefford on 3/4/24. // -class LoxInstance: Equatable { +class LoxInstance { // `klass` is what is used in the interpreter when we need // to know the class of a particular instance. Every Lox // instance, including Lox classes, need to have a non-nil @@ -48,8 +48,4 @@ class LoxInstance: Equatable { func set(propertyName: String, propertyValue: LoxValue) throws { self.properties[propertyName] = propertyValue } - - static func == (lhs: LoxInstance, rhs: LoxInstance) -> Bool { - return lhs === rhs - } } diff --git a/slox/LoxList.swift b/slox/LoxList.swift index d315847..a7413b9 100644 --- a/slox/LoxList.swift +++ b/slox/LoxList.swift @@ -7,21 +7,54 @@ class LoxList: LoxInstance { var elements: [LoxValue] - var count: Int { - return elements.count + + private class AppendImpl: LoxCallable { + var arity: Int = 1 + var listInstance: LoxList + + init(listInstance: LoxList) { + self.listInstance = listInstance + } + + func call(interpreter: Interpreter, args: [LoxValue]) throws -> LoxValue { + let element = args[0] + self.listInstance.elements.append(element) + return .nil + } + } + + private class DeleteAtImpl: LoxCallable { + var arity: Int = 1 + var listInstance: LoxList + + init(listInstance: LoxList) { + self.listInstance = listInstance + } + + func call(interpreter: Interpreter, args: [LoxValue]) throws -> LoxValue { + guard case .number(let index) = args[0] else { + throw RuntimeError.indexMustBeANumber + } + + return self.listInstance.elements.remove(at: Int(index)) + } } - init(elements: [LoxValue], klass: LoxClass) { + init(elements: [LoxValue]) { self.elements = elements - super.init(klass: klass) + super.init(klass: nil) } override func get(propertyName: String) throws -> LoxValue { switch propertyName { case "count": return .number(Double(elements.count)) + case "append": + return .callable(AppendImpl(listInstance: self)) + case "deleteAt": + return .callable(DeleteAtImpl(listInstance: self)) default: - return try super.get(propertyName: propertyName) + throw RuntimeError.undefinedProperty(propertyName) } } diff --git a/slox/LoxValue.swift b/slox/LoxValue.swift index 318435a..733e4cc 100644 --- a/slox/LoxValue.swift +++ b/slox/LoxValue.swift @@ -13,6 +13,7 @@ enum LoxValue: CustomStringConvertible, Equatable { case userDefinedFunction(UserDefinedFunction) case nativeFunction(NativeFunction) case instance(LoxInstance) + case callable(LoxCallable) var description: String { switch self { @@ -42,6 +43,8 @@ enum LoxValue: CustomStringConvertible, Equatable { return string case .instance(let instance): return "" + case .callable: + return "" } } @@ -74,4 +77,20 @@ enum LoxValue: CustomStringConvertible, Equatable { return true } } + + // NOTA BENE: This equality conformance is only for unit tests + static func == (lhs: LoxValue, rhs: LoxValue) -> Bool { + switch (lhs, rhs) { + case (.string(let lhsString), .string(let rhsString)): + return lhsString == rhsString + case (.number(let lhsNumber), .number(let rhsNumber)): + return lhsNumber == rhsNumber + case (.boolean(let lhsBoolean), .boolean(let rhsBoolean)): + return lhsBoolean == rhsBoolean + case (.nil, .nil): + return true + default: + return false + } + } } diff --git a/slox/NativeFunction.swift b/slox/NativeFunction.swift index b8f89be..ddf0dc7 100644 --- a/slox/NativeFunction.swift +++ b/slox/NativeFunction.swift @@ -9,17 +9,11 @@ import Foundation enum NativeFunction: LoxCallable, Equatable, CaseIterable { case clock - case appendNative - case deleteAtNative var arity: Int { switch self { case .clock: return 0 - case .appendNative: - return 2 - case .deleteAtNative: - return 2 } } @@ -27,25 +21,6 @@ enum NativeFunction: LoxCallable, Equatable, CaseIterable { switch self { case .clock: return .number(Date().timeIntervalSince1970) - case .appendNative: - guard case .instance(let loxList as LoxList) = args[0] else { - throw RuntimeError.notAList - } - - let element = args[1] - loxList.elements.append(element) - - return .nil - case .deleteAtNative: - guard case .instance(let loxList as LoxList) = args[0] else { - throw RuntimeError.notAList - } - - guard case .number(let index) = args[1] else { - throw RuntimeError.indexMustBeANumber - } - - return loxList.elements.remove(at: Int(index)) } } }