mirror of
https://github.com/github/semantic.git
synced 2024-11-29 11:02:26 +03:00
Merge pull request #62 from github/update-swift-target
Update Swift target
This commit is contained in:
commit
26f8e63ee3
@ -61,6 +61,16 @@
|
|||||||
ReferencedContainer = "container:Doubt.xcodeproj">
|
ReferencedContainer = "container:Doubt.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</BuildableProductRunnable>
|
</BuildableProductRunnable>
|
||||||
|
<CommandLineArguments>
|
||||||
|
<CommandLineArgument
|
||||||
|
argument = "$(SRCROOT)/doubt-swift/Fixtures/Algorithm.old.swift"
|
||||||
|
isEnabled = "YES">
|
||||||
|
</CommandLineArgument>
|
||||||
|
<CommandLineArgument
|
||||||
|
argument = "$(SRCROOT)/doubt-swift/Fixtures/Algorithm.new.swift"
|
||||||
|
isEnabled = "YES">
|
||||||
|
</CommandLineArgument>
|
||||||
|
</CommandLineArguments>
|
||||||
<AdditionalOptions>
|
<AdditionalOptions>
|
||||||
</AdditionalOptions>
|
</AdditionalOptions>
|
||||||
</LaunchAction>
|
</LaunchAction>
|
||||||
|
146
prototype/doubt-swift/Fixtures/Algorithm.new.swift
Normal file
146
prototype/doubt-swift/Fixtures/Algorithm.new.swift
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
/// An operation of diffing over terms or collections of terms.
|
||||||
|
public enum Algorithm<Recur, A> {
|
||||||
|
/// The type of `Term`s over which `Algorithm`s operate.
|
||||||
|
public typealias Term = Fix<A>
|
||||||
|
|
||||||
|
/// The type of `Diff`s which `Algorithm`s produce.
|
||||||
|
public typealias Diff = Free<A, Patch<A>>
|
||||||
|
|
||||||
|
/// Indicates that diffing should compare the enclosed `Term`s.
|
||||||
|
///
|
||||||
|
/// When run, the enclosed function will be applied to the resulting `Diff`.
|
||||||
|
case Recursive(Term, Term, Diff -> Recur)
|
||||||
|
|
||||||
|
/// Represents a diff to be performed on a collection of terms identified by keys.
|
||||||
|
case ByKey([String:Term], [String:Term], [String:Diff] -> Recur)
|
||||||
|
|
||||||
|
/// Represents a diff to be performed over an array of terms by index.
|
||||||
|
case ByIndex([Term], [Term], [Diff] -> Recur)
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: Functor
|
||||||
|
|
||||||
|
public func map<Other>(transform: Recur -> Other) -> Algorithm<Other, A> {
|
||||||
|
switch self {
|
||||||
|
case let .Recursive(a, b, f):
|
||||||
|
return .Recursive(a, b, f >>> transform)
|
||||||
|
case let .ByKey(a, b, f):
|
||||||
|
return .ByKey(a, b, f >>> transform)
|
||||||
|
case let .ByIndex(a, b, f):
|
||||||
|
return .ByIndex(a, b, f >>> transform)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// The free monad over `Algorithm`, implementing the language of diffing.
|
||||||
|
///
|
||||||
|
/// As with `Free`, this is “free” in the sense of “unconstrained,” i.e. “the monad induced by `Algorithm` without extra assumptions.”
|
||||||
|
///
|
||||||
|
/// Where `Algorithm` models a single diffing strategy, `FreeAlgorithm` models the recursive selection of diffing strategies at each node. Thus, a value in `FreeAlgorithm` models an algorithm for constructing a value in the type `B` from the resulting diffs. By this means, diffing can be adapted not just to the specific grammar, but to specific trees produced by that grammar, and even the values of type `A` encapsulated at each node.
|
||||||
|
public enum FreeAlgorithm<A, B> {
|
||||||
|
/// The type of `Term`s over which `FreeAlgorithm`s operate.
|
||||||
|
public typealias Term = Algorithm<FreeAlgorithm, A>.Term
|
||||||
|
|
||||||
|
/// The type of `Diff`s which `FreeAlgorithm`s produce.
|
||||||
|
public typealias Diff = Algorithm<FreeAlgorithm, A>.Diff
|
||||||
|
|
||||||
|
/// The injection of a value of type `B` into an `Algorithm`.
|
||||||
|
///
|
||||||
|
/// Equally, a way to return a result or throw an error during computation, as determined by the type which `B` is instantiated to, and the specific context in which it is being evaluated.
|
||||||
|
case Pure(B)
|
||||||
|
|
||||||
|
/// A recursive instantiation of `Algorithm`, unrolling another iteration of the recursive type.
|
||||||
|
case Roll(Algorithm<FreeAlgorithm, A>)
|
||||||
|
|
||||||
|
public func analysis<C>(@noescape ifPure ifPure: B -> C, @noescape ifRoll: Algorithm<FreeAlgorithm, A> -> C) -> C {
|
||||||
|
switch self {
|
||||||
|
case let .Pure(b):
|
||||||
|
return ifPure(b)
|
||||||
|
case let .Roll(a):
|
||||||
|
return ifRoll(a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: Functor
|
||||||
|
|
||||||
|
public func map<Other>(transform: B -> Other) -> FreeAlgorithm<A, Other> {
|
||||||
|
return analysis(ifPure: transform >>> FreeAlgorithm<A, Other>.Pure, ifRoll: { .Roll($0.map { $0.map(transform) }) })
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: Monad
|
||||||
|
|
||||||
|
public func flatMap<C>(transform: B -> FreeAlgorithm<A, C>) -> FreeAlgorithm<A, C> {
|
||||||
|
return analysis(ifPure: transform, ifRoll: { .Roll($0.map { $0.flatMap(transform) }) })
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Evaluates the encoded algorithm, returning its result.
|
||||||
|
public func evaluate(equals: (A, A) -> Bool, recur: (Term, Term) -> Diff) -> B {
|
||||||
|
switch self {
|
||||||
|
case let .Pure(b):
|
||||||
|
return b
|
||||||
|
|
||||||
|
case let .Roll(.Recursive(a, b, f)):
|
||||||
|
return f(Term.equals(equals)(a, b)
|
||||||
|
? Diff(b)
|
||||||
|
// This must not call `recur` with `a` and `b`, as that would infinite loop if actually recursive.
|
||||||
|
: Diff.Pure(.Replace(a, b))).evaluate(equals, recur: recur)
|
||||||
|
|
||||||
|
case let .Roll(.ByKey(a, b, f)):
|
||||||
|
let deleted = Set(a.keys).subtract(b.keys).map { ($0, Diff.Pure(Patch.Delete(a[$0]!))) }
|
||||||
|
let inserted = Set(b.keys).subtract(a.keys).map { ($0, Diff.Pure(Patch.Insert(b[$0]!))) }
|
||||||
|
let patched = Set(a.keys).intersect(b.keys).map { ($0, recur(a[$0]!, b[$0]!)) }
|
||||||
|
return f(Dictionary(elements: deleted + inserted + patched)).evaluate(equals, recur: recur)
|
||||||
|
|
||||||
|
case let .Roll(.ByIndex(a, b, f)):
|
||||||
|
return f(SES(a, b, equals: equals, recur: recur)).evaluate(equals, recur: recur)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension FreeAlgorithm where A: Equatable {
|
||||||
|
public func evaluate(recur: (Term, Term) -> Diff) -> B {
|
||||||
|
return evaluate(==, recur: recur)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A hack to work around the unavailability of same-type requirements.
|
||||||
|
public protocol FreeConvertible {
|
||||||
|
typealias RollType
|
||||||
|
typealias PureType
|
||||||
|
|
||||||
|
init(free: Free<RollType, PureType>)
|
||||||
|
var free: Free<RollType, PureType> { get }
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Free: FreeConvertible {
|
||||||
|
public init(free: Free<A, B>) { self = free }
|
||||||
|
public var free: Free { return self }
|
||||||
|
}
|
||||||
|
|
||||||
|
extension FreeAlgorithm where B: FreeConvertible, B.RollType == A, B.PureType == Patch<A> {
|
||||||
|
/// `FreeAlgorithm<A, Diff>`s can be constructed from a pair of `Term`s using `ByKey` when `Keyed`, `ByIndex` when `Indexed`, and `Recursive` otherwise.
|
||||||
|
public init(_ a: Term, _ b: Term) {
|
||||||
|
switch (a.out, b.out) {
|
||||||
|
case let (.Keyed(a), .Keyed(b)):
|
||||||
|
self = .Roll(.ByKey(a, b, Syntax.Keyed >>> Free.Roll >>> B.init >>> Pure))
|
||||||
|
case let (.Indexed(a), .Indexed(b)):
|
||||||
|
self = .Roll(.ByIndex(a, b, Syntax.Indexed >>> Free.Roll >>> B.init >>> Pure))
|
||||||
|
default:
|
||||||
|
self = .Roll(.Recursive(a, b, B.init >>> FreeAlgorithm.Pure))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func evaluate(equals: (A, A) -> Bool) -> B {
|
||||||
|
return evaluate(equals, recur: { FreeAlgorithm($0, $1).evaluate(equals).free })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension FreeAlgorithm where A: Equatable, B: FreeConvertible, B.RollType == A, B.PureType == Patch<A> {
|
||||||
|
public func evaluate() -> B {
|
||||||
|
return evaluate(==)
|
||||||
|
}
|
||||||
|
}
|
136
prototype/doubt-swift/Fixtures/Algorithm.old.swift
Normal file
136
prototype/doubt-swift/Fixtures/Algorithm.old.swift
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
/// A language of diffing algorithms.
|
||||||
|
public enum Algorithm<Recur, A> {
|
||||||
|
/// The type of `Term`s over which `Algorithm`s operate.
|
||||||
|
public typealias Term = Fix<A>
|
||||||
|
|
||||||
|
/// The type of `Diff`s which `Algorithm`s produce.
|
||||||
|
public typealias Diff = Free<A, Patch<A>>
|
||||||
|
|
||||||
|
/// Indicates that diffing should compare the enclosed `Term`s.
|
||||||
|
///
|
||||||
|
/// When run, the enclosed function will be applied to the resulting `Diff`.
|
||||||
|
case Recursive(Term, Term, Diff -> Recur)
|
||||||
|
|
||||||
|
/// Represents a diff to be performed on a collection of terms identified by keys.
|
||||||
|
case ByKey([String:Term], [String:Term], [String:Diff] -> Recur)
|
||||||
|
// fixme: SES 😰
|
||||||
|
// case ByIndex([Term], [Term], [Diff] -> Recur)
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: Functor
|
||||||
|
|
||||||
|
public func map<Other>(transform: Recur -> Other) -> Algorithm<Other, A> {
|
||||||
|
switch self {
|
||||||
|
case let .Recursive(a, b, f):
|
||||||
|
return .Recursive(a, b, f >>> transform)
|
||||||
|
case let .ByKey(a, b, f):
|
||||||
|
return .ByKey(a, b, f >>> transform)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - Running
|
||||||
|
|
||||||
|
extension Algorithm {
|
||||||
|
/// Evaluates the encoded algorithm, returning its result.
|
||||||
|
public func evaluate(equals: (A, A) -> Bool) -> Recur {
|
||||||
|
/// Deep-copies a `Term` into a `Diff` without changes.
|
||||||
|
func copy(b: Term) -> Diff {
|
||||||
|
return Diff.Roll(b.out.map(copy))
|
||||||
|
}
|
||||||
|
|
||||||
|
switch self {
|
||||||
|
case let .Recursive(a, b, f):
|
||||||
|
return f(Fix.equals(equals)(a, b)
|
||||||
|
? copy(b)
|
||||||
|
: Diff.Pure(.Replace(a, b)))
|
||||||
|
|
||||||
|
case let .ByKey(a, b, f):
|
||||||
|
let deleted = Set(a.keys).subtract(b.keys).map { ($0, Diff.Pure(Patch.Delete(a[$0]!))) }
|
||||||
|
let inserted = Set(b.keys).subtract(a.keys).map { ($0, Diff.Pure(Patch.Insert(b[$0]!))) }
|
||||||
|
let patched = Set(a.keys).intersect(b.keys).map { ($0, Diff.Pure(Patch.Replace(a[$0]!, b[$0]!))) }
|
||||||
|
return f(Dictionary(elements: deleted + inserted + patched))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// The free monad over `Algorithm`.
|
||||||
|
///
|
||||||
|
/// As with `Free`, this is “free” in the sense of “unconstrained,” i.e. “the monad induced by `Algorithm` without extra assumptions.”
|
||||||
|
public enum FreeAlgorithm<A, B> {
|
||||||
|
/// The injection of a value of type `B` into an `Algorithm`.
|
||||||
|
///
|
||||||
|
/// Equally, a way to return a result or throw an error during computation, as determined by the type which `B` is instantiated to, and the specific context in which it is being evaluated.
|
||||||
|
case Pure(B)
|
||||||
|
|
||||||
|
/// A recursive instantiation of `Algorithm`, unrolling another iteration of the recursive type.
|
||||||
|
case Roll(Algorithm<FreeAlgorithm, A>)
|
||||||
|
|
||||||
|
public func analysis<C>(@noescape ifPure ifPure: B -> C, @noescape ifRoll: Algorithm<FreeAlgorithm, A> -> C) -> C {
|
||||||
|
switch self {
|
||||||
|
case let .Pure(b):
|
||||||
|
return ifPure(b)
|
||||||
|
case let .Roll(a):
|
||||||
|
return ifRoll(a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: Functor
|
||||||
|
|
||||||
|
public func map<Other>(transform: B -> Other) -> FreeAlgorithm<A, Other> {
|
||||||
|
return analysis(ifPure: transform >>> FreeAlgorithm<A, Other>.Pure, ifRoll: { .Roll($0.map { $0.map(transform) }) })
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: Monad
|
||||||
|
|
||||||
|
public func flatMap<C>(transform: B -> FreeAlgorithm<A, C>) -> FreeAlgorithm<A, C> {
|
||||||
|
return analysis(ifPure: transform, ifRoll: { .Roll($0.map { $0.flatMap(transform) }) })
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Evaluates the encoded algorithm, returning its result.
|
||||||
|
public func evaluate(equals: (A, A) -> Bool) -> B {
|
||||||
|
switch self {
|
||||||
|
case let .Pure(b):
|
||||||
|
return b
|
||||||
|
|
||||||
|
case let .Roll(r):
|
||||||
|
return r.evaluate(equals).evaluate(equals)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension FreeAlgorithm where A: Equatable {
|
||||||
|
public func evaluate() -> B {
|
||||||
|
return evaluate(==)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A hack to work around the unavailability of same-type requirements.
|
||||||
|
public protocol FreeConvertible {
|
||||||
|
typealias RollType
|
||||||
|
typealias PureType
|
||||||
|
|
||||||
|
init(free: Free<RollType, PureType>)
|
||||||
|
var free: Free<RollType, PureType> { get }
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Free: FreeConvertible {
|
||||||
|
public init(free: Free<A, B>) { self = free }
|
||||||
|
public var free: Free { return self }
|
||||||
|
}
|
||||||
|
|
||||||
|
extension FreeAlgorithm where B: FreeConvertible, B.RollType == A, B.PureType == Patch<A> {
|
||||||
|
public init(_ a: Fix<A>, _ b: Fix<A>) {
|
||||||
|
switch (a.out, b.out) {
|
||||||
|
case let (.Keyed(a), .Keyed(b)):
|
||||||
|
self = .Roll(.ByKey(a, b, Syntax.Keyed >>> Free.Roll >>> B.init >>> FreeAlgorithm.Pure))
|
||||||
|
default:
|
||||||
|
self = .Roll(.Recursive(a, b, B.init >>> FreeAlgorithm.Pure))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -13,10 +13,16 @@ extension String: StringConvertible {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension Info: StringConvertible {
|
||||||
|
public init(string: String) {
|
||||||
|
self = .Literal(string, [])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private struct Bail: ErrorType {}
|
private struct Bail: ErrorType {}
|
||||||
|
|
||||||
extension Term where A: StringConvertible {
|
extension Fix where A: StringConvertible {
|
||||||
/// Constructs a Term representing `JSON`.
|
/// Constructs a term representing `JSON`.
|
||||||
init?(JSON: Doubt.JSON) {
|
init?(JSON: Doubt.JSON) {
|
||||||
func bail<B>() throws -> B {
|
func bail<B>() throws -> B {
|
||||||
throw Bail()
|
throw Bail()
|
||||||
@ -29,23 +35,24 @@ extension Term where A: StringConvertible {
|
|||||||
let kind = d["key.kind"]?.string
|
let kind = d["key.kind"]?.string
|
||||||
switch kind {
|
switch kind {
|
||||||
case
|
case
|
||||||
.Some("source.lang.swift.decl.class"),
|
.Some("source.lang.swift.decl.class"),
|
||||||
.Some("source.lang.swift.decl.extension"),
|
.Some("source.lang.swift.decl.extension"),
|
||||||
.Some("source.lang.swift.decl.enum"),
|
.Some("source.lang.swift.decl.enum"),
|
||||||
.Some("source.lang.swift.decl.struct"):
|
.Some("source.lang.swift.decl.struct"),
|
||||||
self = .Branch([ .Leaf(A(string: name)), .Branch(try substructure.map { try Term(JSON: $0) ?? bail() }) ])
|
.Some("source.lang.swift.decl.protocol"):
|
||||||
|
self = .In(.Indexed([ .In(.Leaf(A(string: name))), .In(.Indexed(try substructure.map { try Fix(JSON: $0) ?? bail() })) ]))
|
||||||
|
|
||||||
case .Some("source.lang.swift.decl.enumelement"):
|
case .Some("source.lang.swift.decl.enumelement"):
|
||||||
fallthrough
|
fallthrough
|
||||||
case
|
case
|
||||||
.Some("source.lang.swift.decl.function.method.instance"),
|
.Some("source.lang.swift.decl.function.method.instance"),
|
||||||
.Some("source.lang.swift.decl.function.free"):
|
.Some("source.lang.swift.decl.function.free"):
|
||||||
self = .Branch([ .Leaf(A(string: name)), .Branch(try substructure.map { try Term(JSON: $0) ?? bail() }) ])
|
self = .In(.Indexed([ .In(.Leaf(A(string: name))), .In(.Indexed(try substructure.map { try Fix(JSON: $0) ?? bail() })) ]))
|
||||||
|
|
||||||
case
|
case
|
||||||
.Some("source.lang.swift.decl.var.instance"),
|
.Some("source.lang.swift.decl.var.instance"),
|
||||||
.Some("source.lang.swift.decl.var.static"):
|
.Some("source.lang.swift.decl.var.static"):
|
||||||
self = .Leaf(A(string: name))
|
self = .In(.Leaf(A(string: name)))
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil
|
||||||
@ -53,13 +60,10 @@ extension Term where A: StringConvertible {
|
|||||||
|
|
||||||
case let .Dictionary(d) where d["key.kind"]?.string == "source.lang.swift.decl.enumcase" && d["key.substructure"]?.array?.count == 1:
|
case let .Dictionary(d) where d["key.kind"]?.string == "source.lang.swift.decl.enumcase" && d["key.substructure"]?.array?.count == 1:
|
||||||
let substructure = d["key.substructure"]?.array ?? []
|
let substructure = d["key.substructure"]?.array ?? []
|
||||||
self = try Term(JSON: substructure[0]) ?? bail()
|
self = try Fix(JSON: substructure[0]) ?? bail()
|
||||||
|
|
||||||
case let .Dictionary(d) where d["key.kind"]?.string == "source.lang.swift.syntaxtype.comment.mark":
|
case let .Dictionary(d) where d["key.kind"]?.string == "source.lang.swift.syntaxtype.comment.mark":
|
||||||
self = .Empty
|
self = .In(.Leaf(A(string: "mark")))
|
||||||
|
|
||||||
case .Null:
|
|
||||||
self = .Empty
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil
|
||||||
@ -69,7 +73,7 @@ extension Term where A: StringConvertible {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructs a Term representing the `JSON` in a file at `path`.
|
/// Constructs a term representing the `JSON` in a file at `path`.
|
||||||
init?(path: String, JSON: Doubt.JSON) {
|
init?(path: String, JSON: Doubt.JSON) {
|
||||||
func bail<B>() throws -> B {
|
func bail<B>() throws -> B {
|
||||||
throw Bail()
|
throw Bail()
|
||||||
@ -77,7 +81,7 @@ extension Term where A: StringConvertible {
|
|||||||
do {
|
do {
|
||||||
switch JSON.dictionary?["key.substructure"] {
|
switch JSON.dictionary?["key.substructure"] {
|
||||||
case let .Some(.Array(a)):
|
case let .Some(.Array(a)):
|
||||||
self = .Roll(.Branch(try a.map { try Term(JSON: $0) ?? bail() }))
|
self = .In(.Indexed(try a.map { try Fix(JSON: $0) ?? bail() }))
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -85,23 +89,20 @@ extension Term where A: StringConvertible {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
extension Term where A: StringConvertible {
|
|
||||||
init?(path: String) {
|
init?(path: String) {
|
||||||
guard path != "/dev/null" else {
|
guard path != "/dev/null" else {
|
||||||
self = .Empty
|
return nil
|
||||||
return
|
|
||||||
}
|
}
|
||||||
guard let term = File(path: path)
|
guard let term = File(path: path)
|
||||||
.map(Structure.init)
|
.map(Structure.init)
|
||||||
.map({ $0.dictionary })
|
.map({ $0.dictionary })
|
||||||
.map(toAnyObject)
|
.map(toAnyObject)
|
||||||
.flatMap({ JSON(object: $0).flatMap { Term(path: path, JSON: $0) } }) else { return nil }
|
.flatMap({ JSON(object: $0).flatMap { Fix(path: path, JSON: $0) } }) else { return nil }
|
||||||
self = term
|
self = term
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let a = arguments[1].flatMap(Term<Info>.init), b = arguments[2].flatMap(Term<Info>.init) {
|
if let a = arguments[1].flatMap({ Fix<Info>(path: $0) }), b = arguments[2].flatMap({ Fix<Info>(path: $0) }) {
|
||||||
print(String(reflecting: Diff(a, b)))
|
print(String(reflecting: FreeAlgorithm<Info, Free<Info, Patch<Info>>>(a, b).evaluate()))
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user