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

Distinguish doubles from ints #27

Merged
merged 4 commits into from Mar 23, 2024
Merged
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
81 changes: 56 additions & 25 deletions slox/Interpreter.swift
Expand Up @@ -336,11 +336,14 @@ class Interpreter {

switch oper.type {
case .minus:
guard case .number(let number) = value else {
switch value {
case .double(let number):
return .double(-number)
case .int(let number):
return .int(-number)
default:
throw RuntimeError.unaryOperandMustBeNumber
}

return .number(-number)
case .bang:
return .boolean(!value.isTruthy)
default:
Expand All @@ -354,17 +357,17 @@ class Interpreter {
let leftValue = try evaluate(expr: leftExpr)
let rightValue = try evaluate(expr: rightExpr)

if case .number(let leftNumber) = leftValue,
case .number(let rightNumber) = rightValue {
switch (leftValue, rightValue) {
case (.int(let leftNumber), .int(let rightNumber)):
switch oper.type {
case .plus:
return .number(leftNumber + rightNumber)
return .int(leftNumber + rightNumber)
case .minus:
return .number(leftNumber - rightNumber)
return .int(leftNumber - rightNumber)
case .star:
return .number(leftNumber * rightNumber)
return .int(leftNumber * rightNumber)
case .slash:
return .number(leftNumber / rightNumber)
return .int(leftNumber / rightNumber)
case .greater:
return .boolean(leftNumber > rightNumber)
case .greaterEqual:
Expand All @@ -376,19 +379,47 @@ class Interpreter {
default:
break
}
}

if case .string(let leftString) = leftValue,
case .string(let rightString) = rightValue,
case .plus = oper.type {
return .string(leftString + rightString)
}
case (.int, .double), (.double, .int), (.double, .double):
let leftNumber = try leftValue.convertToRawDouble()
let rightNumber = try rightValue.convertToRawDouble()

if case .instance(let leftList as LoxList) = leftValue,
case .instance(let rightList as LoxList) = rightValue,
case .plus = oper.type {
let newElements = leftList.elements + rightList.elements
return try makeList(elements: newElements)
switch oper.type {
case .plus:
return .double(leftNumber + rightNumber)
case .minus:
return .double(leftNumber - rightNumber)
case .star:
return .double(leftNumber * rightNumber)
case .slash:
return .double(leftNumber / rightNumber)
case .greater:
return .boolean(leftNumber > rightNumber)
case .greaterEqual:
return .boolean(leftNumber >= rightNumber)
case .less:
return .boolean(leftNumber < rightNumber)
case .lessEqual:
return .boolean(leftNumber <= rightNumber)
default:
break
}
case (.string(let leftString), .string(let rightString)):
switch oper.type {
case .plus:
return .string(leftString + rightString)
default:
break
}
case (.instance(let leftList as LoxList), .instance(let rightList as LoxList)):
switch oper.type {
case .plus:
let newElements = leftList.elements + rightList.elements
return try makeList(elements: newElements)
default:
break
}
default:
break
}

switch oper.type {
Expand Down Expand Up @@ -534,8 +565,8 @@ class Interpreter {
throw RuntimeError.notAList
}

guard case .number(let index) = try evaluate(expr: indexExpr) else {
throw RuntimeError.indexMustBeANumber
guard case .int(let index) = try evaluate(expr: indexExpr) else {
throw RuntimeError.indexMustBeAnInteger
}

return list[Int(index)]
Expand All @@ -548,8 +579,8 @@ class Interpreter {
throw RuntimeError.notAList
}

guard case .number(let index) = try evaluate(expr: indexExpr) else {
throw RuntimeError.indexMustBeANumber
guard case .int(let index) = try evaluate(expr: indexExpr) else {
throw RuntimeError.indexMustBeAnInteger
}

let value = try evaluate(expr: valueExpr)
Expand Down
2 changes: 1 addition & 1 deletion slox/LoxList.swift
Expand Up @@ -19,7 +19,7 @@ class LoxList: LoxInstance {
override func get(propertyName: String) throws -> LoxValue {
switch propertyName {
case "count":
return .number(Double(elements.count))
return .int(elements.count)
default:
return try super.get(propertyName: propertyName)
}
Expand Down
37 changes: 34 additions & 3 deletions slox/LoxValue.swift
Expand Up @@ -7,7 +7,8 @@

enum LoxValue: CustomStringConvertible, Equatable {
case string(String)
case number(Double)
case double(Double)
case int(Int)
case boolean(Bool)
case `nil`
case userDefinedFunction(UserDefinedFunction)
Expand All @@ -18,7 +19,9 @@ enum LoxValue: CustomStringConvertible, Equatable {
switch self {
case .string(let string):
return "\"\(string)\""
case .number(let number):
case .double(let number):
return "\(number)"
case .int(let number):
return "\(number)"
case .boolean(let boolean):
return "\(boolean)"
Expand Down Expand Up @@ -50,8 +53,14 @@ enum LoxValue: CustomStringConvertible, Equatable {
switch (self, to) {
case (.nil, .nil):
return true
case (.number(let leftNumber), .number(let rightNumber)):
case (.int(let leftNumber), .int(let rightNumber)):
return leftNumber == rightNumber
case (.double(let leftNumber), .double(let rightNumber)):
return leftNumber == rightNumber
case (.int(let leftNumber), .double(let rightNumber)):
return Double(leftNumber) == rightNumber
case (.double(let leftNumber), .int(let rightNumber)):
return leftNumber == Double(rightNumber)
case (.string(let leftString), .string(let rightString)):
return leftString == rightString
case (.boolean(let leftBoolean), .boolean(let rightBoolean)):
Expand All @@ -74,4 +83,26 @@ enum LoxValue: CustomStringConvertible, Equatable {
return true
}
}

func convertToRawInt() throws -> Int {
switch self {
case .int(let number):
return number
case .double(let number):
return Int(number)
default:
throw RuntimeError.notANumber
}
}

func convertToRawDouble() throws -> Double {
switch self {
case .int(let number):
return Double(number)
case .double(let number):
return number
default:
throw RuntimeError.notANumber
}
}
}
6 changes: 3 additions & 3 deletions slox/NativeFunction.swift
Expand Up @@ -26,7 +26,7 @@ enum NativeFunction: LoxCallable, Equatable, CaseIterable {
func call(interpreter: Interpreter, args: [LoxValue]) throws -> LoxValue {
switch self {
case .clock:
return .number(Date().timeIntervalSince1970)
return .double(Date().timeIntervalSince1970)
case .appendNative:
guard case .instance(let loxList as LoxList) = args[0] else {
throw RuntimeError.notAList
Expand All @@ -41,8 +41,8 @@ enum NativeFunction: LoxCallable, Equatable, CaseIterable {
throw RuntimeError.notAList
}

guard case .number(let index) = args[1] else {
throw RuntimeError.indexMustBeANumber
guard case .int(let index) = args[1] else {
throw RuntimeError.indexMustBeAnInteger
}

return loxList.elements.remove(at: Int(index))
Expand Down
8 changes: 6 additions & 2 deletions slox/Parser.swift
Expand Up @@ -609,9 +609,13 @@ struct Parser {
return .literal(.nil)
}

if currentTokenMatchesAny(types: [.number]) {
if currentTokenMatchesAny(types: [.double]) {
let number = Double(previousToken.lexeme)!
return .literal(.number(number))
return .literal(.double(number))
}
if currentTokenMatchesAny(types: [.int]) {
let number = Int(previousToken.lexeme)!
return .literal(.int(number))
}

if currentTokenMatchesAny(types: [.string]) {
Expand Down
7 changes: 5 additions & 2 deletions slox/RuntimeError.swift
Expand Up @@ -18,13 +18,14 @@ enum RuntimeError: CustomStringConvertible, Equatable, LocalizedError {
case notACallableObject
case notAnInstance
case notAList
case notANumber
case onlyInstancesHaveProperties
case undefinedProperty(String)
case wrongArity(Int, Int)
case notALambda
case couldNotFindAncestorEnvironmentAtDepth(Int)
case superclassMustBeAClass
case indexMustBeANumber
case indexMustBeAnInteger

var description: String {
switch self {
Expand All @@ -48,6 +49,8 @@ enum RuntimeError: CustomStringConvertible, Equatable, LocalizedError {
return "Error: expected an instance"
case .notAList:
return "Error: expected a list"
case .notANumber:
return "Error: expected a number"
case .onlyInstancesHaveProperties:
return "Error: can only get/set properties of instances"
case .undefinedProperty(let name):
Expand All @@ -60,7 +63,7 @@ enum RuntimeError: CustomStringConvertible, Equatable, LocalizedError {
return "Error: could not find ancestor environment at depth \(depth)."
case .superclassMustBeAClass:
return "Error: superclass must be a class"
case .indexMustBeANumber:
case .indexMustBeAnInteger:
return "Error: index must be a number"
}
}
Expand Down
4 changes: 3 additions & 1 deletion slox/Scanner.swift
Expand Up @@ -162,8 +162,10 @@ struct Scanner {
advanceCursor()
}

var tokenType: TokenType = .int
if nextIndex < source.endIndex,
source[nextIndex] == "." {
tokenType = .double
advanceCursor()

while nextIndex < source.endIndex,
Expand All @@ -172,7 +174,7 @@ struct Scanner {
}
}

addToken(type: .number)
addToken(type: tokenType)
}

mutating private func handleIdentifier() {
Expand Down
3 changes: 2 additions & 1 deletion slox/TokenType.swift
Expand Up @@ -34,7 +34,8 @@ enum TokenType: Equatable {
// Literals
case identifier
case string
case number
case double
case int

// Keywords
case and
Expand Down