-
Notifications
You must be signed in to change notification settings - Fork 1
/
control.mdtlbl
155 lines (138 loc) · 3.76 KB
/
control.mdtlbl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# 控制语句
# 如while do_while skip if elif goto的条件都是用的Cmp
# Cmp的格式为 Value cmp Value.
# 当然, 有几个特例. 如`_`是always, Value是会展开为 ... != false
# 对于这些比较符, 都进行了映射. 我们不用去输入equal notEqual lessThanEq这样的
# 只需要输入 == != <= 即可
n = 2;
if n < 0 {
print "n < 0";
} elif n == 1 {
print "n == 1";
} elif n > 0 {
print "n > 0";
} else
print "n == 0";
end;
#* 以上代码会被展开为
set n 2
jump 10 lessThan n 0
jump 6 equal n 1
jump 8 greaterThan n 0
print "n == 0"
jump 11 always 0 0
print "n == 1"
jump 11 always 0 0
print "n > 0"
jump 11 always 0 0
print "n < 0"
end
*#
skip n > 5 {
print "! (n > 5)";
}
end;
#* 以上代码会被展开为
jump 2 greaterThan n 5
print "! (n > 5)"
end
*#
# 由此可以看到, skip相当于构建了一个跨越这个块的goto
while n < 2 {
op n n + 1;
}
end;
#* 以上代码会被展开为
jump 3 greaterThanEq n 2
op add n n 1
jump 1 lessThan n 2
end
*#
# 由上述代码可以看出, while是由一个相反条件跳过循环体, 并在循环体末尾跳到循环体首部
# 不采用第一句始终goto到条件起始位置而是对其取反是为了取得最高的效率, 完成零开销
# 但是这是有代价的, 这样的话, 就没法在条件比较的内部进行if while等了
# 因为这些都会生成内部跳转内部的goto, 而这里对条件包括表达式在内进行了克隆
# 也就是说这些内部goto的目标都会变成两个地点, 所以会报错
# 解决方法也有, 将需要在内部进行跳转的DExp使用const进行封装,
# const有重命名内部跳转的功能
do {
op n n + 1;
} while n < 6;
#* 以上代码会被展开为
op add n n 1
jump 0 lessThan n 6
*#
# 由上述代码可以看出, do_while就是单纯往回跳完成循环
:x
op n n + 1;
goto :x n < 9;
#* 以上代码会被展开为
op add n n 1
jump 0 lessThan n 9
*#
# 就如同do_while一样, 我们手动完成了它
i = 0;
gwhile i < 10 {
op i i + 1;
}
#* >>>
set i 0
jump 3 always 0 0
op add i i 1
jump 2 lessThan i 10
*#
# 这是为了避免普通while进行条件复制带来的副作用与代码膨胀而诞生的
# 可以看到, 头部并不是对条件反转然后跳过循环体而是直接跳到条件部分
# 然后就是一个do_while结构
# 也就是说它的结构为
i = 0;
goto :x1 _;
do {
op i i + 1;
:x1
} while i < 10;
#* >>>
set i 0
jump 3 always 0 0
op add i i 1
jump 2 lessThan i 10
*#
# 它的缺点是, 需要多一条跳转, 所以这导致while并没有采用这种形式
# 当然, 在手写逻辑时, 我们很喜欢这么写, 因为比较省力
select x {
print 0;
print 1 "is one!";
print 2;
}
#* >>>
op mul __0 x 2
op add @counter @counter __0
print 0
jump 4 always 0 0
print 1
print "is one!"
print 2
jump 0 always 0 0
*#
# select是原始版本, 没有switch那么多的功能,
# 但是相比switch的好处是不用编写case, 直接跳转到指定行
# 第0行, 第1行 以此类推
#
# select有多种展开形式, 当没有语句要生成时它也什么都不会生成
# 当每个case最大行数为一行时它会不采用倍数模式或跳转表模式, 而是直接单元模式
# 当每个case最大行数大于一行时, 它会计算使用倍数模式行数更少还是跳转表式行数更少
# 然后采用行数少的方式
# 对于倍数模式, 填充对齐不会应用于最后一个case
#
# 注: 无论是select switch还是gswitch, 在没有其余处理的情况下,
# 跳转目标小于0、非整数、大于最大case 都是未定义的
# 在0.15.2版本, 对于一些简单控制语句组合, 可以省略其括号, 比如
do do {
print 1;
} while c < d; while a < b;
#* >>>
print 1
jump 0 lessThan c d
jump 0 lessThan a b
*#
# 在0.15.5中, if也可以写进do_while了