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
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");
12 void test_keyboard_in_scenario() {
14 "scenario keyboard-in-scenario [\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"
24 " memory-should-contain [\n"
33 " 9 <- 1\n" // end of test events
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")
50 :(before
"End Primitive Recipe Numbers")
51 put(Recipe_ordinal
, "assume-console", ASSUME_CONSOLE
);
52 :(before
"End Primitive Recipe Checks")
53 case ASSUME_CONSOLE
: {
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
+ "]");
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
);
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
;
83 put(Memory
, curr_address
+1, to_integer(key
));
84 else if (contains_key(Key
, key
))
85 put(Memory
, curr_address
+1, Key
[key
]);
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);
92 // distinguish from unicode
93 put(Memory
, curr_address
, /*tag for 'keycode' variant of 'event' exclusive-container*/1);
95 curr_address
+= size_of_event();
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
);
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
);
129 :(before
"End Globals")
130 map
<string
, int> Key
;
131 :(before
"End One-time Setup")
132 initialize_key_names();
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
);
194 bool is_scenario(const recipe
& caller
) {
195 return starts_with(caller
.name
, "scenario_");
197 void initialize_special_name(reagent
& r
) {
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() {
206 "scenario events-in-scenario [\n"
207 " assume-console [\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"
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"
225 " memory-should-contain [\n"
226 " 1 <- 0\n" // 'text'
228 " 3 <- 0\n" // unused
229 " 4 <- 0\n" // unused
230 " 5 <- 0\n" // 'text'
232 " 7 <- 0\n" // unused
233 " 8 <- 0\n" // unused
234 " 9 <- 0\n" // 'text'
236 " 11 <- 0\n" // unused
237 " 12 <- 0\n" // unused
238 " 13 <- 2\n" // 'mouse'
239 " 14 <- 65513\n" // mouse click
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
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")
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
: {
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();
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
));
286 int count_events(const recipe
& r
) {
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
);
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
);
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
);