Skip to content

Commit

Permalink
update doc of architecture
Browse files Browse the repository at this point in the history
  • Loading branch information
vita-dounai committed Sep 13, 2020
1 parent a4ce784 commit 5f4a92c
Showing 1 changed file with 2 additions and 2 deletions.
4 changes: 2 additions & 2 deletions docs/in_depth/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Rust源代码文件编译需要经过下列阶段(我们在图中省略了优

![](../../images/in_depth/architecture/ast.png)

3. 然后,编译器开始分析AST并执行宏展开过程。此阶段是是最为重要的阶段,因为Liquid主要工作在这个阶段。以[HelloWorld合约](../quick_start/introduction.md)为例,编译器构造出HelloWorld合约的AST后,当扫描至AST中表示"```#[liquid::contract(version = "0.1.0")]```"的语法树节点时,编译器能够知道,此处正在调用[属性宏](https://doc.rust-lang.org/reference/procedural-macros.html#attribute-macros)(Rust中一种特殊的宏),因此会开始寻找`contract`属性宏的定义并尝试进行宏展开。在Liquid中,`contract`属性宏的定义如下:
3. 然后,编译器开始分析AST并执行宏展开过程。此阶段是是最为重要的阶段,因为Liquid主要工作在这个阶段。以[HelloWorld合约](../quick_start/introduction.md)为例,编译器构造出HelloWorld合约的AST后,当扫描至AST中表示"#[liquid::contract(version = "0.1.0")]"的语法树节点时,编译器能够知道,此处正在调用[属性宏](https://doc.rust-lang.org/reference/procedural-macros.html#attribute-macros)(Rust中一种特殊的宏),因此会开始寻找`contract`属性宏的定义并尝试进行宏展开。在Liquid中,`contract`属性宏的定义如下:

```rust
#[proc_macro_attribute]
Expand All @@ -43,7 +43,7 @@ Rust源代码文件编译需要经过下列阶段(我们在图中省略了优
}
```

在上述代码中可以看到,属性宏是以函数的形式定义的,其输入是若干个标记序列(TokenStream),其输出同样是一个标记序列。事实上,在Rust语言中,宏的确可以理解为将某一个AST变换到另外一个AST的函数!只是Rust编译器并不会向属性宏直接传递AST,而且会将其调用位置所在的语法树节点转换为标记序列传递给属性宏,由属性宏的编写者自行决定如何处理这段标记序列。无论如何处理,属性宏都需要返回一段标记序列,Rust编译器接收到这段标记序列后,会将其重新编译为AST插入到原调用位置,从而完成代码编译期更新。具体到Liquid的`contract`属性宏,当编译器进行展开时,`contract`属性宏会获取到自身及其后跟随的`mod`(即我们用来定义合约状态及合约方法的模块)的标记序列,并将其解析为一颗AST。随后,`contract`属性宏会自顶向下扫描这颗AST,当遇到使用```#[liquid(storage)] struct ...```语法定义的合约状态时,会进行语法检查及代码变换,将对结构体成员的读写操作变为对链上状态读写接口的调用。同理,在合约代码中使用```#[liquid(event)] struct ...```定义事件、使用```#[liquid(methods)] impl ...```定义合约方法等也会经历同样的代码变换过程,只是变换及桥接到区块链底层平台的方式不尽相同。
在上述代码中可以看到,属性宏是以函数的形式定义的,其输入是若干个标记序列(TokenStream),其输出同样是一个标记序列。事实上,在Rust语言中,宏的确可以理解为将某一个AST变换到另外一个AST的函数!只是Rust编译器并不会向属性宏直接传递AST,而且会将其调用位置所在的语法树节点转换为标记序列传递给属性宏,由属性宏的编写者自行决定如何处理这段标记序列。无论如何处理,属性宏都需要返回一段标记序列,Rust编译器接收到这段标记序列后,会将其重新编译为AST插入到原调用位置,从而完成代码编译期更新。具体到Liquid的`contract`属性宏,当编译器进行展开时,`contract`属性宏会获取到自身及其后跟随的`mod`(即我们用来定义合约状态及合约方法的模块)的标记序列,并将其解析为一颗AST。随后,`contract`属性宏会自顶向下扫描这颗AST,当遇到使用"#[liquid(storage)] struct ..."语法定义的合约状态时,会进行语法检查及代码变换,将对结构体成员的读写操作变为对链上状态读写接口的调用。同理,在合约代码中使用"#[liquid(event)] struct ..."语法定义事件、使用"#[liquid(methods)] impl ..."语法定义合约方法等也会经历同样的代码变换过程,只是变换及桥接到区块链底层平台的方式不尽相同。

4. 编译器将进行宏展开之后的AST翻译为可执行文件:若是需要在本机运行单元测试,则会将AST翻译为本机所使用的操作系统及CPU所能识别的可执行文件格式及二进制代码;若是需要可以在区块链上部署运行,则会将AST翻译为Wasm格式字节码。至此,合约的基本构建流程结束。

Expand Down

0 comments on commit 5f4a92c

Please sign in to comment.