Upgrade to Pro — share decks privately, control downloads, hide ads and more …

SwiftSyntaxをうまく使おう

 SwiftSyntaxをうまく使おう

わいわいswiftc #35

omochimetaru

May 01, 2022
Tweet

More Decks by omochimetaru

Other Decks in Programming

Transcript

  1. ύʔαຊମ͸ผ഑෍ • SwiftSyntax ͸͋͘·Ͱͨͩͷ libSyntax ΁ͷSwiftϒϦο δ • libSyntax ͸XcodeͱҰॹʹ഑෍

    → ࣮ߦ؀ڥʹΑͬͯόʔ δϣϯ͕ҟͳΔ • SwiftSyntax ͷόʔδϣϯΛݻఆͰ͖ͯ΋ɺ࣮ߦ؀ڥͷ libSyntax ͷόʔδϣϯ͕ݻఆͰ͖ͳ͍ 7
  2. ղܾํ๏ • libSyntax ΋Ұॹʹ഑෍͢Δ • SwiftPMͷ binary target ͳΒɺxcframework ͷதʹ

    libSyntax ΛຒΊࠐΊΔ • ͍ͭͰʹιʔε෦෼ͷϏϧυ࣌ؒ΋ΧοτͰ͖Δ 9
  3. 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
  4. 11

  5. ؆୯ʹ࢖͑Δ // 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
  6. 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
  7. for decl in structDecl.members.members { if let varDecl = decl.decl.as(VariableDeclSyntax.self)

    { ... } } • .members.members ? • decl.decl ? • .as(VariableDeclSyntax.self) ? 17
  8. 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
  9. 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
  10. func testGenericParameter() throws { let result = try XCTReadTypes(""" struct

    S<T> { 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
  11. 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