一、表义符/符号
1.1 语法
下表中的各种表义符会在本书中标有 词法 和 句法 的文法片段中用到:
表义符 | 示例 | 释义 |
---|---|---|
CAPITAL | KW_IF, INTEGER_LITERAL | 由词法分析生成的单一 token |
ItalicCamelCase | LetStatement, Item | 句法产生式(syntactical production) |
string | x, while, * | 确切的字面字符(串) |
\x | \n, \r, \t, \0 | 转义字符 |
x? | pub? | 可选项 |
x* | OuterAttribute* | x 重复零次或多次 |
x+ | MacroMatch+ | x 重复一次或多次 |
xa..b | HEX_DIGIT1..6 | x 重复 a 到 b 次 |
| | u8 | u16, Block | | Item 或 |
[ ] | [b B] | 列举的任意字符 |
[ - ] | [a-z] | a 到 z 范围内的任意字符(包括 a 和 z) |
~[ ] | ~[b B] | 列举范围外的任意字符(序列) |
~string | ~\n, ~*/ | 此字符序列外的任意字符(序列) |
( ) | (, Parameter)? | 程序项分组 |
1.2 字符串表产生式
文法中的一些规则 — 比如一元运算符,二元运算符和关键字 — 会以简化形式:作为可打印字符串的列表形式(在本书的相关章节的头部的各种产生式定义/句法规则里)给出。这些规则构成了关于 token 规则的规则子集,并且它们被假定为源码编译时的词法分析阶段的结果被再次输入给解析器,然后由一个DFA驱动,对此字符串表里的所有条目(string table entries)进行析取(disjunction)操作(来进行句法分析)。
本书还约定,当文法表中出现如 monospace
这样的字符串时,它代表对这些产生式中的单一 token 成员的隐式引用。
二、词法结构
2.1 输入格式
Rust 输入被解释为用 UTF-8 编码的 Unicode 字符序列。
2.2 关键字
Rust 将关键字分为三类:
- 严格关键字
- 保留关键字
- 弱关键字
严格关键字
这类关键字只能在正确的上下文中使用。它们不能用作以下名称:
- 程序项(item)
- 变量和函数参数
- 字段(field)和变体
- 类型参数
- 生存期参数或者循环标签
- 宏或属性
- 宏占位符
- crate
词法分析:
KW_AS : as
KW_BREAK : break
KW_CONST : const
KW_CONTINUE : continue
KW_CRATE : crate
KW_ELSE : else
KW_ENUM : enum
KW_EXTERN : extern
KW_FALSE : false
KW_FN : fn
KW_FOR : for
KW_IF : if
KW_IMPL : impl
KW_IN : in
KW_LET : let
KW_LOOP : loop
KW_MATCH : match
KW_MOD : mod
KW_MOVE : move
KW_MUT : mut
KW_PUB : pub
KW_REF : ref
KW_RETURN : return
KW_SELFVALUE : self
KW_SELFTYPE : Self
KW_STATIC : static
KW_STRUCT : struct
KW_SUPER : super
KW_TRAIT : trait
KW_TRUE : true
KW_TYPE : type
KW_UNSAFE : unsafe
KW_USE : use
KW_WHERE : where
KW_WHILE : while
以下关键字从 2018 版开始启用。
词法分析 2018+
KW_ASYNC : async
KW_AWAIT : await
KW_DYN : dyn
保留关键字
这类关键字目前还没有被使用,但是它们被保留以备将来使用。它们具有与严格关键字相同的限制。这样做的原因是通过禁止当前程序使用这些关键字,从而使当前程序能兼容 Rust 的未来版本。
词法分析
KW_ABSTRACT : abstract
KW_BECOME : become
KW_BOX : box
KW_DO : do
KW_FINAL : final
KW_MACRO : macro
KW_OVERRIDE : override
KW_PRIV : priv
KW_TYPEOF : typeof
KW_UNSIZED : unsized
KW_VIRTUAL : virtual
KW_YIELD : yield
以下关键字从 2018 版开始成为保留关键字。
词法分析 2018+
KW_TRY : try
弱关键字
这类关键字只有在特定的上下文中才有特殊的意义。例如,可以声明名为 union 的变量或方法。
- macro_rules 用于创建自定义宏。
- union 用于声明联合体(union),它只有在联合体声明中使用时才是关键字。
- ‘static 用于静态生存期,不能用作通用泛型生存期参数和循环标签
// error[E0262]: invalid lifetime parameter name:'static
fn invalid_lifetime_parameter<’static>(s: &’static str) -> &’static str { s } - 在 2015 版次中,当 dyn 用在非 :: 开头的路径限定的类型前时,它是关键字。
从 2018 版开始,dyn 被提升为一个严格关键字。
词法分析
KW_UNION : union
KW_STATICLIFETIME : ‘static词法分析 2015
KW_DYN : dyn
2.3 标识符
词法分析:
IDENTIFIEROR_KEYWORD :
XID_Start XID_Continue*
| XID_Continue+RAW_IDENTIFIER : r# IDENTIFIER_OR_KEYWORD 排除 crate, self, super, Self
NON_KEYWORD_IDENTIFIER : IDENTIFIER_OR_KEYWORD 排除严格关键字和保留关键字
IDENTIFIER :
NON_KEYWORD_IDENTIFIER | RAW_IDENTIFIER
标识符遵循 Unicode标准附录31 中针对 Unicode 13.0版的规范,后面所述内容在此版本中均有备述。 这里举一些标识符的示例:
- foo
- _identifier
- r#true
- Москва
- 東京
其中 UAX #31 中要求标识符使用的(产生式)参数如下:
- 起始字符 := XID_Start,外加一个下划线 (U+005F)
- 后续字符 := XID_Continue
- 中间字符 := 空
还有一个附加约束,即单个下划线字符不是标识符。注意: 以下划线开头的标识符通常用于表示有意不会被实际使用的标识符,且会使 rustc 对未被使用的警告静音。
如果标识符没有下面原生标识符章节中描述的 r#
前缀,那它不能是严格关键字或保留关键字。
标识符中不允许使用零宽度非连接符(ZWNJ U+200C)和零宽度连接符(ZWJ U+200D)。
在下列情况下,标识符仅限于 XID_Start 和 XID_Continue 的ASCII子集:
- extern crate声明
- 路径中引用的外部 crate名
- 从文件系统中载入的未被path属性限定的模块名
- 被 no_mangle属性限定的程序项
- 外部块中的程序项名称
标准化
标识符使用Unicode标准附录15中定义的规范化形式C(NFC)进行规范化。如果两个标识符的 NFC形式相等,那么它们就是等价的。
[过程宏] [proc macro]和声明宏在其输入中接受规范化的标识符。
原生标识符
除了有形式前缀 r#
修饰外,原生标识符与普通标识符类似。(注意形式前缀 r#
不包括在实际标识符中。)与普通标识符不同,原生标识符可以是除上面列出的 RAW_IDENTIFIER 之外的任何严格关键字或保留关键字。
2.4 注释
词法分析
LINE_COMMENT :(译者注:行注释)
/ (~[ !] | ** | BlockCommentOrDoc) | //BLOCK_COMMENT :(译者注:块注释)
/ (~[ !] | | BlockCommentOrDoc) (BlockCommentOrDoc | ~/) */
| //
| /*/INNER_LINE_DOC :(译者注:内部行文档型注释)
//! ~[\n IsolatedCR]*INNER_BLOCK_DOC :(译者注:内部块文档型注释)
/! ( BlockCommentOrDoc | ~[/ IsolatedCR] ) /OUTER_LINE_DOC :(译者注:外部行文档型注释)
/// (~/ ~[\n IsolatedCR]*)?OUTER_BLOCK_DOC :(译者注:外部块文档型注释)
/* (~ | BlockCommentOrDoc ) (BlockCommentOrDoc | ~[/ IsolatedCR]) */BlockCommentOrDoc :(译者注:块注释或文档型注释)
BLOCK_COMMENT
| OUTER_BLOCK_DOC
| INNER_BLOCK_DOCIsolatedCR :
后面没有跟 \n 的 \r
非文档型注释
Rust 代码中的注释一般遵循 C++ 风格的行(//)和块(/* … */)注释形式,也支持嵌套的块注释。
非文档型注释(Non-doc comments)被解释为某种形式的空白符。
文档型注释
以三个斜线(///)开始的行文档型注释,以及块文档型注释(/** … */),均为内部文档型注释。它们被当做 doc属性的特殊句法解析。也就是说,它们等同于把注释内容写入 #[doc=”…”] 里。例如:/// Foo 等同于 #[doc=”Foo”],/** Bar */ 等同于 #[doc=”Bar”]。
以 //! 开始的行文档型注释,以及 /*! … */ 形式的块文档型注释属于注释体所在对象的文档型注释,而非注释体之后的程序项的。也就是说,它们等同于把注释内容写入 #![doc=”…”] 里。//! 注释通常用于标注模块位于的文件。
孤立的 CRs(\r),如果其后没有紧跟有 LF(\n),则不能出现在文档型注释中。
示例
|
2.5 空白符
空白符是非空字符串,它里面只包含具有 Pattern_White_Space 属性的 Unicode 字符,即:
- U+0009 (水平制表符, ‘\t’)
- U+000A (换行符, ‘\n’)
- U+000B (垂直制表符)
- U+000C (分页符)
- U+000D (回车符, ‘\r’)
- U+0020 (空格符, ‘ ‘)
- U+0085 (下一行标记符)
- U+200E (从左到右标记符)
- U+200F (从右到标左记符)
- U+2028 (行分隔符)
- U+2029 (段分隔符)
Rust是一种“格式自由(free-form)”的语言,这意味着所有形式的空白符在文法中仅用于分隔 tokens 的作用,没有语义意义。
Rust 程序中,如果将一个空白符元素替换为任何其他合法的空白符元素(例如单个空格字符),它们仍有相同的意义。
2.6 token
token 是采用非递归方式的正则文法(regular languages)定义的基本语法产生式(primitive productions)。
Rust 源码输入可以被分解成以下几类 token:
- 关键字
- 标识符
- 字面量
- 生存期
- 标点符号
- 分隔符
字面量
字面量是一个由单一 token(而不是由一连串 tokens)组成的表达式,它立即、直接表示它所代表的值,而不是通过名称或其他一些求值/计算规则来引用它。字面量是常量表达式的一种形式,所以它(主要)用在编译时求值。