From 3cd6662d03a5f3f3acb9e3a13e8e57d49c37403b Mon Sep 17 00:00:00 2001 From: quephird Date: Fri, 22 Mar 2024 15:04:23 -0700 Subject: [PATCH 1/3] First cut. --- slox/Interpreter.swift | 14 +++++++++++++- slox/RuntimeError.swift | 6 +++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/slox/Interpreter.swift b/slox/Interpreter.swift index 3d927fd..e58cc7b 100644 --- a/slox/Interpreter.swift +++ b/slox/Interpreter.swift @@ -384,13 +384,25 @@ class Interpreter { return .string(leftString + rightString) } + if case .instance(let leftList as LoxList) = leftValue, + case .instance(let rightList as LoxList) = rightValue, + case .plus = oper.type { + guard case .instance(let listClass as LoxClass) = try environment.getValue(name: "List") else { + fatalError() + } + + let newElements = leftList.elements + rightList.elements + let list = LoxList(elements: newElements, klass: listClass) + return .instance(list) + } + switch oper.type { case .bangEqual: return .boolean(!leftValue.isEqual(to: rightValue)) case .equalEqual: return .boolean(leftValue.isEqual(to: rightValue)) case .plus: - throw RuntimeError.binaryOperandsMustBeNumbersOrStrings + throw RuntimeError.binaryOperandsMustBeNumbersOrStringsOrLists case .minus, .star, .slash, .greater, .greaterEqual, .less, .lessEqual: throw RuntimeError.binaryOperandsMustBeNumbers default: diff --git a/slox/RuntimeError.swift b/slox/RuntimeError.swift index 14d9dc4..2cf3a2e 100644 --- a/slox/RuntimeError.swift +++ b/slox/RuntimeError.swift @@ -11,7 +11,7 @@ enum RuntimeError: CustomStringConvertible, Equatable, LocalizedError { case unaryOperandMustBeNumber case unsupportedUnaryOperator case binaryOperandsMustBeNumbers - case binaryOperandsMustBeNumbersOrStrings + case binaryOperandsMustBeNumbersOrStringsOrLists case unsupportedBinaryOperator case undefinedVariable(String) case notAFunctionDeclaration @@ -34,8 +34,8 @@ enum RuntimeError: CustomStringConvertible, Equatable, LocalizedError { return "Error: unsupported unary operator" case .binaryOperandsMustBeNumbers: return "Error: operands must be both numbers" - case .binaryOperandsMustBeNumbersOrStrings: - return "Error: operands must be either both numbers or both strings" + case .binaryOperandsMustBeNumbersOrStringsOrLists: + return "Error: operands must be either both numbers, strings, or lists" case .unsupportedBinaryOperator: return "Error: unsupported binary operator" case .undefinedVariable(let name): From 2e7ddcad642626035bed98e78a9c2eba8dd7ab53 Mon Sep 17 00:00:00 2001 From: quephird Date: Fri, 22 Mar 2024 15:26:32 -0700 Subject: [PATCH 2/3] Added supporting test, albeit not a great one. --- sloxTests/InterpreterTests.swift | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/sloxTests/InterpreterTests.swift b/sloxTests/InterpreterTests.swift index 52a8130..c2b4082 100644 --- a/sloxTests/InterpreterTests.swift +++ b/sloxTests/InterpreterTests.swift @@ -479,6 +479,18 @@ quux.count XCTAssertEqual(actual, expected) } + func testInterpretAddingTwoLists() throws { + let input = """ +var xyzzy = [1, 2, 3] + [4, 5, 6]; +xyzzy.count +""" + + let interpreter = Interpreter() + let actual = try interpreter.interpretRepl(source: input) + let expected: LoxValue = .number(6) + XCTAssertEqual(actual, expected) + } + func testInterpretExpressionWithListSubscriptingMethodInvocationAndPropertyGetting() throws { let input = """ class Foo { } From 2c698773ef97d04669e893ec3814d5f6ddc1ac66 Mon Sep 17 00:00:00 2001 From: quephird Date: Sat, 23 Mar 2024 15:15:53 -0700 Subject: [PATCH 3/3] Introduced helper in `Interpreter` to reduce duplication, and fortified supporting unit test. --- slox/Interpreter.swift | 24 +++++++++++------------- sloxTests/InterpreterTests.swift | 17 ++++++++++++++--- 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/slox/Interpreter.swift b/slox/Interpreter.swift index e58cc7b..24910b7 100644 --- a/slox/Interpreter.swift +++ b/slox/Interpreter.swift @@ -387,13 +387,8 @@ class Interpreter { if case .instance(let leftList as LoxList) = leftValue, case .instance(let rightList as LoxList) = rightValue, case .plus = oper.type { - guard case .instance(let listClass as LoxClass) = try environment.getValue(name: "List") else { - fatalError() - } - let newElements = leftList.elements + rightList.elements - let list = LoxList(elements: newElements, klass: listClass) - return .instance(list) + return try makeList(elements: newElements) } switch oper.type { @@ -530,13 +525,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) - return .instance(list) + return try makeList(elements: elementValues) } private func handleSubscriptGetExpression(listExpr: ResolvedExpression, @@ -568,4 +557,13 @@ class Interpreter { list[Int(index)] = value return value } + + private func makeList(elements: [LoxValue]) throws -> LoxValue { + guard case .instance(let listClass as LoxClass) = try environment.getValue(name: "List") else { + fatalError() + } + + let list = LoxList(elements: elements, klass: listClass) + return .instance(list) + } } diff --git a/sloxTests/InterpreterTests.swift b/sloxTests/InterpreterTests.swift index c2b4082..e36fb4c 100644 --- a/sloxTests/InterpreterTests.swift +++ b/sloxTests/InterpreterTests.swift @@ -482,12 +482,23 @@ quux.count func testInterpretAddingTwoLists() throws { let input = """ var xyzzy = [1, 2, 3] + [4, 5, 6]; -xyzzy.count +xyzzy """ let interpreter = Interpreter() - let actual = try interpreter.interpretRepl(source: input) - let expected: LoxValue = .number(6) + guard case .instance(let list as LoxList) = try interpreter.interpretRepl(source: input) else { + XCTFail() + return + } + let actual = list.elements + let expected: [LoxValue] = [ + .number(1), + .number(2), + .number(3), + .number(4), + .number(5), + .number(6), + ] XCTAssertEqual(actual, expected) }