2015-10-02 23:36:05 +03:00
|
|
|
/// A patch to some part of a `Syntax` tree.
|
2015-10-15 15:54:33 +03:00
|
|
|
public enum Patch<A>: CustomDebugStringConvertible {
|
2015-10-14 19:17:39 +03:00
|
|
|
case Replace(A, A)
|
|
|
|
case Insert(A)
|
|
|
|
case Delete(A)
|
2015-10-02 23:53:43 +03:00
|
|
|
|
2015-10-14 19:17:39 +03:00
|
|
|
public var state: (before: A?, after: A?) {
|
2015-10-02 23:38:02 +03:00
|
|
|
switch self {
|
|
|
|
case let .Replace(a, b):
|
|
|
|
return (a, b)
|
2015-10-06 23:31:44 +03:00
|
|
|
case let .Insert(b):
|
|
|
|
return (nil, b)
|
|
|
|
case let .Delete(a):
|
|
|
|
return (a, nil)
|
2015-10-02 23:38:02 +03:00
|
|
|
}
|
|
|
|
}
|
2015-10-06 23:45:36 +03:00
|
|
|
|
2015-10-07 00:07:16 +03:00
|
|
|
|
2015-10-07 15:41:54 +03:00
|
|
|
public var inverse: Patch {
|
|
|
|
switch self {
|
|
|
|
case let .Replace(a, b):
|
|
|
|
return .Replace(b, a)
|
|
|
|
case let .Insert(b):
|
|
|
|
return .Delete(b)
|
|
|
|
case let .Delete(a):
|
|
|
|
return .Insert(a)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-10-06 23:45:36 +03:00
|
|
|
// MARK: CustomDebugStringConvertible
|
|
|
|
|
|
|
|
public var debugDescription: String {
|
|
|
|
switch self {
|
|
|
|
case let .Replace(a, b):
|
|
|
|
return ".Replace(\(String(reflecting: a)), \(String(reflecting: b)))"
|
|
|
|
case let .Insert(b):
|
|
|
|
return ".Insert(\(String(reflecting: b)))"
|
|
|
|
case let .Delete(a):
|
|
|
|
return ".Delete(\(String(reflecting: a)))"
|
2015-10-15 22:08:06 +03:00
|
|
|
}
|
2015-10-07 16:54:28 +03:00
|
|
|
}
|
2015-10-02 23:34:00 +03:00
|
|
|
}
|
2015-10-02 23:49:04 +03:00
|
|
|
|
|
|
|
|
2015-10-17 00:31:58 +03:00
|
|
|
// MARK: - Functor
|
|
|
|
|
|
|
|
extension Patch {
|
2015-10-28 19:14:15 +03:00
|
|
|
public func map<B>(@noescape transform: A throws -> B) rethrows -> Patch<B> {
|
2015-10-17 00:31:58 +03:00
|
|
|
switch self {
|
|
|
|
case let .Replace(a, b):
|
2015-10-28 19:14:15 +03:00
|
|
|
return try .Replace(transform(a), transform(b))
|
2015-10-17 00:31:58 +03:00
|
|
|
case let .Delete(a):
|
2015-10-28 19:14:15 +03:00
|
|
|
return try .Delete(transform(a))
|
2015-10-17 00:31:58 +03:00
|
|
|
case let .Insert(b):
|
2015-10-28 19:14:15 +03:00
|
|
|
return try .Insert(transform(b))
|
2015-10-17 00:31:58 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-10-02 23:49:04 +03:00
|
|
|
// MARK: - Equality
|
|
|
|
|
|
|
|
extension Patch {
|
|
|
|
public static func equals(param: (A, A) -> Bool)(_ left: Patch, _ right: Patch) -> Bool {
|
2015-10-14 19:17:39 +03:00
|
|
|
return Optional.equals(param)(left.state.before, right.state.before)
|
|
|
|
&& Optional.equals(param)(left.state.after, right.state.after)
|
2015-10-02 23:49:04 +03:00
|
|
|
}
|
|
|
|
}
|
2015-10-02 23:50:59 +03:00
|
|
|
|
|
|
|
|
2015-10-15 22:23:36 +03:00
|
|
|
// MARK: - Cost calculations
|
|
|
|
|
|
|
|
extension Patch {
|
|
|
|
/// Returns a function which computes the size of a `patch` as the sum of the sizes of its terms, as computed by `size`.
|
2015-10-28 19:15:18 +03:00
|
|
|
public static func sum(@noescape size: A throws -> Int)(_ patch: Patch) rethrows -> Int {
|
|
|
|
return try (patch.state.before.map(size) ?? 0) + (patch.state.after.map(size) ?? 0)
|
2015-10-15 22:23:36 +03:00
|
|
|
}
|
2015-10-15 22:23:51 +03:00
|
|
|
|
|
|
|
/// Returns a function which computes the size of a `patch` as the absolute difference of the sizes of its terms, as computed by `size`.
|
2015-10-28 19:15:36 +03:00
|
|
|
public static func difference(@noescape size: A throws -> Int)(_ patch: Patch) rethrows -> Int {
|
|
|
|
return try abs((patch.state.before.map(size) ?? 0) - (patch.state.after.map(size) ?? 0))
|
2015-10-15 22:23:51 +03:00
|
|
|
}
|
2015-10-15 22:23:36 +03:00
|
|
|
}
|
|
|
|
|
2015-10-15 22:24:58 +03:00
|
|
|
extension Patch where A: TermType {
|
|
|
|
/// Computes the size of a `patch` as the sum of the sizes of its terms.
|
|
|
|
public static func sum(patch: Patch) -> Int {
|
|
|
|
return sum { $0.size } (patch)
|
|
|
|
}
|
2015-10-15 22:25:04 +03:00
|
|
|
|
|
|
|
/// Computes the size of a `patch` as the absolute difference of the sizes of its terms.
|
|
|
|
public static func difference(patch: Patch) -> Int {
|
|
|
|
return difference { $0.size } (patch)
|
|
|
|
}
|
2015-10-15 22:24:58 +03:00
|
|
|
}
|
|
|
|
|
2015-10-15 22:23:36 +03:00
|
|
|
|
2015-10-09 15:41:08 +03:00
|
|
|
// MARK: - JSON
|
2015-10-08 14:28:03 +03:00
|
|
|
|
|
|
|
extension Patch {
|
|
|
|
public func JSON(ifLeaf: A -> Doubt.JSON) -> Doubt.JSON {
|
|
|
|
switch self {
|
|
|
|
case let .Replace(a, b):
|
2015-10-09 15:43:57 +03:00
|
|
|
return [
|
|
|
|
"replace": [
|
2015-10-14 19:17:39 +03:00
|
|
|
"before": ifLeaf(a),
|
|
|
|
"after": ifLeaf(b),
|
2015-10-09 15:43:57 +03:00
|
|
|
]
|
|
|
|
]
|
2015-10-08 14:28:03 +03:00
|
|
|
case let .Insert(b):
|
2015-10-09 15:43:57 +03:00
|
|
|
return [
|
2015-10-14 19:17:39 +03:00
|
|
|
"insert": ifLeaf(b),
|
2015-10-09 15:43:57 +03:00
|
|
|
]
|
2015-10-08 14:28:03 +03:00
|
|
|
case let .Delete(a):
|
2015-10-09 15:43:57 +03:00
|
|
|
return [
|
2015-10-14 19:17:39 +03:00
|
|
|
"delete": ifLeaf(a)
|
2015-10-09 15:43:57 +03:00
|
|
|
]
|
2015-10-08 14:28:03 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-10-09 17:06:06 +03:00
|
|
|
extension Patch where A: CustomJSONConvertible {
|
|
|
|
public var JSON: Doubt.JSON {
|
2015-10-14 19:17:39 +03:00
|
|
|
return JSON { $0.JSON }
|
2015-10-09 17:06:06 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-10-16 16:21:41 +03:00
|
|
|
// MARK: - PatchType
|
2015-10-15 01:48:15 +03:00
|
|
|
|
2015-10-22 17:37:00 +03:00
|
|
|
/// A hack to enable constrained extensions on `Free<Leaf, Annotation, Patch<Term: CofreeType where Term.Leaf == Leaf, Term.Annotation == Annotation>`.
|
2015-10-16 16:21:41 +03:00
|
|
|
public protocol PatchType {
|
2015-10-14 19:02:57 +03:00
|
|
|
typealias Element
|
2015-10-07 04:24:01 +03:00
|
|
|
|
2015-10-16 16:22:41 +03:00
|
|
|
var state: (before: Element?, after: Element?) { get }
|
|
|
|
|
2015-10-16 16:23:51 +03:00
|
|
|
var inverse: Self { get }
|
|
|
|
|
2015-10-16 16:28:25 +03:00
|
|
|
init(replacing before: Element, with after: Element)
|
2015-10-16 16:28:37 +03:00
|
|
|
init(deleting before: Element)
|
2015-10-16 16:28:45 +03:00
|
|
|
init(inserting after: Element)
|
2015-10-07 04:24:01 +03:00
|
|
|
}
|
2015-10-07 04:24:43 +03:00
|
|
|
|
2015-10-16 16:21:41 +03:00
|
|
|
extension Patch: PatchType {
|
2015-10-16 16:28:25 +03:00
|
|
|
public init(replacing before: A, with after: A) {
|
|
|
|
self = .Replace(before, after)
|
|
|
|
}
|
2015-10-16 16:28:37 +03:00
|
|
|
|
|
|
|
public init(deleting before: A) {
|
|
|
|
self = .Delete(before)
|
|
|
|
}
|
2015-10-16 16:28:45 +03:00
|
|
|
|
|
|
|
public init(inserting after: A) {
|
|
|
|
self = .Insert(after)
|
|
|
|
}
|
2015-10-07 04:24:43 +03:00
|
|
|
}
|
2015-10-16 18:09:30 +03:00
|
|
|
|
|
|
|
|
|
|
|
extension PatchType where Element: CofreeType, Element.Annotation == Range<String.Index> {
|
|
|
|
public func JSON(a a: String, b: String) -> Doubt.JSON {
|
|
|
|
return [
|
|
|
|
"before": state.before.map { $0.JSON(a) } ?? nil,
|
|
|
|
"after": state.after.map { $0.JSON(b) } ?? nil,
|
|
|
|
]
|
|
|
|
}
|
|
|
|
}
|