1
1
mirror of https://github.com/github/semantic.git synced 2024-11-29 02:44:36 +03:00
semantic/prototype/Doubt/JSONParser.swift

122 lines
3.3 KiB
Swift
Raw Normal View History

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 Either
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-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) })*)
// Quoted strings parser
2015-10-21 18:21:37 +03:00
// TODO: Improve string parsing
2015-10-16 22:41:35 +03:00
let stringBody: StringParser = { $0.map({ String($0) }).joinWithSeparator("") } <^>
2015-10-21 18:21:37 +03:00
String.lift(noneOf("\"")*)
2015-10-20 18:11:06 +03:00
let quoted = %"\"" *> stringBody <* %"\"" <* whitespace
2015-10-16 22:41:35 +03:00
typealias MembersParser = Parser<String, [(String, CofreeJSON)]>.Function;
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 {
let pairs: Parser<String, (String, CofreeJSON)>.Function = (curry(pair) <^>
2015-10-21 18:25:33 +03:00
quoted
<* whitespace
<* %":"
<* whitespace
<*> json)
2015-10-16 22:41:35 +03:00
let separatedPairs: MembersParser = (%"," *> whitespace *> pairs <* whitespace)*
let oneOrMore: MembersParser = curry { [$0] + $1 } <^>
2015-10-21 18:17:28 +03:00
pairs
<* whitespace
<*> separatedPairs
2015-10-16 22:41:35 +03:00
return oneOrMore <|> pure([])
}
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 {
let value: Parser<String, CofreeJSON>.Function = whitespace *> json <* whitespace
2015-10-21 18:25:37 +03:00
return sepBy(value, %",")
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)
<* %"]"
<* whitespace --> {
Cofree($1, .Indexed($2))
} <?> "array"
let object: JSONParser = %"{"
*> whitespace
*> members(json)
<* whitespace
<* %"}"
<* whitespace
2015-10-16 22:41:35 +03:00
--> { (_, range, values) in
2015-10-26 20:31:25 +03:00
let vs: [CofreeJSON] = values.map({ (string, value) in
let valueRange = value.extract
let newRange = Range(start: range.startIndex, end: valueRange.endIndex)
return Cofree(newRange, .Keyed([string: value]))
})
return Cofree(range, .Fixed(vs))
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-21 18:11:49 +03:00
return object <|> array <|> string <|> numberParser <|> boolean <|> null
}