2015-10-15 17:45:32 +03:00
|
|
|
|
/// An interpreter of `Algorithm`s.
|
2015-10-15 18:00:27 +03:00
|
|
|
|
public struct Interpreter<Term: TermType> {
|
2015-10-15 18:01:35 +03:00
|
|
|
|
public typealias Diff = Free<Term.LeafType, Patch<Term>>
|
|
|
|
|
|
2015-10-15 18:27:44 +03:00
|
|
|
|
private let equals: (Term, Term) -> Bool
|
2015-10-15 19:15:27 +03:00
|
|
|
|
private let comparable: (Term, Term) -> Bool
|
2015-10-15 18:50:43 +03:00
|
|
|
|
private let cost: Diff -> Int
|
2015-10-15 18:00:49 +03:00
|
|
|
|
|
2015-10-15 19:15:30 +03:00
|
|
|
|
/// Diff `a` against `b`, if comparable.
|
2015-10-15 18:26:35 +03:00
|
|
|
|
private func recur(a: Term, _ b: Term) -> Diff? {
|
2015-10-15 18:04:37 +03:00
|
|
|
|
if equals(a, b) { return Diff(b) }
|
2015-10-15 18:26:35 +03:00
|
|
|
|
|
2015-10-15 18:07:35 +03:00
|
|
|
|
let algorithm: Algorithm<Term, Diff>
|
|
|
|
|
switch (a.unwrap, b.unwrap) {
|
|
|
|
|
case let (.Keyed(a), .Keyed(b)):
|
|
|
|
|
algorithm = .Roll(.ByKey(a, b, Syntax.Keyed >>> Diff.Roll >>> Algorithm.Pure))
|
|
|
|
|
case let (.Indexed(a), .Indexed(b)):
|
|
|
|
|
algorithm = .Roll(.ByIndex(a, b, Syntax.Indexed >>> Diff.Roll >>> Algorithm.Pure))
|
|
|
|
|
default:
|
|
|
|
|
algorithm = .Roll(.Recursive(a, b, Algorithm.Pure))
|
|
|
|
|
}
|
2015-10-15 18:29:04 +03:00
|
|
|
|
return recur(algorithm)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private func recur(algorithm: Algorithm<Term, Diff>) -> Diff? {
|
2015-10-15 18:29:49 +03:00
|
|
|
|
switch algorithm {
|
|
|
|
|
case let .Pure(diff):
|
|
|
|
|
return diff
|
2015-10-15 18:33:13 +03:00
|
|
|
|
|
|
|
|
|
case let .Roll(.Recursive(a, b, f)):
|
2015-10-15 18:48:49 +03:00
|
|
|
|
// Recur structurally into both terms, patching differing sub-terms. This is akin to unification, except that it computes a patched tree instead of a substitution. It’s also a little like a structural zip on pairs of terms.
|
2015-10-15 18:33:13 +03:00
|
|
|
|
switch (a.unwrap, b.unwrap) {
|
|
|
|
|
case let (.Indexed(a), .Indexed(b)) where a.count == b.count:
|
|
|
|
|
return recur(f(.Indexed(zip(a, b).map(run))))
|
|
|
|
|
|
2015-10-15 18:45:40 +03:00
|
|
|
|
case let (.Keyed(a), .Keyed(b)) where Array(a.keys) == Array(b.keys):
|
|
|
|
|
return recur(f(.Keyed(Dictionary(elements: b.keys.map { ($0, self.run(a[$0]!, b[$0]!)) }))))
|
|
|
|
|
|
2015-10-15 18:33:13 +03:00
|
|
|
|
default:
|
2015-10-15 18:47:27 +03:00
|
|
|
|
// This must not call `recur` directly with `a` and `b`, as that would infinite loop if actually recursive.
|
|
|
|
|
return recur(f(.Replace(a, b)))
|
2015-10-15 18:33:13 +03:00
|
|
|
|
}
|
|
|
|
|
|
2015-10-15 18:50:04 +03:00
|
|
|
|
case let .Roll(.ByKey(a, b, f)):
|
|
|
|
|
// Perform [set reconciliation](https://en.wikipedia.org/wiki/Data_synchronization#Unordered_data) on the keys, followed by recurring into the values of the intersecting keys.
|
|
|
|
|
let deleted = Set(a.keys).subtract(b.keys).map { ($0, Diff.Delete(a[$0]!)) }
|
|
|
|
|
let inserted = Set(b.keys).subtract(a.keys).map { ($0, Diff.Insert(b[$0]!)) }
|
|
|
|
|
let patched = Set(a.keys).intersect(b.keys).map { ($0, run(a[$0]!, b[$0]!)) }
|
|
|
|
|
return recur(f(Dictionary(elements: deleted + inserted + patched)))
|
|
|
|
|
|
2015-10-15 18:51:01 +03:00
|
|
|
|
case let .Roll(.ByIndex(a, b, f)):
|
|
|
|
|
return recur(f(SES(a, b, cost: cost, recur: recur)))
|
2015-10-15 18:29:49 +03:00
|
|
|
|
}
|
2015-10-15 18:26:35 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public func run(a: Term, _ b: Term) -> Diff {
|
2015-10-15 18:29:56 +03:00
|
|
|
|
return recur(a, b) ?? .Replace(a, b)
|
2015-10-15 18:04:37 +03:00
|
|
|
|
}
|
2015-10-15 17:58:57 +03:00
|
|
|
|
}
|
2015-10-15 18:07:35 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import Prelude
|