aboutsummaryrefslogtreecommitdiff
path: root/doc/docs/zh
diff options
context:
space:
mode:
Diffstat (limited to 'doc/docs/zh')
-rw-r--r--doc/docs/zh/doc/assignment.md142
-rw-r--r--doc/docs/zh/doc/attributes.md46
-rw-r--r--doc/docs/zh/doc/backcalls.md69
-rw-r--r--doc/docs/zh/doc/comment.md30
-rw-r--r--doc/docs/zh/doc/comprehensions.md272
-rw-r--r--doc/docs/zh/doc/conditionals.md150
-rw-r--r--doc/docs/zh/doc/continue.md41
-rw-r--r--doc/docs/zh/doc/destructuring-assignment.md241
-rw-r--r--doc/docs/zh/doc/do.md66
-rw-r--r--doc/docs/zh/doc/for-loop.md125
-rw-r--r--doc/docs/zh/doc/function-literals.md494
-rw-r--r--doc/docs/zh/doc/function-stubs.md48
-rw-r--r--doc/docs/zh/doc/if-assignment.md73
-rwxr-xr-xdoc/docs/zh/doc/index.md5452
-rw-r--r--doc/docs/zh/doc/installation.md43
-rw-r--r--doc/docs/zh/doc/introduction.md103
-rw-r--r--doc/docs/zh/doc/licence-mit.md9
-rw-r--r--doc/docs/zh/doc/line-decorators.md44
-rw-r--r--doc/docs/zh/doc/literals.md111
-rw-r--r--doc/docs/zh/doc/macro.md276
-rw-r--r--doc/docs/zh/doc/module.md245
-rw-r--r--doc/docs/zh/doc/object-oriented-programming.md550
-rw-r--r--doc/docs/zh/doc/operator.md461
-rw-r--r--doc/docs/zh/doc/switch.md296
-rw-r--r--doc/docs/zh/doc/table-literals.md168
-rw-r--r--doc/docs/zh/doc/the-using-clause-controlling-destructive-assignment.md98
-rw-r--r--doc/docs/zh/doc/the-yuescript-library.md821
-rw-r--r--doc/docs/zh/doc/try.md105
-rw-r--r--doc/docs/zh/doc/usage.md110
-rw-r--r--doc/docs/zh/doc/varargs-assignment.md24
-rw-r--r--doc/docs/zh/doc/while-loop.md69
-rw-r--r--doc/docs/zh/doc/whitespace.md43
-rw-r--r--doc/docs/zh/doc/with-statement.md126
-rw-r--r--doc/docs/zh/index.md1
34 files changed, 5504 insertions, 5448 deletions
diff --git a/doc/docs/zh/doc/assignment.md b/doc/docs/zh/doc/assignment.md
new file mode 100644
index 0000000..49561fb
--- /dev/null
+++ b/doc/docs/zh/doc/assignment.md
@@ -0,0 +1,142 @@
1# 赋值
2
3  月之脚本中定义的变量是动态类型的,并默认为局部变量。但你可以通过 **local** 和 **global** 声明来改变声明变量的作用范围。
4
5```yuescript
6hello = "world"
7a, b, c = 1, 2, 3
8hello = 123 -- 访问现有的变量
9```
10<YueDisplay>
11
12```yue
13hello = "world"
14a, b, c = 1, 2, 3
15hello = 123 -- 访问现有的变量
16```
17
18</YueDisplay>
19
20## 执行更新
21
22&emsp;&emsp;你可以使用各式二进制运算符执行更新赋值。
23
24```yuescript
25x = 1
26x += 1
27x -= 1
28x *= 10
29x /= 10
30x %= 10
31s ..= "world" -- 如果执行更新的局部变量不存在,将新建一个局部变量
32arg or= "默认值"
33```
34<YueDisplay>
35
36```yue
37x = 1
38x += 1
39x -= 1
40x *= 10
41x /= 10
42x %= 10
43s ..= "world" -- 如果执行更新的局部变量不存在,将新建一个局部变量
44arg or= "默认值"
45```
46
47</YueDisplay>
48
49## 链式赋值
50
51&emsp;&emsp;你可以进行链式赋值,将多个项目赋予相同的值。
52
53```yuescript
54a = b = c = d = e = 0
55x = y = z = f!
56```
57<YueDisplay>
58
59```yue
60a = b = c = d = e = 0
61x = y = z = f!
62```
63
64</YueDisplay>
65
66## 显式声明局部变量
67
68```yuescript
69do
70 local a = 1
71 local *
72 print "预先声明后续所有变量为局部变量"
73 x = -> 1 + y + z
74 y, z = 2, 3
75 global instance = Item\new!
76
77do
78 local X = 1
79 local ^
80 print "只预先声明后续大写的变量为局部变量"
81 a = 1
82 B = 2
83```
84<YueDisplay>
85
86```yue
87do
88 local a = 1
89 local *
90 print "预先声明后续所有变量为局部变量"
91 x = -> 1 + y + z
92 y, z = 2, 3
93 global instance = Item\new!
94
95do
96 local X = 1
97 local ^
98 print "只预先声明后续大写的变量为局部变量"
99 a = 1
100 B = 2
101```
102
103</YueDisplay>
104
105## 显式声明全局变量
106
107```yuescript
108do
109 global a = 1
110 global *
111 print "预先声明所有变量为全局变量"
112 x = -> 1 + y + z
113 y, z = 2, 3
114
115do
116 global x = 1
117 global ^
118 print "只预先声明大写的变量为全局变量"
119 a = 1
120 B = 2
121 local Temp = "一个局部值"
122```
123<YueDisplay>
124
125```yue
126do
127 global a = 1
128 global *
129 print "预先声明所有变量为全局变量"
130 x = -> 1 + y + z
131 y, z = 2, 3
132
133do
134 global x = 1
135 global ^
136 print "只预先声明大写的变量为全局变量"
137 a = 1
138 B = 2
139 local Temp = "一个局部值"
140```
141
142</YueDisplay>
diff --git a/doc/docs/zh/doc/attributes.md b/doc/docs/zh/doc/attributes.md
new file mode 100644
index 0000000..3dc3b0b
--- /dev/null
+++ b/doc/docs/zh/doc/attributes.md
@@ -0,0 +1,46 @@
1# 属性
2
3&emsp;&emsp;月之脚本现在提供了 Lua 5.4 新增的叫做属性的语法支持。在月之脚本编译到的 Lua 目标版本低于 5.4 时,你仍然可以同时使用`const` 和 `close` 的属性声明语法,并获得常量检查和作用域回调的功能。
4
5```yuescript
6const a = 123
7close _ = <close>: -> print "超出范围。"
8```
9<YueDisplay>
10
11```yue
12const a = 123
13close _ = <close>: -> print "超出范围。"
14```
15
16</YueDisplay>
17
18&emsp;&emsp;你可以对进行解构得到的变量标记为常量。
19
20```yuescript
21const {:a, :b, c, d} = tb
22-- a = 1
23```
24<YueDisplay>
25
26```yue
27const {:a, :b, c, d} = tb
28-- a = 1
29```
30
31</YueDisplay>
32
33&emsp;&emsp;你也可以声明全局变量为常量。
34
35```yuescript
36global const Constant = 123
37-- Constant = 1
38```
39<YueDisplay>
40
41```yue
42global const Constant = 123
43-- Constant = 1
44```
45
46</YueDisplay>
diff --git a/doc/docs/zh/doc/backcalls.md b/doc/docs/zh/doc/backcalls.md
new file mode 100644
index 0000000..4c32fac
--- /dev/null
+++ b/doc/docs/zh/doc/backcalls.md
@@ -0,0 +1,69 @@
1# 反向回调
2
3&emsp;&emsp;反向回调用于减少函数回调的嵌套。它们使用指向左侧的箭头,并且默认会被定义为传入后续函数调用的最后一个参数。它的语法大部分与常规箭头函数相同,只是它指向另一方向,并且后续的函数体不需要进行缩进。
4
5```yuescript
6x <- f
7print "hello" .. x
8```
9<YueDisplay>
10
11```yue
12x <- f
13print "hello" .. x
14```
15
16</YueDisplay>
17
18&emsp;&emsp;月之脚本也提供了粗箭头反向回调函数。
19
20```yuescript
21<= f
22print @value
23```
24<YueDisplay>
25
26```yue
27<= f
28print @value
29```
30
31</YueDisplay>
32
33&emsp;&emsp;你可以通过一个占位符指定回调函数的传参位置。
34
35```yuescript
36(x) <- map _, [1, 2, 3]
37x * 2
38```
39<YueDisplay>
40
41```yue
42(x) <- map _, [1, 2, 3]
43x * 2
44```
45
46</YueDisplay>
47
48&emsp;&emsp;如果你希望在反向回调处理后继续编写更多其它的代码,可以使用 do 语句将不属于反向回调的代码分隔开。对于非粗箭头函数的反向回调,回调返回值的括号也是可以省略的。
49
50```yuescript
51result, msg = do
52 data <- readAsync "文件名.txt"
53 print data
54 info <- processAsync data
55 check info
56print result, msg
57```
58<YueDisplay>
59
60```yue
61result, msg = do
62 data <- readAsync "文件名.txt"
63 print data
64 info <- processAsync data
65 check info
66print result, msg
67```
68
69</YueDisplay>
diff --git a/doc/docs/zh/doc/comment.md b/doc/docs/zh/doc/comment.md
new file mode 100644
index 0000000..f6a7b4e
--- /dev/null
+++ b/doc/docs/zh/doc/comment.md
@@ -0,0 +1,30 @@
1# 注释
2
3```yuescript
4-- 我是一个注释
5
6str = --[[
7这是一个多行注释。
8没问题。
9]] strA \ -- 注释 1
10 .. strB \ -- 注释 2
11 .. strC
12
13func --[[端口]] 3000, --[[ip]] "192.168.1.1"
14```
15<YueDisplay>
16
17```yue
18-- 我是一个注释
19
20str = --[[
21这是一个多行注释。
22没问题。
23]] strA \ -- 注释 1
24 .. strB \ -- 注释 2
25 .. strC
26
27func --[[端口]] 3000, --[[ip]] "192.168.1.1"
28```
29
30</YueDisplay>
diff --git a/doc/docs/zh/doc/comprehensions.md b/doc/docs/zh/doc/comprehensions.md
new file mode 100644
index 0000000..fd25b22
--- /dev/null
+++ b/doc/docs/zh/doc/comprehensions.md
@@ -0,0 +1,272 @@
1# 推导式
2
3&emsp;&emsp;推导式为我们提供了一种便捷的语法,通过遍历现有对象并对其值应用表达式来构造出新的表格。月之脚本有两种推导式:列表推导式和表格推导式。它们最终都是产生 Lua 表格;列表推导式将值累积到类似数组的表格中,而表格推导式允许你在每次遍历时设置新表格的键和值。
4
5## 列表推导式
6
7&emsp;&emsp;以下操作创建了一个 items 表的副本,但所有包含的值都翻倍了。
8
9```yuescript
10items = [1, 2, 3, 4]
11doubled = [item * 2 for i, item in ipairs items]
12```
13<YueDisplay>
14
15```yue
16items = [1, 2, 3, 4]
17doubled = [item * 2 for i, item in ipairs items]
18```
19
20</YueDisplay>
21
22&emsp;&emsp;可以使用 `when` 子句筛选新表中包含的项目:
23
24```yuescript
25slice = [item for i, item in ipairs items when i > 1 and i < 3]
26```
27<YueDisplay>
28
29```yue
30slice = [item for i, item in ipairs items when i > 1 and i < 3]
31```
32
33</YueDisplay>
34
35&emsp;&emsp;因为我们常常需要迭代数值索引表的值,所以引入了 **\*** 操作符来做语法简化。doubled 示例可以重写为:
36
37```yuescript
38doubled = [item * 2 for item in *items]
39```
40<YueDisplay>
41
42```yue
43doubled = [item * 2 for item in *items]
44```
45
46</YueDisplay>
47
48&emsp;&emsp;在列表推导式中,你还可以使用展开操作符 `...` 来实现对列表嵌套层级进行扁平化的处理:
49
50```yuescript
51data =
52 a: [1, 2, 3]
53 b: [4, 5, 6]
54
55flat = [...v for k,v in pairs data]
56-- flat 现在为 [1, 2, 3, 4, 5, 6]
57```
58<YueDisplay>
59
60```yue
61data =
62 a: [1, 2, 3]
63 b: [4, 5, 6]
64
65flat = [...v for k,v in pairs data]
66-- flat 现在为 [1, 2, 3, 4, 5, 6]
67```
68
69</YueDisplay>
70
71&emsp;&emsp;for 和 when 子句可以根据需要进行链式操作。唯一的要求是推导式中至少要有一个 for 子句。
72
73&emsp;&emsp;使用多个 for 子句与使用多重循环的效果相同:
74
75```yuescript
76x_coords = [4, 5, 6, 7]
77y_coords = [9, 2, 3]
78
79points = [ [x, y] for x in *x_coords \
80for y in *y_coords]
81```
82<YueDisplay>
83
84```yue
85x_coords = [4, 5, 6, 7]
86y_coords = [9, 2, 3]
87
88points = [ [x, y] for x in *x_coords \
89for y in *y_coords]
90```
91
92</YueDisplay>
93
94&emsp;&emsp;在推导式中也可以使用简单的数值 for 循环:
95
96```yuescript
97evens = [i for i = 1, 100 when i % 2 == 0]
98```
99<YueDisplay>
100
101```yue
102evens = [i for i = 1, 100 when i % 2 == 0]
103```
104
105</YueDisplay>
106
107## 表格推导式
108
109&emsp;&emsp;表格推导式和列表推导式的语法非常相似,只是要使用 **{** 和 **}** 并从每次迭代中取两个值。
110
111&emsp;&emsp;以下示例生成了表格 thing 的副本:
112
113```yuescript
114thing = {
115 color: "red"
116 name: "fast"
117 width: 123
118}
119
120thing_copy = {k, v for k, v in pairs thing}
121```
122<YueDisplay>
123
124```yue
125thing = {
126 color: "red"
127 name: "fast"
128 width: 123
129}
130
131thing_copy = {k, v for k, v in pairs thing}
132```
133
134</YueDisplay>
135
136```yuescript
137no_color = {k, v for k, v in pairs thing when k != "color"}
138```
139<YueDisplay>
140
141```yue
142no_color = {k, v for k, v in pairs thing when k != "color"}
143```
144
145</YueDisplay>
146
147&emsp;&emsp;**\*** 操作符在表格推导式中能使用。在下面的例子里,我们为几个数字创建了一个平方根查找表。
148
149```yuescript
150numbers = [1, 2, 3, 4]
151sqrts = {i, math.sqrt i for i in *numbers}
152```
153<YueDisplay>
154
155```yue
156numbers = [1, 2, 3, 4]
157sqrts = {i, math.sqrt i for i in *numbers}
158```
159
160</YueDisplay>
161
162&emsp;&emsp;表格推导式中的键值元组也可以来自单个表达式,在这种情况下,表达式在计算后应返回两个值。第一个用作键,第二个用作值:
163
164&emsp;&emsp;在下面的示例中,我们将一些数组转换为一个表,其中每个数组里的第一项是键,第二项是值。
165
166```yuescript
167tuples = [ ["hello", "world"], ["foo", "bar"]]
168tbl = {unpack tuple for tuple in *tuples}
169```
170<YueDisplay>
171
172```yue
173tuples = [ ["hello", "world"], ["foo", "bar"]]
174tbl = {unpack tuple for tuple in *tuples}
175```
176
177</YueDisplay>
178
179## 切片
180
181&emsp;&emsp;当使用 **\*** 操作符时,月之脚本还提供了一种特殊的语法来限制要遍历的列表范围。这个语法也相当于在 for 循环中设置迭代边界和步长。
182
183&emsp;&emsp;下面的案例中,我们在切片中设置最小和最大边界,取索引在 1 到 5 之间(包括 1 和 5)的所有项目:
184
185```yuescript
186slice = [item for item in *items[1, 5]]
187```
188<YueDisplay>
189
190```yue
191slice = [item for item in *items[1, 5]]
192```
193
194</YueDisplay>
195
196&emsp;&emsp;切片的任意参数都可以省略,并会使用默认值。在如下示例中,如果省略了最大索引边界,它默认为表的长度。使下面的代码取除第一个元素之外的所有元素:
197
198```yuescript
199slice = [item for item in *items[2,]]
200```
201<YueDisplay>
202
203```yue
204slice = [item for item in *items[2,]]
205```
206
207</YueDisplay>
208
209&emsp;&emsp;如果省略了最小边界,便默认会设置为 1。这里我们只提供一个步长,并留下其他边界为空。这样会使得代码取出所有奇数索引的项目:(1, 3, 5, …)
210
211```yuescript
212slice = [item for item in *items[,,2]]
213```
214<YueDisplay>
215
216
217```yue
218slice = [item for item in *items[,,2]]
219```
220
221</YueDisplay>
222
223&emsp;&emsp;最小和最大边界都可以是负数,使用负数意味着边界是从表的末尾开始计算的。
224
225```yuescript
226-- 取最后4个元素
227slice = [item for item in *items[-4,-1]]
228```
229<YueDisplay>
230
231```yue
232-- 取最后4个元素
233slice = [item for item in *items[-4,-1]]
234```
235
236</YueDisplay>
237
238&emsp;&emsp;切片的步长也可以是负数,这意味着元素会以相反的顺序被取出。
239
240```yuescript
241reverse_slice = [item for item in *items[-1,1,-1]]
242```
243<YueDisplay>
244
245```yue
246reverse_slice = [item for item in *items[-1,1,-1]]
247```
248
249</YueDisplay>
250
251### 切片表达式
252
253&emsp;&emsp;切片也可以作为表达式来使用。可以用于获取一个表包含的子列表。
254
255```yuescript
256-- 取第2和第4个元素作为新的列表
257sub_list = items[2, 4]
258
259-- 取最后4个元素作为新的列表
260last_four_items = items[-4, -1]
261```
262<YueDisplay>
263
264```yue
265-- 取第2和第4个元素作为新的列表
266sub_list = items[2, 4]
267
268-- 取最后4个元素作为新的列表
269last_four_items = items[-4, -1]
270```
271
272</YueDisplay>
diff --git a/doc/docs/zh/doc/conditionals.md b/doc/docs/zh/doc/conditionals.md
new file mode 100644
index 0000000..e4b217a
--- /dev/null
+++ b/doc/docs/zh/doc/conditionals.md
@@ -0,0 +1,150 @@
1# 条件语句
2
3```yuescript
4have_coins = false
5if have_coins
6 print "有硬币"
7else
8 print "没有硬币"
9```
10<YueDisplay>
11
12```yue
13have_coins = false
14if have_coins
15 print "有硬币"
16else
17 print "没有硬币"
18```
19
20</YueDisplay>
21
22&emsp;&emsp;对于简单的语句,也可以使用简短的语法:
23
24```yuescript
25have_coins = false
26if have_coins then print "有硬币" else print "没有硬币"
27```
28<YueDisplay>
29
30```yue
31have_coins = false
32if have_coins then print "有硬币" else print "没有硬币"
33```
34
35</YueDisplay>
36
37&emsp;&emsp;因为 if 语句可以用作表达式,所以也可以这样写:
38
39```yuescript
40have_coins = false
41print if have_coins then "有硬币" else "没有硬币"
42```
43<YueDisplay>
44
45```yue
46have_coins = false
47print if have_coins then "有硬币" else "没有硬币"
48```
49
50</YueDisplay>
51
52&emsp;&emsp;条件语句也可以作为表达式用在返回语句和赋值语句中:
53
54```yuescript
55is_tall = (name) ->
56 if name == "Rob"
57 true
58 else
59 false
60
61message = if is_tall "Rob"
62 "我很高"
63else
64 "我不是很高"
65
66print message -- 打印: 我很高
67```
68<YueDisplay>
69
70```yue
71is_tall = (name) ->
72 if name == "Rob"
73 true
74 else
75 false
76
77message = if is_tall "Rob"
78 "我很高"
79else
80 "我不是很高"
81
82print message -- 打印: 我很高
83```
84
85</YueDisplay>
86
87&emsp;&emsp;if 的反义词是 unless(相当于 if not,正如“如果”对应“除非”):
88
89```yuescript
90unless os.date("%A") == "Monday"
91 print "今天不是星期一!"
92```
93<YueDisplay>
94
95
96```yue
97unless os.date("%A") == "Monday"
98 print "今天不是星期一!"
99```
100
101</YueDisplay>
102
103```yuescript
104print "你真幸运!" unless math.random! > 0.1
105```
106<YueDisplay>
107
108```yue
109print "你真幸运!" unless math.random! > 0.1
110```
111
112</YueDisplay>
113
114## 范围表达式
115
116&emsp;&emsp;你可以使用范围表达式来编写进行范围检查的代码。
117
118```yuescript
119a = 5
120
121if a in [1, 3, 5, 7]
122 print "检查离散值的相等性"
123
124if a in list
125 print "检查`a`是否在列表中"
126```
127<YueDisplay>
128
129```yue
130a = 5
131
132if a in [1, 3, 5, 7]
133 print "检查离散值的相等性"
134
135if a in list
136 print "检查`a`是否在列表中"
137```
138
139</YueDisplay>
140
141```yuescript
142print "你很幸运!" unless math.random! > 0.1
143```
144<YueDisplay>
145
146```yue
147print "你很幸运!" unless math.random! > 0.1
148```
149
150</YueDisplay>
diff --git a/doc/docs/zh/doc/continue.md b/doc/docs/zh/doc/continue.md
new file mode 100644
index 0000000..9fab8a3
--- /dev/null
+++ b/doc/docs/zh/doc/continue.md
@@ -0,0 +1,41 @@
1# continue 语句
2
3&emsp;&emsp;继续语句可以用来跳出当前的循环迭代。
4
5```yuescript
6i = 0
7while i < 10
8 i += 1
9 continue if i % 2 == 0
10 print i
11```
12<YueDisplay>
13
14```yue
15i = 0
16while i < 10
17 i += 1
18 continue if i % 2 == 0
19 print i
20```
21
22</YueDisplay>
23
24&emsp;&emsp;继续语句也可以与各种循环表达式一起使用,以防止当前的循环迭代结果累积到结果列表中。以下示例将数组表过滤为仅包含偶数的数组:
25
26```yuescript
27my_numbers = [1, 2, 3, 4, 5, 6]
28odds = for x in *my_numbers
29 continue if x % 2 == 1
30 x
31```
32<YueDisplay>
33
34```yue
35my_numbers = [1, 2, 3, 4, 5, 6]
36odds = for x in *my_numbers
37 continue if x % 2 == 1
38 x
39```
40
41</YueDisplay>
diff --git a/doc/docs/zh/doc/destructuring-assignment.md b/doc/docs/zh/doc/destructuring-assignment.md
new file mode 100644
index 0000000..205a1ff
--- /dev/null
+++ b/doc/docs/zh/doc/destructuring-assignment.md
@@ -0,0 +1,241 @@
1# 解构赋值
2
3&emsp;&emsp;解构赋值是一种快速从 Lua 表中按名称或基于数组中的位置提取值的方法。
4
5&emsp;&emsp;通常当你看到一个字面量的 Lua 表,比如 `{1,2,3}`,它位于赋值的右侧,因为它是一个值。解构赋值语句的写法就是交换了字面量 Lua 表的角色,并将其放在赋值语句的左侧。
6
7&emsp;&emsp;最好是通过示例来解释。以下是如何从表格中解包前两个值的方法:
8
9```yuescript
10thing = [1, 2]
11
12[a, b] = thing
13print a, b
14```
15<YueDisplay>
16
17
18```yue
19thing = [1, 2]
20
21[a, b] = thing
22print a, b
23```
24
25</YueDisplay>
26
27&emsp;&emsp;在解构表格字面量中,键代表从右侧读取的键,值代表读取的值将被赋予的名称。
28
29```yuescript
30obj = {
31 hello: "world"
32 day: "tuesday"
33 length: 20
34}
35
36{hello: hello, day: the_day} = obj
37print hello, the_day
38
39:day = obj -- 可以不带大括号进行简单的解构
40```
41<YueDisplay>
42
43```yue
44obj = {
45 hello: "world"
46 day: "tuesday"
47 length: 20
48}
49
50{hello: hello, day: the_day} = obj
51print hello, the_day
52
53:day = obj -- 可以不带大括号进行简单的解构
54```
55
56</YueDisplay>
57
58&emsp;&emsp;这也适用于嵌套的数据结构:
59
60```yuescript
61obj2 = {
62 numbers: [1,2,3,4]
63 properties: {
64 color: "green"
65 height: 13.5
66 }
67}
68
69{numbers: [first, second], properties: {color: color}} = obj2
70print first, second, color
71```
72<YueDisplay>
73
74```yue
75obj2 = {
76 numbers: [1,2,3,4]
77 properties: {
78 color: "green"
79 height: 13.5
80 }
81}
82
83{numbers: [first, second]} = obj2
84print first, second, color
85```
86
87</YueDisplay>
88
89&emsp;&emsp;如果解构语句很复杂,也可以任意将其分散在几行中。稍微复杂一些的示例:
90
91```yuescript
92{
93 numbers: [first, second]
94 properties: {
95 color: color
96 }
97} = obj2
98```
99<YueDisplay>
100
101```yue
102{
103 numbers: [first, second]
104 properties: {
105 color: color
106 }
107} = obj2
108```
109
110</YueDisplay>
111
112&emsp;&emsp;有时候我们会需要从 Lua 表中提取值并将它们赋给与键同名的局部变量。为了避免编写重复代码,我们可以使用 **:** 前缀操作符:
113
114```yuescript
115{:concat, :insert} = table
116```
117<YueDisplay>
118
119```yue
120{:concat, :insert} = table
121```
122
123</YueDisplay>
124
125&emsp;&emsp;这样的用法与导入语法有些相似。但我们可以通过混合语法重命名我们想要提取的字段:
126
127```yuescript
128{:mix, :max, random: rand} = math
129```
130<YueDisplay>
131
132```yue
133{:mix, :max, random: rand} = math
134```
135
136</YueDisplay>
137
138&emsp;&emsp;在进行解构时,你可以指定默认值,如:
139
140```yuescript
141{:name = "nameless", :job = "jobless"} = person
142```
143<YueDisplay>
144
145```yue
146{:name = "nameless", :job = "jobless"} = person
147```
148
149</YueDisplay>
150
151&emsp;&emsp;在进行列表解构时,你可以使用`_`作为占位符:
152
153```yuescript
154[_, two, _, four] = items
155```
156<YueDisplay>
157
158```yue
159[_, two, _, four] = items
160```
161
162</YueDisplay>
163
164## 范围解构
165
166&emsp;&emsp;你可以使用展开运算符 `...` 在列表解构中来捕获一个范围的值到子列表中。这在当你想要从列表的开头和结尾提取特定元素,同时收集中间的元素时非常有用。
167
168```yuescript
169orders = ["first", "second", "third", "fourth", "last"]
170[first, ...bulk, last] = orders
171print first -- 打印: first
172print bulk -- 打印: {"second", "third", "fourth"}
173print last -- 打印: last
174```
175<YueDisplay>
176
177```yue
178orders = ["first", "second", "third", "fourth", "last"]
179[first, ...bulk, last] = orders
180print first -- 打印: first
181print bulk -- 打印: {"second", "third", "fourth"}
182print last -- 打印: last
183```
184
185</YueDisplay>
186
187&emsp;&emsp;展开运算符可以用在不同的位置来捕获不同的范围,并且你可以使用 `_` 作为占位符来表示你想跳过对应范围的捕获:
188
189```yuescript
190-- 捕获第一个元素之后的所有元素
191[first, ...rest] = orders
192
193-- 捕获最后一个元素之前的所有元素
194[...start, last] = orders
195
196-- 跳过中间的元素,只捕获第一个和最后一个元素
197[first, ..._, last] = orders
198```
199<YueDisplay>
200
201```yue
202-- 捕获第一个元素之后的所有元素
203[first, ...rest] = orders
204
205-- 捕获最后一个元素之前的所有元素
206[...start, last] = orders
207
208-- 跳过中间的元素,只捕获第一个和最后一个元素
209[first, ..._, last] = orders
210```
211
212</YueDisplay>
213
214## 在其它地方的解构赋值
215
216&emsp;&emsp;解构赋值也可以出现在其它隐式进行赋值的地方。一个例子是用在 for 循环中:
217
218```yuescript
219tuples = [
220 ["hello", "world"]
221 ["egg", "head"]
222]
223
224for [left, right] in *tuples
225 print left, right
226```
227<YueDisplay>
228
229```yue
230tuples = [
231 ["hello", "world"]
232 ["egg", "head"]
233]
234
235for [left, right] in *tuples
236 print left, right
237```
238
239</YueDisplay>
240
241&emsp;&emsp;我们知道数组表中的每个元素都是一个两项的元组,所以我们可以直接在 for 语句的名称子句中使用解构来解包它。
diff --git a/doc/docs/zh/doc/do.md b/doc/docs/zh/doc/do.md
new file mode 100644
index 0000000..52dca26
--- /dev/null
+++ b/doc/docs/zh/doc/do.md
@@ -0,0 +1,66 @@
1# do 语句
2
3&emsp;&emsp;当用作语句时,do 语句的作用就像在 Lua 中差不多。
4
5```yuescript
6do
7 var = "hello"
8 print var
9print var -- 这里是nil
10```
11<YueDisplay>
12
13```yue
14do
15 var = "hello"
16 print var
17print var -- 这里是nil
18```
19
20</YueDisplay>
21
22&emsp;&emsp;月之脚本的 **do** 也可以用作表达式。允许你将多行代码的处理合并为一个表达式,并将 do 语句代码块的最后一个语句作为表达式返回的结果。
23
24```yuescript
25counter = do
26 i = 0
27 ->
28 i += 1
29 i
30
31print counter!
32print counter!
33```
34<YueDisplay>
35
36```yue
37counter = do
38 i = 0
39 ->
40 i += 1
41 i
42
43print counter!
44print counter!
45```
46
47</YueDisplay>
48
49```yuescript
50tbl = {
51 key: do
52 print "分配键值!"
53 1234
54}
55```
56<YueDisplay>
57
58```yue
59tbl = {
60 key: do
61 print "分配键值!"
62 1234
63}
64```
65
66</YueDisplay>
diff --git a/doc/docs/zh/doc/for-loop.md b/doc/docs/zh/doc/for-loop.md
new file mode 100644
index 0000000..212ff81
--- /dev/null
+++ b/doc/docs/zh/doc/for-loop.md
@@ -0,0 +1,125 @@
1# for 循环
2
3&emsp;&emsp;Lua 中有两种 for 循环形式,数字型和通用型:
4
5```yuescript
6for i = 10, 20
7 print i
8
9for k = 1, 15, 2 -- 提供了一个遍历的步长
10 print k
11
12for key, value in pairs object
13 print key, value
14```
15<YueDisplay>
16
17```yue
18for i = 10, 20
19 print i
20
21for k = 1, 15, 2 -- 提供了一个遍历的步长
22 print k
23
24for key, value in pairs object
25 print key, value
26```
27
28</YueDisplay>
29
30&emsp;&emsp;可以使用切片和 **\*** 操作符,就像在列表推导中一样:
31
32```yuescript
33for item in *items[2, 4]
34 print item
35```
36<YueDisplay>
37
38```yue
39for item in *items[2, 4]
40 print item
41```
42
43</YueDisplay>
44
45&emsp;&emsp;当代码语句只有一行时,循环语句也都可以写作更短的语法:
46
47```yuescript
48for item in *items do print item
49
50for j = 1, 10, 3 do print j
51```
52<YueDisplay>
53
54```yue
55for item in *items do print item
56
57for j = 1, 10, 3 do print j
58```
59
60</YueDisplay>
61
62&emsp;&emsp;for 循环也可以用作表达式。for 循环主体中的最后一条语句会被强制转换为一个返回值的表达式,并会将表达式计算结果的值追加到一个作为结果的数组表中。
63
64&emsp;&emsp;将每个偶数加倍:
65
66```yuescript
67doubled_evens = for i = 1, 20
68 if i % 2 == 0
69 i * 2
70 else
71 i
72```
73<YueDisplay>
74
75```yue
76doubled_evens = for i = 1, 20
77 if i % 2 == 0
78 i * 2
79 else
80 i
81```
82
83</YueDisplay>
84
85&emsp;&emsp;此外,for 循环还支持带返回值的 break 语句,这样循环本身就可以作为一个表达式,在满足条件时提前退出并返回有意义的结果。
86
87&emsp;&emsp;例如,查找第一个大于 10 的数字:
88
89```yuescript
90first_large = for n in *numbers
91 break n if n > 10
92```
93<YueDisplay>
94
95```yue
96first_large = for n in *numbers
97 break n if n > 10
98```
99
100</YueDisplay>
101
102&emsp;&emsp;你还可以结合 for 循环表达式与 continue 语句来过滤值。
103
104&emsp;&emsp;注意出现在函数体末尾的 for 循环,不会被当作是一个表达式并将循环结果累积到一个列表中作为返回值(相反,函数将返回 nil)。如果要函数末尾的循环转换为列表表达式,可以显式地使用返回语句加 for 循环表达式。
105
106```yuescript
107func_a = -> for i = 1, 10 do print i
108func_b = -> return for i = 1, 10 do i
109
110print func_a! -- 打印 nil
111print func_b! -- 打印 table 对象
112```
113<YueDisplay>
114
115```yue
116func_a = -> for i = 1, 10 do print i
117func_b = -> return for i = 1, 10 do i
118
119print func_a! -- 打印 nil
120print func_b! -- 打印 table 对象
121```
122
123</YueDisplay>
124
125&emsp;&emsp;这样做是为了避免在不需要返回循环结果的函数,创建无效的返回值表格。
diff --git a/doc/docs/zh/doc/function-literals.md b/doc/docs/zh/doc/function-literals.md
new file mode 100644
index 0000000..efe920b
--- /dev/null
+++ b/doc/docs/zh/doc/function-literals.md
@@ -0,0 +1,494 @@
1# 函数字面量
2
3&emsp;&emsp;所有函数都是使用月之脚本的函数表达式创建的。一个简单的函数可以用箭头表示为:**->**。
4
5```yuescript
6my_function = ->
7my_function() -- 调用空函数
8```
9<YueDisplay>
10
11```yue
12my_function = ->
13my_function() -- 调用空函数
14```
15
16</YueDisplay>
17
18&emsp;&emsp;函数体可以是紧跟在箭头后的一个语句,或者是在后面的行上使用同样缩进的一系列语句:
19
20```yuescript
21func_a = -> print "你好,世界"
22
23func_b = ->
24 value = 100
25 print "这个值是:", value
26```
27<YueDisplay>
28
29```yue
30func_a = -> print "你好,世界"
31
32func_b = ->
33 value = 100
34 print "这个值是:", value
35```
36
37</YueDisplay>
38
39&emsp;&emsp;如果一个函数没有参数,可以使用 **\!** 操作符调用它,而不是空括号。使用 **\!** 调用没有参数的函数是推荐的写法。
40
41```yuescript
42func_a!
43func_b()
44```
45<YueDisplay>
46
47```yue
48func_a!
49func_b()
50```
51
52</YueDisplay>
53
54&emsp;&emsp;带有参数的函数可以通过在箭头前加上括号中的参数名列表来进行创建:
55
56```yuescript
57sum = (x, y) -> print "数字的和", x + y
58```
59<YueDisplay>
60
61```yue
62sum = (x, y) -> print "数字的和", x + y
63```
64
65</YueDisplay>
66
67&emsp;&emsp;函数可以通过在函数名后列出参数来调用。当对函数做嵌套的调用时,后面列出的参数会应用于左侧最近的函数。
68
69```yuescript
70sum 10, 20
71print sum 10, 20
72
73a b c "a", "b", "c"
74```
75<YueDisplay>
76
77```yue
78sum 10, 20
79print sum 10, 20
80
81a b c "a", "b", "c"
82```
83
84</YueDisplay>
85
86&emsp;&emsp;为了避免在调用函数时产生歧义,也可以使用括号将参数括起来。比如在以下的例子中是必需的,这样才能确保参数被传入到正确的函数。
87
88```yuescript
89print "x:", sum(10, 20), "y:", sum(30, 40)
90```
91<YueDisplay>
92
93```yue
94print "x:", sum(10, 20), "y:", sum(30, 40)
95```
96
97</YueDisplay>
98
99&emsp;&emsp;注意:函数名与开始括号之间不能有任何空格。
100
101&emsp;&emsp;函数会将函数体中的最后一个语句强制转换为返回语句,这被称作隐式返回:
102
103```yuescript
104sum = (x, y) -> x + y
105print "数字的和是", sum 10, 20
106```
107<YueDisplay>
108
109```yue
110sum = (x, y) -> x + y
111print "数字的和是", sum 10, 20
112```
113
114</YueDisplay>
115
116&emsp;&emsp;如果你需要做显式返回,可以使用 return 关键字:
117
118```yuescript
119sum = (x, y) -> return x + y
120```
121<YueDisplay>
122
123```yue
124sum = (x, y) -> return x + y
125```
126
127</YueDisplay>
128
129&emsp;&emsp;就像在Lua中一样,函数可以返回多个值。最后一个语句必须是由逗号分隔的值列表:
130
131```yuescript
132mystery = (x, y) -> x + y, x - y
133a, b = mystery 10, 20
134```
135<YueDisplay>
136
137```yue
138mystery = (x, y) -> x + y, x - y
139a, b = mystery 10, 20
140```
141
142</YueDisplay>
143
144## 粗箭头
145
146&emsp;&emsp;因为在 Lua 中调用方法时,经常习惯将对象作为第一个参数传入,所以月之脚本提供了一种特殊的语法来创建自动包含 self 参数的函数。
147
148```yuescript
149func = (num) => @value + num
150```
151<YueDisplay>
152
153```yue
154func = (num) => @value + num
155```
156
157</YueDisplay>
158
159## 参数默认值
160
161&emsp;&emsp;可以为函数的参数提供默认值。如果参数的值为 nil,则确定该参数为空。任何具有默认值的 nil 参数在函数体运行之前都会被替换。
162
163```yuescript
164my_function = (name = "某物", height = 100) ->
165 print "你好,我是", name
166 print "我的高度是", height
167```
168<YueDisplay>
169
170```yue
171my_function = (name = "某物", height = 100) ->
172 print "你好,我是", name
173 print "我的高度是", height
174```
175
176</YueDisplay>
177
178&emsp;&emsp;函数参数的默认值表达式在函数体中会按参数声明的顺序进行计算。因此,在默认值的表达式中可以访问先前声明的参数。
179
180```yuescript
181some_args = (x = 100, y = x + 1000) ->
182 print x + y
183```
184<YueDisplay>
185
186```yue
187some_args = (x = 100, y = x + 1000) ->
188 print x + y
189```
190
191</YueDisplay>
192
193## 注意事项
194
195&emsp;&emsp;由于月之脚本支持无需括号的表达式式函数调用,因此为了避免因空白字符造成的解析歧义,需要进行一些限制。
196
197&emsp;&emsp;减号(-)在表达式中既可以作为一元取反操作符,也可以作为二元减法操作符。请注意下面这些示例的编译方式:
198
199```yuescript
200a = x - 10
201b = x-10
202c = x -y
203d = x- z
204```
205<YueDisplay>
206
207```yue
208a = x - 10
209b = x-10
210c = x -y
211d = x- z
212```
213
214</YueDisplay>
215
216&emsp;&emsp;当函数调用的第一个参数是字符串字面量时,可以通过空白控制其优先级。在 Lua 中,常见的写法是调用仅有一个字符串或表字面量参数的函数时省略括号。
217
218&emsp;&emsp;当变量名和字符串字面量之间没有空格时,函数的调用优先级高于后续表达式,因此此时无法再传入其他参数。
219
220&emsp;&emsp;当变量名和字符串字面量之间有空格时,字符串字面量会作为后续表达式(如果存在)的参数,这样可以传递参数列表。
221
222```yuescript
223x = func"hello" + 100
224y = func "hello" + 100
225```
226<YueDisplay>
227
228```yue
229x = func"hello" + 100
230y = func "hello" + 100
231```
232
233</YueDisplay>
234
235## 多行参数
236
237&emsp;&emsp;当调用接收大量参数的函数时,将参数列表分成多行是很方便的。由于月之脚本语言对空白字符的敏感性,做参数列表的分割时务必要小心。
238
239&emsp;&emsp;如果要将参数列表写到下一行,那么当前行必须以逗号结束。并且下一行的缩进必须比当前的缩进多。一旦做了参数的缩进,所有其他参数列表的行必须保持相同的缩进级别,以成为参数列表的一部分。
240
241```yuescript
242my_func 5, 4, 3,
243 8, 9, 10
244
245cool_func 1, 2,
246 3, 4,
247 5, 6,
248 7, 8
249```
250<YueDisplay>
251
252```yue
253my_func 5, 4, 3,
254 8, 9, 10
255
256cool_func 1, 2,
257 3, 4,
258 5, 6,
259 7, 8
260```
261
262</YueDisplay>
263
264&emsp;&emsp;这种调用方式可以做嵌套。并通过缩进级别来确定参数属于哪一个函数。
265
266```yuescript
267my_func 5, 6, 7,
268 6, another_func 6, 7, 8,
269 9, 1, 2,
270 5, 4
271```
272<YueDisplay>
273
274```yue
275my_func 5, 6, 7,
276 6, another_func 6, 7, 8,
277 9, 1, 2,
278 5, 4
279```
280
281</YueDisplay>
282
283&emsp;&emsp;因为 Lua 表也使用逗号作为分隔符,这种缩进语法有助于让值成为参数列表的一部分,而不是 Lua 表的一部分。
284
285```yuescript
286x = [
287 1, 2, 3, 4, a_func 4, 5,
288 5, 6,
289 8, 9, 10
290]
291```
292<YueDisplay>
293
294```yue
295x = [
296 1, 2, 3, 4, a_func 4, 5,
297 5, 6,
298 8, 9, 10
299]
300```
301
302</YueDisplay>
303
304&emsp;&emsp;有个不常见的写法可以注意一下,如果我们将在后面使用较低的缩进,我们可以为函数参数提供更深的缩进来区分列表的归属。
305
306```yuescript
307y = [ my_func 1, 2, 3,
308 4, 5,
309 5, 6, 7
310]
311```
312<YueDisplay>
313
314```yue
315y = [ my_func 1, 2, 3,
316 4, 5,
317 5, 6, 7
318]
319```
320
321</YueDisplay>
322
323&emsp;&emsp;对于其它有代码块跟随的语句,比如条件语句,也可以通过小心安排缩进来做类似的事。比如我们可以通过调整缩进级别来控制一些值归属于哪个语句:
324
325```yuescript
326if func 1, 2, 3,
327 "你好",
328 "世界"
329 print "你好"
330 print "我在if内部"
331
332if func 1, 2, 3,
333 "你好",
334 "世界"
335 print "hello"
336 print "我在if内部"
337```
338<YueDisplay>
339
340```yue
341if func 1, 2, 3,
342 "你好",
343 "世界"
344 print "你好"
345 print "我在if内部"
346
347if func 1, 2, 3,
348 "你好",
349 "世界"
350 print "你好"
351 print "我在if内部"
352```
353
354</YueDisplay>
355
356## 参数解构
357
358&emsp;&emsp;月之脚本支持在函数形参位置对传入对象进行解构。适用两类解构表子面量:
359
360- 使用 {} 包裹的字面量/对象形参,支持提供获得空字段时的默认值(例如 {:a, :b}、{a: a1 = 123})。
361
362- 无 {} 包裹、以键值/简写键序列开头,直至遇到其它表达式终止(例如 :a, b: b1, :c),表示从同一个对象中解构多个字段。
363
364```yuescript
365f1 = (:a, :b, :c) ->
366 print a, b, c
367
368f1 a: 1, b: "2", c: {}
369
370f2 = ({a: a1 = 123, :b = 'abc'}, c = {}) ->
371 print a1, b, c
372
373arg1 = {a: 0}
374f2 arg1, arg2
375```
376<YueDisplay>
377
378```yue
379f1 = (:a, :b, :c) ->
380 print a, b, c
381
382f1 a: 1, b: "2", c: {}
383
384f2 = ({a: a1 = 123, :b = 'abc'}, c = {}) ->
385 print a1, b, c
386
387arg1 = {a: 0}
388f2 arg1, arg2
389```
390
391</YueDisplay>
392
393## 前置返回表达式
394
395&emsp;&emsp;在深度嵌套的函数体中,为了提升返回值的可读性及编写便利性,我们新增了 “前置返回表达式” 语法。其形式如下:
396
397```yuescript
398findFirstEven = (list): nil ->
399 for item in *list
400 if type(item) == "table"
401 for sub in *item
402 if sub % 2 == 0
403 return sub
404```
405<YueDisplay>
406
407```yue
408findFirstEven = (list): nil ->
409 for item in *list
410 if type(item) == "table"
411 for sub in *item
412 if sub % 2 == 0
413 return sub
414```
415
416</YueDisplay>
417
418&emsp;&emsp;这个写法等价于:
419
420```yuescript
421findFirstEven = (list) ->
422 for item in *list
423 if type(item) == "table"
424 for sub in *item
425 if sub % 2 == 0
426 return sub
427 nil
428```
429<YueDisplay>
430
431```yue
432findFirstEven = (list) ->
433 for item in *list
434 if type(item) == "table"
435 for sub in *item
436 if sub % 2 == 0
437 return sub
438 nil
439```
440
441</YueDisplay>
442
443&emsp;&emsp;唯一的区别在于:你可以将函数的返回值表达式提前写在 `->` 或 `=>` 前,用以指示该函数应隐式返回该表达式的值。这样即使在多层循环或条件判断的场景下,也无需编写尾行悬挂的返回表达式,逻辑结构会更加直观清晰。
444
445## 命名变长参数
446
447&emsp;&emsp;你可以使用 `(...t) ->` 语法来将变长参数自动存储到一个命名表中。这个表会包含所有传入的参数(包括 `nil` 值),并且会在表的 `n` 字段中存储实际传入的参数个数(包括 `nil` 值在内的个数)。
448
449```yuescript
450f = (...t) ->
451 print "参数个数:", t.n
452 print "表长度:", #t
453 for i = 1, t.n
454 print t[i]
455
456f 1, 2, 3
457f "a", "b", "c", "d"
458f!
459
460-- 处理包含 nil 的情况
461process = (...args) ->
462 sum = 0
463 for i = 1, args.n
464 if args[i] != nil and type(args[i]) == "number"
465 sum += args[i]
466 sum
467
468process 1, nil, 3, nil, 5
469```
470<YueDisplay>
471
472```yue
473f = (...t) ->
474 print "参数个数:", t.n
475 print "表长度:", #t
476 for i = 1, t.n
477 print t[i]
478
479f 1, 2, 3
480f "a", "b", "c", "d"
481f!
482
483-- 处理包含 nil 的情况
484process = (...args) ->
485 sum = 0
486 for i = 1, args.n
487 if args[i] != nil and type(args[i]) == "number"
488 sum += args[i]
489 sum
490
491process 1, nil, 3, nil, 5
492```
493
494</YueDisplay>
diff --git a/doc/docs/zh/doc/function-stubs.md b/doc/docs/zh/doc/function-stubs.md
new file mode 100644
index 0000000..af08feb
--- /dev/null
+++ b/doc/docs/zh/doc/function-stubs.md
@@ -0,0 +1,48 @@
1# 函数存根
2
3&emsp;&emsp;在编程中,将对象的方法作为函数类型的值进行传递是一种常见做法,尤其是在将实例方法作为回调函数传递给其他函数的情形中。当目标函数需要将该对象作为其第一个参数时,我们需要找到一种方式将对象和函数绑定在一起,以便能够正确地调用该函数。
4
5&emsp;&emsp;函数存根(stub)语法提供了一种便捷的方法来创建一个新的闭包函数,这个函数将对象和原函数绑定在一起。这样,当调用这个新创建的函数时,它会在正确的对象上下文中执行原有的函数。
6
7&emsp;&emsp;这种语法类似于使用 \ 操作符调用实例方法的方式,区别在于,这里不需要在 \ 操作符后面附加参数列表。
8
9```yuescript
10my_object = {
11 value: 1000
12 write: => print "值为:", @value
13}
14
15run_callback = (func) ->
16 print "运行回调..."
17 func!
18
19-- 这样写不起作用:
20-- 函数没有引用my_object
21run_callback my_object.write
22
23-- 函数存根语法
24-- 让我们把对象捆绑到一个新函数中
25run_callback my_object\write
26```
27<YueDisplay>
28
29```yue
30my_object = {
31 value: 1000
32 write: => print "值为:", @value
33}
34
35run_callback = (func) ->
36 print "运行回调..."
37 func!
38
39-- 这样写不起作用:
40-- 函数没有引用my_object
41run_callback my_object.write
42
43-- 函数存根语法
44-- 让我们把对象捆绑到一个新函数中
45run_callback my_object\write
46```
47
48</YueDisplay>
diff --git a/doc/docs/zh/doc/if-assignment.md b/doc/docs/zh/doc/if-assignment.md
new file mode 100644
index 0000000..e4077e1
--- /dev/null
+++ b/doc/docs/zh/doc/if-assignment.md
@@ -0,0 +1,73 @@
1# If 赋值
2
3&emsp;&emsp;`if` 和 `elseif` 代码块可以在条件表达式的位置进行赋值。在代码执行到要计算条件时,会首先进行赋值计算,并使用赋与的值作为分支判断的条件。赋值的变量仅在条件分支的代码块内有效,这意味着如果值不是真值,那么它就不会被用到。注意,你必须使用“海象运算符” `:=` 而不是 `=` 来做赋值。
4
5```yuescript
6if user := database.find_user "moon"
7 print user.name
8```
9<YueDisplay>
10
11```yue
12if user := database.find_user "moon"
13 print user.name
14```
15
16</YueDisplay>
17
18```yuescript
19if hello := os.getenv "hello"
20 print "你有 hello", hello
21elseif world := os.getenv "world"
22 print "你有 world", world
23else
24 print "什么都没有 :("
25```
26<YueDisplay>
27
28```yue
29if hello := os.getenv "hello"
30 print "你有 hello", hello
31elseif world := os.getenv "world"
32 print "你有 world", world
33else
34 print "什么都没有 :("
35```
36
37</YueDisplay>
38
39&emsp;&emsp;使用多个返回值的 If 赋值。只有第一个值会被检查,其他值都有同样的作用域。
40
41```yuescript
42if success, result := pcall -> "无报错地获取结果"
43 print result -- 变量 result 是有作用域的
44print "好的"
45```
46<YueDisplay>
47
48```yue
49if success, result := pcall -> "无报错地获取结果"
50 print result -- 变量 result 是有作用域的
51print "好的"
52```
53
54</YueDisplay>
55
56## While 赋值
57
58&emsp;&emsp;你可以在 while 循环中同样使用赋值来获取循环条件的值。
59
60```yuescript
61while byte := stream\read_one!
62 -- 对 byte 做一些操作
63 print byte
64```
65<YueDisplay>
66
67```yue
68while byte := stream\read_one!
69 -- 对 byte 做一些操作
70 print byte
71```
72
73</YueDisplay>
diff --git a/doc/docs/zh/doc/index.md b/doc/docs/zh/doc/index.md
index 5250325..7611fc6 100755
--- a/doc/docs/zh/doc/index.md
+++ b/doc/docs/zh/doc/index.md
@@ -1,5453 +1,11 @@
1--- 1---
2sidebar: auto
3title: 参考手册 2title: 参考手册
4--- 3---
5 4
6# 月之脚本 5# 月之脚本文档
7 6
8<img src="/image/yuescript.svg" width="250px" height="250px" alt="logo" style="padding-top: 3em;"/> 7<img src="/image/yuescript.svg" width="250px" height="250px" alt="logo" style="padding-top: 3em;padding-bottom: 2em;"/>
9 8
10## 介绍 9欢迎来到 <b>月之脚本(YueScript)</b> 官方文档!<br/>
11 10这里收录了语言特性、用法、参考示例和资源。<br/>
12月之脚本(YueScript)是一种动态语言,可以编译为 Lua。它是 [MoonScript](https://github.com/leafo/moonscript) 的方言。用月之脚本编写的代码既有表现力又非常简洁。它适合编写一些更易于维护的代码,并在嵌入 Lua 的环境中运行,如游戏或网站服务器。 11请选择左侧的章节索引或目录,开启你的月之脚本之旅 ☽
13
14Yue(月)是中文中“月亮”的名称。
15
16### 月之脚本概览
17```yuescript
18-- 导入语法
19import p, to_lua from "yue"
20
21-- 隐式对象
22inventory =
23 equipment:
24 - "sword"
25 - "shield"
26 items:
27 - name: "potion"
28 count: 10
29 - name: "bread"
30 count: 3
31
32-- 列表推导
33map = (arr, action) ->
34 [action item for item in *arr]
35
36filter = (arr, cond) ->
37 [item for item in *arr when cond item]
38
39reduce = (arr, init, action): init ->
40 init = action init, item for item in *arr
41
42-- 管道操作符
43[1, 2, 3]
44 |> map (x) -> x * 2
45 |> filter (x) -> x > 4
46 |> reduce 0, (a, b) -> a + b
47 |> print
48
49-- 元表操作
50apple =
51 size: 15
52 <index>:
53 color: 0x00ffff
54
55with apple
56 p .size, .color, .<index> if .<>?
57
58-- 类似js的导出语法
59export 🌛 = "月之脚本"
60```
61<YueDisplay>
62
63```yue
64-- 导入语法
65import p, to_lua from "yue"
66
67-- 隐式对象
68inventory =
69 equipment:
70 - "sword"
71 - "shield"
72 items:
73 - name: "potion"
74 count: 10
75 - name: "bread"
76 count: 3
77
78-- 列表推导
79map = (arr, action) ->
80 [action item for item in *arr]
81
82filter = (arr, cond) ->
83 [item for item in *arr when cond item]
84
85reduce = (arr, init, action): init ->
86 init = action init, item for item in *arr
87
88-- 管道操作符
89[1, 2, 3]
90 |> map (x) -> x * 2
91 |> filter (x) -> x > 4
92 |> reduce 0, (a, b) -> a + b
93 |> print
94
95-- 元表操作
96apple =
97 size: 15
98 <index>:
99 color: 0x00ffff
100
101with apple
102 p .size, .color, .<index> if .<>?
103
104-- 类似js的导出语法
105export 🌛 = "月之脚本"
106```
107
108</YueDisplay>
109
110## 安装
111
112* **Lua 模块**
113
114&emsp;安装 [luarocks](https://luarocks.org),一个 Lua 模块的包管理器。然后作为 Lua 模块和可执行文件安装它:
115
116```
117> luarocks install yuescript
118```
119
120&emsp;或者你可以自己构建 `yue.so` 文件:
121
122```
123> make shared LUAI=/usr/local/include/lua LUAL=/usr/local/lib/lua
124```
125
126&emsp;然后从路径 **bin/shared/yue.so** 获取二进制文件。
127
128* **构建二进制工具**
129
130&emsp;克隆项目仓库,然后构建并安装可执行文件:
131```
132> make install
133```
134
135&emsp;构建不带宏功能的月之脚本编译工具:
136```
137> make install NO_MACRO=true
138```
139
140&emsp;构建不带内置Lua二进制文件的月之脚本编译工具:
141```
142> make install NO_LUA=true
143```
144
145* **下载预编译的二进制程序**
146
147&emsp;你可以下载预编译的二进制程序,包括兼容不同 Lua 版本的二进制可执行文件和库文件。
148
149&emsp;在[这里](https://github.com/IppClub/YueScript/releases)下载预编译的二进制程序。
150
151## 使用方法
152
153### Lua 模块
154
155在 Lua 中使用月之脚本模块:
156
157* **用法 1**
158在 Lua 中引入 "你的脚本入口文件.yue"。
159```Lua
160require("yue")("你的脚本入口文件")
161```
162当你在同一路径下把 "你的脚本入口文件.yue" 编译成了 "你的脚本入口文件.lua" 时,仍然可以使用这个代码加载 .lua 代码文件。在其余的月之脚本文件中,只需正常使用 **require** 或 **import** 进行脚本引用即可。错误消息中的代码行号也会被正确处理。
163
164* **用法 2**
165手动引入月之脚本模块并重写错误消息来帮助调试。
166```lua
167local yue = require("yue")
168yue.insert_loaders()
169local success, result = xpcall(function()
170 return require("yuescript_module_name")
171end, function(err)
172 return yue.traceback(err)
173end)
174```
175
176* **用法 3**
177在 Lua 中使用月之脚本编译器功能。
178```lua
179local yue = require("yue")
180local codes, err, globals = yue.to_lua([[
181f = ->
182 print "hello world"
183f!
184]],{
185 implicit_return_root = true,
186 reserve_line_number = true,
187 lint_global = true,
188 space_over_tab = false,
189 options = {
190 target = "5.4",
191 path = "/script"
192 }
193})
194```
195
196### 月之脚本编译工具
197
198使用月之脚本编译工具:
199```sh
200命令行用法: yue
201 [选项] [<文件/目录>] ...
202 yue -e <代码或文件> [参数...]
203 yue -w [<目录>] [选项]
204 yue -
205
206说明:
207 - '-' 或 '--' 必须作为唯一且第一个参数,用于读取标准输入。
208 - '-o/--output' 不能与多个输入文件一起使用。
209 - '-w/--watch' 仅能用于目录,不能用于单个文件。
210 - 使用 '-e/--execute' 时,后续的参数将作为脚本参数传递。
211
212选项:
213 -h, --help 显示帮助信息并退出
214 -e <字符串>, --execute <字符串> 执行文件或原始代码
215 -m, --minify 生成压缩(最小化)代码
216 -r, --rewrite 重写输出以匹配原始代码行号
217 -t <目标路径>, --output-to <目标路径>
218 指定编译后文件的输出路径
219 -o <文件>, --output <文件> 将输出写入文件
220 -p, --print 输出到标准输出
221 -b, --benchmark 输出编译耗时(不写入文件)
222 -g, --globals 显示用到的全局变量及其所在的名称、行号、列号
223 -s, --spaces 用空格代替制表符(tab)输出代码
224 -l, --line-numbers 输出源代码的行号
225 -j, --no-implicit-return 禁用文件末尾的隐式返回
226 -c, --reserve-comments 保留源代码中的注释
227 -w [<目录>], --watch [<目录>]
228 监视目录变化并自动编译
229 -v, --version 显示版本信息
230 - 从标准输入读取,输出到标准输出(仅能作为唯一参数)
231 -- 等同于 '-',为兼容旧版本保留
232
233 --target <版本> 指定生成代码的 Lua 版本 (只能为 5.1 ~ 5.5)
234 --path <路径字符串> 附加一个 Lua 搜索路径到 package.path
235 --<键>=<值> 以 key=value 形式传递编译器选项(保持已有用法)
236
237 不带选项直接运行可进入交互模式(REPL),在交互模式里输入单独的符号 '$'
238 可用于开始或结束多行模式。
239```
240&emsp;&emsp;使用案例:
241&emsp;&emsp;递归编译当前路径下扩展名为 **.yue** 的每个月之脚本文件: **yue .**
242&emsp;&emsp;编译并将结果保存到目标路径: **yue -t /target/path/ .**
243&emsp;&emsp;编译并保留调试信息: **yue -l .**
244&emsp;&emsp;编译并生成压缩代码: **yue -m .**
245&emsp;&emsp;直接执行代码: **yue -e 'print 123'**
246&emsp;&emsp;执行一个月之脚本文件: **yue -e main.yue**
247
248## 宏
249
250### 常见用法
251
252宏函数用于在编译时执行一段代码来生成新的代码,并将生成的代码插入到最终编译结果中。
253
254```yuescript
255macro PI2 = -> math.pi * 2
256area = $PI2 * 5
257
258macro HELLO = -> "'你好 世界'"
259print $HELLO
260
261macro config = (debugging) ->
262 global debugMode = debugging == "true"
263 ""
264
265macro asserts = (cond) ->
266 debugMode and "assert #{cond}" or ""
267
268macro assert = (cond) ->
269 debugMode and "assert #{cond}" or "#{cond}"
270
271$config true
272$asserts item ~= nil
273
274$config false
275value = $assert item
276
277-- 宏函数参数传递的表达式会被转换为字符串
278macro and = (...) -> "#{ table.concat {...}, ' and ' }"
279if $and f1!, f2!, f3!
280 print "OK"
281```
282<YueDisplay>
283
284```yue
285macro PI2 = -> math.pi * 2
286area = $PI2 * 5
287
288macro HELLO = -> "'你好 世界'"
289print $HELLO
290
291macro config = (debugging) ->
292 global debugMode = debugging == "true"
293 ""
294
295macro asserts = (cond) ->
296 debugMode and "assert #{cond}" or ""
297
298macro assert = (cond) ->
299 debugMode and "assert #{cond}" or "#{cond}"
300
301$config true
302$asserts item ~= nil
303
304$config false
305value = $assert item
306
307-- 宏函数参数传递的表达式会被转换为字符串
308macro and = (...) -> "#{ table.concat {...}, ' and ' }"
309if $and f1!, f2!, f3!
310 print "OK"
311```
312
313</YueDisplay>
314
315### 直接插入代码
316
317宏函数可以返回一个包含月之脚本代码的字符串,或是一个包含 Lua 代码字符串的配置表。
318```yuescript
319macro yueFunc = (var) -> "local #{var} = ->"
320$yueFunc funcA
321funcA = -> "无法访问宏生成月之脚本里定义的变量"
322
323macro luaFunc = (var) -> {
324 code: "local function #{var}() end"
325 type: "lua"
326}
327$luaFunc funcB
328funcB = -> "无法访问宏生成 Lua 代码里定义的变量"
329
330macro lua = (code) -> {
331 :code
332 type: "lua"
333}
334
335-- raw字符串的开始和结束符号会自动被去除了再传入宏函数
336$lua[==[
337-- 插入原始Lua代码
338if cond then
339 print("输出")
340end
341]==]
342```
343<YueDisplay>
344
345```yue
346macro yueFunc = (var) -> "local #{var} = ->"
347$yueFunc funcA
348funcA = -> "无法访问宏生成月之脚本里定义的变量"
349
350macro luaFunc = (var) -> {
351 code: "local function #{var}() end"
352 type: "lua"
353}
354$luaFunc funcB
355funcB = -> "无法访问宏生成 Lua 代码里定义的变量"
356
357macro lua = (code) -> {
358 :code
359 type: "lua"
360}
361
362-- raw字符串的开始和结束符号会自动被去除了再传入宏函数
363$lua[==[
364-- 插入原始Lua代码
365if cond then
366 print("输出")
367end
368]==]
369```
370
371</YueDisplay>
372
373### 导出宏
374
375宏函数可以从一个模块中导出,并在另一个模块中导入。你必须将导出的宏函数放在一个单独的文件中使用,而且只有宏定义、宏导入和宏展开可以放入这个宏导出模块中。
376```yuescript
377-- 文件: utils.yue
378export macro map = (items, action) -> "[#{action} for _ in *#{items}]"
379export macro filter = (items, action) -> "[_ for _ in *#{items} when #{action}]"
380export macro foreach = (items, action) -> "for _ in *#{items}
381 #{action}"
382
383-- 文件 main.yue
384import "utils" as {
385 $, -- 表示导入所有宏的符号
386 $foreach: $each -- 重命名宏 $foreach 为 $each
387}
388[1, 2, 3] |> $map(_ * 2) |> $filter(_ > 4) |> $each print _
389```
390<YueDisplay>
391
392```yue
393-- 文件: utils.yue
394export macro map = (items, action) -> "[#{action} for _ in *#{items}]"
395export macro filter = (items, action) -> "[_ for _ in *#{items} when #{action}]"
396export macro foreach = (items, action) -> "for _ in *#{items}
397 #{action}"
398-- 文件 main.yue
399-- 在浏览器中不支持import函数,请在真实环境中尝试
400--[[
401import "utils" as {
402 $, -- 表示导入所有宏的符号
403 $foreach: $each -- 重命名宏 $foreach 为 $each
404}
405[1, 2, 3] |> $map(_ * 2) |> $filter(_ > 4) |> $each print _
406]]
407```
408
409</YueDisplay>
410
411### 内置宏
412
413月之脚本中有一些内置可以直接使用的宏,但你可以通过声明相同名称的宏来覆盖它们。
414```yuescript
415print $FILE -- 获取当前模块名称的字符串
416print $LINE -- 获取当前代码行数:2
417```
418<YueDisplay>
419
420```yue
421print $FILE -- 获取当前模块名称的字符串
422print $LINE -- 获取当前代码行数:2
423```
424
425</YueDisplay>
426
427### 用宏生成宏
428
429在月之脚本中,宏函数允许你在编译时生成代码。通过嵌套的宏函数,你可以创建更复杂的生成模式。这个特性允许你定义一个宏函数,用它来生成另一个宏函数,从而实现更加动态的代码生成。
430
431```yuescript
432macro Enum = (...) ->
433 items = {...}
434 itemSet = {item, true for item in *items}
435 (item) ->
436 error "got \"#{item}\", expecting one of #{table.concat items, ', '}" unless itemSet[item]
437 "\"#{item}\""
438
439macro BodyType = $Enum(
440 Static
441 Dynamic
442 Kinematic
443)
444
445print "有效的枚举类型:", $BodyType Static
446-- print "编译报错的枚举类型:", $BodyType Unknown
447```
448<YueDisplay>
449
450```yue
451macro Enum = (...) ->
452 items = {...}
453 itemSet = {item, true for item in *items}
454 (item) ->
455 error "got \"#{item}\", expecting one of #{table.concat items, ', '}" unless itemSet[item]
456 "\"#{item}\""
457
458macro BodyType = $Enum(
459 Static
460 Dynamic
461 Kinematic
462)
463
464print "有效的枚举类型:", $BodyType Static
465-- print "编译报错的枚举类型:", $BodyType Unknown
466```
467
468</YueDisplay>
469
470### 宏参数检查
471
472可以直接在参数列表中声明期望的 AST 节点类型,并在编译时检查传入的宏参数是否符合预期。
473
474```yuescript
475macro printNumAndStr = (num `Num, str `String) -> |
476 print(
477 #{num}
478 #{str}
479 )
480
481$printNumAndStr 123, "hello"
482```
483<YueDisplay>
484
485```yue
486macro printNumAndStr = (num `Num, str `String) -> |
487 print(
488 #{num}
489 #{str}
490 )
491
492$printNumAndStr 123, "hello"
493```
494
495</YueDisplay>
496
497如果需要做更加灵活的参数检查操作,可以使用内置的 `$is_ast` 宏函数在合适的位置进行手动检查。
498
499```yuescript
500macro printNumAndStr = (num, str) ->
501 error "expected Num as first argument" unless $is_ast Num, num
502 error "expected String as second argument" unless $is_ast String, str
503 "print(#{num}, #{str})"
504
505$printNumAndStr 123, "hello"
506```
507<YueDisplay>
508
509```yue
510macro printNumAndStr = (num, str) ->
511 error "expected Num as first argument" unless $is_ast Num, num
512 error "expected String as second argument" unless $is_ast String, str
513 "print(#{num}, #{str})"
514
515$printNumAndStr 123, "hello"
516```
517
518</YueDisplay>
519
520更多关于可用 AST 节点的详细信息,请参考 [yue_parser.cpp](https://github.com/IppClub/YueScript/blob/main/src/yuescript/yue_parser.cpp) 中大写的规则定义。
521
522## 操作符
523
524Lua 的所有二元和一元操作符在月之脚本中都是可用的。此外,**!=** 符号是 **~=** 的别名,而 **\\** 或 **::** 均可用于编写链式函数调用,如写作 `tb\func!` 或 `tb::func!`。此外月之脚本还提供了一些其他特殊的操作符,以编写更具表达力的代码。
525
526```yuescript
527tb\func! if tb ~= nil
528tb::func! if tb != nil
529```
530<YueDisplay>
531
532```yue
533tb\func! if tb ~= nil
534tb::func! if tb != nil
535```
536
537</YueDisplay>
538
539### 链式比较
540
541你可以在月之脚本中进行比较表达式的链式书写:
542
543```yuescript
544print 1 < 2 <= 2 < 3 == 3 > 2 >= 1 == 1 < 3 != 5
545-- 输出:true
546
547a = 5
548print 1 <= a <= 10
549-- 输出:true
550```
551<YueDisplay>
552
553```yue
554print 1 < 2 <= 2 < 3 == 3 > 2 >= 1 == 1 < 3 != 5
555-- 输出:true
556
557a = 5
558print 1 <= a <= 10
559-- 输出:true
560```
561
562</YueDisplay>
563
564可以注意一下链式比较表达式的求值行为:
565
566```yuescript
567v = (x) ->
568 print x
569 x
570
571print v(1) < v(2) <= v(3)
572--[[
573 输出:
574 2
575 1
576 3
577 true
578]]
579
580print v(1) > v(2) <= v(3)
581--[[
582 输出:
583 2
584 1
585 false
586]]
587```
588<YueDisplay>
589
590```yue
591v = (x) ->
592 print x
593 x
594
595print v(1) < v(2) <= v(3)
596--[[
597 输出:
598 2
599 1
600 3
601 true
602]]
603
604print v(1) > v(2) <= v(3)
605--[[
606 输出:
607 2
608 1
609 false
610]]
611```
612
613</YueDisplay>
614
615在上面的例子里,中间的表达式 `v(2)` 仅被计算一次,如果把表达式写成 `v(1) < v(2) and v(2) <= v(3)` 的方式,中间的 `v(2)` 才会被计算两次。在链式比较中,求值的顺序往往是未定义的。所以强烈建议不要在链式比较中使用具有副作用(比如做打印操作)的表达式。如果需要使用有副作用的函数,应明确使用短路 `and` 运算符来做连接。
616
617### 表追加
618
619**[] =** 操作符用于向 Lua 表的最后插入值。
620
621```yuescript
622tab = []
623tab[] = "Value"
624```
625<YueDisplay>
626
627```yue
628tab = []
629tab[] = "Value"
630```
631
632</YueDisplay>
633
634你还可以使用展开操作符 `...` 来将一个列表中的所有元素追加到另一个列表中:
635
636```yuescript
637tbA = [1, 2, 3]
638tbB = [4, 5, 6]
639tbA[] = ...tbB
640-- tbA 现在为 [1, 2, 3, 4, 5, 6]
641```
642<YueDisplay>
643
644```yue
645tbA = [1, 2, 3]
646tbB = [4, 5, 6]
647tbA[] = ...tbB
648-- tbA 现在为 [1, 2, 3, 4, 5, 6]
649```
650
651</YueDisplay>
652
653### 表扩展
654
655你可以使用前置 `...` 操作符在 Lua 表中插入数组表或哈希表。
656
657```yuescript
658parts =
659 * "shoulders"
660 * "knees"
661lyrics =
662 * "head"
663 * ...parts
664 * "and"
665 * "toes"
666
667copy = {...other}
668
669a = {1, 2, 3, x: 1}
670b = {4, 5, y: 1}
671merge = {...a, ...b}
672```
673<YueDisplay>
674
675```yue
676parts =
677 * "shoulders"
678 * "knees"
679lyrics =
680 * "head"
681 * ...parts
682 * "and"
683 * "toes"
684
685copy = {...other}
686
687a = {1, 2, 3, x: 1}
688b = {4, 5, y: 1}
689merge = {...a, ...b}
690```
691
692</YueDisplay>
693
694### 表反向索引
695
696你可以使用 **#** 操作符来反向索引表中的元素。
697
698```yuescript
699last = data.items[#]
700second_last = data.items[#-1]
701data.items[#] = 1
702```
703<YueDisplay>
704
705```yue
706last = data.items[#]
707second_last = data.items[#-1]
708data.items[#] = 1
709```
710
711</YueDisplay>
712
713### 元表
714
715**<>** 操作符可提供元表操作的快捷方式。
716
717* **元表创建**
718使用空括号 **<>** 或被 **<>** 包围的元方法键创建普通的 Lua 表。
719
720```yuescript
721mt = {}
722add = (right) => <>: mt, value: @value + right.value
723mt.__add = add
724
725a = <>: mt, value: 1
726-- 使用与临时变量名相同的字段名,将临时变量赋值给元表
727b = :<add>, value: 2
728c = <add>: mt.__add, value: 3
729
730d = a + b + c
731print d.value
732
733close _ = <close>: -> print "超出范围"
734```
735<YueDisplay>
736
737```yue
738mt = {}
739add = (right) => <>: mt, value: @value + right.value
740mt.__add = add
741
742a = <>: mt, value: 1
743-- 使用与临时变量名相同的字段名,将临时变量赋值给元表
744b = :<add>, value: 2
745c = <add>: mt.__add, value: 3
746
747d = a + b + c
748print d.value
749
750close _ = <close>: -> print "超出范围"
751```
752
753</YueDisplay>
754
755* **元表访问**
756使用 **<>** 或被 **<>** 包围的元方法名或在 **<>** 中编写某些表达式来访问元表。
757
758```yuescript
759-- 使用包含字段 "value" 的元表创建
760tb = <"value">: 123
761tb.<index> = tb.<>
762print tb.value
763
764tb.<> = __index: {item: "hello"}
765print tb.item
766```
767<YueDisplay>
768
769
770```yue
771-- 使用包含字段 "value" 的元表创建
772tb = <"value">: 123
773tb.<index> = tb.<>
774print tb.value
775tb.<> = __index: {item: "hello"}
776print tb.item
777```
778
779</YueDisplay>
780
781* **元表解构**
782使用被 **<>** 包围的元方法键解构元表。
783
784```yuescript
785{item, :new, :<close>, <index>: getter} = tb
786print item, new, close, getter
787```
788<YueDisplay>
789
790```yue
791{item, :new, :<close>, <index>: getter} = tb
792print item, new, close, getter
793```
794
795</YueDisplay>
796
797### 存在性
798
799**?** 运算符可以在多种上下文中用来检查存在性。
800
801```yuescript
802func?!
803print abc?["你好 世界"]?.xyz
804
805x = tab?.value
806len = utf8?.len or string?.len or (o) -> #o
807
808if print and x?
809 print x
810
811with? io.open "test.txt", "w"
812 \write "你好"
813 \close!
814```
815<YueDisplay>
816
817```yue
818func?!
819print abc?["你好 世界"]?.xyz
820
821x = tab?.value
822len = utf8?.len or string?.len or (o) -> #o
823
824if print and x?
825 print x
826
827with? io.open "test.txt", "w"
828 \write "你好"
829 \close!
830```
831
832</YueDisplay>
833
834### 管道
835
836与其使用一系列嵌套的函数调用,你还可以考虑使用运算符 **|>** 来传递值。
837
838```yuescript
839"你好" |> print
8401 |> print 2 -- 将管道项作为第一个参数插入
8412 |> print 1, _, 3 -- 带有占位符的管道
842
843-- 多行的管道表达式
844readFile "example.txt"
845 |> extract language, {}
846 |> parse language
847 |> emit
848 |> render
849 |> print
850```
851<YueDisplay>
852
853```yue
854"你好" |> print
8551 |> print 2 -- 将管道项作为第一个参数插入
8562 |> print 1, _, 3 -- 带有占位符的管道
857-- 多行的管道表达式
858readFile "example.txt"
859 |> extract language, {}
860 |> parse language
861 |> emit
862 |> render
863 |> print
864```
865
866</YueDisplay>
867
868### 空值合并
869
870如果其左操作数不是 **nil**,则nil合并运算符 **??** 返回其左操作数的值;否则,它将计算右操作数并返回其结果。如果左操作数计算结果为非 nil 的值,**??** 运算符将不再计算其右操作数。
871```yuescript
872local a, b, c, d
873a = b ?? c ?? d
874func a ?? {}
875
876a ??= false
877```
878<YueDisplay>
879
880```yue
881local a, b, c, d
882a = b ?? c ?? d
883func a ?? {}
884a ??= false
885```
886
887</YueDisplay>
888
889### 隐式对象
890
891你可以在表格块内使用符号 **\*** 或是 **-** 开始编写一系列隐式结构。如果你正在创建隐式对象,对象的字段必须具有相同的缩进。
892
893```yuescript
894-- 赋值时使用隐式对象
895list =
896 * 1
897 * 2
898 * 3
899
900-- 函数调用时使用隐式对象
901func
902 * 1
903 * 2
904 * 3
905
906-- 返回时使用隐式对象
907f = ->
908 return
909 * 1
910 * 2
911 * 3
912
913-- 表格时使用隐式对象
914tb =
915 name: "abc"
916
917 values:
918 - "a"
919 - "b"
920 - "c"
921
922 objects:
923 - name: "a"
924 value: 1
925 func: => @value + 1
926 tb:
927 fieldA: 1
928
929 - name: "b"
930 value: 2
931 func: => @value + 2
932 tb: { }
933```
934<YueDisplay>
935
936```yue
937-- 赋值时使用隐式对象
938list =
939 * 1
940 * 2
941 * 3
942
943-- 函数调用时使用隐式对象
944func
945 * 1
946 * 2
947 * 3
948
949-- 返回时使用隐式对象
950f = ->
951 return
952 * 1
953 * 2
954 * 3
955
956-- 表格时使用隐式对象
957tb =
958 name: "abc"
959
960 values:
961 - "a"
962 - "b"
963 - "c"
964
965 objects:
966 - name: "a"
967 value: 1
968 func: => @value + 1
969 tb:
970 fieldA: 1
971
972 - name: "b"
973 value: 2
974 func: => @value + 2
975 tb: { }
976```
977
978</YueDisplay>
979
980## 模块
981
982### 导入
983
984导入语句是一个语法糖,用于需要引入一个模块或者从已导入的模块中提取子项目。从模块导入的变量默认为不可修改的常量。
985
986```yuescript
987-- 用作表解构
988do
989 import insert, concat from table
990 -- 当给 insert, concat 变量赋值时,编译器会报告错误
991 import C, Ct, Cmt from require "lpeg"
992 -- 快捷写法引入模块的子项
993 import x, y, z from 'mymodule'
994 -- 使用Python风格的导入
995 from 'module' import a, b, c
996
997-- 快捷地导入一个模块
998do
999 import 'module'
1000 import 'module_x'
1001 import "d-a-s-h-e-s"
1002 import "module.part"
1003
1004-- 导入模块后起一个别名使用,或是进行导入模块表的解构
1005do
1006 import "player" as PlayerModule
1007 import "lpeg" as :C, :Ct, :Cmt
1008 import "export" as {one, two, Something:{umm:{ch}}}
1009```
1010<YueDisplay>
1011
1012```yue
1013-- 用作表解构
1014do
1015 import insert, concat from table
1016 -- 当给 insert, concat 变量赋值时,编译器会报告错误
1017 import C, Ct, Cmt from require "lpeg"
1018 -- 快捷写法引入模块的子项
1019 import x, y, z from 'mymodule'
1020 -- 使用Python风格的导入
1021 from 'module' import a, b, c
1022
1023-- 快捷地导入一个模块
1024do
1025 import 'module'
1026 import 'module_x'
1027 import "d-a-s-h-e-s"
1028 import "module.part"
1029
1030-- 导入模块后起一个别名使用,或是进行导入模块表的解构
1031do
1032 import "player" as PlayerModule
1033 import "lpeg" as :C, :Ct, :Cmt
1034 import "export" as {one, two, Something:{umm:{ch}}}
1035```
1036
1037</YueDisplay>
1038
1039### 导入全局变量
1040
1041你可以使用 `import` 将指定的全局变量导入到本地变量中。当导入一系列对全局变量的链式访问时,最后一个访问的字段将被赋值给本地变量。
1042
1043```yuescript
1044do
1045 import tostring
1046 import table.concat
1047 print concat ["a", tostring 1]
1048```
1049<YueDisplay>
1050
1051```yue
1052do
1053 import tostring
1054 import table.concat
1055 print concat ["a", tostring 1]
1056```
1057
1058</YueDisplay>
1059
1060#### 自动导入
1061
1062在一个代码块的顶部写 `import global`,会将当前作用域中尚未显式声明或赋值过的变量名,自动导入为本地常量,并在该语句的位置绑定到同名的全局变量。
1063
1064但是在同一作用域中被显式声明为全局的变量不会被自动导入,因此可以继续进行赋值操作。
1065
1066```yuescript
1067do
1068 import global
1069 print "hello"
1070 math.random 3
1071 -- print = nil -- 报错:自动导入的全局变量为常量
1072
1073do
1074 -- 被显式声明为全局的变量不会被自动导入
1075 import global
1076 global FLAG
1077 print FLAG
1078 FLAG = 123
1079```
1080<YueDisplay>
1081
1082```yue
1083do
1084 import global
1085 print "hello"
1086 math.random 3
1087 -- print = nil -- 报错:自动导入的全局变量是常量
1088
1089do
1090 -- 被显式声明为全局的变量不会被自动导入
1091 import global
1092 global FLAG
1093 print FLAG
1094 FLAG = 123
1095```
1096
1097</YueDisplay>
1098
1099### 导出
1100
1101导出语句提供了一种简洁的方式来定义当前的模块。
1102
1103* **命名导出**
1104带命名的导出将定义一个局部变量,并在导出的表中添加一个同名的字段。
1105
1106```yuescript
1107export a, b, c = 1, 2, 3
1108export cool = "cat"
1109
1110export What = if this
1111 "abc"
1112else
1113 "def"
1114
1115export y = ->
1116 hallo = 3434
1117
1118export class Something
1119 umm: "cool"
1120```
1121<YueDisplay>
1122
1123```yue
1124export a, b, c = 1, 2, 3
1125export cool = "cat"
1126
1127export What = if this
1128 "abc"
1129else
1130 "def"
1131
1132export y = ->
1133 hallo = 3434
1134
1135export class Something
1136 umm: "cool"
1137```
1138
1139</YueDisplay>
1140
1141使用解构进行命名导出。
1142
1143```yuescript
1144export :loadstring, to_lua: tolua = yue
1145export {itemA: {:fieldA = '默认值'}} = tb
1146```
1147<YueDisplay>
1148
1149```yue
1150export :loadstring, to_lua: tolua = yue
1151export {itemA: {:fieldA = '默认值'}} = tb
1152```
1153
1154</YueDisplay>
1155
1156从模块导出命名项目时,可以不用创建局部变量。
1157
1158```yuescript
1159export.itemA = tb
1160export.<index> = items
1161export["a-b-c"] = 123
1162```
1163<YueDisplay>
1164
1165```yue
1166export.itemA = tb
1167export.<index> = items
1168export["a-b-c"] = 123
1169```
1170
1171</YueDisplay>
1172
1173* **未命名导出**
1174未命名导出会将要导出的目标项目添加到导出表的数组部分。
1175
1176```yuescript
1177d, e, f = 3, 2, 1
1178export d, e, f
1179
1180export if this
1181 123
1182else
1183 456
1184
1185export with tmp
1186 j = 2000
1187```
1188<YueDisplay>
1189
1190```yue
1191d, e, f = 3, 2, 1
1192export d, e, f
1193
1194export if this
1195 123
1196else
1197 456
1198
1199export with tmp
1200 j = 2000
1201```
1202
1203</YueDisplay>
1204
1205* **默认导出**
1206在导出语句中使用 **default** 关键字,来替换导出的表为一个目标的对象。
1207
1208```yuescript
1209export default ->
1210 print "你好"
1211 123
1212```
1213<YueDisplay>
1214
1215```yue
1216export default ->
1217 print "你好"
1218 123
1219```
1220
1221</YueDisplay>
1222
1223## 赋值
1224
1225月之脚本中定义的变量是动态类型的,并默认为局部变量。但你可以通过 **local** 和 **global** 声明来改变声明变量的作用范围。
1226
1227```yuescript
1228hello = "world"
1229a, b, c = 1, 2, 3
1230hello = 123 -- 访问现有的变量
1231```
1232<YueDisplay>
1233
1234```yue
1235hello = "world"
1236a, b, c = 1, 2, 3
1237hello = 123 -- 访问现有的变量
1238```
1239
1240</YueDisplay>
1241
1242### 执行更新
1243
1244你可以使用各式二进制运算符执行更新赋值。
1245```yuescript
1246x = 1
1247x += 1
1248x -= 1
1249x *= 10
1250x /= 10
1251x %= 10
1252s ..= "world" -- 如果执行更新的局部变量不存在,将新建一个局部变量
1253arg or= "默认值"
1254```
1255<YueDisplay>
1256
1257```yue
1258x = 1
1259x += 1
1260x -= 1
1261x *= 10
1262x /= 10
1263x %= 10
1264s ..= "world" -- 如果执行更新的局部变量不存在,将新建一个局部变量
1265arg or= "默认值"
1266```
1267
1268</YueDisplay>
1269
1270### 链式赋值
1271
1272你可以进行链式赋值,将多个项目赋予相同的值。
1273```yuescript
1274a = b = c = d = e = 0
1275x = y = z = f!
1276```
1277<YueDisplay>
1278
1279```yue
1280a = b = c = d = e = 0
1281x = y = z = f!
1282```
1283
1284</YueDisplay>
1285
1286### 显式声明局部变量
1287```yuescript
1288do
1289 local a = 1
1290 local *
1291 print "预先声明后续所有变量为局部变量"
1292 x = -> 1 + y + z
1293 y, z = 2, 3
1294 global instance = Item\new!
1295
1296do
1297 local X = 1
1298 local ^
1299 print "只预先声明后续大写的变量为局部变量"
1300 a = 1
1301 B = 2
1302```
1303<YueDisplay>
1304
1305```yue
1306do
1307 local a = 1
1308 local *
1309 print "预先声明后续所有变量为局部变量"
1310 x = -> 1 + y + z
1311 y, z = 2, 3
1312 global instance = Item\new!
1313
1314do
1315 local X = 1
1316 local ^
1317 print "只预先声明后续大写的变量为局部变量"
1318 a = 1
1319 B = 2
1320```
1321
1322</YueDisplay>
1323
1324### 显式声明全局变量
1325```yuescript
1326do
1327 global a = 1
1328 global *
1329 print "预先声明所有变量为全局变量"
1330 x = -> 1 + y + z
1331 y, z = 2, 3
1332
1333do
1334 global x = 1
1335 global ^
1336 print "只预先声明大写的变量为全局变量"
1337 a = 1
1338 B = 2
1339 local Temp = "一个局部值"
1340```
1341<YueDisplay>
1342
1343```yue
1344do
1345 global a = 1
1346 global *
1347 print "预先声明所有变量为全局变量"
1348 x = -> 1 + y + z
1349 y, z = 2, 3
1350
1351do
1352 global x = 1
1353 global ^
1354 print "只预先声明大写的变量为全局变量"
1355 a = 1
1356 B = 2
1357 local Temp = "一个局部值"
1358```
1359
1360</YueDisplay>
1361
1362## 解构赋值
1363
1364解构赋值是一种快速从 Lua 表中按名称或基于数组中的位置提取值的方法。
1365
1366通常当你看到一个字面量的 Lua 表,比如 `{1,2,3}`,它位于赋值的右侧,因为它是一个值。解构赋值语句的写法就是交换了字面量 Lua 表的角色,并将其放在赋值语句的左侧。
1367
1368最好是通过示例来解释。以下是如何从表格中解包前两个值的方法:
1369
1370```yuescript
1371thing = [1, 2]
1372
1373[a, b] = thing
1374print a, b
1375```
1376<YueDisplay>
1377
1378
1379```yue
1380thing = [1, 2]
1381
1382[a, b] = thing
1383print a, b
1384```
1385
1386</YueDisplay>
1387
1388在解构表格字面量中,键代表从右侧读取的键,值代表读取的值将被赋予的名称。
1389
1390```yuescript
1391obj = {
1392 hello: "world"
1393 day: "tuesday"
1394 length: 20
1395}
1396
1397{hello: hello, day: the_day} = obj
1398print hello, the_day
1399
1400:day = obj -- 可以不带大括号进行简单的解构
1401```
1402<YueDisplay>
1403
1404```yue
1405obj = {
1406 hello: "world"
1407 day: "tuesday"
1408 length: 20
1409}
1410
1411{hello: hello, day: the_day} = obj
1412print hello, the_day
1413
1414:day = obj -- 可以不带大括号进行简单的解构
1415```
1416
1417</YueDisplay>
1418
1419这也适用于嵌套的数据结构:
1420
1421```yuescript
1422obj2 = {
1423 numbers: [1,2,3,4]
1424 properties: {
1425 color: "green"
1426 height: 13.5
1427 }
1428}
1429
1430{numbers: [first, second], properties: {color: color}} = obj2
1431print first, second, color
1432```
1433<YueDisplay>
1434
1435```yue
1436obj2 = {
1437 numbers: [1,2,3,4]
1438 properties: {
1439 color: "green"
1440 height: 13.5
1441 }
1442}
1443
1444{numbers: [first, second]} = obj2
1445print first, second, color
1446```
1447
1448</YueDisplay>
1449
1450如果解构语句很复杂,也可以任意将其分散在几行中。稍微复杂一些的示例:
1451
1452```yuescript
1453{
1454 numbers: [first, second]
1455 properties: {
1456 color: color
1457 }
1458} = obj2
1459```
1460<YueDisplay>
1461
1462```yue
1463{
1464 numbers: [first, second]
1465 properties: {
1466 color: color
1467 }
1468} = obj2
1469```
1470
1471</YueDisplay>
1472
1473有时候我们会需要从 Lua 表中提取值并将它们赋给与键同名的局部变量。为了避免编写重复代码,我们可以使用 **:** 前缀操作符:
1474
1475```yuescript
1476{:concat, :insert} = table
1477```
1478<YueDisplay>
1479
1480```yue
1481{:concat, :insert} = table
1482```
1483
1484</YueDisplay>
1485
1486这样的用法与导入语法有些相似。但我们可以通过混合语法重命名我们想要提取的字段:
1487
1488```yuescript
1489{:mix, :max, random: rand} = math
1490```
1491<YueDisplay>
1492
1493```yue
1494{:mix, :max, random: rand} = math
1495```
1496
1497</YueDisplay>
1498
1499在进行解构时,你可以指定默认值,如:
1500
1501```yuescript
1502{:name = "nameless", :job = "jobless"} = person
1503```
1504<YueDisplay>
1505
1506```yue
1507{:name = "nameless", :job = "jobless"} = person
1508```
1509
1510</YueDisplay>
1511
1512在进行列表解构时,你可以使用`_`作为占位符:
1513
1514```yuescript
1515[_, two, _, four] = items
1516```
1517<YueDisplay>
1518
1519```yue
1520[_, two, _, four] = items
1521```
1522
1523</YueDisplay>
1524
1525### 范围解构
1526
1527你可以使用展开运算符 `...` 在列表解构中来捕获一个范围的值到子列表中。这在当你想要从列表的开头和结尾提取特定元素,同时收集中间的元素时非常有用。
1528
1529```yuescript
1530orders = ["first", "second", "third", "fourth", "last"]
1531[first, ...bulk, last] = orders
1532print first -- 打印: first
1533print bulk -- 打印: {"second", "third", "fourth"}
1534print last -- 打印: last
1535```
1536<YueDisplay>
1537
1538```yue
1539orders = ["first", "second", "third", "fourth", "last"]
1540[first, ...bulk, last] = orders
1541print first -- 打印: first
1542print bulk -- 打印: {"second", "third", "fourth"}
1543print last -- 打印: last
1544```
1545
1546</YueDisplay>
1547
1548展开运算符可以用在不同的位置来捕获不同的范围,并且你可以使用 `_` 作为占位符来表示你想跳过对应范围的捕获:
1549
1550```yuescript
1551-- 捕获第一个元素之后的所有元素
1552[first, ...rest] = orders
1553
1554-- 捕获最后一个元素之前的所有元素
1555[...start, last] = orders
1556
1557-- 跳过中间的元素,只捕获第一个和最后一个元素
1558[first, ..._, last] = orders
1559```
1560<YueDisplay>
1561
1562```yue
1563-- 捕获第一个元素之后的所有元素
1564[first, ...rest] = orders
1565
1566-- 捕获最后一个元素之前的所有元素
1567[...start, last] = orders
1568
1569-- 跳过中间的元素,只捕获第一个和最后一个元素
1570[first, ..._, last] = orders
1571```
1572
1573</YueDisplay>
1574
1575### 在其它地方的解构赋值
1576
1577解构赋值也可以出现在其它隐式进行赋值的地方。一个例子是用在 for 循环中:
1578
1579```yuescript
1580tuples = [
1581 ["hello", "world"]
1582 ["egg", "head"]
1583]
1584
1585for [left, right] in *tuples
1586 print left, right
1587```
1588<YueDisplay>
1589
1590```yue
1591tuples = [
1592 ["hello", "world"]
1593 ["egg", "head"]
1594]
1595
1596for [left, right] in *tuples
1597 print left, right
1598```
1599
1600</YueDisplay>
1601
1602我们知道数组表中的每个元素都是一个两项的元组,所以我们可以直接在 for 语句的名称子句中使用解构来解包它。
1603
1604## If 赋值
1605
1606`if` 和 `elseif` 代码块可以在条件表达式的位置进行赋值。在代码执行到要计算条件时,会首先进行赋值计算,并使用赋与的值作为分支判断的条件。赋值的变量仅在条件分支的代码块内有效,这意味着如果值不是真值,那么它就不会被用到。注意,你必须使用“海象运算符” `:=` 而不是 `=` 来做赋值。
1607
1608```yuescript
1609if user := database.find_user "moon"
1610 print user.name
1611```
1612<YueDisplay>
1613
1614```yue
1615if user := database.find_user "moon"
1616 print user.name
1617```
1618
1619</YueDisplay>
1620
1621```yuescript
1622if hello := os.getenv "hello"
1623 print "你有 hello", hello
1624elseif world := os.getenv "world"
1625 print "你有 world", world
1626else
1627 print "什么都没有 :("
1628```
1629<YueDisplay>
1630
1631```yue
1632if hello := os.getenv "hello"
1633 print "你有 hello", hello
1634elseif world := os.getenv "world"
1635 print "你有 world", world
1636else
1637 print "什么都没有 :("
1638```
1639
1640</YueDisplay>
1641
1642使用多个返回值的 If 赋值。只有第一个值会被检查,其他值都有同样的作用域。
1643```yuescript
1644if success, result := pcall -> "无报错地获取结果"
1645 print result -- 变量 result 是有作用域的
1646print "好的"
1647```
1648<YueDisplay>
1649
1650```yue
1651if success, result := pcall -> "无报错地获取结果"
1652 print result -- 变量 result 是有作用域的
1653print "好的"
1654```
1655
1656</YueDisplay>
1657
1658### While 赋值
1659
1660你可以在 while 循环中同样使用赋值来获取循环条件的值。
1661```yuescript
1662while byte := stream\read_one!
1663 -- 对 byte 做一些操作
1664 print byte
1665```
1666<YueDisplay>
1667
1668```yue
1669while byte := stream\read_one!
1670 -- 对 byte 做一些操作
1671 print byte
1672```
1673
1674</YueDisplay>
1675
1676## 可变参数赋值
1677
1678你可以将函数返回的结果赋值给一个可变参数符号 `...`。然后使用 Lua 的方式访问其内容。
1679```yuescript
1680list = [1, 2, 3, 4, 5]
1681fn = (ok) -> ok, table.unpack list
1682ok, ... = fn true
1683count = select '#', ...
1684first = select 1, ...
1685print ok, count, first
1686```
1687<YueDisplay>
1688
1689```yue
1690list = [1, 2, 3, 4, 5]
1691fn = (ok) -> ok, table.unpack list
1692ok, ... = fn true
1693count = select '#', ...
1694first = select 1, ...
1695print ok, count, first
1696```
1697
1698</YueDisplay>
1699
1700## 空白
1701
1702月之脚本是一个对空白敏感的语言。你必须在相同的缩进中使用空格 **' '** 或制表符 **'\t'** 来编写一些代码块,如函数体、值列表和一些控制块。包含不同空白的表达式可能意味着不同的事情。制表符被视为4个空格,但最好不要混合使用空格和制表符。
1703
1704### 语句分隔符
1705
1706一条语句通常以换行结束。你也可以使用分号 `;` 显式结束一条语句,从而在同一行中编写多条语句:
1707
1708```yuescript
1709a = 1; b = 2; print a + b
1710```
1711<YueDisplay>
1712
1713```yue
1714a = 1; b = 2; print a + b
1715```
1716
1717</YueDisplay>
1718
1719### 多行链式调用
1720
1721你可以使用相同的缩进来编写多行链式函数调用。
1722```yuescript
1723Rx.Observable
1724 .fromRange 1, 8
1725 \filter (x) -> x % 2 == 0
1726 \concat Rx.Observable.of 'who do we appreciate'
1727 \map (value) -> value .. '!'
1728 \subscribe print
1729```
1730<YueDisplay>
1731
1732```yue
1733Rx.Observable
1734 .fromRange 1, 8
1735 \filter (x) -> x % 2 == 0
1736 \concat Rx.Observable.of 'who do we appreciate'
1737 \map (value) -> value .. '!'
1738 \subscribe print
1739```
1740
1741</YueDisplay>
1742
1743## 注释
1744
1745```yuescript
1746-- 我是一个注释
1747
1748str = --[[
1749这是一个多行注释。
1750没问题。
1751]] strA \ -- 注释 1
1752 .. strB \ -- 注释 2
1753 .. strC
1754
1755func --[[端口]] 3000, --[[ip]] "192.168.1.1"
1756```
1757<YueDisplay>
1758
1759```yue
1760-- 我是一个注释
1761
1762str = --[[
1763这是一个多行注释。
1764没问题。
1765]] strA \ -- 注释 1
1766 .. strB \ -- 注释 2
1767 .. strC
1768
1769func --[[端口]] 3000, --[[ip]] "192.168.1.1"
1770```
1771
1772</YueDisplay>
1773
1774## 错误处理
1775
1776用于统一进行 Lua 错误处理的便捷语法。
1777
1778```yuescript
1779try
1780 func 1, 2, 3
1781catch err
1782 print yue.traceback err
1783
1784success, result = try
1785 func 1, 2, 3
1786catch err
1787 yue.traceback err
1788
1789try func 1, 2, 3
1790catch err
1791 print yue.traceback err
1792
1793success, result = try func 1, 2, 3
1794
1795try
1796 print "尝试中"
1797 func 1, 2, 3
1798
1799-- 使用if赋值模式
1800if success, result := try func 1, 2, 3
1801catch err
1802 print yue.traceback err
1803 print result
1804```
1805<YueDisplay>
1806
1807```yue
1808try
1809 func 1, 2, 3
1810catch err
1811 print yue.traceback err
1812
1813success, result = try
1814 func 1, 2, 3
1815catch err
1816 yue.traceback err
1817
1818try func 1, 2, 3
1819catch err
1820 print yue.traceback err
1821
1822success, result = try func 1, 2, 3
1823
1824try
1825 print "尝试中"
1826 func 1, 2, 3
1827
1828-- 使用if赋值模式
1829if success, result := try func 1, 2, 3
1830catch err
1831 print yue.traceback err
1832 print result
1833```
1834
1835</YueDisplay>
1836
1837### 错误处理简化
1838
1839`try?` 是 `try` 的功能简化语法,它不再返回 `try` 语句的布尔状态,并在成功时直接返回 `try` 代码块的结果,失败时返回 `nil` 值而非错误对象。
1840
1841```yuescript
1842a, b, c = try? func!
1843
1844-- 与空值合并运算符一起使用
1845a = (try? func!) ?? "default"
1846
1847-- 作为函数参数
1848f try? func!
1849
1850-- 带 catch 块的 try!
1851f try?
1852 print 123
1853 func!
1854catch e
1855 print e
1856 e
1857```
1858<YueDisplay>
1859
1860```yue
1861a, b, c = try? func!
1862
1863-- 与空值合并运算符一起使用
1864a = (try? func!) ?? "default"
1865
1866-- 作为函数参数
1867f try? func!
1868
1869-- 带 catch 块的 try!
1870f try?
1871 print 123
1872 func!
1873catch e
1874 print e
1875 e
1876```
1877
1878</YueDisplay>
1879
1880## 属性
1881
1882月之脚本现在提供了 Lua 5.4 新增的叫做属性的语法支持。在月之脚本编译到的 Lua 目标版本低于 5.4 时,你仍然可以同时使用`const` 和 `close` 的属性声明语法,并获得常量检查和作用域回调的功能。
1883
1884```yuescript
1885const a = 123
1886close _ = <close>: -> print "超出范围。"
1887```
1888<YueDisplay>
1889
1890```yue
1891const a = 123
1892close _ = <close>: -> print "超出范围。"
1893```
1894
1895</YueDisplay>
1896
1897你可以对进行解构得到的变量标记为常量。
1898
1899```yuescript
1900const {:a, :b, c, d} = tb
1901-- a = 1
1902```
1903<YueDisplay>
1904
1905```yue
1906const {:a, :b, c, d} = tb
1907-- a = 1
1908```
1909
1910</YueDisplay>
1911
1912你也可以声明全局变量为常量。
1913
1914```yuescript
1915global const Constant = 123
1916-- Constant = 1
1917```
1918<YueDisplay>
1919
1920```yue
1921global const Constant = 123
1922-- Constant = 1
1923```
1924
1925</YueDisplay>
1926
1927## 字面量
1928
1929Lua 中的所有基本字面量都可以在月之脚本中使用。包括数字、字符串、布尔值和 **nil**。
1930
1931但与 Lua 不同的是,单引号和双引号字符串内部允许有换行:
1932
1933```yuescript
1934some_string = "这是一个字符串
1935 并包括一个换行。"
1936
1937-- 使用#{}语法可以将表达式插入到字符串字面量中。
1938-- 字符串插值只在双引号字符串中可用。
1939print "我有#{math.random! * 100}%的把握。"
1940```
1941<YueDisplay>
1942
1943```yue
1944some_string = "这是一个字符串
1945 并包括一个换行。"
1946
1947-- 使用#{}语法可以将表达式插入到字符串字面量中。
1948-- 字符串插值只在双引号字符串中可用。
1949print "我有#{math.random! * 100}%的把握。"
1950```
1951
1952</YueDisplay>
1953
1954### 数字字面量
1955
1956你可以在数字字面量中使用下划线来增加可读性。
1957
1958```yuescript
1959integer = 1_000_000
1960hex = 0xEF_BB_BF
1961binary = 0B10011
1962```
1963<YueDisplay>
1964
1965
1966```yue
1967integer = 1_000_000
1968hex = 0xEF_BB_BF
1969binary = 0B10011
1970```
1971
1972</YueDisplay>
1973
1974### YAML 风格字符串
1975
1976使用 `|` 前缀标记一个多行 YAML 风格字符串:
1977
1978```yuescript
1979str = |
1980 key: value
1981 list:
1982 - item1
1983 - #{expr}
1984```
1985<YueDisplay>
1986
1987```yue
1988str = |
1989 key: value
1990 list:
1991 - item1
1992 - #{expr}
1993```
1994
1995</YueDisplay>
1996
1997其效果类似于原生 Lua 的多行拼接,所有文本(含换行)将被保留下来,并支持 `#{...}` 语法,通过 `tostring(expr)` 插入表达式结果。
1998
1999YAML 风格的多行字符串会自动检测首行后最小的公共缩进,并从所有行中删除该前缀空白字符。这让你可以在代码中对齐文本,但输出字符串不会带多余缩进。
2000
2001```yuescript
2002fn = ->
2003 str = |
2004 foo:
2005 bar: baz
2006 return str
2007```
2008<YueDisplay>
2009
2010```yue
2011fn = ->
2012 str = |
2013 foo:
2014 bar: baz
2015 return str
2016```
2017
2018</YueDisplay>
2019
2020输出字符串中的 foo: 对齐到行首,不会带有函数缩进空格。保留内部缩进的相对结构,适合书写结构化嵌套样式的内容。
2021
2022支持自动处理字符中的引号、反斜杠等特殊符号,无需手动转义:
2023
2024```yuescript
2025str = |
2026 path: "C:\Program Files\App"
2027 note: 'He said: "#{Hello}!"'
2028```
2029<YueDisplay>
2030
2031```yue
2032str = |
2033 path: "C:\Program Files\App"
2034 note: 'He said: "#{Hello}!"'
2035```
2036
2037</YueDisplay>
2038
2039## 函数字面量
2040
2041所有函数都是使用月之脚本的函数表达式创建的。一个简单的函数可以用箭头表示为:**->**。
2042
2043```yuescript
2044my_function = ->
2045my_function() -- 调用空函数
2046```
2047<YueDisplay>
2048
2049```yue
2050my_function = ->
2051my_function() -- 调用空函数
2052```
2053
2054</YueDisplay>
2055
2056函数体可以是紧跟在箭头后的一个语句,或者是在后面的行上使用同样缩进的一系列语句:
2057
2058```yuescript
2059func_a = -> print "你好,世界"
2060
2061func_b = ->
2062 value = 100
2063 print "这个值是:", value
2064```
2065<YueDisplay>
2066
2067```yue
2068func_a = -> print "你好,世界"
2069
2070func_b = ->
2071 value = 100
2072 print "这个值是:", value
2073```
2074
2075</YueDisplay>
2076
2077如果一个函数没有参数,可以使用 **\!** 操作符调用它,而不是空括号。使用 **\!** 调用没有参数的函数是推荐的写法。
2078
2079```yuescript
2080func_a!
2081func_b()
2082```
2083<YueDisplay>
2084
2085```yue
2086func_a!
2087func_b()
2088```
2089
2090</YueDisplay>
2091
2092带有参数的函数可以通过在箭头前加上括号中的参数名列表来进行创建:
2093
2094```yuescript
2095sum = (x, y) -> print "数字的和", x + y
2096```
2097<YueDisplay>
2098
2099```yue
2100sum = (x, y) -> print "数字的和", x + y
2101```
2102
2103</YueDisplay>
2104
2105函数可以通过在函数名后列出参数来调用。当对函数做嵌套的调用时,后面列出的参数会应用于左侧最近的函数。
2106
2107```yuescript
2108sum 10, 20
2109print sum 10, 20
2110
2111a b c "a", "b", "c"
2112```
2113<YueDisplay>
2114
2115```yue
2116sum 10, 20
2117print sum 10, 20
2118
2119a b c "a", "b", "c"
2120```
2121
2122</YueDisplay>
2123
2124为了避免在调用函数时产生歧义,也可以使用括号将参数括起来。比如在以下的例子中是必需的,这样才能确保参数被传入到正确的函数。
2125
2126```yuescript
2127print "x:", sum(10, 20), "y:", sum(30, 40)
2128```
2129<YueDisplay>
2130
2131```yue
2132print "x:", sum(10, 20), "y:", sum(30, 40)
2133```
2134
2135</YueDisplay>
2136
2137注意:函数名与开始括号之间不能有任何空格。
2138
2139函数会将函数体中的最后一个语句强制转换为返回语句,这被称作隐式返回:
2140
2141```yuescript
2142sum = (x, y) -> x + y
2143print "数字的和是", sum 10, 20
2144```
2145<YueDisplay>
2146
2147```yue
2148sum = (x, y) -> x + y
2149print "数字的和是", sum 10, 20
2150```
2151
2152</YueDisplay>
2153
2154如果你需要做显式返回,可以使用 return 关键字:
2155
2156```yuescript
2157sum = (x, y) -> return x + y
2158```
2159<YueDisplay>
2160
2161```yue
2162sum = (x, y) -> return x + y
2163```
2164
2165</YueDisplay>
2166
2167就像在Lua中一样,函数可以返回多个值。最后一个语句必须是由逗号分隔的值列表:
2168
2169```yuescript
2170mystery = (x, y) -> x + y, x - y
2171a, b = mystery 10, 20
2172```
2173<YueDisplay>
2174
2175```yue
2176mystery = (x, y) -> x + y, x - y
2177a, b = mystery 10, 20
2178```
2179
2180</YueDisplay>
2181
2182### 粗箭头
2183
2184因为在 Lua 中调用方法时,经常习惯将对象作为第一个参数传入,所以月之脚本提供了一种特殊的语法来创建自动包含 self 参数的函数。
2185
2186```yuescript
2187func = (num) => @value + num
2188```
2189<YueDisplay>
2190
2191```yue
2192func = (num) => @value + num
2193```
2194
2195</YueDisplay>
2196
2197### 参数默认值
2198
2199可以为函数的参数提供默认值。如果参数的值为 nil,则确定该参数为空。任何具有默认值的 nil 参数在函数体运行之前都会被替换。
2200
2201```yuescript
2202my_function = (name = "某物", height = 100) ->
2203 print "你好,我是", name
2204 print "我的高度是", height
2205```
2206<YueDisplay>
2207
2208```yue
2209my_function = (name = "某物", height = 100) ->
2210 print "你好,我是", name
2211 print "我的高度是", height
2212```
2213
2214</YueDisplay>
2215
2216函数参数的默认值表达式在函数体中会按参数声明的顺序进行计算。因此,在默认值的表达式中可以访问先前声明的参数。
2217
2218```yuescript
2219some_args = (x = 100, y = x + 1000) ->
2220 print x + y
2221```
2222<YueDisplay>
2223
2224```yue
2225some_args = (x = 100, y = x + 1000) ->
2226 print x + y
2227```
2228
2229</YueDisplay>
2230
2231### 多行参数
2232
2233当调用接收大量参数的函数时,将参数列表分成多行是很方便的。由于月之脚本语言对空白字符的敏感性,做参数列表的分割时务必要小心。
2234
2235如果要将参数列表写到下一行,那么当前行必须以逗号结束。并且下一行的缩进必须比当前的缩进多。一旦做了参数的缩进,所有其他参数列表的行必须保持相同的缩进级别,以成为参数列表的一部分。
2236
2237```yuescript
2238my_func 5, 4, 3,
2239 8, 9, 10
2240
2241cool_func 1, 2,
2242 3, 4,
2243 5, 6,
2244 7, 8
2245```
2246<YueDisplay>
2247
2248```yue
2249my_func 5, 4, 3,
2250 8, 9, 10
2251
2252cool_func 1, 2,
2253 3, 4,
2254 5, 6,
2255 7, 8
2256```
2257
2258</YueDisplay>
2259
2260这种调用方式可以做嵌套。并通过缩进级别来确定参数属于哪一个函数。
2261
2262```yuescript
2263my_func 5, 6, 7,
2264 6, another_func 6, 7, 8,
2265 9, 1, 2,
2266 5, 4
2267```
2268<YueDisplay>
2269
2270```yue
2271my_func 5, 6, 7,
2272 6, another_func 6, 7, 8,
2273 9, 1, 2,
2274 5, 4
2275```
2276
2277</YueDisplay>
2278
2279因为 Lua 表也使用逗号作为分隔符,这种缩进语法有助于让值成为参数列表的一部分,而不是 Lua 表的一部分。
2280
2281```yuescript
2282x = [
2283 1, 2, 3, 4, a_func 4, 5,
2284 5, 6,
2285 8, 9, 10
2286]
2287```
2288<YueDisplay>
2289
2290```yue
2291x = [
2292 1, 2, 3, 4, a_func 4, 5,
2293 5, 6,
2294 8, 9, 10
2295]
2296```
2297
2298</YueDisplay>
2299
2300有个不常见的写法可以注意一下,如果我们将在后面使用较低的缩进,我们可以为函数参数提供更深的缩进来区分列表的归属。
2301
2302```yuescript
2303y = [ my_func 1, 2, 3,
2304 4, 5,
2305 5, 6, 7
2306]
2307```
2308<YueDisplay>
2309
2310```yue
2311y = [ my_func 1, 2, 3,
2312 4, 5,
2313 5, 6, 7
2314]
2315```
2316
2317</YueDisplay>
2318
2319对于其它有代码块跟随的语句,比如条件语句,也可以通过小心安排缩进来做类似的事。比如我们可以通过调整缩进级别来控制一些值归属于哪个语句:
2320
2321```yuescript
2322if func 1, 2, 3,
2323 "你好",
2324 "世界"
2325 print "你好"
2326 print "我在if内部"
2327
2328if func 1, 2, 3,
2329 "你好",
2330 "世界"
2331 print "hello"
2332 print "我在if内部"
2333```
2334<YueDisplay>
2335
2336```yue
2337if func 1, 2, 3,
2338 "你好",
2339 "世界"
2340 print "你好"
2341 print "我在if内部"
2342
2343if func 1, 2, 3,
2344 "你好",
2345 "世界"
2346 print "你好"
2347 print "我在if内部"
2348```
2349
2350</YueDisplay>
2351
2352### 参数解构
2353
2354月之脚本支持在函数形参位置对传入对象进行解构。适用两类解构表子面量:
2355
2356- 使用 {} 包裹的字面量/对象形参,支持提供获得空字段时的默认值(例如 {:a, :b}、{a: a1 = 123})。
2357
2358- 无 {} 包裹、以键值/简写键序列开头,直至遇到其它表达式终止(例如 :a, b: b1, :c),表示从同一个对象中解构多个字段。
2359
2360```yuescript
2361f1 = (:a, :b, :c) ->
2362 print a, b, c
2363
2364f1 a: 1, b: "2", c: {}
2365
2366f2 = ({a: a1 = 123, :b = 'abc'}, c = {}) ->
2367 print a1, b, c
2368
2369arg1 = {a: 0}
2370f2 arg1, arg2
2371```
2372<YueDisplay>
2373
2374```yue
2375f1 = (:a, :b, :c) ->
2376 print a, b, c
2377
2378f1 a: 1, b: "2", c: {}
2379
2380f2 = ({a: a1 = 123, :b = 'abc'}, c = {}) ->
2381 print a1, b, c
2382
2383arg1 = {a: 0}
2384f2 arg1, arg2
2385```
2386
2387</YueDisplay>
2388
2389### 前置返回表达式
2390
2391在深度嵌套的函数体中,为了提升返回值的可读性及编写便利性,我们新增了 “前置返回表达式” 语法。其形式如下:
2392
2393```yuescript
2394findFirstEven = (list): nil ->
2395 for item in *list
2396 if type(item) == "table"
2397 for sub in *item
2398 if sub % 2 == 0
2399 return sub
2400```
2401<YueDisplay>
2402
2403```yue
2404findFirstEven = (list): nil ->
2405 for item in *list
2406 if type(item) == "table"
2407 for sub in *item
2408 if sub % 2 == 0
2409 return sub
2410```
2411
2412</YueDisplay>
2413
2414这个写法等价于:
2415
2416```yuescript
2417findFirstEven = (list) ->
2418 for item in *list
2419 if type(item) == "table"
2420 for sub in *item
2421 if sub % 2 == 0
2422 return sub
2423 nil
2424```
2425<YueDisplay>
2426
2427```yue
2428findFirstEven = (list) ->
2429 for item in *list
2430 if type(item) == "table"
2431 for sub in *item
2432 if sub % 2 == 0
2433 return sub
2434 nil
2435```
2436
2437</YueDisplay>
2438
2439唯一的区别在于:你可以将函数的返回值表达式提前写在 `->` 或 `=>` 前,用以指示该函数应隐式返回该表达式的值。这样即使在多层循环或条件判断的场景下,也无需编写尾行悬挂的返回表达式,逻辑结构会更加直观清晰。
2440
2441### 命名变长参数
2442
2443你可以使用 `(...t) ->` 语法来将变长参数自动存储到一个命名表中。这个表会包含所有传入的参数(包括 `nil` 值),并且会在表的 `n` 字段中存储实际传入的参数个数(包括 `nil` 值在内的个数)。
2444
2445```yuescript
2446f = (...t) ->
2447 print "参数个数:", t.n
2448 print "表长度:", #t
2449 for i = 1, t.n
2450 print t[i]
2451
2452f 1, 2, 3
2453f "a", "b", "c", "d"
2454f!
2455
2456-- 处理包含 nil 的情况
2457process = (...args) ->
2458 sum = 0
2459 for i = 1, args.n
2460 if args[i] != nil and type(args[i]) == "number"
2461 sum += args[i]
2462 sum
2463
2464process 1, nil, 3, nil, 5
2465```
2466<YueDisplay>
2467
2468```yue
2469f = (...t) ->
2470 print "参数个数:", t.n
2471 print "表长度:", #t
2472 for i = 1, t.n
2473 print t[i]
2474
2475f 1, 2, 3
2476f "a", "b", "c", "d"
2477f!
2478
2479-- 处理包含 nil 的情况
2480process = (...args) ->
2481 sum = 0
2482 for i = 1, args.n
2483 if args[i] != nil and type(args[i]) == "number"
2484 sum += args[i]
2485 sum
2486
2487process 1, nil, 3, nil, 5
2488```
2489
2490</YueDisplay>
2491
2492## 反向回调
2493
2494反向回调用于减少函数回调的嵌套。它们使用指向左侧的箭头,并且默认会被定义为传入后续函数调用的最后一个参数。它的语法大部分与常规箭头函数相同,只是它指向另一方向,并且后续的函数体不需要进行缩进。
2495
2496```yuescript
2497<- f
2498print "hello"
2499```
2500<YueDisplay>
2501
2502```yue
2503<- f
2504print "hello"
2505```
2506
2507</YueDisplay>
2508
2509月之脚本也提供了粗箭头反向回调函数。
2510
2511```yuescript
2512<= f
2513print @value
2514```
2515<YueDisplay>
2516
2517```yue
2518<= f
2519print @value
2520```
2521
2522</YueDisplay>
2523
2524你可以通过一个占位符指定回调函数的传参位置。
2525
2526```yuescript
2527(x) <- map _, [1, 2, 3]
2528x * 2
2529```
2530<YueDisplay>
2531
2532```yue
2533(x) <- map _, [1, 2, 3]
2534x * 2
2535```
2536
2537</YueDisplay>
2538
2539如果你希望在反向回调处理后继续编写更多其它的代码,可以使用 do 语句将不属于反向回调的代码分隔开。对于非粗箭头函数的反向回调,回调返回值的括号也是可以省略的。
2540
2541```yuescript
2542result, msg = do
2543 data <- readAsync "文件名.txt"
2544 print data
2545 info <- processAsync data
2546 check info
2547print result, msg
2548```
2549<YueDisplay>
2550
2551```yue
2552result, msg = do
2553 data <- readAsync "文件名.txt"
2554 print data
2555 info <- processAsync data
2556 check info
2557print result, msg
2558```
2559
2560</YueDisplay>
2561
2562## 表格字面量
2563
2564和 Lua 一样,表格可以通过花括号进行定义。
2565
2566```yuescript
2567some_values = [1, 2, 3, 4]
2568```
2569<YueDisplay>
2570
2571```yue
2572some_values = [1, 2, 3, 4]
2573```
2574
2575</YueDisplay>
2576
2577但与Lua不同的是,给表格中的键赋值是用 **:**(而不是 **=**)。
2578
2579```yuescript
2580some_values = {
2581 name: "Bill",
2582 age: 200,
2583 ["favorite food"]: "rice"
2584}
2585```
2586<YueDisplay>
2587
2588```yue
2589some_values = {
2590 name: "Bill",
2591 age: 200,
2592 ["favorite food"]: "rice"
2593}
2594```
2595
2596</YueDisplay>
2597
2598如果只分配一个键值对的表格,可以省略花括号。
2599
2600```yuescript
2601profile =
2602 height: "4英尺",
2603 shoe_size: 13,
2604 favorite_foods: ["冰淇淋", "甜甜圈"]
2605```
2606<YueDisplay>
2607
2608```yue
2609profile =
2610 height: "4英尺",
2611 shoe_size: 13,
2612 favorite_foods: ["冰淇淋", "甜甜圈"]
2613```
2614
2615</YueDisplay>
2616
2617可以使用换行符而不使用逗号(或两者都用)来分隔表格中的值:
2618
2619```yuescript
2620values = {
2621 1, 2, 3, 4
2622 5, 6, 7, 8
2623 name: "超人"
2624 occupation: "打击犯罪"
2625}
2626```
2627<YueDisplay>
2628
2629```yue
2630values = {
2631 1, 2, 3, 4
2632 5, 6, 7, 8
2633 name: "超人"
2634 occupation: "打击犯罪"
2635}
2636```
2637
2638</YueDisplay>
2639
2640创建单行表格字面量时,也可以省略花括号:
2641
2642```yuescript
2643my_function dance: "探戈", partner: "无"
2644
2645y = type: "狗", legs: 4, tails: 1
2646```
2647<YueDisplay>
2648
2649```yue
2650my_function dance: "探戈", partner: "无"
2651
2652y = type: "狗", legs: 4, tails: 1
2653```
2654
2655</YueDisplay>
2656
2657表格字面量的键可以使用 Lua 语言的关键字,而无需转义:
2658
2659```yuescript
2660tbl = {
2661 do: "某事"
2662 end: "饥饿"
2663}
2664```
2665<YueDisplay>
2666
2667```yue
2668tbl = {
2669 do: "某事"
2670 end: "饥饿"
2671}
2672```
2673
2674</YueDisplay>
2675
2676如果你要构造一个由变量组成的表,并希望键与变量名相同,那么可以使用 **:** 前缀操作符:
2677
2678```yuescript
2679hair = "金色"
2680height = 200
2681person = { :hair, :height, shoe_size: 40 }
2682
2683print_table :hair, :height
2684```
2685<YueDisplay>
2686
2687```yue
2688hair = "金色"
2689height = 200
2690person = { :hair, :height, shoe_size: 40 }
2691
2692print_table :hair, :height
2693```
2694
2695</YueDisplay>
2696
2697如果你希望表中字段的键是某个表达式的结果,那么可以用 **[ ]** 包裹它,就像在 Lua 中一样。如果键中有任何特殊字符,也可以直接使用字符串字面量作为键,省略方括号。
2698
2699```yuescript
2700t = {
2701 [1 + 2]: "你好"
2702 "你好 世界": true
2703}
2704```
2705<YueDisplay>
2706
2707```yue
2708t = {
2709 [1 + 2]: "你好"
2710 "你好 世界": true
2711}
2712```
2713
2714</YueDisplay>
2715
2716Lua 的表同时具有数组部分和哈希部分,但有时候你会希望在书写 Lua 表时,对 Lua 表做数组和哈希不同用法的语义区分。然后你可以用 **[ ]** 而不是 **{ }** 来编写表示数组的 Lua 表,并且不允许在数组 Lua 表中写入任何键值对。
2717
2718```yuescript
2719some_values = [ 1, 2, 3, 4 ]
2720list_with_one_element = [ 1, ]
2721```
2722<YueDisplay>
2723
2724```yue
2725some_values = [ 1, 2, 3, 4 ]
2726list_with_one_element = [ 1, ]
2727```
2728
2729</YueDisplay>
2730
2731## 推导式
2732
2733推导式为我们提供了一种便捷的语法,通过遍历现有对象并对其值应用表达式来构造出新的表格。月之脚本有两种推导式:列表推导式和表格推导式。它们最终都是产生 Lua 表格;列表推导式将值累积到类似数组的表格中,而表格推导式允许你在每次遍历时设置新表格的键和值。
2734
2735### 列表推导式
2736
2737以下操作创建了一个 items 表的副本,但所有包含的值都翻倍了。
2738
2739```yuescript
2740items = [1, 2, 3, 4]
2741doubled = [item * 2 for i, item in ipairs items]
2742```
2743<YueDisplay>
2744
2745```yue
2746items = [1, 2, 3, 4]
2747doubled = [item * 2 for i, item in ipairs items]
2748```
2749
2750</YueDisplay>
2751
2752可以使用 `when` 子句筛选新表中包含的项目:
2753
2754```yuescript
2755slice = [item for i, item in ipairs items when i > 1 and i < 3]
2756```
2757<YueDisplay>
2758
2759```yue
2760slice = [item for i, item in ipairs items when i > 1 and i < 3]
2761```
2762
2763</YueDisplay>
2764
2765因为我们常常需要迭代数值索引表的值,所以引入了 **\*** 操作符来做语法简化。doubled 示例可以重写为:
2766
2767```yuescript
2768doubled = [item * 2 for item in *items]
2769```
2770<YueDisplay>
2771
2772```yue
2773doubled = [item * 2 for item in *items]
2774```
2775
2776</YueDisplay>
2777
2778在列表推导式中,你还可以使用展开操作符 `...` 来实现对列表嵌套层级进行扁平化的处理:
2779
2780```yuescript
2781data =
2782 a: [1, 2, 3]
2783 b: [4, 5, 6]
2784
2785flat = [...v for k,v in pairs data]
2786-- flat 现在为 [1, 2, 3, 4, 5, 6]
2787```
2788<YueDisplay>
2789
2790```yue
2791data =
2792 a: [1, 2, 3]
2793 b: [4, 5, 6]
2794
2795flat = [...v for k,v in pairs data]
2796-- flat 现在为 [1, 2, 3, 4, 5, 6]
2797```
2798
2799</YueDisplay>
2800
2801for 和 when 子句可以根据需要进行链式操作。唯一的要求是推导式中至少要有一个 for 子句。
2802
2803使用多个 for 子句与使用多重循环的效果相同:
2804
2805```yuescript
2806x_coords = [4, 5, 6, 7]
2807y_coords = [9, 2, 3]
2808
2809points = [ [x, y] for x in *x_coords \
2810for y in *y_coords]
2811```
2812<YueDisplay>
2813
2814```yue
2815x_coords = [4, 5, 6, 7]
2816y_coords = [9, 2, 3]
2817
2818points = [ [x, y] for x in *x_coords \
2819for y in *y_coords]
2820```
2821
2822</YueDisplay>
2823
2824在推导式中也可以使用简单的数值 for 循环:
2825
2826```yuescript
2827evens = [i for i = 1, 100 when i % 2 == 0]
2828```
2829<YueDisplay>
2830
2831```yue
2832evens = [i for i = 1, 100 when i % 2 == 0]
2833```
2834
2835</YueDisplay>
2836
2837### 表格推导式
2838
2839表格推导式和列表推导式的语法非常相似,只是要使用 **{** 和 **}** 并从每次迭代中取两个值。
2840
2841以下示例生成了表格 thing 的副本:
2842
2843```yuescript
2844thing = {
2845 color: "red"
2846 name: "fast"
2847 width: 123
2848}
2849
2850thing_copy = {k, v for k, v in pairs thing}
2851```
2852<YueDisplay>
2853
2854```yue
2855thing = {
2856 color: "red"
2857 name: "fast"
2858 width: 123
2859}
2860
2861thing_copy = {k, v for k, v in pairs thing}
2862```
2863
2864</YueDisplay>
2865
2866```yuescript
2867no_color = {k, v for k, v in pairs thing when k != "color"}
2868```
2869<YueDisplay>
2870
2871```yue
2872no_color = {k, v for k, v in pairs thing when k != "color"}
2873```
2874
2875</YueDisplay>
2876
2877**\*** 操作符在表格推导式中能使用。在下面的例子里,我们为几个数字创建了一个平方根查找表。
2878
2879```yuescript
2880numbers = [1, 2, 3, 4]
2881sqrts = {i, math.sqrt i for i in *numbers}
2882```
2883<YueDisplay>
2884
2885```yue
2886numbers = [1, 2, 3, 4]
2887sqrts = {i, math.sqrt i for i in *numbers}
2888```
2889
2890</YueDisplay>
2891
2892表格推导式中的键值元组也可以来自单个表达式,在这种情况下,表达式在计算后应返回两个值。第一个用作键,第二个用作值:
2893
2894在下面的示例中,我们将一些数组转换为一个表,其中每个数组里的第一项是键,第二项是值。
2895
2896```yuescript
2897tuples = [ ["hello", "world"], ["foo", "bar"]]
2898tbl = {unpack tuple for tuple in *tuples}
2899```
2900<YueDisplay>
2901
2902```yue
2903tuples = [ ["hello", "world"], ["foo", "bar"]]
2904tbl = {unpack tuple for tuple in *tuples}
2905```
2906
2907</YueDisplay>
2908
2909### 切片
2910
2911当使用 **\*** 操作符时,月之脚本还提供了一种特殊的语法来限制要遍历的列表范围。这个语法也相当于在 for 循环中设置迭代边界和步长。
2912
2913下面的案例中,我们在切片中设置最小和最大边界,取索引在 1 到 5 之间(包括 1 和 5)的所有项目:
2914
2915```yuescript
2916slice = [item for item in *items[1, 5]]
2917```
2918<YueDisplay>
2919
2920```yue
2921slice = [item for item in *items[1, 5]]
2922```
2923
2924</YueDisplay>
2925
2926切片的任意参数都可以省略,并会使用默认值。在如下示例中,如果省略了最大索引边界,它默认为表的长度。使下面的代码取除第一个元素之外的所有元素:
2927
2928```yuescript
2929slice = [item for item in *items[2,]]
2930```
2931<YueDisplay>
2932
2933```yue
2934slice = [item for item in *items[2,]]
2935```
2936
2937</YueDisplay>
2938
2939如果省略了最小边界,便默认会设置为 1。这里我们只提供一个步长,并留下其他边界为空。这样会使得代码取出所有奇数索引的项目:(1, 3, 5, …)
2940
2941```yuescript
2942slice = [item for item in *items[,,2]]
2943```
2944<YueDisplay>
2945
2946
2947```yue
2948slice = [item for item in *items[,,2]]
2949```
2950
2951</YueDisplay>
2952
2953最小和最大边界都可以是负数,使用负数意味着边界是从表的末尾开始计算的。
2954
2955```yuescript
2956-- 取最后4个元素
2957slice = [item for item in *items[-4,-1]]
2958```
2959<YueDisplay>
2960
2961```yue
2962-- 取最后4个元素
2963slice = [item for item in *items[-4,-1]]
2964```
2965
2966</YueDisplay>
2967
2968切片的步长也可以是负数,这意味着元素会以相反的顺序被取出。
2969
2970```yuescript
2971reverse_slice = [item for item in *items[-1,1,-1]]
2972```
2973<YueDisplay>
2974
2975```yue
2976reverse_slice = [item for item in *items[-1,1,-1]]
2977```
2978
2979</YueDisplay>
2980
2981#### 切片表达式
2982
2983切片也可以作为表达式来使用。可以用于获取一个表包含的子列表。
2984
2985```yuescript
2986-- 取第2和第4个元素作为新的列表
2987sub_list = items[2, 4]
2988```
2989<YueDisplay>
2990
2991```yue
2992-- 取第2和第4个元素作为新的列表
2993sub_list = items[2, 4]
2994```
2995
2996</YueDisplay>
2997
2998## for 循环
2999
3000Lua 中有两种 for 循环形式,数字型和通用型:
3001
3002```yuescript
3003for i = 10, 20
3004 print i
3005
3006for k = 1, 15, 2 -- 提供了一个遍历的步长
3007 print k
3008
3009for key, value in pairs object
3010 print key, value
3011```
3012<YueDisplay>
3013
3014```yue
3015for i = 10, 20
3016 print i
3017
3018for k = 1, 15, 2 -- 提供了一个遍历的步长
3019 print k
3020
3021for key, value in pairs object
3022 print key, value
3023```
3024
3025</YueDisplay>
3026
3027可以使用切片和 **\*** 操作符,就像在列表推导中一样:
3028
3029```yuescript
3030for item in *items[2, 4]
3031 print item
3032```
3033<YueDisplay>
3034
3035```yue
3036for item in *items[2, 4]
3037 print item
3038```
3039
3040</YueDisplay>
3041
3042当代码语句只有一行时,循环语句也都可以写作更短的语法:
3043
3044```yuescript
3045for item in *items do print item
3046
3047for j = 1, 10, 3 do print j
3048```
3049<YueDisplay>
3050
3051```yue
3052for item in *items do print item
3053
3054for j = 1, 10, 3 do print j
3055```
3056
3057</YueDisplay>
3058
3059for 循环也可以用作表达式。for 循环主体中的最后一条语句会被强制转换为一个返回值的表达式,并会将表达式计算结果的值追加到一个作为结果的数组表中。
3060
3061将每个偶数加倍:
3062
3063```yuescript
3064doubled_evens = for i = 1, 20
3065 if i % 2 == 0
3066 i * 2
3067 else
3068 i
3069```
3070<YueDisplay>
3071
3072```yue
3073doubled_evens = for i = 1, 20
3074 if i % 2 == 0
3075 i * 2
3076 else
3077 i
3078```
3079
3080</YueDisplay>
3081
3082此外,for 循环还支持带返回值的 break 语句,这样循环本身就可以作为一个表达式,在满足条件时提前退出并返回有意义的结果。
3083
3084例如,查找第一个大于 10 的数字:
3085
3086```yuescript
3087first_large = for n in *numbers
3088 break n if n > 10
3089```
3090<YueDisplay>
3091
3092```yue
3093first_large = for n in *numbers
3094 break n if n > 10
3095```
3096
3097</YueDisplay>
3098
3099你还可以结合 for 循环表达式与 continue 语句来过滤值。
3100
3101注意出现在函数体末尾的 for 循环,不会被当作是一个表达式并将循环结果累积到一个列表中作为返回值(相反,函数将返回 nil)。如果要函数末尾的循环转换为列表表达式,可以显式地使用返回语句加 for 循环表达式。
3102
3103```yuescript
3104func_a = -> for i = 1, 10 do print i
3105func_b = -> return for i = 1, 10 do i
3106
3107print func_a! -- 打印 nil
3108print func_b! -- 打印 table 对象
3109```
3110<YueDisplay>
3111
3112```yue
3113func_a = -> for i = 1, 10 do print i
3114func_b = -> return for i = 1, 10 do i
3115
3116print func_a! -- 打印 nil
3117print func_b! -- 打印 table 对象
3118```
3119
3120</YueDisplay>
3121
3122这样做是为了避免在不需要返回循环结果的函数,创建无效的返回值表格。
3123
3124## repeat 循环
3125
3126repeat 循环是从 Lua 语言中搬过来的相似语法:
3127
3128```yuescript
3129i = 10
3130repeat
3131 print i
3132 i -= 1
3133until i == 0
3134```
3135<YueDisplay>
3136
3137```yue
3138i = 10
3139repeat
3140 print i
3141 i -= 1
3142until i == 0
3143```
3144
3145</YueDisplay>
3146
3147## while 循环
3148
3149在月之脚本中的 while 循环有四种写法:
3150
3151```yuescript
3152i = 10
3153while i > 0
3154 print i
3155 i -= 1
3156
3157while running == true do my_function!
3158```
3159<YueDisplay>
3160
3161```yue
3162i = 10
3163while i > 0
3164 print i
3165 i -= 1
3166
3167while running == true do my_function!
3168```
3169
3170</YueDisplay>
3171
3172```yuescript
3173i = 10
3174until i == 0
3175 print i
3176 i -= 1
3177
3178until running == false do my_function!
3179```
3180<YueDisplay>
3181
3182```yue
3183i = 10
3184until i == 0
3185 print i
3186 i -= 1
3187until running == false do my_function!
3188```
3189
3190</YueDisplay>
3191
3192像 for 循环的语法一样,while 循环也可以作为一个表达式使用。为了使函数返回 while 循环的累积列表值,必须明确使用返回语句返回 while 循环表达式。
3193
3194## 继续
3195
3196继续语句可以用来跳出当前的循环迭代。
3197
3198```yuescript
3199i = 0
3200while i < 10
3201 i += 1
3202 continue if i % 2 == 0
3203 print i
3204```
3205<YueDisplay>
3206
3207```yue
3208i = 0
3209while i < 10
3210 i += 1
3211 continue if i % 2 == 0
3212 print i
3213```
3214
3215</YueDisplay>
3216
3217继续语句也可以与各种循环表达式一起使用,以防止当前的循环迭代结果累积到结果列表中。以下示例将数组表过滤为仅包含偶数的数组:
3218
3219```yuescript
3220my_numbers = [1, 2, 3, 4, 5, 6]
3221odds = for x in *my_numbers
3222 continue if x % 2 == 1
3223 x
3224```
3225<YueDisplay>
3226
3227```yue
3228my_numbers = [1, 2, 3, 4, 5, 6]
3229odds = for x in *my_numbers
3230 continue if x % 2 == 1
3231 x
3232```
3233
3234</YueDisplay>
3235
3236## 条件语句
3237
3238```yuescript
3239have_coins = false
3240if have_coins
3241 print "有硬币"
3242else
3243 print "没有硬币"
3244```
3245<YueDisplay>
3246
3247```yue
3248have_coins = false
3249if have_coins
3250 print "有硬币"
3251else
3252 print "没有硬币"
3253```
3254
3255</YueDisplay>
3256
3257对于简单的语句,也可以使用简短的语法:
3258
3259```yuescript
3260have_coins = false
3261if have_coins then print "有硬币" else print "没有硬币"
3262```
3263<YueDisplay>
3264
3265```yue
3266have_coins = false
3267if have_coins then print "有硬币" else print "没有硬币"
3268```
3269
3270</YueDisplay>
3271
3272因为if语句可以用作表达式,所以也可以这样写:
3273
3274```yuescript
3275have_coins = false
3276print if have_coins then "有硬币" else "没有硬币"
3277```
3278<YueDisplay>
3279
3280```yue
3281have_coins = false
3282print if have_coins then "有硬币" else "没有硬币"
3283```
3284
3285</YueDisplay>
3286
3287条件语句也可以作为表达式用在返回语句和赋值语句中:
3288
3289```yuescript
3290is_tall = (name) ->
3291 if name == "Rob"
3292 true
3293 else
3294 false
3295
3296message = if is_tall "Rob"
3297 "我很高"
3298else
3299 "我不是很高"
3300
3301print message -- 打印: 我很高
3302```
3303<YueDisplay>
3304
3305```yue
3306is_tall = (name) ->
3307 if name == "Rob"
3308 true
3309 else
3310 false
3311
3312message = if is_tall "Rob"
3313 "我很高"
3314else
3315 "我不是很高"
3316
3317print message -- 打印: 我很高
3318```
3319
3320</YueDisplay>
3321
3322if 的反义词是 unless(相当于 if not,正如“如果”对应“除非”):
3323
3324```yuescript
3325unless os.date("%A") == "Monday"
3326 print "今天不是星期一!"
3327```
3328<YueDisplay>
3329
3330
3331```yue
3332unless os.date("%A") == "Monday"
3333 print "今天不是星期一!"
3334```
3335
3336</YueDisplay>
3337
3338```yuescript
3339print "你真幸运!" unless math.random! > 0.1
3340```
3341<YueDisplay>
3342
3343```yue
3344print "你真幸运!" unless math.random! > 0.1
3345```
3346
3347</YueDisplay>
3348
3349### 范围表达式
3350
3351你可以使用范围表达式来编写进行范围检查的代码。
3352
3353```yuescript
3354a = 5
3355
3356if a in [1, 3, 5, 7]
3357 print "检查离散值的相等性"
3358
3359if a in list
3360 print "检查`a`是否在列表中"
3361```
3362<YueDisplay>
3363
3364```yue
3365a = 5
3366
3367if a in [1, 3, 5, 7]
3368 print "检查离散值的相等性"
3369
3370if a in list
3371 print "检查`a`是否在列表中"
3372```
3373
3374</YueDisplay>
3375
3376```yuescript
3377print "你很幸运!" unless math.random! > 0.1
3378```
3379<YueDisplay>
3380
3381```yue
3382print "你很幸运!" unless math.random! > 0.1
3383```
3384
3385</YueDisplay>
3386
3387## 代码行修饰符
3388
3389为了方便编写代码,循环语句和 if 语句可以应用于单行代码语句的末尾:
3390
3391```yuescript
3392print "你好,世界" if name == "Rob"
3393```
3394<YueDisplay>
3395
3396```yue
3397print "你好,世界" if name == "Rob"
3398```
3399
3400</YueDisplay>
3401
3402修饰 for 循环的示例:
3403
3404```yuescript
3405print "项目: ", item for item in *items
3406```
3407<YueDisplay>
3408
3409```yue
3410print "项目: ", item for item in *items
3411```
3412
3413</YueDisplay>
3414
3415修饰 while 循环的示例:
3416
3417```yuescript
3418game\update! while game\isRunning!
3419
3420reader\parse_line! until reader\eof!
3421```
3422<YueDisplay>
3423
3424```yue
3425game\update! while game\isRunning!
3426
3427reader\parse_line! until reader\eof!
3428```
3429
3430</YueDisplay>
3431
3432## switch 语句
3433
3434switch 语句是为了简化检查一系列相同值的if语句而提供的简写语法。要注意用于比较检查的目标值只会计算一次。和 if 语句一样,switch 语句在最后可以接一个 else 代码块来处理没有匹配的情况。在生成的 Lua 代码中,进行比较是使用 == 操作符完成的。switch 语句中也可以使用赋值表达式来储存临时变量值。
3435
3436```yuescript
3437switch name := "Dan"
3438 when "Robert"
3439 print "你是Robert"
3440 when "Dan", "Daniel"
3441 print "你的名字是Dan"
3442 else
3443 print "我不认识你,你的名字是#{name}"
3444```
3445<YueDisplay>
3446
3447```yue
3448switch name := "Dan"
3449 when "Robert"
3450 print "你是Robert"
3451 when "Dan", "Daniel"
3452 print "你的名字是Dan"
3453 else
3454 print "我不认识你,你的名字是#{name}"
3455```
3456
3457</YueDisplay>
3458
3459switch 语句的 when 子句中可以通过使用逗号分隔的列表来匹配多个值。
3460
3461switch 语句也可以作为表达式使用,下面我们可以将 switch 语句返回的结果分配给一个变量:
3462
3463```yuescript
3464b = 1
3465next_number = switch b
3466 when 1
3467 2
3468 when 2
3469 3
3470 else
3471 error "数字数得太大了!"
3472```
3473<YueDisplay>
3474
3475```yue
3476b = 1
3477next_number = switch b
3478 when 1
3479 2
3480 when 2
3481 3
3482 else
3483 error "数字数得太大了!"
3484```
3485
3486</YueDisplay>
3487
3488我们可以使用 then 关键字在 when 子句的同一行上编写处理代码。else 代码块的后续代码中要写在同一行上不需要额外的关键字。
3489
3490```yuescript
3491msg = switch math.random(1, 5)
3492 when 1 then "你很幸运"
3493 when 2 then "你差点很幸运"
3494 else "不太幸运"
3495```
3496<YueDisplay>
3497
3498```yue
3499msg = switch math.random(1, 5)
3500 when 1 then "你很幸运"
3501 when 2 then "你差点很幸运"
3502 else "不太幸运"
3503```
3504
3505</YueDisplay>
3506
3507如果在编写 switch 语句时希望少写一个缩进,那么你可以把第一个 when 子句放在 switch 开始语句的第一行,然后后续的子语句就都可以都少写一个缩进。
3508
3509```yuescript
3510switch math.random(1, 5)
3511 when 1
3512 print "你很幸运" -- 两个缩进级别
3513 else
3514 print "不太幸运"
3515
3516switch math.random(1, 5) when 1
3517 print "你很幸运" -- 一个缩进级别
3518else
3519 print "不太幸运"
3520```
3521<YueDisplay>
3522
3523```yue
3524switch math.random(1, 5)
3525 when 1
3526 print "你很幸运" -- 两个缩进级别
3527 else
3528 print "不太幸运"
3529
3530switch math.random(1, 5) when 1
3531 print "你很幸运" -- 一个缩进级别
3532else
3533 print "不太幸运"
3534```
3535
3536</YueDisplay>
3537
3538值得注意的是,在生成 Lua 代码时,我们要做检查的目标变量会放在 == 表达式的右侧。当你希望给 when 子句的比较对象定义一个 \_\_eq 元方法来重载判断逻辑时,可能会有用。
3539
3540### 表格匹配
3541
3542在 switch 的 when 子句中,如果期待检查目标是一个表格,且可以通过特定的结构进行解构并获得非 nil 值,那么你可以尝试使用表格匹配的语法。
3543
3544```yuescript
3545items =
3546 * x: 100
3547 y: 200
3548 * width: 300
3549 height: 400
3550
3551for item in *items
3552 switch item
3553 when :x, :y
3554 print "Vec2 #{x}, #{y}"
3555 when :width, :height
3556 print "尺寸 #{width}, #{height}"
3557```
3558<YueDisplay>
3559
3560```yue
3561items =
3562 * x: 100
3563 y: 200
3564 * width: 300
3565 height: 400
3566
3567for item in *items
3568 switch item
3569 when :x, :y
3570 print "Vec2 #{x}, #{y}"
3571 when :width, :height
3572 print "尺寸 #{width}, #{height}"
3573```
3574
3575</YueDisplay>
3576
3577你可以使用默认值来选择性地解构表格的某些字段。
3578
3579```yuescript
3580item = {}
3581
3582{pos: {:x = 50, :y = 200}} = item -- 获取错误:尝试索引nil值(字段'pos')
3583
3584switch item
3585 when {pos: {:x = 50, :y = 200}}
3586 print "Vec2 #{x}, #{y}" -- 表格解构仍然会通过
3587```
3588<YueDisplay>
3589
3590```yue
3591item = {}
3592
3593{pos: {:x = 50, :y = 200}} = item -- 获取错误:尝试索引nil值(字段'pos')
3594
3595switch item
3596 when {pos: {:x = 50, :y = 200}}
3597 print "Vec2 #{x}, #{y}" -- 表格解构仍然会通过
3598```
3599
3600</YueDisplay>
3601
3602你也可以匹配数组元素、表格字段,甚至使用数组或表格字面量来匹配嵌套的结构。
3603
3604匹配数组元素。
3605
3606```yuescript
3607switch tb
3608 when [1, 2, 3]
3609 print "1, 2, 3"
3610 when [1, b, 3]
3611 print "1, #{b}, 3"
3612 when [1, 2, b = 3] -- 变量b有默认值
3613 print "1, 2, #{b}"
3614```
3615<YueDisplay>
3616
3617```yue
3618switch tb
3619 when [1, 2, 3]
3620 print "1, 2, 3"
3621 when [1, b, 3]
3622 print "1, #{b}, 3"
3623 when [1, 2, b = 3] -- 变量b有默认值
3624 print "1, 2, #{b}"
3625```
3626
3627</YueDisplay>
3628
3629匹配表格字段。
3630
3631```yuescript
3632switch tb
3633 when success: true, :result
3634 print "成功", result
3635 when success: false
3636 print "失败", result
3637 else
3638 print "无效值"
3639```
3640<YueDisplay>
3641
3642```yue
3643switch tb
3644 when success: true, :result
3645 print "成功", result
3646 when success: false
3647 print "失败", result
3648 else
3649 print "无效值"
3650```
3651
3652</YueDisplay>
3653
3654匹配嵌套的表格结构。
3655
3656```yuescript
3657switch tb
3658 when data: {type: "success", :content}
3659 print "成功", content
3660 when data: {type: "error", :content}
3661 print "失败", content
3662 else
3663 print "无效值"
3664```
3665<YueDisplay>
3666
3667```yue
3668switch tb
3669 when data: {type: "success", :content}
3670 print "成功", content
3671 when data: {type: "error", :content}
3672 print "失败", content
3673 else
3674 print "无效值"
3675```
3676
3677</YueDisplay>
3678
3679匹配表格数组。
3680
3681```yuescript
3682switch tb
3683 when [
3684 {a: 1, b: 2}
3685 {a: 3, b: 4}
3686 {a: 5, b: 6}
3687 fourth
3688 ]
3689 print "匹配成功", fourth
3690```
3691<YueDisplay>
3692
3693```yue
3694switch tb
3695 when [
3696 {a: 1, b: 2}
3697 {a: 3, b: 4}
3698 {a: 5, b: 6}
3699 fourth
3700 ]
3701 print "匹配成功", fourth
3702```
3703
3704</YueDisplay>
3705
3706匹配一个列表并捕获特定范围内的元素。
3707
3708```yuescript
3709segments = ["admin", "users", "logs", "view"]
3710switch segments
3711 when [...groups, resource, action]
3712 print "Group:", groups -- 打印: {"admin", "users"}
3713 print "Resource:", resource -- 打印: "logs"
3714 print "Action:", action -- 打印: "view"
3715```
3716<YueDisplay>
3717
3718```yue
3719segments = ["admin", "users", "logs", "view"]
3720switch segments
3721 when [...groups, resource, action]
3722 print "Group:", groups -- 打印: {"admin", "users"}
3723 print "Resource:", resource -- 打印: "logs"
3724 print "Action:", action -- 打印: "view"
3725```
3726
3727</YueDisplay>
3728
3729## 面向对象编程
3730
3731在以下的示例中,月之脚本生成的 Lua 代码可能看起来会很复杂。所以最好主要关注月之脚本代码层面的意义,然后如果你想知道关于面向对象功能的实现细节,再查看 Lua 代码。
3732
3733一个简单的类:
3734
3735```yuescript
3736class Inventory
3737 new: =>
3738 @items = {}
3739
3740 add_item: (name) =>
3741 if @items[name]
3742 @items[name] += 1
3743 else
3744 @items[name] = 1
3745```
3746<YueDisplay>
3747
3748```yue
3749class Inventory
3750 new: =>
3751 @items = {}
3752
3753 add_item: (name) =>
3754 if @items[name]
3755 @items[name] += 1
3756 else
3757 @items[name] = 1
3758```
3759
3760</YueDisplay>
3761
3762在月之脚本中采用面向对象的编程方式时,通常会使用类声明语句结合 Lua 表格字面量来做类定义。这个类的定义包含了它的所有方法和属性。在这种结构中,键名为 “new” 的成员扮演了一个重要的角色,是作为构造函数来使用。
3763
3764值得注意的是,类中的方法都采用了粗箭头函数语法。当在类的实例上调用方法时,该实例会自动作为第一个参数被传入,因此粗箭头函数用于生成一个名为 “self” 的参数。
3765
3766此外,“@” 前缀在变量名上起到了简化作用,代表 “self”。例如,`@items` 就等同于 `self.items`。
3767
3768为了创建类的一个新实例,可以将类名当作一个函数来调用,这样就可以生成并返回一个新的实例。
3769
3770```yuescript
3771inv = Inventory!
3772inv\add_item "t-shirt"
3773inv\add_item "pants"
3774```
3775<YueDisplay>
3776
3777
3778```yue
3779inv = Inventory!
3780inv\add_item "t-shirt"
3781inv\add_item "pants"
3782```
3783
3784</YueDisplay>
3785
3786在月之脚本的类中,由于需要将类的实例作为参数传入到调用的方法中,因此使用了 **\\** 操作符做类的成员函数调用。
3787
3788需要特别注意的是,类的所有属性在其实例之间是共享的。这对于函数类型的成员属性通常不会造成问题,但对于其他类型的属性,可能会导致意外的结果。
3789
3790例如,在下面的示例中,clothes 属性在所有实例之间共享。因此,对这个属性在一个实例中的修改,将会影响到其他所有实例。
3791
3792```yuescript
3793class Person
3794 clothes: []
3795 give_item: (name) =>
3796 table.insert @clothes, name
3797
3798a = Person!
3799b = Person!
3800
3801a\give_item "pants"
3802b\give_item "shirt"
3803
3804-- 会同时打印出裤子和衬衫
3805print item for item in *a.clothes
3806```
3807<YueDisplay>
3808
3809```yue
3810class Person
3811 clothes: []
3812 give_item: (name) =>
3813 table.insert @clothes, name
3814
3815a = Person!
3816b = Person!
3817
3818a\give_item "pants"
3819b\give_item "shirt"
3820
3821-- 会同时打印出裤子和衬衫
3822print item for item in *a.clothes
3823```
3824
3825</YueDisplay>
3826
3827避免这个问题的正确方法是在构造函数中创建对象的可变状态:
3828
3829```yuescript
3830class Person
3831 new: =>
3832 @clothes = []
3833```
3834<YueDisplay>
3835
3836```yue
3837class Person
3838 new: =>
3839 @clothes = []
3840```
3841
3842</YueDisplay>
3843
3844### 继承
3845
3846`extends` 关键字可以在类声明中使用,以继承另一个类的属性和方法。
3847
3848```yuescript
3849class BackPack extends Inventory
3850 size: 10
3851 add_item: (name) =>
3852 if #@items > size then error "背包已满"
3853 super name
3854```
3855<YueDisplay>
3856
3857```yue
3858class BackPack extends Inventory
3859 size: 10
3860 add_item: (name) =>
3861 if #@items > size then error "背包已满"
3862 super name
3863```
3864
3865</YueDisplay>
3866
3867
3868在这一部分,我们对月之脚本中的 `Inventory` 类进行了扩展,加入了对可以携带物品数量的限制。
3869
3870在这个特定的例子中,子类并没有定义自己的构造函数。因此,当创建一个新的实例时,系统会默认调用父类的构造函数。但如果我们在子类中定义了构造函数,我们可以利用 `super` 方法来调用并执行父类的构造函数。
3871
3872此外,当一个类继承自另一个类时,它会尝试调用父类上的 `__inherited` 方法(如果这个方法存在的话),以此来向父类发送通知。这个 `__inherited` 函数接受两个参数:被继承的父类和继承的子类。
3873
3874```yuescript
3875class Shelf
3876 @__inherited: (child) =>
3877 print @__name, "被", child.__name, "继承"
3878
3879-- 将打印: Shelf 被 Cupboard 继承
3880class Cupboard extends Shelf
3881```
3882<YueDisplay>
3883
3884```yue
3885class Shelf
3886 @__inherited: (child) =>
3887 print @__name, "被", child.__name, "继承"
3888
3889-- 将打印: Shelf 被 Cupboard 继承
3890class Cupboard extends Shelf
3891```
3892
3893</YueDisplay>
3894
3895### super 关键字
3896
3897`super` 是一个特别的关键字,它有两种不同的使用方式:既可以当作一个对象来看待,也可以像调用函数那样使用。它仅在类的内部使用时具有特殊的功能。
3898
3899当 `super` 被作为一个函数调用时,它将调用父类中与之同名的函数。此时,当前的 `self` 会自动作为第一个参数传递,正如上面提到的继承示例所展示的那样。
3900
3901在将 `super` 当作普通值使用时,它实际上是对父类对象的引用。通过这种方式,我们可以访问父类中可能被子类覆盖的值,就像访问任何普通对象一样。
3902
3903此外,当使用 `\` 操作符与 `super` 一起使用时,`self`将被插入为第一个参数,而不是使用 `super` 本身的值。而在使用`.`操作符来检索函数时,则会返回父类中的原始函数。
3904
3905下面是一些使用 `super` 的不同方法的示例:
3906
3907```yuescript
3908class MyClass extends ParentClass
3909 a_method: =>
3910 -- 以下效果相同:
3911 super "你好", "世界"
3912 super\a_method "你好", "世界"
3913 super.a_method self, "你好", "世界"
3914
3915 -- super 作为值等于父类:
3916 assert super == ParentClass
3917```
3918<YueDisplay>
3919
3920```yue
3921class MyClass extends ParentClass
3922 a_method: =>
3923 -- 以下效果相同:
3924 super "你好", "世界"
3925 super\a_method "你好", "世界"
3926 super.a_method self, "你好", "世界"
3927
3928 -- super 作为值等于父类:
3929 assert super == ParentClass
3930```
3931
3932</YueDisplay>
3933
3934**super** 也可以用在函数存根的左侧。唯一的主要区别是,生成的函数不是绑定到 super 的值,而是绑定到 self。
3935
3936### 类型
3937
3938每个类的实例都带有它的类型。这存储在特殊的 \_\_class 属性中。此属性会保存类对象。类对象是我们用来构建新实例的对象。我们还可以索引类对象以检索类方法和属性。
3939
3940```yuescript
3941b = BackPack!
3942assert b.__class == BackPack
3943
3944print BackPack.size -- 打印 10
3945```
3946<YueDisplay>
3947
3948```yue
3949b = BackPack!
3950assert b.__class == BackPack
3951
3952print BackPack.size -- 打印 10
3953```
3954
3955</YueDisplay>
3956
3957### 类对象
3958
3959
3960在月之脚本中,当我们编写类的定义语句时,实际上是在创建一个类对象。这个类对象被保存在一个与该类同名的变量中。
3961
3962类对象具有函数的特性,可以被调用来创建新的实例。这正是我们在之前示例中所展示的创建类实例的方式。
3963
3964一个类由两个表构成:类表本身和一个基表。基表作为所有实例的元表。在类声明中列出的所有属性都存放在基表中。
3965
3966如果在类对象的元表中找不到某个属性,系统会从基表中检索该属性。这就意味着我们可以直接从类本身访问到其方法和属性。
3967
3968需要特别注意的是,对类对象的赋值并不会影响到基表,因此这不是向实例添加新方法的正确方式。相反,需要直接修改基表。关于这点,可以参考下面的 “__base” 字段。
3969
3970此外,类对象包含几个特殊的属性:当类被声明时,类的名称会作为一个字符串存储在类对象的 “__name” 字段中。
3971
3972```yuescript
3973print BackPack.__name -- 打印 Backpack
3974```
3975<YueDisplay>
3976
3977```yue
3978print BackPack.__name -- 打印 Backpack
3979```
3980
3981</YueDisplay>
3982
3983基础对象被保存在一个名为 `__base` 的特殊表中。我们可以编辑这个表,以便为那些已经创建出来的实例和还未创建的实例增加新的功能。
3984
3985另外,如果一个类是从另一个类派生而来的,那么其父类对象则会被存储在名为 `__parent` 的地方。这种机制允许在类之间实现继承和功能扩展。
3986
3987### 类变量
3988
3989我们可以直接在类对象中创建变量,而不是在类的基对象中,通过在类声明中的属性名前使用 @。
3990
3991```yuescript
3992class Things
3993 @some_func: => print "Hello from", @__name
3994
3995Things\some_func!
3996
3997-- 类变量在实例中不可见
3998assert Things().some_func == nil
3999```
4000<YueDisplay>
4001
4002```yue
4003class Things
4004 @some_func: => print "Hello from", @__name
4005
4006Things\some_func!
4007
4008-- 类变量在实例中不可见
4009assert Things().some_func == nil
4010```
4011
4012</YueDisplay>
4013
4014在表达式中,我们可以使用 @@ 来访问存储在 `self.__class` 中的值。因此,`@@hello` 是 `self.__class.hello` 的简写。
4015
4016```yuescript
4017class Counter
4018 @count: 0
4019
4020 new: =>
4021 @@count += 1
4022
4023Counter!
4024Counter!
4025
4026print Counter.count -- 输出 2
4027```
4028<YueDisplay>
4029
4030```yue
4031class Counter
4032 @count: 0
4033
4034 new: =>
4035 @@count += 1
4036
4037Counter!
4038Counter!
4039
4040print Counter.count -- 输出 2
4041```
4042
4043</YueDisplay>
4044
4045@@ 的调用语义与 @ 类似。调用 @@ 时,会使用 Lua 的冒号语法将类作为第一个参数传入。
4046
4047```yuescript
4048@@hello 1,2,3,4
4049```
4050<YueDisplay>
4051
4052```yue
4053@@hello 1,2,3,4
4054```
4055
4056</YueDisplay>
4057
4058### 类声明语句
4059
4060在类声明的主体中,除了键/值对外,我们还可以编写普通的表达式。在这种类声明体中的普通代码的上下文中,self 等于类对象,而不是实例对象。
4061
4062以下是创建类变量的另一种方法:
4063
4064```yuescript
4065class Things
4066 @class_var = "hello world"
4067```
4068<YueDisplay>
4069
4070```yue
4071class Things
4072 @class_var = "hello world"
4073```
4074
4075</YueDisplay>
4076
4077这些表达式会在所有属性被添加到类的基对象后执行。
4078
4079在类的主体中声明的所有变量都会限制作用域只在类声明的范围。这对于放置只有类方法可以访问的私有值或辅助函数很方便:
4080
4081```yuescript
4082class MoreThings
4083 secret = 123
4084 log = (msg) -> print "LOG:", msg
4085
4086 some_method: =>
4087 log "hello world: " .. secret
4088```
4089<YueDisplay>
4090
4091```yue
4092class MoreThings
4093 secret = 123
4094 log = (msg) -> print "LOG:", msg
4095
4096 some_method: =>
4097 log "hello world: " .. secret
4098```
4099
4100</YueDisplay>
4101
4102### @ 和 @@ 值
4103
4104当 @ 和 @@ 前缀在一个名字前时,它们分别代表在 self 和 self.\_\_class 中访问的那个名字。
4105
4106如果它们单独使用,它们是 self 和 self.\_\_class 的别名。
4107
4108```yuescript
4109assert @ == self
4110assert @@ == self.__class
4111```
4112<YueDisplay>
4113
4114```yue
4115assert @ == self
4116assert @@ == self.__class
4117```
4118
4119</YueDisplay>
4120
4121例如,使用 @@ 从实例方法快速创建同一类的新实例的方法:
4122
4123```yuescript
4124some_instance_method = (...) => @@ ...
4125```
4126<YueDisplay>
4127
4128```yue
4129some_instance_method = (...) => @@ ...
4130```
4131
4132</YueDisplay>
4133
4134### 构造属性提升
4135
4136为了减少编写简单值对象定义的代码。你可以这样简单写一个类:
4137
4138```yuescript
4139class Something
4140 new: (@foo, @bar, @@biz, @@baz) =>
4141
4142-- 这是以下声明的简写形式
4143
4144class Something
4145 new: (foo, bar, biz, baz) =>
4146 @foo = foo
4147 @bar = bar
4148 @@biz = biz
4149 @@baz = baz
4150```
4151<YueDisplay>
4152
4153```yue
4154class Something
4155 new: (@foo, @bar, @@biz, @@baz) =>
4156
4157-- 这是以下声明的简写形式
4158
4159class Something
4160 new: (foo, bar, biz, baz) =>
4161 @foo = foo
4162 @bar = bar
4163 @@biz = biz
4164 @@baz = baz
4165```
4166
4167</YueDisplay>
4168
4169你也可以使用这种语法为一个函数初始化传入对象的字段。
4170
4171```yuescript
4172new = (@fieldA, @fieldB) => @
4173obj = new {}, 123, "abc"
4174print obj
4175```
4176<YueDisplay>
4177
4178```yue
4179new = (@fieldA, @fieldB) => @
4180obj = new {}, 123, "abc"
4181print obj
4182```
4183
4184</YueDisplay>
4185
4186### 类表达式
4187
4188类声明的语法也可以作为一个表达式使用,可以赋值给一个变量或者被返回语句返回。
4189
4190```yuescript
4191x = class Bucket
4192 drops: 0
4193 add_drop: => @drops += 1
4194```
4195<YueDisplay>
4196
4197```yue
4198x = class Bucket
4199 drops: 0
4200 add_drop: => @drops += 1
4201```
4202
4203</YueDisplay>
4204
4205### 匿名类
4206
4207声明类时可以省略名称。如果类的表达式不在赋值语句中,\_\_name 属性将为 nil。如果出现在赋值语句中,赋值操作左侧的名称将代替 nil。
4208
4209```yuescript
4210BigBucket = class extends Bucket
4211 add_drop: => @drops += 10
4212
4213assert Bucket.__name == "BigBucket"
4214```
4215<YueDisplay>
4216
4217```yue
4218BigBucket = class extends Bucket
4219 add_drop: => @drops += 10
4220
4221assert Bucket.__name == "BigBucket"
4222```
4223
4224</YueDisplay>
4225
4226你甚至可以省略掉主体,这意味着你可以这样写一个空白的匿名类:
4227
4228```yuescript
4229x = class
4230```
4231<YueDisplay>
4232
4233```yue
4234x = class
4235```
4236
4237</YueDisplay>
4238
4239### 类混合
4240
4241你可以通过使用 `using` 关键字来实现类混合。这意味着你可以从一个普通 Lua 表格或已定义的类对象中,复制函数到你创建的新类中。当你使用普通 Lua 表格进行类混合时,你有机会用自己的实现来重写类的索引方法(例如元方法 `__index`)。然而,当你从一个类对象做混合时,需要注意的是该类对象的元方法将不会被复制到新类。
4242
4243```yuescript
4244MyIndex = __index: var: 1
4245
4246class X using MyIndex
4247 func: =>
4248 print 123
4249
4250x = X!
4251print x.var
4252
4253class Y using X
4254
4255y = Y!
4256y\func!
4257
4258assert y.__class.__parent ~= X -- X 不是 Y 的父类
4259```
4260<YueDisplay>
4261
4262```yue
4263MyIndex = __index: var: 1
4264
4265class X using MyIndex
4266 func: =>
4267 print 123
4268
4269x = X!
4270print x.var
4271
4272class Y using X
4273
4274y = Y!
4275y\func!
4276
4277assert y.__class.__parent ~= X -- X 不是 Y 的父类
4278```
4279
4280</YueDisplay>
4281
4282## with 语句
4283
4284在编写 Lua 代码时,我们在创建对象后的常见操作是立即调用这个对象一系列操作函数并设置一系列属性。
4285
4286这导致在代码中多次重复引用对象的名称,增加了不必要的文本噪音。一个常见的解决方案是在创建对象时,在构造函数传入一个表,该表包含要覆盖设置的键和值的集合。这样做的缺点是该对象的构造函数必须支持这种初始化形式。
4287
4288with 块有助于简化编写这样的代码。在 with 块内,我们可以使用以 . 或 \ 开头的特殊语句,这些语句代表我们正在使用的对象的操作。
4289
4290例如,我们可以这样处理一个新创建的对象:
4291
4292```yuescript
4293with Person!
4294 .name = "Oswald"
4295 \add_relative my_dad
4296 \save!
4297 print .name
4298```
4299<YueDisplay>
4300
4301```yue
4302with Person!
4303 .name = "Oswald"
4304 \add_relative my_dad
4305 \save!
4306 print .name
4307```
4308
4309</YueDisplay>
4310
4311with 语句也可以用作一个表达式,并返回它的代码块正在处理的对象。
4312
4313```yuescript
4314file = with File "favorite_foods.txt"
4315 \set_encoding "utf8"
4316```
4317<YueDisplay>
4318
4319```yue
4320file = with File "favorite_foods.txt"
4321 \set_encoding "utf8"
4322```
4323
4324</YueDisplay>
4325
4326或者…
4327
4328```yuescript
4329create_person = (name, relatives) ->
4330 with Person!
4331 .name = name
4332 \add_relative relative for relative in *relatives
4333
4334me = create_person "Leaf", [dad, mother, sister]
4335```
4336<YueDisplay>
4337
4338```yue
4339create_person = (name, relatives) ->
4340 with Person!
4341 .name = name
4342 \add_relative relative for relative in *relatives
4343
4344me = create_person "Leaf", [dad, mother, sister]
4345```
4346
4347</YueDisplay>
4348
4349在此用法中,with 可以被视为K组合子(k-combinator)的一种特殊形式。
4350
4351如果你想给表达式另外起一个名称的话,with 语句中的表达式也可以是一个赋值语句。
4352
4353```yuescript
4354with str := "你好"
4355 print "原始:", str
4356 print "大写:", \upper!
4357```
4358<YueDisplay>
4359
4360```yue
4361with str := "你好"
4362 print "原始:", str
4363 print "大写:", \upper!
4364```
4365
4366</YueDisplay>
4367
4368你可以在 `with` 语句中使用 `[]` 访问特殊键。
4369
4370```yuescript
4371with tb
4372 [1] = 1
4373 print [2]
4374 with [abc]
4375 [3] = [2]\func!
4376 ["key-name"] = value
4377 [] = "abc" -- 追加到 "tb"
4378```
4379<YueDisplay>
4380
4381```yue
4382with tb
4383 [1] = 1
4384 print [2]
4385 with [abc]
4386 [3] = [2]\func!
4387 ["key-name"] = value
4388 [] = "abc" -- 追加到 "tb"
4389```
4390
4391</YueDisplay>
4392
4393`with?` 是 `with` 语法的一个增强版本,引入了存在性检查,用于在不显式判空的情况下安全访问可能为 nil 的对象。
4394
4395```yuescript
4396with? obj
4397 print obj.name
4398```
4399<YueDisplay>
4400
4401```yue
4402with? obj
4403 print obj.name
4404```
4405
4406</YueDisplay>
4407
4408## do 语句
4409
4410当用作语句时,do 语句的作用就像在 Lua 中差不多。
4411
4412```yuescript
4413do
4414 var = "hello"
4415 print var
4416print var -- 这里是nil
4417```
4418<YueDisplay>
4419
4420```yue
4421do
4422 var = "hello"
4423 print var
4424print var -- 这里是nil
4425```
4426
4427</YueDisplay>
4428
4429月之脚本的 **do** 也可以用作表达式。允许你将多行代码的处理合并为一个表达式,并将 do 语句代码块的最后一个语句作为表达式返回的结果。
4430
4431```yuescript
4432counter = do
4433 i = 0
4434 ->
4435 i += 1
4436 i
4437
4438print counter!
4439print counter!
4440```
4441<YueDisplay>
4442
4443```yue
4444counter = do
4445 i = 0
4446 ->
4447 i += 1
4448 i
4449
4450print counter!
4451print counter!
4452```
4453
4454</YueDisplay>
4455
4456```yuescript
4457tbl = {
4458 key: do
4459 print "分配键值!"
4460 1234
4461}
4462```
4463<YueDisplay>
4464
4465```yue
4466tbl = {
4467 key: do
4468 print "分配键值!"
4469 1234
4470}
4471```
4472
4473</YueDisplay>
4474
4475## 函数存根
4476
4477在编程中,将对象的方法作为函数类型的值进行传递是一种常见做法,尤其是在将实例方法作为回调函数传递给其他函数的情形中。当目标函数需要将该对象作为其第一个参数时,我们需要找到一种方式将对象和函数绑定在一起,以便能够正确地调用该函数。
4478
4479函数存根(stub)语法提供了一种便捷的方法来创建一个新的闭包函数,这个函数将对象和原函数绑定在一起。这样,当调用这个新创建的函数时,它会在正确的对象上下文中执行原有的函数。
4480
4481这种语法类似于使用 \ 操作符调用实例方法的方式,区别在于,这里不需要在 \ 操作符后面附加参数列表。
4482
4483```yuescript
4484my_object = {
4485 value: 1000
4486 write: => print "值为:", @value
4487}
4488
4489run_callback = (func) ->
4490 print "运行回调..."
4491 func!
4492
4493-- 这样写不起作用:
4494-- 函数没有引用my_object
4495run_callback my_object.write
4496
4497-- 函数存根语法
4498-- 让我们把对象捆绑到一个新函数中
4499run_callback my_object\write
4500```
4501<YueDisplay>
4502
4503```yue
4504my_object = {
4505 value: 1000
4506 write: => print "值为:", @value
4507}
4508
4509run_callback = (func) ->
4510 print "运行回调..."
4511 func!
4512
4513-- 这样写不起作用:
4514-- 函数没有引用my_object
4515run_callback my_object.write
4516
4517-- 函数存根语法
4518-- 让我们把对象捆绑到一个新函数中
4519run_callback my_object\write
4520```
4521
4522</YueDisplay>
4523
4524## 使用 using 语句:防止破坏性赋值
4525
4526Lua 的变量作用域是降低代码复杂度的重要工具。然而,随着代码量的增加,维护这些变量可能变得更加困难。比如,看看下面的代码片段:
4527
4528```yuescript
4529i = 100
4530
4531-- 许多代码行...
4532
4533my_func = ->
4534 i = 10
4535 while i > 0
4536 print i
4537 i -= 1
4538
4539my_func!
4540
4541print i -- 将打印 0
4542```
4543<YueDisplay>
4544
4545```yue
4546i = 100
4547
4548-- 许多代码行...
4549
4550my_func = ->
4551 i = 10
4552 while i > 0
4553 print i
4554 i -= 1
4555
4556my_func!
4557
4558print i -- 将打印 0
4559```
4560
4561</YueDisplay>
4562
4563在 `my_func` 中,我们不小心覆盖了变量 `i` 的值。虽然在这个例子中这个问题很明显,但在一个庞大的或者是由多人共同维护的代码库中,很难追踪每个变量的声明情况。
4564
4565如果我们可以明确指出哪些变量是我们想在当前作用域内修改的,并且防止我们不小心更改了其他作用域中同名的变量,那将大有裨益。
4566
4567`using` 语句就是为此而生。`using nil` 确保函数内部的赋值不会意外地影响到外部作用域的变量。我们只需将 `using` 子句放在函数的参数列表之后;若函数没有参数,则直接放在括号内即可。
4568
4569```yuescript
4570i = 100
4571
4572my_func = (using nil) ->
4573 i = "hello" -- 这里创建了一个新的局部变量
4574
4575my_func!
4576print i -- 打印 100,i 没有受到影响
4577```
4578<YueDisplay>
4579
4580```yue
4581i = 100
4582
4583my_func = (using nil) ->
4584 i = "hello" -- 这里创建了一个新的局部变量
4585
4586my_func!
4587print i -- 打印 100,i 没有受到影响
4588```
4589
4590</YueDisplay>
4591
4592using子句中可以填写多个用逗号分隔名称。指定可以访问和修改的外部变量的名称:
4593
4594```yuescript
4595tmp = 1213
4596i, k = 100, 50
4597
4598my_func = (add using k, i) ->
4599 tmp = tmp + add -- 创建了一个新的局部tmp
4600 i += tmp
4601 k += tmp
4602
4603my_func(22)
4604print i, k -- 这些已经被更新
4605```
4606<YueDisplay>
4607
4608```yue
4609tmp = 1213
4610i, k = 100, 50
4611
4612my_func = (add using k, i) ->
4613 tmp = tmp + add -- 创建了一个新的局部tmp
4614 i += tmp
4615 k += tmp
4616
4617my_func(22)
4618print i, k -- 这些已经被更新
4619```
4620
4621</YueDisplay>
4622
4623## 月之脚本语言库
4624
4625使用 `require("yue")` 来访问。
4626
4627### yue
4628
4629**描述:**
4630
4631月之脚本语言库。
4632
4633#### version
4634
4635**类型:** 成员变量。
4636
4637**描述:**
4638
4639月之脚本版本。
4640
4641**签名:**
4642```lua
4643version: string
4644```
4645
4646#### dirsep
4647
4648**类型:** 成员变量。
4649
4650**描述:**
4651
4652当前平台的文件分隔符。
4653
4654**签名:**
4655```lua
4656dirsep: string
4657```
4658
4659#### yue_compiled
4660
4661**类型:** 成员变量。
4662
4663**描述:**
4664
4665编译模块代码缓存。
4666
4667**签名:**
4668```lua
4669yue_compiled: {string: string}
4670```
4671
4672#### to_lua
4673
4674**类型:** 函数。
4675
4676**描述:**
4677
4678月之脚本的编译函数。它将 YueScript 代码编译为 Lua 代码。
4679
4680**签名:**
4681```lua
4682to_lua: function(code: string, config?: Config):
4683 --[[codes]] string | nil,
4684 --[[error]] string | nil,
4685 --[[globals]] {{string, integer, integer}} | nil
4686```
4687
4688**参数:**
4689
4690| 参数名 | 类型 | 描述 |
4691| --- | --- | --- |
4692| code | string | YueScript 代码。 |
4693| config | Config | [可选] 编译器选项。 |
4694
4695**返回值:**
4696
4697| 返回类型 | 描述 |
4698| --- | --- |
4699| string \| nil | 编译后的 Lua 代码,如果编译失败则为 nil。 |
4700| string \| nil | 错误消息,如果编译成功则为 nil。 |
4701| {{string, integer, integer}} \| nil | 代码中出现的全局变量(带有名称、行和列),如果编译器选项 `lint_global` 为 false 则为 nil。 |
4702
4703#### file_exist
4704
4705**类型:** 函数。
4706
4707**描述:**
4708
4709检查源文件是否存在的函数。可以覆盖该函数以自定义行为。
4710
4711**签名:**
4712```lua
4713file_exist: function(filename: string): boolean
4714```
4715
4716**参数:**
4717
4718| 参数名 | 类型 | 描述 |
4719| --- | --- | --- |
4720| filename | string | 文件名。 |
4721
4722**返回值:**
4723
4724| 返回类型 | 描述 |
4725| --- | --- |
4726| boolean | 文件是否存在。 |
4727
4728#### read_file
4729
4730**类型:** 函数。
4731
4732**描述:**
4733
4734读取源文件的函数。可以覆盖该函数以自定义行为。
4735
4736**签名:**
4737```lua
4738read_file: function(filename: string): string
4739```
4740
4741**参数:**
4742
4743| 参数名 | 类型 | 描述 |
4744| --- | --- | --- |
4745| filename | string | 文件名。 |
4746
4747**返回值:**
4748
4749| 返回类型 | 描述 |
4750| --- | --- |
4751| string | 文件内容。 |
4752
4753#### insert_loader
4754
4755**类型:** 函数。
4756
4757**描述:**
4758
4759将 YueScript 加载器插入到 Lua 包加载器(搜索器)中。
4760
4761**签名:**
4762```lua
4763insert_loader: function(pos?: integer): boolean
4764```
4765
4766**参数:**
4767
4768| 参数名 | 类型 | 描述 |
4769| --- | --- | --- |
4770| pos | integer | [可选] 要插入加载器的位置。默认为 3。 |
4771
4772**返回值:**
4773
4774| 返回类型 | 描述 |
4775| --- | --- |
4776| boolean | 是否成功插入加载器。如果加载器已经插入,则返回失败。 |
4777
4778#### remove_loader
4779
4780**类型:** 函数。
4781
4782**描述:**
4783
4784从 Lua 包加载器(搜索器)中移除 YueScript 加载器。
4785
4786**签名:**
4787```lua
4788remove_loader: function(): boolean
4789```
4790
4791**返回值:**
4792
4793| 返回类型 | 描述 |
4794| --- | --- |
4795| boolean | 是否成功移除加载器。如果加载器未插入,则返回失败。 |
4796
4797#### loadstring
4798
4799**类型:** 函数。
4800
4801**描述:**
4802
4803将 YueScript 代码字符串加载为一个函数。
4804
4805**签名:**
4806```lua
4807loadstring: function(input: string, chunkname: string, env: table, config?: Config):
4808 --[[loaded function]] nil | function(...: any): (any...),
4809 --[[error]] string | nil
4810```
4811
4812**参数:**
4813
4814| 参数名 | 类型 | 描述 |
4815| --- | --- | --- |
4816| input | string | YueScript 代码。 |
4817| chunkname | string | 代码块的名称。 |
4818| env | table | 环境表。 |
4819| config | Config | [可选] 编译器选项。 |
4820
4821**返回值:**
4822
4823| 返回类型 | 描述 |
4824| --- | --- |
4825| function \| nil | 加载的函数,如果加载失败则为 nil。 |
4826| string \| nil | 错误消息,如果加载成功则为 nil。 |
4827
4828#### loadstring
4829
4830**类型:** 函数。
4831
4832**描述:**
4833
4834将 YueScript 代码字符串加载为一个函数。
4835
4836**签名:**
4837```lua
4838loadstring: function(input: string, chunkname: string, config?: Config):
4839 --[[loaded function]] nil | function(...: any): (any...),
4840 --[[error]] string | nil
4841```
4842
4843**参数:**
4844
4845| 参数名 | 类型 | 描述 |
4846| --- | --- | --- |
4847| input | string | YueScript 代码。 |
4848| chunkname | string | 代码块的名称。 |
4849| config | Config | [可选] 编译器选项。 |
4850
4851**返回值:**
4852
4853| 返回类型 | 描述 |
4854| --- | --- |
4855| function \| nil | 加载的函数,如果加载失败则为 nil。 |
4856| string \| nil | 错误消息,如果加载成功则为 nil。 |
4857
4858#### loadstring
4859
4860**类型:** 函数。
4861
4862**描述:**
4863
4864将 YueScript 代码字符串加载为一个函数。
4865
4866**签名:**
4867```lua
4868loadstring: function(input: string, config?: Config):
4869 --[[loaded function]] nil | function(...: any): (any...),
4870 --[[error]] string | nil
4871```
4872
4873**参数:**
4874
4875| 参数名 | 类型 | 描述 |
4876| --- | --- | --- |
4877| input | string | YueScript 代码。 |
4878| config | Config | [可选] 编译器选项。 |
4879
4880**返回值:**
4881
4882| 返回类型 | 描述 |
4883| --- | --- |
4884| function \| nil | 加载的函数,如果加载失败则为 nil。 |
4885| string \| nil | 错误消息,如果加载成功则为 nil。 |
4886
4887#### loadfile
4888
4889**类型:** 函数。
4890
4891**描述:**
4892
4893将 YueScript 代码文件加载为一个函数。
4894
4895**签名:**
4896```lua
4897loadfile: function(filename: string, env: table, config?: Config):
4898 nil | function(...: any): (any...),
4899 string | nil
4900```
4901
4902**参数:**
4903
4904| 参数名 | 类型 | 描述 |
4905| --- | --- | --- |
4906| filename | string | 文件名。 |
4907| env | table | 环境表。 |
4908| config | Config | [可选] 编译器选项。 |
4909
4910**返回值:**
4911
4912| 返回类型 | 描述 |
4913| --- | --- |
4914| function \| nil | 加载的函数,如果加载失败则为 nil。 |
4915| string \| nil | 错误消息,如果加载成功则为 nil。 |
4916
4917#### loadfile
4918
4919**类型:** 函数。
4920
4921**描述:**
4922
4923将 YueScript 代码文件加载为一个函数。
4924
4925**签名:**
4926```lua
4927loadfile: function(filename: string, config?: Config):
4928 nil | function(...: any): (any...),
4929 string | nil
4930```
4931
4932**参数:**
4933
4934| 参数名 | 类型 | 描述 |
4935| --- | --- | --- |
4936| filename | string | 文件名。 |
4937| config | Config | [可选] 编译器选项。 |
4938
4939**返回值:**
4940
4941| 返回类型 | 描述 |
4942| --- | --- |
4943| function \| nil | 加载的函数,如果加载失败则为 nil。 |
4944| string \| nil | 错误消息,如果加载成功则为 nil。 |
4945
4946#### dofile
4947
4948**类型:** 函数。
4949
4950**描述:**
4951
4952将 YueScript 代码文件加载为一个函数并执行。
4953
4954**签名:**
4955```lua
4956dofile: function(filename: string, env: table, config?: Config): any...
4957```
4958
4959**参数:**
4960
4961| 参数名 | 类型 | 描述 |
4962| --- | --- | --- |
4963| filename | string | 文件名。 |
4964| env | table | 环境表。 |
4965| config | Config | [可选] 编译器选项。 |
4966
4967**返回值:**
4968
4969| 返回类型 | 描述 |
4970| --- | --- |
4971| any... | 加载的函数执行后的返回值。 |
4972
4973#### dofile
4974
4975**类型:** 函数。
4976
4977**描述:**
4978
4979将 YueScript 代码文件加载为一个函数并执行。
4980
4981**签名:**
4982```lua
4983dofile: function(filename: string, config?: Config): any...
4984```
4985
4986**参数:**
4987
4988| 参数名 | 类型 | 描述 |
4989| --- | --- | --- |
4990| filename | string | 文件名。 |
4991| config | Config | [可选] 编译器选项。 |
4992
4993**返回值:**
4994
4995| 返回类型 | 描述 |
4996| --- | --- |
4997| any... | 加载的函数执行后的返回值。 |
4998
4999#### find_modulepath
5000
5001**类型:** 函数。
5002
5003**描述:**
5004
5005将 YueScript 模块名解析为文件路径。
5006
5007**签名:**
5008```lua
5009find_modulepath: function(name: string): string
5010```
5011
5012**参数:**
5013
5014| 参数名 | 类型 | 描述 |
5015| --- | --- | --- |
5016| name | string | 模块名。 |
5017
5018**返回值:**
5019
5020| 返回类型 | 描述 |
5021| --- | --- |
5022| string | 文件路径。 |
5023
5024#### pcall
5025
5026**类型:** 函数。
5027
5028**描述:**
5029
5030在保护模式下调用一个函数。
5031会捕获任何错误,执行成功则返回成功状态和结果,否则为失败状态和错误信息。
5032当发生错误时,将错误信息中的代码行号重写为 YueScript 代码中的原始行号。
5033
5034**签名:**
5035```lua
5036pcall: function(f: function, ...: any): boolean, any...
5037```
5038
5039**参数:**
5040
5041| 参数名 | 类型 | 描述 |
5042| --- | --- | --- |
5043| f | function | 要调用的函数。 |
5044| ... | any | 要传递给函数的参数。 |
5045
5046**返回值:**
5047
5048| 返回类型 | 描述 |
5049| --- | --- |
5050| boolean, ... | 状态码和函数结果或错误信息。 |
5051
5052#### require
5053
5054**类型:** 函数。
5055
5056**描述:**
5057
5058加载给定的模块。可以是 Lua 模块或 YueScript 模块。
5059如果模块是 YueScript 模块且加载失败,则将错误信息中的代码行号重写为 YueScript 代码中的原始行号。
5060
5061**签名:**
5062```lua
5063require: function(name: string): any...
5064```
5065
5066**参数:**
5067
5068| 参数名 | 类型 | 描述 |
5069| --- | --- | --- |
5070| modname | string | 要加载的模块名。 |
5071
5072**返回值:**
5073
5074| 返回类型 | 描述 |
5075| --- | --- |
5076| any | 如果模块已经加载,则返回 package.loaded[modname] 中存储的值。否则,尝试查找加载器并返回 package.loaded[modname] 的最终值和加载器数据作为第二个结果。 |
5077
5078#### p
5079
5080**类型:** 函数。
5081
5082**描述:**
5083
5084检查传递的值的内部结构,并打印值出它的字符串表示。
5085
5086**签名:**
5087```lua
5088p: function(...: any)
5089```
5090
5091**参数:**
5092
5093| 参数名 | 类型 | 描述 |
5094| --- | --- | --- |
5095| ... | any | 要检查的值。 |
5096
5097#### options
5098
5099**类型:** 成员变量。
5100
5101**描述:**
5102
5103当前编译器选项。
5104
5105**签名:**
5106```lua
5107options: Config.Options
5108```
5109
5110#### traceback
5111
5112**类型:** 函数。
5113
5114**描述:**
5115
5116重写堆栈跟踪中的行号为 YueScript 代码中的原始行号的 traceback 函数。
5117
5118**签名:**
5119```lua
5120traceback: function(message: string): string
5121```
5122
5123**参数:**
5124
5125| 参数名 | 类型 | 描述 |
5126| --- | --- | --- |
5127| message | string | 堆栈跟踪消息。 |
5128
5129**返回值:**
5130
5131| 返回类型 | 描述 |
5132| --- | --- |
5133| string | 重写后的堆栈跟踪消息。 |
5134
5135#### is_ast
5136
5137**类型:** 函数。
5138
5139**描述:**
5140
5141检查代码是否匹配指定的 AST。
5142
5143**签名:**
5144```lua
5145is_ast: function(astName: string, code: string): boolean
5146```
5147
5148**参数:**
5149
5150| 参数名 | 类型 | 描述 |
5151| --- | --- | --- |
5152| astName | string | AST 名称。 |
5153| code | string | 代码。 |
5154
5155**返回值:**
5156
5157| 返回类型 | 描述 |
5158| --- | --- |
5159| boolean | 代码是否匹配 AST。 |
5160
5161#### AST
5162
5163**类型:** 成员变量。
5164
5165**描述:**
5166
5167AST 类型定义,带有名称、行、列和子节点。
5168
5169**签名:**
5170```lua
5171type AST = {string, integer, integer, any}
5172```
5173
5174#### to_ast
5175
5176**类型:** 函数。
5177
5178**描述:**
5179
5180将代码转换为 AST。
5181
5182**签名:**
5183```lua
5184to_ast: function(code: string, flattenLevel?: number, astName?: string, reserveComment?: boolean):
5185 --[[AST]] AST | nil,
5186 --[[error]] nil | string
5187```
5188
5189**参数:**
5190
5191| 参数名 | 类型 | 描述 |
5192| --- | --- | --- |
5193| code | string | 代码。 |
5194| flattenLevel | integer | [可选] 扁平化级别。级别越高,会消除更多的 AST 结构的嵌套。默认为 0。最大为 2。 |
5195| astName | string | [可选] AST 名称。默认为 "File"。 |
5196| reserveComment | boolean | [可选] 是否保留原始注释。默认为 false。 |
5197
5198**返回值:**
5199
5200| 返回类型 | 描述 |
5201| --- | --- |
5202| AST \| nil | AST,如果转换失败则为 nil。 |
5203| string \| nil | 错误消息,如果转换成功则为 nil。 |
5204
5205#### format
5206
5207**类型:** 函数。
5208
5209**描述:**
5210
5211格式化 YueScript 代码。
5212
5213**签名:**
5214```lua
5215format: function(code: string, tabSize?: number, reserveComment?: boolean): string
5216```
5217
5218**参数:**
5219
5220| 参数名 | 类型 | 描述 |
5221| --- | --- | --- |
5222| code | string | 代码。 |
5223| tabSize | integer | [可选] 制表符大小。默认为 4。 |
5224| reserveComment | boolean | [可选] 是否保留原始注释。默认为 true。 |
5225
5226**返回值:**
5227
5228| 返回类型 | 描述 |
5229| --- | --- |
5230| string | 格式化后的代码。 |
5231
5232#### __call
5233
5234**类型:** 元方法。
5235
5236**描述:**
5237
5238导入 YueScript 模块。
5239如果发生加载失败,则将错误信息中的代码行号重写为 YueScript 代码中的原始行号。
5240
5241**签名:**
5242```lua
5243metamethod __call: function(self: yue, module: string): any...
5244```
5245
5246**参数:**
5247
5248| 参数名 | 类型 | 描述 |
5249| --- | --- | --- |
5250| module | string | 模块名。 |
5251
5252**返回值:**
5253
5254| 返回类型 | 描述 |
5255| --- | --- |
5256| any | 模块值。 |
5257
5258### Config
5259
5260**描述:**
5261
5262编译器编译选项。
5263
5264#### lint_global
5265
5266**类型:** 成员变量。
5267
5268**描述:**
5269
5270编译器是否应该收集代码中出现的全局变量。
5271
5272**签名:**
5273```lua
5274lint_global: boolean
5275```
5276
5277#### implicit_return_root
5278
5279**类型:** 成员变量。
5280
5281**描述:**
5282
5283编译器是否应该对根层级的代码块进行隐式的表达式返回。
5284
5285**签名:**
5286```lua
5287implicit_return_root: boolean
5288```
5289
5290#### reserve_line_number
5291
5292**类型:** 成员变量。
5293
5294**描述:**
5295
5296编译器是否应该在编译后的代码中保留原始行号。
5297
5298**签名:**
5299```lua
5300reserve_line_number: boolean
5301```
5302
5303#### reserve_comment
5304
5305**类型:** 成员变量。
5306
5307**描述:**
5308
5309编译器是否应该在编译后的代码中保留原始注释。
5310
5311**签名:**
5312```lua
5313reserve_comment: boolean
5314```
5315
5316#### space_over_tab
5317
5318**类型:** 成员变量。
5319
5320**描述:**
5321
5322编译器是否应该在编译后的代码中使用空格字符而不是制表符字符。
5323
5324**签名:**
5325```lua
5326space_over_tab: boolean
5327```
5328
5329#### same_module
5330
5331**类型:** 成员变量。
5332
5333**描述:**
5334
5335编译器是否应该将要编译的代码视为当前正在编译的模块。仅供编译器内部使用。
5336
5337**签名:**
5338```lua
5339same_module: boolean
5340```
5341
5342#### line_offset
5343
5344**类型:** 成员变量。
5345
5346**描述:**
5347
5348编译器错误消息是否应该包含行号偏移量。仅供编译器内部使用。
5349
5350**签名:**
5351```lua
5352line_offset: integer
5353```
5354
5355#### yue.Config.LuaTarget
5356
5357**类型:** 枚举。
5358
5359**描述:**
5360
5361目标 Lua 版本枚举。
5362
5363**签名:**
5364```lua
5365enum LuaTarget
5366 "5.1"
5367 "5.2"
5368 "5.3"
5369 "5.4"
5370 "5.5"
5371end
5372```
5373
5374#### options
5375
5376**类型:** 成员变量。
5377
5378**描述:**
5379
5380要传递给编译函数的额外选项。
5381
5382**签名:**
5383```lua
5384options: Options
5385```
5386
5387### Options
5388
5389**描述:**
5390
5391额外编译器选项定义。
5392
5393#### target
5394
5395**类型:** 成员变量。
5396
5397**描述:**
5398
5399编译目标 Lua 版本。
5400
5401**签名:**
5402```lua
5403target: LuaTarget
5404```
5405
5406#### path
5407
5408**类型:** 成员变量。
5409
5410**描述:**
5411
5412额外模块搜索路径。
5413
5414**签名:**
5415```lua
5416path: string
5417```
5418
5419#### dump_locals
5420
5421**类型:** 成员变量。
5422
5423**描述:**
5424
5425是否在回溯错误消息中输出代码块的局部变量。默认为 false。
5426
5427**签名:**
5428```lua
5429dump_locals: boolean
5430```
5431
5432#### simplified
5433
5434**类型:** 成员变量。
5435
5436**描述:**
5437
5438是否简化输出的错误消息。默认为 true。
5439
5440**签名:**
5441```lua
5442simplified: boolean
5443```
5444
5445## MIT 许可证
5446
5447版权 (c) 2017-2025 李瑾 \<dragon-fly@qq.com\>
5448
5449特此免费授予任何获得本软件副本和相关文档文件(下称“软件”)的人不受限制地处置该软件的权利,包括不受限制地使用、复制、修改、合并、发布、分发、转授许可和/或出售该软件副本,以及再授权被配发了本软件的人如上的权利,须在下列条件下:
5450上述版权声明和本许可声明应包含在该软件的所有副本或实质成分中。
5451本软件是“如此”提供的,没有任何形式的明示或暗示的保证,包括但不限于对适销性、特定用途的适用性和不侵权的保证。在任何情况下,作者或版权持有人都不对任何索赔、损害或其他责任负责,无论这些追责来自合同、侵权或其它行为中,还是产生于、源于或有关于本软件以及本软件的使用或其它处置。
5452
5453<CompilerModal />
diff --git a/doc/docs/zh/doc/installation.md b/doc/docs/zh/doc/installation.md
new file mode 100644
index 0000000..618da2a
--- /dev/null
+++ b/doc/docs/zh/doc/installation.md
@@ -0,0 +1,43 @@
1# 安装
2
3## Lua 模块
4
5&emsp;安装 [luarocks](https://luarocks.org),一个 Lua 模块的包管理器。然后作为 Lua 模块和可执行文件安装它:
6
7```shell
8luarocks install yuescript
9```
10
11&emsp;或者你可以自己构建 `yue.so` 文件:
12
13```shell
14make shared LUAI=/usr/local/include/lua LUAL=/usr/local/lib/lua
15```
16
17&emsp;然后从路径 **bin/shared/yue.so** 获取二进制文件。
18
19## 构建二进制工具
20
21&emsp;克隆项目仓库,然后构建并安装可执行文件:
22
23```shell
24make install
25```
26
27&emsp;构建不带宏功能的月之脚本编译工具:
28
29```shell
30make install NO_MACRO=true
31```
32
33&emsp;构建不带内置Lua二进制文件的月之脚本编译工具:
34
35```shell
36make install NO_LUA=true
37```
38
39## 下载预编译的二进制程序
40
41&emsp;你可以下载预编译的二进制程序,包括兼容不同 Lua 版本的二进制可执行文件和库文件。
42
43&emsp;在[这里](https://github.com/IppClub/YueScript/releases)下载预编译的二进制程序。
diff --git a/doc/docs/zh/doc/introduction.md b/doc/docs/zh/doc/introduction.md
new file mode 100644
index 0000000..827d163
--- /dev/null
+++ b/doc/docs/zh/doc/introduction.md
@@ -0,0 +1,103 @@
1# 介绍
2
3月之脚本(YueScript)是一种动态语言,可以编译为 Lua。它是 [MoonScript](https://github.com/leafo/moonscript) 的方言。用月之脚本编写的代码既有表现力又非常简洁。它适合编写一些更易于维护的代码,并在嵌入 Lua 的环境中运行,如游戏或网站服务器。
4
5Yue(月)是中文中“月亮”的名称。
6
7## 月之脚本概览
8```yuescript
9-- 导入语法
10import p, to_lua from "yue"
11
12-- 隐式对象
13inventory =
14 equipment:
15 - "sword"
16 - "shield"
17 items:
18 - name: "potion"
19 count: 10
20 - name: "bread"
21 count: 3
22
23-- 列表推导
24map = (arr, action) ->
25 [action item for item in *arr]
26
27filter = (arr, cond) ->
28 [item for item in *arr when cond item]
29
30reduce = (arr, init, action): init ->
31 init = action init, item for item in *arr
32
33-- 管道操作符
34[1, 2, 3]
35 |> map (x) -> x * 2
36 |> filter (x) -> x > 4
37 |> reduce 0, (a, b) -> a + b
38 |> print
39
40-- 元表操作
41apple =
42 size: 15
43 <index>:
44 color: 0x00ffff
45
46with apple
47 p .size, .color, .<index> if .<>?
48
49-- 类似js的导出语法
50export 🌛 = "月之脚本"
51```
52<YueDisplay>
53
54```yue
55-- 导入语法
56import p, to_lua from "yue"
57
58-- 隐式对象
59inventory =
60 equipment:
61 - "sword"
62 - "shield"
63 items:
64 - name: "potion"
65 count: 10
66 - name: "bread"
67 count: 3
68
69-- 列表推导
70map = (arr, action) ->
71 [action item for item in *arr]
72
73filter = (arr, cond) ->
74 [item for item in *arr when cond item]
75
76reduce = (arr, init, action): init ->
77 init = action init, item for item in *arr
78
79-- 管道操作符
80[1, 2, 3]
81 |> map (x) -> x * 2
82 |> filter (x) -> x > 4
83 |> reduce 0, (a, b) -> a + b
84 |> print
85
86-- 元表操作
87apple =
88 size: 15
89 <index>:
90 color: 0x00ffff
91
92with apple
93 p .size, .color, .<index> if .<>?
94
95-- 类似js的导出语法
96export 🌛 = "月之脚本"
97```
98
99</YueDisplay>
100
101## 关于 Dora SSR
102
103月之脚本是与开源游戏引擎 [Dora SSR](https://github.com/Dora-SSR/Dora-SSR) 一起开发和维护的。它已被用于创建引擎工具、游戏原型和演示,在实际的游戏项目中验证其能力,同时它也帮助增强了 Dora SSR 游戏引擎的开发体验。
diff --git a/doc/docs/zh/doc/licence-mit.md b/doc/docs/zh/doc/licence-mit.md
new file mode 100644
index 0000000..1a07518
--- /dev/null
+++ b/doc/docs/zh/doc/licence-mit.md
@@ -0,0 +1,9 @@
1# MIT 许可证
2
3版权 (c) 2017-2026 李瑾 \<dragon-fly@qq.com\>
4
5特此免费授予任何获得本软件副本和相关文档文件(下称“软件”)的人不受限制地处置该软件的权利,包括不受限制地使用、复制、修改、合并、发布、分发、转授许可和/或出售该软件副本,以及再授权被配发了本软件的人如上的权利,须在下列条件下:
6上述版权声明和本许可声明应包含在该软件的所有副本或实质成分中。
7本软件是“如此”提供的,没有任何形式的明示或暗示的保证,包括但不限于对适销性、特定用途的适用性和不侵权的保证。在任何情况下,作者或版权持有人都不对任何索赔、损害或其他责任负责,无论这些追责来自合同、侵权或其它行为中,还是产生于、源于或有关于本软件以及本软件的使用或其它处置。
8
9<CompilerModal />
diff --git a/doc/docs/zh/doc/line-decorators.md b/doc/docs/zh/doc/line-decorators.md
new file mode 100644
index 0000000..86a1bd9
--- /dev/null
+++ b/doc/docs/zh/doc/line-decorators.md
@@ -0,0 +1,44 @@
1# 代码行修饰
2
3&emsp;&emsp;为了方便编写代码,循环语句和 if 语句可以应用于单行代码语句的末尾:
4
5```yuescript
6print "你好,世界" if name == "Rob"
7```
8<YueDisplay>
9
10```yue
11print "你好,世界" if name == "Rob"
12```
13
14</YueDisplay>
15
16&emsp;&emsp;修饰 for 循环的示例:
17
18```yuescript
19print "项目: ", item for item in *items
20```
21<YueDisplay>
22
23```yue
24print "项目: ", item for item in *items
25```
26
27</YueDisplay>
28
29&emsp;&emsp;修饰 while 循环的示例:
30
31```yuescript
32game\update! while game\isRunning!
33
34reader\parse_line! until reader\eof!
35```
36<YueDisplay>
37
38```yue
39game\update! while game\isRunning!
40
41reader\parse_line! until reader\eof!
42```
43
44</YueDisplay>
diff --git a/doc/docs/zh/doc/literals.md b/doc/docs/zh/doc/literals.md
new file mode 100644
index 0000000..1592bae
--- /dev/null
+++ b/doc/docs/zh/doc/literals.md
@@ -0,0 +1,111 @@
1# 字面量
2
3&emsp;&emsp;Lua 中的所有基本字面量都可以在月之脚本中使用。包括数字、字符串、布尔值和 **nil**。
4
5&emsp;&emsp;但与 Lua 不同的是,单引号和双引号字符串内部允许有换行:
6
7```yuescript
8some_string = "这是一个字符串
9 并包括一个换行。"
10
11-- 使用#{}语法可以将表达式插入到字符串字面量中。
12-- 字符串插值只在双引号字符串中可用。
13print "我有#{math.random! * 100}%的把握。"
14```
15<YueDisplay>
16
17```yue
18some_string = "这是一个字符串
19 并包括一个换行。"
20
21-- 使用#{}语法可以将表达式插入到字符串字面量中。
22-- 字符串插值只在双引号字符串中可用。
23print "我有#{math.random! * 100}%的把握。"
24```
25
26</YueDisplay>
27
28## 数字字面量
29
30&emsp;&emsp;你可以在数字字面量中使用下划线来增加可读性。
31
32```yuescript
33integer = 1_000_000
34hex = 0xEF_BB_BF
35binary = 0B10011
36```
37<YueDisplay>
38
39
40```yue
41integer = 1_000_000
42hex = 0xEF_BB_BF
43binary = 0B10011
44```
45
46</YueDisplay>
47
48## YAML 风格字符串
49
50&emsp;&emsp;使用 `|` 前缀标记一个多行 YAML 风格字符串:
51
52```yuescript
53str = |
54 key: value
55 list:
56 - item1
57 - #{expr}
58```
59<YueDisplay>
60
61```yue
62str = |
63 key: value
64 list:
65 - item1
66 - #{expr}
67```
68
69</YueDisplay>
70
71&emsp;&emsp;其效果类似于原生 Lua 的多行拼接,所有文本(含换行)将被保留下来,并支持 `#{...}` 语法,通过 `tostring(expr)` 插入表达式结果。
72
73&emsp;&emsp;YAML 风格的多行字符串会自动检测首行后最小的公共缩进,并从所有行中删除该前缀空白字符。这让你可以在代码中对齐文本,但输出字符串不会带多余缩进。
74
75```yuescript
76fn = ->
77 str = |
78 foo:
79 bar: baz
80 return str
81```
82<YueDisplay>
83
84```yue
85fn = ->
86 str = |
87 foo:
88 bar: baz
89 return str
90```
91
92</YueDisplay>
93
94&emsp;&emsp;输出字符串中的 foo: 对齐到行首,不会带有函数缩进空格。保留内部缩进的相对结构,适合书写结构化嵌套样式的内容。
95
96&emsp;&emsp;支持自动处理字符中的引号、反斜杠等特殊符号,无需手动转义:
97
98```yuescript
99str = |
100 path: "C:\Program Files\App"
101 note: 'He said: "#{Hello}!"'
102```
103<YueDisplay>
104
105```yue
106str = |
107 path: "C:\Program Files\App"
108 note: 'He said: "#{Hello}!"'
109```
110
111</YueDisplay>
diff --git a/doc/docs/zh/doc/macro.md b/doc/docs/zh/doc/macro.md
new file mode 100644
index 0000000..924b3ab
--- /dev/null
+++ b/doc/docs/zh/doc/macro.md
@@ -0,0 +1,276 @@
1# 宏
2
3## 常见用法
4
5&emsp;&emsp;宏函数用于在编译时执行一段代码来生成新的代码,并将生成的代码插入到最终编译结果中。
6
7```yuescript
8macro PI2 = -> math.pi * 2
9area = $PI2 * 5
10
11macro HELLO = -> "'你好 世界'"
12print $HELLO
13
14macro config = (debugging) ->
15 global debugMode = debugging == "true"
16 ""
17
18macro asserts = (cond) ->
19 debugMode and "assert #{cond}" or ""
20
21macro assert = (cond) ->
22 debugMode and "assert #{cond}" or "#{cond}"
23
24$config true
25$asserts item ~= nil
26
27$config false
28value = $assert item
29
30-- 宏函数参数传递的表达式会被转换为字符串
31macro and = (...) -> "#{ table.concat {...}, ' and ' }"
32if $and f1!, f2!, f3!
33 print "OK"
34```
35<YueDisplay>
36
37```yue
38macro PI2 = -> math.pi * 2
39area = $PI2 * 5
40
41macro HELLO = -> "'你好 世界'"
42print $HELLO
43
44macro config = (debugging) ->
45 global debugMode = debugging == "true"
46 ""
47
48macro asserts = (cond) ->
49 debugMode and "assert #{cond}" or ""
50
51macro assert = (cond) ->
52 debugMode and "assert #{cond}" or "#{cond}"
53
54$config true
55$asserts item ~= nil
56
57$config false
58value = $assert item
59
60-- 宏函数参数传递的表达式会被转换为字符串
61macro and = (...) -> "#{ table.concat {...}, ' and ' }"
62if $and f1!, f2!, f3!
63 print "OK"
64```
65
66</YueDisplay>
67
68## 直接插入代码
69
70&emsp;&emsp;宏函数可以返回一个包含月之脚本代码的字符串,或是一个包含 Lua 代码字符串的配置表。
71
72```yuescript
73macro yueFunc = (var) -> "local #{var} = ->"
74$yueFunc funcA
75funcA = -> "无法访问宏生成月之脚本里定义的变量"
76
77macro luaFunc = (var) -> {
78 code: "local function #{var}() end"
79 type: "lua"
80}
81$luaFunc funcB
82funcB = -> "无法访问宏生成 Lua 代码里定义的变量"
83
84macro lua = (code) -> {
85 :code
86 type: "lua"
87}
88
89-- raw字符串的开始和结束符号会自动被去除了再传入宏函数
90$lua[==[
91-- 插入原始Lua代码
92if cond then
93 print("输出")
94end
95]==]
96```
97<YueDisplay>
98
99```yue
100macro yueFunc = (var) -> "local #{var} = ->"
101$yueFunc funcA
102funcA = -> "无法访问宏生成月之脚本里定义的变量"
103
104macro luaFunc = (var) -> {
105 code: "local function #{var}() end"
106 type: "lua"
107}
108$luaFunc funcB
109funcB = -> "无法访问宏生成 Lua 代码里定义的变量"
110
111macro lua = (code) -> {
112 :code
113 type: "lua"
114}
115
116-- raw字符串的开始和结束符号会自动被去除了再传入宏函数
117$lua[==[
118-- 插入原始Lua代码
119if cond then
120 print("输出")
121end
122]==]
123```
124
125</YueDisplay>
126
127## 导出宏
128
129&emsp;&emsp;宏函数可以从一个模块中导出,并在另一个模块中导入。你必须将导出的宏函数放在一个单独的文件中使用,而且只有宏定义、宏导入和宏展开可以放入这个宏导出模块中。
130
131```yuescript
132-- 文件: utils.yue
133export macro map = (items, action) -> "[#{action} for _ in *#{items}]"
134export macro filter = (items, action) -> "[_ for _ in *#{items} when #{action}]"
135export macro foreach = (items, action) -> "for _ in *#{items}
136 #{action}"
137
138-- 文件 main.yue
139import "utils" as {
140 $, -- 表示导入所有宏的符号
141 $foreach: $each -- 重命名宏 $foreach 为 $each
142}
143[1, 2, 3] |> $map(_ * 2) |> $filter(_ > 4) |> $each print _
144```
145<YueDisplay>
146
147```yue
148-- 文件: utils.yue
149export macro map = (items, action) -> "[#{action} for _ in *#{items}]"
150export macro filter = (items, action) -> "[_ for _ in *#{items} when #{action}]"
151export macro foreach = (items, action) -> "for _ in *#{items}
152 #{action}"
153-- 文件 main.yue
154-- 在浏览器中不支持import函数,请在真实环境中尝试
155--[[
156import "utils" as {
157 $, -- 表示导入所有宏的符号
158 $foreach: $each -- 重命名宏 $foreach 为 $each
159}
160[1, 2, 3] |> $map(_ * 2) |> $filter(_ > 4) |> $each print _
161]]
162```
163
164</YueDisplay>
165
166## 内置宏
167
168&emsp;&emsp;月之脚本中有一些内置可以直接使用的宏,但你可以通过声明相同名称的宏来覆盖它们。
169
170```yuescript
171print $FILE -- 获取当前模块名称的字符串
172print $LINE -- 获取当前代码行数:2
173```
174<YueDisplay>
175
176```yue
177print $FILE -- 获取当前模块名称的字符串
178print $LINE -- 获取当前代码行数:2
179```
180
181</YueDisplay>
182
183## 用宏生成宏
184
185&emsp;&emsp;在月之脚本中,宏函数允许你在编译时生成代码。通过嵌套的宏函数,你可以创建更复杂的生成模式。这个特性允许你定义一个宏函数,用它来生成另一个宏函数,从而实现更加动态的代码生成。
186
187```yuescript
188macro Enum = (...) ->
189 items = {...}
190 itemSet = {item, true for item in *items}
191 (item) ->
192 error "got \"#{item}\", expecting one of #{table.concat items, ', '}" unless itemSet[item]
193 "\"#{item}\""
194
195macro BodyType = $Enum(
196 Static
197 Dynamic
198 Kinematic
199)
200
201print "有效的枚举类型:", $BodyType Static
202-- print "编译报错的枚举类型:", $BodyType Unknown
203```
204<YueDisplay>
205
206```yue
207macro Enum = (...) ->
208 items = {...}
209 itemSet = {item, true for item in *items}
210 (item) ->
211 error "got \"#{item}\", expecting one of #{table.concat items, ', '}" unless itemSet[item]
212 "\"#{item}\""
213
214macro BodyType = $Enum(
215 Static
216 Dynamic
217 Kinematic
218)
219
220print "有效的枚举类型:", $BodyType Static
221-- print "编译报错的枚举类型:", $BodyType Unknown
222```
223
224</YueDisplay>
225
226## 宏参数检查
227
228&emsp;&emsp;可以直接在参数列表中声明期望的 AST 节点类型,并在编译时检查传入的宏参数是否符合预期。
229
230```yuescript
231macro printNumAndStr = (num `Num, str `String) -> |
232 print(
233 #{num}
234 #{str}
235 )
236
237$printNumAndStr 123, "hello"
238```
239<YueDisplay>
240
241```yue
242macro printNumAndStr = (num `Num, str `String) -> |
243 print(
244 #{num}
245 #{str}
246 )
247
248$printNumAndStr 123, "hello"
249```
250
251</YueDisplay>
252
253&emsp;&emsp;如果需要做更加灵活的参数检查操作,可以使用内置的 `$is_ast` 宏函数在合适的位置进行手动检查。
254
255```yuescript
256macro printNumAndStr = (num, str) ->
257 error "expected Num as first argument" unless $is_ast Num, num
258 error "expected String as second argument" unless $is_ast String, str
259 "print(#{num}, #{str})"
260
261$printNumAndStr 123, "hello"
262```
263<YueDisplay>
264
265```yue
266macro printNumAndStr = (num, str) ->
267 error "expected Num as first argument" unless $is_ast Num, num
268 error "expected String as second argument" unless $is_ast String, str
269 "print(#{num}, #{str})"
270
271$printNumAndStr 123, "hello"
272```
273
274</YueDisplay>
275
276&emsp;&emsp;更多关于可用 AST 节点的详细信息,请参考 [yue_parser.cpp](https://github.com/IppClub/YueScript/blob/main/src/yuescript/yue_parser.cpp) 中大写的规则定义。
diff --git a/doc/docs/zh/doc/module.md b/doc/docs/zh/doc/module.md
new file mode 100644
index 0000000..bae6618
--- /dev/null
+++ b/doc/docs/zh/doc/module.md
@@ -0,0 +1,245 @@
1# 模块
2
3## 导入
4
5&emsp;&emsp;导入语句是一个语法糖,用于需要引入一个模块或者从已导入的模块中提取子项目。从模块导入的变量默认为不可修改的常量。
6
7```yuescript
8-- 用作表解构
9do
10 import insert, concat from table
11 -- 当给 insert, concat 变量赋值时,编译器会报告错误
12 import C, Ct, Cmt from require "lpeg"
13 -- 快捷写法引入模块的子项
14 import x, y, z from 'mymodule'
15 -- 使用Python风格的导入
16 from 'module' import a, b, c
17
18-- 快捷地导入一个模块
19do
20 import 'module'
21 import 'module_x'
22 import "d-a-s-h-e-s"
23 import "module.part"
24
25-- 导入模块后起一个别名使用,或是进行导入模块表的解构
26do
27 import "player" as PlayerModule
28 import "lpeg" as :C, :Ct, :Cmt
29 import "export" as {one, two, Something:{umm:{ch}}}
30```
31<YueDisplay>
32
33```yue
34-- 用作表解构
35do
36 import insert, concat from table
37 -- 当给 insert, concat 变量赋值时,编译器会报告错误
38 import C, Ct, Cmt from require "lpeg"
39 -- 快捷写法引入模块的子项
40 import x, y, z from 'mymodule'
41 -- 使用Python风格的导入
42 from 'module' import a, b, c
43
44-- 快捷地导入一个模块
45do
46 import 'module'
47 import 'module_x'
48 import "d-a-s-h-e-s"
49 import "module.part"
50
51-- 导入模块后起一个别名使用,或是进行导入模块表的解构
52do
53 import "player" as PlayerModule
54 import "lpeg" as :C, :Ct, :Cmt
55 import "export" as {one, two, Something:{umm:{ch}}}
56```
57
58</YueDisplay>
59
60## 导入全局变量
61
62&emsp;&emsp;你可以使用 `import` 将指定的全局变量导入到本地变量中。当导入一系列对全局变量的链式访问时,最后一个访问的字段将被赋值给本地变量。
63
64```yuescript
65do
66 import tostring
67 import table.concat
68 print concat ["a", tostring 1]
69```
70<YueDisplay>
71
72```yue
73do
74 import tostring
75 import table.concat
76 print concat ["a", tostring 1]
77```
78
79</YueDisplay>
80
81### 自动全局变量导入
82
83&emsp;&emsp;在一个代码块的顶部写 `import global`,会将当前作用域中尚未显式声明或赋值过的变量名,自动导入为本地常量,并在该语句的位置绑定到同名的全局变量。
84
85&emsp;&emsp;但是在同一作用域中被显式声明为全局的变量不会被自动导入,因此可以继续进行赋值操作。
86
87```yuescript
88do
89 import global
90 print "hello"
91 math.random 3
92 -- print = nil -- 报错:自动导入的全局变量为常量
93
94do
95 -- 被显式声明为全局的变量不会被自动导入
96 import global
97 global FLAG
98 print FLAG
99 FLAG = 123
100```
101<YueDisplay>
102
103```yue
104do
105 import global
106 print "hello"
107 math.random 3
108 -- print = nil -- 报错:自动导入的全局变量是常量
109
110do
111 -- 被显式声明为全局的变量不会被自动导入
112 import global
113 global FLAG
114 print FLAG
115 FLAG = 123
116```
117
118</YueDisplay>
119
120## 导出
121
122&emsp;&emsp;导出语句提供了一种简洁的方式来定义当前的模块。
123
124### 命名导出
125
126&emsp;&emsp;带命名的导出将定义一个局部变量,并在导出的表中添加一个同名的字段。
127
128```yuescript
129export a, b, c = 1, 2, 3
130export cool = "cat"
131
132export What = if this
133 "abc"
134else
135 "def"
136
137export y = ->
138 hallo = 3434
139
140export class Something
141 umm: "cool"
142```
143<YueDisplay>
144
145```yue
146export a, b, c = 1, 2, 3
147export cool = "cat"
148
149export What = if this
150 "abc"
151else
152 "def"
153
154export y = ->
155 hallo = 3434
156
157export class Something
158 umm: "cool"
159```
160
161</YueDisplay>
162
163&emsp;&emsp;使用解构进行命名导出。
164
165```yuescript
166export :loadstring, to_lua: tolua = yue
167export {itemA: {:fieldA = '默认值'}} = tb
168```
169<YueDisplay>
170
171```yue
172export :loadstring, to_lua: tolua = yue
173export {itemA: {:fieldA = '默认值'}} = tb
174```
175
176</YueDisplay>
177
178&emsp;&emsp;从模块导出命名项目时,可以不用创建局部变量。
179
180```yuescript
181export.itemA = tb
182export.<index> = items
183export["a-b-c"] = 123
184```
185<YueDisplay>
186
187```yue
188export.itemA = tb
189export.<index> = items
190export["a-b-c"] = 123
191```
192
193</YueDisplay>
194
195### 未命名导出
196
197&emsp;&emsp;未命名导出会将要导出的目标项目添加到导出表的数组部分。
198
199```yuescript
200d, e, f = 3, 2, 1
201export d, e, f
202
203export if this
204 123
205else
206 456
207
208export with tmp
209 j = 2000
210```
211<YueDisplay>
212
213```yue
214d, e, f = 3, 2, 1
215export d, e, f
216
217export if this
218 123
219else
220 456
221
222export with tmp
223 j = 2000
224```
225
226</YueDisplay>
227
228### 默认导出
229
230&emsp;&emsp;在导出语句中使用 **default** 关键字,来替换导出的表为一个目标的对象。
231
232```yuescript
233export default ->
234 print "你好"
235 123
236```
237<YueDisplay>
238
239```yue
240export default ->
241 print "你好"
242 123
243```
244
245</YueDisplay>
diff --git a/doc/docs/zh/doc/object-oriented-programming.md b/doc/docs/zh/doc/object-oriented-programming.md
new file mode 100644
index 0000000..9eb94d8
--- /dev/null
+++ b/doc/docs/zh/doc/object-oriented-programming.md
@@ -0,0 +1,550 @@
1# 面向对象编程
2
3&emsp;&emsp;在以下的示例中,月之脚本生成的 Lua 代码可能看起来会很复杂。所以最好主要关注月之脚本代码层面的意义,然后如果你想知道关于面向对象功能的实现细节,再查看 Lua 代码。
4
5&emsp;&emsp;一个简单的类:
6
7```yuescript
8class Inventory
9 new: =>
10 @items = {}
11
12 add_item: (name) =>
13 if @items[name]
14 @items[name] += 1
15 else
16 @items[name] = 1
17```
18<YueDisplay>
19
20```yue
21class Inventory
22 new: =>
23 @items = {}
24
25 add_item: (name) =>
26 if @items[name]
27 @items[name] += 1
28 else
29 @items[name] = 1
30```
31
32</YueDisplay>
33
34&emsp;&emsp;在月之脚本中采用面向对象的编程方式时,通常会使用类声明语句结合 Lua 表格字面量来做类定义。这个类的定义包含了它的所有方法和属性。在这种结构中,键名为 “new” 的成员扮演了一个重要的角色,是作为构造函数来使用。
35
36&emsp;&emsp;值得注意的是,类中的方法都采用了粗箭头函数语法。当在类的实例上调用方法时,该实例会自动作为第一个参数被传入,因此粗箭头函数用于生成一个名为 “self” 的参数。
37
38&emsp;&emsp;此外,“@” 前缀在变量名上起到了简化作用,代表 “self”。例如,`@items` 就等同于 `self.items`。
39
40&emsp;&emsp;为了创建类的一个新实例,可以将类名当作一个函数来调用,这样就可以生成并返回一个新的实例。
41
42```yuescript
43inv = Inventory!
44inv\add_item "t-shirt"
45inv\add_item "pants"
46```
47<YueDisplay>
48
49
50```yue
51inv = Inventory!
52inv\add_item "t-shirt"
53inv\add_item "pants"
54```
55
56</YueDisplay>
57
58&emsp;&emsp;在月之脚本的类中,由于需要将类的实例作为参数传入到调用的方法中,因此使用了 **\\** 操作符做类的成员函数调用。
59
60&emsp;&emsp;需要特别注意的是,类的所有属性在其实例之间是共享的。这对于函数类型的成员属性通常不会造成问题,但对于其他类型的属性,可能会导致意外的结果。
61
62&emsp;&emsp;例如,在下面的示例中,clothes 属性在所有实例之间共享。因此,对这个属性在一个实例中的修改,将会影响到其他所有实例。
63
64```yuescript
65class Person
66 clothes: []
67 give_item: (name) =>
68 table.insert @clothes, name
69
70a = Person!
71b = Person!
72
73a\give_item "pants"
74b\give_item "shirt"
75
76-- 会同时打印出裤子和衬衫
77print item for item in *a.clothes
78```
79<YueDisplay>
80
81```yue
82class Person
83 clothes: []
84 give_item: (name) =>
85 table.insert @clothes, name
86
87a = Person!
88b = Person!
89
90a\give_item "pants"
91b\give_item "shirt"
92
93-- 会同时打印出裤子和衬衫
94print item for item in *a.clothes
95```
96
97</YueDisplay>
98
99&emsp;&emsp;避免这个问题的正确方法是在构造函数中创建对象的可变状态:
100
101```yuescript
102class Person
103 new: =>
104 @clothes = []
105```
106<YueDisplay>
107
108```yue
109class Person
110 new: =>
111 @clothes = []
112```
113
114</YueDisplay>
115
116## 继承
117
118&emsp;&emsp;`extends` 关键字可以在类声明中使用,以继承另一个类的属性和方法。
119
120```yuescript
121class BackPack extends Inventory
122 size: 10
123 add_item: (name) =>
124 if #@items > size then error "背包已满"
125 super name
126```
127<YueDisplay>
128
129```yue
130class BackPack extends Inventory
131 size: 10
132 add_item: (name) =>
133 if #@items > size then error "背包已满"
134 super name
135```
136
137</YueDisplay>
138
139&emsp;&emsp;在这一部分,我们对月之脚本中的 `Inventory` 类进行了扩展,加入了对可以携带物品数量的限制。
140
141&emsp;&emsp;在这个特定的例子中,子类并没有定义自己的构造函数。因此,当创建一个新的实例时,系统会默认调用父类的构造函数。但如果我们在子类中定义了构造函数,我们可以利用 `super` 方法来调用并执行父类的构造函数。
142
143&emsp;&emsp;此外,当一个类继承自另一个类时,它会尝试调用父类上的 `__inherited` 方法(如果这个方法存在的话),以此来向父类发送通知。这个 `__inherited` 函数接受两个参数:被继承的父类和继承的子类。
144
145```yuescript
146class Shelf
147 @__inherited: (child) =>
148 print @__name, "被", child.__name, "继承"
149
150-- 将打印: Shelf 被 Cupboard 继承
151class Cupboard extends Shelf
152```
153<YueDisplay>
154
155```yue
156class Shelf
157 @__inherited: (child) =>
158 print @__name, "被", child.__name, "继承"
159
160-- 将打印: Shelf 被 Cupboard 继承
161class Cupboard extends Shelf
162```
163
164</YueDisplay>
165
166## super 关键字
167
168&emsp;&emsp;`super` 是一个特别的关键字,它有两种不同的使用方式:既可以当作一个对象来看待,也可以像调用函数那样使用。它仅在类的内部使用时具有特殊的功能。
169
170&emsp;&emsp;当 `super` 被作为一个函数调用时,它将调用父类中与之同名的函数。此时,当前的 `self` 会自动作为第一个参数传递,正如上面提到的继承示例所展示的那样。
171
172&emsp;&emsp;在将 `super` 当作普通值使用时,它实际上是对父类对象的引用。通过这种方式,我们可以访问父类中可能被子类覆盖的值,就像访问任何普通对象一样。
173
174&emsp;&emsp;此外,当使用 `\` 操作符与 `super` 一起使用时,`self`将被插入为第一个参数,而不是使用 `super` 本身的值。而在使用`.`操作符来检索函数时,则会返回父类中的原始函数。
175
176&emsp;&emsp;下面是一些使用 `super` 的不同方法的示例:
177
178```yuescript
179class MyClass extends ParentClass
180 a_method: =>
181 -- 以下效果相同:
182 super "你好", "世界"
183 super\a_method "你好", "世界"
184 super.a_method self, "你好", "世界"
185
186 -- super 作为值等于父类:
187 assert super == ParentClass
188```
189<YueDisplay>
190
191```yue
192class MyClass extends ParentClass
193 a_method: =>
194 -- 以下效果相同:
195 super "你好", "世界"
196 super\a_method "你好", "世界"
197 super.a_method self, "你好", "世界"
198
199 -- super 作为值等于父类:
200 assert super == ParentClass
201```
202
203</YueDisplay>
204
205&emsp;&emsp;**super** 也可以用在函数存根的左侧。唯一的主要区别是,生成的函数不是绑定到 super 的值,而是绑定到 self。
206
207## 类型
208
209&emsp;&emsp;每个类的实例都带有它的类型。这存储在特殊的 \_\_class 属性中。此属性会保存类对象。类对象是我们用来构建新实例的对象。我们还可以索引类对象以检索类方法和属性。
210
211```yuescript
212b = BackPack!
213assert b.__class == BackPack
214
215print BackPack.size -- 打印 10
216```
217<YueDisplay>
218
219```yue
220b = BackPack!
221assert b.__class == BackPack
222
223print BackPack.size -- 打印 10
224```
225
226</YueDisplay>
227
228## 类对象
229
230&emsp;&emsp;在月之脚本中,当我们编写类的定义语句时,实际上是在创建一个类对象。这个类对象被保存在一个与该类同名的变量中。
231
232&emsp;&emsp;类对象具有函数的特性,可以被调用来创建新的实例。这正是我们在之前示例中所展示的创建类实例的方式。
233
234&emsp;&emsp;一个类由两个表构成:类表本身和一个基表。基表作为所有实例的元表。在类声明中列出的所有属性都存放在基表中。
235
236&emsp;&emsp;如果在类对象的元表中找不到某个属性,系统会从基表中检索该属性。这就意味着我们可以直接从类本身访问到其方法和属性。
237
238&emsp;&emsp;需要特别注意的是,对类对象的赋值并不会影响到基表,因此这不是向实例添加新方法的正确方式。相反,需要直接修改基表。关于这点,可以参考下面的 “__base” 字段。
239
240&emsp;&emsp;此外,类对象包含几个特殊的属性:当类被声明时,类的名称会作为一个字符串存储在类对象的 “__name” 字段中。
241
242```yuescript
243print BackPack.__name -- 打印 Backpack
244```
245<YueDisplay>
246
247```yue
248print BackPack.__name -- 打印 Backpack
249```
250
251</YueDisplay>
252
253&emsp;&emsp;基础对象被保存在一个名为 `__base` 的特殊表中。我们可以编辑这个表,以便为那些已经创建出来的实例和还未创建的实例增加新的功能。
254
255&emsp;&emsp;另外,如果一个类是从另一个类派生而来的,那么其父类对象则会被存储在名为 `__parent` 的地方。这种机制允许在类之间实现继承和功能扩展。
256
257## 类变量
258
259&emsp;&emsp;我们可以直接在类对象中创建变量,而不是在类的基对象中,通过在类声明中的属性名前使用 @。
260
261```yuescript
262class Things
263 @some_func: => print "Hello from", @__name
264
265Things\some_func!
266
267-- 类变量在实例中不可见
268assert Things().some_func == nil
269```
270<YueDisplay>
271
272```yue
273class Things
274 @some_func: => print "Hello from", @__name
275
276Things\some_func!
277
278-- 类变量在实例中不可见
279assert Things().some_func == nil
280```
281
282</YueDisplay>
283
284&emsp;&emsp;在表达式中,我们可以使用 @@ 来访问存储在 `self.__class` 中的值。因此,`@@hello` 是 `self.__class.hello` 的简写。
285
286```yuescript
287class Counter
288 @count: 0
289
290 new: =>
291 @@count += 1
292
293Counter!
294Counter!
295
296print Counter.count -- 输出 2
297```
298<YueDisplay>
299
300```yue
301class Counter
302 @count: 0
303
304 new: =>
305 @@count += 1
306
307Counter!
308Counter!
309
310print Counter.count -- 输出 2
311```
312
313</YueDisplay>
314
315&emsp;&emsp;@@ 的调用语义与 @ 类似。调用 @@ 时,会使用 Lua 的冒号语法将类作为第一个参数传入。
316
317```yuescript
318@@hello 1,2,3,4
319```
320<YueDisplay>
321
322```yue
323@@hello 1,2,3,4
324```
325
326</YueDisplay>
327
328## 类声明语句
329
330&emsp;&emsp;在类声明的主体中,除了键/值对外,我们还可以编写普通的表达式。在这种类声明体中的普通代码的上下文中,self 等于类对象,而不是实例对象。
331
332&emsp;&emsp;以下是创建类变量的另一种方法:
333
334```yuescript
335class Things
336 @class_var = "hello world"
337```
338<YueDisplay>
339
340```yue
341class Things
342 @class_var = "hello world"
343```
344
345</YueDisplay>
346
347&emsp;&emsp;这些表达式会在所有属性被添加到类的基对象后执行。
348
349&emsp;&emsp;在类的主体中声明的所有变量都会限制作用域只在类声明的范围。这对于放置只有类方法可以访问的私有值或辅助函数很方便:
350
351```yuescript
352class MoreThings
353 secret = 123
354 log = (msg) -> print "LOG:", msg
355
356 some_method: =>
357 log "hello world: " .. secret
358```
359<YueDisplay>
360
361```yue
362class MoreThings
363 secret = 123
364 log = (msg) -> print "LOG:", msg
365
366 some_method: =>
367 log "hello world: " .. secret
368```
369
370</YueDisplay>
371
372## @ 和 @@ 值
373
374&emsp;&emsp;当 @ 和 @@ 前缀在一个名字前时,它们分别代表在 self 和 self.\_\_class 中访问的那个名字。
375
376&emsp;&emsp;如果它们单独使用,它们是 self 和 self.\_\_class 的别名。
377
378```yuescript
379assert @ == self
380assert @@ == self.__class
381```
382<YueDisplay>
383
384```yue
385assert @ == self
386assert @@ == self.__class
387```
388
389</YueDisplay>
390
391&emsp;&emsp;例如,使用 @@ 从实例方法快速创建同一类的新实例的方法:
392
393```yuescript
394some_instance_method = (...) => @@ ...
395```
396<YueDisplay>
397
398```yue
399some_instance_method = (...) => @@ ...
400```
401
402</YueDisplay>
403
404## 构造属性提升
405
406&emsp;&emsp;为了减少编写简单值对象定义的代码。你可以这样简单写一个类:
407
408```yuescript
409class Something
410 new: (@foo, @bar, @@biz, @@baz) =>
411
412-- 这是以下声明的简写形式
413
414class Something
415 new: (foo, bar, biz, baz) =>
416 @foo = foo
417 @bar = bar
418 @@biz = biz
419 @@baz = baz
420```
421<YueDisplay>
422
423```yue
424class Something
425 new: (@foo, @bar, @@biz, @@baz) =>
426
427-- 这是以下声明的简写形式
428
429class Something
430 new: (foo, bar, biz, baz) =>
431 @foo = foo
432 @bar = bar
433 @@biz = biz
434 @@baz = baz
435```
436
437</YueDisplay>
438
439&emsp;&emsp;你也可以使用这种语法为一个函数初始化传入对象的字段。
440
441```yuescript
442new = (@fieldA, @fieldB) => @
443obj = new {}, 123, "abc"
444print obj
445```
446<YueDisplay>
447
448```yue
449new = (@fieldA, @fieldB) => @
450obj = new {}, 123, "abc"
451print obj
452```
453
454</YueDisplay>
455
456## 类表达式
457
458&emsp;&emsp;类声明的语法也可以作为一个表达式使用,可以赋值给一个变量或者被返回语句返回。
459
460```yuescript
461x = class Bucket
462 drops: 0
463 add_drop: => @drops += 1
464```
465<YueDisplay>
466
467```yue
468x = class Bucket
469 drops: 0
470 add_drop: => @drops += 1
471```
472
473</YueDisplay>
474
475## 匿名类
476
477&emsp;&emsp;声明类时可以省略名称。如果类的表达式不在赋值语句中,\_\_name 属性将为 nil。如果出现在赋值语句中,赋值操作左侧的名称将代替 nil。
478
479```yuescript
480BigBucket = class extends Bucket
481 add_drop: => @drops += 10
482
483assert Bucket.__name == "BigBucket"
484```
485<YueDisplay>
486
487```yue
488BigBucket = class extends Bucket
489 add_drop: => @drops += 10
490
491assert Bucket.__name == "BigBucket"
492```
493
494</YueDisplay>
495
496&emsp;&emsp;你甚至可以省略掉主体,这意味着你可以这样写一个空白的匿名类:
497
498```yuescript
499x = class
500```
501<YueDisplay>
502
503```yue
504x = class
505```
506
507</YueDisplay>
508
509## 类混合
510
511&emsp;&emsp;你可以通过使用 `using` 关键字来实现类混合。这意味着你可以从一个普通 Lua 表格或已定义的类对象中,复制函数到你创建的新类中。当你使用普通 Lua 表格进行类混合时,你有机会用自己的实现来重写类的索引方法(例如元方法 `__index`)。然而,当你从一个类对象做混合时,需要注意的是该类对象的元方法将不会被复制到新类。
512
513```yuescript
514MyIndex = __index: var: 1
515
516class X using MyIndex
517 func: =>
518 print 123
519
520x = X!
521print x.var
522
523class Y using X
524
525y = Y!
526y\func!
527
528assert y.__class.__parent ~= X -- X 不是 Y 的父类
529```
530<YueDisplay>
531
532```yue
533MyIndex = __index: var: 1
534
535class X using MyIndex
536 func: =>
537 print 123
538
539x = X!
540print x.var
541
542class Y using X
543
544y = Y!
545y\func!
546
547assert y.__class.__parent ~= X -- X 不是 Y 的父类
548```
549
550</YueDisplay>
diff --git a/doc/docs/zh/doc/operator.md b/doc/docs/zh/doc/operator.md
new file mode 100644
index 0000000..6c9fc5b
--- /dev/null
+++ b/doc/docs/zh/doc/operator.md
@@ -0,0 +1,461 @@
1# 操作符
2
3&emsp;&emsp;Lua 的所有二元和一元操作符在月之脚本中都是可用的。此外,**!=** 符号是 **~=** 的别名,而 **\\** 或 **::** 均可用于编写链式函数调用,如写作 `tb\func!` 或 `tb::func!`。此外月之脚本还提供了一些其他特殊的操作符,以编写更具表达力的代码。
4
5```yuescript
6tb\func! if tb ~= nil
7tb::func! if tb != nil
8```
9<YueDisplay>
10
11```yue
12tb\func! if tb ~= nil
13tb::func! if tb != nil
14```
15
16</YueDisplay>
17
18## 链式比较
19
20&emsp;&emsp;你可以在月之脚本中进行比较表达式的链式书写:
21
22```yuescript
23print 1 < 2 <= 2 < 3 == 3 > 2 >= 1 == 1 < 3 != 5
24-- 输出:true
25
26a = 5
27print 1 <= a <= 10
28-- 输出:true
29```
30<YueDisplay>
31
32```yue
33print 1 < 2 <= 2 < 3 == 3 > 2 >= 1 == 1 < 3 != 5
34-- 输出:true
35
36a = 5
37print 1 <= a <= 10
38-- 输出:true
39```
40
41</YueDisplay>
42
43&emsp;&emsp;可以注意一下链式比较表达式的求值行为:
44
45```yuescript
46v = (x) ->
47 print x
48 x
49
50print v(1) < v(2) <= v(3)
51--[[
52 输出:
53 2
54 1
55 3
56 true
57]]
58
59print v(1) > v(2) <= v(3)
60--[[
61 输出:
62 2
63 1
64 false
65]]
66```
67<YueDisplay>
68
69```yue
70v = (x) ->
71 print x
72 x
73
74print v(1) < v(2) <= v(3)
75--[[
76 输出:
77 2
78 1
79 3
80 true
81]]
82
83print v(1) > v(2) <= v(3)
84--[[
85 输出:
86 2
87 1
88 false
89]]
90```
91
92</YueDisplay>
93
94&emsp;&emsp;在上面的例子里,中间的表达式 `v(2)` 仅被计算一次,如果把表达式写成 `v(1) < v(2) and v(2) <= v(3)` 的方式,中间的 `v(2)` 才会被计算两次。在链式比较中,求值的顺序往往是未定义的。所以强烈建议不要在链式比较中使用具有副作用(比如做打印操作)的表达式。如果需要使用有副作用的函数,应明确使用短路 `and` 运算符来做连接。
95
96## 表追加
97
98&emsp;&emsp;**[] =** 操作符用于向 Lua 表的最后插入值。
99
100```yuescript
101tab = []
102tab[] = "Value"
103```
104<YueDisplay>
105
106```yue
107tab = []
108tab[] = "Value"
109```
110
111</YueDisplay>
112
113&emsp;&emsp;你还可以使用展开操作符 `...` 来将一个列表中的所有元素追加到另一个列表中:
114
115```yuescript
116tbA = [1, 2, 3]
117tbB = [4, 5, 6]
118tbA[] = ...tbB
119-- tbA 现在为 [1, 2, 3, 4, 5, 6]
120```
121<YueDisplay>
122
123```yue
124tbA = [1, 2, 3]
125tbB = [4, 5, 6]
126tbA[] = ...tbB
127-- tbA 现在为 [1, 2, 3, 4, 5, 6]
128```
129
130</YueDisplay>
131
132## 表扩展
133
134&emsp;&emsp;你可以使用前置 `...` 操作符在 Lua 表中插入数组表或哈希表。
135
136```yuescript
137parts =
138 * "shoulders"
139 * "knees"
140lyrics =
141 * "head"
142 * ...parts
143 * "and"
144 * "toes"
145
146copy = {...other}
147
148a = {1, 2, 3, x: 1}
149b = {4, 5, y: 1}
150merge = {...a, ...b}
151```
152<YueDisplay>
153
154```yue
155parts =
156 * "shoulders"
157 * "knees"
158lyrics =
159 * "head"
160 * ...parts
161 * "and"
162 * "toes"
163
164copy = {...other}
165
166a = {1, 2, 3, x: 1}
167b = {4, 5, y: 1}
168merge = {...a, ...b}
169```
170
171</YueDisplay>
172
173## 表反向索引
174
175&emsp;&emsp;你可以使用 **#** 操作符来反向索引表中的元素。
176
177```yuescript
178last = data.items[#]
179second_last = data.items[#-1]
180data.items[#] = 1
181```
182<YueDisplay>
183
184```yue
185last = data.items[#]
186second_last = data.items[#-1]
187data.items[#] = 1
188```
189
190</YueDisplay>
191
192## 元表
193
194&emsp;&emsp;**<>** 操作符可提供元表操作的快捷方式。
195
196### 元表创建
197
198&emsp;&emsp;使用空括号 **<>** 或被 **<>** 包围的元方法键创建普通的 Lua 表。
199
200```yuescript
201mt = {}
202add = (right) => <>: mt, value: @value + right.value
203mt.__add = add
204
205a = <>: mt, value: 1
206-- 使用与临时变量名相同的字段名,将临时变量赋值给元表
207b = :<add>, value: 2
208c = <add>: mt.__add, value: 3
209
210d = a + b + c
211print d.value
212
213close _ = <close>: -> print "超出范围"
214```
215<YueDisplay>
216
217```yue
218mt = {}
219add = (right) => <>: mt, value: @value + right.value
220mt.__add = add
221
222a = <>: mt, value: 1
223-- 使用与临时变量名相同的字段名,将临时变量赋值给元表
224b = :<add>, value: 2
225c = <add>: mt.__add, value: 3
226
227d = a + b + c
228print d.value
229
230close _ = <close>: -> print "超出范围"
231```
232
233</YueDisplay>
234
235### 元表访问
236
237&emsp;&emsp;使用 **<>** 或被 **<>** 包围的元方法名或在 **<>** 中编写某些表达式来访问元表。
238
239```yuescript
240-- 使用包含字段 "value" 的元表创建
241tb = <"value">: 123
242tb.<index> = tb.<>
243print tb.value
244
245tb.<> = __index: {item: "hello"}
246print tb.item
247```
248<YueDisplay>
249
250
251```yue
252-- 使用包含字段 "value" 的元表创建
253tb = <"value">: 123
254tb.<index> = tb.<>
255print tb.value
256tb.<> = __index: {item: "hello"}
257print tb.item
258```
259
260</YueDisplay>
261
262### 元表解构
263
264&emsp;&emsp;使用被 **<>** 包围的元方法键解构元表。
265
266```yuescript
267{item, :new, :<close>, <index>: getter} = tb
268print item, new, close, getter
269```
270<YueDisplay>
271
272```yue
273{item, :new, :<close>, <index>: getter} = tb
274print item, new, close, getter
275```
276
277</YueDisplay>
278
279## 存在性
280
281&emsp;&emsp;**?** 运算符可以在多种上下文中用来检查存在性。
282
283```yuescript
284func?!
285print abc?["你好 世界"]?.xyz
286
287x = tab?.value
288len = utf8?.len or string?.len or (o) -> #o
289
290if print and x?
291 print x
292
293with? io.open "test.txt", "w"
294 \write "你好"
295 \close!
296```
297<YueDisplay>
298
299```yue
300func?!
301print abc?["你好 世界"]?.xyz
302
303x = tab?.value
304len = utf8?.len or string?.len or (o) -> #o
305
306if print and x?
307 print x
308
309with? io.open "test.txt", "w"
310 \write "你好"
311 \close!
312```
313
314</YueDisplay>
315
316## 管道
317
318&emsp;&emsp;与其使用一系列嵌套的函数调用,你还可以考虑使用运算符 **|>** 来传递值。
319
320```yuescript
321"你好" |> print
3221 |> print 2 -- 将管道项作为第一个参数插入
3232 |> print 1, _, 3 -- 带有占位符的管道
324
325-- 多行的管道表达式
326readFile "example.txt"
327 |> extract language, {}
328 |> parse language
329 |> emit
330 |> render
331 |> print
332```
333<YueDisplay>
334
335```yue
336"你好" |> print
3371 |> print 2 -- 将管道项作为第一个参数插入
3382 |> print 1, _, 3 -- 带有占位符的管道
339-- 多行的管道表达式
340readFile "example.txt"
341 |> extract language, {}
342 |> parse language
343 |> emit
344 |> render
345 |> print
346```
347
348</YueDisplay>
349
350## 空值合并
351
352&emsp;&emsp;如果其左操作数不是 **nil**,则nil合并运算符 **??** 返回其左操作数的值;否则,它将计算右操作数并返回其结果。如果左操作数计算结果为非 nil 的值,**??** 运算符将不再计算其右操作数。
353
354```yuescript
355local a, b, c, d
356a = b ?? c ?? d
357func a ?? {}
358
359a ??= false
360```
361<YueDisplay>
362
363```yue
364local a, b, c, d
365a = b ?? c ?? d
366func a ?? {}
367a ??= false
368```
369
370</YueDisplay>
371
372## 隐式对象
373
374&emsp;&emsp;你可以在表格块内使用符号 **\*** 或是 **-** 开始编写一系列隐式结构。如果你正在创建隐式对象,对象的字段必须具有相同的缩进。
375
376```yuescript
377-- 赋值时使用隐式对象
378list =
379 * 1
380 * 2
381 * 3
382
383-- 函数调用时使用隐式对象
384func
385 * 1
386 * 2
387 * 3
388
389-- 返回时使用隐式对象
390f = ->
391 return
392 * 1
393 * 2
394 * 3
395
396-- 表格时使用隐式对象
397tb =
398 name: "abc"
399
400 values:
401 - "a"
402 - "b"
403 - "c"
404
405 objects:
406 - name: "a"
407 value: 1
408 func: => @value + 1
409 tb:
410 fieldA: 1
411
412 - name: "b"
413 value: 2
414 func: => @value + 2
415 tb: { }
416```
417<YueDisplay>
418
419```yue
420-- 赋值时使用隐式对象
421list =
422 * 1
423 * 2
424 * 3
425
426-- 函数调用时使用隐式对象
427func
428 * 1
429 * 2
430 * 3
431
432-- 返回时使用隐式对象
433f = ->
434 return
435 * 1
436 * 2
437 * 3
438
439-- 表格时使用隐式对象
440tb =
441 name: "abc"
442
443 values:
444 - "a"
445 - "b"
446 - "c"
447
448 objects:
449 - name: "a"
450 value: 1
451 func: => @value + 1
452 tb:
453 fieldA: 1
454
455 - name: "b"
456 value: 2
457 func: => @value + 2
458 tb: { }
459```
460
461</YueDisplay>
diff --git a/doc/docs/zh/doc/switch.md b/doc/docs/zh/doc/switch.md
new file mode 100644
index 0000000..700bc2a
--- /dev/null
+++ b/doc/docs/zh/doc/switch.md
@@ -0,0 +1,296 @@
1# switch 语句
2
3&emsp;&emsp;switch 语句是为了简化检查一系列相同值的if语句而提供的简写语法。要注意用于比较检查的目标值只会计算一次。和 if 语句一样,switch 语句在最后可以接一个 else 代码块来处理没有匹配的情况。在生成的 Lua 代码中,进行比较是使用 == 操作符完成的。switch 语句中也可以使用赋值表达式来储存临时变量值。
4
5```yuescript
6switch name := "Dan"
7 when "Robert"
8 print "你是Robert"
9 when "Dan", "Daniel"
10 print "你的名字是Dan"
11 else
12 print "我不认识你,你的名字是#{name}"
13```
14<YueDisplay>
15
16```yue
17switch name := "Dan"
18 when "Robert"
19 print "你是Robert"
20 when "Dan", "Daniel"
21 print "你的名字是Dan"
22 else
23 print "我不认识你,你的名字是#{name}"
24```
25
26</YueDisplay>
27
28&emsp;&emsp;switch 语句的 when 子句中可以通过使用逗号分隔的列表来匹配多个值。
29
30&emsp;&emsp;switch 语句也可以作为表达式使用,下面我们可以将 switch 语句返回的结果分配给一个变量:
31
32```yuescript
33b = 1
34next_number = switch b
35 when 1
36 2
37 when 2
38 3
39 else
40 error "数字数得太大了!"
41```
42<YueDisplay>
43
44```yue
45b = 1
46next_number = switch b
47 when 1
48 2
49 when 2
50 3
51 else
52 error "数字数得太大了!"
53```
54
55</YueDisplay>
56
57&emsp;&emsp;我们可以使用 then 关键字在 when 子句的同一行上编写处理代码。else 代码块的后续代码中要写在同一行上不需要额外的关键字。
58
59```yuescript
60msg = switch math.random(1, 5)
61 when 1 then "你很幸运"
62 when 2 then "你差点很幸运"
63 else "不太幸运"
64```
65<YueDisplay>
66
67```yue
68msg = switch math.random(1, 5)
69 when 1 then "你很幸运"
70 when 2 then "你差点很幸运"
71 else "不太幸运"
72```
73
74</YueDisplay>
75
76&emsp;&emsp;如果在编写 switch 语句时希望少写一个缩进,那么你可以把第一个 when 子句放在 switch 开始语句的第一行,然后后续的子语句就都可以都少写一个缩进。
77
78```yuescript
79switch math.random(1, 5)
80 when 1
81 print "你很幸运" -- 两个缩进级别
82 else
83 print "不太幸运"
84
85switch math.random(1, 5) when 1
86 print "你很幸运" -- 一个缩进级别
87else
88 print "不太幸运"
89```
90<YueDisplay>
91
92```yue
93switch math.random(1, 5)
94 when 1
95 print "你很幸运" -- 两个缩进级别
96 else
97 print "不太幸运"
98
99switch math.random(1, 5) when 1
100 print "你很幸运" -- 一个缩进级别
101else
102 print "不太幸运"
103```
104
105</YueDisplay>
106
107&emsp;&emsp;值得注意的是,在生成 Lua 代码时,我们要做检查的目标变量会放在 == 表达式的右侧。当你希望给 when 子句的比较对象定义一个 \_\_eq 元方法来重载判断逻辑时,可能会有用。
108
109## 表格匹配
110
111&emsp;&emsp;在 switch 的 when 子句中,如果期待检查目标是一个表格,且可以通过特定的结构进行解构并获得非 nil 值,那么你可以尝试使用表格匹配的语法。
112
113```yuescript
114items =
115 * x: 100
116 y: 200
117 * width: 300
118 height: 400
119
120for item in *items
121 switch item
122 when :x, :y
123 print "Vec2 #{x}, #{y}"
124 when :width, :height
125 print "尺寸 #{width}, #{height}"
126```
127<YueDisplay>
128
129```yue
130items =
131 * x: 100
132 y: 200
133 * width: 300
134 height: 400
135
136for item in *items
137 switch item
138 when :x, :y
139 print "Vec2 #{x}, #{y}"
140 when :width, :height
141 print "尺寸 #{width}, #{height}"
142```
143
144</YueDisplay>
145
146&emsp;&emsp;你可以使用默认值来选择性地解构表格的某些字段。
147
148```yuescript
149item = {}
150
151{pos: {:x = 50, :y = 200}} = item -- 获取错误:尝试索引nil值(字段'pos')
152
153switch item
154 when {pos: {:x = 50, :y = 200}}
155 print "Vec2 #{x}, #{y}" -- 表格解构仍然会通过
156```
157<YueDisplay>
158
159```yue
160item = {}
161
162{pos: {:x = 50, :y = 200}} = item -- 获取错误:尝试索引nil值(字段'pos')
163
164switch item
165 when {pos: {:x = 50, :y = 200}}
166 print "Vec2 #{x}, #{y}" -- 表格解构仍然会通过
167```
168
169</YueDisplay>
170
171&emsp;&emsp;你也可以匹配数组元素、表格字段,甚至使用数组或表格字面量来匹配嵌套的结构。
172
173&emsp;&emsp;匹配数组元素。
174
175```yuescript
176switch tb
177 when [1, 2, 3]
178 print "1, 2, 3"
179 when [1, b, 3]
180 print "1, #{b}, 3"
181 when [1, 2, b = 3] -- 变量b有默认值
182 print "1, 2, #{b}"
183```
184<YueDisplay>
185
186```yue
187switch tb
188 when [1, 2, 3]
189 print "1, 2, 3"
190 when [1, b, 3]
191 print "1, #{b}, 3"
192 when [1, 2, b = 3] -- 变量b有默认值
193 print "1, 2, #{b}"
194```
195
196</YueDisplay>
197
198&emsp;&emsp;匹配表格字段。
199
200```yuescript
201switch tb
202 when success: true, :result
203 print "成功", result
204 when success: false
205 print "失败", result
206 else
207 print "无效值"
208```
209<YueDisplay>
210
211```yue
212switch tb
213 when success: true, :result
214 print "成功", result
215 when success: false
216 print "失败", result
217 else
218 print "无效值"
219```
220
221</YueDisplay>
222
223&emsp;&emsp;匹配嵌套的表格结构。
224
225```yuescript
226switch tb
227 when data: {type: "success", :content}
228 print "成功", content
229 when data: {type: "error", :content}
230 print "失败", content
231 else
232 print "无效值"
233```
234<YueDisplay>
235
236```yue
237switch tb
238 when data: {type: "success", :content}
239 print "成功", content
240 when data: {type: "error", :content}
241 print "失败", content
242 else
243 print "无效值"
244```
245
246</YueDisplay>
247
248&emsp;&emsp;匹配表格数组。
249
250```yuescript
251switch tb
252 when [
253 {a: 1, b: 2}
254 {a: 3, b: 4}
255 {a: 5, b: 6}
256 fourth
257 ]
258 print "匹配成功", fourth
259```
260<YueDisplay>
261
262```yue
263switch tb
264 when [
265 {a: 1, b: 2}
266 {a: 3, b: 4}
267 {a: 5, b: 6}
268 fourth
269 ]
270 print "匹配成功", fourth
271```
272
273</YueDisplay>
274
275&emsp;&emsp;匹配一个列表并捕获特定范围内的元素。
276
277```yuescript
278segments = ["admin", "users", "logs", "view"]
279switch segments
280 when [...groups, resource, action]
281 print "Group:", groups -- 打印: {"admin", "users"}
282 print "Resource:", resource -- 打印: "logs"
283 print "Action:", action -- 打印: "view"
284```
285<YueDisplay>
286
287```yue
288segments = ["admin", "users", "logs", "view"]
289switch segments
290 when [...groups, resource, action]
291 print "Group:", groups -- 打印: {"admin", "users"}
292 print "Resource:", resource -- 打印: "logs"
293 print "Action:", action -- 打印: "view"
294```
295
296</YueDisplay>
diff --git a/doc/docs/zh/doc/table-literals.md b/doc/docs/zh/doc/table-literals.md
new file mode 100644
index 0000000..a111950
--- /dev/null
+++ b/doc/docs/zh/doc/table-literals.md
@@ -0,0 +1,168 @@
1# 表格字面量
2
3&emsp;&emsp;和 Lua 一样,表格可以通过花括号进行定义。
4
5```yuescript
6some_values = [1, 2, 3, 4]
7```
8<YueDisplay>
9
10```yue
11some_values = [1, 2, 3, 4]
12```
13
14</YueDisplay>
15
16&emsp;&emsp;但与Lua不同的是,给表格中的键赋值是用 **:**(而不是 **=**)。
17
18```yuescript
19some_values = {
20 name: "Bill",
21 age: 200,
22 ["favorite food"]: "rice"
23}
24```
25<YueDisplay>
26
27```yue
28some_values = {
29 name: "Bill",
30 age: 200,
31 ["favorite food"]: "rice"
32}
33```
34
35</YueDisplay>
36
37&emsp;&emsp;如果只分配一个键值对的表格,可以省略花括号。
38
39```yuescript
40profile =
41 height: "4英尺",
42 shoe_size: 13,
43 favorite_foods: ["冰淇淋", "甜甜圈"]
44```
45<YueDisplay>
46
47```yue
48profile =
49 height: "4英尺",
50 shoe_size: 13,
51 favorite_foods: ["冰淇淋", "甜甜圈"]
52```
53
54</YueDisplay>
55
56&emsp;&emsp;可以使用换行符而不使用逗号(或两者都用)来分隔表格中的值:
57
58```yuescript
59values = {
60 1, 2, 3, 4
61 5, 6, 7, 8
62 name: "超人"
63 occupation: "打击犯罪"
64}
65```
66<YueDisplay>
67
68```yue
69values = {
70 1, 2, 3, 4
71 5, 6, 7, 8
72 name: "超人"
73 occupation: "打击犯罪"
74}
75```
76
77</YueDisplay>
78
79&emsp;&emsp;创建单行表格字面量时,也可以省略花括号:
80
81```yuescript
82my_function dance: "探戈", partner: "无"
83
84y = type: "狗", legs: 4, tails: 1
85```
86<YueDisplay>
87
88```yue
89my_function dance: "探戈", partner: "无"
90
91y = type: "狗", legs: 4, tails: 1
92```
93
94</YueDisplay>
95
96&emsp;&emsp;表格字面量的键可以使用 Lua 语言的关键字,而无需转义:
97
98```yuescript
99tbl = {
100 do: "某事"
101 end: "饥饿"
102}
103```
104<YueDisplay>
105
106```yue
107tbl = {
108 do: "某事"
109 end: "饥饿"
110}
111```
112
113</YueDisplay>
114
115&emsp;&emsp;如果你要构造一个由变量组成的表,并希望键与变量名相同,那么可以使用 **:** 前缀操作符:
116
117```yuescript
118hair = "金色"
119height = 200
120person = { :hair, :height, shoe_size: 40 }
121
122print_table :hair, :height
123```
124<YueDisplay>
125
126```yue
127hair = "金色"
128height = 200
129person = { :hair, :height, shoe_size: 40 }
130
131print_table :hair, :height
132```
133
134</YueDisplay>
135
136&emsp;&emsp;如果你希望表中字段的键是某个表达式的结果,那么可以用 **[ ]** 包裹它,就像在 Lua 中一样。如果键中有任何特殊字符,也可以直接使用字符串字面量作为键,省略方括号。
137
138```yuescript
139t = {
140 [1 + 2]: "你好"
141 "你好 世界": true
142}
143```
144<YueDisplay>
145
146```yue
147t = {
148 [1 + 2]: "你好"
149 "你好 世界": true
150}
151```
152
153</YueDisplay>
154
155&emsp;&emsp;Lua 的表同时具有数组部分和哈希部分,但有时候你会希望在书写 Lua 表时,对 Lua 表做数组和哈希不同用法的语义区分。然后你可以用 **[ ]** 而不是 **{ }** 来编写表示数组的 Lua 表,并且不允许在数组 Lua 表中写入任何键值对。
156
157```yuescript
158some_values = [ 1, 2, 3, 4 ]
159list_with_one_element = [ 1, ]
160```
161<YueDisplay>
162
163```yue
164some_values = [ 1, 2, 3, 4 ]
165list_with_one_element = [ 1, ]
166```
167
168</YueDisplay>
diff --git a/doc/docs/zh/doc/the-using-clause-controlling-destructive-assignment.md b/doc/docs/zh/doc/the-using-clause-controlling-destructive-assignment.md
new file mode 100644
index 0000000..722de6f
--- /dev/null
+++ b/doc/docs/zh/doc/the-using-clause-controlling-destructive-assignment.md
@@ -0,0 +1,98 @@
1# 使用 using 语句:防止破坏性赋值
2
3&emsp;&emsp;Lua 的变量作用域是降低代码复杂度的重要工具。然而,随着代码量的增加,维护这些变量可能变得更加困难。比如,看看下面的代码片段:
4
5```yuescript
6i = 100
7
8-- 许多代码行...
9
10my_func = ->
11 i = 10
12 while i > 0
13 print i
14 i -= 1
15
16my_func!
17
18print i -- 将打印 0
19```
20<YueDisplay>
21
22```yue
23i = 100
24
25-- 许多代码行...
26
27my_func = ->
28 i = 10
29 while i > 0
30 print i
31 i -= 1
32
33my_func!
34
35print i -- 将打印 0
36```
37
38</YueDisplay>
39
40&emsp;&emsp;在 `my_func` 中,我们不小心覆盖了变量 `i` 的值。虽然在这个例子中这个问题很明显,但在一个庞大的或者是由多人共同维护的代码库中,很难追踪每个变量的声明情况。
41
42&emsp;&emsp;如果我们可以明确指出哪些变量是我们想在当前作用域内修改的,并且防止我们不小心更改了其他作用域中同名的变量,那将大有裨益。
43
44&emsp;&emsp;`using` 语句就是为此而生。`using nil` 确保函数内部的赋值不会意外地影响到外部作用域的变量。我们只需将 `using` 子句放在函数的参数列表之后;若函数没有参数,则直接放在括号内即可。
45
46```yuescript
47i = 100
48
49my_func = (using nil) ->
50 i = "hello" -- 这里创建了一个新的局部变量
51
52my_func!
53print i -- 打印 100,i 没有受到影响
54```
55<YueDisplay>
56
57```yue
58i = 100
59
60my_func = (using nil) ->
61 i = "hello" -- 这里创建了一个新的局部变量
62
63my_func!
64print i -- 打印 100,i 没有受到影响
65```
66
67</YueDisplay>
68
69&emsp;&emsp;using子句中可以填写多个用逗号分隔名称。指定可以访问和修改的外部变量的名称:
70
71```yuescript
72tmp = 1213
73i, k = 100, 50
74
75my_func = (add using k, i) ->
76 tmp = tmp + add -- 创建了一个新的局部tmp
77 i += tmp
78 k += tmp
79
80my_func(22)
81print i, k -- 这些已经被更新
82```
83<YueDisplay>
84
85```yue
86tmp = 1213
87i, k = 100, 50
88
89my_func = (add using k, i) ->
90 tmp = tmp + add -- 创建了一个新的局部tmp
91 i += tmp
92 k += tmp
93
94my_func(22)
95print i, k -- 这些已经被更新
96```
97
98</YueDisplay>
diff --git a/doc/docs/zh/doc/the-yuescript-library.md b/doc/docs/zh/doc/the-yuescript-library.md
new file mode 100644
index 0000000..54e26f7
--- /dev/null
+++ b/doc/docs/zh/doc/the-yuescript-library.md
@@ -0,0 +1,821 @@
1# 月之脚本语言库
2
3在 Lua 中使用 `local yue = require("yue")` 来访问。
4
5## yue
6
7**描述:**
8
9月之脚本语言库。
10
11### version
12
13**类型:** 成员变量。
14
15**描述:**
16
17月之脚本版本。
18
19**签名:**
20```lua
21version: string
22```
23
24### dirsep
25
26**类型:** 成员变量。
27
28**描述:**
29
30当前平台的文件分隔符。
31
32**签名:**
33```lua
34dirsep: string
35```
36
37### yue_compiled
38
39**类型:** 成员变量。
40
41**描述:**
42
43编译模块代码缓存。
44
45**签名:**
46```lua
47yue_compiled: {string: string}
48```
49
50### to_lua
51
52**类型:** 函数。
53
54**描述:**
55
56月之脚本的编译函数。它将 YueScript 代码编译为 Lua 代码。
57
58**签名:**
59```lua
60to_lua: function(code: string, config?: Config):
61 --[[codes]] string | nil,
62 --[[error]] string | nil,
63 --[[globals]] {{string, integer, integer}} | nil
64```
65
66**参数:**
67
68| 参数名 | 类型 | 描述 |
69| --- | --- | --- |
70| code | string | YueScript 代码。 |
71| config | Config | [可选] 编译器选项。 |
72
73**返回值:**
74
75| 返回类型 | 描述 |
76| --- | --- |
77| string \| nil | 编译后的 Lua 代码,如果编译失败则为 nil。 |
78| string \| nil | 错误消息,如果编译成功则为 nil。 |
79| {{string, integer, integer}} \| nil | 代码中出现的全局变量(带有名称、行和列),如果编译器选项 `lint_global` 为 false 则为 nil。 |
80
81### file_exist
82
83**类型:** 函数。
84
85**描述:**
86
87检查源文件是否存在的函数。可以覆盖该函数以自定义行为。
88
89**签名:**
90```lua
91file_exist: function(filename: string): boolean
92```
93
94**参数:**
95
96| 参数名 | 类型 | 描述 |
97| --- | --- | --- |
98| filename | string | 文件名。 |
99
100**返回值:**
101
102| 返回类型 | 描述 |
103| --- | --- |
104| boolean | 文件是否存在。 |
105
106### read_file
107
108**类型:** 函数。
109
110**描述:**
111
112读取源文件的函数。可以覆盖该函数以自定义行为。
113
114**签名:**
115```lua
116read_file: function(filename: string): string
117```
118
119**参数:**
120
121| 参数名 | 类型 | 描述 |
122| --- | --- | --- |
123| filename | string | 文件名。 |
124
125**返回值:**
126
127| 返回类型 | 描述 |
128| --- | --- |
129| string | 文件内容。 |
130
131### insert_loader
132
133**类型:** 函数。
134
135**描述:**
136
137将 YueScript 加载器插入到 Lua 包加载器(搜索器)中。
138
139**签名:**
140```lua
141insert_loader: function(pos?: integer): boolean
142```
143
144**参数:**
145
146| 参数名 | 类型 | 描述 |
147| --- | --- | --- |
148| pos | integer | [可选] 要插入加载器的位置。默认为 3。 |
149
150**返回值:**
151
152| 返回类型 | 描述 |
153| --- | --- |
154| boolean | 是否成功插入加载器。如果加载器已经插入,则返回失败。 |
155
156### remove_loader
157
158**类型:** 函数。
159
160**描述:**
161
162从 Lua 包加载器(搜索器)中移除 YueScript 加载器。
163
164**签名:**
165```lua
166remove_loader: function(): boolean
167```
168
169**返回值:**
170
171| 返回类型 | 描述 |
172| --- | --- |
173| boolean | 是否成功移除加载器。如果加载器未插入,则返回失败。 |
174
175### loadstring
176
177**类型:** 函数。
178
179**描述:**
180
181将 YueScript 代码字符串加载为一个函数。
182
183**签名:**
184```lua
185loadstring: function(input: string, chunkname: string, env: table, config?: Config):
186 --[[loaded function]] nil | function(...: any): (any...),
187 --[[error]] string | nil
188```
189
190**参数:**
191
192| 参数名 | 类型 | 描述 |
193| --- | --- | --- |
194| input | string | YueScript 代码。 |
195| chunkname | string | 代码块的名称。 |
196| env | table | 环境表。 |
197| config | Config | [可选] 编译器选项。 |
198
199**返回值:**
200
201| 返回类型 | 描述 |
202| --- | --- |
203| function \| nil | 加载的函数,如果加载失败则为 nil。 |
204| string \| nil | 错误消息,如果加载成功则为 nil。 |
205
206### loadstring
207
208**类型:** 函数。
209
210**描述:**
211
212将 YueScript 代码字符串加载为一个函数。
213
214**签名:**
215```lua
216loadstring: function(input: string, chunkname: string, config?: Config):
217 --[[loaded function]] nil | function(...: any): (any...),
218 --[[error]] string | nil
219```
220
221**参数:**
222
223| 参数名 | 类型 | 描述 |
224| --- | --- | --- |
225| input | string | YueScript 代码。 |
226| chunkname | string | 代码块的名称。 |
227| config | Config | [可选] 编译器选项。 |
228
229**返回值:**
230
231| 返回类型 | 描述 |
232| --- | --- |
233| function \| nil | 加载的函数,如果加载失败则为 nil。 |
234| string \| nil | 错误消息,如果加载成功则为 nil。 |
235
236### loadstring
237
238**类型:** 函数。
239
240**描述:**
241
242将 YueScript 代码字符串加载为一个函数。
243
244**签名:**
245```lua
246loadstring: function(input: string, config?: Config):
247 --[[loaded function]] nil | function(...: any): (any...),
248 --[[error]] string | nil
249```
250
251**参数:**
252
253| 参数名 | 类型 | 描述 |
254| --- | --- | --- |
255| input | string | YueScript 代码。 |
256| config | Config | [可选] 编译器选项。 |
257
258**返回值:**
259
260| 返回类型 | 描述 |
261| --- | --- |
262| function \| nil | 加载的函数,如果加载失败则为 nil。 |
263| string \| nil | 错误消息,如果加载成功则为 nil。 |
264
265### loadfile
266
267**类型:** 函数。
268
269**描述:**
270
271将 YueScript 代码文件加载为一个函数。
272
273**签名:**
274```lua
275loadfile: function(filename: string, env: table, config?: Config):
276 nil | function(...: any): (any...),
277 string | nil
278```
279
280**参数:**
281
282| 参数名 | 类型 | 描述 |
283| --- | --- | --- |
284| filename | string | 文件名。 |
285| env | table | 环境表。 |
286| config | Config | [可选] 编译器选项。 |
287
288**返回值:**
289
290| 返回类型 | 描述 |
291| --- | --- |
292| function \| nil | 加载的函数,如果加载失败则为 nil。 |
293| string \| nil | 错误消息,如果加载成功则为 nil。 |
294
295### loadfile
296
297**类型:** 函数。
298
299**描述:**
300
301将 YueScript 代码文件加载为一个函数。
302
303**签名:**
304```lua
305loadfile: function(filename: string, config?: Config):
306 nil | function(...: any): (any...),
307 string | nil
308```
309
310**参数:**
311
312| 参数名 | 类型 | 描述 |
313| --- | --- | --- |
314| filename | string | 文件名。 |
315| config | Config | [可选] 编译器选项。 |
316
317**返回值:**
318
319| 返回类型 | 描述 |
320| --- | --- |
321| function \| nil | 加载的函数,如果加载失败则为 nil。 |
322| string \| nil | 错误消息,如果加载成功则为 nil。 |
323
324### dofile
325
326**类型:** 函数。
327
328**描述:**
329
330将 YueScript 代码文件加载为一个函数并执行。
331
332**签名:**
333```lua
334dofile: function(filename: string, env: table, config?: Config): any...
335```
336
337**参数:**
338
339| 参数名 | 类型 | 描述 |
340| --- | --- | --- |
341| filename | string | 文件名。 |
342| env | table | 环境表。 |
343| config | Config | [可选] 编译器选项。 |
344
345**返回值:**
346
347| 返回类型 | 描述 |
348| --- | --- |
349| any... | 加载的函数执行后的返回值。 |
350
351### dofile
352
353**类型:** 函数。
354
355**描述:**
356
357将 YueScript 代码文件加载为一个函数并执行。
358
359**签名:**
360```lua
361dofile: function(filename: string, config?: Config): any...
362```
363
364**参数:**
365
366| 参数名 | 类型 | 描述 |
367| --- | --- | --- |
368| filename | string | 文件名。 |
369| config | Config | [可选] 编译器选项。 |
370
371**返回值:**
372
373| 返回类型 | 描述 |
374| --- | --- |
375| any... | 加载的函数执行后的返回值。 |
376
377### find_modulepath
378
379**类型:** 函数。
380
381**描述:**
382
383将 YueScript 模块名解析为文件路径。
384
385**签名:**
386```lua
387find_modulepath: function(name: string): string
388```
389
390**参数:**
391
392| 参数名 | 类型 | 描述 |
393| --- | --- | --- |
394| name | string | 模块名。 |
395
396**返回值:**
397
398| 返回类型 | 描述 |
399| --- | --- |
400| string | 文件路径。 |
401
402### pcall
403
404**类型:** 函数。
405
406**描述:**
407
408在保护模式下调用一个函数。
409会捕获任何错误,执行成功则返回成功状态和结果,否则为失败状态和错误信息。
410当发生错误时,将错误信息中的代码行号重写为 YueScript 代码中的原始行号。
411
412**签名:**
413```lua
414pcall: function(f: function, ...: any): boolean, any...
415```
416
417**参数:**
418
419| 参数名 | 类型 | 描述 |
420| --- | --- | --- |
421| f | function | 要调用的函数。 |
422| ... | any | 要传递给函数的参数。 |
423
424**返回值:**
425
426| 返回类型 | 描述 |
427| --- | --- |
428| boolean, ... | 状态码和函数结果或错误信息。 |
429
430### require
431
432**类型:** 函数。
433
434**描述:**
435
436加载给定的模块。可以是 Lua 模块或 YueScript 模块。
437如果模块是 YueScript 模块且加载失败,则将错误信息中的代码行号重写为 YueScript 代码中的原始行号。
438
439**签名:**
440```lua
441require: function(name: string): any...
442```
443
444**参数:**
445
446| 参数名 | 类型 | 描述 |
447| --- | --- | --- |
448| modname | string | 要加载的模块名。 |
449
450**返回值:**
451
452| 返回类型 | 描述 |
453| --- | --- |
454| any | 如果模块已经加载,则返回 package.loaded[modname] 中存储的值。否则,尝试查找加载器并返回 package.loaded[modname] 的最终值和加载器数据作为第二个结果。 |
455
456### p
457
458**类型:** 函数。
459
460**描述:**
461
462检查传递的值的内部结构,并打印值出它的字符串表示。
463
464**签名:**
465```lua
466p: function(...: any)
467```
468
469**参数:**
470
471| 参数名 | 类型 | 描述 |
472| --- | --- | --- |
473| ... | any | 要检查的值。 |
474
475### options
476
477**类型:** 成员变量。
478
479**描述:**
480
481当前编译器选项。
482
483**签名:**
484```lua
485options: Config.Options
486```
487
488### traceback
489
490**类型:** 函数。
491
492**描述:**
493
494重写堆栈跟踪中的行号为 YueScript 代码中的原始行号的 traceback 函数。
495
496**签名:**
497```lua
498traceback: function(message: string): string
499```
500
501**参数:**
502
503| 参数名 | 类型 | 描述 |
504| --- | --- | --- |
505| message | string | 堆栈跟踪消息。 |
506
507**返回值:**
508
509| 返回类型 | 描述 |
510| --- | --- |
511| string | 重写后的堆栈跟踪消息。 |
512
513### is_ast
514
515**类型:** 函数。
516
517**描述:**
518
519检查代码是否匹配指定的 AST。
520
521**签名:**
522```lua
523is_ast: function(astName: string, code: string): boolean
524```
525
526**参数:**
527
528| 参数名 | 类型 | 描述 |
529| --- | --- | --- |
530| astName | string | AST 名称。 |
531| code | string | 代码。 |
532
533**返回值:**
534
535| 返回类型 | 描述 |
536| --- | --- |
537| boolean | 代码是否匹配 AST。 |
538
539### AST
540
541**类型:** 成员变量。
542
543**描述:**
544
545AST 类型定义,带有名称、行、列和子节点。
546
547**签名:**
548```lua
549type AST = {string, integer, integer, any}
550```
551
552### to_ast
553
554**类型:** 函数。
555
556**描述:**
557
558将代码转换为 AST。
559
560**签名:**
561```lua
562to_ast: function(code: string, flattenLevel?: number, astName?: string, reserveComment?: boolean):
563 --[[AST]] AST | nil,
564 --[[error]] nil | string
565```
566
567**参数:**
568
569| 参数名 | 类型 | 描述 |
570| --- | --- | --- |
571| code | string | 代码。 |
572| flattenLevel | integer | [可选] 扁平化级别。级别越高,会消除更多的 AST 结构的嵌套。默认为 0。最大为 2。 |
573| astName | string | [可选] AST 名称。默认为 "File"。 |
574| reserveComment | boolean | [可选] 是否保留原始注释。默认为 false。 |
575
576**返回值:**
577
578| 返回类型 | 描述 |
579| --- | --- |
580| AST \| nil | AST,如果转换失败则为 nil。 |
581| string \| nil | 错误消息,如果转换成功则为 nil。 |
582
583### format
584
585**类型:** 函数。
586
587**描述:**
588
589格式化 YueScript 代码。
590
591**签名:**
592```lua
593format: function(code: string, tabSize?: number, reserveComment?: boolean): string
594```
595
596**参数:**
597
598| 参数名 | 类型 | 描述 |
599| --- | --- | --- |
600| code | string | 代码。 |
601| tabSize | integer | [可选] 制表符大小。默认为 4。 |
602| reserveComment | boolean | [可选] 是否保留原始注释。默认为 true。 |
603
604**返回值:**
605
606| 返回类型 | 描述 |
607| --- | --- |
608| string | 格式化后的代码。 |
609
610### __call
611
612**类型:** 元方法。
613
614**描述:**
615
616导入 YueScript 模块。
617如果发生加载失败,则将错误信息中的代码行号重写为 YueScript 代码中的原始行号。
618
619**签名:**
620```lua
621metamethod __call: function(self: yue, module: string): any...
622```
623
624**参数:**
625
626| 参数名 | 类型 | 描述 |
627| --- | --- | --- |
628| module | string | 模块名。 |
629
630**返回值:**
631
632| 返回类型 | 描述 |
633| --- | --- |
634| any | 模块值。 |
635
636## Config
637
638**描述:**
639
640编译器编译选项。
641
642### lint_global
643
644**类型:** 成员变量。
645
646**描述:**
647
648编译器是否应该收集代码中出现的全局变量。
649
650**签名:**
651```lua
652lint_global: boolean
653```
654
655### implicit_return_root
656
657**类型:** 成员变量。
658
659**描述:**
660
661编译器是否应该对根层级的代码块进行隐式的表达式返回。
662
663**签名:**
664```lua
665implicit_return_root: boolean
666```
667
668### reserve_line_number
669
670**类型:** 成员变量。
671
672**描述:**
673
674编译器是否应该在编译后的代码中保留原始行号。
675
676**签名:**
677```lua
678reserve_line_number: boolean
679```
680
681### reserve_comment
682
683**类型:** 成员变量。
684
685**描述:**
686
687编译器是否应该在编译后的代码中保留原始注释。
688
689**签名:**
690```lua
691reserve_comment: boolean
692```
693
694### space_over_tab
695
696**类型:** 成员变量。
697
698**描述:**
699
700编译器是否应该在编译后的代码中使用空格字符而不是制表符字符。
701
702**签名:**
703```lua
704space_over_tab: boolean
705```
706
707### same_module
708
709**类型:** 成员变量。
710
711**描述:**
712
713编译器是否应该将要编译的代码视为当前正在编译的模块。仅供编译器内部使用。
714
715**签名:**
716```lua
717same_module: boolean
718```
719
720### line_offset
721
722**类型:** 成员变量。
723
724**描述:**
725
726编译器错误消息是否应该包含行号偏移量。仅供编译器内部使用。
727
728**签名:**
729```lua
730line_offset: integer
731```
732
733### yue.Config.LuaTarget
734
735**类型:** 枚举。
736
737**描述:**
738
739目标 Lua 版本枚举。
740
741**签名:**
742```lua
743enum LuaTarget
744 "5.1"
745 "5.2"
746 "5.3"
747 "5.4"
748 "5.5"
749end
750```
751
752### options
753
754**类型:** 成员变量。
755
756**描述:**
757
758要传递给编译函数的额外选项。
759
760**签名:**
761```lua
762options: Options
763```
764
765## Options
766
767**描述:**
768
769额外编译器选项定义。
770
771### target
772
773**类型:** 成员变量。
774
775**描述:**
776
777编译目标 Lua 版本。
778
779**签名:**
780```lua
781target: LuaTarget
782```
783
784### path
785
786**类型:** 成员变量。
787
788**描述:**
789
790额外模块搜索路径。
791
792**签名:**
793```lua
794path: string
795```
796
797### dump_locals
798
799**类型:** 成员变量。
800
801**描述:**
802
803是否在回溯错误消息中输出代码块的局部变量。默认为 false。
804
805**签名:**
806```lua
807dump_locals: boolean
808```
809
810### simplified
811
812**类型:** 成员变量。
813
814**描述:**
815
816是否简化输出的错误消息。默认为 true。
817
818**签名:**
819```lua
820simplified: boolean
821```
diff --git a/doc/docs/zh/doc/try.md b/doc/docs/zh/doc/try.md
new file mode 100644
index 0000000..b4de24d
--- /dev/null
+++ b/doc/docs/zh/doc/try.md
@@ -0,0 +1,105 @@
1# 错误处理
2
3&emsp;&emsp;用于统一进行 Lua 错误处理的便捷语法。
4
5```yuescript
6try
7 func 1, 2, 3
8catch err
9 print yue.traceback err
10
11success, result = try
12 func 1, 2, 3
13catch err
14 yue.traceback err
15
16try func 1, 2, 3
17catch err
18 print yue.traceback err
19
20success, result = try func 1, 2, 3
21
22try
23 print "尝试中"
24 func 1, 2, 3
25
26-- 使用if赋值模式
27if success, result := try func 1, 2, 3
28catch err
29 print yue.traceback err
30 print result
31```
32<YueDisplay>
33
34```yue
35try
36 func 1, 2, 3
37catch err
38 print yue.traceback err
39
40success, result = try
41 func 1, 2, 3
42catch err
43 yue.traceback err
44
45try func 1, 2, 3
46catch err
47 print yue.traceback err
48
49success, result = try func 1, 2, 3
50
51try
52 print "尝试中"
53 func 1, 2, 3
54
55-- 使用if赋值模式
56if success, result := try func 1, 2, 3
57catch err
58 print yue.traceback err
59 print result
60```
61
62</YueDisplay>
63
64## 错误处理简化
65
66&emsp;&emsp;`try?` 是 `try` 的功能简化语法,它不再返回 `try` 语句的布尔状态,并在成功时直接返回 `try` 代码块的结果,失败时返回 `nil` 值而非错误对象。
67
68```yuescript
69a, b, c = try? func!
70
71-- 与空值合并运算符一起使用
72a = (try? func!) ?? "default"
73
74-- 作为函数参数
75f try? func!
76
77-- 带 catch 块的 try!
78f try?
79 print 123
80 func!
81catch e
82 print e
83 e
84```
85<YueDisplay>
86
87```yue
88a, b, c = try? func!
89
90-- 与空值合并运算符一起使用
91a = (try? func!) ?? "default"
92
93-- 作为函数参数
94f try? func!
95
96-- 带 catch 块的 try!
97f try?
98 print 123
99 func!
100catch e
101 print e
102 e
103```
104
105</YueDisplay>
diff --git a/doc/docs/zh/doc/usage.md b/doc/docs/zh/doc/usage.md
new file mode 100644
index 0000000..b9d84d4
--- /dev/null
+++ b/doc/docs/zh/doc/usage.md
@@ -0,0 +1,110 @@
1# 使用方法
2
3## Lua 模块
4
5&emsp;&emsp;在 Lua 中使用月之脚本模块:
6
7* **用法 1**
8
9 &emsp;&emsp;在 Lua 中引入 "你的脚本入口文件.yue"。
10
11 ```Lua
12 require("yue")("你的脚本入口文件")
13 ```
14
15 &emsp;&emsp;当你在同一路径下把 "你的脚本入口文件.yue" 编译成了 "你的脚本入口文件.lua" 时,仍然可以使用这个代码加载 .lua 代码文件。在其余的月之脚本文件中,只需正常使用 **require** 或 **import** 进行脚本引用即可。错误消息中的代码行号也会被正确处理。
16
17* **用法 2**
18
19 &emsp;&emsp;手动引入月之脚本模块并重写错误消息来帮助调试。
20 ```lua
21 local yue = require("yue")
22 yue.insert_loaders()
23 local success, result = xpcall(function()
24 return require("yuescript_module_name")
25 end, function(err)
26 return yue.traceback(err)
27 end)
28 ```
29
30* **用法 3**
31
32 &emsp;&emsp;在 Lua 中使用月之脚本编译器功能。
33 ```lua
34 local yue = require("yue")
35 local codes, err, globals = yue.to_lua([[
36 f = ->
37 print "hello world"
38 f!
39 ]],{
40 implicit_return_root = true,
41 reserve_line_number = true,
42 lint_global = true,
43 space_over_tab = false,
44 options = {
45 target = "5.4",
46 path = "/script"
47 }
48 })
49 ```
50
51## 月之脚本编译工具
52
53&emsp;&emsp;使用月之脚本编译工具:
54
55```shell
56> yue -h
57命令行用法: yue
58 [选项] [<文件/目录>] ...
59 yue -e <代码或文件> [参数...]
60 yue -w [<目录>] [选项]
61 yue -
62
63说明:
64 - '-' 或 '--' 必须作为唯一且第一个参数,用于读取标准输入。
65 - '-o/--output' 不能与多个输入文件一起使用。
66 - '-w/--watch' 仅能用于目录,不能用于单个文件。
67 - 使用 '-e/--execute' 时,后续的参数将作为脚本参数传递。
68
69选项:
70 -h, --help 显示帮助信息并退出
71 -e <字符串>, --execute <字符串> 执行文件或原始代码
72 -m, --minify 生成压缩(最小化)代码
73 -r, --rewrite 重写输出以匹配原始代码行号
74 -t <目标路径>, --output-to <目标路径>
75 指定编译后文件的输出路径
76 -o <文件>, --output <文件> 将输出写入文件
77 -p, --print 输出到标准输出
78 -b, --benchmark 输出编译耗时(不写入文件)
79 -g, --globals 显示用到的全局变量及其所在的名称、行号、列号
80 -s, --spaces 用空格代替制表符(tab)输出代码
81 -l, --line-numbers 输出源代码的行号
82 -j, --no-implicit-return 禁用文件末尾的隐式返回
83 -c, --reserve-comments 保留源代码中的注释
84 -w [<目录>], --watch [<目录>]
85 监视目录变化并自动编译
86 -v, --version 显示版本信息
87 - 从标准输入读取,输出到标准输出(仅能作为唯一参数)
88 -- 等同于 '-',为兼容旧版本保留
89
90 --target <版本> 指定生成代码的 Lua 版本 (只能为 5.1 ~ 5.5)
91 --path <路径字符串> 附加一个 Lua 搜索路径到 package.path
92 --<键>=<值> 以 key=value 形式传递编译器选项(保持已有用法)
93
94 不带选项直接运行可进入交互模式(REPL),在交互模式里输入单独的符号 '$'
95 可用于开始或结束多行模式。
96```
97
98&emsp;&emsp;使用案例:
99
100&emsp;&emsp;递归编译当前路径下扩展名为 **.yue** 的每个月之脚本文件: **yue .**
101
102&emsp;&emsp;编译并将结果保存到目标路径: **yue -t /target/path/ .**
103
104&emsp;&emsp;编译并保留调试信息: **yue -l .**
105
106&emsp;&emsp;编译并生成压缩代码: **yue -m .**
107
108&emsp;&emsp;直接执行代码: **yue -e 'print 123'**
109
110&emsp;&emsp;执行一个月之脚本文件: **yue -e main.yue**
diff --git a/doc/docs/zh/doc/varargs-assignment.md b/doc/docs/zh/doc/varargs-assignment.md
new file mode 100644
index 0000000..6cc4278
--- /dev/null
+++ b/doc/docs/zh/doc/varargs-assignment.md
@@ -0,0 +1,24 @@
1# 可变参数赋值
2
3&emsp;&emsp;你可以将函数返回的结果赋值给一个可变参数符号 `...`。然后使用 Lua 的方式访问其内容。
4
5```yuescript
6list = [1, 2, 3, 4, 5]
7fn = (ok) -> ok, table.unpack list
8ok, ... = fn true
9count = select '#', ...
10first = select 1, ...
11print ok, count, first
12```
13<YueDisplay>
14
15```yue
16list = [1, 2, 3, 4, 5]
17fn = (ok) -> ok, table.unpack list
18ok, ... = fn true
19count = select '#', ...
20first = select 1, ...
21print ok, count, first
22```
23
24</YueDisplay>
diff --git a/doc/docs/zh/doc/while-loop.md b/doc/docs/zh/doc/while-loop.md
new file mode 100644
index 0000000..5995890
--- /dev/null
+++ b/doc/docs/zh/doc/while-loop.md
@@ -0,0 +1,69 @@
1# while 循环
2
3&emsp;&emsp;在月之脚本中的 while 循环支持几种不同的写法:
4
5```yuescript
6i = 10
7while i > 0
8 print i
9 i -= 1
10
11while running == true do my_function!
12```
13<YueDisplay>
14
15```yue
16i = 10
17while i > 0
18 print i
19 i -= 1
20
21while running == true do my_function!
22```
23
24</YueDisplay>
25
26```yuescript
27i = 10
28until i == 0
29 print i
30 i -= 1
31
32until running == false do my_function!
33```
34<YueDisplay>
35
36```yue
37i = 10
38until i == 0
39 print i
40 i -= 1
41until running == false do my_function!
42```
43
44</YueDisplay>
45
46&emsp;&emsp;像 for 循环的语法一样,while 循环也可以作为一个表达式使用。为了使函数返回 while 循环的累积列表值,必须明确使用返回语句返回 while 循环表达式。
47
48## repeat 循环
49
50&emsp;&emsp;repeat 循环是从 Lua 语言中搬过来的相似语法:
51
52```yuescript
53i = 10
54repeat
55 print i
56 i -= 1
57until i == 0
58```
59<YueDisplay>
60
61```yue
62i = 10
63repeat
64 print i
65 i -= 1
66until i == 0
67```
68
69</YueDisplay>
diff --git a/doc/docs/zh/doc/whitespace.md b/doc/docs/zh/doc/whitespace.md
new file mode 100644
index 0000000..1886e23
--- /dev/null
+++ b/doc/docs/zh/doc/whitespace.md
@@ -0,0 +1,43 @@
1# 空白
2
3&emsp;&emsp;月之脚本是一个对空白敏感的语言。你必须在相同的缩进中使用空格 **' '** 或制表符 **'\t'** 来编写一些代码块,如函数体、值列表和一些控制块。包含不同空白的表达式可能意味着不同的事情。制表符被视为4个空格,但最好不要混合使用空格和制表符。
4
5## 语句分隔符
6
7&emsp;&emsp;一条语句通常以换行结束。你也可以使用分号 `;` 显式结束一条语句,从而在同一行中编写多条语句:
8
9```yuescript
10a = 1; b = 2; print a + b
11```
12<YueDisplay>
13
14```yue
15a = 1; b = 2; print a + b
16```
17
18</YueDisplay>
19
20## 多行链式调用
21
22&emsp;&emsp;你可以使用相同的缩进来编写多行链式函数调用。
23
24```yuescript
25Rx.Observable
26 .fromRange 1, 8
27 \filter (x) -> x % 2 == 0
28 \concat Rx.Observable.of 'who do we appreciate'
29 \map (value) -> value .. '!'
30 \subscribe print
31```
32<YueDisplay>
33
34```yue
35Rx.Observable
36 .fromRange 1, 8
37 \filter (x) -> x % 2 == 0
38 \concat Rx.Observable.of 'who do we appreciate'
39 \map (value) -> value .. '!'
40 \subscribe print
41```
42
43</YueDisplay>
diff --git a/doc/docs/zh/doc/with-statement.md b/doc/docs/zh/doc/with-statement.md
new file mode 100644
index 0000000..fbd3633
--- /dev/null
+++ b/doc/docs/zh/doc/with-statement.md
@@ -0,0 +1,126 @@
1# with 语句
2
3
4在编写 Lua 代码时,我们在创建对象后的常见操作是立即调用这个对象一系列操作函数并设置一系列属性。
5
6这导致在代码中多次重复引用对象的名称,增加了不必要的文本噪音。一个常见的解决方案是在创建对象时,在构造函数传入一个表,该表包含要覆盖设置的键和值的集合。这样做的缺点是该对象的构造函数必须支持这种初始化形式。
7
8with 块有助于简化编写这样的代码。在 with 块内,我们可以使用以 . 或 \ 开头的特殊语句,这些语句代表我们正在使用的对象的操作。
9
10例如,我们可以这样处理一个新创建的对象:
11
12```yuescript
13with Person!
14 .name = "Oswald"
15 \add_relative my_dad
16 \save!
17 print .name
18```
19<YueDisplay>
20
21```yue
22with Person!
23 .name = "Oswald"
24 \add_relative my_dad
25 \save!
26 print .name
27```
28
29</YueDisplay>
30
31with 语句也可以用作一个表达式,并返回它的代码块正在处理的对象。
32
33```yuescript
34file = with File "favorite_foods.txt"
35 \set_encoding "utf8"
36```
37<YueDisplay>
38
39```yue
40file = with File "favorite_foods.txt"
41 \set_encoding "utf8"
42```
43
44</YueDisplay>
45
46或者…
47
48```yuescript
49create_person = (name, relatives) ->
50 with Person!
51 .name = name
52 \add_relative relative for relative in *relatives
53
54me = create_person "Leaf", [dad, mother, sister]
55```
56<YueDisplay>
57
58```yue
59create_person = (name, relatives) ->
60 with Person!
61 .name = name
62 \add_relative relative for relative in *relatives
63
64me = create_person "Leaf", [dad, mother, sister]
65```
66
67</YueDisplay>
68
69在此用法中,with 可以被视为K组合子(k-combinator)的一种特殊形式。
70
71如果你想给表达式另外起一个名称的话,with 语句中的表达式也可以是一个赋值语句。
72
73```yuescript
74with str := "你好"
75 print "原始:", str
76 print "大写:", \upper!
77```
78<YueDisplay>
79
80```yue
81with str := "你好"
82 print "原始:", str
83 print "大写:", \upper!
84```
85
86</YueDisplay>
87
88你可以在 `with` 语句中使用 `[]` 访问特殊键。
89
90```yuescript
91with tb
92 [1] = 1
93 print [2]
94 with [abc]
95 [3] = [2]\func!
96 ["key-name"] = value
97 [] = "abc" -- 追加到 "tb"
98```
99<YueDisplay>
100
101```yue
102with tb
103 [1] = 1
104 print [2]
105 with [abc]
106 [3] = [2]\func!
107 ["key-name"] = value
108 [] = "abc" -- 追加到 "tb"
109```
110
111</YueDisplay>
112
113`with?` 是 `with` 语法的一个增强版本,引入了存在性检查,用于在不显式判空的情况下安全访问可能为 nil 的对象。
114
115```yuescript
116with? obj
117 print obj.name
118```
119<YueDisplay>
120
121```yue
122with? obj
123 print obj.name
124```
125
126</YueDisplay>
diff --git a/doc/docs/zh/index.md b/doc/docs/zh/index.md
index 9068956..ea42085 100644
--- a/doc/docs/zh/index.md
+++ b/doc/docs/zh/index.md
@@ -17,7 +17,6 @@ features:
17 details: 管道、模式匹配、切片与解构,同时保留 Lua 互操作性。 17 details: 管道、模式匹配、切片与解构,同时保留 Lua 互操作性。
18 - title: 快速迭代 18 - title: 快速迭代
19 details: 虚心接受用户反馈,以帮助改进和加速语言的开发和演进! 19 details: 虚心接受用户反馈,以帮助改进和加速语言的开发和演进!
20footer: MIT Licensed | Copyright © 2017-2026 Li Jin
21--- 20---
22 21
23### 版权和协议 22### 版权和协议