2015-10-01 21:25:17 +03:00
|
|
|
/// A term in a syntax tree.
|
|
|
|
///
|
|
|
|
/// This is a fixpoint of Syntax, essentially enabling it to recur through Term instances.
|
2015-10-01 20:52:32 +03:00
|
|
|
public enum Term<A: Equatable>: CustomDebugStringConvertible, CustomDocConvertible, CustomStringConvertible, Equatable {
|
2015-09-30 21:42:58 +03:00
|
|
|
public init(_ out: Syntax<Term, A>) {
|
2015-09-15 20:49:23 +03:00
|
|
|
self = .Roll(out)
|
2015-07-18 22:43:49 +03:00
|
|
|
}
|
|
|
|
|
2015-09-30 21:42:58 +03:00
|
|
|
indirect case Roll(Syntax<Term, A>)
|
2015-07-18 22:43:49 +03:00
|
|
|
|
2015-09-30 21:42:58 +03:00
|
|
|
public var syntax: Syntax<Term, A> {
|
2015-09-29 19:48:53 +03:00
|
|
|
switch self {
|
|
|
|
case let .Roll(syntax):
|
|
|
|
return syntax
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-15 21:07:35 +03:00
|
|
|
public var debugDescription: String {
|
2015-09-30 22:03:42 +03:00
|
|
|
return syntax.debugDescription
|
2015-07-18 22:43:49 +03:00
|
|
|
}
|
|
|
|
|
2015-09-29 05:36:22 +03:00
|
|
|
public var doc: Doc {
|
2015-09-30 22:03:42 +03:00
|
|
|
return syntax.doc
|
2015-07-18 22:43:49 +03:00
|
|
|
}
|
2015-09-30 21:42:58 +03:00
|
|
|
}
|
2015-09-23 20:34:45 +03:00
|
|
|
|
2015-10-01 22:26:15 +03:00
|
|
|
public func == <A: Equatable> (left: Term<A>, right: Term<A>) -> Bool {
|
2015-10-02 21:15:13 +03:00
|
|
|
return Syntax.equals(ifLeaf: ==, ifRecur: ==)(left.syntax, right.syntax)
|
2015-10-01 22:26:15 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
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-09-30 17:31:48 +03:00
|
|
|
public enum Syntax<Recur, A>: CustomDebugStringConvertible, CustomDocConvertible {
|
2015-09-30 19:41:20 +03:00
|
|
|
case Leaf(A)
|
2015-10-03 00:12:49 +03:00
|
|
|
case Indexed([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-09-30 17:31:48 +03:00
|
|
|
public func map<T>(@noescape transform: Recur -> T) -> 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):
|
|
|
|
return .Indexed(x.map(transform))
|
2015-10-03 00:18:38 +03:00
|
|
|
case let .Keyed(d):
|
|
|
|
return .Keyed(Dictionary(elements: d.map { ($0, transform($1)) }))
|
2015-07-18 22:43:49 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-03 00:11:35 +03:00
|
|
|
// fixme: 🔥
|
2015-09-30 18:42:41 +03:00
|
|
|
public func reduce<T>(initial: T, @noescape combine: (T, Recur) throws -> T) rethrows -> T {
|
2015-09-16 19:22:53 +03:00
|
|
|
switch self {
|
2015-10-03 00:12:49 +03:00
|
|
|
case let .Indexed(x):
|
2015-10-03 00:11:02 +03:00
|
|
|
return try x.reduce(initial, combine: combine)
|
2015-09-16 19:22:53 +03:00
|
|
|
|
2015-10-03 00:18:38 +03:00
|
|
|
case let .Keyed(d):
|
|
|
|
return try d.lazy.map { $1 }.reduce(initial, combine: combine)
|
|
|
|
|
2015-09-16 19:22:53 +03:00
|
|
|
default:
|
|
|
|
return initial
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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-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-29 05:36:22 +03:00
|
|
|
public var doc: Doc {
|
2015-07-18 22:43:49 +03:00
|
|
|
switch self {
|
2015-09-30 17:36:25 +03:00
|
|
|
case let .Leaf(n):
|
2015-09-30 19:41:20 +03:00
|
|
|
return Doc(n)
|
2015-10-03 00:12:49 +03:00
|
|
|
case let .Indexed(x):
|
2015-10-03 00:11:02 +03:00
|
|
|
return x.map(Doc.init).joinWithSeparator(", ").bracket("[", "]")
|
2015-10-03 00:18:38 +03:00
|
|
|
case let .Keyed(d):
|
|
|
|
return d.lazy.map { Doc($0) <> Doc(":") <+> Doc($1) }.joinWithSeparator(", ").bracket("[", "]")
|
2015-07-18 22:43:49 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-09-30 00:03:11 +03:00
|
|
|
|
2015-10-02 21:12:54 +03:00
|
|
|
|
|
|
|
// MARK: - Equality
|
|
|
|
|
2015-10-02 21:15:13 +03:00
|
|
|
extension Syntax {
|
|
|
|
public static func equals(ifLeaf ifLeaf: (A, A) -> Bool, ifRecur: (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-02 21:15:13 +03:00
|
|
|
return ifLeaf(l1, l2)
|
2015-10-03 00:12:49 +03:00
|
|
|
case let (.Indexed(v1), .Indexed(v2)):
|
2015-10-03 00:11:02 +03:00
|
|
|
return v1.count == v2.count && zip(v1, v2).lazy.map(ifRecur).reduce(true) { $0 && $1 }
|
2015-10-03 00:18:38 +03:00
|
|
|
case let (.Keyed(d1), .Keyed(d2)):
|
|
|
|
return Array(d1.keys) == Array(d2.keys) && d1.keys.lazy.map { ifRecur(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-02 21:15:13 +03:00
|
|
|
return Syntax.equals(ifLeaf: ==, ifRecur: ==)(left, right)
|
2015-10-01 22:25:49 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-10-01 16:54:44 +03:00
|
|
|
extension Term where A: Hashable {
|
|
|
|
public var hash: Hash {
|
2015-10-02 21:33:41 +03:00
|
|
|
return syntax.hash(ifLeaf: Hash.init, ifRecur: { $0.hash })
|
2015-10-01 16:54:44 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-02 21:33:41 +03:00
|
|
|
extension Syntax {
|
|
|
|
public func hash(ifLeaf ifLeaf: A -> Hash, ifRecur: Recur -> Hash) -> Hash {
|
2015-09-30 00:03:11 +03:00
|
|
|
switch self {
|
2015-09-30 17:36:25 +03:00
|
|
|
case let .Leaf(n):
|
2015-10-02 21:33:41 +03:00
|
|
|
return Hash("Leaf", ifLeaf(n))
|
2015-10-03 00:12:49 +03:00
|
|
|
case let .Indexed(x):
|
|
|
|
return Hash("Indexed", .Ordered(x.map(ifRecur)))
|
2015-10-03 00:18:38 +03:00
|
|
|
case let .Keyed(d):
|
|
|
|
return Hash("Keyed", .Ordered(d.keys.sort().map { Hash($0, ifRecur(d[$0]!)) }))
|
2015-09-30 00:03:11 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-10-01 16:53:30 +03:00
|
|
|
|
|
|
|
extension Syntax where Recur: Hashable, A: Hashable {
|
|
|
|
public var hash: Hash {
|
2015-10-02 21:33:41 +03:00
|
|
|
return hash(ifLeaf: Hash.init, ifRecur: Hash.init)
|
2015-10-01 16:53:30 +03:00
|
|
|
}
|
|
|
|
}
|