ํฐ์คํ ๋ฆฌ ๋ทฐ
์๋
ํ์ธ์!
์กฐ๊ธ ๋ฆ์ ๊ฐ์ด ์์ง๋ง, Swift 5.9์์๋ถํฐ ์ ๊ณตํ๊ธฐ ์์ํ macro์ ๋ํด ๊ณต๋ถํด ๋ณด๊ณ ์
WWDC 23 - Write Swift macros ์ธ์
์ ๋ฃ๊ณ ์ ๋ฆฌํด ๋ณด์์ต๋๋ค.
Swift์ macro๋ฅผ ์ฌ์ฉํ๋ฉด ๊ธฐ์กด์ ๋ฐ๋ณต๋๋ ์ฝ๋๋ฅผ ์ปดํ์ผํ์์ ์์ฑํ์ฌ, ๊ฐ๋ฐ ์์ฐ์ฑ์ ๋์ฌ์ค๋ค.
Overview
let calculations = [
( 1 + 1, "1 + 1" ),
( 2 + 3, "2 + 3" ),
( 7 - 3, "7 - 3" ),
( 5 - 2, "5 - 2" ),
( 3 * 2, "3 * 2" ),
( 3 * 5, "3 * 5" )
]
์ด๋ ๊ฒ ํ์๋ค์ด ๊ณ์ฐ ๋ฅ๋ ฅ์ ์ฐ์ตํ๋ ๋ฐ ์ฌ์ฉํ ์ ์๋ ๊ณ์ฐ ๋ชฉ๋ก์ด ์๋ค.
tuple์ ์ผ์ชฝ์๋ ์ ์ํ ๊ฒฐ๊ณผ๊ฐ ํ์๋๊ณ , tuple์ ์ค๋ฅธ์ชฝ์๋ ๊ณ์ฐ์์ด ๋ฌธ์์ด๋ก ๋ํ๋๋ค.
์ด๋, 2๊ฐ์ง์ ๋ฌธ์ ์ ์ด ๋ฐ์ํ๋ค.
- tuple์ ๋ํ ๋ด์ฉ์ธ (๊ฒฐ๊ณผ, ๊ณ์ฐ์)์ ๊ฐ๋ฐ์๊ฐ ์๊ธฐ๋ก ์ ๋ ฅํ๊ณ ์๊ธฐ ๋๋ฌธ์, ๊ฒฐ๊ณผ์ ์์ด ๋ง์ง ์์ ์ ์๋ค.
- ๊ฐ ๋์ถ์ ์ํด์ , ๋ฐ๋ณต์ ์ด๊ณ ์ค๋ณต์ ์ธ ์ฝ๋ ์์ฑ์ด ํ์ํ๋ค.
ํ์ง๋ง, Swift 5.9์์๋ macro๋ฅผ ์ ์ํ์ฌ ์ด ์์ ์ ๊ฐ์ํํ ์ ์๋ค!
let calculations = [
#stringify(1 + 1),
#stringify(2 + 3),
#stringify(7 - 3),
#stringify(5 - 2),
#stringify(3 * 2),
#stringify(3 * 5)
]
#stringify๋ผ๋ ์ด๋ฆ์ macro๋ '๊ณ์ฐ'๋ง ๋จ์ผ ๋งค๊ฐ๋ณ์๋ก ์ทจ๊ธํ๋ค.
๊ทธ๋ฆฌ๊ณ , ์ปดํ์ผํ์์ ์์์ ๋ณธ tupleํ์์ผ๋ก ๋ณํ๋์ด ๊ณ์ฐ๊ณผ ๊ฒฐ๊ณผ๊ฐ ์ผ์นํ๋๋ก ๋ณด์ฅํ๋ค.
@freestanding(expression)
macro stringify(_ value: Int) -> (Int, String)
#stringify๋ ํจ์์ ์๊น์๊ฐ ๋งค์ฐ ํก์ฌํ๋ฐ, ์ ์๋ฅผ ๋งค๊ฐ๋ณ์๋ก ํ์ฌ, ๊ฒฐ๊ณผ์ ๊ณ์ฐ์์ tupleํํ๋ก ๋ฐํํ๋ค.
๋ํ ๋งคํฌ๋ก๋ฅผ ์ฌ์ฉํ ๋ ์๋์ ์ผ์ด์ค์๋ ์ปดํ์ผ ์๋ฌ๋ฅผ ๋ฐ์์ํจ๋ค.
- ๋งคํฌ๋ก ํํ์์ ์ธ์๊ฐ ๋งค๊ฐ๋ณ์์ ์ผ์นํ์ง ์๋ ๊ฒฝ์ฐ
- ๋งคํฌ๋ก ํํ์์ ์ธ์์ ๋ํ ํ์ ๊ฒ์ฌ๋ฅผ ์ํํ์ง ์๋ ๊ฒฝ์ฐ
#stringify("hello world") // ERROR >> Cannot convert value of type 'String' to expected argument type 'Int'
์ด์ฒ๋ผ ๋งคํฌ๋ก ํํ์์ ์ธ์์ ๋ค๋ฅธ ํ์ ์ ๊ฐ์ ๋งค๊ฐ๋ณ์๋ก ์์ฑํ๋ฉด ์๋ฌ๊ฐ ๋ฐ์
์ฆ, Compiler๋ ๋ชจ๋ ์ธ์๊ฐ macro์ ํ๋ผ๋ฏธํฐ์ ์ผ์นํ๋์ง ๋จผ์ ํ์ธ ํ, macro๋ฅผ ๋์์ํจ๋ค.
๊ทธ๋ฆฌ๊ณ ํด๋น #stringify๋ ๋
๋ฆฝํ ํํ์ ๋งคํฌ๋ก(Freestanding Expression macro) ์ด๊ธฐ ๋๋ฌธ์,
ํํ์์ ์์ฑํ ์ ์๋ ์์น๋ผ๋ฉด ์ด๋์๋ ์ผ๋ฐ์ ์ธ ๊ฐ์ฒ๋ผ ์ฌ์ฉํ ์ ์๋ค. (ํจ์์ return ๊ฒฐ๊ณผ์ ๊ฐ์...)
์ฌ๊ธฐ์ ํํ์์ ์์ฑํ ์ ์๋ ์์น ๋?
๋ณ์ ํ ๋น, ํจ์ ํธ์ถ, ์ฐ์ฐ ๋ฑ ๊ณผ ๊ฐ์ด ์ผ๋ฐ์ ์ธ ๊ฐ์ด ์์นํ ์ ์๋ ๊ณณ์ ์๋ฏธํ๋ค.์ฆ, type ์์ ๋์ค๋ ํค์๋ (ex. final class A {... }์์์ final) ๋ฑ์ ํํ์์ ์์น๊ฐ ์๋๋ค!
โ ๋จ์ผ ๋งคํฌ๋ก ํํ์
macro๋ ํ์ฅ์ ์ํํ๊ณ ์, Compiler Plug-in์์ ๊ตฌํ์ ์ ์ํ๋ค.
๊ทธ๋ฆฌ๊ณ , Compiler๋ macro ํํ์์ ์ ์ฒด ์์ค์ฝ๋๋ฅผ ํด๋นํ๋ macro Plug-in์ ์ ๋ฌํ๋ค.
macro Plug-in์ macro์ ์์ค ์ฝ๋๋ฅผ SwiftSyntax ํธ๋ฆฌ๋ก ํ์ฑ ํ๋๋ฐ, ์ด SwiftSyntax ํธ๋ฆฌ๋, macro์ ๊ตฌ์กฐ์ ํํ์ด์ macro ๋์์ ๊ธฐ๋ฐ์ด ๋๋ค.
#stringify macro๋ SwiftSyntax ํธ๋ฆฌ์์ ๋ ธ๋์ ๊ตฌ์กฐ๋ก ํํ๋๋ค.
- identifier : macro์ ์ด๋ฆ์ ์ ์
- InfixOperatorExprSyntax : 2 + 3์ด๋ผ๋ ์ค์์ฐ์ฐ์ ์
macro๋ ๊ตฌํ ์์ฒด๋ฅผ Swift๋ก ๊ตฌํํ๊ณ , SwiftSyntaxํธ๋ฆฌ๋ก ๋ณํ์ด ๋งค์ฐ ์์ ๋กญ๋ค.
์ ์์์ ๊ฒฐ๊ณผ์ธ tuple์ ์์ฑํ๊ณ , ์์ฑ๋ SwiftSyntax ํธ๋ฆฌ๋ฅผ ๋ค์ ์์ค ์ฝ๋๋ก ์ง๋ ฌํํด์ Compiler๋ก ๋ณด๋ด๋ฉด, ๊ธฐ์กด์ #stringify(2+3)๋ผ๋ macro ํํ์์ด ํ์ฅ๋ ์ฝ๋๋ก ๋์ฒด๋๋ค.
Create a macro
Xcode์์ macro Template๋ฅผ ์ง์ ๋ง๋ค์ด๋ณด์.
1๏ธโฃ macro ํ ํ๋ฆฟ ์์ฑํ๊ธฐ
Xcode >> File >> New >> Package... ๋ฅผ ์ ํํ๋ค.
Swift macro๋ฅผ ์ ํํ๊ณ , ์๋กญ๊ฒ ์์ฑํด์ค๋ค.
์์ฑ๋ Swift macro ํฌํ๋ฆฟ์ ํ์ธํด ๋ณด๋ฉด, ์ด์ ์ ์ฐ๋ฆฌ๊ฐ ๋ค๋ค๋ #stringify๊ฐ ํํ์์ ์ฌ์ฉ๋๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
์ค์ macro๊ฐ ํ์ฅ๋ ๋ชจ์ต์ ํ์ธํ๋ ค๋ฉด, ํํ์์ ์ฐํด๋ฆญํด์ Expand macro๋ฅผ ์ ํํ๋ฉด ๋๋ค
2๏ธโฃ macro ์ง์ ์ ์ํ๊ธฐ
์ฐ๋ฆฌ๊ฐ ์ง์ macro๋ฅผ ์ ์ํด ๋ณด๊ธฐ ์ ์, ์ฐ์ #stringify๊ฐ ์ด๋ป๊ฒ ์ ์๋์ด์๋์ง๋ฅผ ํ์ธํด ๋ณด์.
์ฌ์ง ์ ์ฝ๋์ ๊ฐ์ด ์ ์ํ ๋์ , ์ ๋ค๋ฆญ ํ์
์ผ๋ก ์ ์๋์ด ๋ชจ๋ ํ์
T๋ฅผ ๋ฐ์ ์ ์๋ค.
๋ํ macro๋ #externalmacro๋ก ์ ์ธ๋๋ค.
์ฆ, ํด๋น macro๋ฅผ ์ค์ ๋ก ์ํํ๋ ค๋ฉด WWDCmacro ๋ชจ๋์์ Stringifymacroํ์ ์ ๊ฐ์ ธ์ค๋ผ๊ณ Compiler์๊ฒ ์๋ฆฌ๋ ๊ฒ์ด๋ค.
#stringify ๋งคํฌ๋ก๋, ๋ ๋ฆฝํ ํํ์ ๋งคํฌ๋ก(Freestanding Expression macro)๋ก ์ ์๋๊ธฐ ๋๋ฌธ์ Expressionmacro๋ฅผ ์ค์ํด์ผ ํ๋ค.
์ ์ฝ๋์์ ์ ์ ์๋ฏ์ด, Expressionmacro๋ expansion(of node: in context) ํจ์๋ฅผ ํ์์๊ตฌ์ฌํญ์ผ๋ก ๊ฐ๋๋ค.
expansion(of node: in context)๋ Syntax Tree(๊ตฌ๋ฌธ ํธ๋ฆฌ)์ Context๋ฅผ ์ธ์๋ก ์ทจํ๋๋ฐ, ์ด๋ ์ปดํ์ผ๋ฌ์์ ํต์ ์ ํ์ํ ์์๋ค์ด๋ค.
์ดํ์ expansion(of node: in context) ํจ์๋ ์ฌ์์ฑ๋ ํํ์ ๊ตฌ๋ฌธ์ return ํ๊ฒ ๋๋ค.
ํจ์ ๋ด๋ถ ๊ตฌํ์ ์ดํด๋ณด๋ฉด, ์ฐ์ node์ ๋จ์ผ ์ธ์(argumentList.first?.expression) ๋ฅผ macro์ ํํ์์ผ๋ก ๊ฐ์ ธ์จ๋ค.
#stringify๋ฅผ ์ ์ธํ ๋ ๋จ์ผ ๋งค๊ฐ๋ณ์๋ฅผ ์ทจํ๋๋ก ์ ์ธํ๊ธฐ ๋๋ฌธ์ ํด๋น ์ธ์๋ ๋ฐ๋์ ์กด์ฌํ๊ณ , ์กด์ฌํ๋ ๋ชจ๋ ์ธ์์ ๋ํ ํ์ ๊ฒ์ฌ๋ฅผ ํด์ผ ํ๋ค.
๊ทธ๋ฆฌ๊ณ ๋ฌธ์์ด ๋ณด๊ฐ๋ฒ์ ํ์ฉํด์, tuple์ Syntax Tree(๊ตฌ๋ฌธ ํธ๋ฆฌ)๋ฅผ ์์ฑํ๊ฒ ๋๋ค.
์ด ํจ์์์ ๋ฐํํ๋ ๊ฐ์ ๋จ์ํ ๋ฌธ์์ด์ด ์๋๋ผ ExprSyntax
์ฆ, ํํ์ ๊ตฌ๋ฌธ์ ๋ฐํํ๋ ๊ฒ์ด๋ค.
macro๋ ์ด ํํ์ ๊ตฌ๋ฌธ์ Swift ํ์๋ฅผ ์ด์ฉํ์ฌ ํด์์ ์งํํ๊ณ , ์ค์ ํํ์์ผ๋ก ๋ณํ๋๋ค.
3๏ธโฃ macro ํ ์คํธ ์ฝ๋ ์์ฑํ๊ธฐ
macro๋ ๋ฐํ์์์ expansion ๋์ด ์ค์ ์ฝ๋๋ก ์ ์ฉ๋๊ธฐ ์ ์๋, ๋ฒ๊ทธ๊ฐ ์๋์ง ์์์ฐจ๋ฆฌ๊ธฐ ๊น๋ค๋กญ๋ค.
๋ฐ๋ผ์ macro๊ฐ ์ ๋๋ก ๋์ํ๋์ง ํ
์คํธํ๋ ๊ณผ์ ์ด ์์ด์ผ ํ๋ค.
macro ํ ํ๋ฆฟ์์ ์ ๊ณตํ๋ Unit-Test๋ฅผ ํ์ธํด๋ณด์.
assertmacroExpansion
- SwiftSyntax ํจํค์ง์ ํจ์์ด๊ณ , #stringify๊ฐ ์ฌ๋ฐ๋ฅด๊ฒ expansion ๋๋์ง๋ฅผ ๊ฒ์ฆ
- ์์ ์์์์๋ ์ฒซ ๋ฒ์งธ ์ธ์๋ก #stringify์ ํํ์์ ๋ฆฌํฐ๋ด๋ก ๋ฐ๊ณ , expandedSource๋ผ๋ ์ด๋ฆ์ ํ๋ผ๋ฏธํฐ์๋ expansion ๋ ๊ฒฐ๊ณผ๋ฅผ ๋ฌธ์์ด๋ก ์ ๋ ฅ๋ฐ๋๋ค
- ๊ทธ๋ฆฌ๊ณ macros ์ธ์์๋ [String: macro.Type] ํ์ ์ ๋ฐ๋๋ฐ, ์ด๋ expansion์ผ๋ก ์ฌ์ฉํ macro๋ฅผ ์ง์ ํ๋ ๊ฒ์ด๋ค.
์ค์ ํด๋น ํ ์คํธ์ฝ๋๋ฅผ ์คํ์ํค๋ฉด, ์ฑ๊ณตํ๊ฒ ๋๋ค.
์ง๊ธ๊น์ง ์ ๋ฆฌ๋ macro์ ํน์ง
- macro ์ ์ธ์ ํ๊ธฐ ์ํด์, macro์ signature์ ์ญํ ์ ์ ์ํ๋ค.
- ์ดํ Compiler plug-in์ด ๋ด๋ถ์ ์ผ๋ก ๊ตฌ๋ฌธํธ๋ฆฌ๋ก ๋ณํํ์ฌ ๊ฒฐ๊ณผ์ ๋ํ ํด์์ ํ๊ณ , ์ด๋ฅผ expansion์์ ๊ฒฐ๊ณผ ์ฝ๋๋ก ๋ณํํ๋ค.
- ๋ํ, macro๋ testable ํ๋ค.
macro roles
macro๋ ์ง๊ธ๊น์ง ์์๋ณด์๋ @freestanding(expression)(๋ ๋ฆฝํ ํํ์ ๋งคํฌ๋ก) ์ด์ธ์๋ ์๋ง์ ํํ๊ฐ ์กด์ฌํ๋ค.
์ด๋ฒ ์ธ์ ์์๋ @attached(member)์ ๋ํด ์ฃผ๋ก ์์๋ณผ ์์ ์.
@attached(member) macro ์๊ฐ
@attached(member)๋ฅผ ์ ์ฉํ๊ธฐ ์ํด ๊ฐ์ ํ ์ํฉ์ ์๋์ ๊ฐ๋ค.
''๋๋ ํ์ฌ ์คํค ๊ฐ์ฌ๋ก ์ผ ํ๊ณ ์๊ณ , ํ์๋ค๊ณผ ํจ๊ป ๊ฐ ์ฌํ์ ๊ณํํ๋ ๋ฐ ์ฌ์ฉํ ์ฑ์ ๊ฐ๋ฐ ์ค์ด๋ค.
๊ทธ๋ฆฌ๊ณ ์คํค์ฅ์ ๊ฐ์ ๋, ํ์๋ค์ ์คํค ์์ค์ ๋ฐ๋ผ ๋๋ฌด ์ด๋ ค์ด ์ฌ๋กํ์ ์ด๋ณด์๋ฅผ ๋ฐ๋ ค๊ฐ์ง ์๋๋ก ํ๊ณ ์ถ๋ค."
enum Slope {
case beginnersParadise
case practiceRun
case livingRoom
case olympicRun
case blackBeauty
}
enum EasySlope {
case beginnersParadise
case practiceRun
init? (_ slope: Slope) {
switch slope {
case .beginnersParadise: self = .beginnersParadise
case .practiceRun: self = .practiceRun
default: return nil
}
}
var slope: Slope {
switch self {
case .beginnersParadise: return .beginnersParadise
case .practiceRun: return .practiceRun
}
}
}
์ด๋ ๊ฒ Slope์ EasySlope ์ด๊ฑฐํ์ ์ ์ํจ์ผ๋ก์จ, ์ ํ๋ Slope๋ฅผ ๊ธฐ์ค์ผ๋ก EasySlope์ ์ํ๋์ง๋ฅผ ํ์ธํ ์ ์์ ํ๋ฅญํ ํ์ ์์ ์ฑ์ ์ ๊ณตํ ์ ์๋ค.
ํ์ง๋ง, ๋ฐ๋ณต์ ์ธ ์ฝ๋๊ฐ ๋๋ฌด ๋ง๊ณ ์ด๋ฅผ macro๋ฅผ ํ์ฉํด์ ๊ฐ์ ํด ๋ณด์
๋ฐ๋ณต์ ์ธ Slope์ ๋ํ macro ์์ฑ
๋ชฉํ๋, EasySlope์ initializer์ computed-property๋ฅผ ์๋์ผ๋ก ์์ฑํ๋ ๊ฒ์ด๋ค.
- initializer์ computed-property๋ ๋ชจ๋ EasySlopeํ์ ์ member์ด๋ฏ๋ก, @attatched(member)๋ฅผ ์ ์ธํด์ผ ํ๋ค.
- macro์ ๊ตฌํ์ ํฌํจํ Compiler plug-in์ ์์ฑํ๋ค.
-> ์ด๋, macro์ ๋ํ ํ ์คํธ๋ฅผ ์งํํด์ผ ํ๊ธฐ ๋๋ฌธ์ ๊ตฌํ์ฒด๋ ๋น์๋๋ค(empty implementation) - macro์ ๋ํ ํ ์คํธ ์ฝ๋๋ฅผ ์์ฑํ๋ค.
- ํ ์คํธ๊ฐ ์ฑ๊ณตํ๋ฉด, ํ ์คํธ ์ผ์ด์ค์ ์ผ์นํ๋๋ก ์ค์ ๊ตฌํ์ฒด๋ฅผ ๊ตฌํํ๋ค.
- ์์ฑ๋ macro๋ฅผ ์ค์ App์ ์ฝ๋์ ํตํฉํ๋ค.
/// Defines a subset of the `Slope` enum
///
/// Generates an initializer that converts a `Slope` to this type if the slope is
/// declared in this subser, otherewise return `nil`
///
/// - Implement: All enum cases declared in this macro must also exist in the
/// `Slope` enum.
@attached(member, names: named(init))
public macro SlopeSubset() = #externalmacro(module: "WWDCMacros", type: "SlopeSubsetMacro")
macro์ ์ด๋ฆ์ SlopeSubset์ผ๋ก ๋ช
๋ช
ํ๋ค
-> EasySlope๋ Slope์ ๋ถ๋ถ์งํฉ ์ด๊ธฐ ๋๋ฌธ์..!!
@attached(member, names: named(init))
๋ํ, ์ฌ๊ธฐ์ ์ ์ ์๋ฏ์ด macro๋ฅผ ๊ตฌํ์์ ์์ฑํ ๋ฉค๋ฒ์ ์ด๋ฆ๋ ์ ์ํ๋ค..!
public macro SlopeSubset() = #externalmacro(module: "WWDCMacros", type: "SlopeSubsetMacro")
ํ์ฌ๋ macro์ signature๋ฅผ ์ ์ธํ ์ํ์ง๋ง, ์ค์ ๋์์ ์ํํ๋ expansion์ ์์ง ๊ตฌํํ์ง ์์๋ค.
expansion์ ๊ตฌํํ๊ธฐ ์ํด์, WWDCmacros ๋ชจ๋์ SlopeSubsetmacro ํ์
์ ์ฐธ์กฐํ๋ค.
์ด์ ํด๋น ํ์ (SlopeSubsetmacro)์ ์์ฑํด์ ์ค์ macro๋ฅผ ๊ตฌํํด ๋ณด์!
/// Implementation of the `SlopeSubset` macro.
public struct SlopeSubsetmacro: MemberMacro {
}
@main
struct WWDCPlugin: CompilerPlugin {
let providingmacros: [Macro.Type] = [
]
}
์ฐ๋ฆฌ๋ ํ์ฌ SlopeSubset์ @attatchd(member)๋ก ์ ์ธํ๊ธฐ ๋๋ฌธ์, ํด๋น SlopeSubset๊ฐ ๋ฐ๋ฅด๋ Type์ธ SlopeSubsetmacro์ ๊ตฌํ์ฒด๋ Membermacro ํ๋กํ ์ฝ์ ์ค์ํด์ผ ํ๋ค.
public struct SlopeSubsetmacro: MemberMacro {
public static func expansion(
of node: AttributeSyntax,
providingMembersOf declaration: some DeclGroupSyntax,
in context: some macroExpansionContext
) throws -> [DeclSyntax] {
return [] /// > ์ค์ ๊ตฌํ์ฒด๋ฅผ ๊ตฌํํ๊ธฐ ์ ์, ์ฐ์ ํ
์คํธ ์ฝ๋๋ฅผ ์์ฑํ๊ธฐ ์ํด ๋น ๋ฐฐ์ด์ ๋ฆฌํด
}
}
/// > ์ปดํ์ผ๋ฌ์๊ฒ, `SlopeSubsetmacro`๋ผ๋ macroํ์
์ด ์กด์ฌํจ์ ๋
ธ์ถํจ
@main
struct WWDCPlugin: CompilerPlugin {
let providingmacros: [Macro.Type] = [
SlopeSubsetmacro.self
]
}
Membermacro ํ๋กํ ์ฝ ๋ํ Expressionmacro๊ณผ ๋ง์ฐฌ๊ฐ์ง๋ก, ํ์ ์๊ตฌ์ฌํญ์ผ๋ก expansion ํจ์๋ฅผ ๊ฐ๋๋ค.
- node: macro๋ฅผ Type์ ์ ์ฉํ ๋ ์ฌ์ฉํ๋ ์ค์ attribute๋ฅผ ๊ฐ์ ธ์ด
-> ์ฌ๊ธฐ์๋ @SlopeSubset (macro์ Signature) - declaration: macro๊ฐ ์ ์ฉ๋ ์ค์ ํ์
-> ์ฌ๊ธฐ์๋ EasySlope.Self
์ด์ ์ค์ ๊ตฌํ์ ํ๊ธฐ ์ ์, macro๊ฐ ์ ๋์ํ๋์ง ํ์ธํ๊ธฐ ์ํด ํ ์คํธ ์ผ์ด์ค๋ฅผ ์์ฑํด๋ณด์.
final class WWDCTests: XCTestCase {
func testSlopeSubset() {
assertmacroExpansion(
originalSource: String,
expandedSource: String,
macros: [String : any Macro.Type]
)
}
}
ํ ํ๋ฆฟ์ ํ ์คํธ ์ผ์ด์ค์ฒ๋ผ assertmacroExpansion์ ํตํด macro์ ๋์์ ๊ฒ์ฆํ๋ค.
let testmacros: [String: Macro.Type] = [
"SlopeSubset": SlopeSubsetmacro.self,
]
final class WWDCTests: XCTestCase {
func testSlopeSubset() {
assertMacroExpansion(
"""
@SlopeSubset
public enum EasySlope {
case beginnersParadice
case practiceRun
}
""",
expandedSource:
"""
public enum EasySlope {
case beginnersParadice
case practiceRun
}
""",
macros: testmacros
)
}
}
- originalSource์๋ ์ค์ ๋ก ๊ฒ์ฆํ๊ณ ์ ํ๋ ์ฝ๋์ธ ์๋์ string ๋ฆฌํฐ๋ด์ ์ ๋๋ค.
- SlopeSubsetmacro์ ๋ํ expansionํจ์์ ๊ตฌํ์ฒด๊ฐ ํ์ฌ๋ ๋น ๋ฐฐ์ด์ ๋ฐํํ๊ธฐ ๋๋ฌธ์, macro์ ์ํด์ ๋ณํ๋๋ ๊ฐ์ ์๋ฌด๊ฒ๋ ์๋ค๋ ๊ฒ์ ์๋ฏธํ๋ค. ์ฆ, ์๋ณธ ์ฝ๋ ๊ทธ๋๋ก expansion ๋๋ค.
- ๋ง์ง๋ง์ผ๋ก, ํ ์คํธํ macro์ ์ด๋ฆ์ธ SlopeSubset๊ณผ ํด๋น macro์ ๊ตฌํ์ฒด์ธ SlopeSubsetmacro๋ฅผ ๋งคํํ testmacro๋ฅผ ์์ฑํ๊ณ ํจ์์ ์ ๋ฌํ๋ค.
๊ทธ๋ฆฌ๊ณ ํด๋น ํ
์คํธ ์ฝ๋๋ฅผ ์ค์ ๋ก ์คํ์์ผ ๋ณด๋ฉด, ์ฑ๊ณต์ ์ผ๋ก ํต๊ณผํ๊ฒ ๋๋ค.
์ด๋, ์ฃผ์ํ ์ ์ macro๋ ๊ฐ๋ฐ ์ค์ธ ํ๋ซํผ(์: Mac์ macOS)์์๋ง ์ง์๋๋ฏ๋ก, ์ค์ ํ ์คํธ๊ฐ ์๋ํ๋ ค๋ฉด ๋น๋ ๋์์ ์๋ฎฌ๋ ์ดํฐ๊ฐ ์๋ Mac์ผ๋ก ์ง์ ํด์ผ ํ๋ค!!!
์ด๋ฒ์ ์ฐ๋ฆฌ๊ฐ ์ค์ ๋ก ์์ฑํ๊ธธ ์ํ๋ initializer ์ฝ๋๋ฅผ expandedSources์ ๋ฃ๊ณ ํ ์คํธ๋ฅผ ๋๋ ค๋ณด์
๋น์ฐํ๊ฒ๋, ์์ง macro์ ์ค์ ๊ตฌํ๋ถ์์๋ ์๋ฌด๋ฐ ์์ ์ด ์๊ธฐ ๋๋ฌธ์ ํ ์คํธ๋ ์คํจํ๊ฒ ๋๋ค.
๊ทธ๋ผ ์ด์ , ์ค์ ๊ตฌํ๋ถ๋ฅผ ์์ฑํด ๋ณด์
1. declaration์ ๊ฐ์ ธ์ค๊ณ ์ ํ๋ type์ผ๋ก ์บ์คํ ํ๊ธฐ
public static func expansion(
of node: AttributeSyntax,
providingMembersOf declaration: some DeclGroupSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
guard let enumDecl = declaration.as(EnumDeclSyntax.self) else {
//TODO: Emit an error here
return []
}
return []
}
๊ตฌํํ๊ณ ์ ํ๋ initializer๋ EasySlopes์ ์ด๊ฑฐํ์ ์ ์ธ๋ ๋ชจ๋ ์์๋ฅผ ์๋กญ๊ฒ ์ ํํ๋ค.
์ด๋ฅผ ์ํด declaration์์ ๋ชจ๋ case๋ฅผ ํ์ํด์์ผ ํ๋ฏ๋ก declaration์ enum์ผ๋ก ์บ์คํ
ํ๋ค.
๊ทธ๋ฆฌ๊ณ , enum์ด ์๋ ํ์ ์ด macro๋ก ๋์ด์ค๋ฉด error๋ฅผ ๋ฐฉ์ถํด์ผ ํ๋ฏ๋ก ์ผ๋จ TODO๋ก ๋๋ค.
2. ์บ์คํ ํ enum์ผ๋ก๋ถํฐ ๋ชจ๋ ์์๋ฅผ ๊ฐ์ ธ์ค๊ธฐ
์ด์ ์บ์คํ ํ enum์์ ๋ชจ๋ ์์(case)๋ค์ ๊ฐ์ ธ์์ผ ํ๋๋ฐ, Syntax Tree(๊ตฌ๋ฌธ ํธ๋ฆฌ)๋ฅผ ๋ถ์ํ์ฌ ํ์ํ ๊ฐ์ ๊ฐ์ ธ์์ผ ํ๋ค.
Syntax Tree(๊ตฌ๋ฌธ ํธ๋ฆฌ)๋ฅผ ๋ถ์ํ๊ธฐ ์ํด์ ๋๋ฒ๊น ๋จ๊ณ์์ braking-point๋ฅผ ์ง์ ํ์ฌ print-object(po)๋ฅผ ํ์ฉํ๋ฉด ์ค์ Syntax Tree(๊ตฌ๋ฌธ ํธ๋ฆฌ)์ ๊ตฌ์กฐ๋ฅผ ํ์ธํ๊ณ ํ์ํ ๊ฐ์ ํ์ธํ ์ ์๋ค.
์ถ๋ ฅ๋ Syntax Tree(๊ตฌ๋ฌธ ํธ๋ฆฌ)์์ ๊ฐ์ฅ ์ ์ชฝ์ ์๋ ๋ ธ๋๋, ์ฐ๋ฆฌ๊ฐ ํ์ํ EnumCaseDeclSyntax ์ธ beginnersParadice, practiceRun์ ํํํ๊ณ ์๋ค.
์ด 2๊ฐ์ง ์์๋ฅผ ๊ฐ์ ธ์ค๊ธฐ ์ํด์ , Syntax Tree(๊ตฌ๋ฌธ ํธ๋ฆฌ)์ ๊ตฌ์กฐ๋ฅผ ๋จ๊ณ๋ณ๋ก ํ๊ณ ๊ฐ์ ์ ๊ทผํด์ผ ํ๋ค.
2-1. Syntax Tree(๊ตฌ๋ฌธ ํธ๋ฆฌ)๋ฅผ ๋ถ์ํ๊ณ ํ์ํ ๋ฐ์ดํฐ์ ์ ๊ทผํ๊ธฐ
๊ฐ์ฅ ๋ฐ๊นฅ์ EnumDeclSyntax์ ์๋ memberBlock์ ์ค๊ดํธ(Brace)์ ์ค์ ๋ฉค๋ฒ(members)๋ฅผ ํฌํจํ๋ค.
๋ด๋ถ member๋ค์ ์ ๊ทผํ๊ธฐ ์ํด์ , enumDecl.memberBlock.members๋ก ์์ํด์ผ ํ ๊ฒ์ด๋ค.
members ๋ด๋ถ์ index๋ก ๊ตฌ๋ถ๋ member๋, ์ ์ธ๋ถ(decl)์ optional๋ก ์ธ๋ฏธ์ฝ๋ก (์ฌ๊ธฐ์ ์์)์ ํฌํจํ๋ค.
์ฐ๋ฆฌ๋ ์ด ์ค์์ case๋ฅผ ์ค์ ๋ก ์ ์ธํ๋ ๋ถ๋ถ์ธ EnumCaseElememtListSyntax๊ฐ ํ์ํ๋ค.
๋ฐ๋ผ์, compactMap์ ํ์ฉํด์ EnumCaseDeclSyntaxํ์
์ผ๋ก ์บ์คํ
๋๋ ๋ชจ๋ ๊ฐ์ ๊ฐ์ ธ์จ๋ค.
-> EnumCaseDeclSyntax๊ฐ ์๋ ํ์
์ ๋ชจ๋ ์ ๊ฑฐํ๊ธฐ ์ํด compactMapํ์ฉ
์ด๋ ๊ฒ ๊ฐ์ ธ์จ let caseDecls๋ ๊ฐ ๊ฐ ๋จ์ผ ์์์ผ ์๋ ์์ง๋ง, ํ ์ค์ ์ฌ๋ฌ ์์๋ฅผ ํ ๋ฒ์ ์ ์ํ ์๋ ์๋ค
-> case beginnersParadice, practiceRun ์ด๋ฐ ์์ผ๋ก..!!
์ด๋ฐ ๊ฒฝ์ฐ๋ฅผ ๋์ํ๊ธฐ ์ํด์ ๊ฐ ์์๋ณ๋ก flatMap์ ํตํด ๋ชจ๋ elements๋ฅผ ๊ฐ์ ธ์์ผ ํ๋ค.
3. ์ค์ initializer ๊ตฌ์ถํ๊ธฐ
๋ชจ๋ ์ด๊ฑฐํ ์์๋ฅผ ํ์ํ์ผ๋ฏ๋ก, ์ค์ initializer๋ฅผ ๊ตฌ์ถํด ๋ณด์.
initializer์ ์ ์ธ๋ถ์๋ switch ํํ์์ด ์กด์ฌํ๋ค.
ํด๋น ํํ์์๋, ๊ฐ ์์์ ๋ํ ๋์ ์ผ์ด์ค์ nil์ ๋ฐํํ๋ default๋ฅผ ๊ฐ๊ณ , ์ฐ๋ฆฌ๋ ์ด ๋ชจ๋ ์ผ์ด์ค์ ๋ํ Syntax Node(๊ตฌ๋ฌธ ๋
ธ๋)๋ฅผ ์์ฑํด์ผ ํ๋ค.
๊ทธ๋ฆฌ๊ณ ์ด๋ฐ Syntax Node(๊ตฌ๋ฌธ ๋ ธ๋)๋ฅผ ์์ฑํ๊ธฐ ์ํด์ ์๋์ 2๊ฐ์ง ๋ฐฉ๋ฒ์ด ์๋ค.
- po๋ฅผ ํ์ฉํ Syntax Tree(๊ตฌ๋ฌธ ํธ๋ฆฌ) ์ถ๋ ฅํด์ ์ฐพ๋ ๋ฐฉ๋ฒ
- SwiftSyntax ๋ฌธ์๋ฅผ ์ง์ ์ฝ๊ณ ๋ช ๋ น์ด ์ฐพ๋ ๋ฐฉ๋ฒ
3-1. initializerDeclSyntax
๊ตฌ์ถํ๊ธฐ
์ฐ์ , initializerDeclSyntax๋ฅผ ๋ง๋ค์ด๋ณด๋๋ก ํ์.
initializerDeclSyntax๋ @resultBuilder๋ฅผ ํ์ฉํด์ ๋ณธ๋ฌธ์ ๋น๋ํ๊ณ , header๋ฅผ ์ง์ ํ ์ ์๋ค.
header๋ init ํค์๋์ ๋ชจ๋ ๋งค๊ฐ๋ณ์๋ฅผ ์ง์ ํ๊ณ , ์ด๊ฒ์ @resultBuilder์์ for ๋ฃจํ๋ฅผ ์ฌ์ฉํ์ฌ ํ์ํ ์์๋ง๋ค ์ ๋ถ ๋ฐ๋ณตํ ์ ์๋ค.
๋ณธ๋ฌธ ๋ด๋ถ์๋ switch ํํ์์ด ํ์ํ๋ค.
๋ง์ฐฌ๊ฐ์ง๋ก switch ํํ์์ ์ํ ๊ตฌ๋ฌธ์ธ SwitchExprSyntax ๋ InitializerDeclSyntax์ ๋ง์ฐฌ๊ฐ์ง๋ก header์ @resultBuilder์์ ์ฌ์ฉํ ๋ณธ๋ฌธ ์ฝ๋๋ธ๋ก์ด ํ์ํ๋ค.
๊ทธ๋ฆฌ๊ณ switch์ @resultBuilder๋ธ๋ก์๋ ์์์ ๋ชจ์๋ elements๋ค์ ํ์ฉํ ์ ์๋ค.
์ด๋, ๋์ผ ๊ตฌ๋ฌธ์ ๋ํ ๋ฐ๋ณต์ด ์์ผ๋ for๋ฌธ์ ํ์ฉํ๋ฉด ๋๋ค
๋ํ, case์ ๋ํ ํํ์์ธ SwitchCaseSyntax๋ฅผ ํ์ฉํ๊ณ ๊ฐ ์์์ ๋ํด์ ๋ฌธ์์ด ๋ณด๊ฐ์ ์ฌ์ฉํ์ฌ ํญ๋ชฉ์ ์์ฑํด ์ค๋ค.
๋ฐ๋ณต์ ์ธ case ์ดํ์๋ default์ ํด๋นํ๋ ์์๋ ๋ฐ๋ก for๋ฌธ ๋ฐ์ ์์ฑํด ์ฃผ์.
๊ทธ๋ฆฌ๊ณ ๋ง์ง๋ง์ผ๋ก initializer๋ฅผ DeclSyntax๋ก ๋ณํํ์ฌ return ํด์ค๋ค.
์๋ฆ๋ต๊ฒ ์ฑ๊ณตํ๋ค...!
4. ์ค์ ์ฌ์ฉ์ ์ํด App์ macro Package ์ถ๊ฐํ๊ธฐ
์ด์ ์ค์ App์ธ SampleApp_Ski์ ์ฐ๋ฆฌ๊ฐ ๋ง๋ macro๋ฅผ ์ถ๊ฐํด ๋ณด์.
File >> Add Package Dependencies๋ฅผ ์ ํ
ํ๋จ์ Add Local... ์ ํ ํ, ์์ฑํ WWDC๋ฅผ ์ถ๊ฐํด ์ค๋ค.
์ด์ ์ค์ WWDC ๋ชจ๋์ ๊ฐ์ ธ์์, SlopeSubset macro๋ฅผ EasySlope ํ์ ์ ์ ์ฉํ ์ ์๋ค.
ํ์ง๋ง ์ง๊ธ ์ํ์์ ๋น๋๋ฅผ ํ๋ฉด, ์์ ๊ฐ์ ์๋ฌ๊ฐ ๋ฐ์ํ๋๋ฐ
๋น์ฐํ macro์ ์ํด์ ์์ฑ๋๋ init?()๊ณผ
์ค๋ณต๋๊ธฐ ๋๋ฌธ์, ์ฐ๋ฆฌ๊ฐ ์ง์ ์ ์ํ๋ init?()
์ง์๋ฒ๋ ค์ผ ํ๋ค.
๊ทธ๋ฆฌ๊ณ @SlopeSubset์ ์ฐํด๋ฆญํด์ expand macro๋ฅผ ์ ํํ๋ฉด, ์ปดํ์ผ๋ฌ๋ฅผ ํตํด ํ์ฅ๋ ์ฝ๋๋ฅผ ๋ณผ ์ ์๋ค.
computed-property์ธ var slope: Slope {... } ๋ ๋ง์ฐฌ๊ฐ์ง๋ก macro๋ก ์ ์ํ ์ ์๋ค.
Write macro ์ ๋ฆฌ
- Swift macro package๋ฅผ ์์ฑํด์ template์ ๋ง๋ ๋ค.
- ์ค์ expand ๋ ์ฝ๋๋ฅผ Syntax Tree(๊ตฌ๋ฌธ ํธ๋ฆฌ)๋ก ์์ฑํ๊ธฐ ์ํด, break-point๋ฅผ ์ง์ ๊ฑธ์ด์ ํ์ธํจ
-> ์ด๋ฅผ ํตํด, ์ถ์ถํ element๊น์ง ์ ๊ทผํ๋ Syntax ๊ตฌ์กฐ๋ฅผ ์ ์ ์์ - ํ ์คํธ ์ผ์ด์ค๋ฅผ ํตํด, macro ๊ฒฐ๊ณผ๋ฅผ ์ ํด๋๊ณ ์ค์ expansionํจ์ ๊ตฌํ์ฒด๋ฅผ ๊ฐ๋ฐํ๋ค.
- ๊ตฌํ์ด ์๋ฃ๋ macro๋ฅผ Package์ ํํ๋ก App์ ์ถ๊ฐํ๋ค.
Diagnostics
macro๊ฐ ์ง์ํ์ง ์๋ ๊ธฐ๋ฅ์ macro๋ฅผ ์ฌ์ฉํ๊ฒ ๋๋ค๋ฉด ์ด๋ป๊ฒ ๋ ๊น?
์ด๋ด ๋, ์ฌ์ฉ์๊ฐ ์๋ชป ํ์ฅ๋ ์ฝ๋๋ฅผ ํตํด ๋๋ฒ๊ทธ ํ๋๋ก ํ๋ ๋์ ํญ์ ์ค๋ฅ ๋ฉ์์ง๋ฅผ ๋ด๋ณด๋ด๊ฒ ๋๋ค.
๊ทธ๋ฐ ์๋ฏธ์์ ์์ ์ฝ๋์ ๋จ๊ฒจ๋ TODO๋ฅผ ์ฒ๋ฆฌํ๋๋ก ํ์.
์ด ๊ฒฝ์ฐ๋, @SlopeSubset์ด ์ ์ธ๋ ํ์
์ด enum์ด ์๋ ๊ฒฝ์ฐ์ error๋ฅผ ๋ฐํํ๊ฒ ๋๋ ์ผ์ด์ค์ด๋ค.
final class WWDCTests: XCTestCase {
func testSlopeSubsetOnStruct() {
assertMacroExpansion(
"""
@SlopeSubset
struct Skier {
}
""",
expandedSource:
"""
struct Skier {
}
""",
macros: testMacros
)
}
}
์ด์ ์ ๊ตฌํํ ๋์ ๋ง์ฐฌ๊ฐ์ง๋ก, ์ฐ์ ํ
์คํธ์ผ์ด์ค ๋จผ์ ์ ์ํ๋๋ฐ
์ฐ๋ฆฌ๋ error๋ฅผ ๋ฐ์์ํค๋ ์ผ์ด์ค๋ฅผ ํ
์คํธํด์ผ ํ๊ธฐ ๋๋ฌธ์, struct์ @SlopeSubset์ ์ ์ฉํด ๋ณด์
final class WWDCTests: XCTestCase {
func testSlopeSubsetOnStruct() throws {
assertMacroExpansion(
"""
@SlopeSubset
struct Skier {
}
""",
expandedSource:
"""
struct Skier {
}
""",
diagnostics: [
DiagnosticSpec(message: "@SlopeSubset can only be applied to an enum", line: 1, column: 1)
],
macros: testMacros
)
}
}
์ด๋ ๊ฒ ๋๋ฉด ์ด๊ฑฐํ ์์๊ฐ ์๊ธฐ ๋๋ฌธ์, ์ค์ macro์์๋ initializer๋ฅผ ์์ฑํ์ง ์๊ณ Diagnostics(์ง๋จ)์ ๋ด๋ณด๋ด์ผ ํ๋ค.
-> ์ฌ๊ธฐ์ Diagnostics(์ง๋จ) ์ด๋, 'SlopeSubset์ ์ด๊ฑฐํ์๋ง ์ ์ฉ๋ ์ ์์ต๋๋ค'๋ผ๊ณ ๋ฉ์์ง๋ฅผ ๋จ๊ธฐ๋ ๊ฒ!
```swift
final class WWDCTests: XCTestCase {
func testSlopeSubsetOnStruct() throws {
assertMacroExpansion( // ERROR: failed - Expected 1 diagnostics but received 0:
"""
@SlopeSubset
struct Skier {
}
""",
expandedSource:
"""
struct Skier {
}
""",
diagnostics: [
DiagnosticSpec(message: "@SlopeSubset can only be applied to an enum", line: 1, column: 1)
],
macros: testMacros
)
}
}
์ด ์ํ์์ ํ ์คํธ๋ฅผ ์งํํ๋ฉด ์คํจํ๊ฒ ๋๊ณ , ์์ง ์ง๋จ ๋ฉ์์ง๋ ์ถ๋ ฅ๋์ง ์๋๋ค.
macro error ์ถ๋ ฅํ๊ธฐ
macro์ ๋ํ error๋ Swift Error ํ๋กํ ์ฝ์ ์ค์ํ๋ ๋ชจ๋ ํ์ ์ผ๋ก ํํ ๊ฐ๋ฅํ๋ค.
enum SlopeSubsetError: CustomStringConvertible, Error {
case onlyApplicableToEnum
var description: String {
switch self {
case .onlyApplicableToEnum: return "@SlopeSubset can only be applied to an enum"
}
}
}
/// Implementation of the `SlopeSubset` macro.
public struct SlopeSubsetMacro: MemberMacro {
public static func expansion(
of node: AttributeSyntax,
providingMembersOf declaration: some DeclGroupSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
guard let enumDecl = declaration.as(EnumDeclSyntax.self) else {
throw SlopeSubsetError.onlyApplicableToEnum
}
...
}
}
์ด๋ ๊ฒ expansion๋ด๋ถ์์ error๋ฅผ throw ํ๋ฉด, ์ค์ macro๋ฅผ ์ ์ฉํ๋ attribute์์ ํ์๋๋ค.
+ ์ถ๊ฐ์ ์ผ๋ก ์ข ๋ ๋ง์ ์ผ์ด์ค์ ๋ํ error ๋์์ ํ๋ ค๋ฉด, addDiagnostic ๋ฉ์๋๋ฅผ ํ์ฉํ๋ฉด ๋๋ค.
๊ทธ๋ฆฌ๊ณ ์ค์ ๋ก @SlopeSubset๋ฅผ ๊ตฌ์กฐ์ฒด์ ์ ์ฉํ๋ฉด ์์ ๊ฐ์ด error ๋ฉ์์ง๊ฐ ์ ๋์ค๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
์์ฑ๋ macro์ ์ ๋ค๋ฆญ ํ์ ์ถ๊ฐ -> macro์ ์ผ๋ฐํ
ํด๋น macro๊ฐ Slope๋ผ๋ ๊ณ ์ ์ ์ธ ์ด๊ฑฐํ ํ์ ๋ฟ ์๋๋ผ, ์ผ๋ฐ์ ์ธ ์ด๊ฑฐํ ํ์ ์๋ ์ ์ฉ๋๋ฉด ๋ง์ ๊ฐ๋ฐ์๋ค์ด ํจ๊ณผ์ ์ผ๋ก ์ฌ์ฉํ ์ ์์ ๊ฒ์ด๋ค.
์ด๋ฅผ ์ํด์ , ์ ๋ค๋ฆญ ๋งค๊ฐ๋ณ์๋ฅผ macro์ ์ ์ธ์ ์ถ๊ฐํด ์ฃผ๋ฉด ๋๋ค.
๊ทธ๋ฆฌ๊ณ , SlopeSubset์ด๋ผ๋ ์ด๋ฆ๋ EnumSubset์ผ๋ก ๋ณ๊ฒฝํ๋๋ก ํ์
func testSlopeSubset() {
assertMacroExpansion(
"""
@EnumSlope<Slope>
enum EasySlope {
case beginnersParadice
case practiceRun
}
""",
...
)
// > **(lldb) po node**
// >AttributeSyntax
// >โโatSign: atSign
// >โฐโattributeName: IdentifierTypeSyntax
// >โโname: identifier("EnumSlope")
// >โฐโgenericArgumentClause: GenericArgumentClauseSyntax
// > โโleftAngle: leftAngle
// > โโarguments: GenericArgumentListSyntax
// > โ โฐโ[0]: GenericArgumentSyntax
// > โ โฐโargument: IdentifierTypeSyntax
// > โ โฐโname: identifier("Slope")
// > โฐโrightAngle: rightAngle
๋ํ, macro์ ๊ตฌํ์ฒด์์ ํ์ฌ๋ Slope๋ผ๋ ๊ณ ์ ํ์
๋์ ์ ์ ๋ค๋ฆญ ๋งค๊ฐ๋ณ์๋ฅผ ํ์ํด์ผ ํ๋ค.
์ด์ ๊ณผ ๋ง์ฐฌ๊ฐ์ง๋ก break-point๋ฅผ ํ์ฉํด์ node์ ๋ํ Syntax Tree(๊ตฌ๋ฌธ ํธ๋ฆฌ)๋ฅผ ์ถ๋ ฅํ์ฌ ๋ถ์ํ๋ค.
guard let superSetType = node
.attributeName.as(IdentifierTypeSyntax.self)?
.genericArgumentClause?
.arguments.first?
.argument else {
// TODO: Handle error
return []
}
// > (11db) po superSetType
// > IdentifierTypeSyntax
// > โฐโname: identifier("Slope")
์ด๋ฅผ ์ค์ ์ฝ๋๋ก ์ถ์ถํ๋ฉด ์์ ๊ฐ๋ค.
guard let superSetType = node
.attributeName.as(IdentifierTypeSyntax.self)?
.genericArgumentClause?
.arguments.first?
.argument else {
// TODO: Handle error
return []
}
...
let initializer = try InitializerDeclSyntax("init?(_ slope: \(superSetType))") { ... }
๊ทธ๋ผ ๊ธฐ์กด์ let initializer๋ฅผ ์์ ์ฝ๋์ฒ๋ผ ์์ ํ ์ ์๋ค.
์ดํ ํ ์คํธ๋ฅผ ์งํํ๋ฉด, ๋ชจ๋ ์ ํต๊ณผํ๋ค!
Summary
- macro Package Template๋ฅผ ํตํด ์์ฝ๊ฒ ์์ํ ์ ์๋ค.
- ๋งคํฌ๋ก๋ฅผ ๊ฐ๋ฐํ๋ ๋์ ํ ์คํธ ์ผ์ด์ค๋ฅผ ๊ผญ ์์ฑํด์ ๋งคํฌ๋ก๊ฐ ์์ฑํ๋ ์ฝ๋๊ฐ ์ ํจํ์ง ํ์ธํด์ผ ํ๋ค.
- ์งํํ๋ฉด์ expansion ํจ์์ ์ค๋จ์ ์ ์ค์ ํ๊ณ ๋๋ฒ๊ฑฐ์์ Syntax Tree(๊ตฌ๋ฌธ ํธ๋ฆฌ)๋ฅผ ์ถ๋ ฅํด Syntax Tree(๊ตฌ๋ฌธ ํธ๋ฆฌ)๋ฅผ ๋ถ์ํ ์ ์๋ค.
- ํน์ ์ํฉ์์ ๋งคํฌ๋ก๋ฅผ ์ ์ฉํ ์ ์๋ค๋ฉด ์ปค์คํ ์ค๋ฅ ๋ฉ์์ง๋ฅผ ํญ์ ๋ด๋ณด๋ด์ผ ํฉ๋๋ค
์ฐธ๊ณ ์๋ฃ:
https://medium.com/@gar.hovsepyan/macros-in-swift-a-practical-guide-to-using-fa1a24eba8bb
Macros in Swift: A Practical Guide to Using
I recently encountered a task that required writing a lot of boilerplate code. Remembering that Swift 5.9 introduced macros specifically…
medium.com
https://developer.apple.com/videos/play/wwdc2023/10166/
Write Swift macros - WWDC23 - Videos - Apple Developer
Discover how you can use Swift macros to make your codebase more expressive and easier to read. Code along as we explore how macros can...
developer.apple.com
'๐ > Swift' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Swift] associatedtype๊ฐ ๋ฌด์์ด๋? (0) | 2023.03.07 |
---|---|
[Swift] @discardableResult ๋? (0) | 2023.03.06 |
[Swift] final ํค์๋๋ฅผ ์ฌ์ฉํ๋ ์ด์ ๋?? (0) | 2023.02.08 |
[Swift] lazy ํค์๋๋? (2) | 2023.01.30 |