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