Skip to content

Instantly share code, notes, and snippets.

@owensd
Created October 2, 2015 21:57
Show Gist options
  • Save owensd/33d85872c15c2b496515 to your computer and use it in GitHub Desktop.
Save owensd/33d85872c15c2b496515 to your computer and use it in GitHub Desktop.
A solution to Brent's Bike Shed 3 problem, albeit, a little out-of-spec.
import Foundation
enum LangCoercionError: ErrorType {
case InvalidInteger
case InvalidString
case InvalidAddition
case NotOfTypeTable
}
enum LangValueType {
case Integer
case String
case Table
}
enum LangValue {
case IntegerValue(Int)
case StringValue(String)
indirect case TableValue([String:LangValue])
var type: LangValueType {
switch self {
case .IntegerValue(_): return .Integer
case .StringValue(_): return .String
case .TableValue(_): return .Table
}
}
}
protocol IntegerConvertible {
func integerValue() throws -> Int
}
protocol StringConvertible {
func stringValue() throws -> String
}
protocol Addable {
typealias AddableType
func add(other: AddableType) throws -> AddableType
}
protocol Storeable {
typealias ValueType
typealias KeyType
mutating func set(object object: ValueType, forKey key: KeyType) throws
mutating func remove(forKey key: KeyType) throws
func object(forKey key: KeyType) throws -> ValueType?
func keys() throws -> [KeyType]
}
extension LangValue : IntegerConvertible {
func integerValue() throws -> Int {
switch self {
case let IntegerValue(value): return value
case let StringValue(value): return (value as NSString).integerValue
default: throw LangCoercionError.InvalidInteger
}
}
}
extension LangValue : StringConvertible {
func stringValue() throws -> String {
switch self {
case let IntegerValue(value): return NSString(format: "%d", value) as String
case let StringValue(value): return value
default: throw LangCoercionError.InvalidString
}
}
}
extension LangValue : Addable {
func add(other: LangValue) throws -> LangValue {
switch (self, other) {
case let (.IntegerValue(lvalue), .IntegerValue(rvalue)):
return .IntegerValue(lvalue + rvalue)
case (.StringValue(_), .IntegerValue(_)): fallthrough
case (.IntegerValue(_), .StringValue(_)): fallthrough
case (.StringValue(_), .StringValue(_)):
return try LangValue.StringValue(self.stringValue() + other.stringValue())
default: throw LangCoercionError.InvalidAddition
}
}
}
extension LangValue : Storeable {
mutating func set(object object: LangValue, forKey key: String) throws {
switch self {
case let .TableValue(table):
var copy = table
copy[key] = object
self = LangValue.TableValue(copy)
default: LangCoercionError.NotOfTypeTable
}
}
mutating func remove(forKey key: String) throws {
switch self {
case let .TableValue(table):
var copy = table
copy.removeValueForKey(key)
self = LangValue.TableValue(copy)
default: throw LangCoercionError.NotOfTypeTable
}
}
func object(forKey key: String) throws -> LangValue? {
switch self {
case var .TableValue(table):
return table[key]
default: throw LangCoercionError.NotOfTypeTable
}
}
func keys() throws -> [String] {
switch self {
case let .TableValue(table):
return Array(table.keys)
default: throw LangCoercionError.NotOfTypeTable
}
}
}
extension LangValue : StringLiteralConvertible {
init(stringLiteral value: StringLiteralType) {
self = LangValue.StringValue(value)
}
init(unicodeScalarLiteral value: StringLiteralType) {
self.init(stringLiteral: value)
}
init(extendedGraphemeClusterLiteral value: StringLiteralType) {
self.init(stringLiteral: value)
}
}
extension LangValue : IntegerLiteralConvertible {
init(integerLiteral value: IntegerLiteralType) {
self = LangValue.IntegerValue(value)
}
}
extension LangValue : DictionaryLiteralConvertible {
init(dictionaryLiteral elements: (String, LangValue)...) {
var items = [String:LangValue]()
for e in elements {
items[e.0] = e.1
}
self = LangValue.TableValue(items)
}
}
extension LangValue : Hashable, Equatable {
// These are possibly the worst hashValue implementations ever...
var hashValue: Int {
switch self {
case let StringValue(value): return value.hashValue &* 3
default:
fatalError("This is not implemented as it's not required... yet")
}
}
}
func ==(lhs: LangValue, rhs: LangValue) -> Bool {
switch (lhs, rhs) {
case let (.IntegerValue(lvalue), .IntegerValue(rvalue)): return lvalue == rvalue
case let (.StringValue(lvalue), .StringValue(rvalue)): return lvalue == rvalue
case let (.TableValue(lvalue), .TableValue(rvalue)): return lvalue == rvalue
default: return false
}
}
// TEST: Adding Values
let s: LangValue = "This is a string"
let n: LangValue = 42
let someValues = [s, n]
func addIntegerValuesInArray(values: [LangValue]) throws -> LangValue {
let result = try values.reduce(0) { $0 + (try $1.integerValue()) }
return LangValue.IntegerValue(result)
}
func addStringValuesInArray(values: [LangValue]) throws -> LangValue {
let result = try values.reduce("") { $0 + (try $1.stringValue()) }
return LangValue.StringValue(result)
}
public func assertThrow(@autoclosure condition: () throws -> Bool, @autoclosure _ message: () -> String = "", file: StaticString = __FILE__, line: UInt = __LINE__)
{
do {
let result = try condition()
assert(result)
}
catch {
assert(false, "The condition() threw.")
}
}
let intResult = try addIntegerValuesInArray(someValues)
assertThrow(try intResult.integerValue() == 42)
let stringResult = try addStringValuesInArray(someValues)
assertThrow(try stringResult.stringValue() == "This is a string42")
let unknownResult = try s.add(n)
assert(unknownResult.type == .String)
// TEST: Tables And Sets
var t: LangValue = [:]
try t.set(object: 10, forKey: "SomeInt")
let someString: LangValue = "Some string"
try t.set(object: someString, forKey: "SomeString")
var subtable: LangValue = [:]
try subtable.set(object: 50, forKey: "SubtableInt")
let anotherString: LangValue = "Another string"
try subtable.set(object: anotherString, forKey: "SubtableString")
try t.set(object: subtable, forKey: "Subtable")
func recursiveSetOfStringsInTable(table: LangValue) throws -> Set<LangValue> {
var set = Set<LangValue>()
for key in try table.keys() {
if let item = try table.object(forKey: key) {
if item.type == .String {
set.insert(item)
}
else if item.type == .Table {
let recset = try recursiveSetOfStringsInTable(item)
recset.forEach { set.insert($0) }
}
}
}
return set
}
let setOfStrings = try recursiveSetOfStringsInTable(t)
assert(setOfStrings == Set([someString, anotherString]))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment