Slide 1

Slide 1 text

SwiftSyntaxΛ ͏·͘࢖͓͏ Θ͍Θ͍swiftc #35 @omochimetaru 1

Slide 2

Slide 2 text

SwiftίʔυΛಡΈऔΓ͍ͨ • ϝιουΛࣗಈੜ੒͍ͨ͠ • ଞͷݴޠʹม׵͍ͨ͠ 2

Slide 3

Slide 3 text

SwiftSyntaxΛ࢖͑͹؆୯ʁ ͦ͏Ͱ΋ͳ͍ 3

Slide 4

Slide 4 text

ࠓ೔ͷ࿩ • BinarySwiftSyntax Λ࡞ͬͨ • SwiftTypeReader Λ࡞ͬͨ 4

Slide 5

Slide 5 text

SwiftSyntax͓͞Β͍ • SwiftίϯύΠϥ಺෦ʹɺ৽͘͠៉ྷʹ࡞Γ௚͞ΕͨύʔαϞ δϡʔϧ libSyntax ͕͋Δ • ίʔυฤूͳͲΛߟྀͨ͠APIΛ࣋ͭ • ͦΕΛSwift͔Β࢖͏ͨΊͷϥΠϒϥϦ͕ SwiftSyntax 1 • SwiftPMύοέʔδͱͯ͠ఏڙ 1 https://github.com/apple/swift-syntax 5

Slide 6

Slide 6 text

Ṗͷόʔδϣϯબ୒ 6

Slide 7

Slide 7 text

ύʔαຊମ͸ผ഑෍ • SwiftSyntax ͸͋͘·Ͱͨͩͷ libSyntax ΁ͷSwiftϒϦο δ • libSyntax ͸XcodeͱҰॹʹ഑෍ → ࣮ߦ؀ڥʹΑͬͯόʔ δϣϯ͕ҟͳΔ • SwiftSyntax ͷόʔδϣϯΛݻఆͰ͖ͯ΋ɺ࣮ߦ؀ڥͷ libSyntax ͷόʔδϣϯ͕ݻఆͰ͖ͳ͍ 7

Slide 8

Slide 8 text

ݱ৔Ͱى͖Δ͜ͱ A͞Μʮ͜ͷίʔυδΣωϨʔλ͸SwiftSyntaxΛ࢖͍ͬͯΔ͔ ΒɺXcode13.3Λ xcode-select Ͱࢦఆ͔ͯ͠Β࣮ߦͯͩ͘͠ ͍͞ɻʯ B͞Μʮ·ͩXcode13.2͔͠ೖͬͯͳ͍Ͱ͢ɻʯ C͞Μ ʮXcode14.0 betaͰ࡞ۀͯ͠Δ͔Β੾Γସ͑ͨ͘ͳ͍Ͱ ͢ɻʯ 8

Slide 9

Slide 9 text

ղܾํ๏ • libSyntax ΋Ұॹʹ഑෍͢Δ • SwiftPMͷ binary target ͳΒɺxcframework ͷதʹ libSyntax ΛຒΊࠐΊΔ • ͍ͭͰʹιʔε෦෼ͷϏϧυ࣌ؒ΋ΧοτͰ͖Δ 9

Slide 10

Slide 10 text

BinarySwiftSyntax 2 • SwiftSyntaxΛϏϧυ͢ΔxcodeprojΛ࡞੒ • खݩͷXcode͔Β libSyntax Λநग़ $ cp "$(xcode-select -p)"/Toolchains/XcodeDefault.xctoolchain /usr/lib/swift/macosx/lib_InternalSwiftSyntaxParser.dylib SwiftSyntax/Deps • xcframeworkʹ͢Δ $ xcodebuild archive \ -project SwiftSyntax.xcodeproj \ CODE_SIGN_IDENTITY="" \ CODE_SIGNING_REQUIRED=NO \ CODE_SIGNING_ALLOWED=NO $ xcodebuild -create-xcframework \ -framework build/UninstalledProducts/macosx/SwiftSyntax.framework \ -output dist/Xcode12.5/SwiftSyntax.xcframework 2 https://github.com/omochi/BinarySwiftSyntax 10

Slide 11

Slide 11 text

11

Slide 12

Slide 12 text

؆୯ʹ࢖͑Δ // package.swift let package = Package(name: "foo", products: [ .executable(name: "foo", targets: ["foo"]) ], dependencies: [ .package(url: "https://github.com/omochi/BinarySwiftSyntax", branch: "main") ], targets: [ .target(name: "foo", dependencies: [ // v ͜͜ͰόʔδϣϯࢦఆͰ͖Δ .product(name: "SwiftSyntax-Xcode13.0", package: "BinarySwiftSyntax") ]) ] ) // main.swift import SwiftSyntax let sourceFile: SourceFileSyntax = try SyntaxParser.parse(source: source) 12

Slide 13

Slide 13 text

͜ΕͰίʔυੜ੒͠์୊ʁ ͦ͏Ͱ΋ͳ͍ 13

Slide 14

Slide 14 text

SwiftSyntax͸೉͍͠ 14

Slide 15

Slide 15 text

Swift AST Explorer 3 @k_katsumi 3 https://swift-ast-explorer.com 15

Slide 16

Slide 16 text

let file = try SyntaxParser.parse(source: source) for statement in file.statements { if let structDecl = statement.item.as(StructDeclSyntax.self) { ... } } • .item ? • .as(StructDeclSyntax.self) ? 16

Slide 17

Slide 17 text

for decl in structDecl.members.members { if let varDecl = decl.decl.as(VariableDeclSyntax.self) { ... } } • .members.members ? • decl.decl ? • .as(VariableDeclSyntax.self) ? 17

Slide 18

Slide 18 text

• حົͳAPI͕೉͍͠ • as ϝιουʹΑΔμ΢ϯΩϟετ͕೉͍͠ (swift-ast- explorerඞਢ) • Xcode͕ॏͯ͘ఆ͕ٛΑ͘ݟ͑ͳ͍ (ܕ͕ଟ͗͢Δʁ) 18

Slide 19

Slide 19 text

• SwiftSyntax͸ߏจϨϕϧͷϥΠϒϥϦͳͷͰɺࣗ༝౓΍දݱ ೳྗ͕ߴ͍͕ɺͦͷ෼ෳࡶͰ೉͍͠ • C++޲͚ʹ࡞ͬͨ libSyntax ͱͷϒϦοδͷؔ܎Ͱ(?)ઃܭ ʹΫη͕͋Δ • ߏจϨϕϧͷϥΠϒϥϦͳͷͰɺܕ৘ใͷ໊લղܾͳͲɺҙ ຯ࿦Ϩϕϧ͸αϙʔτ͞Εͳ͍ • ༻్ʹԠͨ͡ϥΠϒϥϦΛҰͭڬΜͰ࢖͍͍ͨ 19

Slide 20

Slide 20 text

SwiftTypeReader 4 • SwiftSyntaxΛϥοϓͯ͠ɺܕ৘ใΛѻ͍΍͍͢APIͰఏڙ͢ ΔϥΠϒϥϦ • ܕҎ֎ͷ৘ใ (ؔ਺ͷຊจͳͲ) ͸ѻΘͳ͍ 4 https://github.com/omochi/SwiftTypeReader 20

Slide 21

Slide 21 text

func testSimple() throws { let result = try XCTReadTypes(""" struct S { var a: Int? } """ ) let s = try XCTUnwrap(result.module.types[safe: 0]?.struct) XCTAssertEqual(s.name, "S") XCTAssertEqual(s.location, Location([.module(name: "main")])) XCTAssertEqual(s.storedProperties.count, 1) let a = try XCTUnwrap(s.storedProperties[safe: 0]) XCTAssertEqual(a.name, "a") let aType = try XCTUnwrap(a.type().struct) XCTAssertEqual(aType.module?.name, "Swift") XCTAssertEqual(aType.name, "Optional") XCTAssertEqual(try aType.genericArguments().count, 1) let aWrappedType = try XCTUnwrap(aType.genericArguments()[safe: 0]?.struct) XCTAssertEqual(aWrappedType.module?.name, "Swift") XCTAssertEqual(aWrappedType.name, "Int") } • .storedProperties 21

Slide 22

Slide 22 text

func testEnum() throws { let result = try XCTReadTypes(""" enum E { case a case b(Int) case c(x: Int, y: Int) } """ ) let e = try XCTUnwrap(result.module.types[safe: 0]?.enum) do { let c = try XCTUnwrap(e.caseElements[safe: 0]) XCTAssertEqual(c.name, "a") } do { let c = try XCTUnwrap(e.caseElements[safe: 1]) XCTAssertEqual(c.name, "b") let x = try XCTUnwrap(c.associatedValues[safe: 0]) XCTAssertNil(x.name) XCTAssertEqual(try x.type().name, "Int") } do { let c = try XCTUnwrap(e.caseElements[safe: 2]) XCTAssertEqual(c.name, "c") let x = try XCTUnwrap(c.associatedValues[safe: 0]) XCTAssertEqual(x.name, "x") XCTAssertEqual(try x.type().name, "Int") let y = try XCTUnwrap(c.associatedValues[safe: 1]) XCTAssertEqual(y.name, "y") XCTAssertEqual(try y.type().name, "Int") } } • .caseElements • .associatedValues 22

Slide 23

Slide 23 text

func testGenericParameter() throws { let result = try XCTReadTypes(""" struct S { var a: T } """ ) let s = try XCTUnwrap(result.module.types[safe: 0]?.struct) XCTAssertEqual(s.name, "S") XCTAssertEqual(s.genericParameters.count, 1) let t = try XCTUnwrap(s.genericParameters[safe: 0]) XCTAssertEqual(t.name, "T") XCTAssertEqual(s.storedProperties.count, 1) let a = try XCTUnwrap(s.storedProperties[safe: 0]) XCTAssertEqual(a.name, "a") let at = try XCTUnwrap(a.type().genericParameter) XCTAssertEqual( at.location, Location([ .module(name: "main"), .type(name: "S") ]) ) } • .genericParameter.location 23

Slide 24

Slide 24 text

ར༻ྫ: SE0295Polyfill 5 • SE-0295 (enumͷcodableରԠ) Λίʔυੜ੒͢ΔϥΠϒϥϦ • Swift5.5 ͕ϦϦʔε͞ΕͨͷͰطʹҾୀ 5 https://github.com/omochi/SE0295Polyfill 24

Slide 25

Slide 25 text

enum Command: Codable { case load(key: String) case store(key: String, value: Int) } 25

Slide 26

Slide 26 text

extension Command { enum CodingKeys: Swift.CodingKey { case load case store } enum LoadCodingKey: Swift.CodingKey { case key } enum StoreCodingKey: Swift.CodingKey { case key case value } } extension Command { public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) switch self { case .load(key: let key): var nestedContainer = container.nestedContainer(keyedBy: LoadCodingKey.self, forKey: .load) try nestedContainer.encode(key, forKey: .key) case .store(key: let key, value: let value): var nestedContainer = container.nestedContainer(keyedBy: StoreCodingKey.self, forKey: .store) try nestedContainer.encode(key, forKey: .key) try nestedContainer.encode(value, forKey: .value) } } } extension Command { public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) if container.allKeys.count != 1 { let context = DecodingError.Context( codingPath: container.codingPath, debugDescription: "Invalid number of keys found, expected one." ) throw DecodingError.typeMismatch(Command.self, context) } switch container.allKeys.first.unsafelyUnwrapped { case .load: let nestedContainer = try container.nestedContainer(keyedBy: LoadCodingKey.self, forKey: .load) let key = try nestedContainer.decode(String.self, forKey: .key) self = .load(key: key) case .store: let nestedContainer = try container.nestedContainer(keyedBy: StoreCodingKey.self, forKey: .store) let key = try nestedContainer.decode(String.self, forKey: .key) let value = try nestedContainer.decode(Int.self, forKey: .value) self = .store(key: key, value: value) } } } 26

Slide 27

Slide 27 text

·ͱΊ • SwiftSyntax͸ libSyntax Λಉࠝͯ͠΄͍͠ • SwiftTypeReaderΛ࢖ͬͯίʔυੜ੒Λ͠Α͏ 27