fix other mandelbrot variants
[mu.git] / archive / 1.vm / 085scenario_console.cc
blob75c2a289a711dd4df11887d57ce91a16a3c79d37
1 //: Clean syntax to manipulate and check the console in scenarios.
2 //: Instruction 'assume-console' implicitly creates a variable called
3 //: 'console' that is accessible inside other 'run' instructions in the
4 //: scenario. Like with the fake screen, 'assume-console' transparently
5 //: supports unicode.
7 //: first make sure we don't mangle this instruction in other transforms
8 :(before "End initialize_transform_rewrite_literal_string_to_text()")
9 recipes_taking_literal_strings.insert("assume-console");
11 :(code)
12 void test_keyboard_in_scenario() {
13 run_mu_scenario(
14 "scenario keyboard-in-scenario [\n"
15 " assume-console [\n"
16 " type [abc]\n"
17 " ]\n"
18 " run [\n"
19 " 1:char, 2:bool <- read-key console\n"
20 " 3:char, 4:bool <- read-key console\n"
21 " 5:char, 6:bool <- read-key console\n"
22 " 7:char, 8:bool, 9:bool <- read-key console\n"
23 " ]\n"
24 " memory-should-contain [\n"
25 " 1 <- 97\n" // 'a'
26 " 2 <- 1\n"
27 " 3 <- 98\n" // 'b'
28 " 4 <- 1\n"
29 " 5 <- 99\n" // 'c'
30 " 6 <- 1\n"
31 " 7 <- 0\n" // unset
32 " 8 <- 1\n"
33 " 9 <- 1\n" // end of test events
34 " ]\n"
35 "]\n"
39 :(before "End Scenario Globals")
40 extern const int CONSOLE = next_predefined_global_for_scenarios(/*size_of(address:console)*/2);
41 //: give 'console' a fixed location in scenarios
42 :(before "End Special Scenario Variable Names(r)")
43 Name[r]["console"] = CONSOLE;
44 //: make 'console' always a raw location in scenarios
45 :(before "End is_special_name Special-cases")
46 if (s == "console") return true;
48 :(before "End Primitive Recipe Declarations")
49 ASSUME_CONSOLE,
50 :(before "End Primitive Recipe Numbers")
51 put(Recipe_ordinal, "assume-console", ASSUME_CONSOLE);
52 :(before "End Primitive Recipe Checks")
53 case ASSUME_CONSOLE: {
54 break;
56 :(before "End Primitive Recipe Implementations")
57 case ASSUME_CONSOLE: {
58 // create a temporary recipe just for parsing; it won't contain valid instructions
59 istringstream in("[" + current_instruction().ingredients.at(0).name + "]");
60 recipe r;
61 slurp_body(in, r);
62 int num_events = count_events(r);
63 // initialize the events like in new-fake-console
64 int size = /*length*/1 + num_events*size_of_event();
65 int event_data_address = allocate(size);
66 // store length
67 put(Memory, event_data_address+/*skip alloc id*/1, num_events);
68 int curr_address = event_data_address + /*skip alloc id*/1 + /*skip length*/1;
69 for (int i = 0; i < SIZE(r.steps); ++i) {
70 const instruction& inst = r.steps.at(i);
71 if (inst.name == "left-click") {
72 trace(Callstack_depth+1, "mem") << "storing 'left-click' event starting at " << Current_routine->alloc << end();
73 put(Memory, curr_address, /*tag for 'touch-event' variant of 'event' exclusive-container*/2);
74 put(Memory, curr_address+/*skip tag*/1+/*offset of 'type' in 'mouse-event'*/0, TB_KEY_MOUSE_LEFT);
75 put(Memory, curr_address+/*skip tag*/1+/*offset of 'row' in 'mouse-event'*/1, to_integer(inst.ingredients.at(0).name));
76 put(Memory, curr_address+/*skip tag*/1+/*offset of 'column' in 'mouse-event'*/2, to_integer(inst.ingredients.at(1).name));
77 curr_address += size_of_event();
79 else if (inst.name == "press") {
80 trace(Callstack_depth+1, "mem") << "storing 'press' event starting at " << curr_address << end();
81 string key = inst.ingredients.at(0).name;
82 if (is_integer(key))
83 put(Memory, curr_address+1, to_integer(key));
84 else if (contains_key(Key, key))
85 put(Memory, curr_address+1, Key[key]);
86 else
87 raise << "assume-console: can't press '" << key << "'\n" << end();
88 if (get_or_insert(Memory, curr_address+1) < 256)
89 // these keys are in ascii
90 put(Memory, curr_address, /*tag for 'text' variant of 'event' exclusive-container*/0);
91 else {
92 // distinguish from unicode
93 put(Memory, curr_address, /*tag for 'keycode' variant of 'event' exclusive-container*/1);
95 curr_address += size_of_event();
97 // End Event Handlers
98 else {
99 // keyboard input
100 assert(inst.name == "type");
101 trace(Callstack_depth+1, "mem") << "storing 'type' event starting at " << curr_address << end();
102 const string& contents = inst.ingredients.at(0).name;
103 const char* raw_contents = contents.c_str();
104 int num_keyboard_events = unicode_length(contents);
105 int curr = 0;
106 for (int i = 0; i < num_keyboard_events; ++i) {
107 trace(Callstack_depth+1, "mem") << "storing 'text' tag at " << curr_address << end();
108 put(Memory, curr_address, /*tag for 'text' variant of 'event' exclusive-container*/0);
109 uint32_t curr_character;
110 assert(curr < SIZE(contents));
111 tb_utf8_char_to_unicode(&curr_character, &raw_contents[curr]);
112 trace(Callstack_depth+1, "mem") << "storing character " << curr_character << " at " << curr_address+/*skip exclusive container tag*/1 << end();
113 put(Memory, curr_address+/*skip exclusive container tag*/1, curr_character);
114 curr += tb_utf8_char_length(raw_contents[curr]);
115 curr_address += size_of_event();
119 assert(curr_address == event_data_address+/*skip alloc id*/1+size);
120 // wrap the array of events in a console object
121 int console_address = allocate(size_of_console());
122 trace(Callstack_depth+1, "mem") << "storing console in " << console_address << end();
123 put(Memory, CONSOLE+/*skip alloc id*/1, console_address);
124 trace(Callstack_depth+1, "mem") << "storing console data in " << console_address+/*offset of 'data' in container 'events'*/1 << end();
125 put(Memory, console_address+/*skip alloc id*/1+/*offset of 'data' in container 'events'*/1+/*skip alloc id of 'data'*/1, event_data_address);
126 break;
129 :(before "End Globals")
130 map<string, int> Key;
131 :(before "End One-time Setup")
132 initialize_key_names();
133 :(code)
134 void initialize_key_names() {
135 Key["F1"] = TB_KEY_F1;
136 Key["F2"] = TB_KEY_F2;
137 Key["F3"] = TB_KEY_F3;
138 Key["F4"] = TB_KEY_F4;
139 Key["F5"] = TB_KEY_F5;
140 Key["F6"] = TB_KEY_F6;
141 Key["F7"] = TB_KEY_F7;
142 Key["F8"] = TB_KEY_F8;
143 Key["F9"] = TB_KEY_F9;
144 Key["F10"] = TB_KEY_F10;
145 Key["F11"] = TB_KEY_F11;
146 Key["F12"] = TB_KEY_F12;
147 Key["insert"] = TB_KEY_INSERT;
148 Key["delete"] = TB_KEY_DELETE;
149 Key["home"] = TB_KEY_HOME;
150 Key["end"] = TB_KEY_END;
151 Key["page-up"] = TB_KEY_PGUP;
152 Key["page-down"] = TB_KEY_PGDN;
153 Key["up-arrow"] = TB_KEY_ARROW_UP;
154 Key["down-arrow"] = TB_KEY_ARROW_DOWN;
155 Key["left-arrow"] = TB_KEY_ARROW_LEFT;
156 Key["right-arrow"] = TB_KEY_ARROW_RIGHT;
157 Key["ctrl-a"] = TB_KEY_CTRL_A;
158 Key["ctrl-b"] = TB_KEY_CTRL_B;
159 Key["ctrl-c"] = TB_KEY_CTRL_C;
160 Key["ctrl-d"] = TB_KEY_CTRL_D;
161 Key["ctrl-e"] = TB_KEY_CTRL_E;
162 Key["ctrl-f"] = TB_KEY_CTRL_F;
163 Key["ctrl-g"] = TB_KEY_CTRL_G;
164 Key["backspace"] = TB_KEY_BACKSPACE;
165 Key["ctrl-h"] = TB_KEY_CTRL_H;
166 Key["tab"] = TB_KEY_TAB;
167 Key["ctrl-i"] = TB_KEY_CTRL_I;
168 Key["ctrl-j"] = TB_KEY_CTRL_J;
169 Key["enter"] = TB_KEY_NEWLINE; // ignore CR/LF distinction; there is only 'enter'
170 Key["ctrl-k"] = TB_KEY_CTRL_K;
171 Key["ctrl-l"] = TB_KEY_CTRL_L;
172 Key["ctrl-m"] = TB_KEY_CTRL_M;
173 Key["ctrl-n"] = TB_KEY_CTRL_N;
174 Key["ctrl-o"] = TB_KEY_CTRL_O;
175 Key["ctrl-p"] = TB_KEY_CTRL_P;
176 Key["ctrl-q"] = TB_KEY_CTRL_Q;
177 Key["ctrl-r"] = TB_KEY_CTRL_R;
178 Key["ctrl-s"] = TB_KEY_CTRL_S;
179 Key["ctrl-t"] = TB_KEY_CTRL_T;
180 Key["ctrl-u"] = TB_KEY_CTRL_U;
181 Key["ctrl-v"] = TB_KEY_CTRL_V;
182 Key["ctrl-w"] = TB_KEY_CTRL_W;
183 Key["ctrl-x"] = TB_KEY_CTRL_X;
184 Key["ctrl-y"] = TB_KEY_CTRL_Y;
185 Key["ctrl-z"] = TB_KEY_CTRL_Z;
186 Key["escape"] = TB_KEY_ESC;
187 Key["ctrl-slash"] = TB_KEY_CTRL_SLASH;
190 :(after "Begin check_or_set_invalid_types(r)")
191 if (is_scenario(caller))
192 initialize_special_name(r);
193 :(code)
194 bool is_scenario(const recipe& caller) {
195 return starts_with(caller.name, "scenario_");
197 void initialize_special_name(reagent& r) {
198 if (r.type) return;
199 // no need for screen
200 if (r.name == "console") r.type = new_type_tree("address:console");
201 // End Initialize Type Of Special Name In Scenario(r)
204 void test_events_in_scenario() {
205 run_mu_scenario(
206 "scenario events-in-scenario [\n"
207 " assume-console [\n"
208 " type [abc]\n"
209 " left-click 0, 1\n"
210 " press up-arrow\n"
211 " type [d]\n"
212 " ]\n"
213 " run [\n"
214 // 3 keyboard events; each event occupies 4 locations
215 " 1:event <- read-event console\n"
216 " 5:event <- read-event console\n"
217 " 9:event <- read-event console\n"
218 // mouse click
219 " 13:event <- read-event console\n"
220 // non-character keycode
221 " 17:event <- read-event console\n"
222 // final keyboard event
223 " 21:event <- read-event console\n"
224 " ]\n"
225 " memory-should-contain [\n"
226 " 1 <- 0\n" // 'text'
227 " 2 <- 97\n" // 'a'
228 " 3 <- 0\n" // unused
229 " 4 <- 0\n" // unused
230 " 5 <- 0\n" // 'text'
231 " 6 <- 98\n" // 'b'
232 " 7 <- 0\n" // unused
233 " 8 <- 0\n" // unused
234 " 9 <- 0\n" // 'text'
235 " 10 <- 99\n" // 'c'
236 " 11 <- 0\n" // unused
237 " 12 <- 0\n" // unused
238 " 13 <- 2\n" // 'mouse'
239 " 14 <- 65513\n" // mouse click
240 " 15 <- 0\n" // row
241 " 16 <- 1\n" // column
242 " 17 <- 1\n" // 'keycode'
243 " 18 <- 65517\n" // up arrow
244 " 19 <- 0\n" // unused
245 " 20 <- 0\n" // unused
246 " 21 <- 0\n" // 'text'
247 " 22 <- 100\n" // 'd'
248 " 23 <- 0\n" // unused
249 " 24 <- 0\n" // unused
250 " 25 <- 0\n"
251 " ]\n"
252 "]\n"
256 //: Deal with special keys and unmatched brackets by allowing each test to
257 //: independently choose the unicode symbol to denote them.
258 :(before "End Primitive Recipe Declarations")
259 REPLACE_IN_CONSOLE,
260 :(before "End Primitive Recipe Numbers")
261 put(Recipe_ordinal, "replace-in-console", REPLACE_IN_CONSOLE);
262 :(before "End Primitive Recipe Checks")
263 case REPLACE_IN_CONSOLE: {
264 break;
266 :(before "End Primitive Recipe Implementations")
267 case REPLACE_IN_CONSOLE: {
268 assert(scalar(ingredients.at(0)));
269 if (!get_or_insert(Memory, CONSOLE)) {
270 raise << "console not initialized\n" << end();
271 break;
273 int console_address = get_or_insert(Memory, CONSOLE);
274 int console_data = get_or_insert(Memory, console_address+1);
275 int length = get_or_insert(Memory, console_data); // array length
276 for (int i = 0, curr = console_data+1; i < length; ++i, curr+=size_of_event()) {
277 if (get_or_insert(Memory, curr) != /*text*/0) continue;
278 if (get_or_insert(Memory, curr+1) != ingredients.at(0).at(0)) continue;
279 for (int n = 0; n < size_of_event(); ++n)
280 put(Memory, curr+n, ingredients.at(1).at(n));
282 break;
285 :(code)
286 int count_events(const recipe& r) {
287 int result = 0;
288 for (int i = 0; i < SIZE(r.steps); ++i) {
289 const instruction& curr = r.steps.at(i);
290 if (curr.name == "type")
291 result += unicode_length(curr.ingredients.at(0).name);
292 else
293 ++result;
295 return result;
298 int size_of_event() {
299 // memoize result if already computed
300 static int result = 0;
301 if (result) return result;
302 type_tree* type = new type_tree("event");
303 result = size_of(type);
304 delete type;
305 return result;
308 int size_of_console() {
309 // memoize result if already computed
310 static int result = 0;
311 if (result) return result;
312 assert(get(Type_ordinal, "console"));
313 type_tree* type = new type_tree("console");
314 result = size_of(type);
315 delete type;
316 return result;