diff --git a/prototype/Doubt.xcodeproj/project.pbxproj b/prototype/Doubt.xcodeproj/project.pbxproj index 78f928695..a114e996b 100644 --- a/prototype/Doubt.xcodeproj/project.pbxproj +++ b/prototype/Doubt.xcodeproj/project.pbxproj @@ -31,7 +31,6 @@ D4A71DC71BB4AC9E0051416D /* VertexTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4A71DC61BB4AC9E0051416D /* VertexTests.swift */; settings = {ASSET_TAGS = (); }; }; D4AAE50E1B5AE22E004E581F /* Doubt.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D4AAE4FD1B5AE22E004E581F /* Doubt.framework */; }; D4AAE5401B5AE2D0004E581F /* Array.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4AAE5351B5AE2D0004E581F /* Array.swift */; }; - D4AAE5411B5AE2D0004E581F /* Diff.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4AAE5361B5AE2D0004E581F /* Diff.swift */; }; D4AAE5421B5AE2D0004E581F /* Doc.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4AAE5371B5AE2D0004E581F /* Doc.swift */; }; D4AAE5451B5AE2D0004E581F /* Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4AAE53A1B5AE2D0004E581F /* Operators.swift */; }; D4AAE5461B5AE2D0004E581F /* Parse.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4AAE53B1B5AE2D0004E581F /* Parse.swift */; }; @@ -116,7 +115,6 @@ D4AAE50D1B5AE22E004E581F /* DoubtTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DoubtTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; D4AAE5141B5AE22E004E581F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; D4AAE5351B5AE2D0004E581F /* Array.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Array.swift; sourceTree = ""; }; - D4AAE5361B5AE2D0004E581F /* Diff.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Diff.swift; sourceTree = ""; }; D4AAE5371B5AE2D0004E581F /* Doc.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Doc.swift; sourceTree = ""; }; D4AAE53A1B5AE2D0004E581F /* Operators.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Operators.swift; sourceTree = ""; }; D4AAE53B1B5AE2D0004E581F /* Parse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Parse.swift; sourceTree = ""; }; @@ -197,7 +195,6 @@ isa = PBXGroup; children = ( D4AAE5351B5AE2D0004E581F /* Array.swift */, - D4AAE5361B5AE2D0004E581F /* Diff.swift */, D4AAE5371B5AE2D0004E581F /* Doc.swift */, D4AAE53A1B5AE2D0004E581F /* Operators.swift */, D4AAE53B1B5AE2D0004E581F /* Parse.swift */, @@ -398,7 +395,6 @@ D4AAE5471B5AE2D0004E581F /* Prelude.swift in Sources */, D4413FEF1BB06D4C00E3C3C1 /* Dictionary.swift in Sources */, D4AAE5481B5AE2D0004E581F /* String.swift in Sources */, - D4AAE5411B5AE2D0004E581F /* Diff.swift in Sources */, D4D7F3171BBB22E500AAB0C0 /* Hash.swift in Sources */, D45A36C91BBC667D00BE3DDE /* Category.swift in Sources */, D4AAE5401B5AE2D0004E581F /* Array.swift in Sources */, diff --git a/prototype/Doubt/Diff.swift b/prototype/Doubt/Diff.swift deleted file mode 100644 index c319d5899..000000000 --- a/prototype/Doubt/Diff.swift +++ /dev/null @@ -1,170 +0,0 @@ -/// A domain-specific language for diffing specific domain languages. -public enum Diff: Comparable, CustomDebugStringConvertible, CustomDocConvertible, AlgebraicHashable { - /// Replace a term with another term. - case Patch(Term?, Term?) - - /// Copy a syntax node, recursively diffing its branches. - /// - /// This represents a node in the syntax which is unchanged, but whose child nodes (if any) may have been changed; thus, its children are themselves represented by `Syntax` instantiated to `Diff`, rather than `Term`. - indirect case Copy(Syntax) - - /// Insert, remove, and patch terms by some assigned identity. - indirect case ByKey([String:Term], [String:Term]) - - /// Insert, remove, and patch terms by index. This computes the SES (or some approximation thereof). - indirect case ByIndex([Term], [Term]) - - public static func Insert(term: Term) -> Diff { - return .Patch(nil, term) - } - - public static func Delete(term: Term) -> Diff { - return .Patch(term, nil) - } - - public init(_ term: Term) { - switch term { - case let .Roll(s): - self = .Copy(s.map(Diff.init)) - } - } - - public var doc: Doc { - switch self { - case let .Patch(a, b): - return (a.map { Doc($0).bracket("{-", "-}") } ?? Doc.Empty) - <> (b.map { Doc($0).bracket("{+", "+}") } ?? Doc.Empty) - case let .Copy(a): - return a.doc - case let .ByKey(a, b): - return a.keys.sort().map { Doc($0) <> Doc(":") <+> Doc(a[$0]!) }.joinWithSeparator(",").bracket("{-", "-}") - <> b.keys.sort().map { Doc($0) <> Doc(":") <+> Doc(b[$0]!) }.joinWithSeparator(",").bracket("{+", "+}") - case let .ByIndex(a, b): - return a.map(Doc.init).joinWithSeparator(",").bracket("{-", "-}") - <> b.map(Doc.init).joinWithSeparator(",").bracket("{+", "+}") - } - } - - public var debugDescription: String { - switch self { - case let .Patch(a, b): - return ".Patch(\(String(reflecting: a)), \(String(reflecting: b)))" - case let .Copy(a): - return ".Copy(\(String(reflecting: a)))" - case let .ByKey(a, b): - return ".ByKey(\(String(reflecting: a)), \(String(reflecting: b)))" - case let .ByIndex(a, b): - return ".ByIndex(\(String(reflecting: a)), \(String(reflecting: b)))" - } - } - - public var hash: Hash { - switch self { - case let .Patch(a, b): - return Hash("Patch", a?.hash ?? Hash.Empty, b?.hash ?? Hash.Empty) - case let .Copy(syntax): - return Hash("Copy", syntax.hash) - case let .ByKey(a, b): - return Hash("ByKey", .Unordered(a.map { Hash($0, $1.hash) }), .Unordered(b.map { Hash($0, $1.hash) })) - case let .ByIndex(a, b): - return Hash("ByIndex", .Ordered(a.map { $0.hash }), .Ordered(b.map { $0.hash })) - } - } - - /// The magnitude, or cost, of a diff. This is, roughly speaking, a count of all the patches involved. - /// - /// This is used to compute an optimal path through the edit graph. - public var magnitude: Int { - func magnitude(syntax: Syntax) -> Int { - return syntax.map { $0.magnitude }.reduce(0, combine: +) - } - switch self { - case .Patch: - return 1 - case let .Copy(s): - return magnitude(s) - case let .ByKey(a, b): - let deleted = Set(a.keys).subtract(b.keys) - let inserted = Set(b.keys).subtract(a.keys) - let diffed = Set(a.keys).intersect(b.keys).map { - Diff(a[$0]!, b[$0]!).magnitude - } - return deleted.count + inserted.count + diffed.reduce(0, combine: +) - case let .ByIndex(a, b): - // fixme: SES 😞 - return 0 - } - } - - public init(_ a: Term, _ b: Term) { - if a == b { - self = Diff(b) - return - } - switch (a.syntax, b.syntax) { - case let (.Leaf(v1), .Leaf(v2)) where v1 == v2: - self = .Copy(.Leaf(v2)) - - case let (.Indexed(v1), .Indexed(v2)): - self = .Copy(.Indexed(Diff.diff(v1, v2))) - - default: - self = .Patch(a, b) - } - } - - public static func diff, C2.Index : RandomAccessIndexType, C2.Generator.Element == Term>(a: C1, _ b: C2) -> [Diff] { - func magnitude(diffs: Stream<(Diff, Int)>) -> Int { -// return diffs.first?.magnitude ?? 0 - return diffs.map { $1 }.reduce(0, combine: +) - } - - func min(a: A, _ rest: A..., _ isLessThan: (A, A) -> Bool) -> A { - return rest.reduce(a, combine: { - isLessThan($0, $1) ? $0 : $1 - }) - } - - func diff(a: Stream>, _ b: Stream>) -> Stream<(Diff, Int)> { - switch (a, b) { - case (.Nil, .Nil): - return .Nil - case (.Nil, .Cons): - return b.map { (Diff.Insert($0), 1) } - case (.Cons, .Nil): - return a.map { (Diff.Delete($0), 1) } - case let (.Cons(x, xs), .Cons(y, ys)): - let copy = Diff(x, y) - let here = Stream.Cons((copy, copy.magnitude), Memo { diff(xs.value, ys.value) }) - let insert = Stream.Cons((Diff.Insert(y), 1), Memo { diff(a, ys.value) }) - let delete = Stream.Cons((Diff.Delete(x), 1), Memo { diff(xs.value, b) }) - return min(here, insert, delete) { - magnitude($0) < magnitude($1) - } - } - } - - return Array(diff(Stream(sequence: a), Stream(sequence: b)).map { $0.0 }) - } -} - - -public func == (left: Diff, right: Diff) -> Bool { - switch (left, right) { - case let (.Patch(a1, b1), .Patch(a2, b2)): - return a1 == a2 && b1 == b2 - case let (.Copy(a), .Copy(b)): - return a == b - case let (.ByKey(a1, b1), .ByKey(a2, b2)): - return a1 == a2 && b1 == b2 - case let (.ByIndex(a1, b1), .ByIndex(a2, b2)): - return a1 == a2 && b1 == b2 - default: - return false - } -} - - -public func < (left: Diff, right: Diff) -> Bool { - return left.magnitude < right.magnitude -}