Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Centralize list methods in LoxList #25

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
22 changes: 3 additions & 19 deletions slox/Interpreter.swift
Expand Up @@ -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() {
Expand All @@ -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 {
Expand Down Expand Up @@ -442,6 +429,8 @@ class Interpreter {
nativeFunction
case .instance(let klass as LoxClass):
klass
case .callable(let callable):
callable
default:
throw RuntimeError.notACallableObject
}
Expand Down Expand Up @@ -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)
}

Expand Down
6 changes: 1 addition & 5 deletions slox/LoxInstance.swift
Expand Up @@ -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
Expand Down Expand Up @@ -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
}
}
43 changes: 38 additions & 5 deletions slox/LoxList.swift
Expand Up @@ -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)
}
}

Expand Down
19 changes: 19 additions & 0 deletions slox/LoxValue.swift
Expand Up @@ -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 {
Expand Down Expand Up @@ -42,6 +43,8 @@ enum LoxValue: CustomStringConvertible, Equatable {
return string
case .instance(let instance):
return "<instance: \(instance.klass.name)>"
case .callable:
return "<callable>"
}
}

Expand Down Expand Up @@ -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
}
}
}
25 changes: 0 additions & 25 deletions slox/NativeFunction.swift
Expand Up @@ -9,43 +9,18 @@ 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
}
}

func call(interpreter: Interpreter, args: [LoxValue]) throws -> LoxValue {
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))
}
}
}