2015-10-16 22:41:35 +03:00
|
|
|
//
|
|
|
|
// JSONParser.swift
|
|
|
|
// Doubt
|
|
|
|
//
|
|
|
|
// Created by Josh Vera on 10/16/15.
|
|
|
|
// Copyright © 2015 GitHub. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
import Foundation
|
|
|
|
import Madness
|
|
|
|
import Prelude
|
|
|
|
|
2015-10-26 22:06:33 +03:00
|
|
|
public typealias CofreeJSON = Cofree<JSONLeaf, Range<String.CharacterView.Index>>
|
|
|
|
public typealias JSONParser = Parser<String, CofreeJSON>.Function
|
2015-10-16 22:41:35 +03:00
|
|
|
|
|
|
|
extension String: CollectionType {
|
|
|
|
public var count: Index.Distance {
|
|
|
|
return characters.count
|
|
|
|
}
|
2015-10-27 23:11:31 +03:00
|
|
|
|
2015-10-20 16:17:45 +03:00
|
|
|
public static func lift<A>(parser: Parser<String.CharacterView, A>.Function) -> Parser<String, A>.Function {
|
|
|
|
return {
|
|
|
|
parser($0.characters, $1)
|
|
|
|
}
|
|
|
|
}
|
2015-10-16 22:41:35 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
typealias StringParser = Parser<String, String>.Function
|
|
|
|
typealias CharacterParser = Parser<String, [Character]>.Function
|
|
|
|
|
2015-10-20 18:11:06 +03:00
|
|
|
// Inlined for performance reasons
|
|
|
|
let whitespaceChars: [Character] = [" ", "\n", "\t", "\r"]
|
|
|
|
let whitespace: CharacterParser = String.lift(satisfy({ whitespaceChars.contains($0) })*)
|
|
|
|
|
2015-10-26 23:37:50 +03:00
|
|
|
// TODO: Parse unicode escape sequence
|
|
|
|
let escapeChar: StringParser = %"\\\\" <|> %"\\\"" <|> %"\\b" <|> %"\\f" <|> %"\\n" <|> %"\\r" <|> %"\\t"
|
|
|
|
let otherChar: StringParser = { String($0) } <^> satisfy { c in
|
|
|
|
c != "\"" && c != "\\"
|
|
|
|
}
|
|
|
|
|
2015-10-20 18:11:06 +03:00
|
|
|
// Quoted strings parser
|
2015-10-21 18:21:37 +03:00
|
|
|
// TODO: Improve string parsing
|
2015-10-27 23:11:38 +03:00
|
|
|
let stringBody: StringParser = { $0.joinWithSeparator("") } <^> many(escapeChar <|> otherChar)
|
2015-10-26 23:37:34 +03:00
|
|
|
let quoted = %"\"" *> stringBody <* %"\""
|
2015-10-16 22:41:35 +03:00
|
|
|
|
2015-10-27 18:18:52 +03:00
|
|
|
typealias MembersParser = Parser<String, [(String, CofreeJSON)]>.Function;
|
2015-10-16 22:41:35 +03:00
|
|
|
|
2015-10-20 18:11:06 +03:00
|
|
|
// Parses an array of (String, CofreeJSON) object members
|
2015-10-16 22:41:35 +03:00
|
|
|
func members(json: JSONParser) -> MembersParser {
|
2015-10-27 18:18:52 +03:00
|
|
|
let pairs: Parser<String, (String, CofreeJSON)>.Function = (curry(pair) <^>
|
2015-10-27 20:28:35 +03:00
|
|
|
(quoted --> { (_, range, key) -> (String, CofreeJSON) in
|
|
|
|
return (key, Cofree(range, .Leaf(.String(key))))
|
|
|
|
})
|
2015-10-21 18:25:33 +03:00
|
|
|
<* whitespace
|
|
|
|
<* %":"
|
|
|
|
<* whitespace
|
2015-10-27 20:15:56 +03:00
|
|
|
<*> json) --> { (_, range, values) in
|
2015-10-27 20:28:35 +03:00
|
|
|
let key = values.0.0
|
|
|
|
return (key, Cofree(range, .Fixed([values.0.1, values.1])))
|
2015-10-26 22:06:45 +03:00
|
|
|
}
|
2015-10-16 22:41:35 +03:00
|
|
|
|
2015-10-26 23:52:30 +03:00
|
|
|
return sepBy(pairs, whitespace <* %"," <* whitespace)
|
2015-10-16 22:41:35 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
typealias ValuesParser = Parser<String, [CofreeJSON]>.Function;
|
2015-10-20 18:11:06 +03:00
|
|
|
|
|
|
|
// Parses an array of CofreeJSON array values
|
2015-10-16 22:41:35 +03:00
|
|
|
func elements(json: JSONParser) -> ValuesParser {
|
2015-10-26 23:37:34 +03:00
|
|
|
let value: Parser<String, CofreeJSON>.Function = whitespace *> json
|
2015-10-26 23:52:30 +03:00
|
|
|
return sepBy(value, whitespace <* %"," <* whitespace)
|
2015-10-16 22:41:35 +03:00
|
|
|
}
|
|
|
|
|
2015-10-26 22:06:33 +03:00
|
|
|
public let json: JSONParser = fix { json in
|
2015-10-16 22:41:35 +03:00
|
|
|
// TODO: Parse backslashed escape characters
|
|
|
|
|
2015-10-21 18:25:28 +03:00
|
|
|
let string: JSONParser = quoted --> {
|
|
|
|
Cofree($1, .Leaf(.String($2)))
|
|
|
|
} <?> "string"
|
|
|
|
|
|
|
|
let array: JSONParser = %"["
|
|
|
|
<* whitespace
|
|
|
|
*> elements(json)
|
2015-10-26 23:37:34 +03:00
|
|
|
<* whitespace
|
2015-10-21 18:25:28 +03:00
|
|
|
<* %"]"
|
2015-10-26 23:37:34 +03:00
|
|
|
--> {
|
2015-10-21 18:25:28 +03:00
|
|
|
Cofree($1, .Indexed($2))
|
|
|
|
} <?> "array"
|
|
|
|
|
|
|
|
let object: JSONParser = %"{"
|
|
|
|
*> whitespace
|
|
|
|
*> members(json)
|
|
|
|
<* whitespace
|
|
|
|
<* %"}"
|
2015-10-27 18:18:52 +03:00
|
|
|
--> { (_, range, values: [(String, CofreeJSON)]) in
|
|
|
|
Cofree(range, .Keyed(Dictionary(elements: values)))
|
2015-10-21 18:25:28 +03:00
|
|
|
} <?> "object"
|
2015-10-16 22:41:35 +03:00
|
|
|
|
2015-10-21 18:11:49 +03:00
|
|
|
let doubleParser: DoubleParser = number
|
|
|
|
let numberParser: JSONParser = String.lift(doubleParser --> { _, range, value in
|
2015-10-20 16:20:08 +03:00
|
|
|
let num = JSONLeaf.Number(value)
|
|
|
|
return Cofree(range, .Leaf(num))
|
2015-10-21 18:25:28 +03:00
|
|
|
}) <?> "number"
|
2015-10-20 18:11:06 +03:00
|
|
|
|
|
|
|
let null: JSONParser = %"null" --> { (_, range, value) in
|
|
|
|
return Cofree(range, .Leaf(.Null))
|
2015-10-21 18:25:28 +03:00
|
|
|
} <?> "null"
|
2015-10-20 18:11:06 +03:00
|
|
|
|
|
|
|
let boolean: JSONParser = %"false" <|> %"true" --> { (_, range, value) in
|
|
|
|
let boolean = value == "true"
|
|
|
|
return Cofree(range, .Leaf(.Boolean(boolean)))
|
2015-10-21 18:25:28 +03:00
|
|
|
} <?> "boolean"
|
2015-10-16 22:41:35 +03:00
|
|
|
|
2015-10-20 18:11:06 +03:00
|
|
|
// TODO: This should be JSON = dict <|> array and
|
|
|
|
// Value = dict | array | string | number | null | bool
|
2015-10-26 23:37:34 +03:00
|
|
|
return (object <|> array <|> string <|> numberParser <|> boolean <|> null) <* whitespace
|
2015-10-21 18:11:49 +03:00
|
|
|
}
|