1
1
mirror of https://github.com/github/semantic.git synced 2024-12-26 00:12:29 +03:00

Merge pull request #40 from github/syntactic-categorization

Syntactic categorization
This commit is contained in:
Rob Rix 2015-09-30 16:08:30 -04:00
commit cb5b7c0b33
10 changed files with 166 additions and 118 deletions

View File

@ -19,6 +19,8 @@
D4413FE91BB055B500E3C3C1 /* Commandant.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D4413FE71BB055AE00E3C3C1 /* Commandant.framework */; }; D4413FE91BB055B500E3C3C1 /* Commandant.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D4413FE71BB055AE00E3C3C1 /* Commandant.framework */; };
D4413FEF1BB06D4C00E3C3C1 /* Dictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4413FEE1BB06D4C00E3C3C1 /* Dictionary.swift */; }; D4413FEF1BB06D4C00E3C3C1 /* Dictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4413FEE1BB06D4C00E3C3C1 /* Dictionary.swift */; };
D4413FF11BB08FDC00E3C3C1 /* JSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4413FF01BB08FDC00E3C3C1 /* JSON.swift */; }; D4413FF11BB08FDC00E3C3C1 /* JSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4413FF01BB08FDC00E3C3C1 /* JSON.swift */; };
D45A36C91BBC667D00BE3DDE /* Category.swift in Sources */ = {isa = PBXBuildFile; fileRef = D45A36C81BBC667D00BE3DDE /* Category.swift */; settings = {ASSET_TAGS = (); }; };
D45A36CD1BBC75DF00BE3DDE /* Info.swift in Sources */ = {isa = PBXBuildFile; fileRef = D45A36CC1BBC75DF00BE3DDE /* Info.swift */; settings = {ASSET_TAGS = (); }; };
D4A71DC51BB45B850051416D /* Vertex.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4A71DC41BB45B850051416D /* Vertex.swift */; settings = {ASSET_TAGS = (); }; }; D4A71DC51BB45B850051416D /* Vertex.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4A71DC41BB45B850051416D /* Vertex.swift */; settings = {ASSET_TAGS = (); }; };
D4A71DC71BB4AC9E0051416D /* VertexTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4A71DC61BB4AC9E0051416D /* VertexTests.swift */; settings = {ASSET_TAGS = (); }; }; 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 */; }; D4AAE50E1B5AE22E004E581F /* Doubt.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D4AAE4FD1B5AE22E004E581F /* Doubt.framework */; };
@ -94,6 +96,8 @@
D4413FE71BB055AE00E3C3C1 /* Commandant.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Commandant.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D4413FE71BB055AE00E3C3C1 /* Commandant.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Commandant.framework; sourceTree = BUILT_PRODUCTS_DIR; };
D4413FEE1BB06D4C00E3C3C1 /* Dictionary.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Dictionary.swift; sourceTree = "<group>"; }; D4413FEE1BB06D4C00E3C3C1 /* Dictionary.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Dictionary.swift; sourceTree = "<group>"; };
D4413FF01BB08FDC00E3C3C1 /* JSON.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSON.swift; sourceTree = "<group>"; }; D4413FF01BB08FDC00E3C3C1 /* JSON.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSON.swift; sourceTree = "<group>"; };
D45A36C81BBC667D00BE3DDE /* Category.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Category.swift; sourceTree = "<group>"; };
D45A36CC1BBC75DF00BE3DDE /* Info.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Info.swift; sourceTree = "<group>"; };
D4A71DC41BB45B850051416D /* Vertex.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Vertex.swift; sourceTree = "<group>"; }; D4A71DC41BB45B850051416D /* Vertex.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Vertex.swift; sourceTree = "<group>"; };
D4A71DC61BB4AC9E0051416D /* VertexTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VertexTests.swift; sourceTree = "<group>"; }; D4A71DC61BB4AC9E0051416D /* VertexTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VertexTests.swift; sourceTree = "<group>"; };
D4AAE4FD1B5AE22E004E581F /* Doubt.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Doubt.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D4AAE4FD1B5AE22E004E581F /* Doubt.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Doubt.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@ -199,6 +203,8 @@
D4413FF01BB08FDC00E3C3C1 /* JSON.swift */, D4413FF01BB08FDC00E3C3C1 /* JSON.swift */,
D4A71DC41BB45B850051416D /* Vertex.swift */, D4A71DC41BB45B850051416D /* Vertex.swift */,
D4D7F3161BBB22E500AAB0C0 /* Hash.swift */, D4D7F3161BBB22E500AAB0C0 /* Hash.swift */,
D45A36C81BBC667D00BE3DDE /* Category.swift */,
D45A36CC1BBC75DF00BE3DDE /* Info.swift */,
D4AAE5001B5AE22E004E581F /* Supporting Files */, D4AAE5001B5AE22E004E581F /* Supporting Files */,
); );
path = Doubt; path = Doubt;
@ -379,6 +385,7 @@
D4AAE5481B5AE2D0004E581F /* String.swift in Sources */, D4AAE5481B5AE2D0004E581F /* String.swift in Sources */,
D4AAE5411B5AE2D0004E581F /* Diff.swift in Sources */, D4AAE5411B5AE2D0004E581F /* Diff.swift in Sources */,
D4D7F3171BBB22E500AAB0C0 /* Hash.swift in Sources */, D4D7F3171BBB22E500AAB0C0 /* Hash.swift in Sources */,
D45A36C91BBC667D00BE3DDE /* Category.swift in Sources */,
D4AAE5401B5AE2D0004E581F /* Array.swift in Sources */, D4AAE5401B5AE2D0004E581F /* Array.swift in Sources */,
D4AAE54C1B5AE42D004E581F /* Equatable.swift in Sources */, D4AAE54C1B5AE42D004E581F /* Equatable.swift in Sources */,
D4A71DC51BB45B850051416D /* Vertex.swift in Sources */, D4A71DC51BB45B850051416D /* Vertex.swift in Sources */,
@ -386,6 +393,7 @@
D432D4731BA9C55300F3FABC /* Stream.swift in Sources */, D432D4731BA9C55300F3FABC /* Stream.swift in Sources */,
D4AAE5421B5AE2D0004E581F /* Doc.swift in Sources */, D4AAE5421B5AE2D0004E581F /* Doc.swift in Sources */,
D432D4771BA9FE6A00F3FABC /* Comparable.swift in Sources */, D432D4771BA9FE6A00F3FABC /* Comparable.swift in Sources */,
D45A36CD1BBC75DF00BE3DDE /* Info.swift in Sources */,
D4AAE5461B5AE2D0004E581F /* Parse.swift in Sources */, D4AAE5461B5AE2D0004E581F /* Parse.swift in Sources */,
D432D4751BA9D6A400F3FABC /* Memo.swift in Sources */, D432D4751BA9D6A400F3FABC /* Memo.swift in Sources */,
D4AAE5491B5AE2D0004E581F /* StringLiteralConvertible.swift in Sources */, D4AAE5491B5AE2D0004E581F /* StringLiteralConvertible.swift in Sources */,

View File

@ -0,0 +1,22 @@
public enum Category: AlgebraicHashable {
case Tag(String)
public var tag: String {
switch self {
case let .Tag(s):
return s
}
}
public var hash: Hash {
return Hash("Tag", Hash(tag))
}
}
public func == (left: Category, right: Category) -> Bool {
switch (left, right) {
case let (.Tag(a), .Tag(b)):
return a == b
}
}

View File

@ -1,16 +1,16 @@
public enum Diff: Comparable, CustomDebugStringConvertible, CustomDocConvertible { public enum Diff: Comparable, CustomDebugStringConvertible, CustomDocConvertible {
case Patch(Term, Term) case Patch(Term<Info>, Term<Info>)
indirect case Copy(Syntax<Diff, String>) indirect case Copy(Syntax<Diff, Info>)
public static func Insert(term: Term) -> Diff { public static func Insert(term: Term<Info>) -> Diff {
return .Patch(.Empty, term) return .Patch(.Empty, term)
} }
public static func Delete(term: Term) -> Diff { public static func Delete(term: Term<Info>) -> Diff {
return .Patch(term, .Empty) return .Patch(term, .Empty)
} }
public init(_ term: Term) { public init(_ term: Term<Info>) {
switch term { switch term {
case let .Roll(s): case let .Roll(s):
self = .Copy(s.map(Diff.init)) self = .Copy(s.map(Diff.init))
@ -45,7 +45,7 @@ public enum Diff: Comparable, CustomDebugStringConvertible, CustomDocConvertible
} }
} }
public init(_ a: Term, _ b: Term) { public init(_ a: Term<Info>, _ b: Term<Info>) {
if a == b { if a == b {
self = Diff(b) self = Diff(b)
return return
@ -62,7 +62,7 @@ public enum Diff: Comparable, CustomDebugStringConvertible, CustomDocConvertible
} }
} }
public static func diff<C1: CollectionType, C2: CollectionType where C1.Index : RandomAccessIndexType, C1.Generator.Element == Term, C2.Index : RandomAccessIndexType, C2.Generator.Element == Term>(a: C1, _ b: C2) -> [Diff] { public static func diff<C1: CollectionType, C2: CollectionType where C1.Index : RandomAccessIndexType, C1.Generator.Element == Term<Info>, C2.Index : RandomAccessIndexType, C2.Generator.Element == Term<Info>>(a: C1, _ b: C2) -> [Diff] {
func magnitude(diffs: Stream<(Diff, Int)>) -> Int { func magnitude(diffs: Stream<(Diff, Int)>) -> Int {
// return diffs.first?.magnitude ?? 0 // return diffs.first?.magnitude ?? 0
return diffs.map { $1 }.reduce(0, combine: +) return diffs.map { $1 }.reduce(0, combine: +)
@ -74,7 +74,7 @@ public enum Diff: Comparable, CustomDebugStringConvertible, CustomDocConvertible
}) })
} }
func diff(a: Stream<Term>, _ b: Stream<Term>) -> Stream<(Diff, Int)> { func diff(a: Stream<Term<Info>>, _ b: Stream<Term<Info>>) -> Stream<(Diff, Int)> {
switch (a, b) { switch (a, b) {
case (.Nil, .Nil): case (.Nil, .Nil):
return .Nil return .Nil

View File

@ -1,20 +1,24 @@
public func == (left: Term, right: Term) -> Bool { public func == <A: Equatable> (left: Term<A>, right: Term<A>) -> Bool {
return left.syntax == right.syntax return equals(left.syntax, right.syntax, ==)
} }
public func == <F: Equatable, A: Equatable> (left: Syntax<F, A>, right: Syntax<F, A>) -> Bool { private func equals<F, A: Equatable>(left: Syntax<F, A>, _ right: Syntax<F, A>, _ recur: (F, F) -> Bool) -> Bool {
switch (left, right) { switch (left, right) {
case (.Empty, .Empty): case (.Empty, .Empty):
return true return true
case let (.Leaf(l1), .Leaf(l2)): case let (.Leaf(l1), .Leaf(l2)):
return l1 == l2 return l1 == l2
case let (.Branch(v1), .Branch(v2)): case let (.Branch(v1), .Branch(v2)):
return v1 == v2 return v1.count == v2.count && zip(v1, v2).reduce(true) { $0 && recur($1.0, $1.1) }
default: default:
return false return false
} }
} }
public func == <F: Equatable, A: Equatable> (left: Syntax<F, A>, right: Syntax<F, A>) -> Bool {
return equals(left, right, ==)
}
public func == (left: Diff, right: Diff) -> Bool { public func == (left: Diff, right: Diff) -> Bool {
switch (left, right) { switch (left, right) {
case let (.Patch(a1, b1), .Patch(a2, b2)): case let (.Patch(a1, b1), .Patch(a2, b2)):

View File

@ -59,12 +59,10 @@ public func == (left: Hash, right: Hash) -> Bool {
} }
} }
public protocol CustomHashConvertible { public protocol AlgebraicHashable: Hashable {
var hash: Hash { get } var hash: Hash { get }
} }
public protocol AlgebraicHashable: CustomHashConvertible, Hashable {}
extension AlgebraicHashable { extension AlgebraicHashable {
public var hashValue: Int { public var hashValue: Int {
return hash.hashValue return hash.hashValue

View File

@ -0,0 +1,18 @@
public enum Info: Equatable {
case Literal(String, Set<Category>)
public var categories: Set<Category> {
switch self {
case let .Literal(_, c):
return c
}
}
}
public func == (left: Info, right: Info) -> Bool {
switch (left, right) {
case let (.Literal(s1, c1), .Literal(s2, c2)):
return s1 == s2 && c1 == c2
}
}

View File

@ -1,11 +1,11 @@
public enum Term: CustomDebugStringConvertible, CustomDocConvertible, CustomStringConvertible, AlgebraicHashable { public enum Term<A>: CustomDebugStringConvertible, CustomDocConvertible, CustomStringConvertible {
public init(_ out: Syntax<Term, String>) { public init(_ out: Syntax<Term, A>) {
self = .Roll(out) self = .Roll(out)
} }
indirect case Roll(Syntax<Term, String>) indirect case Roll(Syntax<Term, A>)
public var syntax: Syntax<Term, String> { public var syntax: Syntax<Term, A> {
switch self { switch self {
case let .Roll(syntax): case let .Roll(syntax):
return syntax return syntax
@ -13,22 +13,11 @@ public enum Term: CustomDebugStringConvertible, CustomDocConvertible, CustomStri
} }
public var debugDescription: String { public var debugDescription: String {
switch self { return syntax.debugDescription
case let .Roll(s):
return s.debugDescription
}
} }
public var doc: Doc { public var doc: Doc {
switch self { return syntax.doc
case let .Roll(s):
return s.doc
}
}
public var hash: Hash {
return syntax.hash
} }
@ -36,90 +25,15 @@ public enum Term: CustomDebugStringConvertible, CustomDocConvertible, CustomStri
return Term(.Empty) return Term(.Empty)
} }
public static let Leaf = Syntax.Leaf >>> Roll public static func Leaf(a: A) -> Term {
public static let Branch: [Term] -> Term = Syntax.Branch >>> Roll return Term(.Leaf(a))
// MARK: JSON representation.
/// Constructs a Term representing the `JSON` in a file at `path`.
public init?(path: String, JSON: Doubt.JSON) {
struct E: ErrorType {}
func die<A>() throws -> A {
throw E()
}
do {
switch JSON.dictionary?["key.substructure"] {
case let .Some(.Array(a)):
self = .Roll(.Branch(try a.map { try Term(JSON: $0) ?? die() }))
default:
return nil
}
} catch _ {
return nil
}
} }
/// Constructs a Term representing `JSON`. public static func Branch(terms: [Term]) -> Term {
public init?(JSON: Doubt.JSON) { return Term(.Branch(terms))
enum Key: String {
case Name = "key.name"
case Substructure = "key.substructure"
}
struct E: ErrorType {}
func die<A>() throws -> A {
throw E()
}
do {
switch JSON {
case let .Dictionary(d) where d["key.name"] != nil:
let name = d["key.name"]?.string ?? ""
let substructure = d["key.substructure"]?.array ?? []
let kind = d["key.kind"]?.string
switch kind {
case
.Some("source.lang.swift.decl.class"),
.Some("source.lang.swift.decl.extension"),
.Some("source.lang.swift.decl.enum"),
.Some("source.lang.swift.decl.struct"):
self = .Branch([ .Leaf(name), .Branch(try substructure.map { try Term(JSON: $0) ?? die() }) ])
case .Some("source.lang.swift.decl.enumelement"):
fallthrough
case
.Some("source.lang.swift.decl.function.method.instance"),
.Some("source.lang.swift.decl.function.free"):
self = .Branch([ .Leaf(name), .Branch(try substructure.map { try Term(JSON: $0) ?? die() }) ])
case
.Some("source.lang.swift.decl.var.instance"),
.Some("source.lang.swift.decl.var.static"):
self = .Leaf(name)
default:
return nil
}
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 ?? []
self = try Term(JSON: substructure[0]) ?? die()
case let .Dictionary(d) where d["key.kind"]?.string == "source.lang.swift.syntaxtype.comment.mark":
self = .Empty
case .Null:
self = .Empty
default:
return nil
}
} catch _ {
return nil
}
} }
} }
public enum Syntax<Recur, A>: CustomDebugStringConvertible, CustomDocConvertible { public enum Syntax<Recur, A>: CustomDebugStringConvertible, CustomDocConvertible {
case Empty case Empty
case Leaf(A) case Leaf(A)

View File

@ -33,10 +33,10 @@ final class DiffTests: XCTestCase {
} }
} }
private let a = Term(.Leaf("a")) private let a: Term<Info> = Term(.Leaf(.Literal("a", [])))
private let b = Term(.Leaf("b")) private let b: Term<Info> = Term(.Leaf(.Literal("b", [])))
private let c = Term(.Leaf("c")) private let c: Term<Info> = Term(.Leaf(.Literal("c", [])))
private let d = Term(.Leaf("d")) private let d: Term<Info> = Term(.Leaf(.Literal("d", [])))
import Doubt import Doubt
import XCTest import XCTest

View File

@ -9,7 +9,7 @@ final class SwiftTests: XCTestCase {
let structure = Structure(file: file) let structure = Structure(file: file)
let dictionary = toAnyObject(structure.dictionary) let dictionary = toAnyObject(structure.dictionary)
print(JSON(object: dictionary).map { Term(path: path, JSON: $0) }) // print(JSON(object: dictionary).map { Term<String>(path: path, JSON: $0) })
} }
} }

View File

@ -3,7 +3,91 @@ import SourceKittenFramework
let arguments = BoundsCheckedArray(array: Process.arguments) let arguments = BoundsCheckedArray(array: Process.arguments)
extension Term { public protocol StringConvertible {
init(string: String)
}
extension String: StringConvertible {
public init(string: String) {
self = string
}
}
private struct Bail: ErrorType {}
extension Term where A: StringConvertible {
/// Constructs a Term representing `JSON`.
init?(JSON: Doubt.JSON) {
func bail<B>() throws -> B {
throw Bail()
}
do {
switch JSON {
case let .Dictionary(d) where d["key.name"] != nil:
let name = d["key.name"]?.string ?? ""
let substructure = d["key.substructure"]?.array ?? []
let kind = d["key.kind"]?.string
switch kind {
case
.Some("source.lang.swift.decl.class"),
.Some("source.lang.swift.decl.extension"),
.Some("source.lang.swift.decl.enum"),
.Some("source.lang.swift.decl.struct"):
self = .Branch([ .Leaf(A(string: name)), .Branch(try substructure.map { try Term(JSON: $0) ?? bail() }) ])
case .Some("source.lang.swift.decl.enumelement"):
fallthrough
case
.Some("source.lang.swift.decl.function.method.instance"),
.Some("source.lang.swift.decl.function.free"):
self = .Branch([ .Leaf(A(string: name)), .Branch(try substructure.map { try Term(JSON: $0) ?? bail() }) ])
case
.Some("source.lang.swift.decl.var.instance"),
.Some("source.lang.swift.decl.var.static"):
self = .Leaf(A(string: name))
default:
return nil
}
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 ?? []
self = try Term(JSON: substructure[0]) ?? bail()
case let .Dictionary(d) where d["key.kind"]?.string == "source.lang.swift.syntaxtype.comment.mark":
self = .Empty
case .Null:
self = .Empty
default:
return nil
}
} catch _ {
return nil
}
}
/// Constructs a Term representing the `JSON` in a file at `path`.
init?(path: String, JSON: Doubt.JSON) {
func bail<B>() throws -> B {
throw Bail()
}
do {
switch JSON.dictionary?["key.substructure"] {
case let .Some(.Array(a)):
self = .Roll(.Branch(try a.map { try Term(JSON: $0) ?? bail() }))
default:
return nil
}
} catch _ {
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 self = .Empty
@ -18,6 +102,6 @@ extension Term {
} }
} }
if let a = arguments[1].flatMap(Term.init), b = arguments[2].flatMap(Term.init) { if let a = arguments[1].flatMap(Term<Info>.init), b = arguments[2].flatMap(Term<Info>.init) {
print(String(reflecting: Diff(a, b))) print(String(reflecting: Diff(a, b)))
} }