1
2
3
4
5
6
7
8
9
10
11 bool is_jump_target(const string& label) {
12 if (label == "{" || label == "}") return false;
13
14 return is_label_word(label);
15 }
16
17 :(scenario jump_to_label)
18 def main [
19 jump +target:label
20 1:num <- copy 0
21 +target
22 ]
23 -mem: storing 0 in location 1
24
25 :(before "End Mu Types Initialization")
26 put(Type_ordinal, "label", 0);
27
28 :(before "End Instruction Modifying Transforms")
29 Transform.push_back(transform_labels);
30
31 :(code)
32 void transform_labels(const recipe_ordinal r) {
33 map<string, int> offset;
34 for (int i = 0; i < SIZE(get(Recipe, r).steps); ++i) {
35 ¦ const instruction& inst = get(Recipe, r).steps.at(i);
36 ¦ if (!inst.is_label) continue;
37 ¦ if (is_jump_target(inst.label)) {
38 ¦ ¦ if (!contains_key(offset, inst.label)) {
39 ¦ ¦ ¦ put(offset, inst.label, i);
40 ¦ ¦ }
41 ¦ ¦ else {
42 ¦ ¦ ¦ raise << maybe(get(Recipe, r).name) << "duplicate label '" << inst.label << "'" << end();
43 ¦ ¦ ¦
44 ¦ ¦ ¦ put(offset, inst.label, 9999);
45 ¦ ¦ }
46 ¦ }
47 }
48 for (int i = 0; i < SIZE(get(Recipe, r).steps); ++i) {
49 ¦ instruction& inst = get(Recipe, r).steps.at(i);
50 ¦ if (inst.name == "jump") {
51 ¦ ¦ if (inst.ingredients.empty()) {
52 ¦ ¦ ¦ raise << maybe(get(Recipe, r).name) << "'" << inst.original_string << "' expects an ingredient but got 0\n" << end();
53 ¦ ¦ ¦ return;
54 ¦ ¦ }
55 ¦ ¦ replace_offset(inst.ingredients.at(0), offset, i, r);
56 ¦ }
57 ¦ if (inst.name == "jump-if" || inst.name == "jump-unless") {
58 ¦ ¦ if (SIZE(inst.ingredients) < 2) {
59 ¦ ¦ ¦ raise << maybe(get(Recipe, r).name) << "'" << inst.original_string << "' expects 2 ingredients but got " << SIZE(inst.ingredients) << '\n' << end();
60 ¦ ¦ ¦ return;
61 ¦ ¦ }
62 ¦ ¦ replace_offset(inst.ingredients.at(1), offset, i, r);
63 ¦ }
64 ¦ if ((inst.name == "loop" || inst.name == "break")
65 ¦ ¦ ¦ && SIZE(inst.ingredients) >= 1) {
66 ¦ ¦ replace_offset(inst.ingredients.at(0), offset, i, r);
67 ¦ }
68 ¦ if ((inst.name == "loop-if" || inst.name == "loop-unless"
69 ¦ ¦ ¦ ¦ ¦ || inst.name == "break-if" || inst.name == "break-unless")
70 ¦ ¦ ¦ && SIZE(inst.ingredients) >= 2) {
71 ¦ ¦ replace_offset(inst.ingredients.at(1), offset, i, r);
72 ¦ }
73 }
74 }
75
76 void replace_offset(reagent& x, map<string, int>& offset, const int current_offset, const recipe_ordinal r) {
77 if (!is_literal(x)) {
78 ¦ raise << maybe(get(Recipe, r).name) << "jump target must be offset or label but is '" << x.original_string << "'\n" << end();
79 ¦ x.set_value(0);
80 ¦ return;
81 }
82 if (x.initialized) return;
83 if (is_integer(x.name)) return;
84 if (!is_jump_target(x.name)) {
85 ¦ raise << maybe(get(Recipe, r).name) << "can't jump to label '" << x.name << "'\n" << end();
86 ¦ x.set_value(0);
87 ¦ return;
88 }
89 if (!contains_key(offset, x.name)) {
90 ¦ raise << maybe(get(Recipe, r).name) << "can't find label '" << x.name << "'\n" << end();
91 ¦ x.set_value(0);
92 ¦ return;
93 }
94 x.set_value(get(offset, x.name) - current_offset);
95 }
96
97 :(scenario break_to_label)
98 def main [
99 {
100 ¦ {
101 ¦ ¦ break +target:label
102 ¦ ¦ 1:num <- copy 0
103 ¦ }
104 }
105 +target
106 ]
107 -mem: storing 0 in location 1
108
109 :(scenario jump_if_to_label)
110 def main [
111 {
112 ¦ {
113 ¦ ¦ jump-if 1, +target:label
114 ¦ ¦ 1:num <- copy 0
115 ¦ }
116 }
117 +target
118 ]
119 -mem: storing 0 in location 1
120
121 :(scenario loop_unless_to_label)
122 def main [
123 {
124 ¦ {
125 ¦ ¦ loop-unless 0, +target:label
126 ¦ ¦ 1:num <- copy 0
127 ¦ }
128 }
129 +target
130 ]
131 -mem: storing 0 in location 1
132
133 :(scenario jump_runs_code_after_label)
134 def main [
135
136 1:num <- copy 0
137 2:num <- copy 0
138 3:num <- copy 0
139 jump +target:label
140 4:num <- copy 0
141 +target
142 5:num <- copy 0
143 ]
144 +mem: storing 0 in location 5
145 -mem: storing 0 in location 4
146
147 :(scenario jump_fails_without_target)
148 % Hide_errors = true;
149 def main [
150 jump
151 ]
152 +error: main: 'jump' expects an ingredient but got 0
153
154 :(scenario jump_fails_without_target_2)
155 % Hide_errors = true;
156 def main [
157 jump-if 1/true
158 ]
159 +error: main: 'jump-if 1/true' expects 2 ingredients but got 1
160
161 :(scenario recipe_fails_on_duplicate_jump_target)
162 % Hide_errors = true;
163 def main [
164 +label
165 1:num <- copy 0
166 +label
167 2:num <- copy 0
168 ]
169 +error: main: duplicate label '+label'
170
171 :(scenario jump_ignores_nontarget_label)
172 % Hide_errors = true;
173 def main [
174
175 1:num <- copy 0
176 2:num <- copy 0
177 3:num <- copy 0
178 jump $target:label
179 4:num <- copy 0
180 $target
181 5:num <- copy 0
182 ]
183 +error: main: can't jump to label '$target'