2015-10-01 21:25:17 +03:00
|
|
|
|
/// A node in a syntax tree. Expressed algebraically to enable representation of both normal syntax trees and their diffs.
|
2015-10-15 15:53:46 +03:00
|
|
|
|
public enum Syntax<Recur, A>: CustomDebugStringConvertible {
|
2015-09-30 19:41:20 +03:00
|
|
|
|
case Leaf(A)
|
2015-10-03 00:12:49 +03:00
|
|
|
|
case Indexed([Recur])
|
2015-10-23 21:47:44 +03:00
|
|
|
|
case Fixed([Recur])
|
2015-10-03 00:18:38 +03:00
|
|
|
|
case Keyed([String:Recur])
|
2015-07-18 22:43:49 +03:00
|
|
|
|
|
2015-10-06 18:08:39 +03:00
|
|
|
|
|
|
|
|
|
// MARK: Functor
|
|
|
|
|
|
2015-10-28 18:51:43 +03:00
|
|
|
|
public func map<T>(@noescape transform: Recur throws -> T) rethrows -> Syntax<T, A> {
|
2015-07-18 22:43:49 +03:00
|
|
|
|
switch self {
|
2015-09-30 17:36:25 +03:00
|
|
|
|
case let .Leaf(n):
|
|
|
|
|
return .Leaf(n)
|
2015-10-03 00:12:49 +03:00
|
|
|
|
case let .Indexed(x):
|
2015-10-28 18:51:43 +03:00
|
|
|
|
return try .Indexed(x.map(transform))
|
2015-10-23 21:47:44 +03:00
|
|
|
|
case let .Fixed(x):
|
2015-10-28 18:51:43 +03:00
|
|
|
|
return try .Fixed(x.map(transform))
|
2015-10-03 00:18:38 +03:00
|
|
|
|
case let .Keyed(d):
|
2015-10-28 18:51:43 +03:00
|
|
|
|
return try .Keyed(Dictionary(elements: d.map { try ($0, transform($1)) }))
|
2015-07-18 22:43:49 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-22 18:35:25 +03:00
|
|
|
|
|
|
|
|
|
// MARK: CustomDebugStringConvertible
|
|
|
|
|
|
2015-09-11 17:17:19 +03:00
|
|
|
|
public var debugDescription: String {
|
2015-07-18 22:43:49 +03:00
|
|
|
|
switch self {
|
2015-09-30 17:36:25 +03:00
|
|
|
|
case let .Leaf(n):
|
|
|
|
|
return ".Leaf(\(n))"
|
2015-10-03 00:12:49 +03:00
|
|
|
|
case let .Indexed(x):
|
2015-10-03 00:13:27 +03:00
|
|
|
|
return ".Indexed(\(String(reflecting: x)))"
|
2015-10-23 21:47:44 +03:00
|
|
|
|
case let .Fixed(x):
|
|
|
|
|
return ".Fixed(\(String(reflecting: x)))"
|
2015-10-03 00:18:38 +03:00
|
|
|
|
case let .Keyed(d):
|
|
|
|
|
return ".Keyed(\(String(reflecting: d)))"
|
2015-07-18 22:43:49 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-09-30 00:03:11 +03:00
|
|
|
|
|
2015-10-02 21:12:54 +03:00
|
|
|
|
|
2015-10-23 01:18:05 +03:00
|
|
|
|
// MARK: - Hylomorphism
|
|
|
|
|
|
2015-10-23 01:31:06 +03:00
|
|
|
|
/// Hylomorphism through `Syntax`.
|
|
|
|
|
///
|
2015-10-23 18:36:05 +03:00
|
|
|
|
/// A hylomorphism (from the Aristotelian philosophy that form and matter are one) is a function of type `A → B` whose call-tree is linear in the size of the nodes produced by `up`. Conceptually, it’s the composition of a catamorphism (see also `cata`) and an anamorphism (see also `ana`), but is implemented by [Stream fusion](http://lambda-the-ultimate.org/node/2192) and as such enjoys O(n) time complexity, O(1) size complexity, and small constant factors for both (modulo inadvisable implementations of `up` and `down`).
|
2015-10-23 01:31:06 +03:00
|
|
|
|
///
|
|
|
|
|
/// Hylomorphisms are used to construct diffs corresponding to equal terms; see also `CofreeType.zip`.
|
|
|
|
|
///
|
|
|
|
|
/// `hylo` can be used with arbitrary functors which can eliminate to and introduce with `Syntax` values.
|
2015-11-03 19:03:57 +03:00
|
|
|
|
public func hylo<A, B, Leaf>(down: Syntax<B, Leaf> -> B, _ up: A -> Syntax<A, Leaf>)(_ a: A) -> B {
|
|
|
|
|
return down(up(a).map(hylo(down, up)))
|
2015-10-23 01:18:05 +03:00
|
|
|
|
}
|
|
|
|
|
|
2015-10-23 01:31:11 +03:00
|
|
|
|
/// Reiteration through `Syntax`.
|
|
|
|
|
///
|
2015-10-23 18:36:05 +03:00
|
|
|
|
/// This is a form of hylomorphism (from the Aristotelian philosophy that form and matter are one). As such, it returns a function of type `A → B` whose call-tree is linear in the size of the nodes produced by `up`. Conceptually, it’s the composition of a catamorphism (see also `cata`) and an anamorphism (see also `ana`), but is implemented by [Stream fusion](http://lambda-the-ultimate.org/node/2192) and as such enjoys O(n) time complexity, O(1) size complexity, and small constant factors for both (modulo inadvisable implementations of `up` and `down`).
|
2015-10-23 01:31:11 +03:00
|
|
|
|
///
|
|
|
|
|
/// Hylomorphisms are used to construct diffs corresponding to equal terms; see also `CofreeType.zip`.
|
|
|
|
|
///
|
2015-10-23 18:36:42 +03:00
|
|
|
|
/// `hylo` can be used with arbitrary functors which can eliminate to and introduce with `Annotation` & `Syntax` pairs.
|
|
|
|
|
public func hylo<A, B, Leaf, Annotation>(down: (Annotation, Syntax<B, Leaf>) -> B, _ up: A -> (Annotation, Syntax<A, Leaf>)) -> A -> B {
|
|
|
|
|
return up >>> { ($0, $1.map(hylo(down, up))) } >>> down
|
2015-10-23 01:18:05 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2015-10-14 23:02:33 +03:00
|
|
|
|
// MARK: - ArrayLiteralConvertible
|
|
|
|
|
|
|
|
|
|
extension Syntax: ArrayLiteralConvertible {
|
|
|
|
|
public init(arrayLiteral: Recur...) {
|
|
|
|
|
self = .Indexed(arrayLiteral)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2015-10-14 23:03:14 +03:00
|
|
|
|
// MARK: - DictionaryLiteralConvertible
|
|
|
|
|
|
|
|
|
|
extension Syntax: DictionaryLiteralConvertible {
|
|
|
|
|
public init(dictionaryLiteral elements: (String, Recur)...) {
|
|
|
|
|
self = .Keyed(Dictionary(elements: elements))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2015-10-02 21:12:54 +03:00
|
|
|
|
// MARK: - Equality
|
|
|
|
|
|
2015-10-02 21:15:13 +03:00
|
|
|
|
extension Syntax {
|
2015-10-22 17:46:51 +03:00
|
|
|
|
public static func equals(leaf leaf: (A, A) -> Bool, recur: (Recur, Recur) -> Bool)(_ left: Syntax<Recur, A>, _ right: Syntax<Recur, A>) -> Bool {
|
2015-10-01 22:25:14 +03:00
|
|
|
|
switch (left, right) {
|
|
|
|
|
case let (.Leaf(l1), .Leaf(l2)):
|
2015-10-22 17:46:51 +03:00
|
|
|
|
return leaf(l1, l2)
|
2015-10-03 00:12:49 +03:00
|
|
|
|
case let (.Indexed(v1), .Indexed(v2)):
|
2015-10-22 17:46:51 +03:00
|
|
|
|
return v1.count == v2.count && zip(v1, v2).lazy.map(recur).reduce(true) { $0 && $1 }
|
2015-10-26 23:38:07 +03:00
|
|
|
|
case let (.Fixed(v1), .Fixed(v2)):
|
|
|
|
|
return v1.count == v2.count && zip(v1, v2).lazy.map(recur).reduce(true) { $0 && $1 }
|
2015-10-03 00:18:38 +03:00
|
|
|
|
case let (.Keyed(d1), .Keyed(d2)):
|
2015-10-22 17:46:51 +03:00
|
|
|
|
return Set(d1.keys) == Set(d2.keys) && d1.keys.map { recur(d1[$0]!, d2[$0]!) }.reduce(true) { $0 && $1 }
|
2015-10-01 22:25:14 +03:00
|
|
|
|
default:
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-01 22:25:49 +03:00
|
|
|
|
public func == <F: Equatable, A: Equatable> (left: Syntax<F, A>, right: Syntax<F, A>) -> Bool {
|
2015-10-22 17:46:51 +03:00
|
|
|
|
return Syntax.equals(leaf: ==, recur: ==)(left, right)
|
2015-10-01 22:25:49 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2015-10-09 15:40:57 +03:00
|
|
|
|
// MARK: - JSON
|
2015-10-08 14:19:37 +03:00
|
|
|
|
|
|
|
|
|
extension Syntax {
|
2015-10-22 17:47:48 +03:00
|
|
|
|
public func JSON(@noescape leaf leaf: A -> Doubt.JSON, @noescape recur: Recur -> Doubt.JSON) -> Doubt.JSON {
|
2015-10-08 14:19:37 +03:00
|
|
|
|
switch self {
|
|
|
|
|
case let .Leaf(a):
|
2015-10-24 01:22:24 +03:00
|
|
|
|
return [ "leaf": leaf(a) ]
|
2015-10-08 14:19:37 +03:00
|
|
|
|
case let .Indexed(a):
|
2015-10-24 01:22:24 +03:00
|
|
|
|
return [ "indexed": .Array(a.map(recur)) ]
|
2015-10-23 21:47:44 +03:00
|
|
|
|
case let .Fixed(a):
|
2015-10-26 16:31:44 +03:00
|
|
|
|
return [ "fixed": .Array(a.map(recur)) ]
|
2015-10-08 14:19:37 +03:00
|
|
|
|
case let .Keyed(d):
|
2015-10-24 01:22:24 +03:00
|
|
|
|
return [ "keyed": .Dictionary(Dictionary(elements: d.map { ($0, recur($1)) })) ]
|
2015-10-08 14:19:37 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-10-23 01:18:05 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import Prelude
|