Swift 的词汇结构描述了怎样的语言字符序列才能形成有效标记。这些有效标记来自底层的语言构建块,用来描述随后章节里的其它语言。一个标记由标识符,关键字,标点符号,文字或者运算符组成。
在大部分情况下,标记是通过输入文本中最长可能输入的子句来从 Swift 源文件的特征中生成。这种行为参考自最长匹配或者最大匹配。
空白和注释
空白有两个用处:在源文件中分隔标记以及帮助决定运算符是前缀还是后缀(参见运算符),但是在其它情况下会被忽略。以下的字符会被认为是空白:空格(U+2000)、换行符(U+1000A)、回车(U+00D)、水平制表符(U+0009)、垂直制表符(U+000B)、换页(U+000C)和空白(U+0000)。
在编译器里,注释会被当作是空白。单行注释以 // 开始然后继续直到遇到换行符 (U+000A)或者回车 (U+000D)。多行注释以 /* 开始以 */ 结尾。内嵌多行注释是可以的,但是注释标签必须相对应。
如同标记格式参考中描述的那样,注释能够包含额外的格式和标记。
标识符
标识符以A到Z的大写或者小写字母,下划线( _ ),基本多语种平面中非组合字母数字的 Unicode 字符,或者基本多语种平面以外的非 Private 使用区域的字符开头。首位字符之后,允许使用数字和组合的 Unicode 字符。
以下划线开头的标识符会被当作内部处理,就算声明时写了 public 访问权限修饰符也是如此。这个规则允许框架作者标记 API 的某些部分不允许客户交互或是依赖,尽管美协限制需要它们声明为公开。额外的,以两个下划线开头的标识符保留给 Swift 编译器和标准库使用。
如果使用保留字作为标识符,在它前后各放一个反引号( ` )。举个栗子, class 不是一个合法的标识符,但是 `class` 是有效的,部分字符不考虑反引号, `x` 和 x 是一个意思。
在一个没有显式形式参数名的闭包里,参数会隐式地命名为 $0 , $1 , $2 等等。这些参数名在闭包的范围内是有效的。
编译器会给有包装映射的属性合成一个以美元符号( $ )开头的标识符。你的代码可以与这些标识符交互,但你不能以这个前缀声明标识符。更多信息见特性章节的属性包装小节。
标识符的语法
identifier → identifier-head identifier-characters opt
identifier → ` identifier-head identifier-characters opt `
identifier → implicit-parameter-name
identifier → property-wrapper-projection
identifier-list → identifier | identifier , identifier-listidentifier-head → Upper- or lowercase letter A through Z
identifier-head → _
identifier-head → U+00A8, U+00AA, U+00AD, U+00AF, U+00B2–U+00B5, or U+00B7–U+00BA
identifier-head → U+00BC–U+00BE, U+00C0–U+00D6, U+00D8–U+00F6, or U+00F8–U+00FF
identifier-head → U+0100–U+02FF, U+0370–U+167F, U+1681–U+180D, or U+180F–U+1DBF
identifier-head → U+1E00–U+1FFF
identifier-head → U+200B–U+200D, U+202A–U+202E, U+203F–U+2040, U+2054, or U+2060–U+206F
identifier-head → U+2070–U+20CF, U+2100–U+218F, U+2460–U+24FF, or U+2776–U+2793
identifier-head → U+2C00–U+2DFF or U+2E80–U+2FFF
identifier-head → U+3004–U+3007, U+3021–U+302F, U+3031–U+303F, or U+3040–U+D7FF
identifier-head → U+F900–U+FD3D, U+FD40–U+FDCF, U+FDF0–U+FE1F, or U+FE30–U+FE44
identifier-head → U+FE47–U+FFFD
identifier-head → U+10000–U+1FFFD, U+20000–U+2FFFD, U+30000–U+3FFFD, or U+40000–U+4FFFD
identifier-head → U+50000–U+5FFFD, U+60000–U+6FFFD, U+70000–U+7FFFD, or U+80000–U+8FFFD
identifier-head → U+90000–U+9FFFD, U+A0000–U+AFFFD, U+B0000–U+BFFFD, or U+C0000–U+CFFFD
identifier-head → U+D0000–U+DFFFD or U+E0000–U+EFFFD
identifier-character → Digit 0 through 9
identifier-character → U+0300–U+036F, U+1DC0–U+1DFF, U+20D0–U+20FF, or U+FE20–U+FE2F
identifier-character → identifier-head
identifier-characters → identifier-character identifier-characters opt
implicit-parameter-name → $ decimal-digits
property-wrapper-projection → $ identifier-characters
关键字和标点符号
接下来的关键字被预留,不能被用作标识符,除非它们像上一节标识符中描述的那样使用反引号( ` )。
- 用在声明中的关键字: associatedtype 、 class 、 deinit 、 enum 、 extension 、 func 、 import 、 init 、 inout 、 internal 、 let 、 operator 、 private 、 protocol 、 public 、 static 、 struct 、 subscript 、 typealias 和 var 。
- 用在语句中的关键字: break 、 case 、 continue 、 default 、 defer 、 do 、 else 、 fallthrough 、 for 、 guard 、 if 、 in 、 repeat 、 return 、 switch 、 where 和 while 。
- 用在表达式和类型中的关键字: as 、 catch 、 dynamicType 、 false 、 is 、 nil , rethrows 、 super 、 self 、 Self 、 throw 、 throws 、 true 和 try 。
- 用在模式中的关键字: _ 。
- 起始于数字标记( # )的关键字: #available 、 #column 、 #else 、 #elseif 、 #endif 、 #file 、 #function 、 #if 、 #line 、 #selector 和 #sourceLocation 。
- 特定上下文中被保留的关键字: associativity 、 convenience 、 dynamic 、 didSet 、 final 、 get 、 infix 、 indirect 、 lazy 、 left 、 mutating 、 none 、 nonmutating 、 optional 、 override 、 postfix 、 precedence 、 prefix 、 Protocol 、 required 、 right 、 set 、 Type 、 unowned 、 weak 和 willSet 。这些关键字在特定上下文语法之外可以被用于标识符。
以下标记被当作保留符号,不能用于自定义操作符: ( 、 ) 、 { 、 } 、 [ 、 ] 、 . 、 , 、 : 、 ; 、 = 、 @ 、 # 、 & (作为前缀操作符)、 -> 、 ` 、 ? 和 ! (作为后缀操作符)。
字面量
字面量是源代码对某类型值的表达,比如数字或字符串。
下面是一些字面量的示例:
1 2 3 4 |
42 // Integer literal 3.14159 // Floating-point literal "Hello, world!" // String literal true // Boolean literal |
字面量本身没有类型。相反的,字面量被解析成无限精度并且 Swift 的类型推断会试图推断出字面量的类型。举个栗子,在声明 let x: Int8 = 42 里,Swfit 使用显式类型标注( :Int8 )来推断整型字面量 42 是 Int8 。如果没有合适的类型信息,Swift 将推断这个字面量的类型是 Swift 标准库中定义的默认的字面量类型。整型字面量的默认类型是 Int ,浮点字面量的默认类型是 Double ,字符串字面量的默认类型是 String ,布尔字面量的默认类型是 Bool 。比如说,在声明 let str = "Hello,world" 中,这个默认推断的字符串字面量 "Hello, world" 是 String 。
当给字面量指定了类型标注的时候,注释的类型必须是可以从这个字面量值实例化的类型。也就是说,类型必须符合下列 Swift 标准库协议之一:整型字面量的 IntegerLiteralConvertible ,浮点字面量的 FloatingPointLiteralConvertible ,字符串字面量的 StringLiteralCovertible ,布尔字面量 的 BooleanLiteralConvertible 。举个栗子, Int8 遵循 IntergerLiteralConvertible 协议,因此它可以在声明 let x: Int8 = 42 里作为整数字面量42的类型标注。
字面量的语法
字面量 → 数值字面量 | 字符串字面量 | 布尔字面量 | 空值字面量
数值字面量 → –可选整型字面量 | –可选浮点字面量
布尔字面量 → true | false
空值字面量 → nil
整数字面量
整型字面量表示未指明精度的整型值。整型字面量默认用十进制表达;你可以用特定的前缀来指定其它进制。二进制用 0b 开头,八进制用 0o 开头,十六进制用 0x 开头。
十进制字面量包含数字 0 到 9 ,二进制包含 0 和 1 ,八进制包含 0 到 7 ,十六进制包含 0 到 9 和大小写的 A 到 F 。
负数字面量在其前面加一个减号( - ),比如 -42 。
为了增加可读性,可以在数字之间增加下划线( _ ),但是它们会被忽略掉,因此不会对字面量的值有任何影响。整型字面量可以在数字之前加零( 0 ),同样也不会影响字面量值。
除非另有说明,整型字面量默认自动推断为 Swift 标准库里的 Int 。如同整型中描述的那样,Swift 标准库同样定义了不同大小的有符号以及无符号整型。
整型字面量语法
整型字面量 —> 二进制字面量
整型字面量 —> 八进制字面量
整型字面量 —> 十进制字面量
整型字面量 —> 十六进制字面量
二进制字面量→ 0b 二进制数字 二进制字面量字符组可选
二进制数字 → 数值 0 到 1
二进制字面量字符 → 二进制数字 | _
二进制字面量字符组 → 二进制字面量字符 二进制字面量字符组可选八进制字面量 → 0o 八进字数字 八进制字符列表可选
八进字数字 → 数值 0 到 7
八进制字符 → 八进字数字 | _
八进制字符组 → 八进制字符 八进制字符列表可选
十进制字面量 → 十进制数字 十进制字符组可选
十进制数字 → 数值 0 到 9
十进制数字列表 → 十进制数字 十进制数字列表可选
十进制字符 → 十进制数字 | _十进制字符列表 → 十进制字符 十进制字符列表可选
十六进制字面量 → 0x 十六进制数字 十六进制字面量字符列表可选
十六进制数字 → 数值 0 到 9, 字母 a 到 f, 或 A 到 F
十六进制字符 → 十六进制数字 | _
十六进制字面量字符列表 → 十六进制字符 十六进制字面量字符列表可选
浮点数字面量
浮点数字面量表示未指明精度的浮点值。
默认来讲,浮点数字面量用十进制表达(无需前缀);你同样可以用十六进制表达(使用 0x 前缀)。
十进制的浮点字面量由一串十进制数字后加要么十进制分数要么十进制指数,或两者兼而有之。十进制分数由一个小数点( . )后跟一串十进制数组成。指数由一个大写或者小写字母 e 为前缀后跟十进制数字串组成,它表示 e 之前的数量乘以10的几次方。例如, 1.25e2 ,表示1.25×102,也就是 125.0。同样的, 1.25e-2 表示1.25×10–2,它等于 0.0125 。
十六进制的浮点数字面量由 0x 前缀,后跟一个可选的十六进制分数,一个十六进制指数。十六进制分数由一个小数点( . )后跟一个十六进制小数组成。指数部分由一个大写或小写的字母 p 为前缀,后跟十进制数字串组成,它表示 p 之前的数量乘以 2 的几次方。例如, 0xFp2 表示15 x 22,结果为 60 。同理, 0xFp 表示 15 x 2-2,结果为 3.75 。
负数浮点字面量在浮点数字面量前面加一个减号( - ),如 -42.5 。
为了增加可读性,可以在数字之间增加下划线( _ ),但是它们会被忽略因此影响字面量的值。浮点数字面量可以在数字之前加零( 0 ),同样的也不会影响字面量值。
除非另有说明,浮点数字面量自动推断为 Swift 标准库里的 Double,它表示64位浮点数。Swift 标准库同样定义了一个 Float 类型,它表示32位浮点数。
浮点型字面量语法
浮点型字面量语法
浮点数字面量 → 十进制字面量 十进制分数可选十进制指数可选
浮点数字面量 → 十六进制字面量 十六进制分数可选十六进制指数
十进制分数 → . 十进制字面量
十进制指数 → 浮点数e 符号可选十进制字面量
十六进制分数 → . 十六进制数字 十六进制字面量字符列表可选
十六进制指数 → 浮点数p 符号可选十进制字面量
浮点数 e → e | E
浮点数 p → p | P
正负号 → + | –
字符串字面量
字符串字面量是被双引号包围的一串字符,使用如下形式:
"characters"
字符串字面量不能包含非转义双引号( " ),非转义反斜杠( \ ),回车,或者换行。
多行字符串使用三个双引号( """ )包围,有如下形式:
1 2 3 |
""" characters """ |
与单行字符串字面量不同的是,多行字符串字面量可以包含不转义的双引号( " ),回车以及换行。它不能包含三个非转义的连续双引号。
""" 之后的回车或者换行开始多行字符串字面量,不是字符串的一部分。 """ 之前回车或者换行结束字面量,也不是字符串的一部分。要让多行字符串字面量的开始或结束带有换行,就在第一行或者最后一行写一个空行。
多行字符串字面量可以使用任何空格或制表符组合进行缩进;这些缩进不会包含在字符串中。 """ 的结束符号决定了缩进:字面量中的任何一个非空行必须起始于多行字符串字面量结束符号的前面;空格和制表符不会被转换。你可以包在缩进后含额外的空格和制表符;这些空格和制表符会在字符串中出现。
多行字符串字面量中的一行结束使用规范化的换行符号。尽管你的源代码混用了回车和换行符,字符串中所有的行结束都必须一样。
特殊字符可以用以下转义序列包含在单行字符串字面量以及多行字符串字面量形式中:
- 空字符( \0 )
- 反斜杠( \\ )
- 水平制表符( \t )
- 换行( \n )
- 回车( \r )
- 双引号( \" )
- 单引号( \' )
- Unicode标量( \u{ n } ),n为一到八的十六进制数字。
通过把表达式放在反斜杠( \ )后的圆括号里,表达式的值可以插入字符串字面量。插值表达式可以包含字符串字面量,但是不能包含非转义的反斜杠(\)、回车或者换行。
比如说,下面的字符串字面量都有着相同的值:
1 2 3 4 5 |
"1 2 3" "1 2 \("3")" "1 2 \(3)" "1 2 \(1 + 2)" let x = 3; "1 2 \(x)" |
默认推断的字符串字面量类型是 String 。关于 String 类型的更多信息,可以看字符串和字符,字符串结构参考(英文)。
字符串字面量可通过 + 运算符在编译时串联。例如,在下面的例子中 textA 的和 textB 的值是完全一样的—没有任何的运行时串联操作执行。
1 2 |
let textA = "Hello " + "world" let textB = "Hello world" |
字符串字面量语法
字符串字面量 → 静态字符串字面量 | 以内插值替换的字符串字面量
静态字符串字面量 → “引用文字可选”
静态字符串字面量 →"""
多行引用文字可选"""
引用字面量 → 引用文字条目 引用文字可选
引用字面量条目 → 转义字符
引用字面量条目 → 除了”, \, U+000A、或者 U+000D的所有Unicode的字符以内插值替换的字符串字面量 → “以内插值替换的文字可选”
以内插值替换的字符串字面量 → “””多行以内插值替换的文字可选”””以内插值替换的文字 → 以内插值替换的文字条目 以内插值替换的文字可选
以内插值替换的文字t → \( 表达式 ) | 引用文字条目
转义字符 → \0 | \ | \t | \n | \r | \” | \’
转义字符 → \u { unicode标量数字 }unicode标量数字 → 一到八位的十六进制数字
正则表达式字面量
正则表达式字面量是一串由斜杠( / )包围的字符,具有下面的格式:
/regular expression/
正则表达式字面量不能由非转义的制表符或者空格开头,并且内部不能包含未转义的斜杠( / ),回车,或者软回车。
在正则表达式字面量中,反斜杠会被理解为正则表达式的一部分而不是字符串字面量中的转义符号。它表示接下来的一个特殊字符应该被解释为字面量,或者下一个非特殊字符应该被特殊解释。比如, /\(/ 匹配一个左括号, /\d/ 匹配一个数字。
一则表达式字面量的扩展界定符是一个或多个围绕在斜杠( / )前后且数量对称的井号( # ),具有如下格式:
#/regular expression/#
1 2 3 |
#/ regular expression /# |
使用扩展界定符的正则表达式字面量可以以未转义的空格或者制表符开头,包含未转义的斜杠( / ),并且跨越多行。对于多行正则表达式字面量来说,起始界定符比如在一行的末尾,结束界定符必须在它自己的行。在多行正则表达式字面量中,扩展正则表达式字面量语法默认启用——特殊地,空格会被忽略且注释可用。
1 2 |
let regex1 = ##/abc/## // OK let regex2 = # #/abc/# # // Error |
如果你需要空的正则表达式字面量,你必须使用扩展界定符语法。
正则表达式的语法
运算符
Swift 标准库定义了大量的运算符供你使用,其中许多已经在基本运算符和高级运算符里讨论过。本部分描述哪些字符可以用来定义自定义运算符。
自定义运算符可以由以下之一的 ASCII 字符 / 、 = 、 - 、 + 、 ! 、 * 、 % 、 < 、 > 、 & 、 | 、 ^ 、 ? 或者 ~ ,或者下面语法中规定的任一个 Unicode 字符之一开始(它包括来自数学运算符、混合符号、装饰 Unicode 块以及其他的符号的字符)。在第一个字符之后,同样可以使用组合 Unicode 字符。你同样也可以定义一串两个以上的点为自定义运算符(比如 .... )。在第一个字符之后,组合 Unicode 字符也可以使用。
你同样可以自己定义以点( . )开头的的运算符。这些运算符可以包含额外的点比如 .+. 。如果运算符没有以点开头,那它就不能在名字的其他地方包含点。比如说, +.+ 将会被视为 + 运算符后跟一个 .+ 运算符。
尽管你可以定义自己的包含问号(?)的运算符,但它们不能由单个的问号符号组成。另外,就算运算符可以包含感叹号(!),后缀运算符也不能以叹号或者问号开头。
注意
以下这些标记 = 、 -> 、 // 、 /* 、 */ 、 . , 前缀运算符 < 、 & 、 ? 、 中缀运算符 ? ,后缀运算符 > 、 ! 以及 ? 是被系统保留的。这些标记不能被重载,也不能用于自定义操作符。
运算符周围的空白用来决定一个运算符是用来做前缀运算符还是后缀运算符还是二元运算符。这种行为有以下规则:
- 如果运算符两侧都有空白或两侧都无空白,就被看作二元运算符。比如, +++ 运算符在 a+++b 和 a +++ b 中被看作二元运算符;
- 如果运算符只有左侧空白,将被看作前缀一元运算符。例如 +++ 运算符在 a +++b 中被看作前缀一元运算符;
- 如果运算符只有右侧空白,将被看作后缀一元运算符。例如 +++ 运算符在 a+++ b 中被看作后缀一元运算符;
- 如果运算符左侧没有空白并紧跟了一个点( . ),将被看作后缀一元运算符。例如 +++ 运算符在 a+++.b 中被看作后缀一元运算符( a+++ .b 而非 a +++ .b )。
基于这些规则,运算符前的字符 ( 、 [ 和 { ,运算符后的字符 ) 、 ] 和 } 以及字符 , 、 ; 和 : 都被视为空白。
以上规则有一点需要注意。如果预定义的运算符 ! 或者 ? 左侧没有空白,会被看做后缀运算符,无论它右边有没有空白。要把 ? 当做可选链运算符,左边必须没有空白。要把它当做三元( ? : )运算符,它必须在两边都有空白。
在特定的构造中,以 < 或者 > 开头的运算符可能会被分隔成两个或者更多的标记。其余的用相同的方式对待,可能会再次分割。因此,像 Dictionary<String, Array<Int>> 这样的构造中没有必要用空格来消除闭合字符 > 的歧义。在这个栗子中,闭合字符 > 不会被视为单独的标记而被误解为 >> 移位运算符的一部分。
学习如何定义新的,自定义运算符,参见自定义运算符(此处应有链接)和运算符声明(此处应有链接)。要学习如何重载现有的运算符,参见运算符功能(此处应有链接)。
运算符的语法
运算符 → 头部运算符 运算符字符组可选
运算符 → 头部点运算符 点运算符字符组可选头部运算符 → / | = | – | + | ! | * | % |< | > |& | | | / | ~ | ?
头部运算符 → U+00A1–U+00A7
头部运算符 → U+00A9 or U+00AB
头部运算符 → U+00AC or U+00AE
头部运算符 → U+00B0–U+00B1, U+00B6, U+00BB, U+00BF, U+00D7, or U+00F7
头部运算符 → U+2016–U+2017 or U+2020–U+2027
头部运算符 → U+2030–U+203E
头部运算符 → U+2041–U+2053头部运算符 → U+2055–U+205E
头部运算符 → U+2190–U+23FF
头部运算符 → U+2500–U+2775
头部运算符 → U+2794–U+2BFF头部运算符 → U+2E00–U+2E7F
头部运算符 → U+3001–U+3003
头部运算符 → U+3008–U+3030
运算符字符 → 运算符头部
运算符字符 → U+0300–U+036F
运算符字符 → U+1DC0–U+1DFF
运算符字符 → U+20D0–U+20FF
运算符字符 → U+FE00–U+FE0F
运算符字符 → U+FE20–U+FE2F
运算符字符 → U+E0100–U+E01EF运算符字符组 → 运算符字符 运算符字符组 (operator_characters)可选
头部点运算符 → ..
头部点运算符字符 → . | 运算符字符
头部点运算符字符组 → 点运算符字符 点运算符字符组可选二元运算符 → 运算符
前置运算符 → 运算符
后置运算符 → 运算符