diff --git a/slox/Interpreter.swift b/slox/Interpreter.swift index 3d927fd..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 { @@ -442,6 +429,8 @@ class Interpreter { nativeFunction case .instance(let klass as LoxClass): klass + case .callable(let callable): + callable default: throw RuntimeError.notACallableObject } @@ -518,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)) } } }