mirror of
https://github.com/github/semantic.git
synced 2024-12-30 18:36:27 +03:00
🔥 the prototype.
This commit is contained in:
parent
32f562eea0
commit
44d437ccdb
6
prototype/.gitignore
vendored
6
prototype/.gitignore
vendored
@ -1,6 +0,0 @@
|
||||
.DS_Store
|
||||
xcuserdata
|
||||
*.xcuserdatad
|
||||
*.xccheckout
|
||||
*.mode*
|
||||
*.pbxuser
|
@ -1,801 +0,0 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
D18FF8821BD0404700D18F2D /* Madness.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D18FF8811BD0404700D18F2D /* Madness.framework */; };
|
||||
D1F5FE201BDE891F0048BAE4 /* JSONParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1F5FE1F1BDE891F0048BAE4 /* JSONParserTests.swift */; };
|
||||
D1F5FE211BDE9C450048BAE4 /* JSONLeaf.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1A0934C1BD188F5005A6326 /* JSONLeaf.swift */; };
|
||||
D1F5FE221BDE9CC10048BAE4 /* JSONParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1A0934A1BD188CA005A6326 /* JSONParser.swift */; };
|
||||
D40B89C41BC319070078E098 /* Matrix.swift in Sources */ = {isa = PBXBuildFile; fileRef = D40B89C31BC319070078E098 /* Matrix.swift */; };
|
||||
D40B89C81BC439000078E098 /* Assertions.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D40B89C71BC439000078E098 /* Assertions.framework */; };
|
||||
D40D72541BCEEB1F001B7A9E /* InterpreterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D40D72531BCEEB1F001B7A9E /* InterpreterTests.swift */; };
|
||||
D40D72561BCFF360001B7A9E /* Interpreter.swift in Sources */ = {isa = PBXBuildFile; fileRef = D40D72551BCFF360001B7A9E /* Interpreter.swift */; };
|
||||
D40D725A1BD15321001B7A9E /* SwiftCheck.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D40D72591BD15321001B7A9E /* SwiftCheck.framework */; };
|
||||
D40D725C1BD15417001B7A9E /* RangedTerm.swift in Sources */ = {isa = PBXBuildFile; fileRef = D40D725B1BD15417001B7A9E /* RangedTerm.swift */; };
|
||||
D40D725E1BD1826C001B7A9E /* TermTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D40D725D1BD1826C001B7A9E /* TermTests.swift */; };
|
||||
D40D72601BD19E69001B7A9E /* DiffTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D40D725F1BD19E69001B7A9E /* DiffTests.swift */; };
|
||||
D40D72621BD1A07E001B7A9E /* UnannotatedTerm.swift in Sources */ = {isa = PBXBuildFile; fileRef = D40D72611BD1A07E001B7A9E /* UnannotatedTerm.swift */; };
|
||||
D40D72641BD1A0C1001B7A9E /* RangedDiff.swift in Sources */ = {isa = PBXBuildFile; fileRef = D40D72631BD1A0C1001B7A9E /* RangedDiff.swift */; };
|
||||
D42F096B1BCCC41600B95610 /* Either.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D42F09671BCCC41600B95610 /* Either.framework */; };
|
||||
D42F096C1BCCC41600B95610 /* Memo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D42F09681BCCC41600B95610 /* Memo.framework */; };
|
||||
D42F096D1BCCC41600B95610 /* Prelude.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D42F09691BCCC41600B95610 /* Prelude.framework */; };
|
||||
D42F096E1BCCC41600B95610 /* Stream.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D42F096A1BCCC41600B95610 /* Stream.framework */; };
|
||||
D42F09771BCCC5DC00B95610 /* Either.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D42F09671BCCC41600B95610 /* Either.framework */; };
|
||||
D42F09781BCCC5DC00B95610 /* Memo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D42F09681BCCC41600B95610 /* Memo.framework */; };
|
||||
D42F09791BCCC5DC00B95610 /* Prelude.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D42F09691BCCC41600B95610 /* Prelude.framework */; };
|
||||
D42F097A1BCCC5DC00B95610 /* Stream.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D42F096A1BCCC41600B95610 /* Stream.framework */; };
|
||||
D42F097C1BCE914A00B95610 /* Cofree.swift in Sources */ = {isa = PBXBuildFile; fileRef = D42F097B1BCE914A00B95610 /* Cofree.swift */; };
|
||||
D42F097E1BCEAEDA00B95610 /* Operation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D42F097D1BCEAEDA00B95610 /* Operation.swift */; };
|
||||
D42F09801BCECB7900B95610 /* TermType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D42F097F1BCECB7900B95610 /* TermType.swift */; };
|
||||
D432D4711BA9AC0B00F3FABC /* SESTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D432D4701BA9AC0B00F3FABC /* SESTests.swift */; };
|
||||
D4413FEF1BB06D4C00E3C3C1 /* Dictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4413FEE1BB06D4C00E3C3C1 /* Dictionary.swift */; };
|
||||
D4413FF11BB08FDC00E3C3C1 /* JSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4413FF01BB08FDC00E3C3C1 /* JSON.swift */; };
|
||||
D45A36C91BBC667D00BE3DDE /* Categorizable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D45A36C81BBC667D00BE3DDE /* Categorizable.swift */; };
|
||||
D46452C71BE7C6C800D7D26E /* Argument.swift in Sources */ = {isa = PBXBuildFile; fileRef = D46452C61BE7C6C800D7D26E /* Argument.swift */; };
|
||||
D46452C91BE7EB2500D7D26E /* Unified.swift in Sources */ = {isa = PBXBuildFile; fileRef = D46452C81BE7EB2500D7D26E /* Unified.swift */; };
|
||||
D46452D81BEA731800D7D26E /* Location.swift in Sources */ = {isa = PBXBuildFile; fileRef = D46452D71BEA731800D7D26E /* Location.swift */; };
|
||||
D464530C1BEBB1F900D7D26E /* LocationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D464530B1BEBB1F900D7D26E /* LocationTests.swift */; };
|
||||
D49FCBC41BBEF98E00C5E9C3 /* Free.swift in Sources */ = {isa = PBXBuildFile; fileRef = D49FCBC31BBEF98E00C5E9C3 /* Free.swift */; };
|
||||
D49FCBC61BBF214300C5E9C3 /* Patch.swift in Sources */ = {isa = PBXBuildFile; fileRef = D49FCBC51BBF214300C5E9C3 /* Patch.swift */; };
|
||||
D49FCBC81BBF2C4300C5E9C3 /* Algorithm.swift in Sources */ = {isa = PBXBuildFile; fileRef = D49FCBC71BBF2C4300C5E9C3 /* Algorithm.swift */; };
|
||||
D4AAE50E1B5AE22E004E581F /* Doubt.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D4AAE4FD1B5AE22E004E581F /* Doubt.framework */; };
|
||||
D4AAE5401B5AE2D0004E581F /* RangeReplaceableCollectionType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4AAE5351B5AE2D0004E581F /* RangeReplaceableCollectionType.swift */; };
|
||||
D4AAE5471B5AE2D0004E581F /* Optional.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4AAE53C1B5AE2D0004E581F /* Optional.swift */; };
|
||||
D4AAE5491B5AE2D0004E581F /* StringLiteralConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4AAE53E1B5AE2D0004E581F /* StringLiteralConvertible.swift */; };
|
||||
D4AAE54A1B5AE2D0004E581F /* Syntax.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4AAE53F1B5AE2D0004E581F /* Syntax.swift */; };
|
||||
D4DF96ED1BC46B630040F41F /* SES.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4DF96EC1BC46B630040F41F /* SES.swift */; };
|
||||
D4DF970C1BC5DF9E0040F41F /* BoundsCheckedArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = D435B7521BB31BBC000902F6 /* BoundsCheckedArray.swift */; };
|
||||
D4FB2CD61BDEBC9000B3CCE0 /* Doubt.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D4AAE4FD1B5AE22E004E581F /* Doubt.framework */; };
|
||||
D4FB2CD71BDEBC9D00B3CCE0 /* Either.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D42F09671BCCC41600B95610 /* Either.framework */; };
|
||||
D4FB2CD81BDEBC9D00B3CCE0 /* Memo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D42F09681BCCC41600B95610 /* Memo.framework */; };
|
||||
D4FB2CD91BDEBC9D00B3CCE0 /* Prelude.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D42F09691BCCC41600B95610 /* Prelude.framework */; };
|
||||
D4FB2CDA1BDEBC9D00B3CCE0 /* Stream.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D42F096A1BCCC41600B95610 /* Stream.framework */; };
|
||||
D4FB2CDC1BDEBCCD00B3CCE0 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4FB2CDB1BDEBCCD00B3CCE0 /* main.swift */; };
|
||||
D4FB2CE01BDEBD1C00B3CCE0 /* libruntime.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D4FB2CDE1BDEBD1C00B3CCE0 /* libruntime.a */; };
|
||||
D4FB2CF81BE1560400B3CCE0 /* TSNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4FB2CF71BE1560400B3CCE0 /* TSNode.swift */; };
|
||||
D4FB2CFA1BE28F6D00B3CCE0 /* parser.c in Sources */ = {isa = PBXBuildFile; fileRef = D4FB2CF91BE28F6D00B3CCE0 /* parser.c */; };
|
||||
D4FB2CFC1BE292BB00B3CCE0 /* parser.c in Sources */ = {isa = PBXBuildFile; fileRef = D4FB2CFB1BE292BB00B3CCE0 /* parser.c */; };
|
||||
D4FB2D021BE2943A00B3CCE0 /* Info.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4FB2D011BE2943A00B3CCE0 /* Info.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
D4AAE50F1B5AE22E004E581F /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = D4AAE4F41B5AE22E004E581F /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = D4AAE4FC1B5AE22E004E581F;
|
||||
remoteInfo = Doubt;
|
||||
};
|
||||
D4FB2CE31BDEBD4200B3CCE0 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = D4AAE4F41B5AE22E004E581F /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = D485A7841BDEB6C5003A17B6;
|
||||
remoteInfo = runtime;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
D18FF8811BD0404700D18F2D /* Madness.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Madness.framework; path = "../../../../Library/Developer/Xcode/DerivedData/Doubt-gibmbsxjgxflybarnuoisglmgdll/Build/Products/Debug/Madness.framework"; sourceTree = "<group>"; };
|
||||
D1A0934A1BD188CA005A6326 /* JSONParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSONParser.swift; sourceTree = "<group>"; };
|
||||
D1A0934C1BD188F5005A6326 /* JSONLeaf.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSONLeaf.swift; sourceTree = "<group>"; };
|
||||
D1F5FE1F1BDE891F0048BAE4 /* JSONParserTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSONParserTests.swift; sourceTree = "<group>"; };
|
||||
D40B89C31BC319070078E098 /* Matrix.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Matrix.swift; sourceTree = "<group>"; };
|
||||
D40B89C71BC439000078E098 /* Assertions.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Assertions.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
D40D72531BCEEB1F001B7A9E /* InterpreterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InterpreterTests.swift; sourceTree = "<group>"; };
|
||||
D40D72551BCFF360001B7A9E /* Interpreter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Interpreter.swift; sourceTree = "<group>"; };
|
||||
D40D72591BD15321001B7A9E /* SwiftCheck.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = SwiftCheck.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
D40D725B1BD15417001B7A9E /* RangedTerm.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RangedTerm.swift; sourceTree = "<group>"; };
|
||||
D40D725D1BD1826C001B7A9E /* TermTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TermTests.swift; sourceTree = "<group>"; };
|
||||
D40D725F1BD19E69001B7A9E /* DiffTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DiffTests.swift; sourceTree = "<group>"; };
|
||||
D40D72611BD1A07E001B7A9E /* UnannotatedTerm.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnannotatedTerm.swift; sourceTree = "<group>"; };
|
||||
D40D72631BD1A0C1001B7A9E /* RangedDiff.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RangedDiff.swift; sourceTree = "<group>"; };
|
||||
D42F09671BCCC41600B95610 /* Either.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Either.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
D42F09681BCCC41600B95610 /* Memo.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Memo.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
D42F09691BCCC41600B95610 /* Prelude.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Prelude.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
D42F096A1BCCC41600B95610 /* Stream.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Stream.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
D42F097B1BCE914A00B95610 /* Cofree.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Cofree.swift; sourceTree = "<group>"; };
|
||||
D42F097D1BCEAEDA00B95610 /* Operation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Operation.swift; sourceTree = "<group>"; };
|
||||
D42F097F1BCECB7900B95610 /* TermType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TermType.swift; sourceTree = "<group>"; };
|
||||
D432D4701BA9AC0B00F3FABC /* SESTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SESTests.swift; sourceTree = "<group>"; };
|
||||
D435B7521BB31BBC000902F6 /* BoundsCheckedArray.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BoundsCheckedArray.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>"; };
|
||||
D45A36C81BBC667D00BE3DDE /* Categorizable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Categorizable.swift; sourceTree = "<group>"; };
|
||||
D46452C61BE7C6C800D7D26E /* Argument.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Argument.swift; sourceTree = "<group>"; };
|
||||
D46452C81BE7EB2500D7D26E /* Unified.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Unified.swift; sourceTree = "<group>"; };
|
||||
D46452D71BEA731800D7D26E /* Location.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Location.swift; sourceTree = "<group>"; };
|
||||
D464530B1BEBB1F900D7D26E /* LocationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocationTests.swift; sourceTree = "<group>"; };
|
||||
D49FCBC31BBEF98E00C5E9C3 /* Free.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Free.swift; sourceTree = "<group>"; };
|
||||
D49FCBC51BBF214300C5E9C3 /* Patch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Patch.swift; sourceTree = "<group>"; };
|
||||
D49FCBC71BBF2C4300C5E9C3 /* Algorithm.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Algorithm.swift; sourceTree = "<group>"; };
|
||||
D4AAE4FD1B5AE22E004E581F /* Doubt.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Doubt.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
D4AAE5011B5AE22E004E581F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
D4AAE5351B5AE2D0004E581F /* RangeReplaceableCollectionType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RangeReplaceableCollectionType.swift; sourceTree = "<group>"; };
|
||||
D4AAE53C1B5AE2D0004E581F /* Optional.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Optional.swift; sourceTree = "<group>"; };
|
||||
D4AAE53E1B5AE2D0004E581F /* StringLiteralConvertible.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StringLiteralConvertible.swift; sourceTree = "<group>"; };
|
||||
D4AAE53F1B5AE2D0004E581F /* Syntax.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Syntax.swift; sourceTree = "<group>"; };
|
||||
D4DF96EC1BC46B630040F41F /* SES.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SES.swift; sourceTree = "<group>"; };
|
||||
D4DF96F01BC54C970040F41F /* Doubt.modulemap */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = "sourcecode.module-map"; path = Doubt.modulemap; sourceTree = "<group>"; };
|
||||
D4FB2CC91BDEBC6300B3CCE0 /* doubt-difftool.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "doubt-difftool.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
D4FB2CDB1BDEBCCD00B3CCE0 /* main.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
|
||||
D4FB2CDE1BDEBD1C00B3CCE0 /* libruntime.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libruntime.a; path = "External/tree-sitter/out/Release/libruntime.a"; sourceTree = SOURCE_ROOT; };
|
||||
D4FB2CE51BDEBE7900B3CCE0 /* doubt-difftool-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "doubt-difftool-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
D4FB2CF71BE1560400B3CCE0 /* TSNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TSNode.swift; sourceTree = "<group>"; };
|
||||
D4FB2CF91BE28F6D00B3CCE0 /* parser.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = parser.c; path = "External/tree-sitter-javascript/src/parser.c"; sourceTree = SOURCE_ROOT; };
|
||||
D4FB2CFB1BE292BB00B3CCE0 /* parser.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = parser.c; path = "External/tree-sitter-c/src/parser.c"; sourceTree = SOURCE_ROOT; };
|
||||
D4FB2D011BE2943A00B3CCE0 /* Info.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Info.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
D4AAE4F91B5AE22E004E581F /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
D18FF8821BD0404700D18F2D /* Madness.framework in Frameworks */,
|
||||
D42F096B1BCCC41600B95610 /* Either.framework in Frameworks */,
|
||||
D42F096C1BCCC41600B95610 /* Memo.framework in Frameworks */,
|
||||
D42F096D1BCCC41600B95610 /* Prelude.framework in Frameworks */,
|
||||
D42F096E1BCCC41600B95610 /* Stream.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
D4AAE50A1B5AE22E004E581F /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
D40D725A1BD15321001B7A9E /* SwiftCheck.framework in Frameworks */,
|
||||
D40B89C81BC439000078E098 /* Assertions.framework in Frameworks */,
|
||||
D4AAE50E1B5AE22E004E581F /* Doubt.framework in Frameworks */,
|
||||
D42F09771BCCC5DC00B95610 /* Either.framework in Frameworks */,
|
||||
D42F09781BCCC5DC00B95610 /* Memo.framework in Frameworks */,
|
||||
D42F09791BCCC5DC00B95610 /* Prelude.framework in Frameworks */,
|
||||
D42F097A1BCCC5DC00B95610 /* Stream.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
D4FB2CC61BDEBC6300B3CCE0 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
D4FB2CD61BDEBC9000B3CCE0 /* Doubt.framework in Frameworks */,
|
||||
D4FB2CD71BDEBC9D00B3CCE0 /* Either.framework in Frameworks */,
|
||||
D4FB2CD81BDEBC9D00B3CCE0 /* Memo.framework in Frameworks */,
|
||||
D4FB2CE01BDEBD1C00B3CCE0 /* libruntime.a in Frameworks */,
|
||||
D4FB2CD91BDEBC9D00B3CCE0 /* Prelude.framework in Frameworks */,
|
||||
D4FB2CDA1BDEBC9D00B3CCE0 /* Stream.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
D4AAE4F31B5AE22E004E581F = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D4AAE4FF1B5AE22E004E581F /* Doubt */,
|
||||
D4AAE5111B5AE22E004E581F /* DoubtTests */,
|
||||
D4FB2CCA1BDEBC6300B3CCE0 /* doubt-difftool */,
|
||||
D4AAE4FE1B5AE22E004E581F /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D4AAE4FE1B5AE22E004E581F /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D4AAE4FD1B5AE22E004E581F /* Doubt.framework */,
|
||||
D4AAE50D1B5AE22E004E581F /* DoubtTests.xctest */,
|
||||
D4FB2CC91BDEBC6300B3CCE0 /* doubt-difftool.app */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D4AAE4FF1B5AE22E004E581F /* Doubt */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D4AAE5351B5AE2D0004E581F /* RangeReplaceableCollectionType.swift */,
|
||||
D4AAE53C1B5AE2D0004E581F /* Optional.swift */,
|
||||
D4AAE53E1B5AE2D0004E581F /* StringLiteralConvertible.swift */,
|
||||
D4AAE53F1B5AE2D0004E581F /* Syntax.swift */,
|
||||
D4413FEE1BB06D4C00E3C3C1 /* Dictionary.swift */,
|
||||
D4413FF01BB08FDC00E3C3C1 /* JSON.swift */,
|
||||
D40B89C31BC319070078E098 /* Matrix.swift */,
|
||||
D45A36C81BBC667D00BE3DDE /* Categorizable.swift */,
|
||||
D49FCBC31BBEF98E00C5E9C3 /* Free.swift */,
|
||||
D42F097B1BCE914A00B95610 /* Cofree.swift */,
|
||||
D49FCBC51BBF214300C5E9C3 /* Patch.swift */,
|
||||
D49FCBC71BBF2C4300C5E9C3 /* Algorithm.swift */,
|
||||
D42F097D1BCEAEDA00B95610 /* Operation.swift */,
|
||||
D4DF96EC1BC46B630040F41F /* SES.swift */,
|
||||
D435B7521BB31BBC000902F6 /* BoundsCheckedArray.swift */,
|
||||
D42F097F1BCECB7900B95610 /* TermType.swift */,
|
||||
D1A0934A1BD188CA005A6326 /* JSONParser.swift */,
|
||||
D1A0934C1BD188F5005A6326 /* JSONLeaf.swift */,
|
||||
D40D72551BCFF360001B7A9E /* Interpreter.swift */,
|
||||
D46452D71BEA731800D7D26E /* Location.swift */,
|
||||
D4AAE5001B5AE22E004E581F /* Supporting Files */,
|
||||
);
|
||||
path = Doubt;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D4AAE5001B5AE22E004E581F /* Supporting Files */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D4AAE5011B5AE22E004E581F /* Info.plist */,
|
||||
D4DF96F01BC54C970040F41F /* Doubt.modulemap */,
|
||||
D42F09671BCCC41600B95610 /* Either.framework */,
|
||||
D42F09681BCCC41600B95610 /* Memo.framework */,
|
||||
D18FF8811BD0404700D18F2D /* Madness.framework */,
|
||||
D42F09691BCCC41600B95610 /* Prelude.framework */,
|
||||
D42F096A1BCCC41600B95610 /* Stream.framework */,
|
||||
);
|
||||
name = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D4AAE5111B5AE22E004E581F /* DoubtTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D1F5FE1F1BDE891F0048BAE4 /* JSONParserTests.swift */,
|
||||
D432D4701BA9AC0B00F3FABC /* SESTests.swift */,
|
||||
D40D72531BCEEB1F001B7A9E /* InterpreterTests.swift */,
|
||||
D40D725B1BD15417001B7A9E /* RangedTerm.swift */,
|
||||
D40D72611BD1A07E001B7A9E /* UnannotatedTerm.swift */,
|
||||
D40D72631BD1A0C1001B7A9E /* RangedDiff.swift */,
|
||||
D40D725D1BD1826C001B7A9E /* TermTests.swift */,
|
||||
D40D725F1BD19E69001B7A9E /* DiffTests.swift */,
|
||||
D464530B1BEBB1F900D7D26E /* LocationTests.swift */,
|
||||
D4AAE5141B5AE22E004E581F /* Info.plist */,
|
||||
D40B89C71BC439000078E098 /* Assertions.framework */,
|
||||
D40D72591BD15321001B7A9E /* SwiftCheck.framework */,
|
||||
);
|
||||
path = DoubtTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D4FB2CCA1BDEBC6300B3CCE0 /* doubt-difftool */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D4FB2CDB1BDEBCCD00B3CCE0 /* main.swift */,
|
||||
D4FB2D011BE2943A00B3CCE0 /* Info.swift */,
|
||||
D4FB2CF71BE1560400B3CCE0 /* TSNode.swift */,
|
||||
D46452C61BE7C6C800D7D26E /* Argument.swift */,
|
||||
D46452C81BE7EB2500D7D26E /* Unified.swift */,
|
||||
D4FB2CE51BDEBE7900B3CCE0 /* doubt-difftool-Bridging-Header.h */,
|
||||
D4FB2D001BE2936800B3CCE0 /* Parsers */,
|
||||
D4FB2CDE1BDEBD1C00B3CCE0 /* libruntime.a */,
|
||||
);
|
||||
path = "doubt-difftool";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D4FB2CFE1BE2934A00B3CCE0 /* C */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D4FB2CFB1BE292BB00B3CCE0 /* parser.c */,
|
||||
);
|
||||
name = C;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D4FB2CFF1BE2934E00B3CCE0 /* JavaScript */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D4FB2CF91BE28F6D00B3CCE0 /* parser.c */,
|
||||
);
|
||||
name = JavaScript;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D4FB2D001BE2936800B3CCE0 /* Parsers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D4FB2CFF1BE2934E00B3CCE0 /* JavaScript */,
|
||||
D4FB2CFE1BE2934A00B3CCE0 /* C */,
|
||||
);
|
||||
name = Parsers;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
D4AAE4FA1B5AE22E004E581F /* Headers */ = {
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXHeadersBuildPhase section */
|
||||
|
||||
/* Begin PBXLegacyTarget section */
|
||||
D485A7841BDEB6C5003A17B6 /* runtime */ = {
|
||||
isa = PBXLegacyTarget;
|
||||
buildArgumentsString = "$(ACTION)";
|
||||
buildConfigurationList = D485A7871BDEB6C5003A17B6 /* Build configuration list for PBXLegacyTarget "runtime" */;
|
||||
buildPhases = (
|
||||
);
|
||||
buildToolPath = "$(SRCROOT)/script/tree-sitter.sh";
|
||||
buildWorkingDirectory = "$(SRCROOT)/External/tree-sitter";
|
||||
dependencies = (
|
||||
);
|
||||
name = runtime;
|
||||
passBuildSettingsInEnvironment = 1;
|
||||
productName = "tree-sitter";
|
||||
};
|
||||
/* End PBXLegacyTarget section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
D4AAE4FC1B5AE22E004E581F /* Doubt */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = D4AAE5171B5AE22E004E581F /* Build configuration list for PBXNativeTarget "Doubt" */;
|
||||
buildPhases = (
|
||||
D4AAE4F81B5AE22E004E581F /* Sources */,
|
||||
D4AAE4F91B5AE22E004E581F /* Frameworks */,
|
||||
D4AAE4FA1B5AE22E004E581F /* Headers */,
|
||||
D4AAE4FB1B5AE22E004E581F /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = Doubt;
|
||||
productName = Doubt;
|
||||
productReference = D4AAE4FD1B5AE22E004E581F /* Doubt.framework */;
|
||||
productType = "com.apple.product-type.framework";
|
||||
};
|
||||
D4AAE50C1B5AE22E004E581F /* DoubtTests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = D4AAE51A1B5AE22E004E581F /* Build configuration list for PBXNativeTarget "DoubtTests" */;
|
||||
buildPhases = (
|
||||
D4AAE5091B5AE22E004E581F /* Sources */,
|
||||
D4AAE50A1B5AE22E004E581F /* Frameworks */,
|
||||
D4AAE50B1B5AE22E004E581F /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
D4AAE5101B5AE22E004E581F /* PBXTargetDependency */,
|
||||
);
|
||||
name = DoubtTests;
|
||||
productName = DoubtTests;
|
||||
productReference = D4AAE50D1B5AE22E004E581F /* DoubtTests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
};
|
||||
D4FB2CC81BDEBC6300B3CCE0 /* doubt-difftool */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = D4FB2CD31BDEBC6300B3CCE0 /* Build configuration list for PBXNativeTarget "doubt-difftool" */;
|
||||
buildPhases = (
|
||||
D4FB2CC51BDEBC6300B3CCE0 /* Sources */,
|
||||
D4FB2CC61BDEBC6300B3CCE0 /* Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
D4FB2CE41BDEBD4200B3CCE0 /* PBXTargetDependency */,
|
||||
);
|
||||
name = "doubt-difftool";
|
||||
productName = "doubt-difftool";
|
||||
productReference = D4FB2CC91BDEBC6300B3CCE0 /* doubt-difftool.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
D4AAE4F41B5AE22E004E581F /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 0710;
|
||||
LastUpgradeCheck = 0710;
|
||||
ORGANIZATIONNAME = GitHub;
|
||||
TargetAttributes = {
|
||||
D485A7841BDEB6C5003A17B6 = {
|
||||
CreatedOnToolsVersion = 7.1;
|
||||
};
|
||||
D4AAE4FC1B5AE22E004E581F = {
|
||||
CreatedOnToolsVersion = 7.0;
|
||||
};
|
||||
D4AAE50C1B5AE22E004E581F = {
|
||||
CreatedOnToolsVersion = 7.0;
|
||||
};
|
||||
D4FB2CC81BDEBC6300B3CCE0 = {
|
||||
CreatedOnToolsVersion = 7.1;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = D4AAE4F71B5AE22E004E581F /* Build configuration list for PBXProject "Doubt" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = D4AAE4F31B5AE22E004E581F;
|
||||
productRefGroup = D4AAE4FE1B5AE22E004E581F /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
D4AAE4FC1B5AE22E004E581F /* Doubt */,
|
||||
D4AAE50C1B5AE22E004E581F /* DoubtTests */,
|
||||
D4FB2CC81BDEBC6300B3CCE0 /* doubt-difftool */,
|
||||
D485A7841BDEB6C5003A17B6 /* runtime */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
D4AAE4FB1B5AE22E004E581F /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
D4AAE50B1B5AE22E004E581F /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
D4AAE4F81B5AE22E004E581F /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
D40D72561BCFF360001B7A9E /* Interpreter.swift in Sources */,
|
||||
D4AAE5471B5AE2D0004E581F /* Optional.swift in Sources */,
|
||||
D46452D81BEA731800D7D26E /* Location.swift in Sources */,
|
||||
D4413FEF1BB06D4C00E3C3C1 /* Dictionary.swift in Sources */,
|
||||
D42F097E1BCEAEDA00B95610 /* Operation.swift in Sources */,
|
||||
D45A36C91BBC667D00BE3DDE /* Categorizable.swift in Sources */,
|
||||
D4DF96ED1BC46B630040F41F /* SES.swift in Sources */,
|
||||
D1F5FE221BDE9CC10048BAE4 /* JSONParser.swift in Sources */,
|
||||
D42F09801BCECB7900B95610 /* TermType.swift in Sources */,
|
||||
D4AAE5401B5AE2D0004E581F /* RangeReplaceableCollectionType.swift in Sources */,
|
||||
D49FCBC41BBEF98E00C5E9C3 /* Free.swift in Sources */,
|
||||
D42F097C1BCE914A00B95610 /* Cofree.swift in Sources */,
|
||||
D4AAE54A1B5AE2D0004E581F /* Syntax.swift in Sources */,
|
||||
D49FCBC61BBF214300C5E9C3 /* Patch.swift in Sources */,
|
||||
D49FCBC81BBF2C4300C5E9C3 /* Algorithm.swift in Sources */,
|
||||
D4DF970C1BC5DF9E0040F41F /* BoundsCheckedArray.swift in Sources */,
|
||||
D40B89C41BC319070078E098 /* Matrix.swift in Sources */,
|
||||
D4AAE5491B5AE2D0004E581F /* StringLiteralConvertible.swift in Sources */,
|
||||
D4413FF11BB08FDC00E3C3C1 /* JSON.swift in Sources */,
|
||||
D1F5FE211BDE9C450048BAE4 /* JSONLeaf.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
D4AAE5091B5AE22E004E581F /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
D40D72641BD1A0C1001B7A9E /* RangedDiff.swift in Sources */,
|
||||
D40D725E1BD1826C001B7A9E /* TermTests.swift in Sources */,
|
||||
D1F5FE201BDE891F0048BAE4 /* JSONParserTests.swift in Sources */,
|
||||
D40D72601BD19E69001B7A9E /* DiffTests.swift in Sources */,
|
||||
D432D4711BA9AC0B00F3FABC /* SESTests.swift in Sources */,
|
||||
D40D72621BD1A07E001B7A9E /* UnannotatedTerm.swift in Sources */,
|
||||
D40D72541BCEEB1F001B7A9E /* InterpreterTests.swift in Sources */,
|
||||
D464530C1BEBB1F900D7D26E /* LocationTests.swift in Sources */,
|
||||
D40D725C1BD15417001B7A9E /* RangedTerm.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
D4FB2CC51BDEBC6300B3CCE0 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
D46452C71BE7C6C800D7D26E /* Argument.swift in Sources */,
|
||||
D4FB2CF81BE1560400B3CCE0 /* TSNode.swift in Sources */,
|
||||
D4FB2CFA1BE28F6D00B3CCE0 /* parser.c in Sources */,
|
||||
D4FB2CFC1BE292BB00B3CCE0 /* parser.c in Sources */,
|
||||
D4FB2D021BE2943A00B3CCE0 /* Info.swift in Sources */,
|
||||
D46452C91BE7EB2500D7D26E /* Unified.swift in Sources */,
|
||||
D4FB2CDC1BDEBCCD00B3CCE0 /* main.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
D4AAE5101B5AE22E004E581F /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = D4AAE4FC1B5AE22E004E581F /* Doubt */;
|
||||
targetProxy = D4AAE50F1B5AE22E004E581F /* PBXContainerItemProxy */;
|
||||
};
|
||||
D4FB2CE41BDEBD4200B3CCE0 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = D485A7841BDEB6C5003A17B6 /* runtime */;
|
||||
targetProxy = D4FB2CE31BDEBD4200B3CCE0 /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
D485A7851BDEB6C5003A17B6 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
DEBUGGING_SYMBOLS = YES;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
OTHER_CFLAGS = "";
|
||||
OTHER_LDFLAGS = "";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
D485A7861BDEB6C5003A17B6 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
OTHER_CFLAGS = "";
|
||||
OTHER_LDFLAGS = "";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
D4AAE5151B5AE22E004E581F /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.10;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = macosx;
|
||||
SWIFT_INSTALL_OBJC_HEADER = NO;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
D4AAE5161B5AE22E004E581F /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.10;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = macosx;
|
||||
SWIFT_DISABLE_SAFETY_CHECKS = YES;
|
||||
SWIFT_INSTALL_OBJC_HEADER = NO;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
D4AAE5181B5AE22E004E581F /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEFINES_MODULE = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
EMBEDDED_CONTENT_CONTAINS_SWIFT = YES;
|
||||
FRAMEWORK_VERSION = A;
|
||||
INFOPLIST_FILE = Doubt/Info.plist;
|
||||
INSTALL_PATH = "@rpath";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
|
||||
MODULEMAP_FILE = Doubt/Doubt.modulemap;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.github.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
D4AAE5191B5AE22E004E581F /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEFINES_MODULE = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
EMBEDDED_CONTENT_CONTAINS_SWIFT = YES;
|
||||
FRAMEWORK_VERSION = A;
|
||||
INFOPLIST_FILE = Doubt/Info.plist;
|
||||
INSTALL_PATH = "@rpath";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
|
||||
MODULEMAP_FILE = Doubt/Doubt.modulemap;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.github.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
D4AAE51B1B5AE22E004E581F /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(DEVELOPER_FRAMEWORKS_DIR)",
|
||||
"$(inherited)",
|
||||
);
|
||||
INFOPLIST_FILE = DoubtTests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.github.DoubtTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
D4AAE51C1B5AE22E004E581F /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(DEVELOPER_FRAMEWORKS_DIR)",
|
||||
"$(inherited)",
|
||||
);
|
||||
INFOPLIST_FILE = DoubtTests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.github.DoubtTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
D4FB2CD41BDEBC6300B3CCE0 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
HEADER_SEARCH_PATHS = "$(SRCROOT)/External/tree-sitter/include/";
|
||||
INFOPLIST_FILE = "doubt-difftool/Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/External/tree-sitter/out/Release",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.11;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.antitypical.doubt-difftool";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "doubt-difftool/doubt-difftool-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
D4FB2CD51BDEBC6300B3CCE0 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
HEADER_SEARCH_PATHS = "$(SRCROOT)/External/tree-sitter/include/";
|
||||
INFOPLIST_FILE = "doubt-difftool/Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/External/tree-sitter/out/Release",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.11;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.antitypical.doubt-difftool";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "doubt-difftool/doubt-difftool-Bridging-Header.h";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
D485A7871BDEB6C5003A17B6 /* Build configuration list for PBXLegacyTarget "runtime" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
D485A7851BDEB6C5003A17B6 /* Debug */,
|
||||
D485A7861BDEB6C5003A17B6 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
D4AAE4F71B5AE22E004E581F /* Build configuration list for PBXProject "Doubt" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
D4AAE5151B5AE22E004E581F /* Debug */,
|
||||
D4AAE5161B5AE22E004E581F /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
D4AAE5171B5AE22E004E581F /* Build configuration list for PBXNativeTarget "Doubt" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
D4AAE5181B5AE22E004E581F /* Debug */,
|
||||
D4AAE5191B5AE22E004E581F /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
D4AAE51A1B5AE22E004E581F /* Build configuration list for PBXNativeTarget "DoubtTests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
D4AAE51B1B5AE22E004E581F /* Debug */,
|
||||
D4AAE51C1B5AE22E004E581F /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
D4FB2CD31BDEBC6300B3CCE0 /* Build configuration list for PBXNativeTarget "doubt-difftool" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
D4FB2CD41BDEBC6300B3CCE0 /* Debug */,
|
||||
D4FB2CD51BDEBC6300B3CCE0 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = D4AAE4F41B5AE22E004E581F /* Project object */;
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:Doubt.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
@ -1,99 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0710"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D4AAE4FC1B5AE22E004E581F"
|
||||
BuildableName = "Doubt.framework"
|
||||
BlueprintName = "Doubt"
|
||||
ReferencedContainer = "container:Doubt.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D4AAE50C1B5AE22E004E581F"
|
||||
BuildableName = "DoubtTests.xctest"
|
||||
BlueprintName = "DoubtTests"
|
||||
ReferencedContainer = "container:Doubt.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D4AAE4FC1B5AE22E004E581F"
|
||||
BuildableName = "Doubt.framework"
|
||||
BlueprintName = "Doubt"
|
||||
ReferencedContainer = "container:Doubt.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D4AAE4FC1B5AE22E004E581F"
|
||||
BuildableName = "Doubt.framework"
|
||||
BlueprintName = "Doubt"
|
||||
ReferencedContainer = "container:Doubt.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D4AAE4FC1B5AE22E004E581F"
|
||||
BuildableName = "Doubt.framework"
|
||||
BlueprintName = "Doubt"
|
||||
ReferencedContainer = "container:Doubt.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
@ -1,105 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0710"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D4FB2CC81BDEBC6300B3CCE0"
|
||||
BuildableName = "doubt-difftool.app"
|
||||
BlueprintName = "doubt-difftool"
|
||||
ReferencedContainer = "container:Doubt.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D4FB2CC81BDEBC6300B3CCE0"
|
||||
BuildableName = "doubt-difftool.app"
|
||||
BlueprintName = "doubt-difftool"
|
||||
ReferencedContainer = "container:Doubt.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "NO"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D4FB2CC81BDEBC6300B3CCE0"
|
||||
BuildableName = "doubt-difftool.app"
|
||||
BlueprintName = "doubt-difftool"
|
||||
ReferencedContainer = "container:Doubt.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<CommandLineArguments>
|
||||
<CommandLineArgument
|
||||
argument = "--unified"
|
||||
isEnabled = "NO">
|
||||
</CommandLineArgument>
|
||||
<CommandLineArgument
|
||||
argument = "https://raw.githubusercontent.com/jquery/jquery/2.1.0/src/effects.js"
|
||||
isEnabled = "YES">
|
||||
</CommandLineArgument>
|
||||
<CommandLineArgument
|
||||
argument = "https://raw.githubusercontent.com/jquery/jquery/2.1.4/src/effects.js"
|
||||
isEnabled = "YES">
|
||||
</CommandLineArgument>
|
||||
</CommandLineArguments>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "NO">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D4FB2CC81BDEBC6300B3CCE0"
|
||||
BuildableName = "doubt-difftool.app"
|
||||
BlueprintName = "doubt-difftool"
|
||||
ReferencedContainer = "container:Doubt.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
@ -1,80 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0710"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D485A7841BDEB6C5003A17B6"
|
||||
BuildableName = "runtime"
|
||||
BlueprintName = "runtime"
|
||||
ReferencedContainer = "container:Doubt.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D485A7841BDEB6C5003A17B6"
|
||||
BuildableName = "runtime"
|
||||
BlueprintName = "runtime"
|
||||
ReferencedContainer = "container:Doubt.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D485A7841BDEB6C5003A17B6"
|
||||
BuildableName = "runtime"
|
||||
BlueprintName = "runtime"
|
||||
ReferencedContainer = "container:Doubt.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
28
prototype/Doubt.xcworkspace/contents.xcworkspacedata
generated
28
prototype/Doubt.xcworkspace/contents.xcworkspacedata
generated
@ -1,28 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "group:Doubt.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:External/Madness/Madness.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:External/Assertions/Assertions.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:External/SwiftCheck/SwiftCheck.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:External/Stream/Stream.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:External/Stream/Carthage/Checkouts/Memo/Memo.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:External/Stream/Carthage/Checkouts/Either/Either.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:External/Stream/Carthage/Checkouts/Prelude/Prelude.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
@ -1,23 +0,0 @@
|
||||
/// The free monad over `Operation`, implementing the language of diffing.
|
||||
///
|
||||
/// As with `Free`, this is “free” in the sense of “unconstrained,” i.e. “the monad induced by `Operation` without extra assumptions.”
|
||||
///
|
||||
/// Where `Operation` models a single diffing strategy, `Algorithm` models the recursive selection of diffing strategies at each node. Thus, a value in `Algorithm` models an algorithm for constructing a value in the type `Result` 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 Algorithm<Term: CofreeType, Result> {
|
||||
/// The type of `Patch`es produced by `Algorithm`s.
|
||||
public typealias Patch = Doubt.Patch<Term>
|
||||
|
||||
/// The type of `Diff`s which `Algorithm`s produce.
|
||||
public typealias Diff = Free<Term.Leaf, (Term.Annotation, Term.Annotation), Patch>
|
||||
|
||||
/// The injection of a value of type `Result` into an `Operation`.
|
||||
///
|
||||
/// Equally, a way to return a result or throw an error during computation, as determined by the type which `Result` is instantiated to, and the specific context in which it is being evaluated.
|
||||
case Pure(Result)
|
||||
|
||||
/// A recursive instantiation of `Operation`, unrolling another iteration of the recursive type.
|
||||
indirect case Roll(Operation<Algorithm, Term, Diff>)
|
||||
}
|
||||
|
||||
|
||||
import Prelude
|
@ -1,16 +0,0 @@
|
||||
public struct BoundsCheckedArray<Element>: CollectionType {
|
||||
public init(array: [Element]) {
|
||||
self.array = array
|
||||
}
|
||||
|
||||
let array: [Element]
|
||||
|
||||
public let startIndex = 0
|
||||
public var endIndex: Int {
|
||||
return array.count
|
||||
}
|
||||
|
||||
public subscript (i: Int) -> Element? {
|
||||
return i < count ? array[i] : nil
|
||||
}
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
/// A type whose values belong to a set of categories.
|
||||
public protocol Categorizable {
|
||||
typealias Category: Hashable
|
||||
|
||||
var categories: Set<Category> { get }
|
||||
}
|
@ -1,227 +0,0 @@
|
||||
/// The cofree comonad over `Syntax`.
|
||||
///
|
||||
/// This is “free” in the sense of “unconstrained” rather than “zero-cost”; it’s the comonad obtained by taking a functor (in this case `Syntax`) and adding the minimum necessary details (the `Annotation` paired with it) to satisfy the comonad laws.
|
||||
///
|
||||
/// This type is dual to `Free`. Where `Free` is inhabited by syntax trees where some terms are replaced with `Annotation`s, `Cofree` is inhabited by syntax trees where all terms are annotated with `Annotation`s. In Doubt, this allows us to e.g. annotate terms with source range information, categorization, etc.
|
||||
public enum Cofree<Leaf, Annotation> {
|
||||
indirect case Unroll(Annotation, Syntax<Cofree, Leaf>)
|
||||
|
||||
public var unwrap: Syntax<Cofree, Leaf> {
|
||||
switch self {
|
||||
case let .Unroll(_, rest):
|
||||
return rest
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public init(_ annotation: Annotation, _ syntax: Syntax<Cofree, Leaf>) {
|
||||
self = .Unroll(annotation, syntax)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - CustomDebugStringConvertible
|
||||
|
||||
extension Cofree: CustomDebugStringConvertible {
|
||||
public var debugDescription: String {
|
||||
return "(\(String(reflecting: extract)), \(String(reflecting: unwrap)))"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Functor
|
||||
|
||||
extension Cofree {
|
||||
public func map<Other>(@noescape transform: Annotation throws -> Other) rethrows -> Cofree<Leaf, Other> {
|
||||
return try .Unroll(transform(extract), unwrap.map { try $0.map(transform) })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Comonad
|
||||
|
||||
extension Cofree {
|
||||
/// Returns the value annotating the syntax tree at this node.
|
||||
public var extract: Annotation {
|
||||
switch self {
|
||||
case let .Unroll(b, _):
|
||||
return b
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a new `Cofree` by recursively applying `transform` to each node, producing the annotations for the copy.
|
||||
public func extend<Other>(transform: Cofree -> Other) -> Cofree<Leaf, Other> {
|
||||
return .Unroll(transform(self), unwrap.map { $0.extend(transform) })
|
||||
}
|
||||
|
||||
/// Returns a new `Cofree` constructed by recursively annotating each subtree with itself.
|
||||
public var duplicate: Cofree<Leaf, Cofree<Leaf, Annotation>> {
|
||||
return extend(id)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Equality
|
||||
|
||||
extension Cofree {
|
||||
public static func equals(annotation annotation: (Annotation, Annotation) -> Bool, leaf: (Leaf, Leaf) -> Bool)(_ left: Cofree, _ right: Cofree) -> Bool {
|
||||
switch (left, right) {
|
||||
case let (.Unroll(a, s), .Unroll(b, t)):
|
||||
return annotation(a, b) && Syntax.equals(leaf: leaf, recur: Cofree.equals(annotation: annotation, leaf: leaf))(s, t)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func == <Leaf: Equatable, Annotation: Equatable> (left: Cofree<Leaf, Annotation>, right: Cofree<Leaf, Annotation>) -> Bool {
|
||||
return Cofree.equals(annotation: ==, leaf: ==)(left, right)
|
||||
}
|
||||
|
||||
|
||||
// MARK: - JSON
|
||||
|
||||
extension Cofree {
|
||||
public func JSON(annotation annotation: Annotation -> Doubt.JSON, leaf: Leaf -> Doubt.JSON) -> Doubt.JSON {
|
||||
return [
|
||||
"extract": annotation(extract),
|
||||
"unwrap": unwrap.JSON(leaf: leaf, recur: { $0.JSON(annotation: annotation, leaf: leaf) })
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
extension Cofree where Leaf: CustomJSONConvertible, Annotation: CustomJSONConvertible {
|
||||
public var JSON: Doubt.JSON {
|
||||
return JSON(annotation: { $0.JSON }, leaf: { $0.JSON })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Categorizable
|
||||
|
||||
extension Cofree where Annotation: Categorizable {
|
||||
var categories: Set<Annotation.Category> {
|
||||
return extract.categories
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - CofreeType
|
||||
|
||||
public protocol CofreeType: TermType {
|
||||
typealias Annotation
|
||||
|
||||
init(_ annotation: Annotation, _ syntax: Syntax<Self, Leaf>)
|
||||
var extract: Annotation { get }
|
||||
}
|
||||
|
||||
extension CofreeType {
|
||||
public static func Introduce(annotation: Annotation)(syntax: Syntax<Self, Leaf>) -> Self {
|
||||
return Self(annotation, syntax)
|
||||
}
|
||||
|
||||
public static func eliminate(term: Self) -> (Annotation, Syntax<Self, Leaf>) {
|
||||
return (term.extract, term.unwrap)
|
||||
}
|
||||
|
||||
|
||||
/// Catamorphism over `CofreeType`s.
|
||||
///
|
||||
/// Folds the tree encoded by the receiver into a single value by recurring top-down through the tree, applying `transform` to leaves, then to branches, and so forth.
|
||||
public func cata<Result>(transform: (Annotation, Syntax<Result, Leaf>) throws -> Result) rethrows -> Result {
|
||||
return try transform(extract, unwrap.map { try $0.cata(transform) })
|
||||
}
|
||||
|
||||
|
||||
/// Constructs a cofree by coiteration.
|
||||
///
|
||||
/// This is an _anamorphism_ (from the Greek “ana,” “upwards”; compare “anabolism”), a generalization of unfolds over regular trees (and datatypes isomorphic to them). The initial seed is used as the annotation of the returned value. The continuation of the structure is unpacked by applying `annotate` to the seed and mapping the resulting syntax’s values recursively. In this manner, the structure is unfolded bottom-up, starting with `seed` and ending at the leaves.
|
||||
///
|
||||
/// As this is the dual of `cata`, it’s unsurprising that we have a similar guarantee: coiteration is linear in the size of the constructed tree.
|
||||
public static func ana(@noescape unfold: Annotation throws -> Syntax<Annotation, Leaf>)(_ seed: Annotation) rethrows -> Self {
|
||||
return try Self(seed, unfold(seed).map { try ana(unfold)($0) })
|
||||
}
|
||||
|
||||
/// `Zip` two `CofreeType` values into a single `Cofree`, pairing their annotations.
|
||||
///
|
||||
/// This is partial, returning `nil` for any pair of values which are not of the same “shape,” i.e. where they wrap `Syntax` values of different constructors. The values of leaves are always taken from the second parameter.
|
||||
public static func zip(a: Self, _ b: Self) -> Cofree<Leaf, (Annotation, Annotation)>? {
|
||||
let annotations = (a.extract, b.extract)
|
||||
switch (a.unwrap, b.unwrap) {
|
||||
case let (.Leaf, .Leaf(b)):
|
||||
return Cofree(annotations, .Leaf(b))
|
||||
case let (.Indexed(a), .Indexed(b)):
|
||||
return Cofree(annotations, .Indexed(Swift.zip(a, b).flatMap(zip)))
|
||||
case let (.Fixed(a), .Fixed(b)):
|
||||
return Cofree(annotations, .Fixed(Swift.zip(a, b).flatMap(zip)))
|
||||
case let (.Keyed(a), .Keyed(b)):
|
||||
return Cofree(annotations, .Keyed(Dictionary(elements: b.keys.flatMap { key in zip(a[key]!, b[key]!).map { (key, $0) } })))
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Cofree: CofreeType {}
|
||||
|
||||
extension CofreeType where Self.Annotation == Range<String.Index> {
|
||||
public func JSON(source: String) -> Doubt.JSON {
|
||||
return unwrap.JSON(
|
||||
leaf: { _ in .String(source[extract]) },
|
||||
recur: {
|
||||
[
|
||||
"range": [
|
||||
"offset": .Number(Double(source.startIndex.distanceTo($0.extract.startIndex))),
|
||||
"length": .Number(Double($0.extract.count)),
|
||||
],
|
||||
"unwrap": $0.JSON(source)
|
||||
]
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Weaving
|
||||
|
||||
extension Cofree {
|
||||
public func explore() -> Location<Cofree> {
|
||||
func weave(cofree: Cofree) -> Location<Cofree>.Unweave {
|
||||
switch cofree {
|
||||
case .Unroll(_, .Leaf):
|
||||
return Location.nullary
|
||||
|
||||
case let .Unroll(annotation, .Indexed(i)):
|
||||
return Location.variadic(i, weave, { Cofree(annotation, .Indexed($0)) })
|
||||
|
||||
case let .Unroll(annotation, .Fixed(f)):
|
||||
return Location.variadic(f, weave, { Cofree(annotation, .Fixed($0)) })
|
||||
|
||||
case let .Unroll(annotation, .Keyed(k)):
|
||||
return Location.variadic(k, weave, { Cofree(annotation, .Keyed($0)) })
|
||||
}
|
||||
}
|
||||
return Location.explore(weave)(self)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Size
|
||||
|
||||
extension Cofree {
|
||||
/// The count of nodes in the receiver.
|
||||
///
|
||||
/// This is used to compute the cost of patches, such that a patch inserting a very large tree will be charged approximately the same as a very large tree consisting of many small patches.
|
||||
public static func size(term: Cofree) -> Int {
|
||||
switch term {
|
||||
case .Unroll(_, .Leaf):
|
||||
return 1
|
||||
case let .Unroll(_, .Indexed(a)):
|
||||
return a.reduce(0) { $0 + size($1) }
|
||||
case let .Unroll(_, .Fixed(a)):
|
||||
return a.reduce(0) { $0 + size($1) }
|
||||
case let .Unroll(_, .Keyed(a)):
|
||||
return a.reduce(0) { $0 + size($1.1) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
import Prelude
|
@ -1,8 +0,0 @@
|
||||
extension Dictionary {
|
||||
public init<C: CollectionType where C.Generator.Element == (Key, Value)>(elements: C) {
|
||||
self.init(minimumCapacity: Int(elements.count.toIntMax()))
|
||||
for (key, value) in elements {
|
||||
self[key] = value
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
framework module Doubt {
|
||||
export *
|
||||
}
|
@ -1,262 +0,0 @@
|
||||
/// The free monad over `Syntax`.
|
||||
///
|
||||
/// This is “free” in the sense of “unconstrained” rather than “zero-cost”; it’s the monad obtained by taking a functor (in this case `Syntax`) and adding the minimum necessary details (the `Pure` case) to satisfy the monad laws.
|
||||
///
|
||||
/// `Syntax` is a non-recursive type parameterized by the type of its child nodes. Instantiating it to `Free` makes it recursive through the `Roll` case, and allows it to wrap values of type `Value` through the `Pure` case.
|
||||
///
|
||||
/// In Doubt, this allows us to represent diffs as values of the `Free` monad obtained from `Syntax`, injecting `Patch` into the tree; or otherwise put, a diff is a tree of mutually-recursive `Free.Roll`/`Syntax` nodes with `Pure` nodes injecting the actual changes.
|
||||
public enum Free<Leaf, Annotation, Value>: CustomDebugStringConvertible {
|
||||
/// The injection of a value of type `Value` into the `Syntax` tree.
|
||||
case Pure(Value)
|
||||
|
||||
/// A recursive instantiation of `Syntax`, unrolling another iteration of the recursive type.
|
||||
indirect case Roll(Annotation, Syntax<Free, Leaf>)
|
||||
|
||||
|
||||
/// Construct a `Free` from a `CofreeType` with matching `Leaf` and `Annotation` types, copying the recursive structure of the term in via hylomorphism.
|
||||
///
|
||||
/// The resulting `Free` value will not have any `Pure` cases.
|
||||
public init<Term: CofreeType where Term.Leaf == Leaf, Term.Annotation == Annotation>(_ term: Term) {
|
||||
self = hylo(Free.Roll, Term.eliminate)(term)
|
||||
}
|
||||
|
||||
|
||||
/// Reduce the receiver by iteration.
|
||||
///
|
||||
/// `Pure` values are simply unpacked. `Roll` values are mapped recursively, and then have `transform` applied to them.
|
||||
///
|
||||
/// This forms a _catamorphism_ (from the Greek “cata”, “downwards”; compare “catastrophe”), a generalization of folds over regular trees (and datatypes isomorphic to them). It operates at the leaves first, and then branches near the periphery, recursively collapsing values by whatever is computed by `transform`. Catamorphisms are themselves an example of _recursion schemes_, which characterize specific well-behaved patterns of recursion. This gives `cata` some useful properties for computations performed over trees.
|
||||
///
|
||||
/// Due to the character of recursion captured by catamorphisms, `cata` ensures that computation will not only halt, but will further be linear in the size of the receiver. (Nesting a call to `cata` will therefore result in O(n²) complexity.) This guarantee is achieved by careful composition of calls to `map` with recursive calls to `cata`, only calling `transform` once the recursive call has completed. `transform` is itself non-recursive, receiving a `Syntax` whose recurrences have already been flattened to `Value`.
|
||||
///
|
||||
/// The linearity of `cata` in the size of the receiver makes it trivial to compute said size, by counting leaves as 1 and summing branches’ children:
|
||||
///
|
||||
/// func size<Leaf, Annotation, Value>(free: Free<Leaf, Annotation, Value>) -> Int {
|
||||
/// return free.cata { flattenedSyntax in
|
||||
/// switch flattenedSyntax {
|
||||
/// case .Leaf:
|
||||
/// return 1
|
||||
/// case let .Indexed(children):
|
||||
/// return children.reduce(0, combine: +)
|
||||
/// case let .Keyed(children):
|
||||
/// return children.lazy.map { $1 }.reduce(0, combine: +)
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// While not every function on a given `Free` can be computed using `cata`, these guarantees of termination and complexity, as well as the brevity and focus on the operation being performed n times, make it a desirable scaffolding for any function which can.
|
||||
///
|
||||
/// For a lucid, in-depth tutorial on recursion schemes, I recommend [Patrick Thomson](https://twitter.com/importantshock)’s _[An Introduction to Recursion Schemes](http://patrickthomson.ghost.io/an-introduction-to-recursion-schemes/)_ and _[Recursion Schemes, Part 2: A Mob of Morphisms](http://patrickthomson.ghost.io/recursion-schemes-part-2/)_.
|
||||
public func cata(@noescape transform: (Annotation, Syntax<Value, Leaf>) throws -> Value) rethrows -> Value {
|
||||
switch self {
|
||||
case let .Pure(a):
|
||||
return a
|
||||
case let .Roll(annotation, syntax):
|
||||
return try transform(annotation, syntax.map { try $0.cata(transform) })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Returns a function which sums `Free`s by first `transform`ing `Pure` values into integers, and then summing these.
|
||||
public static func sum(transform: Value -> Int) -> Free -> Int {
|
||||
func sum(free: Free) -> Int {
|
||||
switch free {
|
||||
case let .Pure(a):
|
||||
return transform(a)
|
||||
case .Roll(_, .Leaf):
|
||||
return 0
|
||||
case let .Roll(_, .Indexed(a)):
|
||||
return a.reduce(0) { into, each in into + sum(each) }
|
||||
case let .Roll(_, .Fixed(a)):
|
||||
return a.reduce(0) { into, each in into + sum(each) }
|
||||
case let .Roll(_, .Keyed(a)):
|
||||
return a.reduce(0) { into, each in into + sum(each.1) }
|
||||
}
|
||||
}
|
||||
return sum
|
||||
}
|
||||
|
||||
|
||||
// MARK: Functor
|
||||
|
||||
public func map<C>(@noescape transform: Value throws -> C) rethrows -> Free<Leaf, Annotation, C> {
|
||||
switch self {
|
||||
case let .Pure(a):
|
||||
return try .Pure(transform(a))
|
||||
case let .Roll(annotation, syntax):
|
||||
return try .Roll(annotation, syntax.map { try $0.map(transform) })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: CustomDebugStringConvertible
|
||||
|
||||
public var debugDescription: String {
|
||||
switch self {
|
||||
case let .Pure(b):
|
||||
return ".Pure(\(String(reflecting: b)))"
|
||||
case let .Roll(s):
|
||||
return ".Roll(\(String(reflecting: s)))"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Monad
|
||||
|
||||
extension Free {
|
||||
public func flatMap<Other>(@noescape transform: Value throws -> Free<Leaf, Annotation, Other>) rethrows -> Free<Leaf, Annotation, Other> {
|
||||
switch self {
|
||||
case let .Pure(a):
|
||||
return try transform(a)
|
||||
case let .Roll(annotation, rest):
|
||||
return try .Roll(annotation, rest.map { try $0.flatMap(transform) })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Anamorphism
|
||||
|
||||
extension Free {
|
||||
public static func Introduce(annotation: Annotation)(syntax: Syntax<Free, Leaf>) -> Free {
|
||||
return Roll(annotation, syntax)
|
||||
}
|
||||
|
||||
/// Anamorphism over `Free`.
|
||||
///
|
||||
/// Unfolds a tree bottom-up by recursively applying `transform` to a series of values starting with `seed`. Since `Syntax.Leaf` does not recur, this will halt when it has produced leaves for every branch.
|
||||
public static func ana(@noescape unfold: Annotation throws -> Syntax<Annotation, Leaf>)(_ seed: Annotation) rethrows -> Free {
|
||||
return try Roll(seed, unfold(seed).map { try ana(unfold)($0) })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension Free where Value: PatchType, Value.Element == Cofree<Leaf, ()> {
|
||||
public typealias Term = Value.Element
|
||||
|
||||
public func merge(@noescape transform: Value -> Term) -> Term {
|
||||
return map(transform).cata { Cofree((), $1) }
|
||||
}
|
||||
|
||||
public func merge(@noescape transform: Value -> Term?) -> Term? {
|
||||
return map(transform).cata { Free.discardNullTerms($1) }
|
||||
}
|
||||
|
||||
private static func discardNullTerms(syntax: Syntax<Term?, Leaf>) -> Term? {
|
||||
switch syntax {
|
||||
case let .Leaf(a):
|
||||
return Cofree((), .Leaf(a))
|
||||
case let .Indexed(a):
|
||||
return Cofree((), .Indexed(a.flatMap(id)))
|
||||
case let .Fixed(a):
|
||||
return Cofree((), .Fixed(a.flatMap(id)))
|
||||
case let .Keyed(a):
|
||||
return Cofree((), .Keyed(Dictionary(elements: a.flatMap { k, v in v.map { (k, $0) } })))
|
||||
}
|
||||
}
|
||||
|
||||
public var before: Term? {
|
||||
return merge { $0.state.before }
|
||||
}
|
||||
|
||||
public var after: Term? {
|
||||
return merge { $0.state.after }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Patch construction
|
||||
|
||||
extension Free where Value: PatchType {
|
||||
public static func Replace(before: Value.Element, _ after: Value.Element) -> Free {
|
||||
return .Pure(Value(replacing: before, with: after))
|
||||
}
|
||||
|
||||
public static func Insert(after: Value.Element) -> Free {
|
||||
return .Pure(Value(inserting: after))
|
||||
}
|
||||
|
||||
public static func Delete(before: Value.Element) -> Free {
|
||||
return .Pure(Value(deleting: before))
|
||||
}
|
||||
|
||||
|
||||
public var inverse: Free {
|
||||
return map { $0.inverse }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Equality
|
||||
|
||||
extension Free {
|
||||
public static func equals(pure pure: (Value, Value) -> Bool, leaf: (Leaf, Leaf) -> Bool, annotation: (Annotation, Annotation) -> Bool)(_ left: Free, _ right: Free) -> Bool {
|
||||
switch (left, right) {
|
||||
case let (.Pure(a), .Pure(b)):
|
||||
return pure(a, b)
|
||||
case let (.Roll(annotation1, syntax1), .Roll(annotation2, syntax2)):
|
||||
return annotation(annotation1, annotation2) && Syntax.equals(leaf: leaf, recur: equals(pure: pure, leaf: leaf, annotation: annotation))(syntax1, syntax2)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func == <Leaf: Equatable, Value: Equatable, Annotation: Equatable> (left: Free<Leaf, Annotation, Value>, right: Free<Leaf, Annotation, Value>) -> Bool {
|
||||
return Free.equals(pure: ==, leaf: ==, annotation: ==)(left, right)
|
||||
}
|
||||
|
||||
public func == <Term: CofreeType, Annotation: Equatable where Term.Leaf: Equatable> (left: Free<Term.Leaf, Annotation, Patch<Term>>, right: Free<Term.Leaf, Annotation, Patch<Term>>) -> Bool {
|
||||
return Free.equals(pure: Patch.equals(Term.equals(==)), leaf: ==, annotation: ==)(left, right)
|
||||
}
|
||||
|
||||
public func == <Term: CofreeType, Annotation where Term.Leaf: Equatable> (left: Free<Term.Leaf, Annotation, Patch<Term>>, right: Free<Term.Leaf, Annotation, Patch<Term>>) -> Bool {
|
||||
return Free.equals(pure: Patch.equals(Term.equals(==)), leaf: ==, annotation: const(true))(left, right)
|
||||
}
|
||||
|
||||
|
||||
// MARK: - JSON
|
||||
|
||||
extension Free {
|
||||
public func JSON(pure pure: Value -> Doubt.JSON, leaf: Leaf -> Doubt.JSON, annotation: Annotation -> Doubt.JSON) -> Doubt.JSON {
|
||||
switch self {
|
||||
case let .Pure(a):
|
||||
return [ "pure": pure(a) ]
|
||||
case let .Roll(a, b):
|
||||
return [
|
||||
"roll": [
|
||||
"extract": annotation(a),
|
||||
"unwrap": b.JSON(leaf: leaf, recur: { $0.JSON(pure: pure, leaf: leaf, annotation: annotation) })
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Weaving
|
||||
|
||||
extension Free {
|
||||
public func explore() -> Location<Free> {
|
||||
func weave(free: Free) -> Location<Free>.Unweave {
|
||||
switch free {
|
||||
case .Pure, .Roll(_, .Leaf):
|
||||
return Location.nullary
|
||||
|
||||
case let .Roll(annotation, .Indexed(i)):
|
||||
return Location.variadic(i, weave, { Free.Roll(annotation, .Indexed($0)) })
|
||||
|
||||
case let .Roll(annotation, .Fixed(f)):
|
||||
return Location.variadic(f, weave, { Free.Roll(annotation, .Fixed($0)) })
|
||||
|
||||
case let .Roll(annotation, .Keyed(k)):
|
||||
return Location.variadic(k, weave, { Free.Roll(annotation, .Keyed($0)) })
|
||||
}
|
||||
}
|
||||
return Location.explore(weave)(self)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
import Prelude
|
@ -1,28 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.0.1</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2015 GitHub. All rights reserved.</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
</plist>
|
@ -1,115 +0,0 @@
|
||||
/// An interpreter of `Algorithm`s.
|
||||
public struct Interpreter<Term: CofreeType> {
|
||||
/// The type of diffs constructed by `Interpreter`s.
|
||||
public typealias Diff = Free<Term.Leaf, (Term.Annotation, Term.Annotation), Patch<Term>>
|
||||
|
||||
/// Constructs an `Interpreter` parameterized by the `equal` and `comparable` tests on `Term`s, and the `cost` function for `Diff`s.
|
||||
///
|
||||
/// `equal` determines whether two terms are equal. This should typically be strict syntactic equality, not e.g. including any annotations of source ranges. If two terms are considered equal by this function, then an unchanged diff will be returned directly. No diffing will be performed, and `comparable` will not be applied to the terms at all.
|
||||
///
|
||||
/// `comparable` determines whether two terms should be considered comparable when encountered at some point within the tree. When diffing two `.Indexed` terms, this will cause diffing to delete one term and insert the other rather than recurring through the pair (possibly constructing a single replacement). This has far-reaching implications for performance, as it enables the caller to dramatically prune the search space.
|
||||
///
|
||||
/// `cost` computes the cost of a single diff. This is used when computing the diff of `.Indexed` terms: the algorithm selects the lowest-total-cost sequence of diffs which completely cover the arrays. This computation is performed a minimum of 2mn and a maximum of 3mn times, where m is the length of the first array and n is the length of the second. Therefore, it should be as efficient as possible, ideally no more than linear in the size of the diff.
|
||||
public init(equal: (Term, Term) -> Bool, comparable: (Term, Term) -> Bool, cost: Diff -> Int) {
|
||||
self.equal = equal
|
||||
self.comparable = comparable
|
||||
self.cost = cost
|
||||
}
|
||||
|
||||
|
||||
/// Computes a term comparable function from a categorizing function.
|
||||
public static func comparable<C>(categorize: Term -> Set<C>)(_ a: Term, _ b: Term) -> Bool {
|
||||
let c0 = categorize(a)
|
||||
let c1 = categorize(b)
|
||||
return c0 == c1 || !categorize(a).intersect(categorize(b)).isEmpty
|
||||
}
|
||||
|
||||
|
||||
/// Run the interpreter over a given pair of `Term`s, producing a `Diff` as its result.
|
||||
public func run(a: Term, _ b: Term) -> Diff {
|
||||
return recur(a, b) ?? .Replace(a, b)
|
||||
}
|
||||
|
||||
|
||||
private let equal: (Term, Term) -> Bool
|
||||
private let comparable: (Term, Term) -> Bool
|
||||
private let cost: Diff -> Int
|
||||
|
||||
/// Diff `a` against `b`, if comparable.
|
||||
private func recur(a: Term, _ b: Term) -> Diff? {
|
||||
// If both terms are equal, we don’t need to bother diffing.
|
||||
//
|
||||
// In that case, zip the two terms together (to pair their annotations), and then map the resulting `Term` (which, since the terms are equal, will be non-nil) into a `Diff`.
|
||||
if equal(a, b) { return Term.zip(a, b).map(Diff.init) }
|
||||
guard comparable(a, b) else { return nil }
|
||||
|
||||
let algorithm: Algorithm<Term, Diff>
|
||||
let annotations = (a.extract, b.extract)
|
||||
|
||||
switch (a.unwrap, b.unwrap) {
|
||||
case let (.Leaf, .Leaf(leaf)) where equal(a, b):
|
||||
return .Roll(annotations, .Leaf(leaf))
|
||||
case let (.Keyed(a), .Keyed(b)):
|
||||
algorithm = .Roll(.ByKey(a, b, Syntax.Keyed >>> Diff.Introduce(annotations) >>> Algorithm.Pure))
|
||||
case let (.Indexed(a), .Indexed(b)):
|
||||
algorithm = .Roll(.ByIndex(a, b, Syntax.Indexed >>> Diff.Introduce(annotations) >>> Algorithm.Pure))
|
||||
case (.Fixed(_), .Fixed(_)):
|
||||
fallthrough
|
||||
default:
|
||||
algorithm = .Roll(.Recursive(a, b, Algorithm.Pure))
|
||||
}
|
||||
return recur(algorithm)
|
||||
}
|
||||
|
||||
private func recur(algorithm: Algorithm<Term, Diff>) -> Diff? {
|
||||
switch algorithm {
|
||||
case let .Pure(diff):
|
||||
return diff
|
||||
|
||||
case let .Roll(.Recursive(a, b, f)):
|
||||
// Recur structurally into both terms, patching differing sub-terms. This is akin to unification, except that it computes a patched tree instead of a substitution. It’s also a little like a structural zip on pairs of terms.
|
||||
let annotations = (a.extract, b.extract)
|
||||
switch (a.unwrap, b.unwrap) {
|
||||
case let (.Indexed(a), .Indexed(b)) where a.count == b.count:
|
||||
return recur(f(.Roll(annotations, .Indexed(zip(a, b).map(run)))))
|
||||
case let (.Fixed(a), .Fixed(b)) where a.count == b.count:
|
||||
return recur(f(.Roll(annotations, .Fixed(zip(a, b).map(run)))))
|
||||
|
||||
case let (.Keyed(a), .Keyed(b)) where Array(a.keys) == Array(b.keys):
|
||||
return recur(f(.Roll(annotations, .Keyed(Dictionary(elements: b.keys.map { ($0, self.run(a[$0]!, b[$0]!)) })))))
|
||||
|
||||
default:
|
||||
// This must not call `recur` directly with `a` and `b`, as that would infinite loop if actually recursive.
|
||||
return recur(f(.Replace(a, b)))
|
||||
}
|
||||
|
||||
case let .Roll(.ByKey(a, b, f)):
|
||||
// Perform [set reconciliation](https://en.wikipedia.org/wiki/Data_synchronization#Unordered_data) on the keys, followed by recurring into the values of the intersecting keys.
|
||||
let deleted = Set(a.keys).subtract(b.keys).map { ($0, Diff.Delete(a[$0]!)) }
|
||||
let inserted = Set(b.keys).subtract(a.keys).map { ($0, Diff.Insert(b[$0]!)) }
|
||||
let patched = Set(a.keys).intersect(b.keys).map { ($0, run(a[$0]!, b[$0]!)) }
|
||||
return recur(f(Dictionary(elements: deleted + inserted + patched)))
|
||||
|
||||
case let .Roll(.ByIndex(a, b, f)):
|
||||
return recur(f(SES(a, b, cost: cost, recur: recur)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Constrained constructors
|
||||
|
||||
extension Interpreter where Term.Leaf: Equatable {
|
||||
public init(comparable: (Term, Term) -> Bool, cost: Diff -> Int) {
|
||||
self.init(equal: Term.equals(==), comparable: comparable, cost: cost)
|
||||
}
|
||||
}
|
||||
|
||||
extension Interpreter where Term: CofreeType, Term.Annotation: Categorizable {
|
||||
public init(equal: (Term, Term) -> Bool, cost: Diff -> Int) {
|
||||
self.init(equal: equal, comparable: Interpreter.comparable { $0.extract.categories }, cost: cost)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
import Prelude
|
@ -1,216 +0,0 @@
|
||||
public enum JSON: ArrayLiteralConvertible, BooleanLiteralConvertible, DictionaryLiteralConvertible, Equatable, FloatLiteralConvertible, IntegerLiteralConvertible, JSONConvertible, NilLiteralConvertible, StringLiteralConvertible {
|
||||
public typealias ArrayType = [Doubt.JSON]
|
||||
public typealias DictionaryType = [Swift.String:Doubt.JSON]
|
||||
|
||||
case Number(Double)
|
||||
case Boolean(Bool)
|
||||
case String(Swift.String)
|
||||
case Array(ArrayType)
|
||||
case Dictionary(DictionaryType)
|
||||
case Null
|
||||
|
||||
|
||||
public init(_ convertible: CustomJSONConvertible) {
|
||||
self = convertible.JSON
|
||||
}
|
||||
|
||||
|
||||
public var number: Double? {
|
||||
if case let .Number(d) = self { return d }
|
||||
return nil
|
||||
}
|
||||
|
||||
public var boolean: Bool? {
|
||||
if case let .Boolean(b) = self { return b }
|
||||
return nil
|
||||
}
|
||||
|
||||
public var string: Swift.String? {
|
||||
if case let .String(s) = self { return s }
|
||||
return nil
|
||||
}
|
||||
|
||||
public var array: ArrayType? {
|
||||
if case let .Array(a) = self { return a }
|
||||
return nil
|
||||
}
|
||||
|
||||
public var dictionary: DictionaryType? {
|
||||
if case let .Dictionary(d) = self { return d }
|
||||
return nil
|
||||
}
|
||||
|
||||
public var isNull: Bool {
|
||||
if case .Null = self { return true }
|
||||
return false
|
||||
}
|
||||
|
||||
public init?(object: AnyObject) {
|
||||
struct E: ErrorType {}
|
||||
func die<T>() throws -> T {
|
||||
throw E()
|
||||
}
|
||||
do {
|
||||
switch object {
|
||||
case let n as Double:
|
||||
self = .Number(n)
|
||||
case let b as Bool:
|
||||
self = .Boolean(b)
|
||||
case let s as Swift.String:
|
||||
self = .String(s)
|
||||
case let a as [AnyObject]:
|
||||
self = .Array(try a.map { try Doubt.JSON(object: $0) ?? die() })
|
||||
case let d as [Swift.String:AnyObject]:
|
||||
self = .Dictionary(Swift.Dictionary(elements: try d.map { ($0, try Doubt.JSON(object: $1) ?? die()) }))
|
||||
case is NSNull:
|
||||
self = .Null
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
} catch { return nil }
|
||||
}
|
||||
|
||||
|
||||
public var object: AnyObject {
|
||||
switch self {
|
||||
case let .Number(n):
|
||||
return n
|
||||
case let .Boolean(b):
|
||||
return b
|
||||
case let .String(s):
|
||||
return s
|
||||
case let .Array(a):
|
||||
return a.map { $0.object }
|
||||
case let .Dictionary(d):
|
||||
return Swift.Dictionary(elements: d.map { ($0, $1.object) })
|
||||
case .Null:
|
||||
return NSNull()
|
||||
}
|
||||
}
|
||||
|
||||
public func serialize() -> NSData {
|
||||
return try! NSJSONSerialization.dataWithJSONObject(object, options: [])
|
||||
}
|
||||
|
||||
|
||||
// MARK: ArrayLiteralConvertible
|
||||
|
||||
public init(arrayLiteral: Doubt.JSON...) {
|
||||
self = .Array(arrayLiteral)
|
||||
}
|
||||
|
||||
|
||||
// MARK: BooleanLiteralConvertible
|
||||
|
||||
public init(booleanLiteral: Bool) {
|
||||
self = .Boolean(booleanLiteral)
|
||||
}
|
||||
|
||||
|
||||
// MARK: DictionaryLiteralConvertible
|
||||
|
||||
public init(dictionaryLiteral: (Swift.String, Doubt.JSON)...) {
|
||||
self = .Dictionary(Swift.Dictionary(elements: dictionaryLiteral))
|
||||
}
|
||||
|
||||
|
||||
// MARK: FloatLiteralConvertible
|
||||
|
||||
public init(floatLiteral: Double) {
|
||||
self = .Number(floatLiteral)
|
||||
}
|
||||
|
||||
|
||||
// MARK: IntegerLiteralConvertible
|
||||
|
||||
public init(integerLiteral: Int) {
|
||||
self = .Number(Double(integerLiteral))
|
||||
}
|
||||
|
||||
|
||||
// MARK: JSONConvertible
|
||||
|
||||
public init?(JSON: Doubt.JSON) {
|
||||
self = JSON
|
||||
}
|
||||
|
||||
public var JSON: Doubt.JSON {
|
||||
return self
|
||||
}
|
||||
|
||||
|
||||
// MARK: NilLiteralConvertible
|
||||
|
||||
public init(nilLiteral: ()) {
|
||||
self = .Null
|
||||
}
|
||||
|
||||
|
||||
// MARK: StringLiteralConvertible
|
||||
|
||||
public init(stringLiteral: Swift.String) {
|
||||
self = .String(stringLiteral)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public func == (left: JSON, right: JSON) -> Bool {
|
||||
switch (left, right) {
|
||||
case let (.Number(a), .Number(b)):
|
||||
return a == b
|
||||
case let (.Boolean(a), .Boolean(b)):
|
||||
return a == b
|
||||
case let (.String(a), .String(b)):
|
||||
return a == b
|
||||
case let (.Array(a), .Array(b)):
|
||||
return a == b
|
||||
case let (.Dictionary(a), .Dictionary(b)):
|
||||
return a == b
|
||||
case (.Null, .Null):
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// A type which can be converted to JSON.
|
||||
public protocol CustomJSONConvertible {
|
||||
var JSON: Doubt.JSON { get }
|
||||
}
|
||||
|
||||
extension String: CustomJSONConvertible {
|
||||
public var JSON: Doubt.JSON {
|
||||
return .String(self)
|
||||
}
|
||||
}
|
||||
|
||||
extension Int: CustomJSONConvertible {
|
||||
public var JSON: Doubt.JSON {
|
||||
return .Number(Double(self))
|
||||
}
|
||||
}
|
||||
|
||||
extension Array where Element: CustomJSONConvertible {
|
||||
public var JSON: Doubt.JSON {
|
||||
return .Array(map { Doubt.JSON($0) })
|
||||
}
|
||||
}
|
||||
|
||||
extension Range where Element: CustomJSONConvertible, Element.Distance: CustomJSONConvertible {
|
||||
public var JSON: Doubt.JSON {
|
||||
return [
|
||||
startIndex.JSON,
|
||||
count.JSON,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// A type which can be converted to and from JSON.
|
||||
public protocol JSONConvertible: CustomJSONConvertible {
|
||||
init?(JSON: Doubt.JSON)
|
||||
}
|
||||
|
||||
|
||||
import Foundation
|
@ -1,69 +0,0 @@
|
||||
public enum JSONLeaf: Categorizable, CustomJSONConvertible, CustomStringConvertible, Equatable {
|
||||
case Number(Double)
|
||||
case Boolean(Bool)
|
||||
case String(Swift.String)
|
||||
case Null
|
||||
|
||||
|
||||
// MARK: Categorizable
|
||||
|
||||
public var categories: Set<Swift.String> {
|
||||
switch self {
|
||||
case .Number:
|
||||
return [ "number" ]
|
||||
case .Boolean:
|
||||
return [ "boolean" ]
|
||||
case .String:
|
||||
return [ "string" ]
|
||||
case .Null:
|
||||
return [ "null" ]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: CustomJSONConvertible
|
||||
|
||||
public var JSON: Doubt.JSON {
|
||||
switch self {
|
||||
case let .Number(n):
|
||||
return .Number(n)
|
||||
case let .Boolean(b):
|
||||
return .Boolean(b)
|
||||
case let .String(s):
|
||||
return .String(s)
|
||||
case .Null:
|
||||
return .Null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: CustomStringConvertible
|
||||
|
||||
public var description: Swift.String {
|
||||
switch self {
|
||||
case let .Number(n):
|
||||
return Swift.String(n)
|
||||
case let .Boolean(b):
|
||||
return Swift.String(b)
|
||||
case let .String(s):
|
||||
return Swift.String(reflecting: s)
|
||||
case .Null:
|
||||
return "null"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func == (left: JSONLeaf, right: JSONLeaf) -> Bool {
|
||||
switch (left, right) {
|
||||
case let (.Number(a), .Number(b)):
|
||||
return a == b
|
||||
case let (.Boolean(a), .Boolean(b)):
|
||||
return a == b
|
||||
case let (.String(a), .String(b)):
|
||||
return a == b
|
||||
case (.Null, .Null):
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
@ -1,86 +0,0 @@
|
||||
//
|
||||
// JSONParser.swift
|
||||
// Doubt
|
||||
//
|
||||
// Created by Josh Vera on 10/16/15.
|
||||
// Copyright © 2015 GitHub. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Madness
|
||||
import Prelude
|
||||
|
||||
public typealias CofreeJSON = Cofree<JSONLeaf, (Range<Line>, Range<Column>, Range<String.Index>)>
|
||||
public typealias JSONParser = Parser<String.CharacterView, CofreeJSON>.Function
|
||||
|
||||
// Inlined for performance reasons
|
||||
let whitespace = many(oneOf(" \n\r\t"))
|
||||
|
||||
// TODO: Parse unicode escape sequence
|
||||
let escapeChar: StringParser = curry(+) <^> %"\\" <*> ({ String($0) } <^> oneOf("\\\"bfnrt"))
|
||||
let otherChar: StringParser = { String($0) } <^> noneOf("\"\\")
|
||||
|
||||
// Quoted strings parser
|
||||
// TODO: Improve string parsing
|
||||
let stringBody: StringParser = { $0.joinWithSeparator("") } <^> many(escapeChar <|> otherChar)
|
||||
let quoted = %"\"" *> stringBody <* %"\""
|
||||
|
||||
typealias MembersParser = Parser<String.CharacterView, [(String, CofreeJSON)]>.Function;
|
||||
|
||||
// Parses an array of (String, CofreeJSON) object members
|
||||
func members(json: JSONParser) -> MembersParser {
|
||||
let keyAndKeyTerm: Parser<String.CharacterView, (String, CofreeJSON)>.Function = quoted --> { _, lines, columns, range, key in
|
||||
(key, Cofree((lines, columns, range), .Leaf(.String(key))))
|
||||
}
|
||||
let pairs: Parser<String.CharacterView, (String, CofreeJSON)>.Function = (curry(pair) <^>
|
||||
keyAndKeyTerm
|
||||
<* whitespace
|
||||
<* %":"
|
||||
<* whitespace
|
||||
<*> json) --> { _, lines, columns, range, values in
|
||||
(values.0.0, Cofree((lines, columns, range), .Fixed([values.0.1, values.1])))
|
||||
}
|
||||
|
||||
return sepBy(pairs, whitespace <* %"," <* whitespace)
|
||||
}
|
||||
|
||||
public let json: JSONParser = fix { json in
|
||||
let string: JSONParser = quoted --> {
|
||||
Cofree(($1, $2, $3), .Leaf(.String($4)))
|
||||
} <?> "string"
|
||||
|
||||
let array: JSONParser = %"["
|
||||
<* whitespace
|
||||
*> sepBy(whitespace *> json, whitespace <* %"," <* whitespace)
|
||||
<* whitespace
|
||||
<* %"]"
|
||||
--> {
|
||||
Cofree(($1, $2, $3), .Indexed($4))
|
||||
} <?> "array"
|
||||
|
||||
let object: JSONParser = %"{"
|
||||
*> whitespace
|
||||
*> members(json)
|
||||
<* whitespace
|
||||
<* %"}"
|
||||
--> { (_, lines, columns, range, values: [(String, CofreeJSON)]) in
|
||||
Cofree((lines, columns, range), .Keyed(Dictionary(elements: values)))
|
||||
} <?> "object"
|
||||
|
||||
let numberParser: JSONParser = (number --> { _, lines, columns, range, value in
|
||||
Cofree((lines, columns, range), .Leaf(JSONLeaf.Number(value)))
|
||||
}) <?> "number"
|
||||
|
||||
let null: JSONParser = %"null" --> { _, lines, columns, range, value in
|
||||
return Cofree((lines, columns, range), .Leaf(.Null))
|
||||
} <?> "null"
|
||||
|
||||
let boolean: JSONParser = %"false" <|> %"true" --> { _, lines, columns, range, value in
|
||||
let boolean = value == "true"
|
||||
return Cofree((lines, columns, range), .Leaf(.Boolean(boolean)))
|
||||
} <?> "boolean"
|
||||
|
||||
// TODO: This should be JSON = dict <|> array and
|
||||
// Value = dict | array | string | number | null | bool
|
||||
return (object <|> array <|> string <|> numberParser <|> boolean <|> null) <* whitespace
|
||||
}
|
@ -1,154 +0,0 @@
|
||||
/// A `Location` is a structure similar to a `Lens` which allows introspection of & update to subtrees of some tree.
|
||||
///
|
||||
/// In the former case, a `Location` is essentially an implementation of a [Zipper][], allowing exploration into and out of nodes, as well as to adjacent nodes.
|
||||
///
|
||||
/// [Hinze & Jeuring][] then extended this to support replacing the focused node. `Location` implements their approach (see §5, “A Read-write Web”).
|
||||
///
|
||||
/// This is also closely related to [McBride][]’s observations about the derivative of regular types.
|
||||
///
|
||||
/// [Zipper]: https://www.st.cs.uni-saarland.de/edu/seminare/2005/advanced-fp/docs/huet-zipper.pdf
|
||||
/// [Hinze & Jeuring]: https://github.com/github/semantic-diff/files/27297/Weaving.a.Web.pdf
|
||||
/// [McBride]: http://strictlypositive.org/diff.pdf
|
||||
public struct Location<A>: SequenceType {
|
||||
/// Construct a `Location` representing some position within a tree.
|
||||
public init(it: A, into: A -> Location?, outOf: A -> Location?, left: A -> Location?, right: A -> Location?) {
|
||||
self.it = it
|
||||
_left = left
|
||||
_right = right
|
||||
_outOf = outOf
|
||||
_into = into
|
||||
}
|
||||
|
||||
/// The node currently in focus.
|
||||
public let it: A
|
||||
|
||||
/// Returns the `Location` representing the contents of `it`, or `nil` if `it` is a leaf.
|
||||
public var into: Location? { return _into(it) }
|
||||
|
||||
/// Returns the `Location` representing the parent of `it` in the current exploration, or `nil` if the receiver is the `root`.
|
||||
public var outOf: Location? { return _outOf(it) }
|
||||
|
||||
/// Returns the `Location` which immediately precedes the receiver within its parent in the current exploration, or `nil` if the receiver is the `root` or the first child of its parent.
|
||||
public var left: Location? { return _left(it) }
|
||||
|
||||
/// Returns the `Location` which immediately follows the receiver within its parent in the current exploration, or `nil` if the receiver is the `root` or the last child of its parent.
|
||||
public var right: Location? { return _right(it) }
|
||||
|
||||
/// The root `Location` in the current exploration.
|
||||
public var root: Location {
|
||||
return outOf?.root ?? self
|
||||
}
|
||||
|
||||
|
||||
/// Returns the logically next `Location` after the receiver in a pre-order depth-first traversal.
|
||||
public var next: Location? {
|
||||
return into ?? nextAfter
|
||||
}
|
||||
|
||||
/// Returns the logically next `Location` after the receiver and its children in a pre-order depth-first traversal.
|
||||
private var nextAfter: Location? {
|
||||
return right ?? outOf?.nextAfter
|
||||
}
|
||||
|
||||
|
||||
/// Return a new `Location` by replacing the current value with a new one produced by `f`.
|
||||
public func modify(@noescape f: A -> A) -> Location {
|
||||
return Location(it: f(it), into: _into, outOf: _outOf, left: _left, right: _right)
|
||||
}
|
||||
|
||||
|
||||
public typealias Weave = A -> Unweave
|
||||
public typealias Unweave = (A -> Location?) -> Location?
|
||||
|
||||
|
||||
// MARK: - Constructors
|
||||
|
||||
public static func nullary(outOf: A -> Location?) -> Location? {
|
||||
return nil
|
||||
}
|
||||
|
||||
public static func unary(t1: A, _ weave: Weave, _ reconstruct: A -> A)(_ outOf: A -> Location?) -> Location? {
|
||||
return Location(flip(weave), { $0[0] } >>> reconstruct >>> outOf, [t1])
|
||||
}
|
||||
|
||||
public static func binary(t1: A, _ t2: A, _ weave: Weave, _ reconstruct: (A, A) -> A)(_ outOf: A -> Location?) -> Location? {
|
||||
return Location(flip(weave), { ($0[0], $0[1]) } >>> reconstruct >>> outOf, [t1, t2])
|
||||
}
|
||||
|
||||
public static func ternary(t1: A, _ t2: A, _ t3: A, _ weave: Weave, _ reconstruct: (A, A, A) -> A)(_ outOf: A -> Location?) -> Location? {
|
||||
return Location(flip(weave), { ($0[0], $0[1], $0[2]) } >>> reconstruct >>> outOf, [t1, t2, t3])
|
||||
}
|
||||
|
||||
public static func variadic<C: MutableCollectionType where C.Generator.Element == A, C.Index: BidirectionalIndexType>(ts: C, _ weave: Weave, _ reconstruct: C -> A)(_ outOf: A -> Location?) -> Location? {
|
||||
return Location(flip(weave), reconstruct >>> outOf, ts)
|
||||
}
|
||||
|
||||
public static func variadic<Key>(ts: [Key:A], _ weave: Weave, _ reconstruct: [Key:A] -> A)(_ outOf: A -> Location?) -> Location? {
|
||||
return Location(flip(weave), Dictionary.init >>> reconstruct >>> outOf, Array(ts))
|
||||
}
|
||||
|
||||
public static func explore(weave: Weave)(_ a : A) -> Location {
|
||||
return Location(it: a, into: flip(weave)(explore(weave) >>> Optional.Some), outOf: const(nil), left: const(nil), right: const(nil))
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Implementation details
|
||||
|
||||
private init?<C: MutableCollectionType where C.Generator.Element == A, C.Index: BidirectionalIndexType>(_ weave: (A -> Location?) -> A -> Location?, _ outOf: C -> Location?, _ ts: C) {
|
||||
func update(index: C.Index, _ ts: C)(_ f: C -> Location?)(_ a: A) -> Location? {
|
||||
guard ts.indices.contains(index) else { return nil }
|
||||
var copy = ts
|
||||
copy[index] = a
|
||||
return f(copy)
|
||||
}
|
||||
func into(index: C.Index)(_ ts: C) -> Location? {
|
||||
guard ts.indices.contains(index) else { return nil }
|
||||
return Location(it: ts[index], into: weave(update(index, ts)(into(index))), outOf: update(index, ts)(outOf), left: update(index, ts)(into(index.predecessor())), right: update(index, ts)(into(index.successor())))
|
||||
}
|
||||
guard let location = into(ts.startIndex)(ts) else { return nil }
|
||||
self = location
|
||||
}
|
||||
|
||||
private init?<C: MutableCollectionType, Key where C.Generator.Element == (Key, A), C.Index: BidirectionalIndexType>(_ weave: (A -> Location?) -> A -> Location?, _ outOf: C -> Location?, _ ts: C) {
|
||||
func update(index: C.Index, _ ts: C)(_ f: C -> Location?)(_ key: Key)(_ a: A) -> Location? {
|
||||
guard ts.indices.contains(index) else { return nil }
|
||||
var copy = ts
|
||||
copy[index] = (key, a)
|
||||
return f(copy)
|
||||
}
|
||||
func into(index: C.Index)(_ ts: C) -> Location? {
|
||||
guard ts.indices.contains(index) else { return nil }
|
||||
let (key, value) = ts[index]
|
||||
return Location(it: value, into: weave(update(index, ts)(into(index))(key)), outOf: update(index, ts)(outOf)(key), left: update(index, ts)(into(index.predecessor()))(key), right: update(index, ts)(into(index.successor()))(key))
|
||||
}
|
||||
guard let location = into(ts.startIndex)(ts) else { return nil }
|
||||
self = location
|
||||
}
|
||||
|
||||
|
||||
private let _into: A -> Location?
|
||||
private let _outOf: A -> Location?
|
||||
private let _left: A -> Location?
|
||||
private let _right: A -> Location?
|
||||
|
||||
|
||||
// MARK: SequenceType
|
||||
|
||||
public func generate() -> AnyGenerator<Location> {
|
||||
var current: Location? = self
|
||||
return anyGenerator {
|
||||
let next = current
|
||||
current = current?.next
|
||||
return next
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Flipping of curried functions.
|
||||
private func flip<A, B, C>(f: A -> B -> C)(_ b: B)(_ a: A) -> C {
|
||||
return f(a)(b)
|
||||
}
|
||||
|
||||
|
||||
import Prelude
|
@ -1,55 +0,0 @@
|
||||
/// A two-dimensional matrix of memoized values.
|
||||
///
|
||||
/// These values are populated by a function from the coordinates of a given cell to the matrix’s element type.
|
||||
///
|
||||
/// Values are retrieved by subscripting with row/column indices. Out-of-bound indices produce `nil` values, rather than asserting.
|
||||
public struct Matrix<A, I: ForwardIndexType> {
|
||||
public init(across: Range<I>, down: Range<I>, compute: (I, I) -> A) {
|
||||
self.init(across: across, down: down, values: constructRowMajor(across, down: down, forEach: { i, j in Memo { compute(i, j) } }))
|
||||
}
|
||||
|
||||
public let across: Range<I>
|
||||
public let down: Range<I>
|
||||
|
||||
private let width: I.Distance
|
||||
private let values: [Memo<A>]
|
||||
|
||||
public subscript (i: I, j: I) -> Memo<A>? {
|
||||
guard i != across.endIndex && j != down.endIndex else { return nil }
|
||||
let i = across.startIndex.distanceTo(i)
|
||||
let j = down.startIndex.distanceTo(j)
|
||||
return values[Int((i + j * width).toIntMax())]
|
||||
}
|
||||
|
||||
|
||||
// MARK: Functor
|
||||
|
||||
public func map<Other>(transform: A -> Other) -> Matrix<Other, I> {
|
||||
return Matrix<Other, I>(across: across, down: down, values: values.map { $0.map(transform) })
|
||||
}
|
||||
|
||||
|
||||
// MARK: Implementation details
|
||||
|
||||
private init(across: Range<I>, down: Range<I>, values: [Memo<A>]) {
|
||||
self.across = across
|
||||
self.down = down
|
||||
self.values = values
|
||||
self.width = across.count
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructs a row-major ordering of values produced with `forEach`.
|
||||
private func constructRowMajor<A, I: ForwardIndexType>(across: Range<I>, down: Range<I>, @noescape forEach: (I, I) -> A) -> [A] {
|
||||
var values: [A] = []
|
||||
values.reserveCapacity(Int(across.count.toIntMax()) * Int(down.count.toIntMax()))
|
||||
for j in down {
|
||||
for i in across {
|
||||
values.append(forEach(i, j))
|
||||
}
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
|
||||
import Memo
|
@ -1,32 +0,0 @@
|
||||
/// An operation of diffing over terms or collections of terms.
|
||||
public enum Operation<Recur, Term, Diff> {
|
||||
/// 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
|
||||
|
||||
extension Operation {
|
||||
public func map<Other>(transform: Recur -> Other) -> Operation<Other, Term, Diff> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
import Prelude
|
@ -1,12 +0,0 @@
|
||||
extension Optional {
|
||||
static func equals(param: (Wrapped, Wrapped) -> Bool)(_ left: Wrapped?, _ right: Wrapped?) -> Bool {
|
||||
switch (left, right) {
|
||||
case let (.Some(a), .Some(b)):
|
||||
return param(a, b)
|
||||
case (.None, .None):
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
@ -1,175 +0,0 @@
|
||||
/// A patch to some part of a `Syntax` tree.
|
||||
public enum Patch<A>: CustomDebugStringConvertible {
|
||||
case Replace(A, A)
|
||||
case Insert(A)
|
||||
case Delete(A)
|
||||
|
||||
public var state: (before: A?, after: A?) {
|
||||
switch self {
|
||||
case let .Replace(a, b):
|
||||
return (a, b)
|
||||
case let .Insert(b):
|
||||
return (nil, b)
|
||||
case let .Delete(a):
|
||||
return (a, nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public var inverse: Patch {
|
||||
switch self {
|
||||
case let .Replace(a, b):
|
||||
return .Replace(b, a)
|
||||
case let .Insert(b):
|
||||
return .Delete(b)
|
||||
case let .Delete(a):
|
||||
return .Insert(a)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: CustomDebugStringConvertible
|
||||
|
||||
public var debugDescription: String {
|
||||
switch self {
|
||||
case let .Replace(a, b):
|
||||
return ".Replace(\(String(reflecting: a)), \(String(reflecting: b)))"
|
||||
case let .Insert(b):
|
||||
return ".Insert(\(String(reflecting: b)))"
|
||||
case let .Delete(a):
|
||||
return ".Delete(\(String(reflecting: a)))"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Functor
|
||||
|
||||
extension Patch {
|
||||
public func map<B>(@noescape transform: A throws -> B) rethrows -> Patch<B> {
|
||||
switch self {
|
||||
case let .Replace(a, b):
|
||||
return try .Replace(transform(a), transform(b))
|
||||
case let .Delete(a):
|
||||
return try .Delete(transform(a))
|
||||
case let .Insert(b):
|
||||
return try .Insert(transform(b))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Equality
|
||||
|
||||
extension Patch {
|
||||
public static func equals(param: (A, A) -> Bool)(_ left: Patch, _ right: Patch) -> Bool {
|
||||
return Optional.equals(param)(left.state.before, right.state.before)
|
||||
&& Optional.equals(param)(left.state.after, right.state.after)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Cost calculations
|
||||
|
||||
extension Patch {
|
||||
/// Returns a function which computes the size of a `patch` as the sum of the sizes of its terms, as computed by `size`.
|
||||
public static func sum(@noescape size: A -> Int)(_ patch: Patch) -> Int {
|
||||
switch patch {
|
||||
case let .Replace(a, b):
|
||||
return size(a) + size(b)
|
||||
case let .Insert(b):
|
||||
return size(b)
|
||||
case let .Delete(a):
|
||||
return size(a)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a function which computes the size of a `patch` as the absolute difference of the sizes of its terms, as computed by `size`.
|
||||
public static func difference(@noescape size: A -> Int)(_ patch: Patch) -> Int {
|
||||
return abs((patch.state.before.map(size) ?? 0) - (patch.state.after.map(size) ?? 0))
|
||||
}
|
||||
}
|
||||
|
||||
extension Patch where A: TermType {
|
||||
/// Computes the size of a `patch` as the sum of the sizes of its terms.
|
||||
public static func sum(patch: Patch) -> Int {
|
||||
return sum { $0.size } (patch)
|
||||
}
|
||||
|
||||
/// Computes the size of a `patch` as the absolute difference of the sizes of its terms.
|
||||
public static func difference(patch: Patch) -> Int {
|
||||
return difference { $0.size } (patch)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - JSON
|
||||
|
||||
extension Patch {
|
||||
public func JSON(ifLeaf: A -> Doubt.JSON) -> Doubt.JSON {
|
||||
switch self {
|
||||
case let .Replace(a, b):
|
||||
return [
|
||||
"replace": [
|
||||
"before": ifLeaf(a),
|
||||
"after": ifLeaf(b),
|
||||
]
|
||||
]
|
||||
case let .Insert(b):
|
||||
return [
|
||||
"insert": ifLeaf(b),
|
||||
]
|
||||
case let .Delete(a):
|
||||
return [
|
||||
"delete": ifLeaf(a)
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension Patch where A: CustomJSONConvertible {
|
||||
public var JSON: Doubt.JSON {
|
||||
return JSON { $0.JSON }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - PatchType
|
||||
|
||||
/// A hack to enable constrained extensions on `Free<Leaf, Annotation, Patch<Term: CofreeType where Term.Leaf == Leaf, Term.Annotation == Annotation>`.
|
||||
public protocol PatchType {
|
||||
typealias Element
|
||||
|
||||
var state: (before: Element?, after: Element?) { get }
|
||||
|
||||
var inverse: Self { get }
|
||||
|
||||
init(replacing before: Element, with after: Element)
|
||||
init(deleting before: Element)
|
||||
init(inserting after: Element)
|
||||
}
|
||||
|
||||
extension Patch: PatchType {
|
||||
public init(replacing before: A, with after: A) {
|
||||
self = .Replace(before, after)
|
||||
}
|
||||
|
||||
public init(deleting before: A) {
|
||||
self = .Delete(before)
|
||||
}
|
||||
|
||||
public init(inserting after: A) {
|
||||
self = .Insert(after)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension PatchType where Element: CofreeType, Element.Annotation == Range<String.Index> {
|
||||
public func JSON(a a: String, b: String) -> Doubt.JSON {
|
||||
return [
|
||||
"before": state.before.map { $0.JSON(a) } ?? nil,
|
||||
"after": state.after.map { $0.JSON(b) } ?? nil,
|
||||
]
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
extension RangeReplaceableCollectionType {
|
||||
static func cons(head: Generator.Element, _ tail: Self) -> Self {
|
||||
return [ head ] + tail
|
||||
}
|
||||
}
|
@ -1,83 +0,0 @@
|
||||
/// Computes the SES (shortest edit script), i.e. the shortest sequence of diffs (`Free<Leaf, Annotation, Patch<Term>>`) for two arrays of `Term`s which would suffice to transform `a` into `b`.
|
||||
///
|
||||
/// This is computed w.r.t. an `equals` function, which computes the equality of leaf nodes within terms, and a `recur` function, which produces diffs representing matched-up terms.
|
||||
public func SES<Leaf, Annotation, C: CollectionType>(a: C, _ b: C, cost: Free<Leaf, Annotation, Patch<C.Generator.Element>> -> Int, recur: (C.Generator.Element, C.Generator.Element) -> Free<Leaf, Annotation, Patch<C.Generator.Element>>?) -> [Free<Leaf, Annotation, Patch<C.Generator.Element>>] {
|
||||
typealias Diff = Free<Leaf, Annotation, Patch<C.Generator.Element>>
|
||||
|
||||
if a.isEmpty { return b.map { .Insert($0) } }
|
||||
if b.isEmpty { return a.map { .Delete($0) } }
|
||||
|
||||
// A matrix whose values are streams representing paths through the edit graph, carrying both the diff & the cost of the remainder of the path.
|
||||
var matrix: Matrix<Stream<(Diff, Int)>, C.Index>!
|
||||
matrix = Matrix(across: a.startIndex..<a.endIndex.successor(), down: b.startIndex..<b.endIndex.successor()) { i, j in
|
||||
// Some explanation is warranted:
|
||||
//
|
||||
// 1. `matrix` captures itself during construction, because each vertex in the edit graph depends on other vertices. This is safe, because a) `Matrix` populates its fields lazily, and b) vertices only depend on those vertices downwards and rightwards of them.
|
||||
//
|
||||
// 2. `matrix` is sized bigger than `a.count` x `b.count`. This is safe, because a) we only get a[i]/b[j] when right/down are non-nil (respectively), and b) right/down are found by looking up elements (i + 1, j) & (i, j + 1) in the matrix, which returns `nil` when out of bounds. So we only access a[i] and b[j] when i and j are in bounds.
|
||||
|
||||
let right = matrix[i.successor(), j]
|
||||
let down = matrix[i, j.successor()]
|
||||
let diagonal = matrix[i.successor(), j.successor()]
|
||||
|
||||
if let diagonal = diagonal, right = right, down = down {
|
||||
let here = recur(a[i], b[j])
|
||||
if let diagonalDiff = here {
|
||||
let hereCost = cost(diagonalDiff)
|
||||
let diagonalCost = hereCost + (diagonal.value.first?.1 ?? 0)
|
||||
// If the diff at this vertex is zero-cost, we’re not going to find a cheaper one either rightwards or downwards. We can therefore short-circuit selecting the best outgoing edge and save ourselves evaluating the entire row rightwards and the entire column downwards from this point.
|
||||
//
|
||||
// Thus, in the best case (two equal sequences), we now complete in O(n + m). However, this optimization only applies to equalities at the beginning of the edit graph; once inequalities are encountered, the remainder of the diff is effectively O(nm).
|
||||
guard hereCost != 0 else { return .Cons((diagonalDiff, diagonalCost), diagonal) }
|
||||
|
||||
let rightDiff = Diff.Delete(a[i])
|
||||
let rightCost = cost(rightDiff) + (right.value.first?.1 ?? 0)
|
||||
let downDiff = Diff.Insert(b[j])
|
||||
let downCost = cost(downDiff) + (down.value.first?.1 ?? 0)
|
||||
|
||||
if rightCost < downCost && rightCost < diagonalCost {
|
||||
return .Cons((rightDiff, rightCost), right)
|
||||
} else if downCost < diagonalCost {
|
||||
return .Cons((downDiff, downCost), down)
|
||||
} else {
|
||||
return .Cons((diagonalDiff, diagonalCost), diagonal)
|
||||
}
|
||||
} else {
|
||||
let rightDiff = Diff.Delete(a[i])
|
||||
let rightCost = cost(rightDiff) + (right.value.first?.1 ?? 0)
|
||||
let downDiff = Diff.Insert(b[j])
|
||||
let downCost = cost(downDiff) + (down.value.first?.1 ?? 0)
|
||||
|
||||
if rightCost < downCost {
|
||||
return .Cons((rightDiff, rightCost), right)
|
||||
} else {
|
||||
return .Cons((downDiff, downCost), down)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// right extent of the edit graph; can only move down
|
||||
if let down = down {
|
||||
let diff = Diff.Insert(b[j])
|
||||
let cost = cost(diff) + (down.value.first?.1 ?? 0)
|
||||
return .Cons((diff, cost), down)
|
||||
}
|
||||
|
||||
// bottom extent of the edit graph; can only move right
|
||||
if let right = right {
|
||||
let diff = Diff.Delete(a[i])
|
||||
let cost = cost(diff) + (right.value.first?.1 ?? 0)
|
||||
return .Cons((diff, cost), right)
|
||||
}
|
||||
|
||||
// bottom-right corner of the edit graph
|
||||
return Stream.Nil
|
||||
}
|
||||
|
||||
return Array(matrix[a.startIndex, b.startIndex]!.value.map { diff, _ in diff })
|
||||
}
|
||||
|
||||
|
||||
import Memo
|
||||
import Prelude
|
||||
import Stream
|
@ -1,9 +0,0 @@
|
||||
extension StringLiteralConvertible {
|
||||
public init(unicodeScalarLiteral: Self.StringLiteralType) {
|
||||
self.init(stringLiteral: unicodeScalarLiteral)
|
||||
}
|
||||
|
||||
public init(extendedGraphemeClusterLiteral: Self.StringLiteralType) {
|
||||
self.init(stringLiteral: extendedGraphemeClusterLiteral)
|
||||
}
|
||||
}
|
@ -1,128 +0,0 @@
|
||||
/// A node in a syntax tree. Expressed algebraically to enable representation of both normal syntax trees and their diffs.
|
||||
public enum Syntax<Recur, A>: CustomDebugStringConvertible {
|
||||
case Leaf(A)
|
||||
case Indexed([Recur])
|
||||
case Fixed([Recur])
|
||||
case Keyed([String:Recur])
|
||||
|
||||
|
||||
// MARK: Functor
|
||||
|
||||
public func map<T>(@noescape transform: Recur throws -> T) rethrows -> Syntax<T, A> {
|
||||
switch self {
|
||||
case let .Leaf(n):
|
||||
return .Leaf(n)
|
||||
case let .Indexed(x):
|
||||
return try .Indexed(x.map(transform))
|
||||
case let .Fixed(x):
|
||||
return try .Fixed(x.map(transform))
|
||||
case let .Keyed(d):
|
||||
return try .Keyed(Dictionary(elements: d.map { try ($0, transform($1)) }))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: CustomDebugStringConvertible
|
||||
|
||||
public var debugDescription: String {
|
||||
switch self {
|
||||
case let .Leaf(n):
|
||||
return ".Leaf(\(n))"
|
||||
case let .Indexed(x):
|
||||
return ".Indexed(\(String(reflecting: x)))"
|
||||
case let .Fixed(x):
|
||||
return ".Fixed(\(String(reflecting: x)))"
|
||||
case let .Keyed(d):
|
||||
return ".Keyed(\(String(reflecting: d)))"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Hylomorphism
|
||||
|
||||
/// Hylomorphism through `Syntax`.
|
||||
///
|
||||
/// 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`).
|
||||
///
|
||||
/// 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.
|
||||
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)))
|
||||
}
|
||||
|
||||
/// Reiteration through `Syntax`.
|
||||
///
|
||||
/// 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`).
|
||||
///
|
||||
/// 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 `Annotation` & `Syntax` pairs.
|
||||
public func hylo<A, B, Leaf, Annotation>(down: (Annotation, Syntax<B, Leaf>) -> B, _ up: A -> (Annotation, Syntax<A, Leaf>))(_ a: A) -> B {
|
||||
let (annotation, syntax) = up(a)
|
||||
return down(annotation, syntax.map(hylo(down, up)))
|
||||
}
|
||||
|
||||
|
||||
// MARK: - ArrayLiteralConvertible
|
||||
|
||||
extension Syntax: ArrayLiteralConvertible {
|
||||
public init(arrayLiteral: Recur...) {
|
||||
self = .Indexed(arrayLiteral)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - DictionaryLiteralConvertible
|
||||
|
||||
extension Syntax: DictionaryLiteralConvertible {
|
||||
public init(dictionaryLiteral elements: (String, Recur)...) {
|
||||
self = .Keyed(Dictionary(elements: elements))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Equality
|
||||
|
||||
extension Syntax {
|
||||
public static func equals(leaf leaf: (A, A) -> Bool, recur: (Recur, Recur) -> Bool)(_ left: Syntax<Recur, A>, _ right: Syntax<Recur, A>) -> Bool {
|
||||
switch (left, right) {
|
||||
case let (.Leaf(l1), .Leaf(l2)):
|
||||
return leaf(l1, l2)
|
||||
case let (.Indexed(v1), .Indexed(v2)) where v1.count == v2.count:
|
||||
return zip(v1, v2).reduce(true) { $0 && recur($1) }
|
||||
case let (.Fixed(v1), .Fixed(v2)) where v1.count == v2.count:
|
||||
return zip(v1, v2).reduce(true) { $0 && recur($1) }
|
||||
case let (.Keyed(d1), .Keyed(d2)) where Set(d1.keys) == Set(d2.keys):
|
||||
return d1.keys.reduce(true) { $0 && recur(d1[$1]!, d2[$1]!) }
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func == <F: Equatable, A: Equatable> (left: Syntax<F, A>, right: Syntax<F, A>) -> Bool {
|
||||
return Syntax.equals(leaf: ==, recur: ==)(left, right)
|
||||
}
|
||||
|
||||
|
||||
// MARK: - JSON
|
||||
|
||||
extension Syntax {
|
||||
public func JSON(@noescape leaf leaf: A -> Doubt.JSON, @noescape recur: Recur -> Doubt.JSON) -> Doubt.JSON {
|
||||
switch self {
|
||||
case let .Leaf(a):
|
||||
return [ "leaf": leaf(a) ]
|
||||
case let .Indexed(a):
|
||||
return [ "indexed": .Array(a.map(recur)) ]
|
||||
case let .Fixed(a):
|
||||
return [ "fixed": .Array(a.map(recur)) ]
|
||||
case let .Keyed(d):
|
||||
return [ "keyed": .Dictionary(Dictionary(elements: d.map { ($0, recur($1)) })) ]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
import Prelude
|
@ -1,59 +0,0 @@
|
||||
/// The type of terms.
|
||||
public protocol TermType {
|
||||
typealias Leaf
|
||||
|
||||
var unwrap: Syntax<Self, Leaf> { get }
|
||||
}
|
||||
|
||||
|
||||
extension TermType {
|
||||
public static func unwrap(term: Self) -> Syntax<Self, Leaf> {
|
||||
return term.unwrap
|
||||
}
|
||||
|
||||
/// Catamorphism over `TermType`s.
|
||||
///
|
||||
/// Folds the tree encoded by the receiver into a single value by recurring top-down through the tree, applying `transform` to leaves, then to branches, and so forth.
|
||||
public func cata<Result>(transform: Syntax<Result, Leaf> throws -> Result) rethrows -> Result {
|
||||
return try transform(unwrap.map { try $0.cata(transform) })
|
||||
}
|
||||
|
||||
/// Paramorphism over `TermType`s.
|
||||
///
|
||||
/// Folds the tree encoded by the receiver into a single value by recurring top-down through the tree, applying `transform` to leaves, then to branches, and so forth. Each recursive instance is made available in the `Syntax` alongside the result value at that node.
|
||||
public func para<Result>(transform: Syntax<(Self, Result), Leaf> throws -> Result) rethrows -> Result {
|
||||
return try transform(unwrap.map { try ($0, $0.para(transform)) })
|
||||
}
|
||||
|
||||
|
||||
/// The count of nodes in the receiver.
|
||||
///
|
||||
/// This is used to compute the cost of patches, such that a patch inserting a very large tree will be charged approximately the same as a very large tree consisting of many small patches.
|
||||
public var size: Int {
|
||||
func size(term: Self) -> Int {
|
||||
switch term.unwrap {
|
||||
case .Leaf:
|
||||
return 1
|
||||
case let .Indexed(a):
|
||||
return a.reduce(0) { $0 + size($1) }
|
||||
case let .Fixed(a):
|
||||
return a.reduce(0) { $0 + size($1) }
|
||||
case let .Keyed(a):
|
||||
return a.reduce(0) { $0 + size($1.1) }
|
||||
}
|
||||
}
|
||||
return size(self)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Equality
|
||||
|
||||
extension TermType {
|
||||
public static func equals(leaf: (Leaf, Leaf) -> Bool)(_ a: Self, _ b: Self) -> Bool {
|
||||
return Syntax.equals(leaf: leaf, recur: equals(leaf))(a.unwrap, b.unwrap)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
import Prelude
|
@ -1,66 +0,0 @@
|
||||
final class DiffTests: XCTestCase {
|
||||
override static func setUp() {
|
||||
sranddev()
|
||||
}
|
||||
|
||||
typealias Term = RangedTerm.Term
|
||||
typealias Diff = Free<String, (Term.Annotation, Term.Annotation), Patch<Term>>
|
||||
|
||||
let interpreter = Interpreter<Term>(equal: ==, comparable: const(true), cost: Diff.sum(const(1)))
|
||||
|
||||
func testEqualTermsProduceIdentityDiffs() {
|
||||
property("equal terms produce identity diffs") <- forAll { (term: RangedTerm) in
|
||||
Diff.sum(const(1))(self.interpreter.run(term.term, term.term)) == 0
|
||||
}
|
||||
}
|
||||
|
||||
func testRecursivelyCopiedDiffsHaveNoPatches() {
|
||||
property("recursively copying a term into a diff produces no patches") <- forAll { (term: RangedTerm) in
|
||||
Free.sum(const(1))(Free<Term.Leaf, Term.Annotation, Patch<Term>>(term.term)) == 0
|
||||
}
|
||||
}
|
||||
|
||||
func testInequalTermsProduceNonIdentityDiffs() {
|
||||
property("inequal terms produce non-identity diffs") <- forAll { (diff: RangedDiff) in
|
||||
(!Term.equals(annotation: const(true), leaf: ==)(diff.a.term, diff.b.term)) ==> Diff.sum(const(1))(diff.diff) > 0
|
||||
}
|
||||
}
|
||||
|
||||
func testEqualityIsReflexive() {
|
||||
property("equality is reflexive") <- forAll { (diff: RangedDiff) in
|
||||
equal(diff.diff, diff.diff)
|
||||
}
|
||||
}
|
||||
|
||||
func testDoubleInversionIsIdempotent() {
|
||||
property("double inversion is idempotent") <- forAll { (diff: RangedDiff) in
|
||||
equal(diff.diff, diff.diff.inverse.inverse)
|
||||
}
|
||||
}
|
||||
|
||||
func testOriginalTermsAreRecoverable() {
|
||||
let equal = Cofree<String, ()>.equals(annotation: const(true), leaf: ==)
|
||||
property("before state is recoverable") <- forAll { (diff: RangedDiff) in
|
||||
diff.diff.map { $0.map { $0.map(const(())) } }.before.map {
|
||||
equal($0, diff.a.stripped.term)
|
||||
} ?? false
|
||||
}
|
||||
|
||||
property("after state is recoverable") <- forAll { (diff: RangedDiff) in
|
||||
diff.diff.map { $0.map { $0.map(const(())) } }.after.map {
|
||||
equal($0, diff.b.stripped.term)
|
||||
} ?? false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func equal(a: DiffTests.Diff, _ b: DiffTests.Diff) -> Bool {
|
||||
return Free.equals(pure: Patch.equals(Cofree.equals(annotation: ==, leaf: ==)), leaf: ==, annotation: const(true))(a, b)
|
||||
}
|
||||
|
||||
|
||||
@testable import Doubt
|
||||
import Prelude
|
||||
import SwiftCheck
|
||||
import XCTest
|
@ -1,24 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>BNDL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
</plist>
|
@ -1,49 +0,0 @@
|
||||
final class InterpreterTests: XCTestCase {
|
||||
func testRestrictsComparisons() {
|
||||
let comparable: (Term, Term) -> Bool = { $0.extract == 0 && $1.extract == 0 }
|
||||
let i = Interpreter(equal: ==, comparable: comparable, cost: const(1))
|
||||
let d = i.run(a, b)
|
||||
assert(d, ==, restricted)
|
||||
}
|
||||
|
||||
func testComparisonsOfDisjointlyCategorizedTermsAreRestricted() {
|
||||
var effects = 0
|
||||
let categorize: Term -> Set<Int> = { $0.extract == 0 ? [ 0 ] : [ effects++ ] }
|
||||
assert(Interpreter(equal: ==, comparable: Interpreter.comparable(categorize), cost: const(1)).run(a, b), ==, restricted)
|
||||
}
|
||||
|
||||
func testUnrestrictedComparisonsComputeReplacements() {
|
||||
assert(Interpreter(equal: ==, comparable: const(true), cost: const(1)).run(a, b), ==, unrestricted)
|
||||
}
|
||||
|
||||
func testComparisonsOfUncategorizedTermsAreUnrestricted() {
|
||||
assert(Interpreter(equal: ==, comparable: Interpreter.comparable { _ in Set<String>() }, cost: const(1)).run(a, b), ==, unrestricted)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private typealias Term = Cofree<String, Int>
|
||||
private typealias Diff = Free<String, (Int, Int), Patch<Term>>
|
||||
|
||||
private let a = Term(0, [ Term(1, .Leaf("a")), Term(2, .Leaf("b")), Term(3, .Leaf("c")) ])
|
||||
private let b = Term(0, [ Term(1, .Leaf("c")), Term(2, .Leaf("b")), Term(3, .Leaf("a")) ])
|
||||
|
||||
private let restricted = Diff.Roll((0, 0), [
|
||||
.Pure(.Insert(Term(1, .Leaf("c")))),
|
||||
.Pure(.Delete(Term(1, .Leaf("a")))),
|
||||
.Roll((2, 2), .Leaf("b")),
|
||||
.Pure(.Insert(Term(3, .Leaf("a")))),
|
||||
.Pure(.Delete(Term(3, .Leaf("c")))),
|
||||
])
|
||||
|
||||
private let unrestricted = Diff.Roll((0, 0), [
|
||||
.Pure(.Replace(Term(1, .Leaf("a")), Term(1, .Leaf("c")))),
|
||||
.Roll((2, 2), .Leaf("b")),
|
||||
.Pure(.Replace(Term(3, .Leaf("c")), Term(3, .Leaf("a")))),
|
||||
])
|
||||
|
||||
|
||||
import Assertions
|
||||
@testable import Doubt
|
||||
import Prelude
|
||||
import XCTest
|
@ -1,28 +0,0 @@
|
||||
import Assertions
|
||||
import Doubt
|
||||
import Prelude
|
||||
import Madness
|
||||
import XCTest
|
||||
|
||||
final class JSONParserTests: XCTestCase {
|
||||
func testCapturesKeysInKeyedElements() {
|
||||
let dictWithArray = "{ \"hello\":\n [\"world\",\n \"sailor\"\n ]}"
|
||||
let array: Cofree<JSONLeaf, Range<Int>> = Cofree(15..<41, .Indexed([
|
||||
Cofree(16..<23, .Leaf(.String("world"))),
|
||||
Cofree(29..<37, .Leaf(.String("sailor")))
|
||||
]))
|
||||
|
||||
let fixedPairs: Cofree<JSONLeaf, Range<Int>> = Cofree(2..<41, .Fixed([Cofree(2..<9, .Leaf(.String("hello"))), array]))
|
||||
|
||||
let expected: Cofree<JSONLeaf, Range<Int>> = Cofree(0..<42, .Keyed(["hello": fixedPairs]))
|
||||
let actual = Madness.parse(json, input: dictWithArray).right!
|
||||
let startRange = actual.extract.2
|
||||
let new: Cofree<JSONLeaf, Range<Int>> = actual.map({ tuple in
|
||||
let startI: Int = startRange.startIndex.distanceTo(tuple.2.startIndex)
|
||||
let endI: Int = startRange.startIndex.distanceTo(tuple.2.endIndex)
|
||||
return Range(start: startI, end: endI)
|
||||
})
|
||||
|
||||
assert(new, == , expected)
|
||||
}
|
||||
}
|
@ -1,107 +0,0 @@
|
||||
final class LocationTests: XCTestCase {
|
||||
func testExplorationOfATermBeginsAtTheExploredTerm() {
|
||||
assert(term.explore().it, ==, term)
|
||||
}
|
||||
|
||||
func testCannotMoveUpwardsAtTheStartOfAnExploration() {
|
||||
assert(term.explore().outOf?.it, ==, nil)
|
||||
}
|
||||
|
||||
func testCannotMoveSidewaysAtTheStartOfAnExploration() {
|
||||
assert(term.explore().left?.it, ==, nil)
|
||||
assert(term.explore().right?.it, ==, nil)
|
||||
}
|
||||
|
||||
func testCannotMoveDownwardsFromLeaves() {
|
||||
assert(leafA.explore().into?.it, ==, nil)
|
||||
}
|
||||
|
||||
func testCanMoveDownwardsIntoBranches() {
|
||||
assert(term.explore().into?.it, ==, leafA)
|
||||
}
|
||||
|
||||
func testCanMoveBackUpwards() {
|
||||
assert(term.explore().into?.outOf?.it, ==, term)
|
||||
}
|
||||
|
||||
func testCannotMoveLeftwardsFromFirstChildOfBranch() {
|
||||
assert(term.explore().into?.left?.it, ==, nil)
|
||||
}
|
||||
|
||||
func testCanMoveRightwardsFromLeftmostChildOfLongBranch() {
|
||||
assert(term.explore().into?.right?.it, ==, leafB)
|
||||
}
|
||||
|
||||
func testCanExploreBranchesDeeply() {
|
||||
assert(term.explore().into?.right?.right?.into?.it, ==, innerLeafB)
|
||||
}
|
||||
|
||||
func testCanMoveBackUpwardsFromDeepExplorations() {
|
||||
assert(term.explore().into?.right?.right?.into?.right?.outOf?.outOf?.it, ==, term)
|
||||
}
|
||||
|
||||
func testCanReturnToStartOfExplorationFromArbitrarilyDeepNodes() {
|
||||
assert(term.explore().into?.right?.right?.into?.right?.root.it, ==, term)
|
||||
}
|
||||
|
||||
func testSequenceIsPreOrderDepthFirstTraversal() {
|
||||
assert(term.explore().map { $0.it.extract }, ==, [ 0, 1, 2, 3, 5, 4 ])
|
||||
}
|
||||
|
||||
func testModifyReplacesSubtrees() {
|
||||
assert(term.explore().into?.modify(const(leafB)).right?.outOf?.it, ==, Cofree(0, .Indexed([ leafB, leafB, keyed ])))
|
||||
}
|
||||
|
||||
func testMultipleModificationsReplaceMultipleSubtrees() {
|
||||
assert(term.explore().into?.modify(const(leafB)).right?.modify(const(leafA)).outOf?.it, ==, Cofree(0, .Indexed([ leafB, leafA, keyed ])))
|
||||
}
|
||||
|
||||
func testModificationsPreserveKeys() {
|
||||
assert(keyed.explore().into?.modify(const(leafA)).root.it, ==, Cofree(3, .Keyed([ "a": innerLeafA, "b": leafA ])))
|
||||
}
|
||||
|
||||
func testDeepModificationsReplaceDeepSubtrees() {
|
||||
assert(term.explore().into?.modify(const(leafB)).right?.modify(const(leafA)).right?.into?.modify(const(innerLeafA)).right?.modify(const(innerLeafB)).root.it, ==, Cofree(0, .Indexed([ leafB, leafA, Cofree(3, .Keyed([ "a": innerLeafB, "b": innerLeafA ])) ])))
|
||||
}
|
||||
|
||||
func testModificationsCanRefineDiffs() {
|
||||
assert(diff.explore().into?.right?.modify(const(refined)).root.it, ==, Free.Roll(0, .Indexed([ Free.Roll(1, .Leaf("a string")), refined ])))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private let leafA = Cofree(1, .Leaf("a string"))
|
||||
private let leafB = Cofree(2, .Leaf("b string"))
|
||||
private let innerLeafA = Cofree(4, .Leaf("a nested string"))
|
||||
private let innerLeafB = Cofree(5, .Leaf("b nested string"))
|
||||
private let keyed = Cofree(3, .Keyed([
|
||||
"a": innerLeafA,
|
||||
"b": innerLeafB,
|
||||
]))
|
||||
private let term: Cofree<String, Int> = Cofree(0, .Indexed([
|
||||
leafA,
|
||||
leafB,
|
||||
keyed,
|
||||
]))
|
||||
|
||||
private let diff: Free<String, Int, Patch<Cofree<String, Int>>> = Free.Roll(0, .Indexed([
|
||||
Free.Roll(1, .Leaf("a string")),
|
||||
Free.Pure(Patch.Replace(leafA, leafB)), // coarse-grained diff of two leaf nodes
|
||||
]))
|
||||
// fine-grained diff of same
|
||||
private let refined = Free.Roll(2, .Indexed([
|
||||
Free.Pure(Patch.Replace(Cofree(1, .Leaf("a")), Cofree(2, .Leaf("b")))),
|
||||
Free.Roll(4, .Leaf(" ")),
|
||||
Free.Roll(5, .Leaf("s")),
|
||||
Free.Roll(6, .Leaf("t")),
|
||||
Free.Roll(7, .Leaf("r")),
|
||||
Free.Roll(8, .Leaf("i")),
|
||||
Free.Roll(9, .Leaf("n")),
|
||||
Free.Roll(10, .Leaf("g")),
|
||||
]))
|
||||
|
||||
|
||||
import Assertions
|
||||
@testable import Doubt
|
||||
import Prelude
|
||||
import XCTest
|
@ -1,29 +0,0 @@
|
||||
struct RangedDiff {
|
||||
typealias Diff = Free<String, (RangedTerm.Term.Annotation, RangedTerm.Term.Annotation), Patch<RangedTerm.Term>>
|
||||
|
||||
let a: RangedTerm
|
||||
let b: RangedTerm
|
||||
let diff: Diff
|
||||
}
|
||||
|
||||
extension RangedDiff: Arbitrary {
|
||||
static let interpreter = Interpreter<RangedTerm.Term>(equal: ==, comparable: const(true), cost: Diff.sum(Patch.difference))
|
||||
|
||||
static var arbitrary: Gen<RangedDiff> {
|
||||
return RangedTerm.arbitrary.bind { a in
|
||||
RangedTerm.arbitrary.fmap { b in
|
||||
RangedDiff(a: a, b: b, diff: interpreter.run(a.term, b.term))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static func shrink(diff: RangedDiff) -> [RangedDiff] {
|
||||
return RangedTerm.shrink(diff.a).map { RangedDiff(a: $0, b: diff.b, diff: interpreter.run($0.term, diff.b.term)) }
|
||||
+ RangedTerm.shrink(diff.b).map { RangedDiff(a: diff.a, b: $0, diff: interpreter.run(diff.a.term, $0.term)) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
import Doubt
|
||||
import Prelude
|
||||
import SwiftCheck
|
@ -1,24 +0,0 @@
|
||||
struct RangedTerm {
|
||||
typealias Term = Cofree<String, Range<String.Index>>
|
||||
let term: Term
|
||||
let source: String
|
||||
|
||||
var stripped: UnannotatedTerm {
|
||||
return UnannotatedTerm(term: term.map(const(())))
|
||||
}
|
||||
}
|
||||
|
||||
extension RangedTerm: Arbitrary {
|
||||
static var arbitrary: Gen<RangedTerm> {
|
||||
return UnannotatedTerm.arbitrary.fmap { $0.arranged }
|
||||
}
|
||||
|
||||
static func shrink(term: RangedTerm) -> [RangedTerm] {
|
||||
return UnannotatedTerm.shrink(term.stripped).map { $0.arranged }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@testable import Doubt
|
||||
import Prelude
|
||||
import SwiftCheck
|
@ -1,67 +0,0 @@
|
||||
final class SESTests: XCTestCase {
|
||||
func testSESOverEmptyCollectionsIsEmpty() {
|
||||
assert(SES([], []), ==, [])
|
||||
}
|
||||
|
||||
func testSESOverEmptyAndNonEmptyCollectionsIsInsertions() {
|
||||
assert(SES([], [ a, b ]), ==, [ .Insert(a), .Insert(b) ])
|
||||
}
|
||||
|
||||
func testSESOverNonEmptyAndEmptyCollectionsIsDeletions() {
|
||||
assert(SES([ a, b ], []), ==, [ .Delete(a), .Delete(b) ])
|
||||
}
|
||||
|
||||
func testSESCanInsertAtHead() {
|
||||
assert(SES([ a, b, c ], [ d, a, b, c ]), ==, [ .Insert(d), Copy(a), Copy(b), Copy(c) ])
|
||||
}
|
||||
|
||||
func testSESCanDeleteAtHead() {
|
||||
assert(SES([ d, a, b, c ], [ a, b, c ]), ==, [ .Delete(d), Copy(a), Copy(b), Copy(c) ])
|
||||
}
|
||||
|
||||
func testSESCanInsertInMiddle() {
|
||||
assert(SES([ a, b, c ], [ a, d, b, c ]), ==, [ Copy(a), .Insert(d), Copy(b), Copy(c) ])
|
||||
}
|
||||
|
||||
func testSESCanDeleteInMiddle() {
|
||||
assert(SES([ a, d, b, c ], [ a, b, c ]), ==, [ Copy(a), .Delete(d), Copy(b), Copy(c) ])
|
||||
}
|
||||
|
||||
func testInsertsAtEnd() {
|
||||
assert(SES([ a, b, c ], [ a, b, c, d ]), ==, [ Copy(a), Copy(b), Copy(c), .Insert(d) ])
|
||||
}
|
||||
|
||||
func testDeletesAtEnd() {
|
||||
assert(SES([ a, b, c, d ], [ a, b, c ]), ==, [ Copy(a), Copy(b), Copy(c), .Delete(d) ])
|
||||
}
|
||||
|
||||
func testSESOfLongerSequences() {
|
||||
assert(SES([ a, b, c, a, b, b, a ], [ c, b, a, b, a, c ]), ==, [ .Insert(c), .Delete(a), Copy(b), .Delete(c), Copy(a), Copy(b), .Delete(b), Copy(a), .Insert(c) ])
|
||||
}
|
||||
}
|
||||
|
||||
private typealias Term = Cofree<String, ()>
|
||||
private typealias Diff = Free<String, (), Patch<Term>>
|
||||
|
||||
private func Copy(term: Term) -> Diff {
|
||||
return hylo(Diff.Introduce(()), Term.unwrap)(term)
|
||||
}
|
||||
|
||||
private let a = Term((), .Leaf("a"))
|
||||
private let b = Term((), .Leaf("b"))
|
||||
private let c = Term((), .Leaf("c"))
|
||||
private let d = Term((), .Leaf("d"))
|
||||
|
||||
private func SES(a: [Term], _ b: [Term]) -> [Diff] {
|
||||
return SES(a, b, cost: const(1)) { Cofree.equals(annotation: const(true), leaf: ==)($0, $1) ? Copy($1) : nil }
|
||||
}
|
||||
|
||||
private func == (a: [Diff], b: [Diff]) -> Bool {
|
||||
return a.count == b.count && zip(a, b).lazy.map(Diff.equals(pure: Patch.equals(Cofree.equals(annotation: const(true), leaf: ==)), leaf: ==, annotation: const(true))).reduce(true) { $0 && $1 }
|
||||
}
|
||||
|
||||
|
||||
import Assertions
|
||||
@testable import Doubt
|
||||
import Prelude
|
||||
import XCTest
|
@ -1,22 +0,0 @@
|
||||
final class TermTests: XCTestCase {
|
||||
override static func setUp() {
|
||||
sranddev()
|
||||
}
|
||||
|
||||
func testEqualityIsReflexive() {
|
||||
property("equality is reflexive") <- forAll { (term: RangedTerm) in
|
||||
Cofree.equals(annotation: ==, leaf: ==)(term.term, term.term)
|
||||
}
|
||||
}
|
||||
|
||||
func testEqualTermsZipCleanly() {
|
||||
property("equal terms zip to a non-nil value") <- forAll { (term: RangedTerm) in
|
||||
Cofree.zip(term.term, term.term) != nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@testable import Doubt
|
||||
import SwiftCheck
|
||||
import XCTest
|
@ -1,147 +0,0 @@
|
||||
struct UnannotatedTerm {
|
||||
typealias Term = Cofree<String, ()>
|
||||
let term: Term
|
||||
|
||||
static let indexed = (prefix: "[\n\t", separator: ",\n\t", suffix: "\n]")
|
||||
static let fixed = (prefix: "(\n\t", separator: ",\n\t", suffix: "\n)")
|
||||
static let keyed = (prefix: "[\n\t", separator: ",\n\t", suffix: "\n}")
|
||||
|
||||
var source: String {
|
||||
let indexed = UnannotatedTerm.indexed
|
||||
let keyed = UnannotatedTerm.keyed
|
||||
let fixed = UnannotatedTerm.fixed
|
||||
return term.cata {
|
||||
switch $1 {
|
||||
case let .Leaf(s):
|
||||
return s
|
||||
case let .Indexed(s):
|
||||
return indexed.prefix + s.joinWithSeparator(indexed.separator) + indexed.suffix
|
||||
case let .Fixed(s):
|
||||
return fixed.prefix + s.joinWithSeparator(fixed.separator) + fixed.suffix
|
||||
case let .Keyed(s):
|
||||
return keyed.prefix + s.map { "\"\($0)\": \($1)" }.joinWithSeparator(keyed.separator) + keyed.suffix
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var ranged: Cofree<String, Range<Int>> {
|
||||
let indexed = UnannotatedTerm.indexed
|
||||
let keyed = UnannotatedTerm.keyed
|
||||
let fixed = UnannotatedTerm.fixed
|
||||
|
||||
return term.cata {
|
||||
switch $1 {
|
||||
case let .Leaf(s):
|
||||
return Cofree(0..<s.characters.count, .Leaf(s))
|
||||
case let .Indexed(i):
|
||||
var length = indexed.prefix.characters.count
|
||||
var results: [Cofree<String, Range<Int>>] = []
|
||||
for value in i {
|
||||
if results.count > 0 {
|
||||
length += indexed.separator.characters.count
|
||||
}
|
||||
results.append(value.map {
|
||||
($0.startIndex + length)..<($0.endIndex + length)
|
||||
})
|
||||
length += value.extract.count
|
||||
}
|
||||
return Cofree(0..<(length + indexed.suffix.characters.count), .Indexed(results))
|
||||
case let .Fixed(i):
|
||||
var length = fixed.prefix.characters.count
|
||||
var results: [Cofree<String, Range<Int>>] = []
|
||||
for value in i {
|
||||
if results.count > 0 {
|
||||
length += fixed.separator.characters.count
|
||||
}
|
||||
results.append(value.map {
|
||||
($0.startIndex + length)..<($0.endIndex + length)
|
||||
})
|
||||
length += value.extract.count
|
||||
}
|
||||
return Cofree(0..<(length + fixed.suffix.characters.count), .Fixed(results))
|
||||
case let .Keyed(k):
|
||||
var length = keyed.prefix.characters.count
|
||||
var results: [(String, Cofree<String, Range<Int>>)] = []
|
||||
for (key, value) in k {
|
||||
if results.count > 0 {
|
||||
length += keyed.separator.characters.count
|
||||
}
|
||||
results.append((key, value.map {
|
||||
($0.startIndex + length)..<($0.endIndex + length)
|
||||
}))
|
||||
length += value.extract.count + 4 // for the characters around the key
|
||||
}
|
||||
return Cofree(0..<(length + keyed.suffix.characters.count), .Keyed(Dictionary(elements: results)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var arranged: RangedTerm {
|
||||
let source = self.source
|
||||
return RangedTerm(
|
||||
term: ranged.map {
|
||||
source.startIndex.advancedBy($0.startIndex)..<source.startIndex.advancedBy($0.endIndex)
|
||||
},
|
||||
source: source)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension UnannotatedTerm: Arbitrary {
|
||||
static func arbitrary(k: Int) -> Gen<UnannotatedTerm> {
|
||||
let symbol: Gen<String> = Gen<Int>.choose((0, 15)).fmap { "_\($0)" }
|
||||
let leaf: Gen<Term> = symbol.fmap { Term((), .Leaf($0)) }
|
||||
let indexed: Gen<Term> = Gen<Int>.choose((0, k)).bind { n in
|
||||
sequence((0..<n).map { _ in arbitrary(k - 1) }).fmap {
|
||||
Term((), .Indexed($0.map { $0.term }))
|
||||
}
|
||||
}
|
||||
let keyed: Gen<Term> = Gen<Int>.choose((0, k)).bind { n in
|
||||
sequence((0..<n).map { _ in symbol.bind { key in Gen.pure(()).bind { arbitrary(k - 1) }.fmap { (key, $0.term) } } }).fmap {
|
||||
Term((), .Keyed(Dictionary(elements: $0)))
|
||||
}
|
||||
}
|
||||
return Gen.oneOf([
|
||||
leaf,
|
||||
indexed,
|
||||
keyed,
|
||||
]).fmap(UnannotatedTerm.init)
|
||||
}
|
||||
|
||||
static var arbitrary: Gen<UnannotatedTerm> {
|
||||
return arbitrary(4)
|
||||
}
|
||||
|
||||
|
||||
static func shrink(term: UnannotatedTerm) -> [UnannotatedTerm] {
|
||||
let equal = Term.equals(annotation: const(true), leaf: ==)
|
||||
/// A smaller-than-or-equal-to permutation of `term`. Shrinking is performed outward-in by dropping elements from branches.
|
||||
let shrunk: UnannotatedTerm.Term = term.term.para {
|
||||
switch $0 {
|
||||
case let .Leaf(a):
|
||||
return Cofree((), .Leaf(a))
|
||||
case let .Indexed(i):
|
||||
return Cofree((), .Indexed(i.reduce(true) { $0 && equal($1) }
|
||||
? i.dropLast().map { $1 }
|
||||
: i.map { $1 }))
|
||||
case let .Fixed(i):
|
||||
return Cofree((), .Fixed(i.reduce(true) { $0 && equal($1) }
|
||||
? i.dropLast().map { $1 }
|
||||
: i.map { $1 }))
|
||||
case let .Keyed(k):
|
||||
return Cofree((), .Keyed(Dictionary(elements: k.reduce(true) { $0 && equal($1.1) }
|
||||
? k.dropLast().map { ($0, $1.1) }
|
||||
: k.map { ($0, $1.1) })))
|
||||
}
|
||||
}
|
||||
/// If the permutation is unchanged, we cannot shrink any further.
|
||||
return equal(term.term, shrunk)
|
||||
? []
|
||||
: [ UnannotatedTerm(term: shrunk) ]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
import Doubt
|
||||
import Prelude
|
||||
import SwiftCheck
|
@ -1,7 +0,0 @@
|
||||
# Doubt
|
||||
|
||||
Doubt is the prototype implementing semantic diffing features. It is implemented as a library, with a couple of helper tools for specific experiments, and a small HTML/JS UI for reviewing diffs.
|
||||
|
||||
## Building
|
||||
|
||||
Doubt requires Xcode 7 to build.
|
2
prototype/UI/.gitignore
vendored
2
prototype/UI/.gitignore
vendored
@ -1,2 +0,0 @@
|
||||
*.json
|
||||
diff.html
|
16
prototype/UI/dictionary.js
vendored
16
prototype/UI/dictionary.js
vendored
@ -1,16 +0,0 @@
|
||||
function Dictionary(object) {
|
||||
this.values = {};
|
||||
for (key in object) {
|
||||
this.values[key] = object[key];
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
// forall a b. Dictionary String a -> (a -> b) -> Dictionary String b
|
||||
Dictionary.prototype.map = function(transform) {
|
||||
var copy = new Dictionary();
|
||||
for (key in this.values) {
|
||||
copy.values[key] = transform(this.values[key], key);
|
||||
}
|
||||
return copy;
|
||||
}
|
516
prototype/UI/diff.js
vendored
516
prototype/UI/diff.js
vendored
@ -1,516 +0,0 @@
|
||||
function diffFromJSON(json) {
|
||||
if (json.pure != null) {
|
||||
return new Diff({
|
||||
pure: new Patch(json.pure)
|
||||
});
|
||||
}
|
||||
if (json.roll != null) {
|
||||
return new Diff({
|
||||
roll: {
|
||||
extract: json.roll.extract,
|
||||
unwrap: new Syntax(json.roll.unwrap, function(x) {
|
||||
return diffFromJSON(x);
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function Diff(object) {
|
||||
if (object.pure != null) {
|
||||
this.pure = object.pure;
|
||||
}
|
||||
if (object.roll != null) {
|
||||
this.roll = object.roll;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
// forall a b. Diff a -> (a -> b) -> Diff b
|
||||
Diff.prototype.map = function(transform) {
|
||||
if (this.pure != null) {
|
||||
return new Diff({
|
||||
pure: transform(this.pure)
|
||||
});
|
||||
}
|
||||
if (this.roll != null) {
|
||||
return new Diff({
|
||||
roll: {
|
||||
extract: this.roll.extract,
|
||||
unwrap: this.roll.unwrap.map(function(x) {
|
||||
return x.map(transform);
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// forall a. Diff a -> (Syntax a -> a) -> a
|
||||
Diff.prototype.cata = function(transform) {
|
||||
if (this.pure != null) {
|
||||
return this.pure;
|
||||
}
|
||||
if (this.roll != null) {
|
||||
return transform(this.roll.unwrap.map(function(diff) {
|
||||
return diff.cata(transform);
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
function wrap(tagName, element) {
|
||||
if (element == null) {
|
||||
return null;
|
||||
}
|
||||
var node = document.createElement(tagName);
|
||||
node.appendChild(element);
|
||||
return node;
|
||||
}
|
||||
|
||||
/// String -> Syntax a -> Range -> (a -> Range) -> (a -> DOM) -> DOM
|
||||
function termToDOM(source, syntax, extract, getRange, recur) {
|
||||
recur = recur || function(term) {
|
||||
return termToDOM(source, term.unwrap, term.extract, getRange);
|
||||
}
|
||||
var categories = extract.categories;
|
||||
var range = extract.range;
|
||||
var element;
|
||||
if (syntax.leaf != null) {
|
||||
element = document.createElement("span");
|
||||
element.textContent = source.substr(range[0], range[1]);
|
||||
} else if (syntax.indexed != null || syntax.fixed != null) {
|
||||
var values = syntax.indexed || syntax.fixed;
|
||||
element = document.createElement("ul");
|
||||
var previous = range[0];
|
||||
for (i in values) {
|
||||
var child = values[i];
|
||||
if (child.pure == "") continue;
|
||||
var childRange = getRange(child);
|
||||
element.appendChild(document.createTextNode(source.substr(previous, childRange[0] - previous)));
|
||||
element.appendChild(wrap("li", recur(child)));
|
||||
previous = childRange[0] + childRange[1];
|
||||
}
|
||||
element.appendChild(document.createTextNode(source.substr(previous, range[0] + range[1] - previous)));
|
||||
} else if (syntax.keyed != null) {
|
||||
element = document.createElement("dl");
|
||||
var values = [];
|
||||
for (k in syntax.keyed.values) {
|
||||
if (syntax.keyed.values[k].pure == "") continue;
|
||||
values.push([k, syntax.keyed.values[k]]);
|
||||
}
|
||||
values.sort(function(a, b) {
|
||||
if (getRange(a[1])[0] < getRange(b[1])[0]) {
|
||||
return -1;
|
||||
} else if (getRange(a[1])[0] > getRange(b[1])[0]) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
var previous = range[0];
|
||||
for (i in values) {
|
||||
var pair = values[i];
|
||||
var key = pair[0];
|
||||
var child = pair[1];
|
||||
var childRange = getRange(child);
|
||||
element.appendChild(document.createTextNode(source.substr(previous, childRange[0] - previous)));
|
||||
element.appendChild(wrap("dt", document.createTextNode(key)));
|
||||
element.appendChild(wrap("dd", recur(child)));
|
||||
previous = childRange[0] + childRange[1];
|
||||
}
|
||||
element.appendChild(document.createTextNode(source.substr(previous, range[0] + range[1] - previous)));
|
||||
}
|
||||
element["data-range"] = range;
|
||||
|
||||
for (index in categories) {
|
||||
element.classList.add('category-'+categories[index]);
|
||||
}
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
/// Diff -> String -> DOM
|
||||
function diffToDOM(diff, sources, lineNumbers) {
|
||||
|
||||
function getRange(diffOrTerm) {
|
||||
if (diffOrTerm.pure != null) {
|
||||
var beforeRange, afterRange;
|
||||
if (diffOrTerm.pure.before != null) {
|
||||
beforeRange = diffOrTerm.pure.before.extract.range
|
||||
}
|
||||
|
||||
if (diffOrTerm.pure.after != null) {
|
||||
afterRange = diffOrTerm.pure.after.extract.range
|
||||
}
|
||||
|
||||
if (beforeRange == null) {
|
||||
beforeRange = afterRange;
|
||||
}
|
||||
|
||||
if (afterRange == null) {
|
||||
afterRange = beforeRange;
|
||||
}
|
||||
return { "before": beforeRange, "after": afterRange };
|
||||
}
|
||||
if (diffOrTerm.roll != null) {
|
||||
return { before: diffOrTerm.roll.extract.before.range, after: diffOrTerm.roll.extract.after.range };
|
||||
}
|
||||
if (diffOrTerm.extract != null) {
|
||||
return diffOrTerm.extract.range;
|
||||
}
|
||||
}
|
||||
|
||||
if (diff.pure != null) {
|
||||
return pureToDOM(sources, diff.pure, lineNumbers, getRange, function(diff) {
|
||||
return diffToDOM(diff, sources, lineNumbers);
|
||||
})
|
||||
}
|
||||
|
||||
return rollToDOM(sources, diff.roll, lineNumbers, getRange, function(diff) {
|
||||
return diffToDOM(diff, sources, lineNumbers);
|
||||
})
|
||||
}
|
||||
|
||||
function pureToDOM(sources, patch, lineNumbers, getRangeFun, diffToDOMFun) {
|
||||
var elementA, elementB;
|
||||
if (patch.before != null) {
|
||||
elementA = termToDOM(sources.before, patch.before.unwrap, patch.before.extract, getRangeFun);
|
||||
elementA.classList.add("delete");
|
||||
if (patch.after != null) {
|
||||
elementA.classList.add("replace");
|
||||
}
|
||||
|
||||
elementA.setAttribute("data-line-number", patch.before.extract.lines[0])
|
||||
}
|
||||
|
||||
if (patch.after != null) {
|
||||
elementB = termToDOM(sources.after, patch.after.unwrap, patch.after.extract, getRangeFun);
|
||||
elementB.classList.add("insert");
|
||||
if (patch.before != null) {
|
||||
elementB.classList.add("replace");
|
||||
}
|
||||
|
||||
elementB.setAttribute("data-line-number", patch.after.extract.lines[0])
|
||||
}
|
||||
|
||||
if (elementA == null) {
|
||||
elementA = elementB.cloneNode(true);
|
||||
elementA.classList.add("invisible");
|
||||
|
||||
elementA.setAttribute("data-line-number", '\u00A0')
|
||||
}
|
||||
|
||||
if (elementB == null) {
|
||||
elementB = elementA.cloneNode(true);
|
||||
elementB.classList.add("invisible");
|
||||
|
||||
elementB.setAttribute("data-line-number", '\u00A0')
|
||||
}
|
||||
|
||||
return { "before": elementA || "", "after": elementB || "" };
|
||||
}
|
||||
|
||||
function rollToDOM(sources, rollOrTerm, lineNumbers, getRangeFun, diffToDOMFun) {
|
||||
var syntax = rollOrTerm.unwrap
|
||||
var categories = {
|
||||
before: rollOrTerm.extract.before.categories,
|
||||
after: rollOrTerm.extract.after.categories
|
||||
}
|
||||
var range = {
|
||||
before: rollOrTerm.extract.before.range,
|
||||
after: rollOrTerm.extract.after.range
|
||||
}
|
||||
|
||||
var lines = {
|
||||
before: rollOrTerm.extract.before.lines[0],
|
||||
after: rollOrTerm.extract.after.lines[0]
|
||||
}
|
||||
|
||||
var elementA;
|
||||
var elementB;
|
||||
if (syntax.leaf != null) {
|
||||
elementA = document.createElement("span");
|
||||
elementA.textContent = sources.before.substr(range.before[0], range.before[1]);
|
||||
elementB = document.createElement("span");
|
||||
elementB.textContent = sources.after.substr(range.after[0], range.after[1]);
|
||||
|
||||
elementA.setAttribute("data-line-number", lines.before)
|
||||
elementB.setAttribute("data-line-number", lines.after)
|
||||
} else if (syntax.indexed != null || syntax.fixed != null) {
|
||||
var values = syntax.indexed || syntax.fixed;
|
||||
elementA = document.createElement("ul");
|
||||
elementB = document.createElement("ul");
|
||||
var previousBefore = range.before[0];
|
||||
var previousAfter = range.after[0];
|
||||
|
||||
var lineNumbers = { "before": [], "after": [] };
|
||||
for (i in values) {
|
||||
var child = values[i];
|
||||
if (child.pure == "") continue;
|
||||
|
||||
var childRange = getRangeFun(child);
|
||||
var beforeAfterChild = diffToDOMFun(child)
|
||||
if (childRange.before != null) {
|
||||
var beforeRange = childRange.before;
|
||||
|
||||
var li = wrap("li", beforeAfterChild.before);
|
||||
if (beforeAfterChild.before.classList.contains("invisible")) {
|
||||
li.classList.add("invisible");
|
||||
} else {
|
||||
var beforeSeparator = sources.before.substr(previousBefore, beforeRange[0] - previousBefore);
|
||||
elementA.appendChild(document.createTextNode(beforeSeparator));
|
||||
previousBefore = beforeRange[0] + beforeRange[1];
|
||||
}
|
||||
elementA.appendChild(li);
|
||||
|
||||
var lineNumber = beforeAfterChild.before.getAttribute("data-line-number")
|
||||
if (lineNumber != null) {
|
||||
lineNumbers.before.push(lineNumber)
|
||||
}
|
||||
}
|
||||
if (childRange.after != null) {
|
||||
var afterRange = childRange.after;
|
||||
|
||||
var li = wrap("li", beforeAfterChild.after);
|
||||
if (beforeAfterChild.after.classList.contains("invisible")) {
|
||||
li.classList.add("invisible");
|
||||
} else {
|
||||
var afterSeparator = sources.after.substr(previousAfter, afterRange[0] - previousAfter);
|
||||
elementB.appendChild(document.createTextNode(afterSeparator));
|
||||
previousAfter = afterRange[0] + afterRange[1];
|
||||
}
|
||||
elementB.appendChild(li);
|
||||
|
||||
var lineNumber = beforeAfterChild.after.getAttribute("data-line-number");
|
||||
if (lineNumber != null) {
|
||||
lineNumbers.after.push(lineNumber)
|
||||
}
|
||||
}
|
||||
}
|
||||
var beforeText = sources.before.substr(previousBefore, range.before[0] + range.before[1] - previousBefore);
|
||||
var afterText = sources.after.substr(previousAfter, range.after[0] + range.after[1] - previousAfter);
|
||||
|
||||
elementA.appendChild(document.createTextNode(beforeText));
|
||||
elementB.appendChild(document.createTextNode(afterText));
|
||||
|
||||
elementA.setAttribute("data-line-number", lineNumbers.before)
|
||||
elementB.setAttribute("data-line-number", lineNumbers.after)
|
||||
} else if (syntax.keyed != null) {
|
||||
elementA = document.createElement("dl");
|
||||
elementB = document.createElement("dl");
|
||||
|
||||
var befores = [];
|
||||
var afters = [];
|
||||
for (k in syntax.keyed.values) {
|
||||
if (syntax.keyed.values[k].pure == "") continue;
|
||||
var child = syntax.keyed.values[k];
|
||||
|
||||
var ranges = getRangeFun(child);
|
||||
var doms = diffToDOMFun(child)
|
||||
if (ranges.before != null) {
|
||||
befores.push({ "key": k, "range": ranges.before, "child": doms.before });
|
||||
}
|
||||
if (ranges.after != null) {
|
||||
afters.push({ "key": k, "range": ranges.after, "child": doms.after });
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
var sortByRange = function (array) {
|
||||
return function(a, b) {
|
||||
// if a is invisible, sort it based on its range vs. bs opposite range
|
||||
function sortByOppositeRange(el) {
|
||||
var opArray = array === befores ? afters : befores;
|
||||
var opEl = opArray.find(function (e) { return el.key === e.key; });
|
||||
var otherEl = el === a ? b : a;
|
||||
var opOtherEl = opArray.find(function (e) { return otherEl.key === e.key; });
|
||||
|
||||
|
||||
if (el.child.classList.contains("delete") && otherEl.child.classList.contains("insert")) {
|
||||
return -1;
|
||||
}
|
||||
if (el.child.classList.contains("insert") && otherEl.child.classList.contains("delete")) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!el.child.classList.contains("invisible") && !otherEl.child.classList.contains("invisible")) {
|
||||
if (el.range[0] < otherEl.range[0]) {
|
||||
return -1;
|
||||
} else if (el.range[0] > otherEl.range[0]) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (opEl.range[0] < opOtherEl.range[0]) {
|
||||
return -1;
|
||||
} else if (opEl.range[0] > opOtherEl.range[0]) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return sortByOppositeRange(a);
|
||||
}
|
||||
}
|
||||
|
||||
befores.sort(sortByRange(befores));
|
||||
afters.sort(sortByRange(afters));
|
||||
|
||||
var previousA = range.before[0];
|
||||
var previousB = range.after[0];
|
||||
|
||||
var lineNumbers = { "before": [ lines.before ], "after": [ lines.after ] };
|
||||
|
||||
zip(befores, afters, function (a, b) {
|
||||
var key = a.key
|
||||
var childElA = a.child
|
||||
var childRangeA = a.range
|
||||
var childElB = b.child
|
||||
var childRangeB = b.range
|
||||
|
||||
var isFirst = elementA.childNodes.length == 0;
|
||||
|
||||
function penultimateNode(el) {
|
||||
var nodes = Array.prototype.slice.call(el.childNodes).reverse();
|
||||
|
||||
var lastVisibleNode;
|
||||
for (i in nodes) {
|
||||
var node = nodes[i];
|
||||
if (node.nodeType != 3 && node.classList.contains("invisible")) {
|
||||
var nextIndex = parseInt(i, 10) + 1;
|
||||
if (nextIndex < nodes.length) {
|
||||
var nextNode = nodes[nextIndex];
|
||||
if (nextNode.nodeType != 3 && !nextNode.classList.contains("invisible")) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isFirst || !childElA.classList.contains("invisible")) {
|
||||
var text = sources.before.substr(previousA, childRangeA[0] - previousA);
|
||||
|
||||
var beforeNode = penultimateNode(elementA)
|
||||
if (beforeNode != null) {
|
||||
elementA.insertBefore(document.createTextNode(text), beforeNode);
|
||||
} else {
|
||||
elementA.appendChild(document.createTextNode(text));
|
||||
}
|
||||
}
|
||||
|
||||
if (isFirst || !childElB.classList.contains("invisible")) {
|
||||
var text = sources.after.substr(previousB, childRangeB[0] - previousB);
|
||||
|
||||
var beforeNode = penultimateNode(elementB)
|
||||
if (beforeNode != null) {
|
||||
elementB.insertBefore(document.createTextNode(text), beforeNode);
|
||||
} else {
|
||||
elementB.appendChild(document.createTextNode(text));
|
||||
}
|
||||
}
|
||||
|
||||
var dtA = wrap("dt", document.createTextNode(key));
|
||||
elementA.appendChild(dtA);
|
||||
var ddA = wrap("dd", childElA)
|
||||
elementA.appendChild(ddA);
|
||||
if (childElA.classList.contains("invisible")) {
|
||||
dtA.classList.add("invisible");
|
||||
ddA.classList.add("invisible");
|
||||
}
|
||||
|
||||
var lineNumberA = childElA.getAttribute("data-line-number");
|
||||
if (lineNumberA != null) {
|
||||
lineNumbers.before.push(lineNumberA)
|
||||
}
|
||||
|
||||
var dtB = wrap("dt", document.createTextNode(key));
|
||||
elementB.appendChild(dtB);
|
||||
var ddB = wrap("dd", childElB);
|
||||
elementB.appendChild(ddB);
|
||||
if (childElB.classList.contains("invisible")) {
|
||||
dtB.classList.add("invisible");
|
||||
ddB.classList.add("invisible");
|
||||
}
|
||||
|
||||
var lineNumberB = childElB.getAttribute("data-line-number");
|
||||
if (lineNumberB != null) {
|
||||
lineNumbers.after.push(lineNumberB)
|
||||
}
|
||||
|
||||
if (isFirst || !childElA.classList.contains("invisible")) {
|
||||
previousA = childRangeA[0] + childRangeA[1]
|
||||
}
|
||||
if (isFirst || !childElB.classList.contains("invisible")) {
|
||||
previousB = childRangeB[0] + childRangeB[1]
|
||||
}
|
||||
|
||||
var nextIndex = befores.indexOf(a) + 1;
|
||||
if (childElA.classList.contains("invisible") && nextIndex < afters.length) {
|
||||
var nextVisibleElement = afters[nextIndex]
|
||||
while (nextVisibleElement.child.classList.contains("invisible") && ++nextIndex < afters.length) {
|
||||
nextVisibleElement = afters[nextIndex]
|
||||
}
|
||||
if (nextVisibleElement == null) return;
|
||||
|
||||
var beginningOfNextElement = nextVisibleElement.range[0];
|
||||
var text = sources.after.substr(previousB, beginningOfNextElement - previousB);
|
||||
var node = wrap("span", document.createTextNode(text));
|
||||
node.classList.add("invisible");
|
||||
elementA.appendChild(node);
|
||||
}
|
||||
|
||||
var nextIndex = afters.indexOf(b) + 1;
|
||||
if (childElB.classList.contains("invisible") && nextIndex < befores.length) {
|
||||
var nextVisibleElement = befores[nextIndex]
|
||||
while (nextVisibleElement.child.classList.contains("invisible") && ++nextIndex < befores.length) {
|
||||
nextVisibleElement = befores[nextIndex]
|
||||
}
|
||||
if (nextVisibleElement == null) return;
|
||||
|
||||
var beginningOfNextElement = nextVisibleElement.range[0];
|
||||
var text = sources.before.substr(previousA, beginningOfNextElement - previousA);
|
||||
var node = wrap("span", document.createTextNode(text));
|
||||
node.classList.add("invisible");
|
||||
elementB.appendChild(node);
|
||||
}
|
||||
});
|
||||
|
||||
var textA = sources.before.substr(previousA, range.before[0] + range.before[1] - previousA);
|
||||
elementA.appendChild(document.createTextNode(textA));
|
||||
var textB = sources.after.substr(previousB, range.after[0] + range.after[1] - previousB);
|
||||
elementB.appendChild(document.createTextNode(textB));
|
||||
|
||||
|
||||
lineNumbers.before.push(rollOrTerm.extract.before.lines[1])
|
||||
lineNumbers.after.push(rollOrTerm.extract.after.lines[1])
|
||||
elementA.setAttribute("data-line-number", lineNumbers.before)
|
||||
elementB.setAttribute("data-line-number", lineNumbers.after)
|
||||
}
|
||||
|
||||
for (index in categories.before) {
|
||||
elementA.classList.add('category-'+categories.before[index]);
|
||||
}
|
||||
|
||||
for (index in categories.after) {
|
||||
elementB.classList.add('category-'+categories.after[index]);
|
||||
}
|
||||
|
||||
return { "before": elementA, "after": elementB }
|
||||
}
|
||||
|
||||
|
||||
/// ([a], [b]) -> [(a, b)]
|
||||
function zip (a, b, callback) {
|
||||
if (a.length > b.length) {
|
||||
return b.map(function(b, index) {
|
||||
return callback(a[index], b);
|
||||
});
|
||||
} else {
|
||||
return a.map(function(a, index) {
|
||||
return callback(a, b[index]) ;
|
||||
});
|
||||
}
|
||||
}
|
@ -1,72 +0,0 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>semantic-diff</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<script type="text/javascript" src="dictionary.js"></script>
|
||||
<script type="text/javascript" src="diff.js"></script>
|
||||
<script type="text/javascript" src="patch.js"></script>
|
||||
<script type="text/javascript" src="syntax.js"></script>
|
||||
<script type="text/javascript" src="term.js"></script>
|
||||
<script type="text/javascript">
|
||||
function loadJSON(path, callback) {
|
||||
var request = new XMLHttpRequest();
|
||||
request.overrideMimeType("application/json");
|
||||
request.open('GET', '' + path, true);
|
||||
request.onreadystatechange = function () {
|
||||
if (request.readyState == 4 && (request.status == "200" || request.status == 0)) {
|
||||
callback(JSON.parse(request.responseText));
|
||||
}
|
||||
};
|
||||
request.send(null);
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="before">
|
||||
<ul id="before-lines" class="line-numbers"></ul>
|
||||
<div id="before-diff" class="diff"></div>
|
||||
</div>
|
||||
<div id="after">
|
||||
<ul id="after-lines" class="line-numbers"></ul>
|
||||
<div id="after-diff" class="diff"></div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
var unique = function(array) {
|
||||
return array.reduce(function(accum, current) {
|
||||
if (accum.indexOf(current) < 0) {
|
||||
accum.push(current);
|
||||
}
|
||||
return accum;
|
||||
}, []);
|
||||
}
|
||||
|
||||
loadJSON((window.location.search || '?diff.json').substr(1), function (json) {
|
||||
var diff = diffFromJSON(json.diff);
|
||||
|
||||
var beforeLinesEl = document.getElementById("before-lines")
|
||||
var afterLinesEl = document.getElementById("after-lines")
|
||||
|
||||
var dom = diffToDOM(diff,
|
||||
{ "before": json["before"] , "after": json["after"] },
|
||||
{ "before": beforeLinesEl, "after": afterLinesEl });
|
||||
|
||||
var beforeLines = dom.before.getAttribute("data-line-number").split(",")
|
||||
unique(beforeLines).forEach(function(lineNumber) {
|
||||
var node = wrap("li", document.createTextNode(lineNumber));
|
||||
beforeLinesEl.appendChild(node);
|
||||
});
|
||||
var afterLines = dom.after.getAttribute("data-line-number").split(",")
|
||||
unique(afterLines).forEach(function(lineNumber) {
|
||||
var node = wrap("li", document.createTextNode(lineNumber));
|
||||
afterLinesEl.appendChild(node);
|
||||
});
|
||||
|
||||
|
||||
document.getElementById("before-diff").appendChild(dom.before);
|
||||
document.getElementById("after-diff").appendChild(dom.after);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
13
prototype/UI/patch.js
vendored
13
prototype/UI/patch.js
vendored
@ -1,13 +0,0 @@
|
||||
function Patch(patch) {
|
||||
if (patch.delete != null) {
|
||||
this.before = termFromJSON(patch.delete);
|
||||
}
|
||||
if (patch.insert != null) {
|
||||
this.after = termFromJSON(patch.insert);
|
||||
}
|
||||
if (patch.replace != null) {
|
||||
this.before = termFromJSON(patch.replace.before);
|
||||
this.after = termFromJSON(patch.replace.after);
|
||||
}
|
||||
return this;
|
||||
}
|
39
prototype/UI/syntax.js
vendored
39
prototype/UI/syntax.js
vendored
@ -1,39 +0,0 @@
|
||||
function Syntax(json, continuation) {
|
||||
if (json.indexed != null) {
|
||||
this.indexed = json.indexed.map(continuation);
|
||||
}
|
||||
if (json.fixed != null) {
|
||||
this.fixed = json.fixed.map(continuation);
|
||||
}
|
||||
if (json.keyed != null) {
|
||||
this.keyed = (new Dictionary(json.keyed)).map(continuation);
|
||||
}
|
||||
if (json.leaf != null) {
|
||||
this.leaf = json.leaf;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
// forall a b. Syntax a -> (a -> b) -> Syntax b
|
||||
Syntax.prototype.map = function(transform) {
|
||||
if (this.leaf != null) {
|
||||
return new Syntax({
|
||||
leaf: this.leaf
|
||||
}, transform);
|
||||
}
|
||||
if (this.indexed != null) {
|
||||
return new Syntax({
|
||||
indexed: this.indexed
|
||||
}, transform);
|
||||
}
|
||||
if (this.fixed != null) {
|
||||
return new Syntax({
|
||||
fixed: this.fixed
|
||||
}, transform);
|
||||
}
|
||||
if (this.keyed != null) {
|
||||
return new Syntax({
|
||||
keyed: this.keyed.values
|
||||
}, transform);
|
||||
}
|
||||
}
|
14
prototype/UI/term.js
vendored
14
prototype/UI/term.js
vendored
@ -1,14 +0,0 @@
|
||||
function termFromJSON(json) {
|
||||
return new Term({
|
||||
extract: json.extract,
|
||||
unwrap: new Syntax(json.unwrap, function(x) {
|
||||
return termFromJSON(x);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
function Term(object) {
|
||||
this.extract = object.extract;
|
||||
this.unwrap = object.unwrap;
|
||||
return this;
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
/// A list of arguments for the difftool.
|
||||
enum Argument {
|
||||
indirect case OutputFlag(Output, Argument)
|
||||
case Sources(Source, Source)
|
||||
|
||||
var sources: (Source, Source) {
|
||||
switch self {
|
||||
case let .Sources(a, b):
|
||||
return (a, b)
|
||||
case let .OutputFlag(_, rest):
|
||||
return rest.sources
|
||||
}
|
||||
}
|
||||
|
||||
var output: Output {
|
||||
switch self {
|
||||
case let .OutputFlag(output, _):
|
||||
return output
|
||||
default:
|
||||
return .Split
|
||||
}
|
||||
}
|
||||
|
||||
enum Output {
|
||||
case Unified
|
||||
case Split
|
||||
}
|
||||
}
|
||||
|
||||
private let flag: Madness.Parser<[String], Argument.Output>.Function =
|
||||
const(Argument.Output.Unified) <^> satisfy { $0 == "--unified" }
|
||||
<|> const(Argument.Output.Split) <^> satisfy { $0 == "--split" }
|
||||
<|> pure(Argument.Output.Split)
|
||||
|
||||
private let source: Madness.Parser<[String], Source>.Function =
|
||||
{ try! Source($0) } <^> satisfy { !$0.hasPrefix("--") }
|
||||
|
||||
let argumentsParser: Madness.Parser<[String], Argument>.Function = any // skip the path to the difftool
|
||||
*> (curry(Argument.OutputFlag) <^> flag)
|
||||
<*> (curry(Argument.Sources) <^> source <*> source)
|
||||
|
||||
import Madness
|
||||
import Prelude
|
692
prototype/doubt-difftool/Fixtures/JSON/a.json
vendored
692
prototype/doubt-difftool/Fixtures/JSON/a.json
vendored
@ -1,692 +0,0 @@
|
||||
{
|
||||
"AWSTemplateFormatVersion": "2010-09-09",
|
||||
"Conditions": {
|
||||
"EbsVolumeSnapshotIdIsEmpty": {
|
||||
"Fn::Equals": [
|
||||
{
|
||||
"Ref": "EbsVolumeSnapshotId"
|
||||
},
|
||||
""
|
||||
]
|
||||
},
|
||||
"EbsVolumeSnapshotIdIsPresent": {
|
||||
"Fn::Not": [
|
||||
{
|
||||
"Fn::Equals": [
|
||||
{
|
||||
"Ref": "EbsVolumeSnapshotId"
|
||||
},
|
||||
""
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"not_production": {
|
||||
"Fn::Not": [
|
||||
{
|
||||
"Fn::Equals": [
|
||||
{
|
||||
"Ref": "Environment"
|
||||
},
|
||||
"production"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"not_staging": {
|
||||
"Fn::Not": [
|
||||
{
|
||||
"Fn::Equals": [
|
||||
{
|
||||
"Ref": "Environment"
|
||||
},
|
||||
"staging"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"not_test": {
|
||||
"Fn::Not": [
|
||||
{
|
||||
"Fn::Equals": [
|
||||
{
|
||||
"Ref": "Environment"
|
||||
},
|
||||
"test"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"production": {
|
||||
"Fn::Equals": [
|
||||
{
|
||||
"Ref": "Environment"
|
||||
},
|
||||
"production"
|
||||
]
|
||||
},
|
||||
"staging": {
|
||||
"Fn::Equals": [
|
||||
{
|
||||
"Ref": "Environment"
|
||||
},
|
||||
"staging"
|
||||
]
|
||||
},
|
||||
"test": {
|
||||
"Fn::Equals": [
|
||||
{
|
||||
"Ref": "Environment"
|
||||
},
|
||||
"test"
|
||||
]
|
||||
}
|
||||
},
|
||||
"Description": "A single EC2 instance with an EBS volume",
|
||||
"Mappings": {},
|
||||
"Metadata": {},
|
||||
"Outputs": {
|
||||
"AvailabilityZone": {
|
||||
"Description": "The instance availability zone",
|
||||
"Value": {
|
||||
"Fn::GetAtt": [
|
||||
"EbsInstance",
|
||||
"AvailabilityZone"
|
||||
]
|
||||
}
|
||||
},
|
||||
"InstanceID": {
|
||||
"Description": "The instance identifier",
|
||||
"Value": "EbsInstance"
|
||||
},
|
||||
"PrivateDnsName": {
|
||||
"Description": "The instance private DNS name",
|
||||
"Value": {
|
||||
"Fn::GetAtt": [
|
||||
"EbsInstance",
|
||||
"PrivateDnsName"
|
||||
]
|
||||
}
|
||||
},
|
||||
"PrivateIp": {
|
||||
"Description": "The instance private IP address",
|
||||
"Value": {
|
||||
"Fn::GetAtt": [
|
||||
"EbsInstance",
|
||||
"PrivateIp"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Parameters": {
|
||||
"App": {
|
||||
"Description": "App",
|
||||
"MinLength": "1",
|
||||
"Type": "String"
|
||||
},
|
||||
"CreationPolicyResourceSignalCount": {
|
||||
"Default": "1",
|
||||
"Description": "Number of signals to wait on to consider an instance ready",
|
||||
"Type": "String"
|
||||
},
|
||||
"CreationPolicyResourceSignalTimeout": {
|
||||
"Default": "PT1H",
|
||||
"Description": "How long to wait for an instance to signal before timing out",
|
||||
"Type": "String"
|
||||
},
|
||||
"EbsDeleteOnTermination": {
|
||||
"Default": true,
|
||||
"Description": "Determines whether to delete the volume on instance termination. The default value is true",
|
||||
"Type": "String"
|
||||
},
|
||||
"EbsDeviceName": {
|
||||
"Default": "/dev/xvdf",
|
||||
"Description": "The name of the device within Amazon EC2",
|
||||
"Type": "String"
|
||||
},
|
||||
"EbsVolumeSize": {
|
||||
"Default": "40",
|
||||
"Description": "The size of the EBS (gp2) volume",
|
||||
"Type": "String"
|
||||
},
|
||||
"EbsVolumeSnapshotId": {
|
||||
"Default": "",
|
||||
"Description": "The snapshot from which to create the new volume",
|
||||
"Type": "String"
|
||||
},
|
||||
"Environment": {
|
||||
"AllowedValues": [
|
||||
"test",
|
||||
"staging",
|
||||
"production"
|
||||
],
|
||||
"ConstraintDescription": "must be test, staging, or production",
|
||||
"Default": "test",
|
||||
"Description": "Environment. Defaults to test",
|
||||
"Type": "String"
|
||||
},
|
||||
"GpanelSnsTopic": {
|
||||
"Description": "GpanelSnsTopic",
|
||||
"MinLength": "1",
|
||||
"Type": "String"
|
||||
},
|
||||
"IamRole": {
|
||||
"Description": "IamRole",
|
||||
"MinLength": "1",
|
||||
"Type": "String"
|
||||
},
|
||||
"ImageId": {
|
||||
"Description": "AMI",
|
||||
"Type": "AWS::EC2::Image::Id"
|
||||
},
|
||||
"InstanceName": {
|
||||
"Description": "The name of the instance being added",
|
||||
"Type": "String"
|
||||
},
|
||||
"InstanceProfile": {
|
||||
"Description": "InstanceProfile",
|
||||
"MinLength": "1",
|
||||
"Type": "String"
|
||||
},
|
||||
"InstanceType": {
|
||||
"Default": "c4.large",
|
||||
"Description": "Instance Type",
|
||||
"Type": "String"
|
||||
},
|
||||
"KeyName": {
|
||||
"Description": "Key Name",
|
||||
"Type": "AWS::EC2::KeyPair::KeyName"
|
||||
},
|
||||
"LogRetentionInDays": {
|
||||
"Default": "7",
|
||||
"Description": "Days to retain logs in CloudWatch Logs",
|
||||
"Type": "Number"
|
||||
},
|
||||
"ResourceBase": {
|
||||
"Description": "S3 Base URL for stack resources",
|
||||
"Type": "String"
|
||||
},
|
||||
"ResourceBucket": {
|
||||
"Description": "S3 Bucket",
|
||||
"Type": "String"
|
||||
},
|
||||
"Role": {
|
||||
"Description": "Role",
|
||||
"MinLength": "1",
|
||||
"Type": "String"
|
||||
},
|
||||
"SecurityGroupIds": {
|
||||
"Description": "List of security groups to assign to the instance",
|
||||
"Type": "CommaDelimitedList"
|
||||
},
|
||||
"StackIdentifier": {
|
||||
"Description": "Treeish or PID to represent the version of this stack. Related resources are similary named",
|
||||
"Type": "String"
|
||||
},
|
||||
"StackInstance": {
|
||||
"Description": "Stack name and Identifier",
|
||||
"Type": "String"
|
||||
},
|
||||
"Subnet": {
|
||||
"Description": "Subnets",
|
||||
"Type": "AWS::EC2::Subnet::Id"
|
||||
},
|
||||
"Vpc": {
|
||||
"Description": "VPC ID",
|
||||
"Type": "AWS::EC2::VPC::Id"
|
||||
},
|
||||
"Zone": {
|
||||
"Description": "AZs",
|
||||
"Type": "AWS::EC2::AvailabilityZone::Name"
|
||||
},
|
||||
"ZoneId": {
|
||||
"Description": "Zone ID",
|
||||
"Type": "AWS::Route53::HostedZone::Id"
|
||||
},
|
||||
"ZoneName": {
|
||||
"Description": "Zone Domain Name",
|
||||
"MinLength": "1",
|
||||
"Type": "String"
|
||||
}
|
||||
},
|
||||
"Resources": {
|
||||
"EbsInstance": {
|
||||
"CreationPolicy": {
|
||||
"ResourceSignal": {
|
||||
"Count": {
|
||||
"Ref": "CreationPolicyResourceSignalCount"
|
||||
},
|
||||
"Timeout": {
|
||||
"Ref": "CreationPolicyResourceSignalTimeout"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Metadata": {
|
||||
"AWS::CloudFormation::Authentication": {
|
||||
"CoreS3ResourceBucketAuth": {
|
||||
"buckets": [
|
||||
{
|
||||
"Ref": "ResourceBucket"
|
||||
}
|
||||
],
|
||||
"roleName": {
|
||||
"Ref": "IamRole"
|
||||
},
|
||||
"type": "s3"
|
||||
}
|
||||
},
|
||||
"AWS::CloudFormation::Init": {
|
||||
"GpanelConfigset": {
|
||||
"commands": {
|
||||
"/root/gpanel.sh": {
|
||||
"command": "/root/gpanel.sh",
|
||||
"cwd": "/root",
|
||||
"env": {
|
||||
"AWS_DEFAULT_REGION": {
|
||||
"Ref": "AWS::Region"
|
||||
},
|
||||
"ROUTE53_DOMAIN": {
|
||||
"Ref": "ZoneName"
|
||||
},
|
||||
"ROUTE53_ZONEID": {
|
||||
"Ref": "ZoneId"
|
||||
},
|
||||
"TOPIC": {
|
||||
"Ref": "GpanelSnsTopic"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"files": {
|
||||
"/etc/init.d/route53": {
|
||||
"authentication": "CoreS3ResourceBucketAuth",
|
||||
"group": "root",
|
||||
"mode": "000755",
|
||||
"owner": "root",
|
||||
"source": {
|
||||
"Fn::Join": [
|
||||
"/",
|
||||
[
|
||||
{
|
||||
"Ref": "ResourceBase"
|
||||
},
|
||||
{
|
||||
"Ref": "StackInstance"
|
||||
},
|
||||
"resources",
|
||||
"route53.init"
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"/home/ubuntu/.ssh/id_rsa": {
|
||||
"Fn::If": [
|
||||
"test",
|
||||
{
|
||||
"authentication": "CoreS3ResourceBucketAuth",
|
||||
"group": "ubuntu",
|
||||
"mode": "000400",
|
||||
"owner": "ubuntu",
|
||||
"source": {
|
||||
"Fn::Join": [
|
||||
"/",
|
||||
[
|
||||
{
|
||||
"Ref": "ResourceBase"
|
||||
},
|
||||
{
|
||||
"Ref": "StackInstance"
|
||||
},
|
||||
"resources",
|
||||
"github-cloudformation-test"
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Ref": "AWS::NoValue"
|
||||
}
|
||||
]
|
||||
},
|
||||
"/root/gpanel.sh": {
|
||||
"authentication": "CoreS3ResourceBucketAuth",
|
||||
"group": "root",
|
||||
"mode": "000755",
|
||||
"owner": "root",
|
||||
"source": {
|
||||
"Fn::Join": [
|
||||
"/",
|
||||
[
|
||||
{
|
||||
"Ref": "ResourceBase"
|
||||
},
|
||||
{
|
||||
"Ref": "StackInstance"
|
||||
},
|
||||
"resources",
|
||||
"gpanel.sh"
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"/usr/local/sbin/route53": {
|
||||
"authentication": "CoreS3ResourceBucketAuth",
|
||||
"group": "root",
|
||||
"mode": "000755",
|
||||
"owner": "root",
|
||||
"source": {
|
||||
"Fn::Join": [
|
||||
"/",
|
||||
[
|
||||
{
|
||||
"Ref": "ResourceBase"
|
||||
},
|
||||
{
|
||||
"Ref": "StackInstance"
|
||||
},
|
||||
"resources",
|
||||
"route53.sh"
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"PuppetConfigset": {
|
||||
"commands": {
|
||||
"/root/puppet.sh": {
|
||||
"command": "/root/puppet.sh",
|
||||
"cwd": "/root",
|
||||
"env": {
|
||||
"AWS_DEFAULT_REGION": {
|
||||
"Ref": "AWS::Region"
|
||||
},
|
||||
"BRANCH": "master",
|
||||
"ENVIRONMENT": {
|
||||
"Ref": "Environment"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"files": {
|
||||
"/root/puppet.sh": {
|
||||
"authentication": "CoreS3ResourceBucketAuth",
|
||||
"group": "root",
|
||||
"mode": "000755",
|
||||
"owner": "root",
|
||||
"source": {
|
||||
"Fn::Join": [
|
||||
"/",
|
||||
[
|
||||
{
|
||||
"Ref": "ResourceBase"
|
||||
},
|
||||
{
|
||||
"Ref": "StackInstance"
|
||||
},
|
||||
"resources",
|
||||
"puppet.sh"
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"/tmp/awslogs.conf": {
|
||||
"content": {
|
||||
"Fn::Join": [
|
||||
"",
|
||||
[
|
||||
"[general]\n",
|
||||
"state_file= /var/awslogs/state/agent-state\n",
|
||||
"[/var/log/syslog]\n",
|
||||
"file = /var/log/syslog\n",
|
||||
"log_group_name = ",
|
||||
{
|
||||
"Ref": "LogGroup"
|
||||
},
|
||||
"\n",
|
||||
"log_stream_name = {hostname}/syslog\n",
|
||||
"datetime_format = %b %d %H:%M:%S\n",
|
||||
"[/var/log/messages]\n",
|
||||
"file = /var/log/messages\n",
|
||||
"log_group_name = ",
|
||||
{
|
||||
"Ref": "LogGroup"
|
||||
},
|
||||
"\n",
|
||||
"log_stream_name = {hostname}/messages\n",
|
||||
"datetime_format = %b %d %H:%M:%S\n",
|
||||
"[/var/log/cloud-init.log]\n",
|
||||
"file = /var/log/cloud-init.log\n",
|
||||
"log_group_name = ",
|
||||
{
|
||||
"Ref": "LogGroup"
|
||||
},
|
||||
"\n",
|
||||
"log_stream_name = {hostname}/cloud-init.log\n",
|
||||
"datetime_format = \n",
|
||||
"[/var/log/cloud-init.log]\n",
|
||||
"file = /var/log/cloud-init.log\n",
|
||||
"log_group_name = ",
|
||||
{
|
||||
"Ref": "LogGroup"
|
||||
},
|
||||
"\n",
|
||||
"log_stream_name = {hostname}/cloud-init.log\n",
|
||||
"datetime_format = \n",
|
||||
"[/var/log/cfn-init-cmd.log]\n",
|
||||
"file = /var/log/cfn-init-cmd.log\n",
|
||||
"log_group_name = ",
|
||||
{
|
||||
"Ref": "LogGroup"
|
||||
},
|
||||
"\n",
|
||||
"log_stream_name = {hostname}/cfn-init-cmd.log\n",
|
||||
"datetime_format = \n",
|
||||
"[/var/log/cfn-init.log]\n",
|
||||
"file = /var/log/cfn-init.log\n",
|
||||
"log_group_name = ",
|
||||
{
|
||||
"Ref": "LogGroup"
|
||||
},
|
||||
"\n",
|
||||
"log_stream_name = {hostname}/cfn-init.log\n",
|
||||
"datetime_format = \n",
|
||||
"[/var/log/cfn-hup.log]\n",
|
||||
"file = /var/log/cfn-hup.log\n",
|
||||
"log_group_name = ",
|
||||
{
|
||||
"Ref": "LogGroup"
|
||||
},
|
||||
"\n",
|
||||
"log_stream_name = {hostname}/cfn-hup.log\n",
|
||||
"datetime_format = \n",
|
||||
"[/var/log/cfn-wire.log]\n",
|
||||
"file = /var/log/cfn-wire.log\n",
|
||||
"log_group_name = ",
|
||||
{
|
||||
"Ref": "LogGroup"
|
||||
},
|
||||
"\n",
|
||||
"log_stream_name = {hostname}/cfn-wire.log\n",
|
||||
"datetime_format = \n"
|
||||
]
|
||||
]
|
||||
},
|
||||
"group": "root",
|
||||
"mode": "000444",
|
||||
"owner": "root"
|
||||
}
|
||||
}
|
||||
},
|
||||
"configSets": {
|
||||
"default": [
|
||||
"GpanelConfigset",
|
||||
"PuppetConfigset"
|
||||
]
|
||||
}
|
||||
},
|
||||
"facter": {
|
||||
"app": {
|
||||
"Ref": "App"
|
||||
},
|
||||
"environment": {
|
||||
"Ref": "Environment"
|
||||
},
|
||||
"puppet_autorole": "true",
|
||||
"role": {
|
||||
"Ref": "Role"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Properties": {
|
||||
"AvailabilityZone": {
|
||||
"Ref": "Zone"
|
||||
},
|
||||
"BlockDeviceMappings": [
|
||||
{
|
||||
"DeviceName": {
|
||||
"Ref": "EbsDeviceName"
|
||||
},
|
||||
"Ebs": {
|
||||
"DeleteOnTermination": {
|
||||
"Ref": "EbsDeleteOnTermination"
|
||||
},
|
||||
"Encrypted": true,
|
||||
"SnapshotId": {
|
||||
"Fn::If": [
|
||||
"EbsVolumeSnapshotIdIsPresent",
|
||||
{
|
||||
"Ref": "EbsVolumeSnapshotId"
|
||||
},
|
||||
{
|
||||
"Ref": "AWS::NoValue"
|
||||
}
|
||||
]
|
||||
},
|
||||
"VolumeSize": {
|
||||
"Ref": "EbsVolumeSize"
|
||||
},
|
||||
"VolumeType": "gp2"
|
||||
}
|
||||
}
|
||||
],
|
||||
"EbsOptimized": true,
|
||||
"IamInstanceProfile": {
|
||||
"Ref": "InstanceProfile"
|
||||
},
|
||||
"ImageId": {
|
||||
"Ref": "ImageId"
|
||||
},
|
||||
"InstanceType": {
|
||||
"Ref": "InstanceType"
|
||||
},
|
||||
"KeyName": {
|
||||
"Ref": "KeyName"
|
||||
},
|
||||
"Monitoring": "true",
|
||||
"SecurityGroupIds": {
|
||||
"Ref": "SecurityGroupIds"
|
||||
},
|
||||
"SubnetId": {
|
||||
"Ref": "Subnet"
|
||||
},
|
||||
"Tags": [
|
||||
{
|
||||
"Key": "monitored",
|
||||
"Value": "true"
|
||||
},
|
||||
{
|
||||
"Key": "app",
|
||||
"Value": {
|
||||
"Ref": "App"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Key": "role",
|
||||
"Value": {
|
||||
"Ref": "Role"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Key": "env",
|
||||
"Value": {
|
||||
"Ref": "Environment"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Key": "provisioner",
|
||||
"Value": "gh-aws:cloudformation"
|
||||
},
|
||||
{
|
||||
"Key": "Name",
|
||||
"Value": "EbsInstance"
|
||||
}
|
||||
],
|
||||
"UserData": {
|
||||
"Fn::Base64": {
|
||||
"Fn::Join": [
|
||||
"",
|
||||
[
|
||||
"#!/bin/bash -x\n",
|
||||
"# 0 \n",
|
||||
"/sbin/ifconfig eth0 mtu 1436 up\n",
|
||||
"/usr/bin/apt-get install -y python-setuptools\n",
|
||||
"/usr/bin/easy_install --upgrade awscli\n",
|
||||
"/usr/bin/easy_install ",
|
||||
"https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-latest.tar.gz\n",
|
||||
"STACK_STATE=$(aws cloudformation describe-stacks",
|
||||
" --region",
|
||||
{
|
||||
"Ref": "AWS::StackName"
|
||||
},
|
||||
" --stack-name",
|
||||
{
|
||||
"Ref": "AWS::StackName"
|
||||
},
|
||||
" --query 'Stacks[*].StackStatus'",
|
||||
" --output text)\n",
|
||||
"/usr/local/bin/cfn-init -v ",
|
||||
" --configsets default ",
|
||||
" --stack ",
|
||||
{
|
||||
"Ref": "AWS::StackName"
|
||||
},
|
||||
" --resource EbsInstance ",
|
||||
" --region ",
|
||||
{
|
||||
"Ref": "AWS::Region"
|
||||
},
|
||||
"\n",
|
||||
"cfn_init_res=$?\n",
|
||||
"if [ echo $STACK_STATE | grep -q ROLLBACK ]; then\n",
|
||||
" cfn_init_res=0\n",
|
||||
"fi\n",
|
||||
"/usr/local/bin/cfn-signal -e $cfn_init_res ",
|
||||
" --stack ",
|
||||
{
|
||||
"Ref": "AWS::StackName"
|
||||
},
|
||||
" --resource EbsInstance ",
|
||||
" --region ",
|
||||
{
|
||||
"Ref": "AWS::Region"
|
||||
},
|
||||
"\n",
|
||||
"cat /var/log/cfn-init.log \n"
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Type": "AWS::EC2::Instance"
|
||||
},
|
||||
"LogGroup": {
|
||||
"Properties": {
|
||||
"RetentionInDays": {
|
||||
"Ref": "LogRetentionInDays"
|
||||
}
|
||||
},
|
||||
"Type": "AWS::Logs::LogGroup"
|
||||
}
|
||||
}
|
||||
}
|
1822
prototype/doubt-difftool/Fixtures/JSON/b.json
vendored
1822
prototype/doubt-difftool/Fixtures/JSON/b.json
vendored
File diff suppressed because it is too large
Load Diff
53
prototype/doubt-difftool/Fixtures/after.js
vendored
53
prototype/doubt-difftool/Fixtures/after.js
vendored
@ -1,53 +0,0 @@
|
||||
function diffFromJSON(json) {
|
||||
if (json.pure != null) { return new Diff({ pure: new Patch(json.pure) }); }
|
||||
if (json.roll != null) {
|
||||
return new Diff({
|
||||
roll: {
|
||||
extract: json.roll.extract,
|
||||
unwrap: new Syntax(json.roll.unwrap, function(x) { return diffFromJSON(x); })
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function Diff(object) {
|
||||
if (object.pure != null) { this.pure = object.pure; }
|
||||
if (object.roll != null) { this.roll = object.roll; }
|
||||
return this;
|
||||
}
|
||||
|
||||
// forall a b. Diff a -> (a -> b) -> Diff b
|
||||
Diff.prototype.map = function(transform) {
|
||||
if (this.pure != null) { return new Diff({ pure: transform(this.pure) }); }
|
||||
if (this.roll != null) {
|
||||
return new Diff({
|
||||
roll: {
|
||||
extract: this.roll.extract,
|
||||
unwrap: this.roll.unwrap.map(function(x) { return x.map(transform); })
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// forall a. Diff a -> (Syntax a -> a) -> a
|
||||
Diff.prototype.cata = function(transform) {
|
||||
if (this.pure != null) { return this.pure; }
|
||||
if (this.roll != null) {
|
||||
return transform(this.roll.unwrap.map(function(diff) {
|
||||
return diff.cata(transform);
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
/// Diff -> String -> String -> DOM
|
||||
function diffToDOM(diff, which, source) {
|
||||
if (diff.pure != null) { return diff.pure; }
|
||||
|
||||
function getRange(diff) {
|
||||
if (diff.pure != null) { return diff.pure["data-range"]; }
|
||||
if (diff.roll != null) { return diff.roll.extract[which]; }
|
||||
}
|
||||
return rangeAndSyntaxToDOM(getRange(diff), diff.roll.unwrap, source, getRange, function(diff) {
|
||||
return diffToDOM(diff, which, source);
|
||||
})
|
||||
}
|
77
prototype/doubt-difftool/Fixtures/before.js
vendored
77
prototype/doubt-difftool/Fixtures/before.js
vendored
@ -1,77 +0,0 @@
|
||||
function diffFromJSON(json) {
|
||||
if (json.pure != null) {
|
||||
return new Diff({
|
||||
pure: new Patch(json.pure)
|
||||
});
|
||||
}
|
||||
if (json.roll != null) {
|
||||
return new Diff({
|
||||
roll: {
|
||||
extract: json.roll.extract,
|
||||
unwrap: new Syntax(json.roll.unwrap, function(x) {
|
||||
return diffFromJSON(x);
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function Diff(object) {
|
||||
if (object.pure != null) {
|
||||
this.pure = object.pure;
|
||||
}
|
||||
if (object.roll != null) {
|
||||
this.roll = object.roll;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
// forall a b. Diff a -> (a -> b) -> Diff b
|
||||
Diff.prototype.map = function(transform) {
|
||||
if (this.pure != null) {
|
||||
return new Diff({
|
||||
pure: transform(this.pure)
|
||||
});
|
||||
}
|
||||
if (this.roll != null) {
|
||||
return new Diff({
|
||||
roll: {
|
||||
extract: this.roll.extract,
|
||||
unwrap: this.roll.unwrap.map(function(x) {
|
||||
return x.map(transform);
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// forall a. Diff a -> (Syntax a -> a) -> a
|
||||
Diff.prototype.cata = function(transform) {
|
||||
if (this.pure != null) {
|
||||
return this.pure;
|
||||
}
|
||||
if (this.roll != null) {
|
||||
return transform(this.roll.unwrap.map(function(diff) {
|
||||
return diff.cata(transform);
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
/// Diff -> String -> String -> DOM
|
||||
function diffToDOM(diff, which, source) {
|
||||
if (diff.pure != null) {
|
||||
return diff.pure;
|
||||
}
|
||||
|
||||
function getRange(diff) {
|
||||
if (diff.pure != null) {
|
||||
return diff.pure["data-range"];
|
||||
}
|
||||
if (diff.roll != null) {
|
||||
return diff.roll.extract[which];
|
||||
}
|
||||
}
|
||||
return rangeAndSyntaxToDOM(getRange(diff), diff.roll.unwrap, source, getRange, function(diff) {
|
||||
return diffToDOM(diff, which, source);
|
||||
})
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string></string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2015 GitHub. All rights reserved.</string>
|
||||
<key>NSMainNibFile</key>
|
||||
<string>MainMenu</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
<key>PathToUISource</key>
|
||||
<string>$(SRCROOT)/UI/index.html</string>
|
||||
</dict>
|
||||
</plist>
|
@ -1,46 +0,0 @@
|
||||
struct Info: Categorizable, CustomJSONConvertible, Equatable {
|
||||
init(range: Range<Int>, lines: Range<Line>, columns: Range<Column>, categories: Set<String>) {
|
||||
self.range = range
|
||||
self.lines = lines
|
||||
self.columns = columns
|
||||
self.categories = categories
|
||||
}
|
||||
|
||||
init(range: Range<String.CharacterView.Index>, lines: Range<Line>, columns: Range<Column>, categories: Set<String>) {
|
||||
// FIXME: this is terrible. see also https://github.com/github/semantic-diff/issues/136
|
||||
self.range = Int(String(range.startIndex))!..<Int(String(range.endIndex))!
|
||||
self.lines = lines
|
||||
self.columns = columns
|
||||
self.categories = categories
|
||||
}
|
||||
|
||||
let range: Range<Int>
|
||||
|
||||
let lines: Range<Line>
|
||||
|
||||
let columns: Range<Column>
|
||||
|
||||
|
||||
// MARK: Categorizable
|
||||
|
||||
let categories: Set<String>
|
||||
|
||||
|
||||
// MARK: CustomJSONConvertible
|
||||
|
||||
var JSON: Doubt.JSON {
|
||||
return [
|
||||
"range": range.JSON,
|
||||
"lines": lines.JSON,
|
||||
"columns": columns.JSON,
|
||||
"categories": Array(categories).JSON
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
func == (left: Info, right: Info) -> Bool {
|
||||
return left.range == right.range && left.categories == right.categories && left.lines == left.lines && left.columns == right.columns
|
||||
}
|
||||
|
||||
import Madness
|
||||
import Doubt
|
@ -1,45 +0,0 @@
|
||||
// Copyright © 2015 GitHub. All rights reserved.
|
||||
|
||||
typealias TSDocument = COpaquePointer
|
||||
typealias TSLanguage = COpaquePointer
|
||||
|
||||
extension TSNode {
|
||||
func category(document: TSDocument) throws -> String {
|
||||
guard let category = String.fromCString(ts_node_name(self, document)) else { throw "couldn’t make a String from the node name" }
|
||||
return category
|
||||
}
|
||||
|
||||
var range: Range<Int> {
|
||||
let start = ts_node_pos(self).chars
|
||||
return start..<(start + ts_node_size(self).chars)
|
||||
}
|
||||
|
||||
func substring(string: String) throws -> String {
|
||||
guard let result = String(string.utf16[range]) else { throw "could not make a string from utf16 range '\(range)'" }
|
||||
return result
|
||||
}
|
||||
|
||||
var children: ChildrenCollection {
|
||||
return ChildrenCollection(node: self, count: ts_node_child_count(self), child: ts_node_child)
|
||||
}
|
||||
|
||||
var namedChildren: ChildrenCollection {
|
||||
return ChildrenCollection(node: self, count: ts_node_named_child_count(self), child: ts_node_named_child)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct ChildrenCollection: CollectionType {
|
||||
let node: TSNode
|
||||
let count: Int
|
||||
let child: (TSNode, Int) -> TSNode
|
||||
|
||||
subscript (index: Int) -> TSNode {
|
||||
return child(node, index)
|
||||
}
|
||||
|
||||
let startIndex = 0
|
||||
var endIndex: Int {
|
||||
return count
|
||||
}
|
||||
}
|
@ -1,93 +0,0 @@
|
||||
private func unified(term: Term, source: String) -> String {
|
||||
return term.cata { info, syntax -> (String, Range<Int>?) in
|
||||
switch syntax {
|
||||
case .Leaf:
|
||||
return (String(source.utf16[info.range]), info.range)
|
||||
case let .Indexed(i):
|
||||
return (unified(info.range, children: i, source: source), info.range)
|
||||
case let .Fixed(f):
|
||||
return (unified(info.range, children: f, source: source), info.range)
|
||||
case let .Keyed(k):
|
||||
return (unified(info.range, children: k.values.sort(isOrderedBefore), source: source), info.range)
|
||||
}
|
||||
}.0
|
||||
}
|
||||
|
||||
private func isOrderedBefore(a: (String, Range<Int>?), _ b: (String, Range<Int>?)) -> Bool {
|
||||
if let a = a.1, b = b.1 {
|
||||
return a.startIndex < b.startIndex
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private var isTTY = isatty(STDOUT_FILENO) != 0
|
||||
private var isDumb = String.fromCString(getenv("TERM")).map { $0.hasPrefix("dumb") } ?? true
|
||||
private var shouldFormat = isTTY && !isDumb
|
||||
|
||||
private struct Attribute {
|
||||
let colour: Colour
|
||||
let style: Style
|
||||
|
||||
enum Colour: Int {
|
||||
case Black = 30
|
||||
case Red
|
||||
case Green
|
||||
case Yellow
|
||||
case Blue
|
||||
case Purple
|
||||
case Cyan
|
||||
case White
|
||||
}
|
||||
|
||||
enum Style: Int {
|
||||
case Normal = 0
|
||||
case Bold = 1
|
||||
case Underline = 4
|
||||
}
|
||||
|
||||
func wrap(string: String) -> String {
|
||||
return shouldFormat
|
||||
? "\u{001B}[\(style.rawValue);\(colour.rawValue)m\(string)\u{001B}[0m"
|
||||
: string
|
||||
}
|
||||
}
|
||||
|
||||
private func unified(patch: Patch<Term>, before: String, after: String) -> String {
|
||||
return (patch.state.before.map { Attribute(colour: .Red, style: .Bold).wrap("{-\(unified($0, source: before))-}") } ?? "")
|
||||
+ (patch.state.after.map { Attribute(colour: .Green, style: .Bold).wrap("{+\(unified($0, source: after))+}") } ?? "")
|
||||
}
|
||||
|
||||
private func range(patch: Patch<Term>) -> Range<Int>? {
|
||||
return patch.state.after?.extract.range
|
||||
}
|
||||
|
||||
private func unified(range: Range<Int>, children: [(String, Range<Int>?)], source: String) -> String {
|
||||
var previous = range.startIndex
|
||||
var out: String = ""
|
||||
for (string, range) in children {
|
||||
if let range = range {
|
||||
out += String(source.utf16[previous..<range.startIndex])
|
||||
previous = range.endIndex
|
||||
}
|
||||
out += string
|
||||
}
|
||||
return out + String(source.utf16[previous..<range.endIndex])
|
||||
}
|
||||
|
||||
func unified(diff: Diff, before: String, after: String) -> String {
|
||||
return diff.map { (unified($0, before: before, after: after), range($0)) }.cata { info, syntax in
|
||||
switch syntax {
|
||||
case .Leaf:
|
||||
return (String(after.utf16[info.1.range]), info.1.range)
|
||||
case let .Indexed(i):
|
||||
return (unified(info.1.range, children: i, source: after), info.1.range)
|
||||
case let .Fixed(f):
|
||||
return (unified(info.1.range, children: f, source: after), info.1.range)
|
||||
case let .Keyed(k):
|
||||
return (unified(info.1.range, children: k.values.sort(isOrderedBefore), source: after), info.1.range)
|
||||
}
|
||||
}.0
|
||||
}
|
||||
|
||||
|
||||
import Doubt
|
@ -1,4 +0,0 @@
|
||||
#import "tree_sitter/runtime.h"
|
||||
|
||||
extern TSLanguage *ts_language_javascript();
|
||||
extern TSLanguage *ts_language_c();
|
@ -1,205 +0,0 @@
|
||||
import Cocoa
|
||||
import Doubt
|
||||
import Prelude
|
||||
import Madness
|
||||
|
||||
func benchmark<T>(label: String? = nil, _ f: () throws -> T) rethrows -> T {
|
||||
let start = NSDate.timeIntervalSinceReferenceDate()
|
||||
let result = try f()
|
||||
let end = NSDate.timeIntervalSinceReferenceDate()
|
||||
print((label.map { "\($0): " } ?? "") + "\(end - start)s")
|
||||
return result
|
||||
}
|
||||
|
||||
extension String: ErrorType {}
|
||||
|
||||
typealias Term = Cofree<String, Info>
|
||||
typealias Diff = Free<Term.Leaf, (Term.Annotation, Term.Annotation), Patch<Term>>
|
||||
typealias Parser = String throws -> Term
|
||||
|
||||
struct Source {
|
||||
init(_ argument: String) throws {
|
||||
let supportedSchemes = [ "http", "https", "file" ]
|
||||
if let URL = NSURL(string: argument) where supportedSchemes.contains(URL.scheme) {
|
||||
self.URL = URL
|
||||
} else {
|
||||
self.URL = NSURL(fileURLWithPath: argument)
|
||||
}
|
||||
contents = try NSString(contentsOfURL: URL, encoding: NSUTF8StringEncoding) as String
|
||||
}
|
||||
|
||||
let URL: NSURL
|
||||
var type: String {
|
||||
if let pathExtension = URL.pathExtension where pathExtension != "" { return pathExtension }
|
||||
return URL.fragment ?? ""
|
||||
}
|
||||
let contents: String
|
||||
|
||||
private static let languagesByType: [String:TSLanguage] = [
|
||||
"js": ts_language_javascript(),
|
||||
"c": ts_language_c(),
|
||||
"h": ts_language_c(),
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
extension String.UTF16View {
|
||||
subscript (range: Range<Int>) -> String.UTF16View {
|
||||
return self[Index(_offset: range.startIndex)..<Index(_offset: range.endIndex)]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Allow predicates to occur in pattern matching.
|
||||
func ~= <A> (left: A -> Bool, right: A) -> Bool {
|
||||
return left(right)
|
||||
}
|
||||
|
||||
|
||||
func termWithInput(language: TSLanguage)(_ string: String) throws -> Term {
|
||||
let keyedProductions: Set<String> = [ "object" ]
|
||||
let fixedProductions: Set<String> = [ "pair", "rel_op", "math_op", "bool_op", "bitwise_op", "type_op", "math_assignment", "assignment", "subscript_access", "member_access", "new_expression", "function_call", "function", "ternary" ]
|
||||
let document = ts_document_make()
|
||||
defer { ts_document_free(document) }
|
||||
return try string.withCString {
|
||||
ts_document_set_language(document, language)
|
||||
ts_document_set_input_string(document, $0)
|
||||
ts_document_parse(document)
|
||||
let root = ts_document_root_node(document)
|
||||
|
||||
return try Cofree
|
||||
.ana { node, category in
|
||||
let count = node.namedChildren.count
|
||||
guard count > 0 else { return try Syntax.Leaf(node.substring(string)) }
|
||||
switch category {
|
||||
case fixedProductions.contains:
|
||||
return try .Fixed(node.namedChildren.map {
|
||||
($0, try $0.category(document))
|
||||
})
|
||||
case keyedProductions.contains:
|
||||
return try .Keyed(Dictionary(elements: node.namedChildren.map {
|
||||
switch try $0.category(document) {
|
||||
case "pair":
|
||||
return try ($0.namedChildren[0].substring(string), ($0, "pair"))
|
||||
default:
|
||||
// We might have a comment inside an object literal. It should still be assigned a key, however.
|
||||
return try (try node.substring(string), ($0, $0.category(document)))
|
||||
}
|
||||
}))
|
||||
default:
|
||||
return try .Indexed(node.namedChildren.map {
|
||||
($0, try $0.category(document))
|
||||
})
|
||||
}
|
||||
} (root, "program")
|
||||
.map { node, category in
|
||||
// TODO: Calculate line and column from TSNodes
|
||||
Info(range: node.range, lines: 0..<1, columns: 0..<1, categories: [ category ])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func toTerm(term: CofreeJSON) -> Term {
|
||||
let lines = term.extract.0
|
||||
let columns = term.extract.1
|
||||
let range = term.extract.2
|
||||
let annotation = Info(range: range, lines: lines, columns: columns, categories: [])
|
||||
switch term.unwrap {
|
||||
case let .Leaf(a):
|
||||
return Term(Info(range: range, lines: lines, columns: columns, categories: a.categories), Syntax<Term, String>.Leaf(String(a)))
|
||||
case let .Indexed(i):
|
||||
return Term(annotation, .Indexed(i.map(toTerm)))
|
||||
case let .Fixed(f):
|
||||
return Term(annotation, .Fixed(f.map(toTerm)))
|
||||
case let .Keyed(k):
|
||||
return Term(annotation, .Keyed(Dictionary(elements: k.map { ($0, toTerm($1)) })))
|
||||
}
|
||||
}
|
||||
|
||||
func lines(input: String) -> Term {
|
||||
var lines: [Term] = []
|
||||
var previous = 0
|
||||
var lineNumber = 0
|
||||
input.enumerateSubstringsInRange(input.characters.indices, options: .ByLines) { (line, _, enclosingRange, _) in
|
||||
let range: Range<Int> = previous..<(previous + enclosingRange.count)
|
||||
previous = range.endIndex
|
||||
if let line = line {
|
||||
lineNumber += 1
|
||||
lines.append(Term(Info(range: range, lines: 0..<lineNumber, columns: 0..<1, categories: []), Syntax.Leaf(line)))
|
||||
}
|
||||
}
|
||||
return Term(Info(range: 0..<input.utf16.count, lines: 0..<lineNumber, columns: 0..<1, categories: []), .Indexed(lines))
|
||||
}
|
||||
|
||||
func parserForType(type: String) -> String throws -> Term {
|
||||
switch type {
|
||||
case "json":
|
||||
return { (input: String) throws -> Term in
|
||||
switch parse(json, input: input.characters) {
|
||||
case let .Right(term):
|
||||
return toTerm(term)
|
||||
case let .Left(error):
|
||||
throw error.description
|
||||
}
|
||||
}
|
||||
default:
|
||||
if let parser = Source.languagesByType[type].map(termWithInput) {
|
||||
return parser
|
||||
}
|
||||
return lines
|
||||
}
|
||||
}
|
||||
|
||||
extension ForwardIndexType {
|
||||
/// The range encompassing a single index.
|
||||
var range: Range<Self> {
|
||||
return self..<self.successor()
|
||||
}
|
||||
}
|
||||
|
||||
let cost = Diff.sum(Patch.sum(Term.size))
|
||||
|
||||
func refineLeafReplacement(aString: String, _ bString: String)(_ patch: Patch<Term>) -> Diff {
|
||||
guard case let .Replace(.Unroll(aExtract, .Leaf), .Unroll(bExtract, .Leaf)) = patch else { return .Pure(patch) }
|
||||
let a = aString.utf16[aExtract.range].enumerate().map { Term(Info(range: (aExtract.range.startIndex + $0).range, lines: aExtract.lines, columns: aExtract.columns, categories: aExtract.categories), .Leaf(String($1))) }
|
||||
let b = bString.utf16[bExtract.range].enumerate().map { Term(Info(range: (bExtract.range.startIndex + $0).range, lines: bExtract.lines, columns: bExtract.columns, categories: bExtract.categories), .Leaf(String($1))) }
|
||||
return .Roll((aExtract, bExtract), .Indexed(SES(a, b, cost: cost, recur: { Term.equals(annotation: const(true), leaf: ==)($0, $1) ? Term.zip($0, $1).map(Diff.init) : Diff.Replace($0, $1) })))
|
||||
}
|
||||
|
||||
let parsed = benchmark("parsing arguments & loading sources") { parse(argumentsParser, input: Process.arguments) }
|
||||
let arguments: Argument = try parsed.either(ifLeft: { throw "\($0)" }, ifRight: { $0 })
|
||||
let (aSource, bSource) = arguments.sources
|
||||
let jsonURL = NSURL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true).URLByAppendingPathComponent("diff.json")
|
||||
guard let uiPath = NSBundle.mainBundle().infoDictionary?["PathToUISource"] as? String else { throw "need ui path" }
|
||||
guard aSource.type == bSource.type else { throw "can’t compare files of different types" }
|
||||
let parser = parserForType(aSource.type)
|
||||
|
||||
let a = try benchmark("parsing source a") { try parser(aSource.contents) }
|
||||
let b = try benchmark("parsing source b") { try parser(bSource.contents) }
|
||||
let initialDiff = benchmark("diffing") { Interpreter<Term>(equal: Term.equals(annotation: const(true), leaf: ==), comparable: Interpreter<Term>.comparable { $0.extract.categories }, cost: cost).run(a, b) }
|
||||
let diff = benchmark("diffing within leaves") { initialDiff.flatMap(refineLeafReplacement(aSource.contents, bSource.contents)) }
|
||||
switch arguments.output {
|
||||
case .Split:
|
||||
let JSON: Doubt.JSON = [
|
||||
"before": .String(aSource.contents),
|
||||
"after": .String(bSource.contents),
|
||||
"diff": diff.JSON(pure: { $0.JSON { $0.JSON(annotation: { $0.JSON }, leaf: Doubt.JSON.String) } }, leaf: Doubt.JSON.String, annotation: {
|
||||
[
|
||||
"before": $0.JSON,
|
||||
"after": $1.JSON,
|
||||
]
|
||||
}),
|
||||
]
|
||||
let data = JSON.serialize()
|
||||
try data.writeToURL(jsonURL, options: .DataWritingAtomic)
|
||||
|
||||
let components = NSURLComponents()
|
||||
components.scheme = "file"
|
||||
components.path = uiPath
|
||||
components.query = jsonURL.absoluteString
|
||||
if let URL = components.URL {
|
||||
NSWorkspace.sharedWorkspace().openURL(URL)
|
||||
}
|
||||
case .Unified:
|
||||
print(benchmark("formatting unified diff") { unified(diff, before: aSource.contents, after: bSource.contents) })
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
cd `dirname $0`/../External/tree-sitter
|
||||
|
||||
case "$1" in
|
||||
""|build)
|
||||
script/configure.sh
|
||||
make "$PRODUCT_NAME"
|
||||
;;
|
||||
clean)
|
||||
script/clean.sh
|
||||
;;
|
||||
*)
|
||||
echo "I don't know how to '$1'"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
Loading…
Reference in New Issue
Block a user