Skip to content

Instantly share code, notes, and snippets.

@lukesutton
Last active May 31, 2017 04:04
Show Gist options
  • Save lukesutton/bed26bca9687211221e53a20b639b541 to your computer and use it in GitHub Desktop.
Save lukesutton/bed26bca9687211221e53a20b639b541 to your computer and use it in GitHub Desktop.

This is an experiment with constructing and composing functions for querying JSON values. A full write-up is available on my blog.

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