Rust编译器将源码进行Tokenization,转换成一个个token tree,这些token tree可以是不符合Rust语法规则的。这些token有不同的类型。
foo
, Bambous
, self
, we_can_dance
, LaCaravane
, …42
, 72u32
, 0_______0
, 1.0e-40
, "ferris was here"
, …_
, fn
, self
, match
, yield
, macro
, …[
, :
, ::
, ?
, ~
, @
, …需要注意的是self,即是Identifiers,也是Kerwords,大多数情况下是Keywords Macro是Rust的语法扩展
Tokenization后,接下来就是将token stream转换成AST(Abstract Syntax Tree),这一步被称之为**Paprsing,**而宏的处理就是在AST之后开始了。
所有的Token自己就是一个Token Tree,具体点来说他自己就是一个只有一个叶子节点的Token Tree。被**{}、[]、()**包裹的一系列token组成了一个有多个叶子的Token Tree。例如下面这行代码。
a + b + (c + d[0]) + e
转换成Token Tree后如下:
«a» «+» «b» «+» «( )» «+» «e»
╭────────┴──────────╮
«c» «+» «d» «[ ]»
╭─┴─╮
«0»
最终转换出来的是一个具有多个根的token tree,相互嵌套,接着Token tree会被转换成AST,如下:
Token tree中不应该存在不成对的{}、[]、()
Rust的宏,其处理就是在AST构建之后,才开始进行处理,因此调用宏的语法必须是符合Rust语法规则的,属于Rust的语法扩展,下面是宏相关的语法扩展。
# [ $arg ]
; e.g. #[derive(Clone)]
, #[no_mangle]
, …# ! [ $arg ]
; e.g. #![allow(dead_code)]
, #![crate_name="blang"]
, …