Created
August 4, 2016 15:04
-
-
Save wircho/c382d297ad07cf6b705246abd20b47f9 to your computer and use it in GitHub Desktop.
Arithmetic operators that throw on integer type overflow and floating point type errors.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* ThrowingOperators.swift | |
*/ | |
import Darwin | |
import CoreGraphics | |
// MARK: Operators | |
infix operator &&+ {associativity left precedence 140} | |
infix operator &&- {associativity left precedence 140} | |
infix operator &&* {associativity left precedence 150} | |
infix operator &&/ {associativity left precedence 150} | |
infix operator &&% {associativity left precedence 150} | |
prefix operator &&? {} | |
// MARK: Error type | |
public struct ArithmeticOperationError: ErrorType { | |
public enum Type { | |
case overflow | |
case nan | |
case infinity | |
} | |
public enum Operation { | |
case addition | |
case subtraction | |
case multiplication | |
case division | |
case remainder | |
case other(String?) | |
} | |
let type:Type | |
let operation:Operation | |
init(_ type:Type, _ operation:Operation) { | |
self.type = type | |
self.operation = operation | |
} | |
} | |
// MARK: Integer operator implementations | |
public func &&+<T:IntegerArithmeticType>(lhs:T,rhs:T) throws -> T { | |
let (result,overflow) = T.addWithOverflow(lhs,rhs) | |
guard !overflow else { | |
throw ArithmeticOperationError(.overflow,.addition) | |
} | |
return result | |
} | |
public func &&-<T:IntegerArithmeticType>(lhs:T,rhs:T) throws -> T { | |
let (result,overflow) = T.subtractWithOverflow(lhs,rhs) | |
guard !overflow else { | |
throw ArithmeticOperationError(.overflow,.subtraction) | |
} | |
return result | |
} | |
public func &&*<T:IntegerArithmeticType>(lhs:T,rhs:T) throws -> T { | |
let (result,overflow) = T.multiplyWithOverflow(lhs,rhs) | |
guard !overflow else { | |
throw ArithmeticOperationError(.overflow,.multiplication) | |
} | |
return result | |
} | |
public func &&/<T:IntegerArithmeticType>(lhs:T,rhs:T) throws -> T { | |
let (result,overflow) = T.divideWithOverflow(lhs,rhs) | |
guard !overflow else { | |
throw ArithmeticOperationError(.overflow,.division) | |
} | |
return result | |
} | |
public func &&%<T:IntegerArithmeticType>(lhs:T,rhs:T) throws -> T { | |
let (result,overflow) = T.remainderWithOverflow(lhs,rhs) | |
guard !overflow else { | |
throw ArithmeticOperationError(.overflow,.remainder) | |
} | |
return result | |
} | |
// MARK: Floating point helpers | |
private func enforce<T:FloatingPointType>(value:T,_ op:ArithmeticOperationError.Operation) throws -> T { | |
guard !value.isInfinite else { | |
throw ArithmeticOperationError(.infinity,op) | |
} | |
guard !value.isNaN else { | |
throw ArithmeticOperationError(.nan,op) | |
} | |
return value | |
} | |
public func throwing<T:FloatingPointType>(value:T,_ debug:String? = nil) throws -> T { | |
return try enforce(value,.other(debug)) | |
} | |
public prefix func &&?<T:FloatingPointType>(value:T) throws -> T { | |
return try throwing(value) | |
} | |
// MARK: Encapsulating Double, Float, and CGFloat | |
public protocol FloatingPointArithmeticType: FloatingPointType { | |
static func +(_:Self,_:Self) -> Self | |
static func -(_:Self,_:Self) -> Self | |
static func *(_:Self,_:Self) -> Self | |
static func /(_:Self,_:Self) -> Self | |
} | |
extension Double: FloatingPointArithmeticType {} | |
extension Float: FloatingPointArithmeticType {} | |
extension CGFloat: FloatingPointArithmeticType {} | |
// MARK: Floating point operator implementations | |
public func &&+<T:FloatingPointArithmeticType>(lhs:T,rhs:T) throws -> T { | |
return try enforce(lhs + rhs, .addition) | |
} | |
public func &&-<T:FloatingPointArithmeticType>(lhs:T,rhs:T) throws -> T { | |
return try enforce(lhs - rhs, .subtraction) | |
} | |
public func &&*<T:FloatingPointArithmeticType>(lhs:T,rhs:T) throws -> T { | |
return try enforce(lhs * rhs, .multiplication) | |
} | |
public func &&/<T:FloatingPointArithmeticType>(lhs:T,rhs:T) throws -> T { | |
return try enforce(lhs / rhs, .division) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment