Introduction to Macros
Inspect one language lane at a time so line-level text and code deltas stay readable.
Diff Lane
English
0 modified sections0 code block delta0 anchor delta
Diff Lane
中文
1 modified sections0 code block delta0 anchor delta
modified宏的简介textcode+2 lines, -1 line
v1.0.5
Section Text
1
宏可以理解为一种特殊的函数。一般的函数在输入的值上进行计算,然后输出一个新的值,而宏的输入和输出都是程序本身。在输入一段程序后,输出一段新的程序,这段输出的程序随后用于编译和执行。为了把宏的调用和函数调用区分开来,在调用宏时需使用 `@` 加上宏的名称。2
3
如下示例代码希望实现在调试过程中打印某个表达式的值,同时打印出表达式本身。4
5
6
显然,`dprint` 不能被写为常规的函数,因为函数只能获得输入表达式的值,不能获得输入表达式本身。但是,可以将 `dprint` 实现为一个宏来获取输入表达式的程序片段。一个基本的实现如下:7
8
<!-- verify -macro12 -->9
<!-- cfg="--compile-macro" -->10
11
12
在解释每行代码之前,先测试这个宏可以达到预期的效果。首先,在当前目录下创建一个 `define` 文件夹,并在 `define` 文件夹中创建 `dprint.cj` 文件,将以上内容复制到 `dprint.cj` 文件中。另外在当前目录下创建 `main.cj`,包含以下测试代码:13
14
<!-- verify -macro12 -->15
16
17
请注意,得到的目录结构如下:18
19
20
在当前目录(`src`)下,运行编译命令:21
22
23
然后运行 `./main`,可以看到如下输出:24
25
<!-- verify -macro12 -->26
27
28
依次查看代码的每个部分:29
30
- 第 1 行:`macro package define`31
32
宏必须声明在独立的包中(不能和其他 public 函数一起),含有宏的包使用 `macro package` 来声明。这里声明了一个名为 `define` 的宏包。33
34
- 第 2 行:`import std.ast.*`35
36
实现宏需要的数据类型,例如 `Tokens` 和后面会讲到的语法节点类型,位于仓颉标准库的 `ast` 包中,因此任何宏的实现都需要首先引入 `ast` 包。37
38
- 第 3 行:`public macro dprint(input: Tokens): Tokens`39
40
在这里声明一个名为 `dprint` 的宏。由于这个宏是一个非属性宏(之后会解释这个概念),它接受一个类型为 `Tokens` 的参数。该输入代表传给宏的程序片段。宏的返回值也是一个程序片段。41
42
- 第 4 行:`let inputStr = input.toString()`43
44
在宏的实现中,首先将输入的程序片段转化为字符串。在前面的测试案例中,`inputStr` 成为 `"x"` 或 `"x + y"`45
46
- 第 5-7 行:`let result = quote(...)`47
48
这里 [`quote` 表达式](./Tokens_types_and_quote_expressions.md#quote-表达式和插值)是用于构造 [`Tokens`](./Tokens_types_and_quote_expressions.md#tokens-类型) 的一种表达式,它将括号内的程序片段转换为 `Tokens`。在 `quote` 的输入中,可以使用插值 `$(...)` 来将括号内的表达式转换为 `Tokens`,然后插入到 `quote` 构建的 `Tokens` 中。对于以上代码,`$(inputStr)` 中插入了 `inputStr` 字符串的值(包含字符串两端的引号),`$(input)` 中插入了 `input`,即输入的程序片段。因此,如果输入的表达式是 `x + y`,那么形成的`Tokens`为:49
50
```cangjie51
print("x + y" + " = ")52
println(x + y)53
```54
55
- 第 8 行:`return result`56
57
最后,将构造出来的代码片段返回,这两行代码片段将被编译,运行时将输出 `x + y = 5`。58
59
回顾 `dprint` 宏的定义,`dprint` 使用 `Tokens` 作为入参,并使用 `quote` 和插值构造了另一个 `Tokens` 作为返回值。为了使用宏,需要详细了解 `Tokens`、`quote` 和插值的概念,下面将分别介绍它们。Code 1 · cangjie
1
let x = 32
let y = 23
@dprint(x) // 打印 "x = 3"4
@dprint(x + y) // 打印 "x + y = 5"Code 2 · cangjie
1
macro package define2
3
import std.ast.*4
5
public macro dprint(input: Tokens): Tokens {6
let inputStr = input.toString()7
let result = quote(8
print($(inputStr) + " = ")9
println($(input)))10
return result11
}Code 3 · cangjie
1
import define.*2
3
main() {4
let x = 35
let y = 26
@dprint(x)7
@dprint(x + y)8
}Code 4 · text
1
// Directory layout.2
src3
|-- define4
| `-- dprint.cj5
`-- main.cjCode 5 · bash
1
cjc define/*.cj --compile-macro2
cjc main.cj -o mainCode 6 · text
1
x = 32
x + y = 5v1.1.0
Section Text
1
宏可以理解为一种特殊的函数。一般的函数在输入的值上进行计算,然后输出一个新的值,而宏的输入和输出都是程序本身。在输入一段程序后,输出一段新的程序,这段输出的程序随后用于编译和执行。为了把宏的调用和函数调用区分开来,在调用宏时需使用 `@` 加上宏的名称。2
3
如下示例代码希望实现在调试过程中打印某个表达式的值,同时打印出表达式本身。4
5
<!-- code_no_check -->6
7
8
显然,`dprint` 不能被写为常规的函数,因为函数只能获得输入表达式的值,不能获得输入表达式本身。但是,可以将 `dprint` 实现为一个宏来获取输入表达式的程序片段。一个基本的实现如下:9
10
<!-- verify -macro12 -->11
<!-- cfg="--compile-macro" -->12
13
14
在解释每行代码之前,先测试这个宏可以达到预期的效果。首先,在当前目录下创建一个 `define` 文件夹,并在 `define` 文件夹中创建 `dprint.cj` 文件,将以上内容复制到 `dprint.cj` 文件中。另外在当前目录下创建 `main.cj`,包含以下测试代码:15
16
<!-- verify -macro12 -->17
18
19
得到的目录结构如下:20
21
22
在当前目录(`src`)下,运行编译命令:23
24
25
然后运行 `./main`,可以看到如下输出:26
27
<!-- verify -macro12 -->28
29
30
依次查看代码的每个部分:31
32
- 第 1 行:`macro package define`33
34
宏必须声明在独立的包中(不能和其他 public 函数一起),含有宏的包使用 `macro package` 来声明。这里声明了一个名为 `define` 的宏包。35
36
- 第 2 行:`import std.ast.*`37
38
实现宏需要的数据类型,例如 `Tokens` 和后面会讲到的语法节点类型,位于仓颉标准库的 `ast` 包中,因此任何宏的实现都需要首先引入 `ast` 包。39
40
- 第 3 行:`public macro dprint(input: Tokens): Tokens`41
42
在这里声明一个名为 `dprint` 的宏。由于这个宏是一个非属性宏(之后会解释这个概念),它接受一个类型为 `Tokens` 的参数。该输入代表传给宏的程序片段。宏的返回值也是一个程序片段。43
44
- 第 4 行:`let inputStr = input.toString()`45
46
在宏的实现中,首先将输入的程序片段转化为字符串。在前面的测试案例中,`inputStr` 成为 `"x"` 或 `"x + y"`47
48
- 第 5-7 行:`let result = quote(...)`49
50
这里 [`quote` 表达式](./Tokens_types_and_quote_expressions.md#quote-表达式和插值)是用于构造 [`Tokens`](./Tokens_types_and_quote_expressions.md#tokens-类型) 的一种表达式,它将括号内的程序片段转换为 `Tokens`。在 `quote` 的输入中,可以使用插值 `$(...)` 来将括号内的表达式转换为 `Tokens`,然后插入到 `quote` 构建的 `Tokens` 中。对于以上代码,`$(inputStr)` 中插入了 `inputStr` 字符串的值(包含字符串两端的引号),`$(input)` 中插入了 `input`,即输入的程序片段。因此,如果输入的表达式是 `x + y`,那么形成的`Tokens`为:51
52
<!-- code_no_check -->53
54
```cangjie55
print("x + y" + " = ")56
println(x + y)57
```58
59
- 第 8 行:`return result`60
61
最后,将构造出来的代码片段返回,这两行代码片段将被编译,运行时将输出 `x + y = 5`。62
63
回顾 `dprint` 宏的定义,`dprint` 使用 `Tokens` 作为入参,并使用 `quote` 和插值构造了另一个 `Tokens` 作为返回值。为了使用宏,需要详细了解 `Tokens`、`quote` 和插值的概念,下面将分别介绍它们。Code 1 · cangjie
1
let x = 32
let y = 23
@dprint(x) // 打印 "x = 3"4
@dprint(x + y) // 打印 "x + y = 5"Code 2 · cangjie
1
macro package define2
3
import std.ast.*4
5
public macro dprint(input: Tokens): Tokens {6
let inputStr = input.toString()7
let result = quote(8
print($(inputStr) + " = ")9
println($(input)))10
return result11
}Code 3 · cangjie
1
import define.*2
3
main() {4
let x = 35
let y = 26
@dprint(x)7
@dprint(x + y)8
}Code 4 · text
1
// Directory layout.2
src3
|-- define4
| |-- dprint.cj5
|-- main.cjCode 5 · bash
1
cjc define/*.cj --compile-macro2
cjc main.cj -o mainCode 6 · text
1
x = 32
x + y = 5