A solution to Brent's Bike Shed 3 problem, albeit, a little out-of-spec.
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
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