Last active
September 13, 2015 15:37
-
-
Save oisdk/1636179be6be9ac428e6 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
extension CollectionType where Index : BidirectionalIndexType { | |
private func lastIndexOfNot(@noescape isNotElement: Generator.Element throws -> Bool) rethrows -> Index? { | |
for i in indices.reverse() | |
where (try !isNotElement(self[i])) { | |
return i | |
} | |
return nil | |
} | |
} | |
extension CollectionType { | |
private func indexOfNot(@noescape isNotElement: Generator.Element throws -> Bool) rethrows -> Index? { | |
for i in indices | |
where (try !isNotElement(self[i])) { | |
return i | |
} | |
return nil | |
} | |
} | |
extension CollectionType where Generator.Element : Equatable { | |
private func indexOfNot(e: Generator.Element) -> Index? { | |
return indexOfNot { o in o == e } | |
} | |
} | |
extension CollectionType where Index : BidirectionalIndexType, Generator.Element : Equatable { | |
private func lastIndexOfNot(e: Generator.Element) -> Index? { | |
return lastIndexOfNot { o in o == e } | |
} | |
} | |
extension String.CharacterView { | |
private func trim(c: Character) -> String.CharacterView { | |
guard let s = indexOfNot(c), e = lastIndexOfNot(c) else { return "".characters } | |
return self[s...e] | |
} | |
private func trim(cs: Set<Character>) -> String.CharacterView { | |
guard let s = indexOfNot(cs.contains), e = lastIndexOfNot(cs.contains) else { return "".characters } | |
return self[s...e] | |
} | |
private func trim(c0: Character, _ c1: Character, _ rest: Character...) -> String.CharacterView { | |
return trim(Set([c0, c1] + rest)) | |
} | |
} | |
public enum Result<T,E> { case Some(T), Error(E) } | |
public enum JSONError : ErrorType { case UnBalancedBrackets, ParseError, Empty } | |
extension String.CharacterView { | |
func bracketSplit(open: Character, _ close: Character) -> Result<(String.CharacterView, String.CharacterView),JSONError> { | |
var count = 1 | |
for i in indices.dropFirst() { | |
if self[i.predecessor()] == "\\" { continue } | |
if self[i] == close { --count } else | |
if self[i] == open { ++count } | |
if count == 0 { | |
return .Some(self[startIndex.successor()..<i], suffixFrom(i.successor())) | |
} | |
} | |
return .Error(.UnBalancedBrackets) | |
} | |
} | |
public enum JSON { case S(String), D(Double), I(Int), B(Bool), A([JSON]), O([String:JSON]), null } | |
extension String { | |
private func decodeAsAtom() -> Result<JSON,JSONError> { | |
switch self { | |
case "null" : return .Some(.null) | |
case "true" : return .Some(.B(true)) | |
case "false": return .Some(.B(false)) | |
case let s: return | |
Int(s).map { i in .Some(.I(i)) } ?? | |
Double(s).map { d in .Some(.D(d)) } ?? | |
.Error(.ParseError) | |
} | |
} | |
} | |
extension String.CharacterView { | |
private func decodeAsArray() -> Result<[JSON],JSONError> { | |
return decodeAsArrayWith([]) | |
} | |
private func decodeAsArrayWith(p: [JSON]) -> Result<[JSON],JSONError> { | |
switch decodeToDelim() { | |
case let (f,b)?: return b.decodeAsArrayWith(p + [f]) | |
case let .Error(e): | |
if case .Empty = e { return .Some(p) } | |
return .Error(e) | |
} | |
} | |
private func decodeAsDict() -> Result<[String:JSON],JSONError> { | |
return decodeAsDictWith([:]) | |
} | |
private func decodeAsDictWith(var p: [String:JSON]) -> Result<[String:JSON],JSONError> { | |
guard let i = indexOf("\"") else { return .Some(p) } | |
guard let (k,b) = suffixFrom(i).bracketSplit("\"", "\"") else { return .Error(.UnBalancedBrackets) } | |
guard let j = b.indexOf(":") else { return .Error(.ParseError) } | |
guard let (v,d) = b.suffixFrom(j.successor()).decodeToDelim() else { return .Error(.ParseError) } | |
p[String(k)] = v | |
return d.decodeAsDictWith(p) | |
} | |
} | |
extension String.CharacterView { | |
private func decodeToDelim() -> Result<(JSON, String.CharacterView),JSONError> { | |
let wSpace: Set<Character> = [" ", ",", "\n"] | |
guard let i = indexOfNot(wSpace.contains) else { return .Error(.Empty) } | |
switch self[i] { | |
case "[": | |
guard let (f,b) = suffixFrom(i).bracketSplit("[", "]") | |
else { return .Error(.UnBalancedBrackets) } | |
switch f.decodeAsArray() { | |
case let a? : return .Some(.A(a), b) | |
case let .Error(e): return .Error(e) | |
} | |
case "{": | |
guard let (f,b) = suffixFrom(i).bracketSplit("{", "}") | |
else { return .Error(.UnBalancedBrackets) } | |
switch f.decodeAsDict() { | |
case let o? : return .Some(.O(o), b) | |
case let .Error(e): return .Error(e) | |
} | |
case "\"": | |
guard let (f,b) = suffixFrom(i).bracketSplit("\"", "\"") | |
else { return .Error(.UnBalancedBrackets) } | |
return .Some(.S(String(f)), b) | |
default: | |
let suff = suffixFrom(i) | |
let j = suff.indexOf(",") ?? suff.endIndex | |
guard let a = String(suff.prefixUpTo(j).trim(wSpace)).decodeAsAtom() | |
else { return .Error(.ParseError) } | |
return .Some(a, suff.suffixFrom(j)) | |
} | |
} | |
} | |
extension JSON : CustomStringConvertible { | |
private func desWithInd(i: String) -> String { | |
switch self { | |
case let .A(a): | |
let indented = a.map { e in i + " " + e.desWithInd(i + " ") } | |
let joined = indented.joinWithSeparator(",\n") | |
return "[\n" + joined + "\n" + i + "]" | |
case let .O(o): | |
let indented = o.map { (k,v) in i + " \"" + k + "\": " + v.desWithInd(i + " ")} | |
let joined = indented.joinWithSeparator(",\n") | |
return "{\n" + joined + "\n" + i + "}" | |
case let .S(s): return "\"" + s + "\"" | |
case let .B(b): return b.description | |
case let .D(d): return d.description | |
case let .I(i): return i.description | |
case null: return "null" | |
} | |
} | |
public var description: String { | |
return desWithInd("") | |
} | |
} | |
extension String { | |
public func asJSONThrow() throws -> JSON { | |
switch characters.decodeToDelim() { | |
case let j?: return j.0 | |
case let .Error(e): throw e | |
} | |
} | |
public func asJSONResult() -> Result<JSON,JSONError> { | |
switch characters.decodeToDelim() { | |
case let j?: return .Some(j.0) | |
case let .Error(e): return .Error(e) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment