This is an experiment with constructing and composing functions for querying JSON values. A full write-up is available on my blog.
Last active
May 31, 2017 04:04
-
-
Save lukesutton/bed26bca9687211221e53a20b639b541 to your computer and use it in GitHub Desktop.
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
enum JSON { | |
case array([JSON]) | |
case object([String: JSON]) | |
case number(Int) | |
case string(String) | |
case bool(Bool) | |
} | |
extension JSON: ExpressibleByIntegerLiteral { | |
init(integerLiteral: Int) { | |
self = .number(integerLiteral) | |
} | |
} | |
extension JSON: ExpressibleByBooleanLiteral { | |
init(booleanLiteral: Bool) { | |
self = .bool(booleanLiteral) | |
} | |
} | |
extension JSON: ExpressibleByStringLiteral { | |
typealias StringLiteralType = String | |
typealias ExtendedGraphemeClusterLiteralType = String | |
typealias UnicodeScalarLiteralType = String | |
init(stringLiteral: String) { | |
self = .string(stringLiteral) | |
} | |
init(extendedGraphemeClusterLiteral: String) { | |
self.init(stringLiteral: extendedGraphemeClusterLiteral) | |
} | |
init(unicodeScalarLiteral: String) { | |
self.init(stringLiteral: unicodeScalarLiteral) | |
} | |
} | |
extension JSON: ExpressibleByDictionaryLiteral { | |
typealias Key = String | |
typealias Value = JSON | |
init(dictionaryLiteral: (Key, Value)...) { | |
var dict: [Key: Value] = [:] | |
for (key, value) in dictionaryLiteral { | |
dict[key] = value | |
} | |
self = .object(dict) | |
} | |
} | |
extension JSON: ExpressibleByArrayLiteral { | |
init(arrayLiteral: Value...) { | |
self = .array(arrayLiteral) | |
} | |
} | |
func array(_ input: JSON) -> JSON? { | |
switch input { | |
case .array: return input | |
default: return nil | |
} | |
} | |
func object(_ input: JSON) -> JSON? { | |
switch input { | |
case .object: return input | |
default: return nil | |
} | |
} | |
func key(_ key: String) -> (JSON) -> JSON? { | |
return { input in | |
switch object(input) { | |
case let .some(.object(dict)): | |
return dict[key] | |
default: | |
return nil | |
} | |
} | |
} | |
func index(_ index: Int) -> (JSON) -> JSON? { | |
return { input in | |
switch array(input) { | |
case let .some(.array(array)): | |
if index > array.count - 1 { | |
return nil | |
} | |
else { | |
return array[index] | |
} | |
default: | |
return nil | |
} | |
} | |
} | |
func number(_ input: JSON) -> JSON? { | |
switch input { | |
case .number: return input | |
default: return nil | |
} | |
} | |
func bool(_ input: JSON) -> JSON? { | |
switch input { | |
case .bool: return input | |
default: return nil | |
} | |
} | |
func string(_ input: JSON) -> JSON? { | |
switch input { | |
case .string: return input | |
default: return nil | |
} | |
} | |
typealias JSONQuery = (JSON) -> JSON? | |
infix operator >>: AdditionPrecedence | |
func >> (lhs: @escaping JSONQuery, rhs: @escaping JSONQuery) -> JSONQuery { | |
return { input in | |
guard let result = lhs(input) else { return nil } | |
return rhs(result) | |
} | |
} | |
let json: JSON = ["herp": [1, 2, 4]] | |
let query = object >> key("herp") >> array >> index(2) >> number | |
print(query(json) ?? "booo") | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment