Skip to content

Commit 3bac004

Browse files
committed
resolve review comments
1 parent 0b0e3ed commit 3bac004

File tree

1 file changed

+19
-19
lines changed

1 file changed

+19
-19
lines changed

swift-6-beta.docc/LanguageGuide/Macros.md

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,23 @@
44

55
宏会在编译你的源代码时对其进行转换,从而让你避免手动编写重复的代码。在编译过程中,Swift 会先展开代码中的所有宏,然后再像往常一样构建代码。
66

7-
![一个显示宏展开概貌的图表。左侧是 Swift 代码的风格化表示。右侧是由宏添加了几行的相同的代码。](macro-expansion)
7+
![一个宏展开示意图。左侧是 Swift 代码的风格化表示。右侧是由宏添加了几行的相同的代码。](macro-expansion)
88

99
宏展开始终是一种加法操作:宏会添加新代码,但绝不会删除或修改现有代码。
1010

11-
每个宏的输入和宏展开的输出都会被检查,以确保它们是语法上有效的 Swift 代码。同样,你传给宏的值以及宏生成的代码中的值也会被检查,以确保它们具有正确的类型。此外,如果宏的实现在展开宏时遇到错误,编译器会将其视为编译错误。这些保证让使用了宏的代码更容易被推导,也更容易发现诸如宏使用不当或宏实现中的错误这样的问题
11+
每个宏的输入和宏展开的输出都会被检查,以确保它们是语法上有效的 Swift 代码。同样,你传给宏的值以及宏生成的代码中的值也会被检查,以确保它们具有正确的类型。此外,如果宏的实现在展开宏时遇到错误,编译器会将其视为编译错误。这些保证让使用了宏的代码更容易被推导,也让人更容易发现诸如宏使用不当或宏实现有错误这样的问题
1212

1313
Swift 有两种宏:
1414

15-
- *独立宏*可单独出现,无需被附加到任何声明中。
15+
- *独立宏(Freestanding macros)*可单独出现,无需被附加到任何声明中。
1616

17-
- *附加宏*会修改它被附加到的声明。
17+
- *附加宏(Attached macros)*会修改它被附加到的声明。
1818

1919
附加宏和独立宏的调用方式略有不同,但它们都遵循相同的宏展开模型,并都使用相同的方法来实现。下面的章节将更详细地描述这两种宏。
2020

2121
## 独立宏
2222

23-
要调用独立宏,需要在其名称前写入数字符号 (`#`),并在名称后的括号中写入宏的参数。例如:
23+
要调用独立宏,需要在其名称前写入井号 (`#`),并在名称后的括号中写入宏的参数。例如:
2424

2525
```swift
2626
func myFunction() {
@@ -54,7 +54,7 @@ struct SundaeToppings: OptionSet {
5454
}
5555
```
5656

57-
在这段代码中,`SundaeToppings` 选项集中的每个选项都包括对初始化器的调用,这是重复的手动操作。这样的实现方式在添加新选项时容易出错,比如在行尾键入错误的数字。
57+
在这段代码中,`SundaeToppings` 选项集中的每个选项都包括对构造器的调用,这是重复的手动操作。这样的实现方式在添加新选项时容易出错,比如在行尾键入错误的数字。
5858

5959
下面是该代码使用宏后的替代版本:
6060

@@ -69,7 +69,7 @@ struct SundaeToppings {
6969
}
7070
```
7171

72-
此版本的 `SundaeToppings` 调用了 `@OptionSet` 宏。这个宏会读取私有枚举类中的 `case` 列表,生成每个选项的常量列表,并增加对 [`OptionSet`][] 协议的遵循。
72+
此版本的 `SundaeToppings` 调用了 `@OptionSet` 宏。这个宏会读取私有枚举类中的枚举值列表,并为其中的每个值生成常量列表,同时也会为结构体增加对 [`OptionSet`][] 协议的遵循。
7373

7474
[`OptionSet`]: https://developer.apple.com/documentation/swift/optionset
7575

@@ -81,7 +81,7 @@ struct SundaeToppings {
8181
-->
8282

8383

84-
作为对比,下面是 `@OptionSet` 宏展开后的版本的样子。这段代码不是由你自己编写的,只有当你特别要求 Swift 展示宏的展开时,你才会看到它。
84+
作为对比,`@OptionSet` 宏展开后是下面这样。这段代码不是由你自己编写的,只有当你特别要求 Swift 展示宏的展开时,你才会看到它。
8585

8686
```swift
8787
struct SundaeToppings {
@@ -102,7 +102,7 @@ struct SundaeToppings {
102102
extension SundaeToppings: OptionSet { }
103103
```
104104

105-
私有枚举类之后的所有代码都来自于 `@OptionSet` 宏。使用宏生成所有静态变量的 `SundaeToppings` 版本比前面手动编码的版本更易于阅读和维护。
105+
这个结构体中的私有枚举类之后的所有代码都来自于 `@OptionSet` 宏。使用宏生成所有静态变量的 `SundaeToppings` 版本比前面手动编码的版本更易于阅读和维护。
106106

107107
## 宏的声明
108108

@@ -123,7 +123,7 @@ public macro OptionSet<RawType>() =
123123

124124
> 注意:
125125
> 宏的可访问性总是被声明为 `public` 的。
126-
> 由于声明宏的代码与使用宏的代码位于不同的模块中,因此没有任何地方可以应用一个非公共可访问的宏
126+
> 由于声明宏的代码与使用宏的代码位于不同的模块中,因此没有任何地方可以应用一个非公开可访问的宏
127127
128128
宏的声明定义了宏的*角色* —— 包括宏在源代码中可以被调用的位置以及宏可以生成的代码种类。每个宏都有一个或多个角色,作为属性的一部分写在宏声明的开头。下面是 `@OptionSet` 的更完整的声明,包括了指定它的角色的属性:
129129

@@ -134,7 +134,7 @@ public macro OptionSet<RawType>() =
134134
#externalMacro(module: "SwiftMacros", type: "OptionSetMacro")
135135
```
136136

137-
`@attached` 属性在此声明中出现了两次,每个宏角色各用了一次。第一次使用时,`@attached(member)` 表示这个宏会向被作用到的类型添加新的成员。按 `OptionSet` 协议以及一些附加成员的要求,`@OptionSet` 宏添加了一个 `init(rawValue:)` 初始化器。第二次使用时,`@attached(extension, conformances: OptionSet)` 声明了 `@OptionSet` 会添加对 `OptionSet` 协议的遵循。`@OptionSet` 宏会扩展被作用到的类型,使其遵循 `OptionSet` 协议。
137+
`@attached` 属性在此声明中出现了两次,每个宏角色各用了一次。第一次使用时,`@attached(member)` 表示这个宏会向被作用到的类型添加新的成员。按 `OptionSet` 协议以及一些附加成员的要求,`@OptionSet` 宏添加了一个 `init(rawValue:)` 构造器。第二次使用时,`@attached(extension, conformances: OptionSet)` 声明了 `@OptionSet` 会添加对 `OptionSet` 协议的遵循。`@OptionSet` 宏会扩展被作用到的类型,使其遵循 `OptionSet` 协议。
138138

139139
对于独立宏,你可以编写 `@freestanding` 属性来指定其角色:
140140

@@ -195,33 +195,33 @@ let magicNumber = #fourCharacterCode("ABCD")
195195

196196
为了展开上述代码中的宏,编译器读取 Swift 文件并创建该代码的内存表示,也就是*抽象语法树*(AST)。AST 使得代码的结构变得清晰,也使得编写与该结构进行交互的代码变得更容易 —— 例如编写编译器或宏的实现,都需要与 AST 进行交互。以下是上述代码的 AST 表示,略微简化,省略了一些额外的细节:
197197

198-
![一个树状图,以常量作为根结点。该常量有一个名为 magicNumber 的名称和一个值。该常量的值是一个宏调用。这个宏调用有一个名为 fourCharacterCode 的名称和它的参数。参数是一个值为 ABCD 的字符串字面量。](macro-ast-original)
198+
![一个树状图,以常量作为根节点。该常量有一个名为 magicNumber 的名称和一个值。该常量的值是一个宏调用。这个宏调用有一个名为 fourCharacterCode 的名称和它的参数。参数是一个值为 ABCD 的字符串字面量。](macro-ast-original)
199199

200-
上面的图展示了该代码的结构是如何在内存中表示的。AST 中的每个结点对应源代码的一部分。AST 的 “Constant declaration(常量声明)”结点下有两个子结点,分别表示常量声明的两个部分:它的名称和它的值。“Macro call(宏调用)”结点则有表示宏的名称和传递给宏的参数列表的子结点
200+
上面的图展示了该代码的结构是如何在内存中表示的。AST 中的每个节点对应源代码的一部分。AST 的 “Constant declaration(常量声明)”节点下有两个子节点,分别表示常量声明的两个部分:它的名称和它的值。“Macro call(宏调用)”节点则有表示宏的名称和传递给宏的参数列表的子节点
201201

202202
作为构建这个 AST 的一部分,编译器会检查源代码是否是有效的 Swift 代码。例如,`#fourCharacterCode` 只接受一个参数,且该参数必须是一个字符串。如果你尝试传递一个整数参数,或者在字符串字面量的末尾忘记了引号 (`"`),你会在这个过程中的这个点上获得一个错误。
203203

204204
编译器会找到代码中调用宏的地方,并加载实现这些宏的外部二进制文件。对于每个宏调用,编译器将抽象语法树(AST)的一部分传递给该宏的实现。以下是这个部分 AST 的表示:
205205

206-
![一个树状图,以一个宏调用(Macro call)作为根结点。这个宏调用有一个名为 fourCharacterCode 的名称和参数。这个参数是一个值为 ABCD 的字符串字面量。](macro-ast-input)
206+
![一个树状图,以一个宏调用(Macro call)作为根节点。这个宏调用有一个名为 fourCharacterCode 的名称和参数。这个参数是一个值为 ABCD 的字符串字面量。](macro-ast-input)
207207

208208
`#fourCharacterCode` 宏的实现会在展开这个宏时读取这个部分 AST 作为输入。宏的实现仅对其接收到的部分 AST 进行操作,这意味着无论这个宏的前后代码是什么,它的展开方式始终不变。这一限制有助于使宏展开更易于理解,并帮助你的代码能更快得到构建,因为 Swift 可以不必展开那些未变更过的宏。
209209
<!-- TODO TR: Confirm -->
210210
Swift 能通过限制实现宏的代码,帮助宏的作者避免意外读取其他输入:
211211

212-
- 传递给宏实现的抽象语法树(AST)仅包含表示该宏的 AST 结点,而不包括其前后的任何代码。
212+
- 传递给宏实现的抽象语法树(AST)仅包含表示该宏的 AST 节点,而不包括其前后的任何代码。
213213

214-
- 宏的实现运行在一个沙箱环境中,这可以防止其访问文件系统或网络。
214+
- 宏的实现运行在一个沙盒环境中,这可以防止其访问文件系统或网络。
215215

216216
除了这些保护措施,宏的作者有责任不读取或修改宏输入以外的任何内容。例如,宏的展开不得依赖于当前的时间。
217217

218218
`#fourCharacterCode` 的实现会生成了一个包含展开后代码的新 AST。以下是上述代码会返回给编译器的内容:
219219

220220
![一个具有 UInt32 类型的整型字面量 1145258561 的树形图。](macro-ast-output)
221221

222-
当编译器接收到这个展开结果时,它用包含了这个宏展开结果的 AST 结点替换掉包含了宏调用的 AST 结点。在宏展开后,编译器会再次检查以确保程序仍然是语法上有效的 Swift 代码,并且所有的类型都是正确的。这会生成一个可以像往常一样编译的最终 AST:
222+
当编译器接收到这个展开结果时,它用包含了这个宏展开结果的 AST 节点替换掉包含了宏调用的 AST 节点。在宏展开后,编译器会再次检查以确保程序仍然是语法上有效的 Swift 代码,并且所有的类型都是正确的。这会生成一个可以像往常一样编译的最终 AST:
223223

224-
![一个树状图,以常量作为根结点。该常量有一个名为 magicNumber 的名称和一个值。该常量的值是 UInt32 类型的整型字面量 1145258561。](macro-ast-result)
224+
![一个树状图,以常量作为根节点。该常量有一个名为 magicNumber 的名称和一个值。该常量的值是 UInt32 类型的整型字面量 1145258561。](macro-ast-result)
225225

226226
这个 AST 对应于如下的 Swift 代码:
227227

@@ -368,7 +368,7 @@ struct MyProjectMacros: CompilerPlugin {
368368

369369
要展开 `#fourCharacterCode` 宏,Swift 会将使用了此宏的代码的 AST 发送给包含该宏的实现的库。在这个库的内部,Swift 会调用 `FourCharacterCode.expansion(of:in:)` 方法,并将 AST 和上下文作为参数传递给该方法。`expansion(of:in:)` 的实现会找到作为参数传递给 `#fourCharacterCode` 的字符串,并计算出相对应的 32 位无符号整型字面量的值。
370370

371-
在上面的示例中,第一个 `guard` 块从 AST 中提取出字符串字面量,并将该 AST 结点赋值给 `literalSegment`。第二个 `guard` 块调用私有 `fourCharacterCode(for:)` 函数。如果宏使用不当,这两个代码块都可能会抛出错误 —— 错误信息会在被不当调用的位置作为编译器错误抛出。例如,如果你尝试以 `#fourCharacterCode("AB" + "CD")` 的方式来调用该宏,编译器会显示错误信息 "Need a static string"(“需要一个静态字符串”)。
371+
在上面的示例中,第一个 `guard` 块从 AST 中提取出字符串字面量,并将该 AST 节点赋值给 `literalSegment`。第二个 `guard` 块调用私有 `fourCharacterCode(for:)` 函数。如果宏使用不当,这两个代码块都可能会抛出错误 —— 错误信息会在被不当调用的位置作为编译器错误抛出。例如,如果你尝试以 `#fourCharacterCode("AB" + "CD")` 的方式来调用该宏,编译器会显示错误信息 "Need a static string"(“需要一个静态字符串”)。
372372

373373
`expansion(of:in:)` 方法返回了一个 `ExprSyntax` 的实例,`ExprSyntax` 是 SwiftSyntax 中的一种用于表示 AST 中的表达式的类型。由于此类型遵循 `StringLiteralConvertible` 协议,作为一种轻量级的语法,这个宏的实现就使用了一个简单字符串字面量来创建其结果。所有从宏实现中返回的 SwiftSyntax 类型都遵循 `StringLiteralConvertible` 协议,因此你也可以在实现任何宏时使用这种方法。
374374

0 commit comments

Comments
 (0)