https://github.com/akkartik/mu1/blob/master/041jump_target.cc
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) << "'" << to_original_string(inst) << "' 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) << "'" << to_original_string(inst) << "' 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 true
158 ]
159 +error: main: 'jump-if 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'