Tokens
tokens.md
commit: 5afb503a4c1ea3c84370f8f4c08a1cddd1cdf6ad
本章译文最后维护日期:2024-03-09
token 是采用非递归方式的正则文法(regular languages)定义的基本语法产生式(primitive productions)。Rust 源码输入可以被分解成以下几类 token:
在本文档中,“简单”token 会直接在(相关章节头部的)[字符串表产生式(production)][string table production]表单中给出,并以 monospace
字体显示。(译者注:本译作的原文中,在文法表之外的行文中也会大量出现这种直接使用简单token 来替代相关名词的做法,一般此时如果译者觉得这种 token 需要翻译时,会使用诸如:结构体(struct
) 这种形式来翻译。读者需要意识到“struct”是文法里的一个 token,能以其字面形式直接出现在源码里。)
Literals
字面量
字面量是字面量表达式中使用的各种 token。
Examples
示例
Characters and strings
字符和字符串
举例 | # 号的数量1 | 字符集 | 转义 | |
---|---|---|---|---|
字符 | 'H' | 0 | 全部 Unicode | 引号 & ASCII & Unicode |
字符串 | "hello" | 0 | 全部 Unicode | 引号 & ASCII & Unicode |
原生字符串 | r#"hello"# | <256 | 全部 Unicode | N/A |
字节 | b'H' | 0 | 全部 ASCII | 引号 & 字节 |
字节串 | b"hello" | 0 | 全部 ASCII | 引号 & 字节 |
原生字节串 | br#"hello"# | <256 | 全部 ASCII | N/A |
C语言风格的字符串 | c"hello" | 0 | 全部 Unicode | Quote & Byte & Unicode |
原生C语言风格的字符串 | cr#"hello"# | <256 | 全部 Unicode | N/A |
字面量两侧的 #
数量必须相同。
注意: 字符和字符串字面量token 不会包括
U+000D
(CR)后紧跟U+000A
(LF) 的字符序列:这对字符会在编译器读取源文件时被转换为单个U+000A
(LF)字符。
ASCII escapes
ASCII 转义
名称 | |
---|---|
\x41 | 7-bit 字符编码(2位数字,最大值为 0x7F ) |
\n | 换行符 |
\r | 回车符 |
\t | 制表符 |
\\ | 反斜线 |
\0 | Null |
Byte escapes
字节转义
名称 | |
---|---|
\x7F | 8-bit 字符编码(2位数字) |
\n | 换行符 |
\r | 回车符 |
\t | 制表符 |
\\ | 反斜线 |
\0 | Null |
Unicode escapes
unicode 转义
名称 | |
---|---|
\u{7FFF} | 24-bit Unicode 字符编码(最多6个数字) |
Quote escapes
引号转义
Name | |
---|---|
\' | 单引号 |
\" | 双引号 |
Numbers
数字
所有数字字面量允许使用 _
作为可视分隔符,比如:1_234.0E+18f64
Suffixes
后缀
后缀是字面量主体部分后面的字符序列(它们之间不能含有空格),其形式与非原生标识符或关键字相同。
词法
SUFFIX : IDENTIFIER_OR_KEYWORD
SUFFIX_NO_E : SUFFIX not beginning withe
orE
任何带有后缀的字面量(如字符串、整型等)都可以作为有效的 token。
带有后缀的字面量token 可以传递给宏而不会产生错误。
宏自己决定如何解释这种 token,以及是否该报错。
特别是,声明宏的 literal
段指示符匹配带有任意后缀的字面量token。
#![allow(unused)] fn main() { macro_rules! blackhole { ($tt:tt) => () } macro_rules! blackhole_lit { ($l:literal) => () } blackhole!("string"suffix); // OK blackhole_lit!(1suffix); // OK }
但是,那些被解析为字面量表达式或模式的字面量token 的后缀是受限的。 非数字文字标记上的任何后缀都将被拒绝,数字文字标记仅接受以下列表中的后缀。
整数 | 浮点数 |
---|---|
u8 , i8 , u16 , i16 , u32 , i32 , u64 , i64 , u128 , i128 , usize , isize | f32 , f64 |
Character and string literals
字符和字符串字面量
Character literals
字符字面量
词法
CHAR_LITERAL :
'
( ~['
\
\n \r \t] | QUOTE_ESCAPE | ASCII_ESCAPE | UNICODE_ESCAPE )'
SUFFIX?QUOTE_ESCAPE :
\'
|\"
ASCII_ESCAPE :
\x
OCT_DIGIT HEX_DIGIT
|\n
|\r
|\t
|\\
|\0
UNICODE_ESCAPE :
\u{
( HEX_DIGIT_
* )1..6}
字符字面量是位于两个 U+0027
(单引号 '
)字符内的单个 Unicode 字符。当它是 U+0027
自身时,必须前置转义字符 U+005C
(\
)。
String literals
字符串字面量
词法
STRING_LITERAL :
"
(
~["
\
IsolatedCR] (译者注:IsolatedCR:后面没有跟\n
的\r
,首次定义见注释)
| QUOTE_ESCAPE
| ASCII_ESCAPE
| UNICODE_ESCAPE
| STRING_CONTINUE
)*"
SUFFIX?STRING_CONTINUE :
\
后跟 \n
字符串字面量是位于两个 U+0022
(双引号 "
)字符内的任意 Unicode 字符序列。当它是 U+0022
自身时,必须前置转义字符 U+005C
(\
)。
字符串字面量允许使用字符U+000A
(LF) 的形式来换行书写。
但当非转义的字符 U+005C
(\
)后面紧跟着一个换行符时,换行符并不会出现在字符串中。
详细信息请参见字符串接续符转义。
字符U+000D
(CR) 除了作为这种字符串接续符转义的一部分之外,不能出现在字符串字面量中。
Character escapes
字符转义
不管是字符字面量,还是非原生字符串字面量,Rust 都为其提供了额外的转义功能。转义以一个 U+005C
(\
)开始,并后跟如下形式之一:
- 7-bit 码点转义以
U+0078
(x
)开头,后面紧跟两个十六进制数字,其最大值为0x7F
。它表示 ASCII 字符,其码值就等于字面提供的十六进制值。不允许使用更大的值,因为不能确定其是 Unicode 码点还是字节值(byte values)。 - 24-bit 码点转义以
U+0075
(u
)开头,后跟多达六位十六进制数字,位于花括号U+007B
({
)和U+007D
(}
)之间。这表示(需转义到的)Unicode 字符的码点等于花括号里的十六进制值。 - 空白符转义是
U+006E
(n
)、U+0072
(r
) 或者U+0074
(t
) 之一,依次分别表示 Unicode 码点U+000A
(LF),U+000D
(CR),或者U+0009
(HT)。 - null转义 是字符
U+0030
(0
),表示 Unicode 码点U+0000
(NUL)。 - 反斜线转义 是字符
U+005C
(\
),反斜线必须通过转义才能表示其自身。
Raw string literals
原生字符串字面量
词法
RAW_STRING_LITERAL :
r
RAW_STRING_CONTENT SUFFIX?RAW_STRING_CONTENT :
"
( ~ IsolatedCR )* (非贪婪模式)"
|#
RAW_STRING_CONTENT#
原生字符串字面量不做任何转义。它以字符 U+0072
(r
)后跟小于256个字符 U+0023
(#
),以及一个字符 U+0022
(双引号 "
),这样的字符组合开始。
中间原生字符串主体部分可包含除了 U+000D
(CR) 之外的任意 Unicode 字符序列。
最后再后跟另一个 U+0022
(双引号 "
)以及跟与字符串主体前的那段字符组合中的同等数量的 U+0023
(#
)的字符来表示字符串主体的结束。
所有包含在原生字符串文本主体中的 Unicode 字符都代表他们自身:字符 U+0022
(双引号 "
)(除非后跟的纯 U+0023
(#
)字符串与文本主体开始前的对称相等)或字符 U+005C
(\
)此时都没有特殊含义。
字符串字面量示例:
#![allow(unused)] fn main() { "foo"; r"foo"; // foo "\"foo\""; r#""foo""#; // "foo" "foo #\"# bar"; r##"foo #"# bar"##; // foo #"# bar "\x52"; "R"; r"R"; // R "\\x52"; r"\x52"; // \x52 }
Byte and byte string literals
字节和字节串字面量
Byte literals
字节字面量
词法
BYTE_LITERAL :
b'
( ASCII_FOR_CHAR | BYTE_ESCAPE )'
SUFFIX?ASCII_FOR_CHAR :
任何 ASCII 字符 (0x00 到 0x7F), 排除'
,\
, \n, \r 或者 \tBYTE_ESCAPE :
\x
HEX_DIGIT HEX_DIGIT
|\n
|\r
|\t
|\\
|\0
|\'
|\"
字节字面量是单个 ASCII 字符(码值在 U+0000
到 U+007F
区间内)或一个转义字节作为字节字面量的真实主体跟在表示形式意义的字符 U+0062
(b
)和字符 U+0027
(单引号 '
)组合之后,然后再后接字符 U+0027
。如果字符 U+0027
本身要出现在字面量中,它必须经由前置字符 U+005C
(\
)转义。字节字面量等价于一个 u8
8-bit 无符号整型数字字面量。
Byte string literals
字节串字面量
词法
BYTE_STRING_LITERAL :
b"
( ASCII_FOR_STRING | BYTE_ESCAPE | STRING_CONTINUE )*"
SUFFIX?ASCII_FOR_STRING :
任何 ASCII 字符(码值位于 0x00 到 0x7F 之间), 排除"
,\
和 IsolatedCR
非原生字节串字面量是 ASCII 字符和转义字符组成的字符序列,形式是以字符 U+0062
(b
)和字符 U+0022
(双引号 "
)组合开头,以字符 U+0022
结尾。如果字面量中包含字符 U+0022
,则必须由前置的 U+005C
(\
)转义。
或者,字节串字面量也可以是原生字节串字面量(下面有其定义)。
字节串字面量允许使用字符U+000A
(LF) 的形式来换行书写。
但当非转义的字符 U+005C
(\
)后面紧跟着一个换行符时,换行符并不会出现在字符串中。
详细信息请参见字符串接续符转义。
字符U+000D
(CR) 除了作为这种字符串接续符转义的一部分之外,不能出现在字节串字面量中。
一些额外的转义可以在字节或非原生字节串字面量中使用,转义以 U+005C
(\
)开始,并后跟如下形式之一:
- 字节转义以
U+0078
(x
)开始,后跟恰好两位十六进制数字来表示十六进制值代表的字节。 - 空白符转义是字符
U+006E
(n
)、U+0072
(r
),或U+0074
(t
)之一,分别表示字节值0x0A
(ASCII LF)、0x0D
(ASCII CR),或0x09
(ASCII HT)。 - null转义是字符
U+0030
(0
),表示字节值0x00
(ASCII NUL)。 - 反斜线转义是字符
U+005C
(\
),必须被转义以表示其 ASCII 编码0x5C
。
Raw byte string literals
原生字节串字面量
词法
RAW_BYTE_STRING_LITERAL :
br
RAW_BYTE_STRING_CONTENT SUFFIX?RAW_BYTE_STRING_CONTENT :
"
ASCII_FOR_RAW* (非贪婪模式)"
|#
RAW_BYTE_STRING_CONTENT#
ASCII_FOR_RAW :
除了 IsolatedCR 外的任何 ASCII 字符(0x00 到 0x7F)
原生字节串字面量不做任何转义。它们以字符 U+0062
(b
)后跟 U+0072
(r
),再后跟小于256个字符 U+0023
(#
)及字符 U+0022
(双引号 "
),这样的字符组合开始。
中间是原生字节串主体,这部分可包含除了 U+000D
(CR) 外的任意的 ASCII 字符序列。
最后再后跟另一个 U+0022
(双引号 "
)以及跟与字符串主体前的那段字符组合中的同等数量的 U+0023
(#
)的字符来表示字符串主体的结束。
原生字节串字面量不能包含任何非 ASCII 字节。
原生字节串文本主体中的所有字符都代表它们自身的 ASCII 编码,字符 U+0022
(双引号 "
)(除非后跟的纯 U+0023
(#
)字符串与文本主体开始前的对称相等)或字符 U+005C
(\
)此时都没有特殊含义。
字节串字面量示例:
#![allow(unused)] fn main() { b"foo"; br"foo"; // foo b"\"foo\""; br#""foo""#; // "foo" b"foo #\"# bar"; br##"foo #"# bar"##; // foo #"# bar b"\x52"; b"R"; br"R"; // R b"\\x52"; br"\x52"; // \x52 }
C string and raw C string literals
C语言风格的字符串字面量和原生C语言风格的字符串字面量
C string literals
C语言风格的字符串字面量
词法
C_STRING_LITERAL :
c"
(
~["
\
IsolatedCR NUL]
| BYTE_ESCAPE except\0
or\x00
| UNICODE_ESCAPE except\u{0}
,\u{00}
, …,\u{000000}
| STRING_CONTINUE
)*"
SUFFIX?
C语言风格的字符串字面量_是通过前面是字符U+0063
(c
) 和 U+0022
(双引号),后面是字符U+0022
转义过的 Unicode字符序列。如果字面量中存在字符U+0022
,则其前面必须用U+005C
(\
)字符进行转义。
或者,C语言风格的字符串字面量可以是下面定义的_原生C语言风格的字符串字面量。
C语言风格的字符串由字节0x00
隐式终止,因此C语言风格的字符串字面量c""
等同于从字节字符串文字b"\x00"
来手动构造一个&CStr
。除了隐式终止符之外,C语言风格的字符串中不允许再使用字节0x00
。
C语言风格的字符串字面量允许使用字符U+000A
(LF) 的形式来换行书写。
但当非转义的字符 U+005C
(\
)后面紧跟着一个换行符时,换行符并不会出现在字符串中。
详细信息请参见字符串接续符转义。
字符U+000D
(CR) 除了作为这种字符串接续符转义的一部分之外,不能出现在C语言风格的字符串字面量中。
一些额外的转义符在非原生C语言风格的字符串字面量中可用。转义以U+005C
(\
)开头,并后继以下形式之一:
- 一个_字节转义符_以
U+0078
(x
)开头,后面正好跟两个_十六进制数字_。它表示等于所提供的十六进制值的字节序。 - 一个_24位码点转义符_以
U+0075
(u
) 开头,后面最多有六个由大括号U+007B
({
)和U+007D
(}
)包围的_十六进制数字_。它表示由这些十六进制数值通过的UTF-8编码的Unicode码点值。 - _空白转义符_是字符
U+006E
(n
)、U+0072
(r
) 或U+0074
(t
) 之一,分别表示字节0x0A
(ASCII LF)、0x0D
(ASCII CR) 或0x09
(ASCII HT)。 - _反斜杠转义符_就是字符
U+005C
(\
),必须对其进行转义才能表示其ASCII编码的0x5C
。
C语言风格的字符串本身表示没有定义编码类型的字节序,但 C语言风格的字符串字面量又可能包含U+007F
以上的 Unicode字符。这种情况下这些字符将被替换为该字符的UTF-8表示形式的字节。
以下 C语言风格的字符串字面量表达形式等效:
#![allow(unused)] fn main() { c"æ"; // 小写的拉丁字符 AE (U+00E6) c"\u{00E6}"; c"\xC3\xA6"; }
版次差异: 2021版或更高版本中可以使用 C语言风格的字符串字面量。在更低的版次中,
c""
token 会被词法解析器解析为c ""
。
Raw C string literals
原生C语言风格的字符串字面量
Lexer
RAW_C_STRING_LITERAL :
cr
RAW_C_STRING_CONTENT SUFFIX?RAW_C_STRING_CONTENT :
"
( ~ IsolatedCR NUL )* (non-greedy)"
|#
RAW_C_STRING_CONTENT#
原生C语言风格的字符串字面量不处理任何转义。它们以字符U+0063
(c
)开头,然后跟U+0072
(r
),然后再跟少于256个的字符U+0023
(#
)和一个 U+0022
(双引号)字符(记作开头引号)。
中间_原生C语言风格的字符串本体_可以包含除 U+0000
(NUL) 和 U+000D
(CR) 之外的任何 Unicode字符序列。
最后再后跟另一个 U+0022
(双引号 "
)以及跟与字符串主体前的那段字符组合中的同等数量的 U+0023
(#
)的字符来表示字符串主体的结束。
原生C语言风格的字符串本体中包含的所有字符都以 UTF-8编码形式表示。字符U+0022
(双引号)(后面跟有至少与用于开始原生C语言风格的字符串字面量的数量相同的 U+0023
(#
)字符时除外)或 U+005C
(\
) 没有任何特殊含义。
版次差异: 2021版或更高版次可以使用原生C语言风格的字符串字面量。在更低的版次中,
cr""
token 会被词法解析器解析为cr ""
,cr#""#
被解析为cr #""#
(这是不符合语法的)。
Examples for C string and raw C string literals
C语言风格的字符串字面量和原生C语言风格的字符串字面量的示例
#![allow(unused)] fn main() { c"foo"; cr"foo"; // foo c"\"foo\""; cr#""foo""#; // "foo" c"foo #\"# bar"; cr##"foo #"# bar"##; // foo #"# bar c"\x52"; c"R"; cr"R"; // R c"\\x52"; cr"\x52"; // \x52 }
Number literals
数字字面量
数字字面量可以是整型字面量,也可以是浮点型字面量,识别这两种字面量的文法是混合在一起的。
Integer literals
整型字面量
词法
INTEGER_LITERAL :
( DEC_LITERAL | BIN_LITERAL | OCT_LITERAL | HEX_LITERAL ) SUFFIX_NO_E?DEC_LITERAL :
DEC_DIGIT (DEC_DIGIT|_
)*BIN_LITERAL :
0b
(BIN_DIGIT|_
)* BIN_DIGIT (BIN_DIGIT|_
)*OCT_LITERAL :
0o
(OCT_DIGIT|_
)* OCT_DIGIT (OCT_DIGIT|_
)*HEX_LITERAL :
0x
(HEX_DIGIT|_
)* HEX_DIGIT (HEX_DIGIT|_
)*BIN_DIGIT : [
0
-1
]OCT_DIGIT : [
0
-7
]DEC_DIGIT : [
0
-9
]HEX_DIGIT : [
0
-9
a
-f
A
-F
]
整型字面量具备下述 4 种形式之一:
- 十进制字面量以十进制数字开头,后跟十进制数字和*下划线(
_
)*的任意组合。 - 十六进制字面量以字符序列
U+0030
U+0078
(0x
)开头,后跟十六进制数字和下划线的任意组合(至少一个数字)。 - 八进制字面量以字符序列
U+0030
U+006F
(0o
)开头,后跟八进制数字和下划线的任意组合(至少一个数字)。 - 二进制字面量以字符序列
U+0030
U+0062
(0b
)开头,后跟二进制数字和下划线的任意组合(至少一个数字)。
与其它字面量一样,整型字面量后面可紧跟(没有空格)一个上述的后缀。
后缀不能以 e
或 E
开头,因为这将被解析为浮点字面量的指数。
参见整型字面量表达式以了解这些后缀的功能效果。
被正确解析为整型字面量的示例:
#![allow(unused)] fn main() { #![allow(overflowing_literals)] 123; 123i32; 123u32; 123_u32; 0xff; 0xff_u8; 0x01_f32; // 注意这是整数 7986, 不是浮点数 1.0 0x01_e3; // 注意这是整数 483, 不是浮点数 1000.0 0o70; 0o70_i16; 0b1111_1111_1001_0000; 0b1111_1111_1001_0000i64; 0b________1; 0usize; // 下面这些对它们的类型来说太大了,但仍被认为是字面量表达式 128_i8; 256_u8; // 这是一个整型字面量,但被解析器接受为浮点型字面量表达式 5f32; }
注意对于 -1i8
这样的,其实它被分析为两个 token: -
后跟 1i8
。
不被承认为合法字面量表达式的整型字面量:
#![allow(unused)] fn main() { #[cfg(FALSE)] { 0invalidSuffix; 123AFB43; 0b010a; 0xAB_CD_EF_GH; 0b1111_f32; } }
Tuple index
元组索引
词法
TUPLE_INDEX:
INTEGER_LITERAL
元组索引直接与字面量token 进行比较。元组索引以 0
开始,每个后续索引的值以十进制的 1
递增。因此,元组索引只能匹配十进制值,并且该值不能用 0
做前缀字符。
#![allow(unused)] fn main() { let example = ("dog", "cat", "horse"); let dog = example.0; let cat = example.1; // 下面的示例非法. let cat = example.01; // 错误:没有 `01` 字段 let horse = example.0b10; // 错误:没有 `0b10` 字段 }
注意: 元组的索引可能包含一些特定的后缀,但是这不是被故意设计为有效的,可能会在将来的版本中被移除。 更多信息请参见https://github.com/rust-lang/rust/issues/60210。
Floating-point literals
浮点型字面量
词法
FLOAT_LITERAL :
DEC_LITERAL.
_(紧跟着的不能是.
,_
或者 XID_Start类型的字符)__
| DEC_LITERAL.
DEC_LITERAL SUFFIX_NO_E?
| DEC_LITERAL (.
DEC_LITERAL)? FLOAT_EXPONENT SUFFIX?FLOAT_EXPONENT :
(e
|E
) (+
|-
)? (DEC_DIGIT|_
)* DEC_DIGIT (DEC_DIGIT|_
)*
浮点型字面量有如下两种形式:
- 十进制字面量后跟句点字符
U+002E
(.
)。后面可选地跟着另一个十进制数字,还可以再接一个可选的指数。 - 十进制字面量后跟一个指数。
如同整型字面量,浮点型字面量也可后跟一个后缀,但在后缀之前,浮点型字面量部分不以 U+002E
(.
)结尾。
如果字面量不包含指数,则后缀不能以 e
或 E
开头。
参见浮点型字面量表达式以了解这类后缀的功能效果。
各种形式的浮点型字面量示例:
#![allow(unused)] fn main() { 123.0f64; 0.1f64; 0.1f32; 12E+99_f64; let x: f64 = 2.; }
最后一个例子稍显不同,因为不能对一个以句点结尾的浮点型字面量使用后缀句法,2.f64
会尝试在 2
上调用名为 f64
的方法。
请注意,像 -1.0
这样的会被分析为两个 token: -
后跟 1.0
。
不被认为是合法的字面量表达式的浮点型字面量的示例:
#![allow(unused)] fn main() { #[cfg(FALSE)] { 2.0f80; 2e5f80; 2e5e6; 2.0e5e6; 1.3e10u64; } }
Reserved forms similar to number literals
类似于数字字面量的保留形式
Lexer
RESERVED_NUMBER :
BIN_LITERAL [2
-9
]
| OCT_LITERAL [8
-9
]
| ( BIN_LITERAL | OCT_LITERAL | HEX_LITERAL ).
(不能直接后跟.
,_
或一个 XID_Start类型的字符)
| ( BIN_LITERAL | OCT_LITERAL ) (e
|E
)
|0b
_
* end of input or not BIN_DIGIT
|0o
_
* end of input or not OCT_DIGIT
|0x
_
* end of input or not HEX_DIGIT
| DEC_LITERAL ( . DEC_LITERAL)? (e
|E
) (+
|-
)? end of input or not DEC_DIGIT
后面词法形式和数字字面量差不多的保留形式。 由于这些可能会引起歧义,它们会被 token转化器(tokenizer)拒绝,而不是被解释为单独的 token。
-
不带后缀的二进制或八进制字面量,不插入空格的后跟一个超出其进制数字字符范围的十进制数字。
-
不带后缀的二进制、八进制或十六进制字面量,不插入空格的后跟一个句点字符(句点后面的内容与浮点数字面量相同)。
-
不带前缀的二进制或八进制字面量,不加空格的后跟字符
e
或E
。 -
以一个进制数前缀开始的输入,但又不是有效的二进制、八进制或十六进制字面量(因为它没包含数字)。
-
具有浮点型字面量形式且指数中没有数字的输入。
这些保留形式的示例:
#![allow(unused)] fn main() { 0b0102; // 这可不是 `0b010` 后跟 `2` 0o1279; // 这可不是 `0o127` 后跟 `9` 0x80.0; // 这可不是 `0x80` 后跟 `.` and `0` 0b101e; // 这不是一个带后缀的字面量,也不是 `0b101` 后跟 `e` 0b; // 这不是一个整型字面量,也不是 `0` 后跟 `b` 0b_; // 这不是一个整型字面量,也不是 `0` 后跟 `b_` 2e; // 这不是一个浮点型字面量,也不是 `2` 后跟 `e` 2.0e; // 这不是一个浮点型字面量,也不是 `2.0` 后跟 `e` 2em; // 这不是一个带后缀的字面量,也不是 `2` 后跟 `em` 2.0em; // 这不是一个带后缀的字面量,也不是 `2.0` 后跟 `em` }
Lifetimes and loop labels
生存期和循环标签
词法
LIFETIME_TOKEN :
'
IDENTIFIER_OR_KEYWORD (后面没有紧跟'
)
|'_
(后面没有紧跟'
)LIFETIME_OR_LABEL :
'
NON_KEYWORD_IDENTIFIER (后面没有紧跟'
)
生存期参数和循环标签使用 LIFETIME_OR_LABEL 类型的 token。(尽管 LIFETIME_OR_LABEL 是 LIFETIME_TOKEN 的子集,但)任何符合 LIFETIME_TOKEN 约定的 token 也都能被上述词法分析规则所接受,比如 LIFETIME_TOKEN 类型的 token 在宏中就可以畅通无阻的使用。
Punctuation
标点符号
为了完整起见,这里列出了(Rust 里)所有的标点符号的 symbol token。它们各自的用法和含义在链接页面中都有定义。
符号 | 名称 | 使用方法 |
---|---|---|
+ | Plus | 算术加法, trait约束, 可匹配空的宏匹配器(Macro Kleene Matcher) |
- | Minus | 算术减法, 取反 |
* | Star | 算术乘法, 解引用, 裸指针, 可匹配空的宏匹配器, use 通配符 |
/ | Slash | 算术除法 |
% | Percent | 算术取模 |
^ | Caret | 位和逻辑异或 |
! | Not | 位和逻辑非, 宏调用, 内部属性, never型, 否定实现 |
& | And | 位和逻辑与, 借用, 引用, 引用模式 |
| | Or | 位和逻辑或, 闭包, match 中的模式, if let, 和 while let |
&& | AndAnd | 短路与, 借用, 引用, 引用模式 |
|| | OrOr | 短路或, 闭包 |
<< | Shl | 左移位, 嵌套泛型 |
>> | Shr | 右移位, 嵌套泛型 |
+= | PlusEq | 加法及赋值 |
-= | MinusEq | 减法及赋值 |
*= | StarEq | 乘法及赋值 |
/= | SlashEq | 除法及赋值 |
%= | PercentEq | 取模及赋值 |
^= | CaretEq | 按位异或及赋值 |
&= | AndEq | 按位与及赋值 |
|= | OrEq | 按位或及赋值 |
<<= | ShlEq | 左移位及赋值 |
>>= | ShrEq | 右移位及赋值, 嵌套泛型 |
= | Eq | 赋值, 属性, 各种类型定义 |
== | EqEq | 等于 |
!= | Ne | 不等于 |
> | Gt | 大于, 泛型, 路径 |
< | Lt | 小于, 泛型, 路径 |
>= | Ge | 大于或等于, 泛型 |
<= | Le | 小于或等于 |
@ | At | 子模式绑定 |
_ | Underscore | 通配符模式, 自动推断型类型, 常量项中的非命名程序项, 外部 crate, 和 use声明,和解构赋值 |
. | Dot | 字段访问, 元组索引 |
.. | DotDot | 区间, 结构体表达式, 模式,区间模式 |
... | DotDotDot | 可变参数函数, 区间模式 |
..= | DotDotEq | 闭区间, 区间模式 |
, | Comma | 各种分隔符 |
; | Semi | 各种程序项和语句的结束符, 数组类型 |
: | Colon | 各种分隔符 |
:: | PathSep | [路径分隔符]路径 |
-> | RArrow | 函数返回类型, 闭包返回类型, 数组指针类型 |
=> | FatArrow | 匹配臂, 宏 |
<- | LArrow | 左箭头符号在Rust 1.0之后就没有再使用过,但它仍然被视为一个单独的 token |
# | Pound | 属性 |
$ | Dollar | 宏 |
? | Question | 问号运算符, 非确定性内存宽度, 可匹配空的宏匹配器 |
~ | Tilde | 从 Rust 1.0 开始,波浪号操作符就弃用了,但其 token 可能仍在使用 |
Delimiters
定界符
括号用于文法的各个部分,左括号必须始终与右括号配对。括号以及其内的 token 在宏中被称作“token树(token trees)”。括号有三种类型:
括号 | 类型 |
---|---|
{ } | 花/大括号 |
[ ] | 方/中括号 |
( ) | 圆/小括号 |
Reserved prefixes
保留前缀
词法 2021+
RESERVED_TOKEN_DOUBLE_QUOTE : ( IDENTIFIER_OR_KEYWORD 排除b
或c
或r
或br
或cr
|_
)"
RESERVED_TOKEN_SINGLE_QUOTE : ( IDENTIFIER_OR_KEYWORD 排除b
|_
)'
RESERVED_TOKEN_POUND : ( IDENTIFIER_OR_KEYWORD 排除r
或br
或cr
|_
)#
这种被称为 保留前缀 的词法形式是保留供将来使用的。
如果输入的源代码在词法上被解释为非原生标识符(或关键字或 _
),且紧接着的字符是 #
、'
或 "
(没有空格),则将其标识为保留前缀。(译者注:例如 prefix#identifier, prefix"string", prefix'c', 和 prefix#123(其中 prefix可以是任何标识符(但不能是原生标识符))这样词法形式被标识为保留词法,以供将来拓展语法使用)
请注意,原生标识符、原生字符串字面量和原生字节字符串字面量可能包含 #
字符,但不会被解释为包含保留前缀。
类似地,原生字符串字面量、字节字面量、字节字符串字面量、原生字节字符串字面量、C语言风格的字符串字面量和原生C语言风格的字符串字面量中使用的r
、b
、 br
、 c
和 cr
前缀也不会解释为保留前缀。
版次差异:从2021版开始,保留前缀被词法解释器报告为错误(特别是,它们不能再传递给宏了)。
在2021版之前,保留前缀可以被词法解释器接受并被解释为多个 token(例如,后接
#
的标识符或关键字)。在所有的版次中都可以被编译器接受的示例:
#![allow(unused)] fn main() { macro_rules! lexes {($($_:tt)*) => {}} lexes!{a #foo} lexes!{continue 'foo} lexes!{match "..." {}} lexes!{r#let#foo} // 3个 tokens: r#let # foo }
在 2021之前的版次中可以被编译器接受,但之后会被拒绝的示例:
#![allow(unused)] fn main() { macro_rules! lexes {($($_:tt)*) => {}} lexes!{a#foo} lexes!{continue'foo} lexes!{match"..." {}} }